Reproduced Exploit
Parallel 3.1 — processSurplus always reverts for managed collateral
1. Managed collateral lives on an external manager/strategy, never on the diamond. 2. getCollateralSurplus correctly uses manager.totalAssets(). 3. processSurplus self-swaps and transferFroms from the diamond. 4. Diamond balance is 0 → transfer reverts for any positive surplus.
Chain
Other
Category
untagged
Date
Mar 2026
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: 65528-surplusprocesssurplus-always-reverts-for-managed-collateral. Standalone Foundry PoC and full write-up: 65528-surplusprocesssurplus-always-reverts-for-managed-collateral_exp in the
evm-hack-registrymirror.
Vulnerability classes: frozen-funds · permanent · dos-resistance
Reproduction: self-contained Foundry PoC with only
forge-std. Full trace: output.txt. PoC: test/65528-surplusprocesssurplus-always-reverts-for-managed-collateral_exp.sol.
AuditVault taxonomy: lang/solidity · sector/governance · sector/lending · sector/stable · platform/cyfrin · has/github · has/poc · severity/high · impact/dos/permanent · genome: frozen-funds · permanent · dos-resistance
Key info#
| Impact | HIGH — permanent DoS of surplus capture for all managed collateral |
| Protocol | Parallel Parallelizer |
| Vulnerable code | Surplus.processSurplus → Swapper._swap transferFrom diamond |
| Bug class | Surplus measured on manager; pull from diamond (balance always 0) |
| Finding | Cyfrin Parallel 3.1 v2.0, 2026-03-04 · #65528 · 0xStalin |
| Report | Cyfrin report |
| Source | AuditVault |
| Status | Fixed in 2dfad62 |
| Compiler | ^0.8.24 (PoC) |
TL;DR#
- Managed collateral lives on an external manager/strategy, never on the diamond.
getCollateralSurpluscorrectly usesmanager.totalAssets().processSurplusself-swaps andtransferFroms from the diamond.- Diamond balance is 0 → transfer reverts for any positive surplus.
- HARM: strategy yield can never be captured as distributable tokenP.
The vulnerable code#
IERC20(tokenIn).safeTransferFrom(
msg.sender, // @> VULN: diamond has 0 managed collateral
LibManager.transferRecipient(...),
amountIn
);
Fix: LibManager.release surplus to the diamond before the self-swap.
Root cause#
Surplus accounting follows managed total assets, but the swap pull path assumes the diamond holds the tokens — true only for unmanaged collateral.
Preconditions#
- Collateral configured as managed (
isManaged). - Strategy has yield so surplus > 0.
Attack walkthrough#
- Mint tokenP with managed collateral (tokens go user → manager).
- Simulate strategy yield on the manager.
getCollateralSurplusreports positive surplus.processSurplusreverts ontransferFrom(diamond, manager, surplus).
Diagrams#
Impact#
Permanent DoS on surplus processing for managed assets — the primary surplus source (strategy yield) is uncapturable.
Sources#
Sources & further analysis#
Reproductions & code
- Standalone PoC + full trace: 65528-surplusprocesssurplus-always-reverts-for-managed-collateral_exp (evm-hack-registry mirror).
- AuditVault finding: 65528-surplusprocesssurplus-always-reverts-for-managed-collateral.
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.