Feisty Pickle Starling
Medium
The reliance on manual forwardAll calls in StreamEscrow will cause significant timeline extension for streams as delayed forwardAll calls can extend a 4 year stream to more than 4 years, breaking core timing assumptions of the protocol.
There is no implementation in the code limiting the tick duration, or automatically trigger forwardAll
after minimumTickDuration
passes.
The choice to not implement a maximum time between ticks is a mistake as it allows forwardAll
delays to compound, significantly extending stream durations beyond their intended timelines.
function forwardAll() public {
// silently fail if at least a day hasn't passed. this is in order not to revert auction house.
if (block.timestamp < lastForwardTimestamp + minimumTickDuration) {
return;
}
lastForwardTimestamp = toUint48(block.timestamp);
sendETHToTreasury(ethStreamedPerTick);
(uint32 newTick, uint128 ethPerTickEnded) = increaseTicksAndFinishStreams();
emit StreamsForwarded(newTick, ethPerTickEnded, ethStreamedPerTick, lastForwardTimestamp);
}
- A stream needs to exist and be active
- forwardAll calls are delayed beyond minimumTickDuration
N/A
- Stream is created with 1460 ticks (intended 4 year duration)
- Assume each forwardAll call is delayed by 1 hour beyond minimumTickDuration. This delay can be more than 1 hour
- This compounds across all ticks:
Total delay = 1460 ticks * 1 hour delay = 1460 hours = 60 days ≈ 2 months additional duration
- The protocol's core timing assumptions are broken
- Streams intended to last 4 year will take 4 years and 2 months to complete
- Treasury receives payments over a longer period than intended
See attack path
In the contract, implement a logic that automatically trigger forwardAll
after minimumTickDuration
passes.