Findings

17 findings across 11 protocols

Selected work lives on the homepage. Every entry here has a working proof-of-concept or an independently traced root cause against real source and, where possible, forked mainnet state. Severity is reported as found. Including the ones that were not exploitable in practice.

Critical
3
High
6
Medium
7
Informational
1

Showing 17 of 17 findings

Ellipsis Finance

BSC1 finding

lemon.fun

Robinhood Chain1 finding

Flex Finance

Ethereum2 findings

Hydrex

Ethereum1 finding

Noon (USN)

Ethereum2 findings

HyperFX

Ethereum1 finding

Monolith Market

Ethereum1 finding

Ripe Protocol

Base1 finding

Ramses DLMM

Robinhood Chain1 finding

Orvex

Robinhood Chain5 findings

Robinhood Chain

VoterV5 stale reward-index lets a late voter capture other gauges' skipped-epoch rewards

High

VoterV5._vote()/_reset() never refresh a gauge's reward-index checkpoint - unlike real Velodrome, which updates it on every vote action. The checkpoint only advances via distribute()/killGauge(), which is permissionless and unenforced per-epoch, so a gauge that goes unvoted for N epochs then receives even minimal weight captures the full N-epoch accumulated index delta.

Verified with working PoC

Robinhood Chain

ProtocolToken.sol has no burn() - veORVX early-exit claims permanently revert

High

VotingEscrowV2_LockLogic._claim() calls token.burn(penaltyAmount) whenever an early exit's penalty is nonzero, but the real deployed ORVX token (ERC20 + ERC20Permit + Ownable2Step only) has no burn() function at all. Any user - no privileged role needed - who locks ORVX in a NON_PERMANENT lock and later tries to exit early, accepting the documented penalty, has the transaction unconditionally revert.

Verified with working PoC

Robinhood Chain

oORVX.exerciseVe() unconditionally reverts - same missing-burnFrom root cause, independent entry point

Medium

oORVX's exerciseVe() converts option tokens into a permanent veORVX lock via createLockFor(), which unconditionally calls token.burnFrom() for any PERMANENT lock inside VotingEscrowV2_LockLogic._updateLock(). ORVX has no burnFrom() either, so this documented conversion path is completely non-functional for every holder.

Verified — second entry point, same root cause

Robinhood Chain

bveORVX.exerciseVe() calls a function selector that doesn't exist on the real oORVX contract

Medium

bveORVX's optionToken is statically typed as the 5-argument OptionTokenV3.exerciseVe(uint256,uint256,address,uint256,uint256) (selector 0xa9f6ee33), but the contract actually deployed at that address only implements the simpler 2-argument exerciseVe(uint256,address) (selector 0x9130325d) - two entirely different functions as far as the EVM is concerned.

Verified with working PoC

Robinhood Chain

MinterUpgradeableV3._initialize() one-time latch never actually latches

Medium

The guard require(_initializer != address(0), 'already initialized') is satisfied by any nonzero value, including the sentinel it sets on completion - so it never blocks a second call. The governor can re-run the genesis-distribution function at will, silently rewinding whatever decay curve the emission schedule applies.

Verified with working PoC

Panoptic V2

Ethereum1 finding