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
.