Gorgeous Maroon Stallion
High
AmirX.sol
's swap
function could cause totalSupply
to fall below getMinLimit
due to outdated checking
At the start of swap
in AmirX.sol
, the code calls if (ss.destination != address(0)) _verifyStablecoinSwap(wallet, ss);
Among the things checked, _verifyStablecoinSwap
will ensure that Stablecoin(ss.origin).totalSupply() - ss.oAmount >= getMinLimit(ss.origin)
.
However ss.oAmount
is the not real amount that is used for the swap as later on in the function, the variable is changed and passed in to _stablecoinSwap
which no longer checks if the new ss.oAmount
will break the limit.
For context, _verifyStablecoinSwap
checks that ss.oAmount
does not cause totalSupply()
to break below getMinLimit(ss.origin)
function _verifyStablecoinSwap(
address wallet,
StablecoinSwap memory ss
) internal view nonZero(ss) {
.....
if (isXYZ(ss.origin)) {
// Ensure the total supply does not drop below the minimum limit after burning the specified amount.
if (
--> Stablecoin(ss.origin).totalSupply() - ss.oAmount <
--> getMinLimit(ss.origin)
--> ) revert InvalidMintBurnBoundry(ss.origin, ss.oAmount);
} else if (ss.liquiditySafe == address(0)) {
.....
}
.....
}
Referencing the swap
function in AmirX.sol
function swap(
address wallet,
bool directional,
StablecoinSwap memory ss,
DefiSwap memory defi
) external payable onlyRole(SWAPPER_ROLE) whenNotPaused {
// checks if it will fail
1.)-> 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
2.)-> if (fBalance - iBalance != 0) ss.oAmount = fBalance - iBalance;
3.)-> _stablecoinSwap(wallet, ss);
}
} else {
// if stablecoin swap
_stablecoinSwap(wallet, ss);
// if only stablecoin swap
if (defi.walletData.length != 0) _defiSwap(wallet, defi);
}
}
-
As you can see, the limit is only checked in
_verifyStablecoinSwap
ofline 1.)
and thenss.oAmount
changes value inline 2.)
. -
Afterwards
line 3.)
straightaway calls the internal function_stablecoinSwap
. -
The internal function
_stablecoinSwap
is different from the external functionstablecoinSwap
. Namely, the internal function does not check the limit.
Hence the outdated limit check in line 1.)
before the value changes allows totalSupply
to break below the limit against intended behaviour.
No response
No response
No response
Inside the swap
, totalSupply
to breaks below the getMinLimit
No response
Add an updated check after the value changes
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;
+ if (fBalance - iBalance != 0) {
+ ss.oAmount = fBalance - iBalance;
+ _verifyStablecoinSwap(wallet, ss);
+ }
_stablecoinSwap(wallet, ss);
}
} else {
.....
.....
}
}