Dizzy Brunette Mule
High
When the AmirX::swap
function is called, we enter the following block of code:
function swap(
address wallet,
bool directional,
StablecoinSwap memory ss,
DefiSwap memory defi
) external payable onlyRole(SWAPPER_ROLE) whenNotPaused {
// checks if it will fail
if (ss.destination != address(0)) _verifyStablecoinSwap(wallet, ss);
if (defi.walletData.length != 0) _verifyDefiSwap(wallet, defi);
if (directional) {
// if only defi swap
if (ss.destination == address(0)) _defiSwap(wallet, defi);
else {
// if defi then stablecoin swap
//check balance to adjust second swap
uint256 iBalance = ERC20(ss.origin).balanceOf(wallet);
if (defi.walletData.length != 0) _defiSwap(wallet, defi);
uint256 fBalance = ERC20(ss.origin).balanceOf(wallet);
//change balance to reflect change
if (fBalance - iBalance != 0) ss.oAmount = fBalance - iBalance;
_stablecoinSwap(wallet, ss);
}
...
Right here the system performs the swap for ss.origin
token, then changes the ss.oAmount
and calls _stablecoinSwap
.
Peeking into the _stablecoinSwap
function, we see the following transfer:
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
);
Now, Imagine the following scenario. The swap
function is called for the wallet, and defiSwap
to StablecoinSwap
is performed. at first the balance of user for origin token is 0 and after the swap it becomes 100, respectively ss.oAmount == 100
. Then the _stablecoinSwap
function is called, and it performs the transfer ss.stablecoinFeeCurrency
from wallet to ss.stablecoinFeeSafe
. If ss.feeAmount == 10
, users balance of origin token becomes 90 (Because origin token and ss.stablecoinFeeCurrency
are the same token). The problem comes right after that, in the following lines of code:
function _stablecoinSwap(
address wallet,
StablecoinSwap memory ss
) internal {
...
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(
ss.liquiditySafe,
ss.destination,
ss.tAmount
);
}
}
The system tries to burn/transfer tokens from the wallet, but this tokens may not be in the wallet as in the presented edge case (balance of wallet == 90; system wants to burn == 100). This can lead to DoS of the swap
and defiToStablecoinSwap
functions
Making some fee transfers before the before the main burning/transferring functionality
ss.origin
and ss.stablecoinFeeCurrency
to be the same token. ss.Amount + ss.feeAmount
to be higher than the user balance of origin token.
None
Described in the Summary
department
DoS of both swap
and defiToStablecoinSwap
functions for users with low starting ss.origin
token balance
No response
No response