Sablier

Sablier
DeFiFoundry
53,440 USDC
View results
Submission Details
Severity: low
Invalid

The `SablierV2MerkleLockup::hasExpired` function can return incorrect result due to the `block.timestamp` on Arbitrum

Summary

The block.timestamp works differently on Arbitrum.

Vulnerability Details

The project will be deployed on any network which is EVM compatible, that includes also the L2 networks such as Arbitrum. Also, the SablierV2MerkleLockup::hasExpired function relies on block.timestamp to check if the stream has expired.

function hasExpired() public view override returns (bool) {
return EXPIRATION > 0 && EXPIRATION <= block.timestamp;
}

But the block.timestamp on Arbitrum works differently.

In the Arbitrum documentation is written:

Block timestamps on Arbitrum are not linked to the timestamp of the L1 block. They are updated every L2 block based on the sequencer's >clock. These timestamps must follow these two rules:

  • Must be always equal or greater than the previous L2 block timestamp

  • Must fall within the established boundaries (24 hours earlier than the current time or 1 hour in the future). More on this below.

Furthermore, for transactions that are force-included from L1 (bypassing the sequencer), the block timestamp will be equal to either the L1 > timestamp when the transaction was put in the delayed inbox on L1 (not when it was force-included), or the L2 timestamp of the previous L2 > block, whichever of the two timestamps is greater.

https://docs.arbitrum.io/for-devs/concepts/differences-between-arbitrum-ethereum/block-numbers-and-time#block-timestamps-arbitrum-vs-ethereum

Impact

When the protocol is deployed on L2 chains such an Arbitrum, relying on block.timestamp for important functions is not recommended.
For example, the SablierV2MerkleLockup::hasExpired function can return true, if the block.timestamp on Arbitrum returns 1 hour in the future, but in the reality the EXPIRATION is still greater than the curent time. Also, it is possible the scenario when the function returns false because the block.timestamp on Arbitrum returns 24 hours earlier, but in the reality the EXPIRATION is lower than the current time.

Other parts of the protocol also rely on the block.timestamp: SablierV2LockupDynamics, SablierV2LockupLinear, SablierV2LockupTranched, SablierV2Lockup, Helpers. It is important to ensure that block.timestamp always returns the correct time.

Tools Used

Manual Review

Recommendations

Ensure that the return value of the block.timestamp and every function that relies on it is reliable on every chain that the project is deployed. If there is a possibility for deviation, add a threshold.

Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

Info/Gas/Invalid as per Docs

https://docs.codehawks.com/hawks-auditors/how-to-determine-a-finding-validity

Support

FAQs

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