Flow

Sablier
FoundryDeFi
20,000 USDC
View results
Submission Details
Severity: medium
Invalid

Timestamp Manipulation Vulnerability in Sablier's Debt Calculation Enables MEV Extraction Through Block Time Variance

Summary

The _ongoingDebtScaledOf function calculates streaming debt using block.timestamp to measure time elapsed since the last snapshot. It assumes chronologically increasing timestamps for its calculations, functioning as a core component in Sablier's accounting system. The function compares current block time with the stored snapshot time and calculates elapsed duration to determine accrued debt, making it sensitive to the blockchain's timestamp mechanics.

The monotonic time assumption issue in _ongoingDebtScaledOf centers around its reliance on block.timestamp ordering:

function _ongoingDebtScaledOf(uint256 streamId) internal view returns (uint256) {
uint256 blockTimestamp = block.timestamp;
uint256 snapshotTime = _streams[streamId].snapshotTime;
// Assumes blockTimestamp is always > snapshotTime for active streams
if (ratePerSecond == 0 || blockTimestamp <= snapshotTime) {
return 0;
}
unchecked {
// Assumes timestamps only increase
elapsedTime = blockTimestamp - snapshotTime;
}
return elapsedTime * ratePerSecond;
}

While Ethereum generally ensures timestamps increase monotonically (each block's timestamp must be greater than its parent's), miners have some wiggle room - they can set timestamps within about 15 seconds of their local clock. In edge cases, this could lead to:

  1. A transaction being included in a block with timestamp slightly less than expected

  2. MEV searchers selecting specific timestamps to manipulate debt calculations

  3. Small variations in accrued debt based on exact block inclusion time

For example, if a stream update transaction gets included in block with timestamp 100, but the next withdrawal happens in a block with timestamp 98 (due to miner timestamp adjustment), the debt calculation could temporarily show zero when it should show positive debt.

This means debt calculations aren't perfectly deterministic over very short time frames.

Impact

The timestamp manipulation potential in _ongoingDebtScaledOf allows MEV searchers and miners to slightly influence debt calculations by adjusting block timestamps within the allowed bounds (~15 seconds). This could be exploited for profit in high-value streams where minor timing differences affect withdrawal amounts. For instance, strategically timing withdrawals against specific block timestamps could extract additional value, particularly in scenarios involving large token amounts or when interacting with other DeFi protocols that rely on Sablier's debt calculations. While the manipulation window is small, it represents a systematic way to extract value from the protocol's timing assumptions.

Fix

The monotonic time issue in _ongoingDebtScaledOf can be mitigated by using block numbers instead of timestamps for more reliable time tracking:

// Add to Stream struct
struct Stream {
// ... existing fields ...
uint256 snapshotBlock; // New field
}
function _ongoingDebtScaledOf(uint256 streamId) internal view returns (uint256) {
uint256 currentBlock = block.number;
uint256 snapshotBlock = _streams[streamId].snapshotBlock;
uint256 ratePerSecond = _streams[streamId].ratePerSecond.unwrap();
if (ratePerSecond == 0 || currentBlock <= snapshotBlock) {
return 0;
}
// Average block time (12 seconds for Ethereum)
uint256 BLOCK_TIME = 12;
unchecked {
uint256 blocks = currentBlock - snapshotBlock;
uint256 elapsedTime = blocks * BLOCK_TIME;
}
return elapsedTime * ratePerSecond;
}

This approach trades off some time precision for stronger monotonicity guarantees, as block numbers are strictly increasing and cannot be manipulated by miners.

Updates

Lead Judging Commences

inallhonesty Lead Judge 12 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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