@layerzerolabs/utils-upgradeable-evm-contracts 0.2.74
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-lint.log +491 -0
- package/.turbo/turbo-test.log +1356 -0
- package/LICENSE +23 -0
- package/contracts/access/AccessControl2StepUpgradeable.sol +144 -0
- package/contracts/allowlist/AllowlistBaseUpgradeable.sol +184 -0
- package/contracts/allowlist/AllowlistRBACUpgradeable.sol +55 -0
- package/contracts/fee-accounting/FeeHandlerBaseUpgradeable.sol +69 -0
- package/contracts/fee-accounting/FeeHandlerRBACUpgradeable.sol +34 -0
- package/contracts/fee-config/FeeConfigBaseUpgradeable.sol +128 -0
- package/contracts/fee-config/FeeConfigRBACUpgradeable.sol +44 -0
- package/contracts/libs/EnumerableSetPagination.sol +77 -0
- package/contracts/pause/PauseBaseUpgradeable.sol +107 -0
- package/contracts/pause/PauseRBACUpgradeable.sol +47 -0
- package/contracts/pause-by-id/PauseByIDBaseUpgradeable.sol +127 -0
- package/contracts/pause-by-id/PauseByIDRBACUpgradeable.sol +74 -0
- package/contracts/rate-limiter/RateLimiterBaseUpgradeable.sol +636 -0
- package/contracts/rate-limiter/RateLimiterRBACUpgradeable.sol +89 -0
- package/contracts/rate-limiter/libs/RateLimiterUtils.sol +82 -0
- package/foundry.toml +27 -0
- package/package.json +34 -0
- package/solhint.config.js +3 -0
- package/test/AccessControl2StepUpgradeable.t.sol +374 -0
- package/test/AllowlistBaseUpgradeable.t.sol +380 -0
- package/test/AllowlistRBACUpgradeable.t.sol +84 -0
- package/test/EnumerableSetPagination.t.sol +445 -0
- package/test/FeeConfigBaseUpgradeable.t.sol +232 -0
- package/test/FeeConfigRBACUpgradeable.t.sol +59 -0
- package/test/FeeHandlerBaseUpgradeable.t.sol +78 -0
- package/test/FeeHandlerRBACUpgradeable.t.sol +45 -0
- package/test/PauseBaseUpgradeable.t.sol +244 -0
- package/test/PauseByIDBaseUpgradeable.t.sol +337 -0
- package/test/PauseByIDRBACUpgradeable.t.sol +414 -0
- package/test/PauseRBACUpgradeable.t.sol +258 -0
- package/test/RateLimiterBaseUpgradeable.t.sol +826 -0
- package/test/RateLimiterRBACUpgradeable.t.sol +128 -0
- package/test/RateLimiterRBACUpgradeableInvariant.t.sol +444 -0
- package/test/RateLimiterUtils.t.sol +137 -0
|
@@ -0,0 +1,826 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.22;
|
|
3
|
+
|
|
4
|
+
import { IRateLimiter } from "@layerzerolabs/utils-evm-contracts/contracts/interfaces/IRateLimiter.sol";
|
|
5
|
+
import { TransparentUpgradeableProxy } from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
|
|
6
|
+
import { Test } from "forge-std/Test.sol";
|
|
7
|
+
import { RateLimiterBaseUpgradeable } from "./../contracts/rate-limiter/RateLimiterBaseUpgradeable.sol";
|
|
8
|
+
|
|
9
|
+
contract RateLimiterBaseUpgradeableMock is RateLimiterBaseUpgradeable {
|
|
10
|
+
constructor(uint8 _scaleDecimals) RateLimiterBaseUpgradeable(_scaleDecimals) {
|
|
11
|
+
_disableInitializers();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function initialize(bool _useGlobalState) public initializer {
|
|
15
|
+
__RateLimiterBase_init(_useGlobalState);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function setRateLimitGlobalConfig(IRateLimiter.RateLimitGlobalConfig memory _globalConfig) public {
|
|
19
|
+
_setRateLimitGlobalConfig(_globalConfig);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function setRateLimitConfigs(IRateLimiter.SetRateLimitConfigParam[] calldata _configs) public {
|
|
23
|
+
_setRateLimitConfigs(_configs);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function setRateLimitStates(IRateLimiter.SetRateLimitStateParam[] calldata _states) public {
|
|
27
|
+
_setRateLimitStates(_states);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function setRateLimitAddressExemptions(
|
|
31
|
+
IRateLimiter.SetRateLimitAddressExemptionParam[] calldata _exemptions
|
|
32
|
+
) public {
|
|
33
|
+
_setRateLimitAddressExemptions(_exemptions);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function checkpointRateLimits(uint256[] calldata _ids) public {
|
|
37
|
+
_checkpointRateLimits(_ids);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function outflow(uint256 _id, address _from, uint256 _amount) external {
|
|
41
|
+
_outflow(_id, _from, _amount);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function inflow(uint256 _id, address _to, uint256 _amount) external {
|
|
45
|
+
_inflow(_id, _to, _amount);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/// @dev Helper to bypass external visibility for permutation loop (try/catch needs external).
|
|
49
|
+
function runPermutationWrapper(
|
|
50
|
+
bool useDefaultConfig,
|
|
51
|
+
bool useGlobalState,
|
|
52
|
+
bool outboundEnabled,
|
|
53
|
+
bool inboundEnabled,
|
|
54
|
+
bool netAccounting,
|
|
55
|
+
bool isExempt,
|
|
56
|
+
bool isOutflow
|
|
57
|
+
) external {}
|
|
58
|
+
|
|
59
|
+
function getRateLimitUsage(
|
|
60
|
+
uint40 _lastUpdated,
|
|
61
|
+
uint96 _amountInFlight,
|
|
62
|
+
uint96 _limit,
|
|
63
|
+
uint32 _window
|
|
64
|
+
) external view returns (uint256 currentUsage, uint256 availableAmount) {
|
|
65
|
+
return _getRateLimitUsage(_lastUpdated, _amountInFlight, _limit, _window);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function upscaleRateLimitAmount(uint256 _amount) external view returns (uint256) {
|
|
69
|
+
return _upscaleRateLimitAmount(_amount);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
contract RateLimiterBaseUpgradeableTest is Test {
|
|
74
|
+
RateLimiterBaseUpgradeableMock rateLimiter;
|
|
75
|
+
/// @dev Separate instance for global state tests to avoid warm storage overlap or conflict.
|
|
76
|
+
RateLimiterBaseUpgradeableMock rateLimiterGlobal;
|
|
77
|
+
/// @dev Separate instances for conflicting default config permutations.
|
|
78
|
+
RateLimiterBaseUpgradeableMock rateLimiterDefaultNet;
|
|
79
|
+
RateLimiterBaseUpgradeableMock rateLimiterDefaultDisabled;
|
|
80
|
+
|
|
81
|
+
uint256 constant ID_1 = uint256(keccak256("ID_1"));
|
|
82
|
+
uint256 constant ID_2 = uint256(keccak256("ID_2"));
|
|
83
|
+
uint256 constant DEFAULT_ID = 0;
|
|
84
|
+
|
|
85
|
+
address alice = makeAddr("alice");
|
|
86
|
+
address bob = makeAddr("bob");
|
|
87
|
+
|
|
88
|
+
uint256 constant ID_SNAP_DEFAULT_OUTBOUND_GROSS = uint256(keccak256("ID_SNAP_DEFAULT_OUTBOUND_GROSS"));
|
|
89
|
+
uint256 constant ID_SNAP_DEFAULT_INBOUND_GROSS = uint256(keccak256("ID_SNAP_DEFAULT_INBOUND_GROSS"));
|
|
90
|
+
uint256 constant ID_SNAP_DEFAULT_BOTH_NET_OUT = uint256(keccak256("ID_SNAP_DEFAULT_BOTH_NET_OUT"));
|
|
91
|
+
uint256 constant ID_SNAP_DEFAULT_BOTH_NET_IN = uint256(keccak256("ID_SNAP_DEFAULT_BOTH_NET_IN"));
|
|
92
|
+
uint256 constant ID_SNAP_DEFAULT_DISABLED = uint256(keccak256("ID_SNAP_DEFAULT_DISABLED"));
|
|
93
|
+
|
|
94
|
+
uint256 constant ID_SNAP_EXPLICIT_OUTBOUND_GROSS = uint256(keccak256("ID_SNAP_EXPLICIT_OUTBOUND_GROSS"));
|
|
95
|
+
uint256 constant ID_SNAP_EXPLICIT_INBOUND_GROSS = uint256(keccak256("ID_SNAP_EXPLICIT_INBOUND_GROSS"));
|
|
96
|
+
uint256 constant ID_SNAP_EXPLICIT_BOTH_NET_OUT = uint256(keccak256("ID_SNAP_EXPLICIT_BOTH_NET_OUT"));
|
|
97
|
+
uint256 constant ID_SNAP_EXPLICIT_BOTH_NET_IN = uint256(keccak256("ID_SNAP_EXPLICIT_BOTH_NET_IN"));
|
|
98
|
+
uint256 constant ID_SNAP_EXPLICIT_BOTH_GROSS_EXEMPT = uint256(keccak256("ID_SNAP_EXPLICIT_BOTH_GROSS_EXEMPT"));
|
|
99
|
+
uint256 constant ID_SNAP_GLOBAL_OUTBOUND_GROSS = uint256(keccak256("ID_SNAP_GLOBAL_OUTBOUND_GROSS"));
|
|
100
|
+
uint256 constant ID_SNAP_DISABLED = uint256(keccak256("ID_SNAP_DISABLED"));
|
|
101
|
+
|
|
102
|
+
struct PermutationParams {
|
|
103
|
+
/// @dev If true, `id` has `override=false`, config is on Default ID (only relevant if `useGlobalState=false`).
|
|
104
|
+
bool useDefaultConfig;
|
|
105
|
+
/// @dev If true, enable Global State flag.
|
|
106
|
+
bool useGlobalState;
|
|
107
|
+
bool outboundEnabled;
|
|
108
|
+
bool inboundEnabled;
|
|
109
|
+
bool netAccounting;
|
|
110
|
+
bool isExempt;
|
|
111
|
+
/// @dev True = Outflow, False = Inflow.
|
|
112
|
+
bool isOutflow;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function setUp() public virtual {
|
|
116
|
+
rateLimiter = _createRateLimiter(0, false);
|
|
117
|
+
rateLimiterGlobal = _createRateLimiter(0, true);
|
|
118
|
+
rateLimiterDefaultNet = _createRateLimiter(0, false);
|
|
119
|
+
rateLimiterDefaultDisabled = _createRateLimiter(0, false);
|
|
120
|
+
|
|
121
|
+
_setUpConfigs();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function _createRateLimiter(
|
|
125
|
+
uint8 _scaleDecimals,
|
|
126
|
+
bool _useGlobalState
|
|
127
|
+
) internal virtual returns (RateLimiterBaseUpgradeableMock) {
|
|
128
|
+
RateLimiterBaseUpgradeableMock impl = new RateLimiterBaseUpgradeableMock(_scaleDecimals);
|
|
129
|
+
TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy(
|
|
130
|
+
address(impl),
|
|
131
|
+
address(this),
|
|
132
|
+
abi.encodeWithSelector(RateLimiterBaseUpgradeableMock.initialize.selector, _useGlobalState, address(this))
|
|
133
|
+
);
|
|
134
|
+
return RateLimiterBaseUpgradeableMock(address(proxy));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function _setUpConfigs() internal {
|
|
138
|
+
uint96 limit = 1000;
|
|
139
|
+
uint32 window = 1000;
|
|
140
|
+
|
|
141
|
+
IRateLimiter.SetRateLimitConfigParam[] memory configs = new IRateLimiter.SetRateLimitConfigParam[](2);
|
|
142
|
+
configs[0] = _createConfig(DEFAULT_ID, false, true, true, false, false, limit, limit, window, window);
|
|
143
|
+
configs[1] = _createConfig(ID_SNAP_DEFAULT_OUTBOUND_GROSS, false, false, false, false, false, 0, 0, 0, 0);
|
|
144
|
+
rateLimiter.setRateLimitConfigs(configs);
|
|
145
|
+
|
|
146
|
+
configs = new IRateLimiter.SetRateLimitConfigParam[](1);
|
|
147
|
+
configs[0] = _createConfig(ID_SNAP_DEFAULT_INBOUND_GROSS, false, false, false, false, false, 0, 0, 0, 0);
|
|
148
|
+
rateLimiter.setRateLimitConfigs(configs);
|
|
149
|
+
|
|
150
|
+
configs = new IRateLimiter.SetRateLimitConfigParam[](3);
|
|
151
|
+
configs[0] = _createConfig(DEFAULT_ID, false, true, true, true, false, limit, limit, window, window);
|
|
152
|
+
configs[1] = _createConfig(ID_SNAP_DEFAULT_BOTH_NET_OUT, false, false, false, false, false, 0, 0, 0, 0);
|
|
153
|
+
configs[2] = _createConfig(ID_SNAP_DEFAULT_BOTH_NET_IN, false, false, false, false, false, 0, 0, 0, 0);
|
|
154
|
+
rateLimiterDefaultNet.setRateLimitConfigs(configs);
|
|
155
|
+
|
|
156
|
+
IRateLimiter.SetRateLimitStateParam[] memory netStates = new IRateLimiter.SetRateLimitStateParam[](1);
|
|
157
|
+
netStates[0] = _createState(ID_SNAP_DEFAULT_BOTH_NET_OUT, 0, 50, uint40(block.timestamp));
|
|
158
|
+
rateLimiterDefaultNet.setRateLimitStates(netStates);
|
|
159
|
+
netStates[0] = _createState(ID_SNAP_DEFAULT_BOTH_NET_IN, 50, 0, uint40(block.timestamp));
|
|
160
|
+
rateLimiterDefaultNet.setRateLimitStates(netStates);
|
|
161
|
+
|
|
162
|
+
configs = new IRateLimiter.SetRateLimitConfigParam[](2);
|
|
163
|
+
configs[0] = _createConfig(DEFAULT_ID, false, false, false, false, false, limit, limit, window, window);
|
|
164
|
+
configs[1] = _createConfig(ID_SNAP_DEFAULT_DISABLED, false, false, false, false, false, 0, 0, 0, 0);
|
|
165
|
+
rateLimiterDefaultDisabled.setRateLimitConfigs(configs);
|
|
166
|
+
|
|
167
|
+
configs = new IRateLimiter.SetRateLimitConfigParam[](1);
|
|
168
|
+
configs[0] = _createConfig(
|
|
169
|
+
ID_SNAP_EXPLICIT_OUTBOUND_GROSS,
|
|
170
|
+
true,
|
|
171
|
+
true,
|
|
172
|
+
false,
|
|
173
|
+
false,
|
|
174
|
+
false,
|
|
175
|
+
limit,
|
|
176
|
+
limit,
|
|
177
|
+
window,
|
|
178
|
+
window
|
|
179
|
+
);
|
|
180
|
+
rateLimiter.setRateLimitConfigs(configs);
|
|
181
|
+
|
|
182
|
+
configs[0] = _createConfig(
|
|
183
|
+
ID_SNAP_EXPLICIT_INBOUND_GROSS,
|
|
184
|
+
true,
|
|
185
|
+
false,
|
|
186
|
+
true,
|
|
187
|
+
false,
|
|
188
|
+
false,
|
|
189
|
+
limit,
|
|
190
|
+
limit,
|
|
191
|
+
window,
|
|
192
|
+
window
|
|
193
|
+
);
|
|
194
|
+
rateLimiter.setRateLimitConfigs(configs);
|
|
195
|
+
|
|
196
|
+
configs[0] = _createConfig(
|
|
197
|
+
ID_SNAP_EXPLICIT_BOTH_NET_OUT,
|
|
198
|
+
true,
|
|
199
|
+
true,
|
|
200
|
+
true,
|
|
201
|
+
true,
|
|
202
|
+
false,
|
|
203
|
+
limit,
|
|
204
|
+
limit,
|
|
205
|
+
window,
|
|
206
|
+
window
|
|
207
|
+
);
|
|
208
|
+
rateLimiter.setRateLimitConfigs(configs);
|
|
209
|
+
IRateLimiter.SetRateLimitStateParam[] memory states = new IRateLimiter.SetRateLimitStateParam[](1);
|
|
210
|
+
states[0] = _createState(ID_SNAP_EXPLICIT_BOTH_NET_OUT, 0, 50, uint40(block.timestamp));
|
|
211
|
+
rateLimiter.setRateLimitStates(states);
|
|
212
|
+
|
|
213
|
+
configs[0] = _createConfig(
|
|
214
|
+
ID_SNAP_EXPLICIT_BOTH_NET_IN,
|
|
215
|
+
true,
|
|
216
|
+
true,
|
|
217
|
+
true,
|
|
218
|
+
true,
|
|
219
|
+
false,
|
|
220
|
+
limit,
|
|
221
|
+
limit,
|
|
222
|
+
window,
|
|
223
|
+
window
|
|
224
|
+
);
|
|
225
|
+
rateLimiter.setRateLimitConfigs(configs);
|
|
226
|
+
states[0] = _createState(ID_SNAP_EXPLICIT_BOTH_NET_IN, 50, 0, uint40(block.timestamp));
|
|
227
|
+
rateLimiter.setRateLimitStates(states);
|
|
228
|
+
|
|
229
|
+
configs[0] = _createConfig(
|
|
230
|
+
ID_SNAP_EXPLICIT_BOTH_GROSS_EXEMPT,
|
|
231
|
+
true,
|
|
232
|
+
true,
|
|
233
|
+
true,
|
|
234
|
+
false,
|
|
235
|
+
true,
|
|
236
|
+
limit,
|
|
237
|
+
limit,
|
|
238
|
+
window,
|
|
239
|
+
window
|
|
240
|
+
);
|
|
241
|
+
rateLimiter.setRateLimitConfigs(configs);
|
|
242
|
+
IRateLimiter.SetRateLimitAddressExemptionParam[]
|
|
243
|
+
memory exemptions = new IRateLimiter.SetRateLimitAddressExemptionParam[](1);
|
|
244
|
+
exemptions[0] = IRateLimiter.SetRateLimitAddressExemptionParam({ user: alice, isExempt: true });
|
|
245
|
+
rateLimiter.setRateLimitAddressExemptions(exemptions);
|
|
246
|
+
|
|
247
|
+
configs = new IRateLimiter.SetRateLimitConfigParam[](2);
|
|
248
|
+
configs[0] = _createConfig(DEFAULT_ID, false, true, true, false, false, limit, limit, window, window);
|
|
249
|
+
configs[1] = _createConfig(ID_SNAP_GLOBAL_OUTBOUND_GROSS, false, false, false, false, false, 0, 0, 0, 0);
|
|
250
|
+
rateLimiterGlobal.setRateLimitConfigs(configs);
|
|
251
|
+
|
|
252
|
+
configs[0] = _createConfig(ID_SNAP_DISABLED, true, false, true, false, false, limit, limit, window, window);
|
|
253
|
+
rateLimiter.setRateLimitConfigs(configs);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function _createConfig(
|
|
257
|
+
uint256 _id,
|
|
258
|
+
bool _overrideDefaultConfig,
|
|
259
|
+
bool _outboundEnabled,
|
|
260
|
+
bool _inboundEnabled,
|
|
261
|
+
bool _netAccountingEnabled,
|
|
262
|
+
bool _addressExemptionEnabled,
|
|
263
|
+
uint96 _outboundLimit,
|
|
264
|
+
uint96 _inboundLimit,
|
|
265
|
+
uint32 _outboundWindow,
|
|
266
|
+
uint32 _inboundWindow
|
|
267
|
+
) internal pure returns (IRateLimiter.SetRateLimitConfigParam memory) {
|
|
268
|
+
return
|
|
269
|
+
IRateLimiter.SetRateLimitConfigParam({
|
|
270
|
+
id: _id,
|
|
271
|
+
config: IRateLimiter.RateLimitConfig({
|
|
272
|
+
overrideDefaultConfig: _overrideDefaultConfig,
|
|
273
|
+
outboundEnabled: _outboundEnabled,
|
|
274
|
+
inboundEnabled: _inboundEnabled,
|
|
275
|
+
netAccountingEnabled: _netAccountingEnabled,
|
|
276
|
+
addressExemptionEnabled: _addressExemptionEnabled,
|
|
277
|
+
outboundLimit: _outboundLimit,
|
|
278
|
+
inboundLimit: _inboundLimit,
|
|
279
|
+
outboundWindow: _outboundWindow,
|
|
280
|
+
inboundWindow: _inboundWindow
|
|
281
|
+
})
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function _createState(
|
|
286
|
+
uint256 _id,
|
|
287
|
+
uint96 _outboundUsage,
|
|
288
|
+
uint96 _inboundUsage,
|
|
289
|
+
uint40 _lastUpdated
|
|
290
|
+
) internal pure returns (IRateLimiter.SetRateLimitStateParam memory) {
|
|
291
|
+
return
|
|
292
|
+
IRateLimiter.SetRateLimitStateParam({
|
|
293
|
+
id: _id,
|
|
294
|
+
state: IRateLimiter.RateLimitState({
|
|
295
|
+
outboundUsage: _outboundUsage,
|
|
296
|
+
inboundUsage: _inboundUsage,
|
|
297
|
+
lastUpdated: _lastUpdated
|
|
298
|
+
})
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// ============ Constructor / Initialization Tests ============
|
|
303
|
+
|
|
304
|
+
function test_constructor_Revert_InvalidScaledDecimals() public {
|
|
305
|
+
vm.expectRevert(abi.encodeWithSelector(IRateLimiter.InvalidScaledDecimals.selector, 19));
|
|
306
|
+
new RateLimiterBaseUpgradeableMock(19);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function test_constructor_MaxScaledDecimals() public {
|
|
310
|
+
RateLimiterBaseUpgradeableMock impl = new RateLimiterBaseUpgradeableMock(18);
|
|
311
|
+
|
|
312
|
+
assertEq(impl.SCALE_DECIMALS(), 18);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function test_constructor_ZeroScaledDecimals() public {
|
|
316
|
+
RateLimiterBaseUpgradeableMock impl = new RateLimiterBaseUpgradeableMock(0);
|
|
317
|
+
|
|
318
|
+
assertEq(impl.SCALE_DECIMALS(), 0);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// ============ Scaling Tests ============
|
|
322
|
+
|
|
323
|
+
function test_outflow_WithScaleDecimals() public {
|
|
324
|
+
RateLimiterBaseUpgradeableMock scaledLimiter = _createRateLimiter(12, false);
|
|
325
|
+
|
|
326
|
+
uint96 limit = 1_000_000_000;
|
|
327
|
+
uint32 window = 1000;
|
|
328
|
+
|
|
329
|
+
IRateLimiter.SetRateLimitConfigParam[] memory configs = new IRateLimiter.SetRateLimitConfigParam[](1);
|
|
330
|
+
configs[0] = _createConfig(ID_1, true, true, true, true, false, limit, limit, window, window);
|
|
331
|
+
scaledLimiter.setRateLimitConfigs(configs);
|
|
332
|
+
|
|
333
|
+
uint256 scaledAmount = 500 * 1e18;
|
|
334
|
+
|
|
335
|
+
scaledLimiter.outflow(ID_1, alice, scaledAmount);
|
|
336
|
+
|
|
337
|
+
(uint256 outUsage, uint256 outAvail, , ) = scaledLimiter.getRateLimitUsages(ID_1);
|
|
338
|
+
|
|
339
|
+
assertEq(outUsage, scaledAmount);
|
|
340
|
+
assertGt(outAvail, 0);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function test_inflow_WithScaleDecimals() public {
|
|
344
|
+
RateLimiterBaseUpgradeableMock scaledLimiter = _createRateLimiter(12, false);
|
|
345
|
+
|
|
346
|
+
uint96 limit = 1_000_000_000;
|
|
347
|
+
uint32 window = 1000;
|
|
348
|
+
|
|
349
|
+
IRateLimiter.SetRateLimitConfigParam[] memory configs = new IRateLimiter.SetRateLimitConfigParam[](1);
|
|
350
|
+
configs[0] = _createConfig(ID_1, true, true, true, true, false, limit, limit, window, window);
|
|
351
|
+
scaledLimiter.setRateLimitConfigs(configs);
|
|
352
|
+
|
|
353
|
+
uint256 scaledAmount = 300 * 1e18;
|
|
354
|
+
|
|
355
|
+
scaledLimiter.inflow(ID_1, alice, scaledAmount);
|
|
356
|
+
|
|
357
|
+
(, , uint256 inUsage, uint256 inAvail) = scaledLimiter.getRateLimitUsages(ID_1);
|
|
358
|
+
|
|
359
|
+
assertEq(inUsage, scaledAmount);
|
|
360
|
+
assertGt(inAvail, 0);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
function test_upscaleRateLimitAmount() public {
|
|
364
|
+
RateLimiterBaseUpgradeableMock scaledLimiter = _createRateLimiter(12, false);
|
|
365
|
+
|
|
366
|
+
uint256 downscaled = 100;
|
|
367
|
+
uint256 upscaled = scaledLimiter.upscaleRateLimitAmount(downscaled);
|
|
368
|
+
|
|
369
|
+
assertEq(upscaled, 100 * 1e12);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// ============ Global State Tests ============
|
|
373
|
+
|
|
374
|
+
function test_globalState_UsesDefaultIdForState() public {
|
|
375
|
+
RateLimiterBaseUpgradeableMock globalLimiter = _createRateLimiter(0, true);
|
|
376
|
+
|
|
377
|
+
uint96 limit = 1000;
|
|
378
|
+
uint32 window = 1000;
|
|
379
|
+
|
|
380
|
+
IRateLimiter.SetRateLimitConfigParam[] memory configs = new IRateLimiter.SetRateLimitConfigParam[](1);
|
|
381
|
+
configs[0] = _createConfig(DEFAULT_ID, false, true, true, true, false, limit, limit, window, window);
|
|
382
|
+
globalLimiter.setRateLimitConfigs(configs);
|
|
383
|
+
|
|
384
|
+
globalLimiter.outflow(ID_1, alice, 100);
|
|
385
|
+
|
|
386
|
+
IRateLimiter.RateLimit memory defaultState = globalLimiter.rateLimits(DEFAULT_ID);
|
|
387
|
+
IRateLimiter.RateLimit memory id1State = globalLimiter.rateLimits(ID_1);
|
|
388
|
+
|
|
389
|
+
assertEq(defaultState.outboundUsage, 100);
|
|
390
|
+
assertEq(id1State.outboundUsage, 0);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function test_globalState_IgnoresIdSpecificConfig() public {
|
|
394
|
+
RateLimiterBaseUpgradeableMock globalLimiter = _createRateLimiter(0, true);
|
|
395
|
+
|
|
396
|
+
uint96 defaultLimit = 1000;
|
|
397
|
+
uint96 idSpecificLimit = 500;
|
|
398
|
+
uint32 window = 1000;
|
|
399
|
+
|
|
400
|
+
IRateLimiter.SetRateLimitConfigParam[] memory configs = new IRateLimiter.SetRateLimitConfigParam[](2);
|
|
401
|
+
configs[0] = _createConfig(
|
|
402
|
+
DEFAULT_ID,
|
|
403
|
+
false,
|
|
404
|
+
true,
|
|
405
|
+
true,
|
|
406
|
+
true,
|
|
407
|
+
false,
|
|
408
|
+
defaultLimit,
|
|
409
|
+
defaultLimit,
|
|
410
|
+
window,
|
|
411
|
+
window
|
|
412
|
+
);
|
|
413
|
+
configs[1] = _createConfig(
|
|
414
|
+
ID_1,
|
|
415
|
+
true,
|
|
416
|
+
true,
|
|
417
|
+
true,
|
|
418
|
+
true,
|
|
419
|
+
false,
|
|
420
|
+
idSpecificLimit,
|
|
421
|
+
idSpecificLimit,
|
|
422
|
+
window,
|
|
423
|
+
window
|
|
424
|
+
);
|
|
425
|
+
globalLimiter.setRateLimitConfigs(configs);
|
|
426
|
+
|
|
427
|
+
(, uint256 outAvail, , ) = globalLimiter.getRateLimitUsages(ID_1);
|
|
428
|
+
|
|
429
|
+
assertEq(outAvail, defaultLimit);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
function test_globalState_SharesStateAcrossIds() public {
|
|
433
|
+
RateLimiterBaseUpgradeableMock globalLimiter = _createRateLimiter(0, true);
|
|
434
|
+
|
|
435
|
+
uint96 limit = 1000;
|
|
436
|
+
uint32 window = 1000;
|
|
437
|
+
|
|
438
|
+
IRateLimiter.SetRateLimitConfigParam[] memory configs = new IRateLimiter.SetRateLimitConfigParam[](1);
|
|
439
|
+
configs[0] = _createConfig(DEFAULT_ID, false, true, true, true, false, limit, limit, window, window);
|
|
440
|
+
globalLimiter.setRateLimitConfigs(configs);
|
|
441
|
+
|
|
442
|
+
globalLimiter.outflow(ID_1, alice, 100);
|
|
443
|
+
globalLimiter.outflow(ID_2, alice, 200);
|
|
444
|
+
|
|
445
|
+
(uint256 id1OutUsage, , , ) = globalLimiter.getRateLimitUsages(ID_1);
|
|
446
|
+
(uint256 id2OutUsage, , , ) = globalLimiter.getRateLimitUsages(ID_2);
|
|
447
|
+
|
|
448
|
+
assertEq(id1OutUsage, 300);
|
|
449
|
+
assertEq(id2OutUsage, 300);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// ============ setRateLimitGlobalConfig Tests ============
|
|
453
|
+
|
|
454
|
+
function test_setRateLimitGlobalConfig() public {
|
|
455
|
+
IRateLimiter.RateLimitGlobalConfig memory config = IRateLimiter.RateLimitGlobalConfig({
|
|
456
|
+
useGlobalState: true,
|
|
457
|
+
isGloballyDisabled: false
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
vm.expectEmit(true, true, true, true, address(rateLimiter));
|
|
461
|
+
emit IRateLimiter.RateLimitGlobalConfigUpdated(config);
|
|
462
|
+
rateLimiter.setRateLimitGlobalConfig(config);
|
|
463
|
+
|
|
464
|
+
IRateLimiter.RateLimitGlobalConfig memory retrieved = rateLimiter.getRateLimitGlobalConfig();
|
|
465
|
+
assertTrue(retrieved.useGlobalState);
|
|
466
|
+
assertFalse(retrieved.isGloballyDisabled);
|
|
467
|
+
|
|
468
|
+
config.useGlobalState = false;
|
|
469
|
+
config.isGloballyDisabled = true;
|
|
470
|
+
|
|
471
|
+
vm.expectEmit(true, true, true, true, address(rateLimiter));
|
|
472
|
+
emit IRateLimiter.RateLimitGlobalConfigUpdated(config);
|
|
473
|
+
rateLimiter.setRateLimitGlobalConfig(config);
|
|
474
|
+
|
|
475
|
+
retrieved = rateLimiter.getRateLimitGlobalConfig();
|
|
476
|
+
assertFalse(retrieved.useGlobalState);
|
|
477
|
+
assertTrue(retrieved.isGloballyDisabled);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
function test_setRateLimitConfigs_Single() public {
|
|
481
|
+
IRateLimiter.SetRateLimitConfigParam[] memory configs = new IRateLimiter.SetRateLimitConfigParam[](1);
|
|
482
|
+
configs[0] = _createConfig(ID_1, false, false, false, true, true, 100, 200, 300, 400);
|
|
483
|
+
|
|
484
|
+
vm.expectEmit(false, false, false, true);
|
|
485
|
+
emit IRateLimiter.RateLimitConfigUpdated(ID_1, configs[0].config);
|
|
486
|
+
|
|
487
|
+
rateLimiter.setRateLimitConfigs(configs);
|
|
488
|
+
|
|
489
|
+
IRateLimiter.RateLimit memory rl = rateLimiter.rateLimits(ID_1);
|
|
490
|
+
assertEq(rl.outboundLimit, 100);
|
|
491
|
+
assertEq(rl.inboundLimit, 200);
|
|
492
|
+
assertEq(rl.outboundWindow, 300);
|
|
493
|
+
assertEq(rl.inboundWindow, 400);
|
|
494
|
+
// NetAccounting (bit 3) = 8, AddressExemption (bit 4) = 16 => 24 total.
|
|
495
|
+
assertEq(rl.configBitmap, 24);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
function test_setRateLimitConfigs_Multiple() public {
|
|
499
|
+
IRateLimiter.SetRateLimitConfigParam[] memory configs = new IRateLimiter.SetRateLimitConfigParam[](2);
|
|
500
|
+
configs[0] = _createConfig(ID_1, true, true, true, true, true, 10, 10, 10, 10);
|
|
501
|
+
configs[1] = _createConfig(ID_2, false, false, false, false, false, 20, 20, 20, 20);
|
|
502
|
+
|
|
503
|
+
rateLimiter.setRateLimitConfigs(configs);
|
|
504
|
+
|
|
505
|
+
IRateLimiter.RateLimit memory rl1 = rateLimiter.rateLimits(ID_1);
|
|
506
|
+
assertEq(rl1.outboundLimit, 10);
|
|
507
|
+
|
|
508
|
+
IRateLimiter.RateLimit memory rl2 = rateLimiter.rateLimits(ID_2);
|
|
509
|
+
assertEq(rl2.outboundLimit, 20);
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
function test_setRateLimitStates_Single() public {
|
|
513
|
+
vm.warp(1000);
|
|
514
|
+
IRateLimiter.SetRateLimitStateParam[] memory states = new IRateLimiter.SetRateLimitStateParam[](1);
|
|
515
|
+
states[0] = _createState(ID_1, 50, 60, 1000);
|
|
516
|
+
|
|
517
|
+
vm.expectEmit(false, false, false, true);
|
|
518
|
+
emit IRateLimiter.RateLimitStateUpdated(ID_1, states[0].state);
|
|
519
|
+
|
|
520
|
+
rateLimiter.setRateLimitStates(states);
|
|
521
|
+
|
|
522
|
+
IRateLimiter.RateLimit memory rl = rateLimiter.rateLimits(ID_1);
|
|
523
|
+
assertEq(rl.outboundUsage, 50);
|
|
524
|
+
assertEq(rl.inboundUsage, 60);
|
|
525
|
+
assertEq(rl.lastUpdated, 1000);
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
function test_setRateLimitStates_Revert_FutureTimestamp() public {
|
|
529
|
+
vm.warp(1000);
|
|
530
|
+
IRateLimiter.SetRateLimitStateParam[] memory states = new IRateLimiter.SetRateLimitStateParam[](1);
|
|
531
|
+
states[0] = _createState(ID_1, 50, 60, 1001); // Future
|
|
532
|
+
|
|
533
|
+
vm.expectRevert(abi.encodeWithSelector(IRateLimiter.LastUpdatedInFuture.selector, 1001, 1000));
|
|
534
|
+
rateLimiter.setRateLimitStates(states);
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
function test_setRateLimitAddressExemptions() public {
|
|
538
|
+
if (rateLimiter.isRateLimitAddressExempt(alice)) {
|
|
539
|
+
IRateLimiter.SetRateLimitAddressExemptionParam[]
|
|
540
|
+
memory resetExemptions = new IRateLimiter.SetRateLimitAddressExemptionParam[](1);
|
|
541
|
+
resetExemptions[0] = IRateLimiter.SetRateLimitAddressExemptionParam({ user: alice, isExempt: false });
|
|
542
|
+
rateLimiter.setRateLimitAddressExemptions(resetExemptions);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
IRateLimiter.SetRateLimitAddressExemptionParam[]
|
|
546
|
+
memory exemptions = new IRateLimiter.SetRateLimitAddressExemptionParam[](2);
|
|
547
|
+
exemptions[0] = IRateLimiter.SetRateLimitAddressExemptionParam({ user: alice, isExempt: true });
|
|
548
|
+
exemptions[1] = IRateLimiter.SetRateLimitAddressExemptionParam({ user: bob, isExempt: true });
|
|
549
|
+
|
|
550
|
+
vm.expectEmit(true, false, false, true);
|
|
551
|
+
emit IRateLimiter.RateLimitAddressExemptionUpdated(alice, true);
|
|
552
|
+
vm.expectEmit(true, false, false, true);
|
|
553
|
+
emit IRateLimiter.RateLimitAddressExemptionUpdated(bob, true);
|
|
554
|
+
|
|
555
|
+
rateLimiter.setRateLimitAddressExemptions(exemptions);
|
|
556
|
+
|
|
557
|
+
assertTrue(rateLimiter.isRateLimitAddressExempt(alice));
|
|
558
|
+
assertTrue(rateLimiter.isRateLimitAddressExempt(bob));
|
|
559
|
+
|
|
560
|
+
exemptions[0].isExempt = false;
|
|
561
|
+
exemptions[1].isExempt = false;
|
|
562
|
+
rateLimiter.setRateLimitAddressExemptions(exemptions);
|
|
563
|
+
assertFalse(rateLimiter.isRateLimitAddressExempt(alice));
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
function test_setRateLimitAddressExemptions_Revert_AlreadyExempt() public {
|
|
567
|
+
if (!rateLimiter.isRateLimitAddressExempt(alice)) {
|
|
568
|
+
IRateLimiter.SetRateLimitAddressExemptionParam[]
|
|
569
|
+
memory initExemptions = new IRateLimiter.SetRateLimitAddressExemptionParam[](1);
|
|
570
|
+
initExemptions[0] = IRateLimiter.SetRateLimitAddressExemptionParam({ user: alice, isExempt: true });
|
|
571
|
+
rateLimiter.setRateLimitAddressExemptions(initExemptions);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
IRateLimiter.SetRateLimitAddressExemptionParam[]
|
|
575
|
+
memory exemptions = new IRateLimiter.SetRateLimitAddressExemptionParam[](1);
|
|
576
|
+
exemptions[0] = IRateLimiter.SetRateLimitAddressExemptionParam({ user: alice, isExempt: true });
|
|
577
|
+
vm.expectRevert(abi.encodeWithSelector(IRateLimiter.ExemptionStateIdempotent.selector, alice, true));
|
|
578
|
+
rateLimiter.setRateLimitAddressExemptions(exemptions);
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
// ============ checkpointRateLimits Tests ============
|
|
582
|
+
|
|
583
|
+
function test_checkpointRateLimits_Single() public {
|
|
584
|
+
IRateLimiter.SetRateLimitConfigParam[] memory configs = new IRateLimiter.SetRateLimitConfigParam[](1);
|
|
585
|
+
configs[0] = _createConfig(ID_1, true, true, true, false, false, 1000, 1000, 100, 100);
|
|
586
|
+
rateLimiter.setRateLimitConfigs(configs);
|
|
587
|
+
|
|
588
|
+
rateLimiter.outflow(ID_1, alice, 1000);
|
|
589
|
+
|
|
590
|
+
vm.warp(block.timestamp + 50);
|
|
591
|
+
|
|
592
|
+
uint256[] memory ids = new uint256[](1);
|
|
593
|
+
ids[0] = ID_1;
|
|
594
|
+
rateLimiter.checkpointRateLimits(ids);
|
|
595
|
+
|
|
596
|
+
IRateLimiter.RateLimit memory rl = rateLimiter.rateLimits(ID_1);
|
|
597
|
+
assertEq(rl.outboundUsage, 500);
|
|
598
|
+
assertEq(rl.lastUpdated, block.timestamp);
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
function test_checkpointRateLimits_Multiple() public {
|
|
602
|
+
IRateLimiter.SetRateLimitConfigParam[] memory configs = new IRateLimiter.SetRateLimitConfigParam[](2);
|
|
603
|
+
configs[0] = _createConfig(ID_1, true, true, true, false, false, 1000, 1000, 100, 100);
|
|
604
|
+
configs[1] = _createConfig(ID_2, true, true, true, false, false, 500, 500, 100, 100);
|
|
605
|
+
rateLimiter.setRateLimitConfigs(configs);
|
|
606
|
+
|
|
607
|
+
rateLimiter.outflow(ID_1, alice, 1000);
|
|
608
|
+
rateLimiter.outflow(ID_2, alice, 500);
|
|
609
|
+
|
|
610
|
+
vm.warp(block.timestamp + 50);
|
|
611
|
+
|
|
612
|
+
uint256[] memory ids = new uint256[](2);
|
|
613
|
+
ids[0] = ID_1;
|
|
614
|
+
ids[1] = ID_2;
|
|
615
|
+
rateLimiter.checkpointRateLimits(ids);
|
|
616
|
+
|
|
617
|
+
IRateLimiter.RateLimit memory rl1 = rateLimiter.rateLimits(ID_1);
|
|
618
|
+
assertEq(rl1.outboundUsage, 500);
|
|
619
|
+
assertEq(rl1.lastUpdated, block.timestamp);
|
|
620
|
+
|
|
621
|
+
IRateLimiter.RateLimit memory rl2 = rateLimiter.rateLimits(ID_2);
|
|
622
|
+
assertEq(rl2.outboundUsage, 250);
|
|
623
|
+
assertEq(rl2.lastUpdated, block.timestamp);
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
function test_checkpointRateLimits_Empty() public {
|
|
627
|
+
uint256[] memory ids = new uint256[](0);
|
|
628
|
+
rateLimiter.checkpointRateLimits(ids);
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
function test_checkpointRateLimits_PreservesDecay() public {
|
|
632
|
+
IRateLimiter.SetRateLimitConfigParam[] memory configs = new IRateLimiter.SetRateLimitConfigParam[](1);
|
|
633
|
+
configs[0] = _createConfig(ID_1, true, true, true, false, false, 1000, 1000, 100, 100);
|
|
634
|
+
rateLimiter.setRateLimitConfigs(configs);
|
|
635
|
+
|
|
636
|
+
rateLimiter.outflow(ID_1, alice, 1000);
|
|
637
|
+
|
|
638
|
+
vm.warp(block.timestamp + 50);
|
|
639
|
+
|
|
640
|
+
(uint256 outUsageBefore, uint256 outAvailBefore, , ) = rateLimiter.getRateLimitUsages(ID_1);
|
|
641
|
+
assertEq(outUsageBefore, 500);
|
|
642
|
+
assertEq(outAvailBefore, 500);
|
|
643
|
+
|
|
644
|
+
uint256[] memory ids = new uint256[](1);
|
|
645
|
+
ids[0] = ID_1;
|
|
646
|
+
rateLimiter.checkpointRateLimits(ids);
|
|
647
|
+
|
|
648
|
+
(uint256 outUsageAfter, uint256 outAvailAfter, , ) = rateLimiter.getRateLimitUsages(ID_1);
|
|
649
|
+
assertEq(outUsageAfter, 500);
|
|
650
|
+
assertEq(outAvailAfter, 500);
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
function test_setRateLimitConfigs_DoesNotAutoCheckpoint() public {
|
|
654
|
+
IRateLimiter.SetRateLimitConfigParam[] memory configs = new IRateLimiter.SetRateLimitConfigParam[](1);
|
|
655
|
+
configs[0] = _createConfig(ID_1, true, true, true, false, false, 1000, 1000, 100, 100);
|
|
656
|
+
rateLimiter.setRateLimitConfigs(configs);
|
|
657
|
+
|
|
658
|
+
rateLimiter.outflow(ID_1, alice, 1000);
|
|
659
|
+
|
|
660
|
+
vm.warp(block.timestamp + 50);
|
|
661
|
+
|
|
662
|
+
/// @dev Usage should still be 1000 in storage (no checkpoint).
|
|
663
|
+
IRateLimiter.RateLimit memory rlBefore = rateLimiter.rateLimits(ID_1);
|
|
664
|
+
assertEq(rlBefore.outboundUsage, 1000);
|
|
665
|
+
|
|
666
|
+
/// @dev Setting configs should NOT checkpoint anymore.
|
|
667
|
+
configs[0] = _createConfig(ID_1, true, true, true, false, false, 2000, 2000, 200, 200);
|
|
668
|
+
rateLimiter.setRateLimitConfigs(configs);
|
|
669
|
+
|
|
670
|
+
/// @dev Raw storage still has the old usage since no checkpoint was performed.
|
|
671
|
+
IRateLimiter.RateLimit memory rlAfter = rateLimiter.rateLimits(ID_1);
|
|
672
|
+
assertEq(rlAfter.outboundUsage, 1000);
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
// ============ Outflow/Inflow Tests ============
|
|
676
|
+
|
|
677
|
+
function test_outflow_DefaultConfig_SpecificState_Outbound_Gross_NotExempt() public {
|
|
678
|
+
rateLimiter.outflow(ID_SNAP_DEFAULT_OUTBOUND_GROSS, alice, 100);
|
|
679
|
+
|
|
680
|
+
(uint256 usage, , , ) = rateLimiter.getRateLimitUsages(ID_SNAP_DEFAULT_OUTBOUND_GROSS);
|
|
681
|
+
assertEq(usage, 100);
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
function test_inflow_DefaultConfig_SpecificState_Inbound_Gross_NotExempt() public {
|
|
685
|
+
rateLimiter.inflow(ID_SNAP_DEFAULT_INBOUND_GROSS, alice, 100);
|
|
686
|
+
|
|
687
|
+
(, , uint256 usage, ) = rateLimiter.getRateLimitUsages(ID_SNAP_DEFAULT_INBOUND_GROSS);
|
|
688
|
+
assertEq(usage, 100);
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
function test_outflow_ExplicitConfig_SpecificState_Outbound_Gross_NotExempt() public {
|
|
692
|
+
rateLimiter.outflow(ID_SNAP_EXPLICIT_OUTBOUND_GROSS, alice, 100);
|
|
693
|
+
|
|
694
|
+
(uint256 usage, , , ) = rateLimiter.getRateLimitUsages(ID_SNAP_EXPLICIT_OUTBOUND_GROSS);
|
|
695
|
+
assertEq(usage, 100);
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
function test_outflow_ExplicitConfig_SpecificState_Both_Gross_Exempt() public {
|
|
699
|
+
rateLimiter.outflow(ID_SNAP_EXPLICIT_BOTH_GROSS_EXEMPT, alice, 100);
|
|
700
|
+
|
|
701
|
+
(uint256 usage, , , ) = rateLimiter.getRateLimitUsages(ID_SNAP_EXPLICIT_BOTH_GROSS_EXEMPT);
|
|
702
|
+
assertEq(usage, 0);
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
function test_inflow_GloballyDisabled() public {
|
|
706
|
+
rateLimiter.setRateLimitGlobalConfig(
|
|
707
|
+
IRateLimiter.RateLimitGlobalConfig({ useGlobalState: false, isGloballyDisabled: true })
|
|
708
|
+
);
|
|
709
|
+
|
|
710
|
+
rateLimiter.inflow(ID_1, alice, 100);
|
|
711
|
+
|
|
712
|
+
(uint256 outUsage, , uint256 inUsage, ) = rateLimiter.getRateLimitUsages(ID_1);
|
|
713
|
+
assertEq(inUsage, 0);
|
|
714
|
+
assertEq(outUsage, 0);
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
function test_outflow_GloballyDisabled() public {
|
|
718
|
+
rateLimiter.setRateLimitGlobalConfig(
|
|
719
|
+
IRateLimiter.RateLimitGlobalConfig({ useGlobalState: false, isGloballyDisabled: true })
|
|
720
|
+
);
|
|
721
|
+
|
|
722
|
+
rateLimiter.outflow(ID_1, alice, 100);
|
|
723
|
+
|
|
724
|
+
(uint256 outUsage, , uint256 inUsage, ) = rateLimiter.getRateLimitUsages(ID_1);
|
|
725
|
+
assertEq(outUsage, 0);
|
|
726
|
+
assertEq(inUsage, 0);
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
function test_outflow_Revert_RateLimitExceeded() public {
|
|
730
|
+
IRateLimiter.SetRateLimitConfigParam[] memory configs = new IRateLimiter.SetRateLimitConfigParam[](1);
|
|
731
|
+
configs[0] = _createConfig(ID_1, true, true, true, false, false, 100, 100, 100, 100);
|
|
732
|
+
rateLimiter.setRateLimitConfigs(configs);
|
|
733
|
+
|
|
734
|
+
vm.expectRevert(abi.encodeWithSelector(IRateLimiter.RateLimitExceeded.selector, 100, 101));
|
|
735
|
+
rateLimiter.outflow(ID_1, alice, 101);
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
// ============ getRateLimitUsages Disabled Direction Tests ============
|
|
739
|
+
|
|
740
|
+
function test_getRateLimitUsages_WithDecay() public {
|
|
741
|
+
IRateLimiter.SetRateLimitConfigParam[] memory configs = new IRateLimiter.SetRateLimitConfigParam[](1);
|
|
742
|
+
configs[0] = _createConfig(ID_1, true, true, true, false, false, 1000, 1000, 100, 100);
|
|
743
|
+
rateLimiter.setRateLimitConfigs(configs);
|
|
744
|
+
|
|
745
|
+
rateLimiter.outflow(ID_1, alice, 1000);
|
|
746
|
+
|
|
747
|
+
(uint256 outUsageBefore, uint256 outAvailBefore, , ) = rateLimiter.getRateLimitUsages(ID_1);
|
|
748
|
+
assertEq(outUsageBefore, 1000);
|
|
749
|
+
assertEq(outAvailBefore, 0);
|
|
750
|
+
|
|
751
|
+
vm.warp(block.timestamp + 50);
|
|
752
|
+
|
|
753
|
+
(uint256 outUsageAfter, uint256 outAvailAfter, , ) = rateLimiter.getRateLimitUsages(ID_1);
|
|
754
|
+
|
|
755
|
+
assertEq(outUsageAfter, 500);
|
|
756
|
+
assertEq(outAvailAfter, 500);
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
function test_getRateLimitUsages_OutboundDisabled() public view {
|
|
760
|
+
// ID_SNAP_DISABLED: override=true, outbound=false, inbound=true, limit=1000, window=1000.
|
|
761
|
+
(uint256 outUsage, uint256 outAvail, uint256 inUsage, uint256 inAvail) = rateLimiter.getRateLimitUsages(
|
|
762
|
+
ID_SNAP_DISABLED
|
|
763
|
+
);
|
|
764
|
+
|
|
765
|
+
assertEq(outUsage, 0);
|
|
766
|
+
assertEq(outAvail, type(uint256).max);
|
|
767
|
+
assertEq(inUsage, 0);
|
|
768
|
+
assertEq(inAvail, 1000);
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
function test_getRateLimitUsages_InboundDisabled() public view {
|
|
772
|
+
// ID_SNAP_EXPLICIT_OUTBOUND_GROSS: override=true, outbound=true, inbound=false, limit=1000, window=1000.
|
|
773
|
+
(uint256 outUsage, uint256 outAvail, uint256 inUsage, uint256 inAvail) = rateLimiter.getRateLimitUsages(
|
|
774
|
+
ID_SNAP_EXPLICIT_OUTBOUND_GROSS
|
|
775
|
+
);
|
|
776
|
+
|
|
777
|
+
assertEq(outUsage, 0);
|
|
778
|
+
assertEq(outAvail, 1000);
|
|
779
|
+
assertEq(inUsage, 0);
|
|
780
|
+
assertEq(inAvail, type(uint256).max);
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
function test_getRateLimitUsages_BothDisabled() public view {
|
|
784
|
+
// ID_SNAP_DEFAULT_DISABLED falls back to default which has outbound=false, inbound=false.
|
|
785
|
+
(, uint256 outAvail, , uint256 inAvail) = rateLimiterDefaultDisabled.getRateLimitUsages(
|
|
786
|
+
ID_SNAP_DEFAULT_DISABLED
|
|
787
|
+
);
|
|
788
|
+
|
|
789
|
+
assertEq(outAvail, type(uint256).max);
|
|
790
|
+
assertEq(inAvail, type(uint256).max);
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
function test_getRateLimitUsages_GloballyDisabled() public {
|
|
794
|
+
rateLimiter.setRateLimitGlobalConfig(
|
|
795
|
+
IRateLimiter.RateLimitGlobalConfig({ useGlobalState: false, isGloballyDisabled: true })
|
|
796
|
+
);
|
|
797
|
+
|
|
798
|
+
(, uint256 outAvail, , uint256 inAvail) = rateLimiter.getRateLimitUsages(ID_SNAP_EXPLICIT_BOTH_NET_OUT);
|
|
799
|
+
|
|
800
|
+
assertEq(outAvail, type(uint256).max);
|
|
801
|
+
assertEq(inAvail, type(uint256).max);
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
function test_initial_state_closed() public {
|
|
805
|
+
RateLimiterBaseUpgradeableMock freshLimiter = _createRateLimiter(0, false);
|
|
806
|
+
|
|
807
|
+
IRateLimiter.RateLimit memory rl = freshLimiter.rateLimits(0);
|
|
808
|
+
|
|
809
|
+
// Bitmap = encode(false, true, true, true, false) = 0 | 2 | 4 | 8 | 0 = 14.
|
|
810
|
+
assertEq(rl.configBitmap, 14);
|
|
811
|
+
assertEq(rl.outboundLimit, 0);
|
|
812
|
+
assertEq(rl.inboundLimit, 0);
|
|
813
|
+
|
|
814
|
+
vm.expectRevert(abi.encodeWithSelector(IRateLimiter.RateLimitExceeded.selector, 0, 100));
|
|
815
|
+
freshLimiter.outflow(uint256(keccak256("ANY_ID")), address(this), 100);
|
|
816
|
+
|
|
817
|
+
vm.expectRevert(abi.encodeWithSelector(IRateLimiter.RateLimitExceeded.selector, 0, 100));
|
|
818
|
+
freshLimiter.inflow(uint256(keccak256("ANY_ID")), address(this), 100);
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
function test_storageHash() public pure {
|
|
822
|
+
bytes32 storageHash = keccak256(abi.encode(uint256(keccak256("layerzerov2.storage.ratelimiter")) - 1)) &
|
|
823
|
+
~bytes32(uint256(0xff));
|
|
824
|
+
assertEq(storageHash, 0xfc4b3847e0649a09792d4c694ef28e20c43dde62a8b3de98eff85ccb4e1f3000);
|
|
825
|
+
}
|
|
826
|
+
}
|