A vulnerability was discovered in the SablierFlow
smart contract where adjusting the ratePerSecond
multiple times without advancing the block timestamp leads to incorrect snapshot debt accumulation. This results in inaccurate debt calculations, which can cause financial discrepancies and potential exploitation by malicious actors.
In the SablierFlow
contract, users can adjust the ratePerSecond
of a stream using the adjustRatePerSecond
function. The contract should accurately calculate the debt based on the time elapsed and the current rate. However, when multiple adjustments are made at the same timestamp (without advancing time), the snapshot debt increases incorrectly. The expected behavior is that if no time has advanced, the snapshot debt should remain unchanged after adjustments.
Issue Explanation:
Initial Setup:
A stream is created with an initial ratePerSecond
.
Funds are deposited into the stream.
The snapshot debt is recorded before any rate adjustments.
Adjustments Without Time Advancement:
The ratePerSecond
is adjusted multiple times without advancing the block timestamp.
The snapshot debt is recorded after adjustments.
Despite no time passing, the snapshot debt increases, which should not occur.
Debt Calculation After Time Advancement:
The block timestamp is advanced by 10 seconds.
The total debt is calculated and compared against the expected value based on the last ratePerSecond
.
The total debt does not match the expected value, indicating incorrect debt accumulation.
Create the mock file: tests/mocks/ERC20Mock.sol
with the following content:
Create MockFlowNFTDescriptor file: tests/mocks/MockFlowNFTDescriptor.sol
with the following content:
then create the main test file: tests/SnapshotDebtAccumulationBugTest.t.sol
with the following content and then run the main test with command: forge test --mt testSnapshotDebtAccumulationBug -vvvv
Explanation:
Setup Phase:
Deploy Contracts:
MockERC20
token is deployed, and an initial supply is minted to the deployer.
SablierFlow
contract is deployed with necessary parameters.
Logs deployment addresses for clarity.
Token Distribution:
Transfers 10,000 tokens to the sender
address.
Logs the transfer details.
Approval:
The sender
approves SablierFlow
to spend an unlimited amount of tokens on their behalf.
Uses vm.startPrank
to simulate actions from the sender
.
Logs the approval action.
Test Phase (testSnapshotDebtAccumulationBug
):
Stream Creation:
Sets the initial ratePerSecond
to 1 token per second.
Creates a new stream from sender
to recipient
.
Deposits 100 tokens into the stream.
Logs the stream creation and deposit actions.
Snapshot Debt Recording:
Records the snapshotDebtBefore
any rate adjustments.
Logs the initial snapshot debt.
Rate Adjustments Without Time Advancement:
Adjusts the ratePerSecond
to 2 tokens per second.
Immediately adjusts the ratePerSecond
again to 3 tokens per second without advancing time.
Logs each rate adjustment.
Snapshot Debt Verification:
Records the snapshotDebtAfter
adjustments.
Asserts that snapshotDebtAfter
should equal snapshotDebtBefore
since no time has passed.
Logs the verification result.
Time Advancement and Debt Calculation:
Advances the block timestamp by 10 seconds using vm.warp
.
Manually calculates the expected total debt based on the last ratePerSecond
.
Retrieves the totalDebt
from the contract.
Asserts that the totalDebt
matches the expected debt, which is going to fail, meaning the calculation is not done correctly
Logs the calculations and assertions.
https://github.com/Drlaravel/img-debt/blob/main/debt.png
Underlying Issue:
The adjustRatePerSecond
function does not correctly handle multiple adjustments made at the same timestamp. The snapshot debt is erroneously inc
Inaccurate Debt Accounting: Users may be overcharged or undercharged due to incorrect debt calculations, leading to financial losses or unjust gains.
Potential Exploitation: Malicious actors could exploit this flaw to manipulate debt calculations, draining funds from the contract or disrupting the streaming process.
Contract Integrity Risks: The reliability of the SablierFlow
contract is compromised, affecting trust and usability among users and stakeholders.
Foundry: A fast and portable Ethereum development toolkit for compiling, testing, and deploying smart contracts.
Solidity Compiler (solc): Version 0.8.22, used for compiling the Solidity smart contracts.
Forge Std Library: Provides testing utilities and console logging for enhanced debugging.
Console Logs: Added via console.log
statements for step-by-step tracing of the contract's internal state during execution.
Modify Debt Calculation Logic:
Update the adjustRatePerSecond
function to ensure that debt calculations are only based on the elapsed time since the last update.
Implement checks to prevent snapshot debt from increasing if no time has advanced between adjustments.
Implement Time-Based Conditions:
Before updating the snapshot debt, verify if the block timestamp has advanced since the last rate adjustment.
If the timestamp is unchanged, the debt accumulation should not proceed.
Enhance Unit Testing:
Add comprehensive unit tests that cover scenarios with multiple rate adjustments occurring at the same timestamp.
Ensure tests validate that snapshot debt remains accurate in such cases.
Code Review and Auditing:
Perform an in-depth code review focusing on the debt calculation mechanisms within the contract.
Consider third-party security audits to identify and rectify potential vulnerabilities.
Update Documentation:
Clearly document the correct usage of the adjustRatePerSecond
function, including any limitations or expected behaviors.
Provide guidelines on the implications of adjusting rates without time advancement.
By addressing this vulnerability, the SablierFlow
contract will ensure accurate debt calculations, maintain financial integrity, and prevent potential exploitation arising from incorrect snapshot debt accumulation.
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.