MorpheusAI

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

Inappropriate Use of `block.timestamp` as Transaction Deadline in `L2TokenReceiver` Contract while performing `swap` and `increaseLiquidityCurrentRange`

Summary

The L2TokenReceiver contract uses block.timestamp as the deadline for transactions in the swap and increaseLiquidityCurrentRange functions. This practice does not provide the intended protection against the execution of stale transactions and may lead to unintended consequences if transactions are mined with a delay.

When a miner chooses to include the transaction into a block, its validity is established at that moment, as the block.timestamp reflects the current timestamp as the deadline.

Vulnerability Details

The contract sets the deadline for certain operations to block.timestamp, which is the timestamp of the current block. Since the deadline is checked against the timestamp of the block in which the transaction is included, using block.timestamp as the deadline means the check will always pass as long as the transaction is included in any block, rendering the deadline check ineffective.

Please see the execution flows of swap() and increaseLiquidityCurrentRange().

1. swap()

In the code snippet of the swap function below, you can see that on line 65, block.timestamp is utilized as the deadline.

File: contracts/L2TokenReceiver.sol
57: function swap(uint256 amountIn_, uint256 amountOutMinimum_) external onlyOwner returns (uint256) {
58: SwapParams memory params_ = params;
59:
60: ISwapRouter.ExactInputSingleParams memory swapParams_ = ISwapRouter.ExactInputSingleParams({
61: tokenIn: params_.tokenIn,
62: tokenOut: params_.tokenOut,
63: fee: params_.fee,
64: recipient: address(this),
65: @> deadline: block.timestamp,
66: amountIn: amountIn_,
67: amountOutMinimum: amountOutMinimum_,
68: sqrtPriceLimitX96: params_.sqrtPriceLimitX96
69: });
70:
71: @> uint256 amountOut_ = ISwapRouter(router).exactInputSingle(swapParams_);

https://github.com/Cyfrin/2024-01-Morpheus/blob/07c900d22073911afa23b7fa69a4249ab5b713c8/contracts/L2TokenReceiver.sol#L57C1-L71C80

And observe what happens if line 71 calls the exactInputSingle() function from the SwapRouter contract in Uniswap V3.

File: Uniswap/v3-periphery/contracts/SwapRouter.sol
115: function exactInputSingle(ExactInputSingleParams calldata params)
116: external
117: payable
118: override
119: @> checkDeadline(params.deadline)
120: returns (uint256 amountOut)
121: {

https://github.com/Uniswap/v3-periphery/blob/697c2474757ea89fec12a4e6db16a574fe259610/contracts/SwapRouter.sol#L115C5-L121C6

The exactInputSingle() function employs the checkDeadline() modifier at line 119, which effectively compares the same current timestamps at line 8 in the code below.

File: Uniswap/v3-periphery/contracts/base/PeripheryValidation.sol
7: modifier checkDeadline(uint256 deadline) {
8: require(_blockTimestamp() <= deadline, 'Transaction too old');
9: _;
10: }

https://github.com/Uniswap/v3-periphery/blob/697c2474757ea89fec12a4e6db16a574fe259610/contracts/base/PeripheryValidation.sol#L7C1-L10C6

2. increaseLiquidityCurrentRange()

In the provided code snippet for the increaseLiquidityCurrentRange function, you can see that block.timestamp is used as the deadline on line 112.

File: contracts/L2TokenReceiver.sol
78: function increaseLiquidityCurrentRange(
80: uint256 tokenId_,
81: uint256 depositTokenAmountAdd_,
82: uint256 rewardTokenAmountAdd_,
83: uint256 depositTokenAmountMin_,
84: uint256 rewardTokenAmountMin_
85: ) external onlyOwner returns (uint128 liquidity_, uint256 amount0_, uint256 amount1_) {
. /* --- Other Code --- */
105: INonfungiblePositionManager.IncreaseLiquidityParams memory params_ = INonfungiblePositionManager
106: .IncreaseLiquidityParams({
107: tokenId: tokenId_,
108: amount0Desired: amountAdd0_,
109: amount1Desired: amountAdd1_,
110: amount0Min: amountMin0_,
111: amount1Min: amountMin1_,
112: @> deadline: block.timestamp
113: });
114:
115: @> (liquidity_, amount0_, amount1_) = INonfungiblePositionManager(nonfungiblePositionManager).increaseLiquidity(
116: params_
117: );

https://github.com/Cyfrin/2024-01-Morpheus/blob/07c900d22073911afa23b7fa69a4249ab5b713c8/contracts/L2TokenReceiver.sol#L78C1-L117C11

See the line 115 invokes the increaseLiquidity() function from the NonfungiblePositionManager contract in Uniswap V3.

File: Uniswap/v3-periphery/contracts/NonfungiblePositionManager.sol
198: function increaseLiquidity(IncreaseLiquidityParams calldata params)
199: external
200: payable
201: override
202: @> checkDeadline(params.deadline)
203: returns (
204: uint128 liquidity,
205: uint256 amount0,
206: uint256 amount1
207: )
208: {

https://github.com/Uniswap/v3-periphery/blob/697c2474757ea89fec12a4e6db16a574fe259610/contracts/NonfungiblePositionManager.sol#L198C1-L208C6

The increaseLiquidity() function utilizes the checkDeadline() modifier at line 202, effectively comparing the current timestamps as seen on line 8 in the provided code.

File: Uniswap/v3-periphery/contracts/base/PeripheryValidation.sol
7: modifier checkDeadline(uint256 deadline) {
8: require(_blockTimestamp() <= deadline, 'Transaction too old');
9: _;
10: }

https://github.com/Uniswap/v3-periphery/blob/697c2474757ea89fec12a4e6db16a574fe259610/contracts/base/PeripheryValidation.sol#L7C1-L10C6

Impact

The ineffective use of deadlines in transaction execution can expose protocol to significant risks, particularly in volatile market conditions.

Transactions intended to manage swaps, liquidity or preempt liquidation events may be delayed by miners, who could prioritize more profitable operations with their subsequent transactions.

This delay could result in the original transaction being executed at a time when market conditions have shifted unfavorably, leading to substantial slippage or making the protocol vulnerable to front-running.

Additionally, miners may intentionally withhold transactions until the point of maximum slippage, exacerbating the protocol's potential losses and undermining the transaction's original intent.

Tools Used

Manual Review

Recommendations

Modify the contract functions to accept a deadline parameter from the caller instead of using block.timestamp directly within the functions.

- function swap(uint256 amountIn_, uint256 amountOutMinimum_) external onlyOwner returns (uint256) {
+ function swap(uint256 amountIn_, uint256 amountOutMinimum_, uint256 deadline_) 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,
+ deadline: deadline_,
amountIn: amountIn_,
amountOutMinimum: amountOutMinimum_,
sqrtPriceLimitX96: params_.sqrtPriceLimitX96
});
function increaseLiquidityCurrentRange(
uint256 tokenId_,
uint256 depositTokenAmountAdd_,
uint256 rewardTokenAmountAdd_,
uint256 depositTokenAmountMin_,
- uint256 rewardTokenAmountMin_
+ uint256 rewardTokenAmountMin_,
+ uint256 deadline_
) external onlyOwner returns (uint128 liquidity_, uint256 amount0_, uint256 amount1_) {
/* --- Other Code --- */
INonfungiblePositionManager.IncreaseLiquidityParams memory params_ = INonfungiblePositionManager
.IncreaseLiquidityParams({
tokenId: tokenId_,
amount0Desired: amountAdd0_,
amount1Desired: amountAdd1_,
amount0Min: amountMin0_,
amount1Min: amountMin1_,
- deadline: block.timestamp
+ deadline: deadline_
});
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

sovaslava Auditor
over 1 year ago
oxtenma Auditor
over 1 year ago
inallhonesty Lead Judge
over 1 year ago
oxtenma Auditor
over 1 year ago
inallhonesty Lead Judge
over 1 year ago
inallhonesty Lead Judge
over 1 year ago
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.