Reproduced Exploit
Roots — surplus collateral claim reverts and 6 stays in the staker
A partial liquidation records surplusBalances[borrower] = 6 but the collateral itself is staked. claimCollateral zeroes the record and then transfers from the trove manager. The trove manager's balance is 0, so the transfer reverts and the whole claim rolls back. The 6 stays in the staker and the b…
Chain
Other
Category
logic
Date
Feb 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: 55114-h-04-borrowers-fail-to-claim-their-surplus-collateral-pashov. Standalone Foundry PoC and full write-up: 55114-h-04-borrowers-fail-to-claim-their-surplus-collateral-pashov_exp in the
evm-hack-registrymirror.
Vulnerability classes: vuln/logic/liquidation-logic · vuln/dos/frozen-funds
Key info#
| Protocol | Roots TroveManager |
| Finding | Pashov H-04, AuditVault 55114 |
| Vulnerable code | claimCollateral transfers the collateral token from the trove manager |
| Harm | 6 surplus collateral stays in the staker. The borrower receives 0. |
| Chain | local synthetic |
TL;DR#
A partial liquidation records surplusBalances[borrower] = 6 but the collateral itself is staked. claimCollateral zeroes the record and then transfers from the trove manager. The trove manager's balance is 0, so the transfer reverts and the whole claim rolls back. The 6 stays in the staker and the borrower receives nothing. Releasing the tokens with staker.onWithdrawal pays the borrower 6.
The vulnerable code#
function claimCollateral(address receiver) external {
uint256 claimableColl = surplusBalances[msg.sender];
require(claimableColl > 0, "No collateral available to claim");
surplusBalances[msg.sender] = 0;
// @> VULN: the collateral is staked, so this transfer pulls from the TroveManager and reverts.
collateralToken.transfer(receiver, claimableColl);
}
Root cause#
The surplus number is stored on the trove manager. The tokens are not. They were deposited into the staker. A plain ERC-20 transfer uses the caller's balance, which is the trove manager's, and that balance is 0.
Preconditions#
The borrower has a recorded surplus of 6. The staker holds those 6 tokens. The trove manager holds 0.
Attack walkthrough#
- 6 collateral is minted to the staker.
addCollateralSurplusrecords 6 for the borrower. - The borrower calls
claimCollateral. transferfrom the trove manager reverts. The surplus record rolls back.- The staker still holds 6. The borrower holds 0.
The control calls staker.onWithdrawal, which transfers the 6 from the staker to the borrower.
Diagrams#
Impact#
The liquidated borrower cannot collect surplus collateral. 6 tokens remain in the staker.
Remediation#
Withdraw the surplus from the staker, then send it to the borrower. Do not transfer it from the trove manager.
Sources#
- AuditVault finding: https://github.com/Auditware/AuditVault/blob/main/findings/55114-h-04-borrowers-fail-to-claim-their-surplus-collateral-pashov.md
- Pashov report (Roots, 2025-02-09): https://github.com/pashov/audits/blob/master/team/md/Roots-security-review_2025-02-09.md
- Blamed function: TroveManager
claimCollateral, whichtransfers collateral the finding says is held by the Staker.
Sources & further analysis#
Reproductions & code
- Standalone PoC + full trace: 55114-h-04-borrowers-fail-to-claim-their-surplus-collateral-pashov_exp (evm-hack-registry mirror).
- AuditVault finding: 55114-h-04-borrowers-fail-to-claim-their-surplus-collateral-pashov.
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.