TSender

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

Some tokens can be stuck in the contract if the actual amount sent to recipients is less than total amount in the TSender_NoCheck.huff contract

Summary

The vulnerability in the TSender_NoCheck.huff contract arises from the possibility of excess tokens remaining stuck in the contract due to a mismatch between the total allocated amount and the actual sum distributed.

Vulnerability Details

The primary vulnerability lies in the AIRDROP_ERC20 macro in the TSender_NoCheck.huff contract, where there is no check to ensure that the total amount specified for airdrop matches the sum of the amounts sent to individual recipients. Consequently, any surplus tokens remain trapped within the contract without a designated recipient, unable to be retrieved or utilized.

Example Scenario
totalAmount specified is 1000 tokens.
The amounts array sums up to 600 tokens.
400 tokens will remain in the contract and are effectively locked up.

To get the deployBytecode of the TSender_NoCheck.huff contract, run:

huffc src/protocol/TSender_NoCheck.huff -b
function testAirDropLessAmountsThanTotalAmount() public {
TSender huffTSenderNoCheck;
bytes memory deployBytecode_ =
hex"60968060093d393df35f3560e01c6382947abe14610012575f5ffd5b6323b872dd5f5233602052306040526064356060525f5f6064601c5f6004355af16100445763fa10ea065f526004601cfd5b63a9059cbb5f5260043560443560040160840360843560051b60a40160a45b8281803560205203356040525f5f6044601c5f885af161008a5763fa10ea065f526004601cfd5b6020018181106100635700";
address tSenderHuffAddr_;
assembly {
tSenderHuffAddr_ := create(0, add(deployBytecode_, 0x20), mload(deployBytecode_))
}
huffTSenderNoCheck = TSender(tSenderHuffAddr_);
mockERC20 = new MockERC20();
address sender = makeAddr("sender");
uint256 totalAmount = 1000e18;
uint256 amountToSend = 200e18;
uint256 numberOfRecipients = 3;
uint256 amountLeft = totalAmount - (amountToSend * numberOfRecipients); // 400e18
address[] memory recipients = new address[](numberOfRecipients);
recipients[0] = makeAddr("recipient1");
recipients[1] = makeAddr("recipient2");
recipients[2] = makeAddr("recipient3");
uint256 amount = totalAmount;
uint256[] memory amounts = new uint256[](numberOfRecipients);
amounts[0] = amountToSend;
amounts[1] = amountToSend;
amounts[2] = amountToSend;
vm.startPrank(sender);
mockERC20.mint(totalAmount);
mockERC20.approve(address(huffTSenderNoCheck), totalAmount);
vm.stopPrank();
bytes4 selector = TSender.airdropERC20.selector;
bytes memory data = abi.encodeWithSelector(selector, address(mockERC20), recipients, amounts, totalAmount);
// Act
vm.startPrank(sender);
(bool succYul,) = address(huffTSenderNoCheck).call(data);
vm.stopPrank();
//the amount of token remaining in the contract: // 400e18
assert(mockERC20.balanceOf(tSenderHuffAddr_) == amountLeft);
}

Impact

Financial Loss: The sender might lose tokens as they become irretrievable once stuck in the contract.

Tools Used

Foundry

Recommendations

To mitigate this issue, a solution would involve refunding any surplus tokens back to the sender. This ensures that any excess tokens, which are not allocated to recipients, are promptly returned to the original sender, preventing them from becoming stranded within the contract.

Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

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