-
Notifications
You must be signed in to change notification settings - Fork 403
/
RateLimited.t.sol
152 lines (126 loc) · 5.11 KB
/
RateLimited.t.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
// SPDX-License-Identifier: MIT or Apache-2.0
pragma solidity ^0.8.13;
import {Test} from "forge-std/Test.sol";
import {RateLimited} from "../../contracts/libs/RateLimited.sol";
contract RateLimitLibTest is Test {
RateLimited rateLimited;
uint256 constant MAX_CAPACITY = 1 ether;
uint256 constant ONE_PERCENT = 0.01 ether; // Used for assertApproxEqRel
address HOOK = makeAddr("HOOK");
function setUp() public {
rateLimited = new RateLimited(MAX_CAPACITY);
}
function testConstructor_revertsWhen_lowCapacity() public {
vm.expectRevert("Capacity must be greater than DURATION");
new RateLimited(1 days - 1);
}
function testRateLimited_setsNewLimit() external {
assert(rateLimited.setRefillRate(2 ether) > 0);
assertApproxEqRel(rateLimited.maxCapacity(), 2 ether, ONE_PERCENT);
assertEq(rateLimited.refillRate(), uint256(2 ether) / 1 days); // 2 ether / 1 day
}
function testRateLimited_revertsIfMaxNotSet() external {
rateLimited.setRefillRate(0);
vm.expectRevert();
rateLimited.calculateCurrentLevel();
}
function testRateLimited_returnsCurrentFilledLevel_anyDay(
uint40 time
) external {
bound(time, 1 days, 2 days);
vm.warp(time);
// Using approx because division won't be exact
assertApproxEqRel(
rateLimited.calculateCurrentLevel(),
MAX_CAPACITY,
ONE_PERCENT
);
}
function testRateLimited_onlyOwnerCanSetTargetLimit() external {
vm.prank(address(0));
vm.expectRevert();
rateLimited.setRefillRate(1 ether);
}
function testConsumedFilledLevelEvent() public {
uint256 consumeAmount = 0.5 ether;
vm.expectEmit(true, true, false, true);
emit RateLimited.ConsumedFilledLevel(
499999999999993600,
block.timestamp
); // precision loss
rateLimited.validateAndConsumeFilledLevel(consumeAmount);
assertApproxEqRelDecimal(
rateLimited.filledLevel(),
MAX_CAPACITY - consumeAmount,
1e14,
0
);
assertEq(rateLimited.lastUpdated(), block.timestamp);
}
function testRateLimited_neverReturnsGtMaxLimit(
uint256 _newAmount,
uint40 _newTime
) external {
vm.warp(_newTime);
vm.assume(_newAmount <= rateLimited.calculateCurrentLevel());
rateLimited.validateAndConsumeFilledLevel(_newAmount);
assertLe(
rateLimited.calculateCurrentLevel(),
rateLimited.maxCapacity()
);
}
function testRateLimited_decreasesLimitWithinSameDay() external {
vm.warp(1 days);
uint256 currentTargetLimit = rateLimited.calculateCurrentLevel();
uint256 amount = 0.4 ether;
uint256 newLimit = rateLimited.validateAndConsumeFilledLevel(amount);
assertEq(newLimit, currentTargetLimit - amount);
// Consume the same amount
currentTargetLimit = rateLimited.calculateCurrentLevel();
newLimit = rateLimited.validateAndConsumeFilledLevel(amount);
assertEq(newLimit, currentTargetLimit - amount);
// One more to exceed limit
vm.expectRevert();
rateLimited.validateAndConsumeFilledLevel(amount);
}
function testRateLimited_replinishesWithinSameDay() external {
vm.warp(1 days);
uint256 amount = 0.95 ether;
uint256 newLimit = rateLimited.validateAndConsumeFilledLevel(amount);
uint256 currentTargetLimit = rateLimited.calculateCurrentLevel();
assertApproxEqRel(currentTargetLimit, 0.05 ether, ONE_PERCENT);
// Warp to near end-of-day
vm.warp(block.timestamp + 0.99 days);
newLimit = rateLimited.validateAndConsumeFilledLevel(amount);
assertApproxEqRel(newLimit, 0.05 ether, ONE_PERCENT);
}
function testRateLimited_shouldResetLimit_ifDurationExceeds(
uint256 _amount
) external {
// Transfer less than the limit
vm.warp(0.5 days);
uint256 currentTargetLimit = rateLimited.calculateCurrentLevel();
vm.assume(_amount < currentTargetLimit);
uint256 newLimit = rateLimited.validateAndConsumeFilledLevel(_amount);
assertApproxEqRel(newLimit, currentTargetLimit - _amount, ONE_PERCENT);
// Warp to a new cycle
vm.warp(10 days);
currentTargetLimit = rateLimited.calculateCurrentLevel();
assertApproxEqRel(currentTargetLimit, MAX_CAPACITY, ONE_PERCENT);
}
function testCalculateCurrentLevel_revertsWhenCapacityIsZero() public {
rateLimited.setRefillRate(0);
vm.expectRevert("RateLimitNotSet");
rateLimited.calculateCurrentLevel();
}
function testValidateAndConsumeFilledLevel_revertsWhenExceedingLimit()
public
{
vm.warp(1 days);
uint256 initialLevel = rateLimited.calculateCurrentLevel();
uint256 excessAmount = initialLevel + 1 ether;
vm.expectRevert("RateLimitExceeded");
rateLimited.validateAndConsumeFilledLevel(excessAmount);
assertEq(rateLimited.calculateCurrentLevel(), initialLevel);
}
}