Reproduced Exploit
DIA — paidOutReward makes the next unstake revert, and 10000 stays locked
A 10,000 stake that has already run 10 days at 100 bps has a reward of 1,000. The first unstake pays that 1,000, sets reward to 0, and leaves paidOutReward at 1,000.
Chain
Other
Category
arithmetic
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: 57913-paidoutreward-subtraction-may-lead-to-unstake-and-unstakepri. Standalone Foundry PoC and full write-up: 57913-paidoutreward-subtraction-may-lead-to-unstake-and-unstakepri_exp in the
evm-hack-registrymirror.
Vulnerability classes: vuln/arithmetic/underflow · vuln/dos/frozen-funds
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/57913-paidoutreward-subtraction-may-lead-to-unstake-and-unstakepri_exp.sol.
Key info#
| Impact | HIGH — the second unstake reverts and 10,000 WDIA of principal stays locked |
| Protocol | DIA DIAWhitelistedStaking |
| Vulnerable code | rewardToSend = currentStore.reward - currentStore.paidOutReward |
| Bug class | Reward is zeroed and paidOutReward is not |
| Finding | MixBytes — DIA Lumina Staking, June 2025 · id 57913 |
| Report | Lumina Staking |
| Source | AuditVault finding |
| Compiler | solc 0.8.27, cancun, via IR |
TL;DR#
A 10,000 stake that has already run 10 days at 100 bps has a reward of 1,000. The first unstake pays that 1,000, sets reward to 0, and leaves paidOutReward at 1,000.
The user requests unstake again in the same block. The new window is 0 days, so the next reward is 0. unstakePrincipal computes 0 - 1000 and Solidity 0.8 reverts. The 10,000 principal never moves.
The 10 days are stored timestamps, 1 and 1 + 10 days. run() does not warp.
The vulnerable code#
updateReward(stakingStoreIndex);
// @> VULN: rewardToSend = currentStore.reward - currentStore.paidOutReward underflows
uint256 rewardToSend = currentStore.reward - currentStore.paidOutReward;
The first unstake is the line the report describes: it zeros currentStore.reward after adding the payout to paidOutReward, and it does not clear paidOutReward.
Root cause#
paidOutReward is a lifetime accumulator. reward is reset to 0 on every claim. The next short window cannot catch up to the accumulator, so the subtraction underflows and both unstake and unstakePrincipal revert.
Preconditions#
- Rate is 100, meaning 1 percent per day.
- The stored interval is 10 full days, so the first reward is 1,000.
- The user claims once, then requests unstake again before another full day accrues.
Attack walkthrough#
- Put 11,000 WDIA on the staking contract and record a 10,000 stake.
- First
unstakepays 1,000, zerosreward, and leavespaidOutRewardat 1,000. requestUnstakesets the request time to this block. A fresh calculation is 0.unstakePrincipalreverts on0 - 1000.- The contract still holds 10,000 WDIA. The user holds only the first 1,000.
Control: the second call pays principal from getRewardForStakingStore and does not subtract the stale paidOutReward. The user receives the 10,000 and the contract balance is 0.
Diagrams#
Impact#
The staker cannot withdraw the remaining 10,000 WDIA. The report notes they also cannot wait for a larger reward: a second request is refused while the first request is still open, and fulfilling that request is what reverts.
Remediation#
Pay getRewardForStakingStore minus what was already paid only when the new total is greater. When reward is zeroed, do not keep a paidOutReward that the next zero reward cannot cover.
Sources#
- AuditVault finding 57913
- MixBytes DIA Lumina Staking, June 2025
- DIAWhitelistedStaking.sol at 50b70919
Taxonomy#
vuln/arithmetic/underflow·vuln/dos/frozen-funds
Sources & further analysis#
Reproductions & code
- Standalone PoC + full trace: 57913-paidoutreward-subtraction-may-lead-to-unstake-and-unstakepri_exp (evm-hack-registry mirror).
- AuditVault finding: 57913-paidoutreward-subtraction-may-lead-to-unstake-and-unstakepri.
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.