Reproduced Exploit

Devve — airdrop withdrawal counts minutes as days

tokensToWithdraw is supposed to be airdropTokensPerDay times the number of days since the last withdrawal. The code divides the elapsed seconds by 60. Two days is 172800 seconds, 2880 minutes, and 2 days. The caller receives 2880 tokens. Dividing by one day pays 2 and leaves 2878 in the contract.

Jun 2024•Other•logic•3 min read

Chain

Other

Category

logic

Date

Jun 2024

Source

AuditVault

EVM Playground

Source-level debugger — step opcodes and Solidity in sync

evm-hack-analyzer

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.

Loading fork state…

Source & credit. Reproduction of a public audit finding curated by AuditVault — the original finding: 37617-calculation-of-tokenstowithdraw-is-wrong-zokyo-none-devve-ma. Standalone Foundry PoC and full write-up: 37617-calculation-of-tokenstowithdraw-is-wrong-zokyo-none-devve-ma_exp in the evm-hack-registry mirror.


Vulnerability classes: vuln/logic/reward-calculation · vuln/arithmetic/precision-loss

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/37617-calculation-of-tokenstowithdraw-is-wrong-zokyo-none-devve-ma_exp.sol.


Key info#

ImpactHIGH — two days of vesting pays 2880 tokens instead of 2. The extra is 2878
ProtocolDevve withdrawAirdropTokens / getCurrentAirdropBalance
Vulnerable codeairdropTokensPerDay * (elapsed / 60)
Bug classMinutes used where the formula says days
FindingZokyo — Devve · id 37617
ReportZokyo Devve, 30 Jun 2024
SourceAuditVault finding
StatusThe report says the issue was resolved. Reproduced as a local synthetic.
Compilersolc 0.8.27, cancun, via IR

TL;DR#

tokensToWithdraw is supposed to be airdropTokensPerDay times the number of days since the last withdrawal. The code divides the elapsed seconds by 60. Two days is 172800 seconds, 2880 minutes, and 2 days. The caller receives 2880 tokens. Dividing by one day pays 2 and leaves 2878 in the contract.

run() does not warp. The block timestamp is already two days after lastWithdraw, which was stored when the contract was constructed. The same clock is used for the preview and the withdrawal.

The vulnerable code#

SOLIDITY
uint256 elapsed = block.timestamp - lastWithdraw[msg.sender];
// @> VULN: tokensToWithdraw multiplies airdropTokensPerDay by minutes, not days
tokensToWithdraw = airdropTokensPerDay * (elapsed / 60);

getCurrentAirdropBalance uses the same division.

Root cause#

The report says both functions multiply airdropTokenPerDay by minutes passed, not by days passed.

Preconditions#

  • airdropTokensPerDay is 1 token.
  • lastWithdraw is 172800 seconds before the block timestamp.
  • The contract holds at least 2880 tokens.

Attack walkthrough#

  1. getCurrentAirdropBalance returns 2880 tokens.
  2. withdrawAirdropTokens computes the same number and transfers it.
  3. The contract balance is 0. The fair amount is 2.

Diagrams#

flowchart TD A["Two days have elapsed"] --> B["elapsed divided by 60 is 2880"] B --> C["tokensToWithdraw is 2880"] C --> D["2880 tokens transfer to the caller"] D --> E["Fair payout of 2 would have left 2878"]

Impact#

The caller takes 2878 tokens that the daily rate did not vest. Every other claimant is paid from a balance that is already short.

Remediation#

Divide elapsed time by one day before multiplying by airdropTokensPerDay, in both the withdrawal and the view.

How to reproduce#

BASH
cd evm-hack-registry/37617-calculation-of-tokenstowithdraw-is-wrong-zokyo-none-devve-ma_exp
forge test -vv

The test warps once, before either contract is constructed. run() itself does not warp.

Sources#

  • AuditVault finding 37617
  • Zokyo, Devve, 30 June 2024

Sources & further analysis#

Reproductions & code

Alerts & third-party analyses

  • Web3Sec X hacked database: search.
  • Rekt leaderboard: search.
  • Solodit incident search: search.

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.