@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,445 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.22;
|
|
3
|
+
|
|
4
|
+
import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
|
|
5
|
+
import { Test } from "forge-std/Test.sol";
|
|
6
|
+
import { EnumerableSetPagination } from "./../contracts/libs/EnumerableSetPagination.sol";
|
|
7
|
+
|
|
8
|
+
contract AddressPaginationHarness {
|
|
9
|
+
using EnumerableSet for EnumerableSet.AddressSet;
|
|
10
|
+
using EnumerableSetPagination for EnumerableSet.AddressSet;
|
|
11
|
+
|
|
12
|
+
EnumerableSet.AddressSet set;
|
|
13
|
+
|
|
14
|
+
function add(address _addr) external {
|
|
15
|
+
set.add(_addr);
|
|
16
|
+
}
|
|
17
|
+
function remove(address _addr) external {
|
|
18
|
+
set.remove(_addr);
|
|
19
|
+
}
|
|
20
|
+
function length() external view returns (uint256) {
|
|
21
|
+
return set.length();
|
|
22
|
+
}
|
|
23
|
+
function paginate(uint256 _offset, uint256 _limit) external view returns (address[] memory) {
|
|
24
|
+
return set.paginate(_offset, _limit);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
contract UintPaginationHarness {
|
|
29
|
+
using EnumerableSet for EnumerableSet.UintSet;
|
|
30
|
+
using EnumerableSetPagination for EnumerableSet.UintSet;
|
|
31
|
+
|
|
32
|
+
EnumerableSet.UintSet set;
|
|
33
|
+
|
|
34
|
+
function add(uint256 _val) external {
|
|
35
|
+
set.add(_val);
|
|
36
|
+
}
|
|
37
|
+
function remove(uint256 _val) external {
|
|
38
|
+
set.remove(_val);
|
|
39
|
+
}
|
|
40
|
+
function length() external view returns (uint256) {
|
|
41
|
+
return set.length();
|
|
42
|
+
}
|
|
43
|
+
function paginate(uint256 _offset, uint256 _limit) external view returns (uint256[] memory) {
|
|
44
|
+
return set.paginate(_offset, _limit);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
contract EnumerableSetPaginationTest is Test {
|
|
49
|
+
using EnumerableSet for EnumerableSet.AddressSet;
|
|
50
|
+
using EnumerableSet for EnumerableSet.UintSet;
|
|
51
|
+
using EnumerableSetPagination for EnumerableSet.AddressSet;
|
|
52
|
+
using EnumerableSetPagination for EnumerableSet.UintSet;
|
|
53
|
+
|
|
54
|
+
AddressPaginationHarness addrHarness;
|
|
55
|
+
UintPaginationHarness uintHarness;
|
|
56
|
+
|
|
57
|
+
EnumerableSet.AddressSet addrSet;
|
|
58
|
+
EnumerableSet.UintSet uintSet;
|
|
59
|
+
|
|
60
|
+
uint256 constant SET_SIZE = 1_000;
|
|
61
|
+
|
|
62
|
+
function setUp() public {
|
|
63
|
+
addrHarness = new AddressPaginationHarness();
|
|
64
|
+
uintHarness = new UintPaginationHarness();
|
|
65
|
+
|
|
66
|
+
for (uint256 i = 1; i <= SET_SIZE; i++) {
|
|
67
|
+
addrHarness.add(address(uint160(i)));
|
|
68
|
+
addrSet.add(address(uint160(i)));
|
|
69
|
+
uintHarness.add(i);
|
|
70
|
+
uintSet.add(i);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ============ AddressSet — Empty / Single ============
|
|
75
|
+
|
|
76
|
+
function test_address_paginate_EmptySet() public {
|
|
77
|
+
AddressPaginationHarness empty = new AddressPaginationHarness();
|
|
78
|
+
assertEq(empty.paginate(0, 10).length, 0);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function test_address_paginate_SingleElement() public {
|
|
82
|
+
AddressPaginationHarness single = new AddressPaginationHarness();
|
|
83
|
+
single.add(address(0xBEEF));
|
|
84
|
+
address[] memory result = single.paginate(0, 100);
|
|
85
|
+
assertEq(result.length, 1);
|
|
86
|
+
assertEq(result[0], address(0xBEEF));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// ============ AddressSet — Offset ============
|
|
90
|
+
|
|
91
|
+
function test_address_paginate_OffsetZero() public {
|
|
92
|
+
address[] memory result = addrHarness.paginate(0, 10);
|
|
93
|
+
assertEq(result.length, 10);
|
|
94
|
+
for (uint256 i = 0; i < 10; i++) {
|
|
95
|
+
assertEq(result[i], address(uint160(i + 1)));
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function test_address_paginate_MiddleOffset() public {
|
|
100
|
+
address[] memory result = addrHarness.paginate(500, 10);
|
|
101
|
+
assertEq(result.length, 10);
|
|
102
|
+
for (uint256 i = 0; i < 10; i++) {
|
|
103
|
+
assertEq(result[i], address(uint160(501 + i)));
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function test_address_paginate_OffsetAtEnd() public {
|
|
108
|
+
address[] memory result = addrHarness.paginate(SET_SIZE - 1, 100);
|
|
109
|
+
assertEq(result.length, 1);
|
|
110
|
+
assertEq(result[0], address(uint160(SET_SIZE)));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function test_address_paginate_OffsetBeyondEnd() public {
|
|
114
|
+
assertEq(addrHarness.paginate(SET_SIZE, 10).length, 0);
|
|
115
|
+
assertEq(addrHarness.paginate(SET_SIZE + 1000, 10).length, 0);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ============ AddressSet — Limit ============
|
|
119
|
+
|
|
120
|
+
function test_address_paginate_LimitZero() public {
|
|
121
|
+
assertEq(addrHarness.paginate(0, 0).length, 0);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function test_address_paginate_LimitOne() public {
|
|
125
|
+
address[] memory result = addrHarness.paginate(0, 1);
|
|
126
|
+
assertEq(result.length, 1);
|
|
127
|
+
assertEq(result[0], address(uint160(1)));
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function test_address_paginate_LimitExceedsRemaining() public {
|
|
131
|
+
address[] memory result = addrHarness.paginate(SET_SIZE - 5, 100);
|
|
132
|
+
assertEq(result.length, 5);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function test_address_paginate_LimitExceedsTotal() public {
|
|
136
|
+
assertEq(addrHarness.paginate(0, SET_SIZE + 50).length, SET_SIZE);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function test_address_paginate_MaxUint256Limit() public {
|
|
140
|
+
assertEq(addrHarness.paginate(0, type(uint256).max).length, SET_SIZE);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// ============ AddressSet — Multi-Page ============
|
|
144
|
+
|
|
145
|
+
function test_address_paginate_SequentialPagesCompleteSet() public {
|
|
146
|
+
uint256 pageSize = 137;
|
|
147
|
+
uint256 collected;
|
|
148
|
+
|
|
149
|
+
for (uint256 offset = 0; offset < SET_SIZE; offset += pageSize) {
|
|
150
|
+
address[] memory page = addrHarness.paginate(offset, pageSize);
|
|
151
|
+
collected += page.length;
|
|
152
|
+
|
|
153
|
+
for (uint256 i = 0; i < page.length; i++) {
|
|
154
|
+
assertEq(page[i], address(uint160(offset + i + 1)));
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
assertEq(collected, SET_SIZE);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// ============ AddressSet — Dynamic (add/remove) ============
|
|
162
|
+
|
|
163
|
+
function test_address_paginate_AfterRemovingElements() public {
|
|
164
|
+
AddressPaginationHarness h = new AddressPaginationHarness();
|
|
165
|
+
for (uint256 i = 1; i <= 20; i++) h.add(address(uint160(i)));
|
|
166
|
+
|
|
167
|
+
h.remove(address(uint160(5)));
|
|
168
|
+
h.remove(address(uint160(10)));
|
|
169
|
+
h.remove(address(uint160(15)));
|
|
170
|
+
|
|
171
|
+
address[] memory result = h.paginate(0, 100);
|
|
172
|
+
assertEq(result.length, 17);
|
|
173
|
+
|
|
174
|
+
for (uint256 i = 0; i < result.length; i++) {
|
|
175
|
+
assertTrue(result[i] != address(uint160(5)));
|
|
176
|
+
assertTrue(result[i] != address(uint160(10)));
|
|
177
|
+
assertTrue(result[i] != address(uint160(15)));
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// ============ AddressSet — Fuzz ============
|
|
182
|
+
|
|
183
|
+
function test_address_paginate_Fuzz(uint256 _offset, uint256 _limit) public view {
|
|
184
|
+
_offset = bound(_offset, 0, SET_SIZE * 2);
|
|
185
|
+
_limit = bound(_limit, 0, SET_SIZE * 2);
|
|
186
|
+
|
|
187
|
+
address[] memory result = addrSet.paginate(_offset, _limit);
|
|
188
|
+
|
|
189
|
+
if (_offset >= SET_SIZE) {
|
|
190
|
+
assertEq(result.length, 0);
|
|
191
|
+
} else {
|
|
192
|
+
uint256 expected = _offset + _limit > SET_SIZE ? SET_SIZE - _offset : _limit;
|
|
193
|
+
assertEq(result.length, expected);
|
|
194
|
+
for (uint256 i = 0; i < result.length; i++) {
|
|
195
|
+
assertEq(result[i], address(uint160(_offset + i + 1)));
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function test_address_paginate_VaryingSize_Fuzz(uint8 _size, uint256 _offset, uint256 _limit) public {
|
|
201
|
+
EnumerableSet.AddressSet storage s = addrSet;
|
|
202
|
+
while (s.length() > 0) s.remove(s.at(0));
|
|
203
|
+
|
|
204
|
+
uint256 size = uint256(_size);
|
|
205
|
+
_offset = bound(_offset, 0, size + 50);
|
|
206
|
+
_limit = bound(_limit, 0, 300);
|
|
207
|
+
|
|
208
|
+
for (uint256 i = 0; i < size; i++) s.add(address(uint160(i + 1000)));
|
|
209
|
+
|
|
210
|
+
address[] memory result = s.paginate(_offset, _limit);
|
|
211
|
+
|
|
212
|
+
if (size == 0 || _offset >= size) {
|
|
213
|
+
assertEq(result.length, 0);
|
|
214
|
+
} else {
|
|
215
|
+
uint256 remaining = size - _offset;
|
|
216
|
+
assertEq(result.length, remaining < _limit ? remaining : _limit);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// ============ AddressSet — Properties ============
|
|
221
|
+
|
|
222
|
+
function test_address_property_ReturnedLengthNeverExceedsLimit() public view {
|
|
223
|
+
for (uint256 limit = 1; limit <= 50; limit++) {
|
|
224
|
+
for (uint256 offset = 0; offset < SET_SIZE; offset += 2000) {
|
|
225
|
+
assertLe(addrSet.paginate(offset, limit).length, limit);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function test_address_property_ConsecutivePagesNoOverlap() public view {
|
|
231
|
+
uint256 pageSize = 1337;
|
|
232
|
+
for (uint256 offset = 0; offset + pageSize < SET_SIZE; offset += pageSize) {
|
|
233
|
+
address[] memory p1 = addrSet.paginate(offset, pageSize);
|
|
234
|
+
address[] memory p2 = addrSet.paginate(offset + pageSize, pageSize);
|
|
235
|
+
if (p1.length > 0 && p2.length > 0) {
|
|
236
|
+
assertTrue(p1[p1.length - 1] != p2[0]);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// ============ UintSet — Empty / Single ============
|
|
242
|
+
|
|
243
|
+
function test_uint_paginate_EmptySet() public {
|
|
244
|
+
UintPaginationHarness empty = new UintPaginationHarness();
|
|
245
|
+
assertEq(empty.paginate(0, 10).length, 0);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function test_uint_paginate_SingleElement() public {
|
|
249
|
+
UintPaginationHarness single = new UintPaginationHarness();
|
|
250
|
+
single.add(42);
|
|
251
|
+
uint256[] memory result = single.paginate(0, 100);
|
|
252
|
+
assertEq(result.length, 1);
|
|
253
|
+
assertEq(result[0], 42);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// ============ UintSet — Offset ============
|
|
257
|
+
|
|
258
|
+
function test_uint_paginate_OffsetZero() public {
|
|
259
|
+
uint256[] memory result = uintHarness.paginate(0, 10);
|
|
260
|
+
assertEq(result.length, 10);
|
|
261
|
+
for (uint256 i = 0; i < 10; i++) {
|
|
262
|
+
assertEq(result[i], i + 1);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function test_uint_paginate_MiddleOffset() public {
|
|
267
|
+
uint256[] memory result = uintHarness.paginate(500, 10);
|
|
268
|
+
assertEq(result.length, 10);
|
|
269
|
+
for (uint256 i = 0; i < 10; i++) {
|
|
270
|
+
assertEq(result[i], 501 + i);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function test_uint_paginate_OffsetAtEnd() public {
|
|
275
|
+
uint256[] memory result = uintHarness.paginate(SET_SIZE - 1, 100);
|
|
276
|
+
assertEq(result.length, 1);
|
|
277
|
+
assertEq(result[0], SET_SIZE);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function test_uint_paginate_OffsetBeyondEnd() public {
|
|
281
|
+
assertEq(uintHarness.paginate(SET_SIZE, 10).length, 0);
|
|
282
|
+
assertEq(uintHarness.paginate(SET_SIZE + 1000, 10).length, 0);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// ============ UintSet — Limit ============
|
|
286
|
+
|
|
287
|
+
function test_uint_paginate_LimitZero() public {
|
|
288
|
+
assertEq(uintHarness.paginate(0, 0).length, 0);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function test_uint_paginate_LimitOne() public {
|
|
292
|
+
uint256[] memory result = uintHarness.paginate(0, 1);
|
|
293
|
+
assertEq(result.length, 1);
|
|
294
|
+
assertEq(result[0], 1);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function test_uint_paginate_LimitExceedsRemaining() public {
|
|
298
|
+
uint256[] memory result = uintHarness.paginate(SET_SIZE - 5, 100);
|
|
299
|
+
assertEq(result.length, 5);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function test_uint_paginate_LimitExceedsTotal() public {
|
|
303
|
+
assertEq(uintHarness.paginate(0, SET_SIZE + 50).length, SET_SIZE);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function test_uint_paginate_MaxUint256Limit() public {
|
|
307
|
+
assertEq(uintHarness.paginate(0, type(uint256).max).length, SET_SIZE);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// ============ UintSet — Multi-Page ============
|
|
311
|
+
|
|
312
|
+
function test_uint_paginate_SequentialPagesCompleteSet() public {
|
|
313
|
+
uint256 pageSize = 137;
|
|
314
|
+
uint256 collected;
|
|
315
|
+
|
|
316
|
+
for (uint256 offset = 0; offset < SET_SIZE; offset += pageSize) {
|
|
317
|
+
uint256[] memory page = uintHarness.paginate(offset, pageSize);
|
|
318
|
+
collected += page.length;
|
|
319
|
+
|
|
320
|
+
for (uint256 i = 0; i < page.length; i++) {
|
|
321
|
+
assertEq(page[i], offset + i + 1);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
assertEq(collected, SET_SIZE);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// ============ UintSet — Dynamic (add/remove) ============
|
|
329
|
+
|
|
330
|
+
function test_uint_paginate_AfterRemovingElements() public {
|
|
331
|
+
UintPaginationHarness h = new UintPaginationHarness();
|
|
332
|
+
for (uint256 i = 1; i <= 20; i++) h.add(i);
|
|
333
|
+
|
|
334
|
+
h.remove(5);
|
|
335
|
+
h.remove(10);
|
|
336
|
+
h.remove(15);
|
|
337
|
+
|
|
338
|
+
uint256[] memory result = h.paginate(0, 100);
|
|
339
|
+
assertEq(result.length, 17);
|
|
340
|
+
|
|
341
|
+
for (uint256 i = 0; i < result.length; i++) {
|
|
342
|
+
assertTrue(result[i] != 5);
|
|
343
|
+
assertTrue(result[i] != 10);
|
|
344
|
+
assertTrue(result[i] != 15);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// ============ UintSet — Fuzz ============
|
|
349
|
+
|
|
350
|
+
function test_uint_paginate_Fuzz(uint256 _offset, uint256 _limit) public view {
|
|
351
|
+
_offset = bound(_offset, 0, SET_SIZE * 2);
|
|
352
|
+
_limit = bound(_limit, 0, SET_SIZE * 2);
|
|
353
|
+
|
|
354
|
+
uint256[] memory result = uintSet.paginate(_offset, _limit);
|
|
355
|
+
|
|
356
|
+
if (_offset >= SET_SIZE) {
|
|
357
|
+
assertEq(result.length, 0);
|
|
358
|
+
} else {
|
|
359
|
+
uint256 expected = _offset + _limit > SET_SIZE ? SET_SIZE - _offset : _limit;
|
|
360
|
+
assertEq(result.length, expected);
|
|
361
|
+
for (uint256 i = 0; i < result.length; i++) {
|
|
362
|
+
assertEq(result[i], _offset + i + 1);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function test_uint_paginate_VaryingSize_Fuzz(uint8 _size, uint256 _offset, uint256 _limit) public {
|
|
368
|
+
EnumerableSet.UintSet storage s = uintSet;
|
|
369
|
+
while (s.length() > 0) s.remove(s.at(0));
|
|
370
|
+
|
|
371
|
+
uint256 size = uint256(_size);
|
|
372
|
+
_offset = bound(_offset, 0, size + 50);
|
|
373
|
+
_limit = bound(_limit, 0, 300);
|
|
374
|
+
|
|
375
|
+
for (uint256 i = 0; i < size; i++) s.add(i + 1000);
|
|
376
|
+
|
|
377
|
+
uint256[] memory result = s.paginate(_offset, _limit);
|
|
378
|
+
|
|
379
|
+
if (size == 0 || _offset >= size) {
|
|
380
|
+
assertEq(result.length, 0);
|
|
381
|
+
} else {
|
|
382
|
+
uint256 remaining = size - _offset;
|
|
383
|
+
assertEq(result.length, remaining < _limit ? remaining : _limit);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// ============ UintSet — Properties ============
|
|
388
|
+
|
|
389
|
+
function test_uint_property_ReturnedLengthNeverExceedsLimit() public view {
|
|
390
|
+
for (uint256 limit = 1; limit <= 50; limit++) {
|
|
391
|
+
for (uint256 offset = 0; offset < SET_SIZE; offset += 2000) {
|
|
392
|
+
assertLe(uintSet.paginate(offset, limit).length, limit);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function test_uint_property_ConsecutivePagesNoOverlap() public view {
|
|
398
|
+
uint256 pageSize = 137;
|
|
399
|
+
for (uint256 offset = 0; offset + pageSize < SET_SIZE; offset += pageSize) {
|
|
400
|
+
uint256[] memory p1 = uintSet.paginate(offset, pageSize);
|
|
401
|
+
uint256[] memory p2 = uintSet.paginate(offset + pageSize, pageSize);
|
|
402
|
+
if (p1.length > 0 && p2.length > 0) {
|
|
403
|
+
assertTrue(p1[p1.length - 1] != p2[0]);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// ============ Large Set (10,000 items) ============
|
|
409
|
+
|
|
410
|
+
function test_address_paginate_LargeSet() public {
|
|
411
|
+
AddressPaginationHarness large = new AddressPaginationHarness();
|
|
412
|
+
uint256 size = 10_000;
|
|
413
|
+
for (uint256 i = 1; i <= size; i++) large.add(address(uint160(i)));
|
|
414
|
+
|
|
415
|
+
assertEq(large.length(), size);
|
|
416
|
+
assertEq(large.paginate(0, 50).length, 50);
|
|
417
|
+
assertEq(large.paginate(9990, 50).length, 10);
|
|
418
|
+
assertEq(large.paginate(size, 10).length, 0);
|
|
419
|
+
|
|
420
|
+
uint256 collected;
|
|
421
|
+
uint256 pageSize = 500;
|
|
422
|
+
for (uint256 offset = 0; offset < size; offset += pageSize) {
|
|
423
|
+
collected += large.paginate(offset, pageSize).length;
|
|
424
|
+
}
|
|
425
|
+
assertEq(collected, size);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
function test_uint_paginate_LargeSet() public {
|
|
429
|
+
UintPaginationHarness large = new UintPaginationHarness();
|
|
430
|
+
uint256 size = 10_000;
|
|
431
|
+
for (uint256 i = 1; i <= size; i++) large.add(i);
|
|
432
|
+
|
|
433
|
+
assertEq(large.length(), size);
|
|
434
|
+
assertEq(large.paginate(0, 50).length, 50);
|
|
435
|
+
assertEq(large.paginate(9990, 50).length, 10);
|
|
436
|
+
assertEq(large.paginate(size, 10).length, 0);
|
|
437
|
+
|
|
438
|
+
uint256 collected;
|
|
439
|
+
uint256 pageSize = 500;
|
|
440
|
+
for (uint256 offset = 0; offset < size; offset += pageSize) {
|
|
441
|
+
collected += large.paginate(offset, pageSize).length;
|
|
442
|
+
}
|
|
443
|
+
assertEq(collected, size);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.22;
|
|
3
|
+
|
|
4
|
+
import { IFeeConfig } from "@layerzerolabs/utils-evm-contracts/contracts/interfaces/IFeeConfig.sol";
|
|
5
|
+
import { TransparentUpgradeableProxy } from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
|
|
6
|
+
import { Test } from "forge-std/Test.sol";
|
|
7
|
+
import { FeeConfigBaseUpgradeable } from "./../contracts/fee-config/FeeConfigBaseUpgradeable.sol";
|
|
8
|
+
|
|
9
|
+
interface IFeeConfigTestHelper is IFeeConfig {}
|
|
10
|
+
|
|
11
|
+
contract FeeConfigBaseUpgradeableMock is FeeConfigBaseUpgradeable {
|
|
12
|
+
constructor() {
|
|
13
|
+
_disableInitializers();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function initialize() public initializer {}
|
|
17
|
+
|
|
18
|
+
function setDefaultFeeBps(uint16 _feeBps) public {
|
|
19
|
+
_setDefaultFeeBps(_feeBps);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function setFeeBps(uint256 _id, uint16 _feeBps, bool _enabled) public {
|
|
23
|
+
_setFeeBps(_id, _feeBps, _enabled);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
contract FeeConfigBaseUpgradeableTest is Test {
|
|
28
|
+
IFeeConfigTestHelper public feeConfigHelper;
|
|
29
|
+
|
|
30
|
+
uint32 constant EID_1 = 1;
|
|
31
|
+
uint32 constant EID_2 = 2;
|
|
32
|
+
uint16 constant BPS_DENOMINATOR = 10000;
|
|
33
|
+
|
|
34
|
+
function _deployFeeConfigHelper() internal virtual returns (IFeeConfigTestHelper) {
|
|
35
|
+
FeeConfigBaseUpgradeableMock impl = new FeeConfigBaseUpgradeableMock();
|
|
36
|
+
TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy(
|
|
37
|
+
address(impl),
|
|
38
|
+
address(this),
|
|
39
|
+
abi.encodeWithSelector(FeeConfigBaseUpgradeableMock.initialize.selector)
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
return IFeeConfigTestHelper(address(proxy));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function setUp() public virtual {
|
|
46
|
+
feeConfigHelper = _deployFeeConfigHelper();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function test_setDefaultFeeBps_Fuzz(uint16 _feeBps) public {
|
|
50
|
+
if (_feeBps > BPS_DENOMINATOR) {
|
|
51
|
+
vm.expectRevert(abi.encodeWithSelector(IFeeConfig.InvalidBps.selector, _feeBps));
|
|
52
|
+
feeConfigHelper.setDefaultFeeBps(_feeBps);
|
|
53
|
+
} else {
|
|
54
|
+
vm.expectEmit(false, false, false, true);
|
|
55
|
+
emit IFeeConfig.DefaultFeeBpsSet(_feeBps);
|
|
56
|
+
feeConfigHelper.setDefaultFeeBps(_feeBps);
|
|
57
|
+
assertEq(feeConfigHelper.defaultFeeBps(), _feeBps);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function test_setDefaultFeeBps() public {
|
|
62
|
+
test_setDefaultFeeBps_Fuzz(500);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function test_setFeeBps_Fuzz(uint16 _defaultFeeBps, uint32 _dstEid, uint16 _feeBps, bool _enabled) public {
|
|
66
|
+
_defaultFeeBps = uint16(bound(_defaultFeeBps, 0, BPS_DENOMINATOR));
|
|
67
|
+
feeConfigHelper.setDefaultFeeBps(_defaultFeeBps);
|
|
68
|
+
assertEq(feeConfigHelper.defaultFeeBps(), _defaultFeeBps);
|
|
69
|
+
|
|
70
|
+
if (_feeBps > BPS_DENOMINATOR) {
|
|
71
|
+
vm.expectRevert(abi.encodeWithSelector(IFeeConfig.InvalidBps.selector, _feeBps));
|
|
72
|
+
feeConfigHelper.setFeeBps(_dstEid, _feeBps, _enabled);
|
|
73
|
+
} else {
|
|
74
|
+
vm.expectEmit(false, false, false, true);
|
|
75
|
+
emit IFeeConfig.FeeBpsSet(_dstEid, _feeBps, _enabled);
|
|
76
|
+
feeConfigHelper.setFeeBps(_dstEid, _feeBps, _enabled);
|
|
77
|
+
|
|
78
|
+
IFeeConfig.FeeConfig memory config = feeConfigHelper.feeBps(_dstEid);
|
|
79
|
+
assertEq(config.feeBps, _feeBps);
|
|
80
|
+
assertEq(config.enabled, _enabled);
|
|
81
|
+
|
|
82
|
+
uint256 fee = feeConfigHelper.getFee(_dstEid, 10000);
|
|
83
|
+
uint16 expectedBps = _enabled ? _feeBps : _defaultFeeBps;
|
|
84
|
+
assertEq(fee, expectedBps);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function test_setFeeBps() public {
|
|
89
|
+
test_setFeeBps_Fuzz(500, EID_1, 200, true);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function test_getFee_Fuzz(uint16 _defaultFeeBps, uint32 _dstEid, uint240 _amount) public {
|
|
93
|
+
_defaultFeeBps = uint16(bound(_defaultFeeBps, 0, BPS_DENOMINATOR));
|
|
94
|
+
feeConfigHelper.setDefaultFeeBps(_defaultFeeBps);
|
|
95
|
+
|
|
96
|
+
uint256 expectedFee = _defaultFeeBps == 0 ? 0 : (uint256(_amount) * _defaultFeeBps) / BPS_DENOMINATOR;
|
|
97
|
+
assertEq(feeConfigHelper.getFee(_dstEid, _amount), expectedFee);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function test_setDefaultFeeBps_RevertInvalid() public {
|
|
101
|
+
vm.expectRevert(abi.encodeWithSelector(IFeeConfig.InvalidBps.selector, 10001));
|
|
102
|
+
feeConfigHelper.setDefaultFeeBps(10001);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function test_setFeeBps_RevertInvalid() public {
|
|
106
|
+
vm.expectRevert(abi.encodeWithSelector(IFeeConfig.InvalidBps.selector, 10001));
|
|
107
|
+
feeConfigHelper.setFeeBps(EID_1, 10001, true);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function test_getFee_Default() public {
|
|
111
|
+
feeConfigHelper.setDefaultFeeBps(500);
|
|
112
|
+
uint256 amount = 1000;
|
|
113
|
+
uint256 expectedFee = 50;
|
|
114
|
+
assertEq(feeConfigHelper.getFee(EID_1, amount), expectedFee);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function test_getFee_Specific() public {
|
|
118
|
+
feeConfigHelper.setDefaultFeeBps(500);
|
|
119
|
+
feeConfigHelper.setFeeBps(EID_1, 200, true);
|
|
120
|
+
uint256 amount = 1000;
|
|
121
|
+
uint256 expectedFee = 20;
|
|
122
|
+
assertEq(feeConfigHelper.getFee(EID_1, amount), expectedFee);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function test_getFee_SpecificDisabled() public {
|
|
126
|
+
feeConfigHelper.setDefaultFeeBps(500);
|
|
127
|
+
feeConfigHelper.setFeeBps(EID_1, 200, false);
|
|
128
|
+
uint256 amount = 1000;
|
|
129
|
+
uint256 expectedFee = 50;
|
|
130
|
+
assertEq(feeConfigHelper.getFee(EID_1, amount), expectedFee);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function test_getFee_BoundaryConditions() public {
|
|
134
|
+
feeConfigHelper.setDefaultFeeBps(500);
|
|
135
|
+
assertEq(feeConfigHelper.getFee(EID_1, 0), 0);
|
|
136
|
+
feeConfigHelper.setFeeBps(EID_1, 10000, true);
|
|
137
|
+
assertEq(feeConfigHelper.getFee(EID_1, 100), 100);
|
|
138
|
+
feeConfigHelper.setFeeBps(EID_1, 0, true);
|
|
139
|
+
assertEq(feeConfigHelper.getFee(EID_1, 1000), 0);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function test_getFee_Rounding() public {
|
|
143
|
+
feeConfigHelper.setDefaultFeeBps(1000);
|
|
144
|
+
assertEq(feeConfigHelper.getFee(EID_1, 15), 1);
|
|
145
|
+
assertEq(feeConfigHelper.getFee(EID_1, 9), 0);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// ============ getAmountBeforeFee Tests ============
|
|
149
|
+
|
|
150
|
+
function test_getAmountBeforeFee_ZeroBps() public view {
|
|
151
|
+
assertEq(feeConfigHelper.getAmountBeforeFee(EID_1, 0), 0);
|
|
152
|
+
assertEq(feeConfigHelper.getAmountBeforeFee(EID_1, 1000), 1000);
|
|
153
|
+
assertEq(feeConfigHelper.getAmountBeforeFee(EID_1, type(uint256).max), type(uint256).max);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function test_getAmountBeforeFee_MaxBps() public {
|
|
157
|
+
feeConfigHelper.setDefaultFeeBps(BPS_DENOMINATOR);
|
|
158
|
+
assertEq(feeConfigHelper.getAmountBeforeFee(EID_1, 0), 0);
|
|
159
|
+
assertEq(feeConfigHelper.getAmountBeforeFee(EID_1, 1000), 0);
|
|
160
|
+
assertEq(feeConfigHelper.getAmountBeforeFee(EID_1, type(uint256).max), 0);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function test_getAmountBeforeFee_Default() public {
|
|
164
|
+
feeConfigHelper.setDefaultFeeBps(500);
|
|
165
|
+
assertEq(feeConfigHelper.getAmountBeforeFee(EID_1, 950), 1000);
|
|
166
|
+
assertEq(feeConfigHelper.getAmountBeforeFee(EID_2, 950), 1000);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function test_getAmountBeforeFee_Specific() public {
|
|
170
|
+
feeConfigHelper.setDefaultFeeBps(500);
|
|
171
|
+
feeConfigHelper.setFeeBps(EID_1, 200, true);
|
|
172
|
+
assertEq(feeConfigHelper.getAmountBeforeFee(EID_1, 980), 1000);
|
|
173
|
+
assertEq(feeConfigHelper.getAmountBeforeFee(EID_2, 950), 1000);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function test_getAmountBeforeFee_SpecificDisabled() public {
|
|
177
|
+
feeConfigHelper.setDefaultFeeBps(500);
|
|
178
|
+
feeConfigHelper.setFeeBps(EID_1, 200, false);
|
|
179
|
+
assertEq(feeConfigHelper.getAmountBeforeFee(EID_1, 950), 1000);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function test_getAmountBeforeFee_ZeroAmount() public {
|
|
183
|
+
feeConfigHelper.setDefaultFeeBps(500);
|
|
184
|
+
assertEq(feeConfigHelper.getAmountBeforeFee(EID_1, 0), 0);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function test_getAmountBeforeFee_Rounding() public {
|
|
188
|
+
feeConfigHelper.setDefaultFeeBps(1000);
|
|
189
|
+
|
|
190
|
+
// (15 * 10000) / 9000 = 16, getFee(16) = 1, 16-1 = 15.
|
|
191
|
+
assertEq(feeConfigHelper.getAmountBeforeFee(EID_1, 15), 16);
|
|
192
|
+
// (9 * 10000) / 9000 = 10, getFee(10) = 1, 10-1 = 9.
|
|
193
|
+
assertEq(feeConfigHelper.getAmountBeforeFee(EID_1, 9), 10);
|
|
194
|
+
// (1 * 10000) / 9000 = 1, getFee(1) = 0, 1-0 = 1.
|
|
195
|
+
assertEq(feeConfigHelper.getAmountBeforeFee(EID_1, 1), 1);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function test_getAmountBeforeFee_BoundaryConditions() public {
|
|
199
|
+
// 1 bps (0.01%) — near-zero fee.
|
|
200
|
+
feeConfigHelper.setDefaultFeeBps(1);
|
|
201
|
+
assertEq(feeConfigHelper.getAmountBeforeFee(EID_1, 9999), 10000);
|
|
202
|
+
assertEq(feeConfigHelper.getAmountBeforeFee(EID_1, 1), 1);
|
|
203
|
+
|
|
204
|
+
// 9999 bps (99.99%) — near-total fee.
|
|
205
|
+
feeConfigHelper.setDefaultFeeBps(9999);
|
|
206
|
+
// (1 * 10000) / 1 = 10000, getFee(10000) = 9999, 10000-9999 = 1.
|
|
207
|
+
assertEq(feeConfigHelper.getAmountBeforeFee(EID_1, 1), 10000);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function test_getAmountBeforeFee_Fuzz(uint16 _feeBps, uint224 _amountAfterFee) public {
|
|
211
|
+
_feeBps = uint16(bound(_feeBps, 0, BPS_DENOMINATOR));
|
|
212
|
+
feeConfigHelper.setDefaultFeeBps(_feeBps);
|
|
213
|
+
|
|
214
|
+
uint256 amountBefore = feeConfigHelper.getAmountBeforeFee(EID_1, _amountAfterFee);
|
|
215
|
+
|
|
216
|
+
if (_feeBps == BPS_DENOMINATOR) {
|
|
217
|
+
assertEq(amountBefore, 0);
|
|
218
|
+
} else if (_feeBps == 0) {
|
|
219
|
+
assertEq(amountBefore, _amountAfterFee);
|
|
220
|
+
} else {
|
|
221
|
+
// Core inverse invariant: `amountBefore - getFee(amountBefore) == amountAfterFee`.
|
|
222
|
+
uint256 fee = feeConfigHelper.getFee(EID_1, amountBefore);
|
|
223
|
+
assertEq(amountBefore - fee, _amountAfterFee);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function test_storageHash() public pure {
|
|
228
|
+
bytes32 storageHash = keccak256(abi.encode(uint256(keccak256("layerzerov2.storage.feeconfig")) - 1)) &
|
|
229
|
+
~bytes32(uint256(0xff));
|
|
230
|
+
assertEq(storageHash, 0x19c40dd5c7b9d6e4dbe259e67955cccfb75eaf6c218fbfbd413bfcd8248dd800);
|
|
231
|
+
}
|
|
232
|
+
}
|