Omniscia SaucerSwap Labs Audit
UniswapV2Router02 Manual Review Findings
UniswapV2Router02 Manual Review Findings
UVR-01M: Potential Arithmetic Underflow
Type | Severity | Location |
---|---|---|
Mathematical Operations | UniswapV2Router02.sol:L96 |
Description:
The msg.value
of the UniswapV2Router02::addLiquidityETHNewPool
is not guaranteed to be less than feeInTinybars
, potentially leading to an underflow in the UniswapV2Router02::_addLiquidity
function argument calculation.
Impact:
While an underflow can occur, it would result in the code not executing as the amountToken
and amountETH
the function would calculate would be exorbitant and unfulfillable by the msg.sender
.
Example:
83function addLiquidityETHNewPool(84 address token,85 uint amountTokenDesired,86 uint amountTokenMin,87 uint amountETHMin,88 address to,89 uint deadline90) external virtual payable override ensure(deadline) returns (uint amountToken, uint amountETH, uint liquidity) {91 require (IUniswapV2Factory(factory).getPair(token, whbar) == address(0), "UniswapV2Router: POOL ALREADY EXISTS");92 uint256 feeInTinybars = tinycentsToTinybars(IUniswapV2Factory(factory).pairCreateFee());93
94 IUniswapV2Factory(factory).createPair{value: feeInTinybars}(token, whbar);95
96 (amountToken, amountETH) = _addLiquidity(token, whbar, amountTokenDesired, msg.value - feeInTinybars, amountTokenMin, amountETHMin);97 address pair = UniswapV2Library.pairFor(factory, token, whbar);98 99 safeTransferToken(100 token, msg.sender, pair, amountToken101 );102 IWHBAR(WHBAR).deposit{value: amountETH}(msg.sender, pair);103 liquidity = IUniswapV2Pair(pair).mint(to);104 if (msg.value - feeInTinybars > amountETH) TransferHelper.safeTransferETH(msg.sender, msg.value - feeInTinybars - amountETH);105}
Recommendation:
We advise the code to require
that the msg.value
is at least greater-than the feeInTinybars
, preventing this underflow from occurring and rendering the operations in UniswapV2Router02::addLiquidityETHNewPool
safe.
Alleviation (a2c5a0b913):
The SaucerSwap team stated that they believe this to be incorrect. We would like to note that the UniswapV2Router02::addLiquidityETHNewPool
function can invoke UniswapV2Factory::createPair
with a value of feeInTinybars
even if msg.value
is less than that. This is possible because such native calls can tap into the native balance
of the contract, not only the msg.value
.
As an example, a user can send the feeInTinybars
value directly to the UniswapV2Router02
contract and then invoke UniswapV2Router02::addLiquidityETHNewPool
with a msg.value
of 0
. This would cause L94 (L95 in the latest commit hash) to successfully execute and thus permit the underflow.
As such, we still advise the require
check to be introduced.
Alleviation (fd26dbdf6d):
The SaucerSwap team has introduced the recommended require
check in the UniswapV2Router02::addLiquidityETHNewPool
function as advised, rendering this exhibit properly alleviated.
UVR-02M: Potentially Discrepant Exchange Flow
Type | Severity | Location |
---|---|---|
Logical Fault | UniswapV2Router02.sol:L414, L418 |
Description:
The UniswapV2Router02::swapExactTokensForETHSupportingFeeOnTransferTokens
function will perform the sequences of swaps defined in the path
argument with the final recipient being the msg.sender
. Afterwards, it will perform a withdrawal operation from the msg.sender
to the to
address, in turn requiring an approval from the msg.sender
to the WHBAR
contract (Operator) of the whbar
asset (wHBAR
token).
Impact:
The current swapping methodology will require an approval of the msg.sender
to the WHBAR
contract in addition to the approval required on the UniswapV2Router02
which is discrepant with its normal operation.
Example:
397function swapExactTokensForETHSupportingFeeOnTransferTokens(398 uint amountIn,399 uint amountOutMin,400 address[] calldata path,401 address to,402 uint deadline403)404 external405 virtual406 override407 ensure(deadline)408{409 require(path[path.length - 1] == whbar, 'UniswapV2Router: INVALID_PATH');410 uint startAmount = IERC20(whbar).balanceOf(msg.sender);411 safeTransferToken(412 path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amountIn413 );414 _swapSupportingFeeOnTransferTokens(path, msg.sender);415 uint endAmount = IERC20(whbar).balanceOf(msg.sender);416 uint amountOut = endAmount.sub(startAmount);417 require(amountOut >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT');418 IWHBAR(WHBAR).withdraw(msg.sender, to, amountOut);419}
Recommendation:
We advise the code to revise its methodology and to instead set itself as the output of the swaps, unwrapping the whbar
token via the WHBAR
contract on itself similarly to UniswapV2Router02::removeLiquidityETH
.
Alleviation (a2c5a0b913a7ddc21ff96f97fa51f2820a5da7ec):
The code has been revised as advised, setting the router itself as the recipient of the swap chain and unwrapping the asset to the msg.sender
thus ensuring that no superfluous approvals are needed.
UVR-03M: Incorrect Recipient of ETH Outputs
Type | Severity | Location |
---|---|---|
Logical Fault | UniswapV2Router02.sol:L302, L319 |
Description:
The UniswapV2Router02::swapExactTokensForETH
and UniswapV2Router02::swapTokensForExactETH
functions presently misbehave as they set the output of the UniswapV2Router02::_swap
to the to
address, performing a withdrawal of the whbar
asset on the msg.sender
incorrectly.
Impact:
The code presently misbehaves by performing a swap of the amount directly to the to
target and providing them with double the output amount in the form of the whbar
asset unwrapped via the WHBAR
contract.
Example:
288function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline)289 external290 virtual291 override292 ensure(deadline)293 returns (uint[] memory amounts)294{295 require(path[path.length - 1] == whbar, 'UniswapV2Router: INVALID_PATH');296 amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path);297 require(amounts[0] <= amountInMax, 'UniswapV2Router: EXCESSIVE_INPUT_AMOUNT');298 safeTransferToken(299 path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]300 );301 302 _swap(amounts, path, to); 303 IWHBAR(WHBAR).withdraw(msg.sender, to, amounts[amounts.length - 1]);304}305function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)306 external307 virtual308 override309 ensure(deadline)310 returns (uint[] memory amounts)311{312 require(path[path.length - 1] == whbar, 'UniswapV2Router: INVALID_PATH');313 amounts = UniswapV2Library.getAmountsOut(factory, amountIn, path);314 require(amounts[amounts.length - 1] >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT');315
316 safeTransferToken(317 path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]318 );319 _swap(amounts, path, to); 320 IWHBAR(WHBAR).withdraw(msg.sender, to, amounts[amounts.length - 1]);321}
Recommendation:
We advise them to perform the swap with an output to the contract itself, unwrapping the whbar
asset via the WHBAR
operator locally similarly to UniswapV2Router02::removeLiquidityETH
.
Alleviation (a2c5a0b913a7ddc21ff96f97fa51f2820a5da7ec):
The recipient of the swaps has been corrected to the contract itself, permitting it to unwrap the whbar
asset correctly.