Reproduced Exploit
DIA — a 100 percent principal share cannot unstake, because WDIA rejects a zero transfer
A stake with principalWalletShareBps = 10000 assigns every reward to the principal wallet. Unstaking 1,100 (1,000 principal plus 100 reward) therefore sets requestedUnstakeRewardAmount to 0.
Chain
Other
Category
dos
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: 57917-wdia-prohibits-zero-transfers-which-will-block-rewards-distr. Standalone Foundry PoC and full write-up: 57917-wdia-prohibits-zero-transfers-which-will-block-rewards-distr_exp in the
evm-hack-registrymirror.
Vulnerability classes: vuln/dos/frozen-funds · vuln/input-validation/boundary
Reproduction: a self-contained Foundry PoC that compiles and runs offline with only
forge-std— no fork and no RPC. Trace: output.txt. PoC: test/57917-wdia-prohibits-zero-transfers-which-will-block-rewards-distr_exp.sol.
Key info#
| Impact | HIGH — unstake reverts and 1,100 WDIA stays in the pool |
| Protocol | DIA WDIA and DIAExternalStaking |
| Vulnerable code | if (wad == 0) revert ZeroTransferAmount() on the beneficiary transfer |
| Bug class | A zero beneficiary amount reverts the whole unstake |
| Finding | MixBytes — DIA Lumina Staking, June 2025 · id 57917 |
| Report | Lumina Staking |
| Source | AuditVault finding |
| Compiler | solc 0.8.27, cancun, via IR |
TL;DR#
A stake with principalWalletShareBps = 10000 assigns every reward to the principal wallet. Unstaking 1,100 (1,000 principal plus 100 reward) therefore sets requestedUnstakeRewardAmount to 0.
unstake still calls WDIA.transfer for that 0. WDIA reverts with ZeroTransferAmount. The whole call rolls back. The request flag stays set, so the user cannot request again, and the next unstake hits the same zero transfer. 1,100 WDIA remains in the pool.
unstakingDuration is 0, so the request and the unstake are the same block. run() does not warp.
The vulnerable code#
function transferFrom(address src, address dst, uint256 wad) public returns (bool) {
// @> VULN: if (wad == 0) revert ZeroTransferAmount()
if (wad == 0) revert ZeroTransferAmount();
...
}
STAKING_TOKEN.transfer(currentStore.beneficiary, currentStore.requestedUnstakeRewardAmount);
The principal-wallet reward transfer is guarded with > 0. The beneficiary transfer is not. At a 100 percent principal share that amount is 0.
Root cause#
A 100 percent principal share is a configured, valid split. It produces a zero transfer, and the token treats zero as an error instead of a no-op. The staking contract does not skip it.
Preconditions#
principalWalletShareBpsis 10,000.- The position has a non-zero reward, here 100 WDIA on 1,000 of principal, so the unstake amount is 1,100 and the beneficiary slice is 0.
- WDIA reverts on
wad == 0.
Attack walkthrough#
- Stake 1,000 at 10,000 bps and add 100 of rewards.
- Request unstake of 1,100. The beneficiary amount reads back as 0.
unstaketransfers the principal amounts and then transfers 0 to the beneficiary.- WDIA reverts. The call returns false.
- The pool still holds 1,100. The user holds 0.
Control: skip any transfer whose amount is 0. The user receives the full 1,100 and the pool is empty.
Diagrams#
Impact#
The staker cannot finish the unstake. 1,100 WDIA is stuck: 1,000 of their principal and 100 of rewards. A second request is rejected because the first request was stored, and repeating unstake hits the same revert.
Remediation#
Do not call transfer when requestedUnstakeRewardAmount or the principal amount is 0. The token may also accept a zero wad.
Sources#
Taxonomy#
vuln/dos/frozen-funds·vuln/input-validation/boundary
Sources & further analysis#
Reproductions & code
- Standalone PoC + full trace: 57917-wdia-prohibits-zero-transfers-which-will-block-rewards-distr_exp (evm-hack-registry mirror).
- AuditVault finding: 57917-wdia-prohibits-zero-transfers-which-will-block-rewards-distr.
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.