40,000 USDC
View results
Submission Details
Severity: medium
Valid

Contract deployment failed with Fee-On-Transfer tokens (like USDT)

Summary

The Escrow contract fails to deploy when Fee-On-Transfer tokens, such as USDT, are used. The failure is due to discrepancies in transferred amounts caused by the transfer fees associated with such tokens.

Vulnerability Details

When Fee-On-Transfer tokens are used, the actual balance of the contract at the time of creation is less than the expected amount due to fees deducted during the transfer. This discrepancy triggers a revert in the Escrow contract during deployment.

An example scenario is as follows:

  1. A buyer wants to purchase an audit service for 15k USDT.

  2. The buyer calculates the Escrow deployment address using computeEscrowAddress().

  3. The buyer approves the transfer of 15k USDT to the calculated address.

  4. The buyer calls newEscrow(), which successfully transfers tokens (minus a small fee) to the Escrow contract address.

  5. The Escrow contract's constructor triggers a revert because the balance is slightly smaller than the expected price.
    This revert is caused by the following line in the constructor:

if (tokenContract.balanceOf(address(this)) < price) revert Escrow__MustDeployWithTokenBalance();

Impact

The deployment failure prevents users from using the Escrow contract with Fee-On-Transfer tokens like USDT.

Tools Used

Manual review

Recommendations

  1. Modify the newEscrow() function in the EscrowFactory contract to deploy the Escrow contract without transferring any tokens. Introduce a new function in the Escrow contract that allows the buyer to deposit funds after the contract has been created. Once the funds are successfully deposited, set the contract status to "deposited".

  2. Update the constructor of the Escrow contract to remove the immediate balance check during deployment.

  3. Introduce a new status, "Awaiting Deposit", in the Escrow contract. This status should be assigned when the Escrow contract is first created. Once the required funds are received, change the status to "deposited". Ensure that the confirmReceipt() function can only be called when the contract is in the "deposited" status.

Support

FAQs

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