context: Fees.sol
The Fees.sol contract utilizes the UniswapV3 swapRouter to swap profits for WETH tokens.
To utilize these functionalities, the UniswapHandler must call various UniswapV3Router methods and approve the desired
swapping amount of tokens to the UniswapV3 swapRouter.
ISwapRouter.ExactInputSingleParams memory params = ISwapRouter
.ExactInputSingleParams({
tokenIn: _profits,
tokenOut: WETH,
fee: 3000,
recipient: address(this),
deadline: block.timestamp,
amountIn: amount,
amountOutMinimum: 0,
sqrtPriceLimitX96: 0
});
amount = swapRouter.exactInputSingle(params);
ISwapRouter.ExactInputSingleParams() is used in swapRouter.exactInputSingle.
UniswapV3Router requires the callee to provide input arguments that define how much the amount out minimum
UniswapHandler will allow for a trade. This argument is designed to prevent slippage and more importantly, sandwich
attacks.
The fees.sol contract fails to approve the required amount of tokens to the swapRouter to perform the swap, resulting in
the transaction always reverting.
function sellProfits(address _profits) public {
require(_profits != WETH, "not allowed");
uint256 amount = IERC20(_profits).balanceOf(address(this));
ISwapRouter.ExactInputSingleParams memory params = ISwapRouter
.ExactInputSingleParams({
tokenIn: _profits,
tokenOut: WETH,
fee: 3000,
recipient: address(this),
deadline: block.timestamp,
amountIn: amount,
amountOutMinimum: 0,
sqrtPriceLimitX96: 0
});
amount = swapRouter.exactInputSingle(params);
IERC20(WETH).transfer(staking, IERC20(WETH).balanceOf(address(this)));
}
The amountOutMinimum is set to zero, which poses a significant risk in production. This can lead to obtaining an
unusually bad price for a trade due to front-running, sandwich attacks, or other types of price manipulation.
As a result of the current code implementation, users will always experience failed transactions because they do not
approve the tokens for the swapRouter. Even if they approve the tokens, they can still lose their funds due to the
amountOutMinimum being set to zero.
Manual
1.Approve Tokens for Swapping: Before calling the UniswapV3Router's swapExactInputSingle function, the fees.sol contract
should approve the required amount of tokens to the swapRouter. This ensures that the swap can be executed successfully.
2.Set Appropriate amountOutMinimum: Calculate the value of amountOutMinimum using your SDK or an on-chain price oracle.
This will protect against the risk of getting an unusually bad price for a trade due to front-running, sandwich attacks,
or other types of price manipulation.
3.Follow UniswapV3 Single Swaps Docs: Ensure the contract adheres to the guidelines provided in the UniswapV3 Single
Swaps documentation to prevent vulnerabilities and ensure correct functionality.
function swapExactInputSingle(address _profits) external returns (uint256 amountOut) {
require(_profits != WETH, "not allowed");
uint256 amount = IERC20(_profits).balanceOf(address(this));
require(amount != 0, "amount is zero");
// Approve the router to spend amount.
IERC20(_profits).approve(address(swapRouter), amount);
ISwapRouter.ExactInputSingleParams memory params =
ISwapRouter.ExactInputSingleParams({
tokenIn: _profits,
tokenOut: WETH,
fee: 3000,
recipient: msg.sender,
deadline: block.timestamp,
amountIn: amount,
amountOutMinimum: calculated Amount,
sqrtPriceLimitX96: 0
});
// The call to `exactInputSingle` executes the swap.
amountOut = swapRouter.exactInputSingle(params);
By addressing these recommendations, users can safely swap their profits for WETH tokens without encountering
transaction failures or losing funds due to price manipulation risks.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.