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.