F-2025-0002·fee-on-transfer-token
Fee-on-Transfer Tokens Lead to Locked Funds
TL;DR
The contract assumes the amount transferred equals the amount specified, but fee-on-transfer tokens (like SafeMoon) deduct a fee per transfer, leaving the contract with less than recorded in payment.amount and causing settlement failures.
Severity
MEDIUM
Impact
MEDIUM
Likelihood
MEDIUM
Method
MManual review
CAT.
Complexity
LOW
Exploitability
MEDIUM
02Section · Description
Description
The contract assumes that the amount of tokens transferred is equal to the amount specified in the transaction. However, fee-on-transfer tokens (like SafeMoon) deduct a fee from each transfer, resulting in the contract receiving less tokens than expected.
solidity
function _createERC20Payment(address to,address tokenAddress,uint256 amount,uint256 timelockPeriod) internal {// ... checks ...(uint256 feeAmount, uint256 paymentAmount) = _processFee(amount);// Records full amount before transferpayments[idCounter].amount = paymentAmount;// Actual received amount may be less due to transfer feeSafeERC20.safeTransferFrom(token, msg.sender, address(this), amount);// Fee calculation based on pre-fee amountSafeERC20.safeTransfer(token, feeRecipient, feeAmount);}
03Section · Impact
Impact
- Contract receives less tokens than recorded in
payment.amount. - Settlement will fail due to insufficient balance.
- Protocol fees are calculated on pre-fee amount, leading to incorrect fee collection.
- Payments become unserviceable.
- Users lose funds due to failed transactions.
04Section · Recommendation
Recommendation
Check actual received amount:
solidity
function _createERC20Payment(...) internal {uint256 balanceBefore = token.balanceOf(address(this));SafeERC20.safeTransferFrom(token, msg.sender, address(this), amount);uint256 actualReceived = token.balanceOf(address(this)) - balanceBefore;require(actualReceived >= amount - maxSlippage, "Fee-on-transfer not supported");}

