Reproduced Exploit
Reserve StRSR: a small RSR seizure can trigger a new era and wipe a still-valuable stake pool
Chain
Other
Category
logic
Date
Jan 1970
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: 27332-h-02-a-new-era-might-be-triggered-despite-a-significant-valu. Standalone Foundry PoC and full write-up: 27332-h-02-a-new-era-might-be-triggered-despite-a-significant-valu_exp in the
evm-hack-registrymirror.
Vulnerability classes: vuln/logic/wrong-condition · vuln/defi/locked-funds
Reproduction: the test deploys the REAL
StRSRP1Votesat the audited commit. A genuine third-party staker builds up a large stRSR position; a mere 10% follow-on RSR seizure pushes the stake rate just overMAX_STAKE_RATE, and the realbeginEra()branch zeroes the entire era - the staker loses everything.
Root cause#
In StRSRP1.seizeRSR an RSR seizure removes RSR from stakeRSR (the RSR backing the pool) but leaves totalStakes (the stRSR share supply) untouched, then recomputes the exchange rate:
stakeRSR -= stakeRSRToTake;
if (stakeRSR > 0) {
stakeRate = uint192((FIX_ONE_256 * totalStakes + (stakeRSR - 1)) / stakeRSR);
}
if (stakeRSR == 0 || stakeRate > MAX_STAKE_RATE) { // MAX_STAKE_RATE = 1e9 * 1e18
seizedRSR += stakeRSR;
beginEra(); // <-- wipes stakeRSR, totalStakes, era++
}
Because stakeRate == totalStakes * 1e18 / stakeRSR, each seizure raises the rate. The assumption behind resetting the era when stakeRate > MAX_STAKE_RATE is that "there is not much left after the seizure". That assumption is false: a prior seizure can leave the rate just below the threshold while the pool still holds a large, valuable position, so an arbitrarily small follow-on seizure crosses the cap and beginEra() zeroes every staker's balance. The fix (PR #888) adds a governance function to push the era forward deliberately instead of it happening implicitly.
Exploit walkthrough (real numbers)#
The real StRSRP1Votes is deployed behind an ERC1967 proxy; a minimal Main reports the RSR token, the backing manager (the only address permitted to seize), and unfrozen/unpaused state (Main is infrastructure, not part of the era-reset bug). RSR is a minimal real ERC20 (an opaque token to StRSR). The staker is a genuine separate contract.
- Victim stakes 1 RSR ->
totalStakes = 1e18stRSR,stakeRSR = 1e18,stakeRate = 1e18(rate 1.0). - The backing manager seizes
1e18 - 1_050_000_000RSR.stakeRSRfalls to ~1.05e9, drivingstakeRateto ~9.52e26- just underMAX_STAKE_RATE(1e27). No era reset. - As normal usage resumes the victim stakes 1 more RSR. At the elevated rate this mints ~
9.52e26stRSR: the victim now holds a large position (> 1e26 stRSR) and owns the whole era, and ~0.9 RSRof real value still backs the pool. - The backing manager seizes just 10% of the held RSR.
stakeRateticks to ~1.058e27>MAX_STAKE_RATE, so the realbeginEra()fires:totalStakes = 0,era++. - Harm: the victim's
> 1e26stRSR position is wiped to 0 and a new era begins, despite ~90% of the value still being present. The assertions checkcurrentEraincremented,totalSupply() == 0, andbalanceOf(victim) == 0.
Reproduction#
_shared/run-poc/run_poc.sh 27332-h-02-a-new-era-might-be-triggered-despite-a-significant-valu_exp -vvvvv
Expected result: 1 passed. See test/27332-h-02-a-new-era-might-be-triggered-despite-a-significant-valu_exp.sol. The real audited StRSR/StRSRVotes sources are vendored under src/reserve/target/ (byte-identical to the audited commit); only RSR (a plain ERC20) and the Main infrastructure - neither part of the era-reset bug - are minimal stand-ins in src/reserve/target/poc/PoCEnv.sol.
Sources#
- AuditVault finding #27332
- Reserve
StRSR.sol@c4ec2473 - Reserve mitigation PR #888
- Code4rena 2023-06 Reserve report
Sources & further analysis#
Reproductions & code
- Standalone PoC + full trace: 27332-h-02-a-new-era-might-be-triggered-despite-a-significant-valu_exp (evm-hack-registry mirror).
- AuditVault finding: 27332-h-02-a-new-era-might-be-triggered-despite-a-significant-valu.
- Upstream DeFiHackLabs PoC directory: src/test.
Alerts & third-party analyses
- DeFiHackLabs incident explorer: search "Reserve StRSR: a small RSR seizure can trigger a new era and wipe a still-valuable stake pool".
- Web3Sec X hacked database: search.
- Rekt leaderboard: search.
- Solodit incident search: search.
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.