The SablierFlow protocol implements a token streaming mechanism where tokens flow from sender to recipient at a defined rate per second, with each stream represented as an NFT. The system uses snapshots to track debt accumulation and stream status, managing transitions between states like SOLVENT, INSOLVENT, PAUSED, and VOIDED. Critical to the system's accounting is _ongoingDebtScaledOf
, which calculates accumulated debt since the last snapshot. The vulnerability surfaces in the boundary condition handling of this debt calculation, specifically when block.timestamp exactly matches a stream's snapshot time, affecting the protocol's core financial accounting and stream state management.
The issue centers around how debt is calculated at exact snapshot times. Let's look at the critical code paths:
First, the debt calculation starts in _ongoingDebtScaledOf
:
https://github.com/Cyfrin/2024-10-sablier/blob/main/src/SablierFlow.sol#L478
The vulnerability manifests when:
In this case, the function returns 0, meaning NO debt is counted for that second, even though the stream is active.
This affects total debt calculation:
Which then affects status determination:
Let's see a concrete example:
The snapshot boundary issue compounds because:
Every stream operation that updates the snapshot time creates a new boundary point:
These operations include:
Creating stream
Adjusting rate
Pausing/restarting
Some withdrawals
The snapshot boundary vulnerability creates a systematic accounting error in the SablierFlow system. When a stream operation aligns with its snapshot time, the protocol fails to account for one second of streaming debt, effectively creating a "free second" of token flow.
This accounting gap propagates through the contract's core mechanisms, causing the protocol to underreport total debt and incorrectly classify stream solvency states. While a single second of missing debt might appear minimal, it manifests at every snapshot boundary and compounds across multiple streams and operations.
Most critically, it allows recipients to withdraw more tokens than they should be entitled to, directly impacting the protocol's accounting integrity and creating a cumulative token leak. For high-value streams or automated systems making frequent use of stream operations that create new snapshots, this vulnerability provides a reliable attack vector for extracting excess value from affected streams.
Change the timestamp comparison in _ongoingDebtScaledOf:
Alternative Fix - Add Explicit Current Second Handling:
This ensures:
The current second is always counted in debt calculations
No "free" seconds at snapshot boundaries
Accurate status reporting
Proper withdrawal amounts
The vulnerability is subtle but serious because:
It occurs at every snapshot boundary
Affects fundamental stream accounting
Can be exploited for financial gain
Compounds over multiple operations
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.