MorpheusAI

MorpheusAI
Foundry
22,500 USDC
View results
Submission Details
Severity: low
Invalid

Using `block.timestamp` for swap `deadline` offers no protection

Summary

block.timestamp is dynamic and will be valid any time the transaction is executed. it is not a fixed literal unix timestamp like for example 1706875672.

Vulnerability Details

According to Uniswap docs:

deadline is the unix time after which a swap will fail, to protect against long-pending transactions and wild swings in prices.

And unix timestamp at the time of writting this is a literal fixed value like this: 1706875672

In order to pass a deadline to a swap function, it is supposed to be passed as a parameter to the function offchain. The frontend can make this easy for the user by just requesting how many minutes long should the transaction be valid just like it is done on all AMM frontend.
Then the deadline will be calculated with 1706875672 + 20minutes.

This becomes 1706875672 + 20 * 60 seconds = 1706875792

1706875792 would then be passed as deadline parameter to the swap which will make the transaction only valid for 20 minutes from now.

And the above literal value will be prevent the transaction by being kept for too in order to profit from maximum slippage available.

The frontend can get the current unix timestamp with Math.floor(Date.now() / 1000) which is an equivalent of block.timestamp onchain. This is similar to getting the current unix timestamp from the blockchain first before sending the deadline in a different transaction.

Passing the deadline as a literal value offchain prevents a transaction from being kept for too long in order to profit from the slippage in market swings.

Here is the check for deadline in uniswap contract.

modifier ensure(uint deadline) {
require(deadline >= block.timestamp, 'UniswapV2Router: EXPIRED');
_;
}

From the above require statement, block.timestamp as deadline will always pass the validation anytime the transaction is executed but passing 1706875792 will cause the transaction to EXPIRE 20 minutes after the time of this report.

Here is the affected code:

File: L2TokenReceiver.sol
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_;
}

There is also another instance in the same file:

INonfungiblePositionManager.IncreaseLiquidityParams memory params_ = INonfungiblePositionManager
.IncreaseLiquidityParams({
tokenId: tokenId_,
amount0Desired: amountAdd0_,
amount1Desired: amountAdd1_,
amount0Min: amountMin0_,
amount1Min: amountMin1_,
@> deadline: block.timestamp
});

Impact

Swap transaction can be held for long by malicious validators during wild swing in prices until it incurs maximum slippage loss for the user and sandwich ttack profit for the malicious validator.

Tools Used

  • https://docs.uniswap.org/contracts/v3/guides/swaps/single-swaps#swap-input-parameters

  • https://stackoverflow.com/questions/221294/how-do-i-get-a-timestamp-in-javascript

  • https://www.unixtimestamp.com/

Recommendations

deadline should be passed as input parameter offchain because block.timestamp will pass the deadline check even if the transaction is kept for a whole day.

Updates

Lead Judging Commences

inallhonesty Lead Judge over 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

Protocol should not use block.timestamp as deadline in Uniswap interactions because it renders the protection mechanism useless

inallhonesty Lead Judge over 1 year ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.