ElevateFi's $16K Loss and How Olympix Would Have Prevented It
Date: May 19, 2026
Chain: Polygon
Loss: 6,256.53 EFI, about $16K
Class: staking priced from a Uniswap V2 pool's live reserves
At 20:04:48 UTC on May 19, 2026, a single Polygon wallet borrowed about 1.35 million DAI, put it into EFI in one swap, and then staked into ElevateFi's staking vault 100 times in a row. Each stake cost the wallet 7.135 EFI. A minute later the same wallet called claim on the vault and received 6,256.53 EFI, and within ten minutes it had sold the tokens and bridged 16,147.33 DAI off Polygon through LI.FI. Defimon put the loss at about $16K and described it as oracle manipulation through a Uniswap V2 spot price, in its alert on the incident.
The vault didn't misread the price. It read exactly what the EFI/DAI pool said at that moment, and that moment was one the attacker had built with borrowed money.
Packages Sold in Dollars, Paid in EFI
ElevateFi's staking contract, a proxy at 0x816E...Ce74, sells staking packages with a fixed dollar size. When a user stakes, the contract looks up the package's USD amount, asks for the current EFI price, and works out how many EFI tokens the user owes. Rewards and stake balances are tracked in USD as well, and when the user claims, the contract converts those dollars back into EFI at the price of the moment.
That design puts the price at the center of every stake and every claim. In the implementation that was live during the attack, 0xcddc...3174, verified on Polygon Blockscout, the price came from one place:
That's the DAI reserve divided by the EFI reserve of a single Uniswap V2 pair, read at the instant of the call. Anyone who can move those reserves within a transaction can set the price the vault uses.
Where the Price Enters the Stake
stakeEFI() takes a package ID, fetches that price, and converts the package's dollar value into EFI:
The higher the spot price, the fewer EFI a package costs, but the stake is still recorded at its full dollar value. A user who stakes while EFI is pumped buys a full-size dollar position for a fraction of the tokens it would normally take. When the price falls back, every dollar of that position converts into far more EFI than the user paid in.
The function also opens with require(msg.sender == tx.origin, "only EOA"), a check meant to stop contracts, and with them flash loans, from calling it. The attacker's wallet satisfied it while running contract code anyway. Blockscout identifies 0x7abD...37f0 as an EIP-7702 account, a regular wallet delegated to contract code, so the transaction could take flash loans, swap and stake from the wallet's own address, with msg.sender and tx.origin both pointing at it.
Borrow, Pump, Stake 100 Times, Unwind
The attack transaction began with flash loans of DAI from five sources, including Balancer's Vault, BentoBox, a Uniswap V3 pool and an Algebra pool, for a total of about 1.35 million DAI. The wallet swapped 1,351,725.09 DAI into the EFI/DAI pair at 0xAec8...497B and received 12,713.17 EFI, which pushed the pair's DAI reserve up and its EFI reserve down and sent getPriceUSD() soaring.
With the price inflated, the wallet called stakeEFI() 100 times. Each call burned 7.135 EFI from the wallet and minted the same amount to the vault, recording a package at its full dollar value every time, so the wallet locked in 100 dollar-denominated stakes for a total of about 713.5 EFI.
It then sold 11,399.67 EFI back into the pair for 1,346,756.38 DAI, returning the reserves close to where they started, and repaid all five lenders inside the same transaction. The round trip cost it about 4,969 DAI plus the loan fees.
At 20:05:48 UTC, one minute later, the wallet called claim. With EFI back near its normal price, the vault converted the wallet's dollar balances into 6,256.53 EFI and sent them over. Between 20:10 and 20:13 UTC the wallet sold the EFI through a series of swaps, and at 20:13:51 UTC it sent 16,147.33 DAI into LI.FI's Across bridge route.
The Fix Went Live Overnight
At 02:45:35 UTC on May 20, about six and a half hours after the drain, the staking proxy was upgraded to a new implementation, 0x2dd1...de19. In that version getPriceUSD() returns a stored time-weighted price instead of reading the pair's reserves. stakeEFI() and the claim functions also gained a second line under the tx.origin check:
Both changes match the two things the attack depended on. As of November 6, 2026, ElevateFi hadn't published a post-mortem, and we found no statement from the team on recovery or reimbursement.
How Olympix Would Have Flagged the Price Before It Was Spent
Olympix reads a contract as states, transitions and entry points, and it uses signals to narrow every possible path down to the ones that touch an invariant worth breaking. In ElevateFi's vault the invariant is plain from the design: the EFI a user receives back should match the dollar value they actually staked, at prices the market really held, so that staking and claiming in quick succession can't turn a small amount of EFI into a large one.
Olympix's static analysis would have surfaced the signal that matters here. getPriceUSD() reads getReserves() from a single pair and returns the ratio directly, and that value flows into stakeEFI() and the claim path, where it sets how many tokens move. A spot reserve read feeding a mint or payout amount is one of the clearest price-manipulation signals there is, and the tx.origin check in front of it doesn't change that, because an EIP-7702 wallet passes it while executing arbitrary code.
BugPoCer would then have attacked the invariant directly. Counterexample-Guided Invariant Refutation works outward from the whole system, the vault plus the pair it prices from, and searches for a sequence that ends with a user holding more value than they put in. The sequence it needed is short: buy EFI in the pair to raise the reserve ratio, stake a package at the inflated price, sell the EFI back, then claim at the restored price. BugPoCer would have returned that as a counterexample with its minimal conditions and a generated Forge test that replays it, which is the May 19 drain produced in development instead of on Polygon.
Whalebit's staking contract made the same choice of price source and lost about $824K in March, pricing CES deposits from an Algebra pool's spot price. The pool type and the token were different, and the path from a movable price to a payout was the same.
Two Assumptions That Needed Testing
ElevateFi's vault rested on two assumptions: that the pool's current price was a fair price, and that an EOA check kept flash-loaned capital out. A flash loan and an EIP-7702 wallet broke both inside one transaction, and the team's overnight fix replaced both. Running that test in development, with a tool that reads both the vault and the pool it prices from as one system, is how a counterexample like this one turns into a code change before release. To see how Olympix traces price inputs through your own contracts and returns a runnable counterexample for each path that breaks, book a technical walkthrough.
On-Chain References
Protocol: ElevateFi EFI staking vault
Chain: Polygon
Date: May 19, 2026, 20:04:48 UTC
Loss: 6,256.53 EFI paid out by the vault, about $16K per Defimon
Attacker wallet (EIP-7702): 0x7abD...37f0
Staking vault proxy: 0x816E...Ce74
Implementation at the time: 0xcddc...3174
Patched implementation: 0x2dd1...de19
EFI/DAI pair: 0xAec8...497B
EFI token: 0xae84...8169
Stake tx: 0x2bd7...d87e
Claim tx: 0x3b36...8f3c
Bridge-out tx: 0xaa38...2fc3
Block: 87132217
More post-mortems
See every post-mortem →See what Olympix finds on your own code
Send us a repo that's already been audited, and we'll run Olympix at the same commit and show you what comes back.




