How a Recycled Fee-Accounting Bug Drained $20M From Popsicle Finance
Popsicle Finance, a yield-optimization protocol that automates liquidity management on Uniswap V3, lost roughly $20 million to an exploit that was mechanically simple but executed across multiple contracts and pools.
At the center of the protocol sits "Sorbetto Fragola," a contract designed to keep liquidity positions continuously rebalanced within the correct Uniswap V3 price range. The flaw exploited here wasn't in that rebalancing logic directly, but in how the protocol tracked accrued fees whenever its LP tokens changed hands: fee accounting simply wasn't updated properly on transfer.

That gap wasn't new. A near-identical RewardDistribution bug had already surfaced and been exploited in other protocols before this incident, which is part of why the aftermath has drawn as much scrutiny as the hack itself.
The mechanism
The attacker set up three separate contracts, referred to as A, B, and C, and used them to claim the same accrued fees more than once. The core loop was: A deposits into a pool and receives LP tokens, transfers those tokens to B, B calls collectFees() to register rewards against that position, transfers the tokens onward to C, and C calls collectFees() again on the same underlying stake. This loop was run across eight separate Popsicle LP pools.
Using the USDT-WETH pool to illustrate the flow:
- The attacker took out flash loans from Aave — 30 million USDT, 13,000 WETH, 1,400 BTC, 30 million USDC, 3 million DAI, and 200,000 UNI — to fund parallel attacks on eight PLP pools.
- Contract A called
deposit(), adding 30 million USDT and 5,467 WETH of liquidity to the USDT-WETH pool, and received 10.51 PLP tokens. - A transferred the 10.52 PLP tokens to B.
- B called
collectFees(), updating its recorded token rewards. - B transferred the 10.52 PLP tokens to C.
- C called
collectFees(), updating its own recorded token rewards on the same position. - C sent the 10.52 PLP tokens back to A, clearing the way for A to withdraw the underlying liquidity.
- A called
withdraw(), retrieving the original 30 million USDT and 5.46 WETH. - B called
collectFees()again and this time actually collected — 2.15 million USDT and 392 WETH in rewards. - C did the same, pulling out a further 2.15 million USDT and 402 WETH.
- The attacker ran this same sequence against the remaining seven pools, then repaid the Aave flash loan.
By passing one liquidity position through multiple wallets and firing off fee collection at each stop, the attacker converted a single deposit into several rounds of "rewards" that should only have paid out once.
Where the money went
About 4,100 ETH of the proceeds — roughly $10 million at the time — was moved into Tornado Cash almost immediately. As of the time of reporting, the balance of the haul was still sitting untouched in the attacker's wallet: 2,560 WETH, 96 WBTC, and 159,928 DAI.
- Attacker address: 0xf9E3D08196F76f5078882d98941b71C0884BEa52
- Transaction: 0xcd7dae143a4c0223349c16237ce4cd7696b1638d116a72755231ede872ab70fc

Technical breakdown credited to Peckshield and independent researcher Mudit Gupta. Popsicle Finance acknowledged the incident publicly.
The uncomfortable part
Peckshield, which had audited the affected contracts, published its own post-mortem of the exploit rather than waiting for Popsicle Finance to release an official account — an odd sequencing for an auditor reporting on a client's breach. With around $20 million in TVL gone, there's little defense for auditors missing a fee-accounting flaw that had already been documented as an exploited pattern elsewhere. The write-up was thorough, at least, giving Popsicle something tangible in return for what it paid for the audit.
Get new scam files the moment we publish them — usually 2–3 emails a week.