Bright Violet Crocodile
Medium
Fee on transfer tokens may change their fee at any time which will mess up completely the accounting of all functionality in AmirX
and StablecoinHandler.
In both AmirX
and StablecoinHandler
, fee on transfers are dealt with by simply changing the ratio from ss.oAmount
to ss.tAmount
, that is, instead of swapping for example 1000 tokens A for 1000 B, they know tokens A have a 1% fee on transfer so they swap 1010 tokens A for 1000 tokens B. However, the fee may change at any time and become 2% or 0.5%, which will mess up the accounting and make the protocol take a loss or the user.
None.
Fee on transfer tokens change their fee and frontrun stablecoin swap calls.
- Fee on transfer tokens change their fee.
- Any function that does a
_stablecoinSwap()
inAmirX
orStablecoinHandler
is called.
The simplest example is calling StablecoinHandler::stablecoinSwap()
with an amount of USDC of 1010.10 to receive 1000 eUSDC (assuming USDC has a 1% fee although it does not currently). If the fee changes to 2% frontrunning this call, the 1010.10 USDC will only send to the liquiditySafe
an amount of 1010.10 * 98 / 100 = 989.9
instead of 1000 USDC, taking the protocol the loss.
The protocol takes losses whenever a token changes the value of the fee on transfer or the user is penalized if the fee decreases instead.
function _stablecoinSwap(
address wallet,
StablecoinSwap memory ss
) internal {
if (
ss.stablecoinFeeCurrency != address(0) &&
ss.stablecoinFeeSafe != address(0)
)
ERC20PermitUpgradeable(ss.stablecoinFeeCurrency).safeTransferFrom(
wallet,
ss.stablecoinFeeSafe,
ss.feeAmount
);
// Handle the transfer or burning of the origin currency:
// If the origin is a recognized stablecoin (XYZ), burn the specified amount from the wallet.
if (isXYZ(ss.origin)) {
Stablecoin(ss.origin).burnFrom(wallet, ss.oAmount);
} else {
ERC20PermitUpgradeable(ss.origin).safeTransferFrom(
wallet,
ss.liquiditySafe,
ss.oAmount
);
}
// Handle the minting or transferring of the target currency:
// If the target is a recognized stablecoin (XYZ), mint the required amount to the destination address.
if (isXYZ(ss.target)) {
Stablecoin(ss.target).mintTo(ss.destination, ss.tAmount);
} else {
ERC20PermitUpgradeable(ss.target).safeTransferFrom( //@audit stuck if target becomes xyz
ss.liquiditySafe,
ss.destination,
ss.tAmount
);
}
}
Add checks to StablecoinHandler::_stablecoinSwap()
to ensure the fees applied in the transfers match the expected values used when calculating ss.oAmount
and ss.tAmount
.