The contract receives airdrop tokens (the owner funds it by transferring the ERC20 in), and distributes them via claim. The only withdrawal function is claimFees, which moves the contract's ETH balance to the owner:
There is no function anywhere that transfers i_airdropToken out of the contract except via a valid claim. If some recipients never claim (very common for airdrops), their portion of the ERC20 remains in the contract with no recovery path - the owner cannot sweep it, and no one else can move it. The unclaimed tokens are permanently locked.
Likelihood: Medium - airdrops routinely have unclaimed allocations (lost keys, inactive users), so leftover tokens are the expected end state.
Impact: Medium - the unclaimed ERC20 is irrecoverable; protocol/owner cannot reclaim or redistribute it. No user funds already claimed are at risk, but the residual is a permanent loss.
After one legitimate claim, three recipients' worth of tokens remain. claimFees withdraws only ETH and leaves the tokens stuck, and no other recovery function exists. Runnable Foundry test (add to MerkleAirdropTest.t.sol):
Run forge test --mt test_PoC_unclaimedTokensLocked -vv; it passes - claimFees does not recover the ERC20 and no other path exists.
Add an owner-only function to recover leftover airdrop tokens (e.g. after a deadline), so unclaimed allocations are not stranded:
(optionally gate this behind a claim-window expiry so it cannot be used to rug active claimants).
The contest is live. Earn rewards by submitting a finding.
Submissions are being reviewed by our AI judge. Results will be available in a few minutes.
View all submissionsThe contest is complete and the rewards are being distributed.