Summary
The L2TokenReceiver contract uses block.timestamp as the deadline for token swapping and liquidity management within the swap and increaseLiquidityCurrentRange functions. However, setting the deadline as block.timestamp essentially makes the deadline parameter useless and gives malicious miners the ability to hold the transaction for as long as they like and execute it at a more favourable block number.
Vulnerability Details
Both the swap and increaseLiquidityCurrentRange functions utilize block.timestamp to set the deadlines. This method does not effectively safeguard against the manipulation of transaction timing by miners or through MEV strategies. The point of the deadline parameter is that it will check against block.timestamp when the transaction is mined. If block.timestamp <= deadline, then the transaction is successful. However, if you set the deadline as block.timestamp, it is just comparing against the same value and defeats the purpose of the deadline protection. Visit here for more details.
swap function:
function swap(uint256 amountIn_, uint256 amountOutMinimum_) external onlyOwner returns (uint256) {
SwapParams memory params_ = params;
ISwapRouter.ExactInputSingleParams memory swapParams_ = ISwapRouter.ExactInputSingleParams({
tokenIn: params_.tokenIn,
tokenOut: params_.tokenOut,
fee: params_.fee,
recipient: address(this),
@> deadline: block.timestamp,
amountIn: amountIn_,
amountOutMinimum: amountOutMinimum_,
sqrtPriceLimitX96: params_.sqrtPriceLimitX96
});
uint256 amountOut_ = ISwapRouter(router).exactInputSingle(swapParams_);
emit TokensSwapped(params_.tokenIn, params_.tokenOut, amountIn_, amountOut_, amountOutMinimum_);
return amountOut_;
}
increaseLiquidityCurrentRange function:
function increaseLiquidityCurrentRange(
uint256 tokenId_,
uint256 depositTokenAmountAdd_,
uint256 rewardTokenAmountAdd_,
uint256 depositTokenAmountMin_,
uint256 rewardTokenAmountMin_
) external onlyOwner returns (uint128 liquidity_, uint256 amount0_, uint256 amount1_) {
uint256 amountAdd0_;
uint256 amountAdd1_;
uint256 amountMin0_;
uint256 amountMin1_;
(, , address token0_, , , , , , , , , ) = INonfungiblePositionManager(nonfungiblePositionManager).positions(
tokenId_
);
if (token0_ == params.tokenIn) {
amountAdd0_ = depositTokenAmountAdd_;
amountAdd1_ = rewardTokenAmountAdd_;
amountMin0_ = depositTokenAmountMin_;
amountMin1_ = rewardTokenAmountMin_;
} else {
amountAdd0_ = rewardTokenAmountAdd_;
amountAdd1_ = depositTokenAmountAdd_;
amountMin0_ = rewardTokenAmountMin_;
amountMin1_ = depositTokenAmountMin_;
}
INonfungiblePositionManager.IncreaseLiquidityParams memory params_ = INonfungiblePositionManager
.IncreaseLiquidityParams({
tokenId: tokenId_,
amount0Desired: amountAdd0_,
amount1Desired: amountAdd1_,
amount0Min: amountMin0_,
amount1Min: amountMin1_,
@> deadline: block.timestamp
});
(liquidity_, amount0_, amount1_) = INonfungiblePositionManager(nonfungiblePositionManager).increaseLiquidity(
params_
);
emit LiquidityIncreased(tokenId_, amount0_, amount1_, liquidity_, amountMin0_, amountMin1_);
}
Impact
Despite being restricted to onlyOwner, these operations influence the protocol's liquidity and market positions, impacting user assets. It can also lead to suboptimal swap rates or liquidity positions, potentially diminishing the protocol's assets or affecting its ability to provide user services effectively, and loss of user trust.
Tools Used
Manual review.
Recommendations
Consider modifying the swap and increaseLiquidityCurrentRange to allow the owner of the contract to use a specific deadline input parameter, rather than using block.timestamp.