AirDropper

AI First Flight #5
Beginner FriendlyDeFiFoundry
EXP
View results
Submission Details
Impact: low
Likelihood: low
Invalid

Return value is not checked for `ERC20::transfer` function in `Deploy.s.sol::run()`

Impact: L
Likelihood: L

Root + Impact

Description

  • The return bool value of transfer() function is not checked and this may lead to wrong assumption that the protocol is funded when transfer() returns false.

  • The issue is in the code of Deploy.s.sol:

// Deploy the airdropper
function run() public {
vm.startBroadcast();
MerkleAirdrop airdrop = deployMerkleDropper(s_merkleRoot, IERC20(s_zkSyncUSDC));
// Send USDC -> Merkle Air Dropper
@> IERC20(0x1d17CBcF0D6D143135aE902365D2E5e2A16538D4).transfer(address(airdrop), s_amountToAirdrop);
vm.stopBroadcast();
}

Risk

Likelihood:

  • The issue occurs when ERC20::transfer() function returns false and does not revert.

Impact:

  • The MerkleAirdrop.sol contract may be falsly considered as funded when transfer returns false and this value is not checked.

Proof of Concept

Override transfer() function in AirdropToken.sol to make it return false:

contract AirdropToken is ERC20, Ownable {
constructor() ERC20("AirdropToken", "ADT") Ownable(msg.sender) { }
function mint(address account, uint256 amount) external onlyOwner {
_mint(account, amount);
}
function transfer(address to, uint256 value) public override pure returns (bool) {
return false;
}
}

Add the following test to MerkleAirdropTest.t.sol and run it locally using forge test --mt test_transferReturnsFalse command:

function test_transferReturnsFalse() public {
AirdropToken tokenWithModifiedTransfer = new AirdropToken();
MerkleAirdrop merkleAirdrop = new MerkleAirdrop(merkleRoot, tokenWithModifiedTransfer);
tokenWithModifiedTransfer.mint(address(this), amountToSend);
//fund the MerkleAirdrop.sol
bool transferred = tokenWithModifiedTransfer.transfer(address(merkleAirdrop), amountToSend);
assertEq(transferred, false);
uint256 merkleAirdropBalance = tokenWithModifiedTransfer.balanceOf(address(merkleAirdrop));
assertEq(merkleAirdropBalance, 0);
}

The contract deploy does not revert and the transfer just failed implicitly.

Recommended Mitigation

Use SafeERC20 from OpenZeppelin in Deploy.s.sol script:

+ import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
+ using SafeERC20 for IERC20;
function run() public {
vm.startBroadcast();
MerkleAirdrop airdrop = deployMerkleDropper(s_merkleRoot, IERC20(s_zkSyncUSDC));
// Send USDC -> Merkle Air Dropper
- IERC20(0x1d17CBcF0D6D143135aE902365D2E5e2A16538D4).transfer(address(airdrop), s_amountToAirdrop);
+ IERC20(0x1d17CBcF0D6D143135aE902365D2E5e2A16538D4).safeTransfer(address(airdrop), s_amountToAirdrop);
vm.stopBroadcast();
}
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge 1 day ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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

Give us feedback!