Skip to content

Commit 3d2602a

Browse files
test(fuzz): test delay and deviations bounds (#258)
* test(fuzz): test delay and deviations bounds * test: polish withdraw multiple test * chore: rename test file --------- Co-authored-by: andreivladbrg <andreivladbrg@gmail.com>
1 parent 6f59341 commit 3d2602a

File tree

2 files changed

+68
-0
lines changed

2 files changed

+68
-0
lines changed
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
pragma solidity >=0.8.22;
3+
4+
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
5+
import { ud21x18 } from "@prb/math/src/UD21x18.sol";
6+
7+
import { Shared_Integration_Fuzz_Test } from "./Fuzz.t.sol";
8+
9+
contract WithdrawMultiple_Delay_Fuzz_Test is Shared_Integration_Fuzz_Test {
10+
/// @dev Checklist:
11+
/// - It should test multiple withdrawals from the stream.
12+
/// - It should assert that the actual amount withdrawn is less than the desired amount.
13+
/// - It should check that stream delay and deviation are within acceptable limits for realistic values of rps.
14+
///
15+
/// Given enough runs, all of the following scenarios should be fuzzed for USDC:
16+
/// - Multiple values for realistic rps.
17+
/// - Multiple withdrawal counts on the same stream at multiple points in time.
18+
function testFuzz_WithdrawMultiple_Delay(uint128 rps, uint256 withdrawCount, uint40 timeJump) external {
19+
// Bound the rps to a reasonable range [$100/month, $1000/month].
20+
rps = boundUint128(rps, 38_580_246_913_580, 385_802_469_135_800);
21+
22+
IERC20 token = createToken(DECIMALS);
23+
uint256 streamId = createDefaultStream(ud21x18(rps), token);
24+
25+
withdrawCount = _bound(withdrawCount, 10, 100);
26+
27+
// Deposit the sufficient amount.
28+
uint128 sufficientDepositAmount = uint128(rps * 1 days * withdrawCount / SCALE_FACTOR);
29+
deposit(streamId, sufficientDepositAmount);
30+
31+
// Actual total amount withdrawn in a given run.
32+
uint256 actualTotalAmountWithdrawn;
33+
34+
uint40 timeBeforeFirstWithdraw = getBlockTimestamp();
35+
36+
for (uint256 i; i < withdrawCount; ++i) {
37+
timeJump = boundUint40(timeJump, 1 hours, 1 days);
38+
39+
// Warp the time.
40+
vm.warp({ newTimestamp: getBlockTimestamp() + timeJump });
41+
42+
// Withdraw the tokens.
43+
actualTotalAmountWithdrawn += flow.withdrawMax(streamId, users.recipient);
44+
}
45+
46+
// Calculate the total stream period.
47+
uint40 totalStreamPeriod = getBlockTimestamp() - timeBeforeFirstWithdraw;
48+
49+
// Calculate the desired amount.
50+
uint256 desiredTotalAmountWithdrawn = (rps * totalStreamPeriod) / SCALE_FACTOR;
51+
52+
// Calculate the deviation.
53+
uint256 deviationAmount = desiredTotalAmountWithdrawn - actualTotalAmountWithdrawn;
54+
55+
// Calculate the stream delay.
56+
uint256 streamDelay = (deviationAmount * SCALE_FACTOR) / rps;
57+
58+
// Assert that the stream delay is within 5 second for the given fuzzed rps.
59+
assertLe(streamDelay, 5 seconds);
60+
61+
// Assert that the deviation is less than 0.01e6 USDC.
62+
assertLe(deviationAmount, 0.01e6);
63+
64+
// Assert that actual amount withdrawn is always less than the desired amount.
65+
assertLe(actualTotalAmountWithdrawn, desiredTotalAmountWithdrawn);
66+
}
67+
}

tests/utils/Constants.sol

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ abstract contract Constants {
3333
uint8 internal constant DECIMALS = 6;
3434
UD21x18 internal constant RATE_PER_SECOND = UD21x18.wrap(0.001e18); // 86.4 daily
3535
uint128 internal constant RATE_PER_SECOND_U128 = 0.001e18; // 86.4 daily
36+
uint256 internal constant SCALE_FACTOR = 10 ** 12;
3637
bool internal constant TRANSFERABLE = true;
3738

3839
// Streaming amounts

0 commit comments

Comments
 (0)