function test_withdraw_Unbearable1() external {
uint128 rps = 0.000000000002040833e18;
vm.warp(OCT\_1\_2024);
uint256 streamId = flow\.createAndDeposit(users.sender, users.recipient, ud21x18(rps), usdc, true, 0.001e6);
uint40 initialSnapshotTime = OCT\_1\_2024;
vm.warp(initialSnapshotTime + 86\_400 seconds);
console2.log(
"the amount of wei this stream have accrued at timestamp 86\_400(one day of actively streaming is)",
getDescaledAmount(flow\.ongoingDebtScaledOf(streamId), flow\.getTokenDecimals(streamId))
);
vm.warp(initialSnapshotTime + 129\_600 seconds);
console2.log(
"the amount of wei this stream have accrued at timestamp 129\_600(one and half day of actively streaming is)",
getDescaledAmount(flow\.ongoingDebtScaledOf(streamId), flow\.getTokenDecimals(streamId))
);
vm.warp(initialSnapshotTime + 172\_800);
console2.log(
"the amount of wei this stream have accrued at timestamp 172800(two days of actively streaming is)",
getDescaledAmount(flow\.ongoingDebtScaledOf(streamId), flow\.getTokenDecimals(streamId))
);
vm.warp(initialSnapshotTime + 604\_800 seconds);
console2.log(
"the amount of wei this stream have accrued at timestamp 604\_800(one week of actively streaming is)",
getDescaledAmount(flow\.ongoingDebtScaledOf(streamId), flow\.getTokenDecimals(streamId))
);
vm.warp(initialSnapshotTime + 950\_400 seconds);
console2.log(
"the amount of wei this stream have accrued at timestamp 950\_400(one week and four days of actively streaming is)",
getDescaledAmount(flow\.ongoingDebtScaledOf(streamId), flow\.getTokenDecimals(streamId))
);
vm.warp(initialSnapshotTime + 1\_036\_800 seconds);
console2.log(
"the amount of wei this stream have accrued at timestamp 1\_036\_800(one week and five days of actively streaming is)",
getDescaledAmount(flow\.ongoingDebtScaledOf(streamId), flow\.getTokenDecimals(streamId))
);
}
[⠒] Compiling...
[⠘] Compiling 1 files with Solc 0.8.26
[⠃] Solc 0.8.26 finished in 5.75s
Compiler run successful!
Ran 1 test for tests/integration/concrete/withdraw-delay/withdrawDelay.t.sol:WithdrawDelay_Integration_Concrete_Test
[PASS] test_withdraw_Unbearable() (gas: 219855)
Logs:
the amount of wei this stream has accrued at timestamp 86_400(one day of actively streaming is) 0
the amount of wei this stream has accrued at timestamp 129_600(one and half day of actively streaming is) 0
the amount of wei this stream has accrued at timestamp 172800(two days of actively streaming is) 0
the amount of wei this stream has accrued at timestamp 604_800(one week of actively streaming is) 1
the amount of wei this stream has accrued at timestamp 950_400(one week and four days of actively streaming is) 1
the amount of wei this stream has accrued at timestamp 1_036_800(one week and five days of actively streaming is) 2
Traces:
[219855] WithdrawDelay_Integration_Concrete_Test::test_withdraw_Unbearable()
├─ [0] VM::warp(1727740800 [1.727e9])
│ └─ ← [Return]
├─ [173102] Flow::createAndDeposit(sender: [0xCD1722F3947DEf4Cf144679Da39c4c32BDC35681], recipient: [0x006217c47ffA5Eb3F3c92247ffFE22AD998242c5], 2040833 [2.04e6], USDC: [0xa0Cb889
707d426A7A386870A03bc70d1b0697598], true, 1000) │ ├─ [205] USDC::decimals() [staticcall]
│ │ └─ ← [Return] 6
│ ├─ emit Transfer(from: 0x0000000000000000000000000000000000000000, to: recipient: [0x006217c47ffA5Eb3F3c92247ffFE22AD998242c5], tokenId: 2)
│ ├─ emit MetadataUpdate(_tokenId: 2)
│ ├─ emit CreateFlowStream(streamId: 2, sender: sender: [0xCD1722F3947DEf4Cf144679Da39c4c32BDC35681], recipient: recipient: [0x006217c47ffA5Eb3F3c92247ffFE22AD998242c5], ratePerS
econd: 2040833 [2.04e6], token: USDC: [0xa0Cb889707d426A7A386870A03bc70d1b0697598], transferable: true) │ ├─ [32418] USDC::transferFrom(sender: [0xCD1722F3947DEf4Cf144679Da39c4c32BDC35681], Flow: [0x2e234DAe75C793f67A35089C9d99245E1C58470b], 1000)
│ │ ├─ emit Transfer(from: sender: [0xCD1722F3947DEf4Cf144679Da39c4c32BDC35681], to: Flow: [0x2e234DAe75C793f67A35089C9d99245E1C58470b], value: 1000)
│ │ └─ ← [Return] true
│ ├─ emit DepositFlowStream(streamId: 2, funder: sender: [0xCD1722F3947DEf4Cf144679Da39c4c32BDC35681], amount: 1000)
│ └─ ← [Return] 2
├─ [0] VM::warp(1727827200 [1.727e9])
│ └─ ← [Return]
├─ [1161] Flow::ongoingDebtScaledOf(2) [staticcall]
│ └─ ← [Return] 176327971200 [1.763e11]
├─ [796] Flow::getTokenDecimals(2) [staticcall]
│ └─ ← [Return] 6
├─ [0] console::log("the amount of wei this stream have accrued at timestamp 86_400(one day of actively streaming is)", 0) [staticcall]
│ └─ ← [Stop]
├─ [0] VM::warp(1727870400 [1.727e9])
│ └─ ← [Return]
├─ [1161] Flow::ongoingDebtScaledOf(2) [staticcall]
│ └─ ← [Return] 264491956800 [2.644e11]
├─ [796] Flow::getTokenDecimals(2) [staticcall]
│ └─ ← [Return] 6
├─ [0] console::log("the amount of wei this stream have accrued at timestamp 129_600(one and half day of actively streaming is)", 0) [staticcall]
│ └─ ← [Stop]
├─ [0] VM::warp(1727913600 [1.727e9])
│ └─ ← [Return]
├─ [1161] Flow::ongoingDebtScaledOf(2) [staticcall]
│ └─ ← [Return] 352655942400 [3.526e11]
├─ [796] Flow::getTokenDecimals(2) [staticcall]
│ └─ ← [Return] 6
├─ [0] console::log("the amount of wei this stream have accrued at timestamp 172800(two days of actively streaming is)", 0) [staticcall]
│ └─ ← [Stop]
├─ [0] VM::warp(1728345600 [1.728e9])
│ └─ ← [Return]
├─ [1161] Flow::ongoingDebtScaledOf(2) [staticcall]
│ └─ ← [Return] 1234295798400 [1.234e12]
├─ [796] Flow::getTokenDecimals(2) [staticcall]
│ └─ ← [Return] 6
├─ [0] console::log("the amount of wei this stream have accrued at timestamp 604_800(one week of actively streaming is)", 1) [staticcall]
│ └─ ← [Stop]
├─ [0] VM::warp(1728691200 [1.728e9])
│ └─ ← [Return]
├─ [1161] Flow::ongoingDebtScaledOf(2) [staticcall]
│ └─ ← [Return] 1939607683200 [1.939e12]
├─ [796] Flow::getTokenDecimals(2) [staticcall]
│ └─ ← [Return] 6
├─ [0] console::log("the amount of wei this stream have accrued at timestamp 950_400(one week and four days of actively streaming is)", 1) [staticcall]
│ └─ ← [Stop]
├─ [0] VM::warp(1728777600 [1.728e9])
│ └─ ← [Return]
├─ [1161] Flow::ongoingDebtScaledOf(2) [staticcall]
│ └─ ← [Return] 2115935654400 [2.115e12]
├─ [796] Flow::getTokenDecimals(2) [staticcall]
│ └─ ← [Return] 6
├─ [0] console::log("the amount of wei this stream have accrued at timestamp 1_036_800(one week and five days of actively streaming is)", 2) [staticcall]
│ └─ ← [Stop]
└─ ← [Stop]
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 38.44ms (4.56ms CPU time)
Ran 1 test suite in 1.95s (38.44ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)
From the test output above, we can clearly see how discrete the stream is. The stream was only able to accrue 1 wei of the token it is streaming after one week of actively streaming, and it was only able to accrue 2 wei of the token it is streaming after one week and five days of actively streaming. Now that we have seen how much delay there can be for a low decimal and low rps stream to increase its accrued amount from 1 wei to 2 wei, I think it is time to understand the vulnerability I am pointing out. If, for any reason, there is anyone who does not want the recipient of this stream to receive the 2 wei from the stream at a week and five days of the stream actively streaming, they just need to call the withdraw function on the SablierFlow
contract at a week and four days of streaming, and that will withdraw only 1 wei to the recipient who has been waiting all along to withdraw their 2 wei the next day. This will be really annoying and frustrating to the recipient. You can argue that the user doesn't lose any funds and can still withdraw another 1 wei to complete the 2 wei, and that is correct, but the problem is the recipient will now have to wait for a whole week to get the 1 wei which they would have gotten the next day if no one had called the withdraw function on the SablierFlow
contract on their behalf.
This vulnerability leads to the recipient having to wait unnecessarily to withdraw their desired amount from the stream.