Swaps are set with block.timestamp
as deadline. Without a deadline, the transaction might be left hanging in the mempool and be executed way later than the user wanted. That could lead to user getting a worse price, because a validator can just hold onto the transaction. And when it does get around to putting the transaction in a block, it'll be block.timestamp, so they've got no protection there.
Affected code
https://github.com/Cyfrin/2024-01-Morpheus/blob/main/contracts/L2TokenReceiver.sol#L54-L73
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_;
}
Since block.timestamp
is always relative, using it in any way is equivalent to using no deadline at all. Needs to use a user defined input to effectively enforce any deadline.
Vulnerabilities details
This is a well-known vulnerability and here's an article detailing the impact and how Uniswap has a very clear deadline set - Bytes032 - why you should stop using block timestamp as deadline in swaps
Recommended mitigation steps
function swap(
uint256 amountIn_,
uint256 amountOutMinimum_,
+ uint256 deadline_
) external onlyOwner returns (uint256) {
+ require(deadline_ > block.timestamp, "deadline passed")
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,
+ deadline: deadline_
amountIn: amountIn_,
amountOutMinimum: amountOutMinimum_,
sqrtPriceLimitX96: params_.sqrtPriceLimitX96
});
uint256 amountOut_ = ISwapRouter(router).exactInputSingle(swapParams_);
emit TokensSwapped(params_.tokenIn, params_.tokenOut, amountIn_, amountOut_, amountOutMinimum_);
return amountOut_;
}
Tools
Manual review