Omniscia Keyko Audit

MerkleDistributor Static Analysis Findings

MerkleDistributor Static Analysis Findings

MDR-01S: Improper Safe Invocation of ERC-20 Transfer

Description:

The EIP-20 standard dictates that callers of the transfer function should not assume that a bool is not returned, however, certain tokens are actually non-complient (i.e. USDT / Tether) and would cause the code segment to halt execution given that no bool is returned by that token.

Example:

contracts/airgrab/MerkleDistributor.sol
51function claim(
52 uint256 index,
53 address account,
54 uint256 amount,
55 bytes32[] calldata merkleProof
56) external override withinClaimPeriod {
57 require(!isClaimed(index), "MerkleDistributor: Drop already claimed.");
58
59 // Verify the merkle proof.
60 bytes32 node = keccak256(abi.encodePacked(index, account, amount));
61 require(
62 MerkleProof.verify(merkleProof, merkleRoot, node),
63 "MerkleDistributor: Invalid proof."
64 );
65
66 // Mark it claimed and send the token.
67 _setClaimed(index);
68 require(IERC20(token).transfer(account, amount), "MerkleDistributor: Transfer failed.");
69
70 emit Claimed(index, account, amount);
71}
72
73function withdrawUnclaimed() external override onlyOwner claimPeriodEnded {
74 uint256 unclaimedBalance = IERC20(token).balanceOf(address(this));
75 require(
76 IERC20(token).transfer(msg.sender, unclaimedBalance),
77 "MerkleDistributor: Withdrawal failed."
78 );
79 emit Withdrawn(msg.sender, unclaimedBalance);
80}

Recommendation:

As there are other token implementations that also deviate from the EIP-20 standard, we strongly recommend the usage of a safe wrapper library such as SafeERC20 from OpenZeppelin which opportunistically evaluates the returned bool variable only if it exists.

Alleviation:

The safeTransfer implementation is now properly utilised in all instances instead.