Uniswap V2 spot reads distort reward payouts
A reserve-based oracle is only as honest as the pair at the instant it is read. In a thin or temporarily pushed pool, the current reserve ratio can move far enough to change a USD valuation by a large margin. If the vault trusts that number for both stake pricing and later reward conversion, the same distortion gets reused in two different places.
The failure mode is simple. A manipulated EFI/DAI reserve ratio makes EFI look cheaper in USD terms during staking, so a fixed-USD package needs less EFI than it should. The vault then records that bad number as real principal. Later, reward logic reads the pair again and converts USD-denominated rewards back into EFI using the live reserve state, which can still be wrong or can be wrong in a different way.
How the reserve read turns into bad accounting
The staking flow takes a fixed package value in USD and converts it into token amount from the current pair reserves. That means the package price is not anchored to a delayed reference, a time-weighted average, or an external feed. It is anchored to whatever the pair says right now.
That is enough to poison the accounting. The user’s recorded principal sits in storage as a USD figure, even though the token paid in was priced from a manipulated reserve read. Once that USD figure is persisted, it stops being a temporary oracle issue and becomes durable vault state. Any later reward maths that trusts the stored value inherits the error.
Fixed-USD packages become cheap when the pair is pushed
Package 7 in the described flow had a fixed value of $25,000. Under manipulation, each package required only 7.135164705123996531 EFI. The attacker repeated the package-7 stake one hundred times and recorded $2,500,000 of staking principal for 713.5164705123996531 EFI.
That is the core trick. The vault thinks it has sold a large block of fixed-USD staking capacity, but the on-chain cost was based on a reserve ratio that somebody had already nudged. The package looks correctly priced from inside the contract. It is not. It is just faithfully wrong.
tx.origin checks do nothing useful here. An externally owned account can still call into manipulated pricing just fine. The issue is not contract vs wallet. The issue is trusting the pair as a price source.
Reward claims repeat the mistake after the trade has cleared
The later claim path reads spot reserves again and converts USD-denominated rewards into EFI. Thirty-four blocks after the staking call, the same account claimed rewards and the vault paid 6256.533425502594495059 EFI for 25000e18 of USD-denominated rewards. That came out to roughly $25,000 in EFI on the realised side of the claim.
This is where stale thinking hurts. The trade that distorted the pair has already cleared, but the vault still treats the reserve ratio as a live price feed. If the reward code converts from USD into token amount using a fresh spot read, it just repeats the same trust mistake at a different point in the lifecycle. One bad read becomes persistent principal, then persistent payout logic.
Fresh spot reads pay out in the wrong asset amount
A fresh reserve read does not make the value safe. It only makes it current. If the pair is still thin, still easily moved, or still not back to a clean market state, the contract will pay the wrong token amount for a fixed USD reward.
That is a standard spot-price oracle manipulation pattern in DeFi accounting. The exact numbers change, but the behaviour does not. The contract accepts a live reserve ratio as if it were a settled market price, then uses that ratio to convert across denominations. Once the accounting boundary is crossed, the mistake is paid out in tokens.
Hardening the vault against reserve spoofing
Replace spot pricing with a delayed or external reference. A time-weighted average price, a delayed oracle read, or a separate price source breaks the direct link between same-transaction reserve distortion and vault accounting. The main goal is boring: stop a single pair state from deciding both deposit cost and reward value.
The claim path needs the same treatment. If stake entry uses one pricing method and claim conversion uses another, the vault still has a gap. Both paths should reject manipulated reserves in the same way, or neither path is safe.
Test the claim path against manipulated reserves
Testing should cover the full flow, not just the stake function. Push the pair, stake into the fixed-USD package, advance blocks, then claim rewards while the vault still sees a distorted or recently distorted reserve state. That catches the exact failure mode seen here: durable USD principal inflation followed by payout conversion from a bad price.
A useful control is to treat a spot read as untrusted unless it passes a liquidity and freshness check. If the pair can move enough in one transaction to change package pricing, it can move enough to corrupt reward conversion too. That is not a corner case. It is the attack path.



