Summary
The depletionTimeOf
function in SablierFlow contract contains arithmetic operations where intermediate expressions return a lower-order uint type (uint40) than the target type (uint256), which could potentially lead to arithmetic overflow due to implicit type casting.
Vulnerability Details
function depletionTimeOf(uint256 streamId) external view returns (uint256 depletionTime) {
unchecked {
uint256 solvencyAmount = balanceScaled - snapshotDebtScaled + oneMVTScaled;
uint256 solvencyPeriod = solvencyAmount / ratePerSecond;
if (solvencyAmount % ratePerSecond == 0) {
depletionTime = _streams[streamId].snapshotTime + solvencyPeriod;
} else {
depletionTime = _streams[streamId].snapshotTime + solvencyPeriod + 1;
}
}
}
The issue occurs because:
_streams[streamId].snapshotTime
is of type uint40
The arithmetic operations are performed in an unchecked block
When adding uint40 + uint256, the intermediate expression returns uint40 before being assigned to uint256
This could lead to unexpected overflow if solvencyPeriod
is large enough
Impact
If solvencyPeriod
is large enough, the intermediate uint40 calculation could overflow before being stored in the uint256 depletionTime
This could lead to incorrect depletion time calculations
The impact is somewhat mitigated by the fact that solvencyPeriod
would need to be very large to trigger the overflow
The code is within an unchecked block which makes it more concerning
Tools Used
slither .
## pess-potential-arithmetic-overflow
Impact: Medium
Confidence: Medium
* [ ] ID-22
[SablierFlow.depletionTimeOf(uint256)]() contains integer variables whose type is larger than the type of one of its intermediate expressions. Consider casting sub expressions explicitly as they might lead to unexpected overflow:
In `[depletionTime = _streams[streamId].snapshotTime + solvencyPeriod]()` intermidiate expressions returns type of lower order:
`REF_144 + solvencyPeriod` returns uint40, but the type of the resulting expression is uint256.
In `[depletionTime = _streams[streamId].snapshotTime + solvencyPeriod + 1]()` intermidiate expressions returns type of lower order:
`REF_146 + solvencyPeriod` returns uint40, but the type of the resulting expression is uint256.
`... + 1` returns uint40, but the type of the resulting expression is uint256.
src/SablierFlow\.sol#L53-L101
manuel code review
Recommendations:
Explicitly cast snapshotTime to uint256 before performing arithmetic:
function depletionTimeOf(uint256 streamId) external view returns (uint256 depletionTime) {
unchecked {
uint256 solvencyAmount = balanceScaled - snapshotDebtScaled + oneMVTScaled;
uint256 solvencyPeriod = solvencyAmount / ratePerSecond;
if (solvencyAmount % ratePerSecond == 0) {
depletionTime = uint256(_streams[streamId].snapshotTime) + solvencyPeriod;
} else {
depletionTime = uint256(_streams[streamId].snapshotTime) + solvencyPeriod + 1;
}
}
}
Consider removing the unchecked block to ensure safer arithmetic operations:
if (solvencyAmount % ratePerSecond == 0) {
depletionTime = uint256(_streams[streamId].snapshotTime) + solvencyPeriod;
} else {
depletionTime = uint256(_streams[streamId].snapshotTime) + solvencyPeriod + 1;
}