Skip to content

Latest commit

 

History

History
70 lines (44 loc) · 5.12 KB

062.md

File metadata and controls

70 lines (44 loc) · 5.12 KB

Muscular Alabaster Deer

High

Fee-on-Transfer Tokens May Cause Insufficient/Reduced Balances in ss.liquiditySafe, ss.stablecoinFeeSafe, and defi.defiSafe

Summary

The reliance on direct token transfers in _stablecoinSwap and _feeDispersal without validating the actual token balances will cause insufficient/reduced balances in ss.liquiditySafe, ss.stablecoinFeeSafe, and defi.defiSafe as fee-on-transfer tokens deduct a portion of the transferred amount, leading to future transaction failures or reverts when those balances are insufficient to make a transfer from ss.liquiditySafe to ss.destination.

Note: The contest details specify "user's might get less than expected", but NOT the protocol. Additionally, per the sponsor's confirmation, FOT are not in the contract prior. When the wallet does the defi swap some of the funds are sent to the user and the fee is sent to AmirX. Previously the Swapper sent this fund in but that's not what they want here; they should want to use the balance that is sent back from AmirX.

Root Cause

In StablecoinHandler.sol and AmirX.sol, the protocol does not account for fee-on-transfer behavior in the following scenarios:

  1. _stablecoinSwap: When transferring fee-on-transfer tokens to ss.liquiditySafe or from ss.liquiditySafe to ss.destination, the intermediary balance may not match the expected amount. The same shall apply when transferring from wallet to ss.stablecoinFeeSafe.
  2. _feeDispersal and _buyBack: Fee-on-transfer tokens converted to TELCOIN result in less TELCOIN being deposited into defi.defiSafe.

Internal pre-conditions

  1. Swapper Role initiates a swap where ss.origin or ss.target is a fee-on-transfer token.
  2. A portion of the token amount is deducted as a transfer fee.
  3. Swapper Role assumes the nominal ss.oAmount will fully transfer without validating post-transfer balances. This also incurs protocol loss for the reduced amount received by ss.stablecoinFeeSafe
  4. TELCOIN buybacks convert fee-on-transfer tokens with reduced amounts reaching defi.defiSafe.

External pre-conditions

  1. The protocol integrates with fee-on-transfer tokens for swaps or buybacks.
  2. Fee-on-transfer tokens are held by wallet and used as ss.origin, ss.target, or feeToken for buyback operations.

Attack Path

Scenario 1: Insufficient ss.liquiditySafe Balance

Transaction A: ss.origin is a fee-on-transfer token.

  • When transferring to ss.stablecoinFeeSafe and ss.liquiditySafe, the received amount is less than expected. Transaction B: ss.target is a fee-on-transfer token.
  • ss.liquiditySafe attempts to transfer ss.tAmount to ss.destination.
  • If Transaction A and B involve the same nominal amount, the reduced balance in ss.liquiditySafe will cause Transaction B to fail.

Scenario 2: Reduced defi.defiSafe Balance

  • The protocol collects fee-on-transfer tokens and converts them to TELCOIN during _buyBack.
  • Due to the transfer fee, less TELCOIN is deposited into defi.defiSafe.

Scenario 3: Reduced ss.stablecoinFeeSafe Balance

  • Fee-on-transfer tokens are transferred to ss.stablecoinFeeSafe.
  • The safe receives less than the expected fee amount, leading to discrepancies in protocol fee accounting.

Impact

  • The protocol suffers transaction reverts or operational failures due to insufficient balances in ss.liquiditySafe, and a direct loss in defi.defiSafe, and ss.stablecoinFeeSafe.
  • This disrupts core swap and fee-handling functionalities, potentially leading to loss of user trust and protocol downtime/losses.

PoC

No response

Mitigation

After transferring tokens, compare pre- and post-transfer balances to ensure the correct amounts are received. This should concern only the protocol per the readme detail. For instance, the following logic may be incorporated in _stablecoinSwap() when handling the transfer of the origin currency:

uint256 preBalance = IERC20(ss.origin).balanceOf(ss.liquiditySafe);
IERC20(ss.origin).safeTransferFrom(wallet, ss.liquiditySafe, ss.oAmount);
uint256 postBalance = IERC20(ss.origin).balanceOf(ss.liquiditySafe);
require(postBalance - preBalance >= ss.oAmount, "Insufficient tokens received due to fee-on-transfer.");