Acrobatic Violet Poodle
Medium
In Zapper._swapV2()
, whenever _twoHops
is == true Attackers can use bots to sandwich first swap in networks where sandwich attacks are possible. This will cause losses for users as amountOutMin is hardcoded as 0 whenever _twoHops
is == true.
bool _twoHops = _path.length == 3;
if (maxSwap[_path[0]] > 0 && _amountIn > maxSwap[_path[0]]) {
_amountOutMin = (_amountOutMin * maxSwap[_path[0]]) / _amountIn;
_amountIn = maxSwap[_path[0]];
}
IERC20(_path[0]).safeIncreaseAllowance(address(DEX_ADAPTER), _amountIn);
_amountOut =
DEX_ADAPTER.swapV2Single(_path[0], _path[1], _amountIn, _twoHops ? 0 : _amountOutMin, address(this));//@audit-issue frontrunning and backrunning to profit.
In Zapper._swapV2()
, whenever _twoHops
is == true Attackers can use bots to sandwich first swap in networks where sandwich attacks are possible. This will cause losses for users as amountOutMin
is hardcoded as 0 whenever _twoHops
is == true.
According to what protocol states in the contest readme,
Please discuss any design choices you made.
The main consideration for a design choice we made is in a few places we implement unlimited (100%) slippage for dex swaps. Our expectation is wherever we implement this behavior that almost any swap from token0 to token1 will be of small enough value that it would rarely, if ever, be profitable to sandwich for profit by a bot.
They hardcoded amountOutMin
as 0 in places where they thought that the amount being swapped is too small to be profitable for sandwich attacks.
But considering that Zapper._swapV2()
is used by IndexUtils.addLPAndStake()
it is very unlikely that the amount being swapped is going to be small to the extent where the sandwich is unprofitable for the bot.
Attackers can use bots to sniff out and sandwich the first swap that will have amountOutMin
as 0 whenever _twoHops
is true.
The tx flow is IndexUtils.addLPAndStake() -> Zapper.zap() -> Zapper._swapV2()
AmountOutMIn
is 0 during swap
- _twoHops is true.
- amount enough to make the sandwich a profitable one is being swapped.
- The tx is happening in a chain where sandwich attacks are possible.
-
IndexUtils.addLPAndStake()
is called. -
in the process of
IndexUtils.addLPAndStake()
,Zapper._swapV2()
is called -
Here in this line
DEX_ADAPTER.swapV2Single(_path[0], _path[1], _amountIn, _twoHops ? 0 : _amountOutMin, address(this));
the swap occurs with amountOutMin as 0 and attacker's bot sniffs it out and frontruns and backruns it profitting from the sandwich attack.
Medium Severity.
Attackers can sandwich first swap whenever _twoHops
is == true, this will cause loses to users whenever they use IndexUtils.addLPAndStake()
function _swapV2(address[] memory _path, uint256 _amountIn, uint256 _amountOutMin) internal returns (uint256) {
bool _twoHops = _path.length == 3;
address _out = _twoHops ? _path[2] : _path[1];
uint256 _outBefore = IERC20(_out).balanceOf(address(this));
IERC20(_path[0]).safeIncreaseAllowance(address(DEX_ADAPTER), _amountIn);
DEX_ADAPTER.swapV2Single(_path[0], _path[1], _amountIn, _twoHops ? 0 : _amountOutMin, address(this));//@audit-issue frontrunning and backrunning this first swap to profit.
if (_twoHops) {
uint256 _intermediateBal = IERC20(_path[1]).balanceOf(address(this));
IERC20(_path[1]).safeIncreaseAllowance(address(DEX_ADAPTER), _intermediateBal);
DEX_ADAPTER.swapV2Single(_path[1], _path[2], _intermediateBal, _amountOutMin, address(this));
}
return IERC20(_out).balanceOf(address(this)) - _outBefore;
}
No response
Use specified _amountOutMin
even when _twoHops
is == true