Reproduced Exploit
Superfluid Locker — Staked tokens inside FluidLocker can be withdrawn without Unstake
1. Owner stakes all FLUID in the locker (_stakedBalance = balance). 2. provideLiquidity(supAmount) does not check available balance — staked tokens leave to Uniswap. 3. After tax-free withdrawLiquidity, FLUID is at the owner; _stakedBalance still full. 4. Staking reward units keep accruing on phant…
Chain
Other
Category
untagged
Date
Jun 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: 58281-h-1-staked-tokens-inside-fluidlocker-can-be-withdrawn-withou. Standalone Foundry PoC and full write-up: 58281-h-1-staked-tokens-inside-fluidlocker-can-be-withdrawn-withou_exp in the
evm-hack-registrymirror.
Vulnerability classes: cross-contract-state-consistency · variant · staking integrity
Reproduction: self-contained Foundry PoC with only
forge-std— no fork. output.txt · test/58281-…sol.
AuditVault taxonomy: lang/solidity · sector/dex · sector/staking · platform/sherlock · severity/high · genome: cross-contract-state-consistency · variant
Key info#
| Impact | HIGH — phantom stake accrues reward units after tokens left the locker |
| Protocol | Superfluid Locker System — FluidLocker.provideLiquidity |
| Vulnerable code | provideLiquidity never checks supAmount <= getAvailableBalance() |
| Bug class | Missing available-balance validation / double-use of staked funds |
| Finding | Sherlock 2025-06-superfluid-locker-system · H-1 · #58281 · newspacexyz et al. |
| Report | judging issue #177 |
| Source | AuditVault |
| Fix | superfluid-finance/fluid#26 — require available balance |
| Compiler | ^0.8.24 (PoC) |
| Repo | sherlock-audit/2025-06-superfluid-locker-system@d8beaeed |
TL;DR#
- Owner stakes all FLUID in the locker (
_stakedBalance = balance). provideLiquidity(supAmount)does not check available balance — staked tokens leave to Uniswap.- After tax-free
withdrawLiquidity, FLUID is at the owner;_stakedBalancestill full. - Staking reward units keep accruing on phantom stake;
getAvailableBalanceunderflows.
Vulnerable code#
function provideLiquidity(uint256 supAmount) external payable {
// missing: require(supAmount <= getAvailableBalance());
// ...
NONFUNGIBLE_POSITION_MANAGER.mint(..., supAmount, ...); // @> VULN
}
Root cause#
Stake accounting (_stakedBalance) is independent of the FLUID path used by provideLiquidity. Only stake/unstake touch the stake counter; LP paths move the actual tokens.
Preconditions#
- Locker holds FLUID and has a locker owner.
- Owner can stake and call
provideLiquidity/withdrawLiquidity.
Attack walkthrough#
- Fund locker with 100 FLUID; stake all 100.
provideLiquidity(100)— tokens leave despite being staked.- Tax-free withdraw (delay reduced to 0 in synthetic) returns FLUID to owner.
getStakedBalance() == 100while locker balance is 0.
Diagrams#
Impact#
Unbounded phantom stake corrupts the staker reward distribution; free points dominate future reward shares.
Sources#
Sources & further analysis#
Reproductions & code
- Standalone PoC + full trace: 58281-h-1-staked-tokens-inside-fluidlocker-can-be-withdrawn-withou_exp (evm-hack-registry mirror).
- AuditVault finding: 58281-h-1-staked-tokens-inside-fluidlocker-can-be-withdrawn-withou.
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.