Reproduced Exploit
Peapods: leave-as-paired fees revert debond and lock the bonded PEAS
LEAVE_AS_PAIRED_LP_TOKEN makes depositFromPairedLpToken deposit DAI instead of swapping it for the rewards token. _depositRewards then requires the token to be the rewards token whenever nobody has staked. debond calls that path once the pod has fees and the pool has liquidity, so the call reverts…
Loss
100 PEAS stuck in the pod
Chain
Ethereum
Category
dos
Date
Jan 2025
Source
AuditVault
EVM Playground
Source-level debugger — step opcodes and Solidity in sync
The attack is replayed in an in-browser EVM preloaded with the exact dumped fork state. The execution tree shows every call; step by Solidity line or by opcode across all depths — source, Stack, Memory, Storage, Balances (native / ERC-20 / NFT), Transient storage and Return value stay in sync. Click a tree node, opcode, or source line to jump. No backend, no live RPC.
Source & credit. Reproduction of a public audit finding curated by AuditVault — the original finding: 52748-h-3-pod-dos-if-the-leave-as-paired-lp-token-option-is-enable. Standalone Foundry PoC and full write-up: 52748-h-3-pod-dos-if-the-leave-as-paired-lp-token-option-is-enable_exp in the
evm-hack-registrymirror.
Vulnerability classes: vuln/dos/frozen-funds · vuln/logic/wrong-condition
Key info#
| Field | Value |
|---|---|
| Loss | 100 PEAS stuck in the pod |
| Vulnerable contract | TokenRewards._depositRewards |
| Attacker EOA | 0x5274800000000000000000000000000000000001 |
| Attack contract | Exploit |
| Attack tx | Exploit.run() |
| Chain / block / date | Ethereum model · block 0 · 2025-01 |
| Compiler | solc 0.8.27, cancun, via IR |
| Bug class | Paired-token deposit reverts with "R" when totalShares is 0 |
TL;DR#
LEAVE_AS_PAIRED_LP_TOKEN makes depositFromPairedLpToken deposit DAI instead of swapping it for the rewards token. _depositRewards then requires the token to be the rewards token whenever nobody has staked. debond calls that path once the pod has fees and the pool has liquidity, so the call reverts and the bonded PEAS never move. Burning the paired token when totalShares is 0 lets debond return the 100 PEAS.
Background#
Sherlock's January 2025 Peapods Finance contest, issue 175, reported by fibonacci and TessKimy. Staking and debonding both run the fee path, and a Uniswap V2 pool keeps a dust balance after the first mint, so the revert does not clear itself.
The vulnerable code#
if (LEAVE_AS_PAIRED_LP_TOKEN) {
_depositRewards(PAIRED_LP_TOKEN, _amountTkn);
return;
}
if (totalShares == 0) {
if (!burnWhenNoStakers) {
// @> VULN: paired-lp fees revert while nobody is staked, so debond cannot finish
require(_token == rewardsToken, "R");
}
}
Root cause#
The leave-as-paired branch deposits a token that is not rewardsToken, and the zero-staker branch only accepts rewardsToken.
Preconditions#
LEAVE_AS_PAIRED_LP_TOKENis true and the paired token is not the rewards token.totalSharesis 0.- The pod holds fee tokens and the pool's paired balance is nonzero.
Attack walkthrough#
- Bond 100 PEAS. The pod holds them.
- The pool receives 1 DAI and the pod receives 10 DAI of fees.
debondsees the option, the pool balance, and the fees, and callsdepositFromPairedLpToken._depositRewardshitsrequire(_token == rewardsToken)and reverts with "R".- The bond record and the 100 PEAS stay on the pod.
- The control burns the paired token instead of reverting.
debondreturns the 100 PEAS.
Diagrams#
Sources & further analysis#
Reproductions & code
- Standalone PoC + full trace: 52748-h-3-pod-dos-if-the-leave-as-paired-lp-token-option-is-enable_exp (evm-hack-registry mirror).
- AuditVault finding: 52748-h-3-pod-dos-if-the-leave-as-paired-lp-token-option-is-enable.
Alerts & third-party analyses
These dashboards index community alerts tweets, post-mortems, and independent write-ups. Reach them through the protocol name above to cross-check this reproduction against other analyses.