TSender

Cyfrin
DeFiFoundry
15,000 USDC
View results
Submission Details
Severity: medium
Invalid

[M-01] Improper Order of Total Amount Check in `airdropERC20` Function of `TSender` Contract

Summary

The contract TSender has a function called airdropERC20() which is responsible for airdropping ERC20 tokens. The issue arises from the sequence in which the total amount of tokens is verified against the sum of individual airdrop amounts. This discrepancy can lead to failed or partial token transfers, causing inefficiencies in the airdrop process.

Vulnerability Details

The airdropERC20() function performs the check if iszero(eq(addedAmount, totalAmount)) after initiating the token transfers to the recipients. This sequence allows the totalAmount specified to be different from the actualTotal calculated from the amounts array, potentially leading to inconsistencies and incorrect behavior in the airdrop process. For example, if the user specifies totalAmount as 10 tokens but the sum of the amounts array (actualTotal) is 20 tokens, the contract will attempt to transfer the specified amounts. This results in the contract running out of tokens mid-way through the process, causing partial transfers and transaction failure, thereby wasting gas and causing inefficiencies.

Impact

If the totalAmountprovided is less than the sum of the amounts array, the function may proceed with the incorrect assumption, resulting in:

  • Failed Transfers: If the contract runs out of tokens mid-way through the airdrop.

  • Partial Airdrops: Where some recipients receive their tokens while others do not, leading to an incomplete and inefficient distribution.

  • Wasted Gas Costs: And potential loss of funds for users initiating the airdrop due to the transaction reverting late in the process.

These issues can significantly disrupt the intended airdrop operations and cause operational inefficiencies and dissatisfaction among users.

Tools Used

  • Manual Review

Recommendations

  • Pre-transfer Validation: Calculate the actualTotal from the amounts array and verify it against totalAmount before initiating any token transfers. This pre-transfer validation ensures that the total amount specified matches the sum of the individual amounts to be airdropped.

  • Code Adjustment: Move the check if iszero(eq(addedAmount, totalAmount)) to immediately after calculating actualTotal and before any token transfers.

Revised implementation:

function airdropERC20(
address tokenAddress,
address[] calldata recipients,
uint256[] calldata amounts,
uint256 totalAmount
) external {
assembly {
// Check for equal lengths
if iszero(eq(recipients.length, amounts.length)) {
mstore(0x00, 0x50a302d6) // cast sig TSender__LengthsDontMatch()
revert(0x1c, 0x04)
}
// Initialize total amount check
let addedAmount := 0
// Pre-transfer validation: Calculate the actual total amount
let end := add(recipients.offset, shl(5, recipients.length))
let diff := sub(recipients.offset, amounts.offset)
for { let addressOffset := recipients.offset } lt(addressOffset, end) { addressOffset := add(addressOffset, 0x20) } {
let recipient := calldataload(addressOffset)
// Check for zero address
if iszero(recipient) {
mstore(0x00, 0x1647bca2) // cast sig "TSender__ZeroAddress()"
revert(0x1c, 0x04)
}
// Calculate total amount
addedAmount := add(addedAmount, calldataload(sub(addressOffset, diff)))
}
// Check if the totals match
if iszero(eq(addedAmount, totalAmount)) {
mstore(0x00, 0x63b62563) // cast sig TSender__TotalDoesntAddUp()
revert(0x1c, 0x04)
}
// Perform transferFrom to the contract
mstore(0x00, hex"23b872dd") // transferFrom(address,address,uint256) function selector
mstore(0x04, caller())
mstore(0x24, address())
mstore(0x44, totalAmount)
if iszero(call(gas(), tokenAddress, 0, 0x00, 0x64, 0, 0)) {
mstore(0x00, 0xfa10ea06) // cast sig "TSender__TransferFailed()"
revert(0x1c, 0x04)
}
// Perform transfers to recipients
mstore(0x00, hex"a9059cbb") // transfer(address,uint256) function selector
for { let addressOffset := recipients.offset } lt(addressOffset, end) { addressOffset := add(addressOffset, 0x20) } {
let recipient := calldataload(addressOffset)
// to address
mstore(0x04, recipient)
// amount
mstore(0x24, calldataload(sub(addressOffset, diff)))
// Transfer the tokens
if iszero(call(gas(), tokenAddress, 0, 0x00, 0x44, 0, 0)) {
mstore(0x00, 0xfa10ea06) // cast sig "TSender__TransferFailed()"
revert(0x1c, 0x04)
}
}
}
}
Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement
maanvad3r Submitter
about 1 year ago
inallhonesty Lead Judge
about 1 year ago
inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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