TSender

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

Winner cannot receive any rewards if blacklisted from USDC

Summary

https://github.com/Cyfrin/2024-05-TSender/blob/c6da9ef0c28741c007a02dfa07b7e899c1c22e47/src/protocol/TSender.sol#L33-L107

When a competition is finished, the winner will receive their rewards through airdrop. This sends them all the tokens they have won. If the winner is blacklisted from USDC, the airdrop will fail and the winner will not be able to receive their rewards, because the transfer of USDC to them will fail.

Vulnerability Details

`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)
        }


        // transferFrom(address from, address to, uint256 amount)
        // cast sig "transferFrom(address,address,uint256)"
        // This will result in memory looking like this:
        // 0x00: 0x23b872dd00000000000000000000000000000000000000000000000000000000
        mstore(0x00, hex"23b872dd")
        // from address
        mstore(0x04, caller())
        // to address (this contract)
        mstore(0x24, address())
        // total amount
        mstore(0x44, totalAmount)


        if iszero(call(gas(), tokenAddress, 0, 0x00, 0x64, 0, 0)) {
            mstore(0x00, 0xfa10ea06) // cast sig "TSender__TransferFailed()"
            revert(0x1c, 0x04)
        }


        // transfer(address to, uint256 value)
        mstore(0x00, hex"a9059cbb")
        // end of array
        // recipients.offset actually points to the recipients.length offset, not the first address of the array offset
        let end := add(recipients.offset, shl(5, recipients.length))
        let diff := sub(recipients.offset, amounts.offset)


        // Checking totals at the end
        let addedAmount := 0
        for { let addressOffset := recipients.offset } 1 {} {
            let recipient := calldataload(addressOffset)


            // Check to address
            if iszero(recipient) {
                mstore(0x00, 0x1647bca2) // cast sig "TSender__ZeroAddress()"
                revert(0x1c, 0x04)
            }


            // to address
            mstore(0x04, recipient)
            // amount
            mstore(0x24, calldataload(sub(addressOffset, diff)))
            // Keep track of the total amount
            addedAmount := add(addedAmount, mload(0x24))


            // transfer the tokens
            if iszero(call(gas(), tokenAddress, 0, 0x00, 0x44, 0, 0)) {
                mstore(0x00, 0xfa10ea06) // cast sig "TSender__TransferFailed()"
                revert(0x1c, 0x04)
            }


            // increment the address offset
            addressOffset := add(addressOffset, 0x20)
            // if addressOffset >= end, break
            if iszero(lt(addressOffset, end)) { break }
        }


        // Check if the totals match
        if iszero(eq(addedAmount, totalAmount)) {
            mstore(0x00, 0x63b62563) // cast sig TSender__TotalDoesntAddUp()
            revert(0x1c, 0x04)
        }
    }
}

`

Impact

blacklisted winners will not receive their airdrop

Tools Used

Manual Review

Recommendations

Consider not including tokens with blacklists.
Consider adding a function that can allow a user to claim reward tokens one at a time.

Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Known issue

Support

FAQs

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