Reproduced Exploit
Roots: missing queueDropBoost strands staking rewards
Chain
Other
Category
untagged
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: Roots-security-review_2025-02-09. The historical source/toolchain is unavailable; this entry is documentation only and claims no executable Forge PoC.
Vulnerability classes: vuln/logic
Reproduction: a faithful minimal reproduction of the vulnerable finding — the vulnerable code is reproduced verbatim (marked
@>) with faithful minimal doubles; local deploy, no fork.
Root cause#
Staker calls rewardCache.dropBoost(validator, toFulfill) without a preceding queueDropBoost, so the BGT dropBoostQueue is empty and BGT.dropBoost returns false: the boost is never dropped and the toFulfill reward is never freed. A staker's 1000e18 of expected rewards stays permanently stuck in the reward cache (silent staking-reward loss).
// Determine how much boosted reward must be dropped to fund the redemption,
// then drop it (VERBATIM audited branch):
if (toFulfill > 0) {
rewardCache.dropBoost(validator, uint128(toFulfill)); // @> VULN (this line)
Why it's exploitable here#
Staker calls rewardCache.dropBoost(validator, toFulfill) without a preceding queueDropBoost, so the BGT dropBoostQueue is empty and BGT.dropBoost returns false: the boost is never dropped and the toFulfill reward is never freed. A staker's 1000e18 of expected rewards stays permanently stuck in the reward cache (silent staking-reward loss).
Attack path#
Marked-line walkthrough (Playground)#
The EVM Playground pins each step to the exact executed source line in 0xbd4fd5a3ce…:
- L247 — dropBoost called without queueing: Root cause: dropBoost runs with no preceding queueDropBoost, so the BGT dropBoostQueue is empty and the drop silently returns false.
- L261 — Same bug on validator switch: The validator-switch path repeats the mistake — it drops the old boost without queueing it first.
- L266 — Old boost drop attempted: When switching validators the contract tries to drop the previously-boosted amount.
- L270 — Old boost stays stuck too: That drop also silently fails, so the old validator's boost is never released either.
- L277 — Staker paid zero rewards: Because the boost never drops, the toFulfill reward is never freed and the staker receives 0.
- L280 — Loss quantified at the sink probe: The 1000e18 of owed-but-stuck reward is recorded at the harm-probe sink address.
- L284 — 1000e18 reward permanently locked: The staker's full 1000e18 boosted reward stays locked in the BGT cache — a silent staking-reward loss.
PoC#
Registry (Foundry, local deploy — verbatim vulnerable source + harm-asserting test):
cd 55113-h-03-incorrect-boost-management-leads-to-staking-reward-loss_exp
forge test -vvv
The browser Playground replays the same synthetic opcode-for-opcode and measures the harm. Both gates are green (registry forge test PASS + Playground _verify-poc VERDICT: PASS).
Sources & further analysis#
Reproductions & code
- No executable Forge reproduction is claimed; the historical source/toolchain was unavailable for this finding.
- AuditVault finding: Roots-security-review_2025-02-09.
- Upstream DeFiHackLabs PoC directory: src/test.
Alerts & third-party analyses
- DeFiHackLabs incident explorer: search "Roots: missing queueDropBoost strands staking rewards".
- 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.