@bananapus/721-hook-v6 0.0.41 → 0.0.43

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.
Files changed (77) hide show
  1. package/foundry.lock +1 -7
  2. package/foundry.toml +1 -1
  3. package/package.json +20 -9
  4. package/script/Deploy.s.sol +2 -2
  5. package/src/JB721Checkpoints.sol +60 -18
  6. package/src/JB721CheckpointsDeployer.sol +10 -5
  7. package/src/JB721TiersHook.sol +4 -1
  8. package/src/JB721TiersHookProjectDeployer.sol +68 -30
  9. package/src/JB721TiersHookStore.sol +1 -4
  10. package/src/interfaces/IJB721Checkpoints.sol +21 -14
  11. package/src/interfaces/IJB721CheckpointsDeployer.sol +6 -2
  12. package/src/interfaces/IJB721TiersHookProjectDeployer.sol +2 -0
  13. package/test/utils/AccessJBLib.sol +49 -0
  14. package/test/utils/ForTest_JB721TiersHook.sol +246 -0
  15. package/test/utils/TestBaseWorkflow.sol +213 -0
  16. package/test/utils/UnitTestSetup.sol +805 -0
  17. package/.gas-snapshot +0 -152
  18. package/ADMINISTRATION.md +0 -87
  19. package/ARCHITECTURE.md +0 -98
  20. package/AUDIT_INSTRUCTIONS.md +0 -77
  21. package/RISKS.md +0 -118
  22. package/SKILLS.md +0 -43
  23. package/STYLE_GUIDE.md +0 -610
  24. package/USER_JOURNEYS.md +0 -121
  25. package/assets/findings/nana-721-hook-v6-pashov-ai-audit-report-20260330-091257.md +0 -83
  26. package/slither-ci.config.json +0 -10
  27. package/test/721HookAttacks.t.sol +0 -408
  28. package/test/E2E/Pay_Mint_Redeem_E2E.t.sol +0 -985
  29. package/test/Fork.t.sol +0 -2346
  30. package/test/TestAuditGaps.sol +0 -1075
  31. package/test/TestCheckpoints.t.sol +0 -341
  32. package/test/TestSafeTransferReentrancy.t.sol +0 -305
  33. package/test/TestVotingUnitsLifecycle.t.sol +0 -313
  34. package/test/audit/AuditRegressions.t.sol +0 -83
  35. package/test/audit/CrossCurrencySplitNoPrices.t.sol +0 -123
  36. package/test/audit/FreshAudit.t.sol +0 -197
  37. package/test/audit/FutureTierPoC.t.sol +0 -39
  38. package/test/audit/FutureTierRemoval.t.sol +0 -47
  39. package/test/audit/Pass12L18.t.sol +0 -80
  40. package/test/audit/PayCreditsBypassTierSplits.t.sol +0 -200
  41. package/test/audit/ProjectDeployerAuth.t.sol +0 -266
  42. package/test/audit/RepoFindings.t.sol +0 -195
  43. package/test/audit/ReserveActivation.t.sol +0 -87
  44. package/test/audit/RetroactiveReserveBeneficiaryDilution.t.sol +0 -149
  45. package/test/audit/SameCurrencyDecimalMismatch.t.sol +0 -249
  46. package/test/audit/SplitCreditsMismatch.t.sol +0 -219
  47. package/test/audit/SplitFailureRedistribution.t.sol +0 -143
  48. package/test/audit/USDTVoidReturnCompat.t.sol +0 -301
  49. package/test/fork/ERC20CashOutFork.t.sol +0 -633
  50. package/test/fork/ERC20TierSplitFork.t.sol +0 -596
  51. package/test/fork/IssueTokensForSplitsFork.t.sol +0 -516
  52. package/test/invariants/TierLifecycleInvariant.t.sol +0 -188
  53. package/test/invariants/TieredHookStoreInvariant.t.sol +0 -86
  54. package/test/invariants/handlers/TierLifecycleHandler.sol +0 -300
  55. package/test/invariants/handlers/TierStoreHandler.sol +0 -165
  56. package/test/regression/BrokenTerminalDoesNotDos.t.sol +0 -277
  57. package/test/regression/CacheTierLookup.t.sol +0 -190
  58. package/test/regression/ProjectDeployerRulesets.t.sol +0 -358
  59. package/test/regression/ReserveBeneficiaryOverwrite.t.sol +0 -155
  60. package/test/regression/SplitDistributionBugs.t.sol +0 -751
  61. package/test/regression/SplitNoBeneficiary.t.sol +0 -140
  62. package/test/unit/AuditFixes_Unit.t.sol +0 -624
  63. package/test/unit/JB721CheckpointsDeployer_AccessControl.t.sol +0 -116
  64. package/test/unit/JB721TiersRulesetMetadataResolver.t.sol +0 -144
  65. package/test/unit/JBBitmap.t.sol +0 -170
  66. package/test/unit/JBIpfsDecoder.t.sol +0 -136
  67. package/test/unit/TierSupplyReserveCheck.t.sol +0 -221
  68. package/test/unit/adjustTier_Unit.t.sol +0 -1942
  69. package/test/unit/deployer_Unit.t.sol +0 -114
  70. package/test/unit/getters_constructor_Unit.t.sol +0 -593
  71. package/test/unit/mintFor_mintReservesFor_Unit.t.sol +0 -452
  72. package/test/unit/pay_CrossCurrency_Unit.t.sol +0 -530
  73. package/test/unit/pay_Unit.t.sol +0 -1661
  74. package/test/unit/redeem_Unit.t.sol +0 -473
  75. package/test/unit/relayBeneficiary_Unit.t.sol +0 -182
  76. package/test/unit/splitHookDistribution_Unit.t.sol +0 -604
  77. package/test/unit/tierSplitRouting_Unit.t.sol +0 -757
@@ -1,1942 +0,0 @@
1
- // SPDX-License-Identifier: MIT
2
- pragma solidity 0.8.28;
3
-
4
- // forge-lint: disable-next-line(unaliased-plain-import)
5
- import "../utils/UnitTestSetup.sol";
6
- import {JB721TierConfigFlags} from "../../src/structs/JB721TierConfigFlags.sol";
7
- import {JB721TierFlags} from "../../src/structs/JB721TierFlags.sol";
8
-
9
- contract Test_adjustTier_Unit is UnitTestSetup {
10
- using stdStorage for StdStorage;
11
-
12
- function test_adjustTiers_removeTiersMultipleTimes(
13
- uint256 initialNumberOfTiers,
14
- uint256 numberOfTiersToAdd,
15
- uint256 seed
16
- )
17
- public
18
- {
19
- // Include adding X new tiers with 0 current tiers.
20
- initialNumberOfTiers = bound(initialNumberOfTiers, 0, 10);
21
-
22
- numberOfTiersToAdd = bound(numberOfTiersToAdd, 4, 14);
23
- uint16[] memory pricesForTiersToAdd = _createArray(numberOfTiersToAdd, seed);
24
-
25
- // Sort tiers in ascending order.
26
- pricesForTiersToAdd = _sortArray(pricesForTiersToAdd);
27
-
28
- // Initialize the hook with default tiers.
29
- JB721TiersHook hook = _initHookDefaultTiers(initialNumberOfTiers);
30
-
31
- // Create the configs for the new tiers to add.
32
- (JB721TierConfig[] memory tierConfigs,) =
33
- _createTiers(defaultTierConfig, numberOfTiersToAdd, initialNumberOfTiers, pricesForTiersToAdd);
34
-
35
- // Remove 2 tiers and add the new ones. `_addDeleteTiers` calls `adjustTiers`.
36
- uint256 tiersLeft = initialNumberOfTiers;
37
- tiersLeft = _addDeleteTiers(hook, tiersLeft, 2, tierConfigs);
38
-
39
- // Remove another 2 tiers but don't add any new ones.
40
- _addDeleteTiers(hook, tiersLeft, 2, new JB721TierConfig[](0));
41
-
42
- // Fecth a maximum of 100 tiers (will downsize).
43
- JB721Tier[] memory storedTiers = hook.STORE().tiersOf(address(hook), new uint256[](0), false, 0, 100);
44
- // If the tiers have same category, then the the tiers added last will have a lower index in the array.
45
- // Otherwise, the tiers would be sorted by categories.
46
- for (uint256 i = 1; i < storedTiers.length; i++) {
47
- if (storedTiers[i - 1].category == storedTiers[i].category) {
48
- assertGt(storedTiers[i - 1].id, storedTiers[i].id, "Sorting error (if same category).");
49
- } else {
50
- assertLt(storedTiers[i - 1].category, storedTiers[i].category, "Sorting error (if different category).");
51
- }
52
- }
53
- }
54
-
55
- function test_tiersOf_recentlyAddedTiersFetchedFirstWhenSortedAfterTiersCleaned(
56
- uint256 initialNumberOfTiers,
57
- uint256 numberOfTiersToAdd,
58
- uint256 seed
59
- )
60
- public
61
- {
62
- initialNumberOfTiers = bound(initialNumberOfTiers, 3, 14);
63
- numberOfTiersToAdd = bound(numberOfTiersToAdd, 1, 15);
64
- uint16[] memory pricesForTiersToAdd = _createArray(numberOfTiersToAdd, seed);
65
-
66
- // Sort tiers in ascending order.
67
- pricesForTiersToAdd = _sortArray(pricesForTiersToAdd);
68
-
69
- JB721TiersHook hook = _initHookDefaultTiers(initialNumberOfTiers);
70
-
71
- // Create the new tiers to add.
72
- uint256 currentNumberOfTiers = initialNumberOfTiers;
73
-
74
- (JB721TierConfig[] memory tierConfigsToAdd,) =
75
- _createTiers(defaultTierConfig, numberOfTiersToAdd, initialNumberOfTiers, pricesForTiersToAdd);
76
-
77
- currentNumberOfTiers = _addDeleteTiers(hook, currentNumberOfTiers, 0, tierConfigsToAdd);
78
- currentNumberOfTiers = _addDeleteTiers(hook, currentNumberOfTiers, 2, tierConfigsToAdd);
79
-
80
- JB721Tier[] memory storedTiers = hook.STORE().tiersOf(address(hook), new uint256[](0), false, 0, 100);
81
-
82
- assertEq(storedTiers.length, currentNumberOfTiers, "Length mismatch.");
83
- // If the tiers have same category, then the tiers added last will have a lower index in the array.
84
- // Otherwise, the tiers would be sorted by categories.
85
- for (uint256 i = 1; i < storedTiers.length; i++) {
86
- if (storedTiers[i - 1].category == storedTiers[i].category) {
87
- assertGt(storedTiers[i - 1].id, storedTiers[i].id, "Sorting error (if same category).");
88
- } else {
89
- assertLt(storedTiers[i - 1].category, storedTiers[i].category, "Sorting error (if different category).");
90
- }
91
- }
92
- }
93
-
94
- function test_tiersOf_recentlyAddedTiersFetchedFirstWhenSorted(
95
- uint256 initialNumberOfTiers,
96
- uint256 numberOfTiersToAdd,
97
- uint256 seed
98
- )
99
- public
100
- {
101
- // Include adding X new tiers with 0 current tiers.
102
- initialNumberOfTiers = bound(initialNumberOfTiers, 0, 10);
103
-
104
- numberOfTiersToAdd = bound(numberOfTiersToAdd, 4, 14);
105
- uint16[] memory pricesForTiersToAdd = _createArray(numberOfTiersToAdd, seed);
106
-
107
- // Sort tiers in ascending order.
108
- pricesForTiersToAdd = _sortArray(pricesForTiersToAdd);
109
-
110
- // Initialize the hook with default tiers.
111
- JB721TiersHook hook = _initHookDefaultTiers(initialNumberOfTiers);
112
-
113
- // Create the new tiers to add.
114
- (JB721TierConfig[] memory tierConfigs,) =
115
- _createTiers(defaultTierConfig, numberOfTiersToAdd, initialNumberOfTiers, pricesForTiersToAdd);
116
-
117
- // Add the new tiers.
118
- uint256 tiersLeft = initialNumberOfTiers;
119
-
120
- tiersLeft = _addDeleteTiers(hook, tiersLeft, 0, tierConfigs);
121
-
122
- JB721Tier[] memory storedTiers = hook.STORE().tiersOf(address(hook), new uint256[](0), false, 0, 100);
123
- // If the tiers have same category, then the tiers added last will have a lower index in the array.
124
- // Otherwise, the tiers would be sorted by categories.
125
- for (uint256 i = 1; i < storedTiers.length; i++) {
126
- if (storedTiers[i - 1].category == storedTiers[i].category) {
127
- assertGt(storedTiers[i - 1].id, storedTiers[i].id, "Sorting error (if same category).");
128
- } else {
129
- assertLt(storedTiers[i - 1].category, storedTiers[i].category, "Sorting error (if different category).");
130
- }
131
- }
132
- }
133
-
134
- function test_adjustTiers_addNewTiersWithNonSequentialCategories(
135
- uint256 initialNumberOfTiers,
136
- uint256 numberOfTiersToAdd,
137
- uint256 seed
138
- )
139
- public
140
- {
141
- initialNumberOfTiers = bound(initialNumberOfTiers, 2, 10);
142
-
143
- numberOfTiersToAdd = bound(numberOfTiersToAdd, 4, 14);
144
- uint16[] memory pricesForTiersToAdd = _createArray(numberOfTiersToAdd, seed);
145
-
146
- // Sort tiers in ascending order.
147
- pricesForTiersToAdd = _sortArray(pricesForTiersToAdd);
148
-
149
- // Initialize the hook with default tiers.
150
- JB721TiersHook hook = _initHookDefaultTiers(initialNumberOfTiers);
151
-
152
- JB721Tier[] memory defaultStoredTiers = hook.STORE().tiersOf(address(hook), new uint256[](0), false, 0, 100);
153
-
154
- // Create the new tiers to add.
155
- (JB721TierConfig[] memory tierConfigsToAdd, JB721Tier[] memory tiersToAdd) =
156
- _createTiers(defaultTierConfig, numberOfTiersToAdd, initialNumberOfTiers, pricesForTiersToAdd, 2);
157
-
158
- // Add the new tiers.
159
- uint256 tiersLeft = initialNumberOfTiers;
160
-
161
- tiersLeft = _addDeleteTiers(hook, tiersLeft, 0, tierConfigsToAdd);
162
-
163
- JB721Tier[] memory storedTiers = hook.STORE().tiersOf(address(hook), new uint256[](0), false, 0, 100);
164
-
165
- // Check: was the expected number of tiers returned?
166
- assertEq(storedTiers.length, tiersLeft, "Length mismatch.");
167
-
168
- // Check: are all tiers in the new tiers (unsorted)?
169
- assertTrue(_isIn(defaultStoredTiers, storedTiers), "Original tiers not stored."); // Original tiers
170
- assertTrue(_isIn(tiersToAdd, storedTiers), "Added tiers not stored."); // New tiers
171
-
172
- // Check: are all the tiers sorted?
173
- for (uint256 i = 1; i < storedTiers.length; i++) {
174
- assertLt(storedTiers[i - 1].category, storedTiers[i].category, "Sorting error.");
175
- }
176
- }
177
-
178
- function test_adjustTiers_addNewTiers(
179
- uint256 initialNumberOfTiers,
180
- uint256 numberOfTiersToAdd,
181
- uint256 seed
182
- )
183
- public
184
- {
185
- // Include adding X new tiers with 0 current tiers.
186
- initialNumberOfTiers = bound(initialNumberOfTiers, 0, 10);
187
-
188
- numberOfTiersToAdd = bound(numberOfTiersToAdd, 4, 14);
189
- uint16[] memory pricesForTiersToAdd = _createArray(numberOfTiersToAdd, seed);
190
-
191
- // Sort tiers in ascending order.
192
- pricesForTiersToAdd = _sortArray(pricesForTiersToAdd);
193
-
194
- // Initialize the hook with default tiers.
195
- JB721TiersHook hook = _initHookDefaultTiers(initialNumberOfTiers);
196
-
197
- JB721Tier[] memory intialTiers = hook.STORE().tiersOf(address(hook), new uint256[](0), false, 0, 100);
198
-
199
- // Create the new tiers to add.
200
- (JB721TierConfig[] memory tierConfigs, JB721Tier[] memory tiersAdded) =
201
- _createTiers(defaultTierConfig, numberOfTiersToAdd, initialNumberOfTiers, pricesForTiersToAdd);
202
-
203
- // Add the new tiers.
204
- uint256 tiersLeft = initialNumberOfTiers;
205
-
206
- _addDeleteTiers(hook, tiersLeft, 0, tierConfigs);
207
-
208
- JB721Tier[] memory storedTiers = hook.STORE().tiersOf(address(hook), new uint256[](0), false, 0, 100);
209
-
210
- // Check: was the expected number of tiers returned?
211
- assertEq(storedTiers.length, intialTiers.length + tiersAdded.length, "Length mismatch.");
212
-
213
- // Check: are all tiers in the new tiers (unsorted)?
214
- assertTrue(_isIn(intialTiers, storedTiers), "original tiers not found"); // Original tiers
215
- assertTrue(_isIn(tiersAdded, storedTiers), "new tiers not found"); // New tiers
216
-
217
- // Check: are all the tiers sorted?
218
- for (uint256 i = 1; i < storedTiers.length; i++) {
219
- assertLe(storedTiers[i - 1].category, storedTiers[i].category, "Sorting error");
220
- }
221
- }
222
-
223
- function test_adjustTiers_withSameCategoryMultipleTimes(
224
- uint256 initialNumberOfTiers,
225
- uint256 numberOfTiersToAdd,
226
- uint256 seed
227
- )
228
- public
229
- {
230
- initialNumberOfTiers = bound(initialNumberOfTiers, 2, 10);
231
- numberOfTiersToAdd = bound(numberOfTiersToAdd, 4, 14);
232
-
233
- uint16[] memory pricesForTiersToAdd = _createArray(numberOfTiersToAdd, seed);
234
-
235
- // Sort tiers in ascending order.
236
- pricesForTiersToAdd = _sortArray(pricesForTiersToAdd);
237
-
238
- // Initialize the hook with default tiers of a given category.
239
- defaultTierConfig.category = 100;
240
- JB721TiersHook hook = _initHookDefaultTiers(initialNumberOfTiers);
241
-
242
- // Create new tiers to add with a new category.
243
- defaultTierConfig.category = 101;
244
- (JB721TierConfig[] memory tierConfigsToAdd, JB721Tier[] memory tiersToAdd) =
245
- _createTiers(defaultTierConfig, numberOfTiersToAdd, initialNumberOfTiers, pricesForTiersToAdd);
246
-
247
- // Add the new tiers.
248
- _addDeleteTiers(hook, 0, 0, tierConfigsToAdd);
249
-
250
- (tierConfigsToAdd, tiersToAdd) = _createTiers(defaultTierConfig, numberOfTiersToAdd);
251
-
252
- _addDeleteTiers(hook, 0, 0, tierConfigsToAdd);
253
-
254
- // Check: are all tiers stored?
255
- JB721Tier[] memory storedTiers = hook.STORE().tiersOf(address(hook), new uint256[](0), false, 0, 100);
256
- assertEq(storedTiers.length, initialNumberOfTiers + pricesForTiersToAdd.length * 2);
257
-
258
- // Check: are all tiers in the new tiers (unsorted)?
259
- uint256[] memory categories = new uint256[](1);
260
- categories[0] = 101;
261
-
262
- JB721Tier[] memory stored101Tiers =
263
- hook.STORE().tiersOf(address(hook), categories, false, 0, pricesForTiersToAdd.length * 2);
264
-
265
- assertEq(stored101Tiers.length, pricesForTiersToAdd.length * 2);
266
-
267
- // Check: are all the tiers in the initial tiers?
268
- categories[0] = 100;
269
- JB721Tier[] memory stored100Tiers = hook.STORE().tiersOf(address(hook), categories, false, 0, 100);
270
- assertEq(stored100Tiers.length, initialNumberOfTiers);
271
-
272
- // Check: are the tiers sorted correctly?
273
- for (uint256 i; i < pricesForTiersToAdd.length; i++) {
274
- assertGt(stored101Tiers[i].id, stored101Tiers[i + pricesForTiersToAdd.length].id);
275
- }
276
- }
277
-
278
- function test_adjustTiers_withDifferentCategories(
279
- uint256 initialNumberOfTiers,
280
- uint256 numberOfTiersToAdd,
281
- uint256 seed
282
- )
283
- public
284
- {
285
- // Include adding X new tiers with 0 current tiers.
286
- initialNumberOfTiers = bound(initialNumberOfTiers, 1, 10);
287
-
288
- numberOfTiersToAdd = bound(numberOfTiersToAdd, 1, 10);
289
- uint16[] memory pricesForTiersToAdd = _createArray(numberOfTiersToAdd, seed);
290
-
291
- // Sort tiers in ascending order.
292
- pricesForTiersToAdd = _sortArray(pricesForTiersToAdd);
293
-
294
- // Initialize the hook with default tiers.
295
- defaultTierConfig.category = 100;
296
- JB721TiersHook hook = _initHookDefaultTiers(initialNumberOfTiers);
297
-
298
- // Create new tiers to add.
299
- defaultTierConfig.category = 101;
300
- (JB721TierConfig[] memory tierConfigs, JB721Tier[] memory tiersAdded) =
301
- _createTiers(defaultTierConfig, numberOfTiersToAdd, initialNumberOfTiers, pricesForTiersToAdd);
302
-
303
- // Add the new tiers.
304
- uint256 tiersLeft = initialNumberOfTiers;
305
-
306
- tiersLeft = _addDeleteTiers(hook, tiersLeft, 0, tierConfigs);
307
-
308
- defaultTierConfig.category = 102;
309
- (tierConfigs, tiersAdded) =
310
- _createTiers(defaultTierConfig, numberOfTiersToAdd, initialNumberOfTiers, pricesForTiersToAdd);
311
-
312
- // Add the new tiers.
313
- tiersLeft = _addDeleteTiers(hook, tiersLeft, 0, tierConfigs);
314
-
315
- JB721Tier[] memory allStoredTiers = hook.STORE().tiersOf(address(hook), new uint256[](0), false, 0, 100);
316
-
317
- uint256[] memory categories = new uint256[](1);
318
- categories[0] = 102;
319
- JB721Tier[] memory stored102Tiers = hook.STORE().tiersOf(address(hook), categories, false, 0, 100);
320
-
321
- // Check: does the number of stored 102 tiers match the number of tiers that were added?
322
- assertEq(stored102Tiers.length, pricesForTiersToAdd.length);
323
-
324
- // Ensure that each stored 102 tier has category `102`.
325
- for (uint256 i = 0; i < stored102Tiers.length; i++) {
326
- assertEq(stored102Tiers[i].category, uint8(102));
327
- }
328
-
329
- categories[0] = 101;
330
-
331
- JB721Tier[] memory stored101Tiers = hook.STORE().tiersOf(address(hook), categories, false, 0, 100);
332
-
333
- // Check: does the number of stored 101 tiers match the number of tiers that were added?
334
- assertEq(stored101Tiers.length, pricesForTiersToAdd.length);
335
-
336
- // Check: does each stored 101 tier have category `101`?
337
- for (uint256 i = 0; i < stored101Tiers.length; i++) {
338
- assertEq(stored101Tiers[i].category, uint8(101));
339
- }
340
-
341
- // Ensure that the tiers are sorted.
342
- for (uint256 i = 1; i < initialNumberOfTiers + pricesForTiersToAdd.length * 2; i++) {
343
- assertGt(allStoredTiers[i].id, allStoredTiers[i - 1].id);
344
- assertLe(allStoredTiers[i - 1].category, allStoredTiers[i].category);
345
- }
346
- }
347
-
348
- function test_adjustTiers_withZeroCategory(
349
- uint256 initialNumberOfTiers,
350
- uint256 numberOfTiersToAdd,
351
- uint256 seed
352
- )
353
- public
354
- {
355
- initialNumberOfTiers = bound(initialNumberOfTiers, 2, 10);
356
-
357
- numberOfTiersToAdd = bound(numberOfTiersToAdd, 4, 14);
358
- uint16[] memory pricesForTiersToAdd = _createArray(numberOfTiersToAdd, seed);
359
-
360
- // Sort tiers in ascending order.
361
- pricesForTiersToAdd = _sortArray(pricesForTiersToAdd);
362
-
363
- // Use category 0 for the default tiers.
364
- defaultTierConfig.category = 0;
365
-
366
- // Initialize the hook with default tiers.
367
- JB721TiersHook hook = _initHookDefaultTiers(initialNumberOfTiers);
368
-
369
- // Create new tiers to add.
370
- defaultTierConfig.category = 5;
371
- (JB721TierConfig[] memory tierConfigsToAdd,) =
372
- _createTiers(defaultTierConfig, numberOfTiersToAdd, initialNumberOfTiers, pricesForTiersToAdd, 2);
373
-
374
- // Add the new tiers.
375
- uint256 tiersLeft = initialNumberOfTiers;
376
- tiersLeft = _addDeleteTiers(hook, tiersLeft, 0, tierConfigsToAdd);
377
-
378
- // Get all stored tiers (category 0 gets all).
379
- uint256[] memory categories = new uint256[](1);
380
- categories[0] = 0;
381
- JB721Tier[] memory allStoredTiers = hook.STORE().tiersOf(address(hook), categories, false, 0, 100);
382
-
383
- // Check: does the number of stored tiers match the number of tiers that were added?
384
- assertEq(allStoredTiers.length, initialNumberOfTiers);
385
- // Check: does each stored tier have category `0`?
386
- for (uint256 i = 0; i < allStoredTiers.length; i++) {
387
- assertEq(allStoredTiers[i].category, uint8(0));
388
- }
389
- }
390
-
391
- function test_adjustTiers_withDifferentCategoriesAndFetchedTogether(
392
- uint256 initialNumberOfTiers,
393
- uint256 numberOfTiersToAdd,
394
- uint256 seed
395
- )
396
- public
397
- {
398
- initialNumberOfTiers = bound(initialNumberOfTiers, 1, 14);
399
-
400
- numberOfTiersToAdd = bound(numberOfTiersToAdd, 1, 14);
401
- uint16[] memory pricesForTiersToAdd = _createArray(numberOfTiersToAdd, seed);
402
-
403
- // Sort tiers in ascending order.
404
- pricesForTiersToAdd = _sortArray(pricesForTiersToAdd);
405
-
406
- // Initialize the hook with default tiers of category 100.
407
- defaultTierConfig.category = 100;
408
- JB721TiersHook hook = _initHookDefaultTiers(initialNumberOfTiers);
409
-
410
- // Create new tiers to add (with category 101).
411
- defaultTierConfig.category = 101;
412
- (JB721TierConfig[] memory tierConfigsToAdd,) =
413
- _createTiers(defaultTierConfig, numberOfTiersToAdd, initialNumberOfTiers, pricesForTiersToAdd);
414
-
415
- // Add the 101 tiers.
416
- uint256 tiersLeft = initialNumberOfTiers;
417
- tiersLeft = _addDeleteTiers(hook, tiersLeft, 0, tierConfigsToAdd);
418
-
419
- // Create new tiers to add (with category 102).
420
- defaultTierConfig.category = 102;
421
- (tierConfigsToAdd,) =
422
- _createTiers(defaultTierConfig, numberOfTiersToAdd, initialNumberOfTiers, pricesForTiersToAdd);
423
-
424
- // Add the 102 tiers.
425
- tiersLeft = _addDeleteTiers(hook, tiersLeft, 0, tierConfigsToAdd);
426
-
427
- // Get the stored tiers with categories 100-102.
428
- uint256[] memory categories = new uint256[](3);
429
- categories[0] = 102;
430
- categories[1] = 100;
431
- categories[2] = 101;
432
- JB721Tier[] memory allStoredTiers = hook.STORE().tiersOf(address(hook), categories, false, 0, 100);
433
-
434
- // Check: are the correct number of tiers stored?
435
- assertEq(
436
- allStoredTiers.length, initialNumberOfTiers + pricesForTiersToAdd.length * 2, "Wrong total number of tiers."
437
- );
438
-
439
- uint256 maxIndexForTier100 = allStoredTiers.length - pricesForTiersToAdd.length;
440
-
441
- // Check: do the tiers have the correct categories?
442
- for (uint256 i = 0; i < pricesForTiersToAdd.length; i++) {
443
- assertEq(allStoredTiers[i].category, uint8(102), "Wrong, first category (102).");
444
- }
445
-
446
- for (uint256 i = pricesForTiersToAdd.length; i < maxIndexForTier100; i++) {
447
- assertEq(allStoredTiers[i].category, uint8(100), "Wrong, second category (100).");
448
- }
449
-
450
- for (uint256 i = maxIndexForTier100; i < allStoredTiers.length; i++) {
451
- assertEq(allStoredTiers[i].category, uint8(101), "Wrong, third category (101).");
452
- }
453
- }
454
-
455
- function test_adjustTiers_addNewTiers_fetchSpecificTier(
456
- uint256 initialNumberOfTiers,
457
- uint256 numberOfTiersToAdd,
458
- uint256 seed
459
- )
460
- public
461
- {
462
- initialNumberOfTiers = bound(initialNumberOfTiers, 1, 14);
463
-
464
- numberOfTiersToAdd = bound(numberOfTiersToAdd, 1, 14);
465
- uint16[] memory pricesForTiersToAdd = _createArray(numberOfTiersToAdd, seed);
466
-
467
- // Sort tiers in ascending order.
468
- pricesForTiersToAdd = _sortArray(pricesForTiersToAdd);
469
-
470
- // Initialize hook with default tiers from category 100.
471
- defaultTierConfig.category = 100;
472
- JB721TiersHook hook = _initHookDefaultTiers(initialNumberOfTiers);
473
-
474
- // Create new tiers to add (with category 101).
475
- defaultTierConfig.category = 101;
476
- (JB721TierConfig[] memory tierConfigsToAdd,) =
477
- _createTiers(defaultTierConfig, numberOfTiersToAdd, initialNumberOfTiers, pricesForTiersToAdd);
478
-
479
- // Add the new tiers
480
- uint256 tiersLeft = initialNumberOfTiers;
481
- tiersLeft = _addDeleteTiers(hook, tiersLeft, 0, tierConfigsToAdd);
482
-
483
- // Get the tiers from category 101.
484
- uint256[] memory categories = new uint256[](1);
485
- categories[0] = 101;
486
- JB721Tier[] memory storedTiers = hook.STORE()
487
- .tiersOf(address(hook), categories, false, 0, initialNumberOfTiers + pricesForTiersToAdd.length);
488
-
489
- // Check: does the number of stored tiers match the number of tiers that were added?
490
- assertEq(storedTiers.length, pricesForTiersToAdd.length);
491
-
492
- // Check: do the tiers have category 101?
493
- for (uint256 i = 0; i < storedTiers.length; i++) {
494
- assertEq(storedTiers[i].category, uint8(101));
495
- }
496
- }
497
-
498
- function test_adjustTiers_removeTiers(
499
- uint256 initialNumberOfTiers,
500
- uint256 seed,
501
- uint256 numberOfTiersToRemove
502
- )
503
- public
504
- {
505
- initialNumberOfTiers = bound(initialNumberOfTiers, 0, 14);
506
- numberOfTiersToRemove = bound(numberOfTiersToRemove, 0, initialNumberOfTiers);
507
-
508
- // Create random tiers to remove.
509
- uint256[] memory tiersToRemove = new uint256[](numberOfTiersToRemove);
510
-
511
- // Use the `seed` to generate new random tiers, and iterate on `i` to fill the `tiersToRemove` array.
512
- for (uint256 i; i < numberOfTiersToRemove;) {
513
- uint256 newTierCandidate = uint256(keccak256(abi.encode(seed))) % initialNumberOfTiers + 1;
514
- bool invalidTier;
515
- if (newTierCandidate != 0) {
516
- for (uint256 j; j < numberOfTiersToRemove; j++) {
517
- // Same value twice?
518
- if (newTierCandidate == tiersToRemove[j]) {
519
- invalidTier = true;
520
- break;
521
- }
522
- }
523
- if (!invalidTier) {
524
- tiersToRemove[i] = newTierCandidate;
525
- i++;
526
- }
527
- }
528
- // Overflow to loop over (seed is fuzzed, and could start at `max(uint256)`).
529
- unchecked {
530
- seed++;
531
- }
532
- }
533
-
534
- // Order the tiers to remove for event matching (which are ordered too).
535
- tiersToRemove = _sortArray(tiersToRemove);
536
- JB721TierConfig[] memory tierConfigs = new JB721TierConfig[](initialNumberOfTiers);
537
- JB721Tier[] memory tiers = new JB721Tier[](initialNumberOfTiers);
538
-
539
- for (uint256 i; i < initialNumberOfTiers; i++) {
540
- tierConfigs[i] = JB721TierConfig({
541
- price: uint104((i + 1) * 10),
542
- initialSupply: uint32(100),
543
- votingUnits: uint16(0),
544
- // forge-lint: disable-next-line(unsafe-typecast)
545
- reserveFrequency: uint16(i),
546
- reserveBeneficiary: reserveBeneficiary,
547
- encodedIPFSUri: tokenUris[0],
548
- category: uint24(100),
549
- discountPercent: uint8(0),
550
- flags: JB721TierConfigFlags({
551
- allowOwnerMint: false,
552
- useReserveBeneficiaryAsDefault: false,
553
- transfersPausable: false,
554
- useVotingUnits: true,
555
- cantBeRemoved: false,
556
- cantIncreaseDiscountPercent: false,
557
- cantBuyWithCredits: false
558
- }),
559
- splitPercent: 0,
560
- splits: new JBSplit[](0)
561
- });
562
- tiers[i] = JB721Tier({
563
- // forge-lint: disable-next-line(unsafe-typecast)
564
- id: uint32(i + 1),
565
- price: tierConfigs[i].price,
566
- remainingSupply: tierConfigs[i].initialSupply,
567
- initialSupply: tierConfigs[i].initialSupply,
568
- votingUnits: tierConfigs[i].votingUnits,
569
- reserveFrequency: tierConfigs[i].reserveFrequency,
570
- reserveBeneficiary: i == 0 ? address(0) : tierConfigs[i].reserveBeneficiary,
571
- encodedIPFSUri: tierConfigs[i].encodedIPFSUri,
572
- category: tierConfigs[i].category,
573
- discountPercent: tierConfigs[i].discountPercent,
574
- flags: JB721TierFlags({
575
- allowOwnerMint: tierConfigs[i].flags.allowOwnerMint,
576
- transfersPausable: tierConfigs[i].flags.transfersPausable,
577
- cantBeRemoved: tierConfigs[i].flags.cantBeRemoved,
578
- cantIncreaseDiscountPercent: tierConfigs[i].flags.cantIncreaseDiscountPercent,
579
- cantBuyWithCredits: tierConfigs[i].flags.cantBuyWithCredits
580
- }),
581
- splitPercent: 0,
582
- resolvedUri: ""
583
- });
584
- }
585
- ForTest_JB721TiersHookStore store = new ForTest_JB721TiersHookStore();
586
- ForTest_JB721TiersHook hook = new ForTest_JB721TiersHook(
587
- ForTest_JB721TiersHook.ForTestInitConfig({
588
- projectId: projectId,
589
- name: name,
590
- symbol: symbol,
591
- baseUri: baseUri,
592
- tokenUriResolver: IJB721TokenUriResolver(mockTokenUriResolver),
593
- contractUri: contractUri,
594
- tiers: tierConfigs,
595
- flags: JB721TiersHookFlags({
596
- preventOverspending: false,
597
- issueTokensForSplits: false,
598
- noNewTiersWithReserves: false,
599
- noNewTiersWithVotes: false,
600
- noNewTiersWithOwnerMinting: true
601
- })
602
- }),
603
- IJBDirectory(mockJBDirectory),
604
- IJBPrices(mockJBPrices),
605
- IJBRulesets(mockJBRulesets),
606
- IJB721TiersHookStore(address(store)),
607
- IJBSplits(mockJBSplits)
608
- );
609
- hook.transferOwnership(owner);
610
-
611
- // Will be resized later.
612
- JB721TierConfig[] memory tierConfigsRemaining = new JB721TierConfig[](initialNumberOfTiers);
613
- JB721Tier[] memory tiersRemaining = new JB721Tier[](initialNumberOfTiers);
614
- for (uint256 i; i < tiers.length; i++) {
615
- tierConfigsRemaining[i] = tierConfigs[i];
616
- tiersRemaining[i] = tiers[i];
617
- }
618
-
619
- // Iterate through the remaining tiers and remove the ones in `tiersToRemove`.
620
- // Do this by "swapping" the tier to remove with the last element in the array, and then "popping" that last
621
- // element.
622
- for (uint256 i; i < tiersRemaining.length;) {
623
- bool swappedAndPopped;
624
- for (uint256 j; j < tiersToRemove.length; j++) {
625
- if (tiersRemaining[i].id == tiersToRemove[j]) {
626
- // Swap and pop removed tiers.
627
- tiersRemaining[i] = tiersRemaining[tiersRemaining.length - 1];
628
- tierConfigsRemaining[i] = tierConfigsRemaining[tierConfigsRemaining.length - 1];
629
- // Remove the last elelment / reduce array length by 1.
630
- assembly ("memory-safe") {
631
- mstore(tiersRemaining, sub(mload(tiersRemaining), 1))
632
- mstore(tierConfigsRemaining, sub(mload(tierConfigsRemaining), 1))
633
- }
634
- swappedAndPopped = true;
635
- break;
636
- }
637
- }
638
- if (!swappedAndPopped) i++;
639
- }
640
-
641
- // Check: was `RemoveTier` emitted with the correct values?
642
- for (uint256 i; i < tiersToRemove.length; i++) {
643
- vm.expectEmit(true, false, false, true, address(hook));
644
- emit RemoveTier(tiersToRemove[i], owner);
645
- }
646
- vm.prank(owner);
647
- hook.adjustTiers(new JB721TierConfig[](0), tiersToRemove);
648
- {
649
- uint256 finalNumberOfTiers = initialNumberOfTiers - tiersToRemove.length;
650
- JB721Tier[] memory storedTiers =
651
- hook.test_store().tiersOf(address(hook), new uint256[](0), false, 0, finalNumberOfTiers);
652
- // Check: are the expected number of tiers stored?
653
- assertEq(storedTiers.length, finalNumberOfTiers);
654
- // Check: are all of the remaining tiers still stored?
655
- assertTrue(_isIn(tiersRemaining, storedTiers));
656
- // Check: have all the removed tiers tiers been removed from storage?
657
- assertTrue(_isIn(storedTiers, tiersRemaining));
658
- }
659
- }
660
-
661
- function test_adjustTiers_addAndRemoveTiers() public {
662
- uint256 initialNumberOfTiers = 5;
663
- uint256 numberOfTiersToAdd = 5;
664
- uint256 numberOfTiersToRemove = 3;
665
- uint256[] memory tiersToAdd = new uint256[](numberOfTiersToAdd);
666
- tiersToAdd[0] = 1;
667
- tiersToAdd[1] = 4;
668
- tiersToAdd[2] = 5;
669
- tiersToAdd[3] = 6;
670
- tiersToAdd[4] = 10;
671
- uint256[] memory tierIdsToRemove = new uint256[](numberOfTiersToRemove);
672
- tierIdsToRemove[0] = 1;
673
- tierIdsToRemove[1] = 3;
674
- tierIdsToRemove[2] = 4;
675
- // Initial tiers configs and data.
676
- JB721TierConfig[] memory tierConfigs = new JB721TierConfig[](initialNumberOfTiers);
677
- JB721Tier[] memory tiers = new JB721Tier[](initialNumberOfTiers);
678
- for (uint256 i; i < initialNumberOfTiers; i++) {
679
- tierConfigs[i] = JB721TierConfig({
680
- price: uint104((i + 1) * 10),
681
- initialSupply: uint32(100),
682
- votingUnits: uint16(0),
683
- // forge-lint: disable-next-line(unsafe-typecast)
684
- reserveFrequency: uint16(i),
685
- reserveBeneficiary: reserveBeneficiary,
686
- encodedIPFSUri: tokenUris[0],
687
- category: uint24(100),
688
- discountPercent: uint8(0),
689
- flags: JB721TierConfigFlags({
690
- allowOwnerMint: false,
691
- useReserveBeneficiaryAsDefault: false,
692
- transfersPausable: false,
693
- useVotingUnits: true,
694
- cantBeRemoved: false,
695
- cantIncreaseDiscountPercent: false,
696
- cantBuyWithCredits: false
697
- }),
698
- splitPercent: 0,
699
- splits: new JBSplit[](0)
700
- });
701
- tiers[i] = JB721Tier({
702
- // forge-lint: disable-next-line(unsafe-typecast)
703
- id: uint32(i + 1),
704
- price: tierConfigs[i].price,
705
- remainingSupply: tierConfigs[i].initialSupply,
706
- initialSupply: tierConfigs[i].initialSupply,
707
- votingUnits: tierConfigs[i].votingUnits,
708
- reserveFrequency: tierConfigs[i].reserveFrequency,
709
- reserveBeneficiary: i == 0 ? address(0) : tierConfigs[i].reserveBeneficiary,
710
- encodedIPFSUri: tierConfigs[i].encodedIPFSUri,
711
- category: tierConfigs[i].category,
712
- discountPercent: tierConfigs[i].discountPercent,
713
- flags: JB721TierFlags({
714
- allowOwnerMint: tierConfigs[i].flags.allowOwnerMint,
715
- transfersPausable: tierConfigs[i].flags.transfersPausable,
716
- cantBeRemoved: tierConfigs[i].flags.cantBeRemoved,
717
- cantIncreaseDiscountPercent: tierConfigs[i].flags.cantIncreaseDiscountPercent,
718
- cantBuyWithCredits: tierConfigs[i].flags.cantBuyWithCredits
719
- }),
720
- splitPercent: 0,
721
- resolvedUri: ""
722
- });
723
- }
724
- // Deploy the hook and its store with the initial tiers.
725
- vm.etch(hook_i, address(hook).code);
726
- JB721TiersHook hook = JB721TiersHook(hook_i);
727
- hook.initialize(
728
- projectId,
729
- name,
730
- symbol,
731
- baseUri,
732
- IJB721TokenUriResolver(mockTokenUriResolver),
733
- contractUri,
734
- JB721InitTiersConfig({
735
- tiers: tierConfigs, currency: uint32(uint160(JBConstants.NATIVE_TOKEN)), decimals: 18
736
- }),
737
- JB721TiersHookFlags({
738
- preventOverspending: false,
739
- issueTokensForSplits: false,
740
- noNewTiersWithReserves: false,
741
- noNewTiersWithVotes: false,
742
- noNewTiersWithOwnerMinting: true
743
- })
744
- );
745
- hook.transferOwnership(owner);
746
-
747
- // -- Build expected removed/remaining tiers -- //
748
- JB721TierConfig[] memory tierConfigsRemaining = new JB721TierConfig[](2);
749
- JB721Tier[] memory tiersRemaining = new JB721Tier[](2);
750
- uint256 arrayIndex;
751
- for (uint256 i; i < initialNumberOfTiers; i++) {
752
- // Tiers which will remain.
753
- if (i + 1 != 1 && i + 1 != 3 && i + 1 != 4) {
754
- tierConfigsRemaining[arrayIndex] = JB721TierConfig({
755
- price: uint104((i + 1) * 10),
756
- initialSupply: uint32(100),
757
- votingUnits: uint16(0),
758
- // forge-lint: disable-next-line(unsafe-typecast)
759
- reserveFrequency: uint16(i),
760
- reserveBeneficiary: reserveBeneficiary,
761
- encodedIPFSUri: tokenUris[0],
762
- category: uint24(100),
763
- discountPercent: uint8(0),
764
- flags: JB721TierConfigFlags({
765
- allowOwnerMint: false,
766
- useReserveBeneficiaryAsDefault: false,
767
- transfersPausable: false,
768
- useVotingUnits: true,
769
- cantBeRemoved: false,
770
- cantIncreaseDiscountPercent: false,
771
- cantBuyWithCredits: false
772
- }),
773
- splitPercent: 0,
774
- splits: new JBSplit[](0)
775
- });
776
- tiersRemaining[arrayIndex] = JB721Tier({
777
- // forge-lint: disable-next-line(unsafe-typecast)
778
- id: uint32(i + 1),
779
- price: tierConfigsRemaining[arrayIndex].price,
780
- remainingSupply: tierConfigsRemaining[arrayIndex].initialSupply,
781
- initialSupply: tierConfigsRemaining[arrayIndex].initialSupply,
782
- votingUnits: tierConfigsRemaining[arrayIndex].votingUnits,
783
- reserveFrequency: tierConfigsRemaining[arrayIndex].reserveFrequency,
784
- reserveBeneficiary: i == 0 ? address(0) : tierConfigsRemaining[arrayIndex].reserveBeneficiary,
785
- encodedIPFSUri: tierConfigsRemaining[arrayIndex].encodedIPFSUri,
786
- category: tierConfigsRemaining[arrayIndex].category,
787
- discountPercent: tierConfigsRemaining[arrayIndex].discountPercent,
788
- flags: JB721TierFlags({
789
- allowOwnerMint: tierConfigsRemaining[arrayIndex].flags.allowOwnerMint,
790
- transfersPausable: tierConfigsRemaining[arrayIndex].flags.transfersPausable,
791
- cantBeRemoved: tierConfigsRemaining[arrayIndex].flags.cantBeRemoved,
792
- cantIncreaseDiscountPercent: tierConfigsRemaining[arrayIndex].flags.cantIncreaseDiscountPercent,
793
- cantBuyWithCredits: tierConfigsRemaining[arrayIndex].flags.cantBuyWithCredits
794
- }),
795
- splitPercent: 0,
796
- resolvedUri: ""
797
- });
798
- arrayIndex++;
799
- } else {
800
- // Otherwise, part of the tiers removed:
801
- // Check: was `RemoveTier` emitted with the correct values?
802
- vm.expectEmit(true, false, false, true, address(hook));
803
- emit RemoveTier(i + 1, owner);
804
- }
805
- }
806
-
807
- // -- Build expected added tiers -- //
808
- JB721TierConfig[] memory tierConfigsToAdd = new JB721TierConfig[](numberOfTiersToAdd);
809
- JB721Tier[] memory tiersAdded = new JB721Tier[](numberOfTiersToAdd);
810
- for (uint256 i; i < numberOfTiersToAdd; i++) {
811
- tierConfigsToAdd[i] = JB721TierConfig({
812
- price: uint104(tiersToAdd[i]) * 11,
813
- initialSupply: uint32(100),
814
- votingUnits: uint16(0),
815
- reserveFrequency: uint16(0),
816
- reserveBeneficiary: reserveBeneficiary,
817
- encodedIPFSUri: tokenUris[0],
818
- // forge-lint: disable-next-line(unsafe-typecast)
819
- category: uint24(100 + i),
820
- discountPercent: uint8(0),
821
- flags: JB721TierConfigFlags({
822
- allowOwnerMint: false,
823
- useReserveBeneficiaryAsDefault: false,
824
- transfersPausable: false,
825
- useVotingUnits: true,
826
- cantBeRemoved: false,
827
- cantIncreaseDiscountPercent: false,
828
- cantBuyWithCredits: false
829
- }),
830
- splitPercent: 0,
831
- splits: new JBSplit[](0)
832
- });
833
- tiersAdded[i] = JB721Tier({
834
- id: uint32(tiers.length + (i + 1)),
835
- price: tierConfigsToAdd[i].price,
836
- remainingSupply: tierConfigsToAdd[i].initialSupply,
837
- initialSupply: tierConfigsToAdd[i].initialSupply,
838
- votingUnits: tierConfigsToAdd[i].votingUnits,
839
- reserveFrequency: tierConfigsToAdd[i].reserveFrequency,
840
- reserveBeneficiary: address(0),
841
- encodedIPFSUri: tierConfigsToAdd[i].encodedIPFSUri,
842
- category: tierConfigsToAdd[i].category,
843
- discountPercent: tierConfigsToAdd[i].discountPercent,
844
- flags: JB721TierFlags({
845
- allowOwnerMint: tierConfigsToAdd[i].flags.allowOwnerMint,
846
- transfersPausable: tierConfigsToAdd[i].flags.transfersPausable,
847
- cantBeRemoved: tierConfigsToAdd[i].flags.cantBeRemoved,
848
- cantIncreaseDiscountPercent: tierConfigsToAdd[i].flags.cantIncreaseDiscountPercent,
849
- cantBuyWithCredits: tierConfigsToAdd[i].flags.cantBuyWithCredits
850
- }),
851
- splitPercent: 0,
852
- resolvedUri: ""
853
- });
854
- vm.expectEmit(true, true, true, true, address(hook));
855
- emit AddTier(tiersAdded[i].id, tierConfigsToAdd[i], owner);
856
- }
857
- vm.prank(owner);
858
- hook.adjustTiers(tierConfigsToAdd, tierIdsToRemove);
859
- JB721Tier[] memory storedTiers = hook.STORE()
860
- .tiersOf(
861
- address(hook),
862
- new uint256[](0),
863
- false,
864
- 0,
865
- 7 // 7 tiers remaining - hard-coded to avoid stack too deep.
866
- );
867
- // Check: are the expected number of tiers stored?
868
- assertEq(storedTiers.length, 7);
869
- // Check: are all non-deleted and added tiers in the new tiers (unsorted)?
870
- assertTrue(_isIn(tiersRemaining, storedTiers)); // Original tiers
871
- assertTrue(_isIn(tiersAdded, storedTiers)); // New tiers
872
- // Check: are all the deleted tiers removed?
873
- assertFalse(_isIn(tiers, storedTiers)); // Will emit `_isIn: incomplete inclusion but without failing assertion`
874
- // Check: are all the tiers sorted?
875
- for (uint256 j = 1; j < storedTiers.length; j++) {
876
- assertLe(storedTiers[j - 1].category, storedTiers[j].category);
877
- }
878
- }
879
-
880
- function test_adjustTiers_revertIfAddingWithVotingPower(
881
- uint256 initialNumberOfTiers,
882
- uint256 numberTiersToAdd
883
- )
884
- public
885
- {
886
- // Include adding X new tiers with 0 current tiers.
887
- initialNumberOfTiers = bound(initialNumberOfTiers, 0, 15);
888
- numberTiersToAdd = bound(numberTiersToAdd, 1, 15);
889
-
890
- JB721TierConfig[] memory tierConfigs = new JB721TierConfig[](initialNumberOfTiers);
891
- JB721Tier[] memory tiers = new JB721Tier[](initialNumberOfTiers);
892
- for (uint256 i; i < initialNumberOfTiers; i++) {
893
- tierConfigs[i] = JB721TierConfig({
894
- price: uint104((i + 1) * 10),
895
- initialSupply: uint32(100),
896
- votingUnits: uint16(0),
897
- // forge-lint: disable-next-line(unsafe-typecast)
898
- reserveFrequency: uint16(i),
899
- reserveBeneficiary: reserveBeneficiary,
900
- encodedIPFSUri: tokenUris[0],
901
- category: uint24(100),
902
- discountPercent: uint8(0),
903
- flags: JB721TierConfigFlags({
904
- allowOwnerMint: false,
905
- useReserveBeneficiaryAsDefault: false,
906
- transfersPausable: false,
907
- useVotingUnits: true,
908
- cantBeRemoved: false,
909
- cantIncreaseDiscountPercent: false,
910
- cantBuyWithCredits: false
911
- }),
912
- splitPercent: 0,
913
- splits: new JBSplit[](0)
914
- });
915
- tiers[i] = JB721Tier({
916
- // forge-lint: disable-next-line(unsafe-typecast)
917
- id: uint32(i + 1),
918
- price: tierConfigs[i].price,
919
- remainingSupply: tierConfigs[i].initialSupply,
920
- initialSupply: tierConfigs[i].initialSupply,
921
- votingUnits: tierConfigs[i].votingUnits,
922
- reserveFrequency: tierConfigs[i].reserveFrequency,
923
- reserveBeneficiary: tierConfigs[i].reserveBeneficiary,
924
- encodedIPFSUri: tierConfigs[i].encodedIPFSUri,
925
- category: tierConfigs[i].category,
926
- discountPercent: tierConfigs[i].discountPercent,
927
- flags: JB721TierFlags({
928
- allowOwnerMint: tierConfigs[i].flags.allowOwnerMint,
929
- transfersPausable: tierConfigs[i].flags.transfersPausable,
930
- cantBeRemoved: tierConfigs[i].flags.cantBeRemoved,
931
- cantIncreaseDiscountPercent: tierConfigs[i].flags.cantIncreaseDiscountPercent,
932
- cantBuyWithCredits: tierConfigs[i].flags.cantBuyWithCredits
933
- }),
934
- splitPercent: 0,
935
- resolvedUri: ""
936
- });
937
- }
938
- ForTest_JB721TiersHookStore store = new ForTest_JB721TiersHookStore();
939
- ForTest_JB721TiersHook hook = new ForTest_JB721TiersHook(
940
- ForTest_JB721TiersHook.ForTestInitConfig({
941
- projectId: projectId,
942
- name: name,
943
- symbol: symbol,
944
- baseUri: baseUri,
945
- tokenUriResolver: IJB721TokenUriResolver(mockTokenUriResolver),
946
- contractUri: contractUri,
947
- tiers: tierConfigs,
948
- flags: JB721TiersHookFlags({
949
- preventOverspending: false,
950
- issueTokensForSplits: false,
951
- noNewTiersWithReserves: false,
952
- noNewTiersWithVotes: true,
953
- noNewTiersWithOwnerMinting: true
954
- })
955
- }),
956
- IJBDirectory(mockJBDirectory),
957
- IJBPrices(mockJBPrices),
958
- IJBRulesets(mockJBRulesets),
959
- IJB721TiersHookStore(address(store)),
960
- IJBSplits(mockJBSplits)
961
- );
962
- hook.transferOwnership(owner);
963
- JB721TierConfig[] memory tierConfigsToAdd = new JB721TierConfig[](numberTiersToAdd);
964
- JB721Tier[] memory tiersAdded = new JB721Tier[](numberTiersToAdd);
965
- for (uint256 i; i < numberTiersToAdd; i++) {
966
- tierConfigsToAdd[i] = JB721TierConfig({
967
- price: uint104(0),
968
- initialSupply: uint32(100),
969
- // forge-lint: disable-next-line(unsafe-typecast)
970
- votingUnits: uint16(i + 1),
971
- // forge-lint: disable-next-line(unsafe-typecast)
972
- reserveFrequency: uint16(i),
973
- reserveBeneficiary: reserveBeneficiary,
974
- encodedIPFSUri: tokenUris[0],
975
- category: uint24(100),
976
- discountPercent: uint8(0),
977
- flags: JB721TierConfigFlags({
978
- allowOwnerMint: false,
979
- useReserveBeneficiaryAsDefault: false,
980
- transfersPausable: false,
981
- useVotingUnits: true,
982
- cantBeRemoved: false,
983
- cantIncreaseDiscountPercent: false,
984
- cantBuyWithCredits: false
985
- }),
986
- splitPercent: 0,
987
- splits: new JBSplit[](0)
988
- });
989
- tiersAdded[i] = JB721Tier({
990
- id: uint32(tiers.length + (i + 1)),
991
- price: tierConfigsToAdd[i].price,
992
- remainingSupply: tierConfigsToAdd[i].initialSupply,
993
- initialSupply: tierConfigsToAdd[i].initialSupply,
994
- votingUnits: tierConfigsToAdd[i].votingUnits,
995
- reserveFrequency: tierConfigsToAdd[i].reserveFrequency,
996
- reserveBeneficiary: tierConfigsToAdd[i].reserveBeneficiary,
997
- encodedIPFSUri: tierConfigsToAdd[i].encodedIPFSUri,
998
- category: tierConfigsToAdd[i].category,
999
- discountPercent: tierConfigsToAdd[i].discountPercent,
1000
- flags: JB721TierFlags({
1001
- allowOwnerMint: tierConfigsToAdd[i].flags.allowOwnerMint,
1002
- transfersPausable: tierConfigsToAdd[i].flags.transfersPausable,
1003
- cantBeRemoved: tierConfigsToAdd[i].flags.cantBeRemoved,
1004
- cantIncreaseDiscountPercent: tierConfigsToAdd[i].flags.cantIncreaseDiscountPercent,
1005
- cantBuyWithCredits: tierConfigsToAdd[i].flags.cantBuyWithCredits
1006
- }),
1007
- splitPercent: 0,
1008
- resolvedUri: ""
1009
- });
1010
- }
1011
- vm.expectRevert(
1012
- abi.encodeWithSelector(
1013
- JB721TiersHookStore.JB721TiersHookStore_VotingUnitsNotAllowed.selector, initialNumberOfTiers + 1
1014
- )
1015
- );
1016
- vm.prank(owner);
1017
- hook.adjustTiers(tierConfigsToAdd, new uint256[](0));
1018
- }
1019
-
1020
- function test_adjustTiers_revertIfAddingWithReserveFrequency(
1021
- uint256 initialNumberOfTiers,
1022
- uint256 numberTiersToAdd
1023
- )
1024
- public
1025
- {
1026
- // Include adding X new tiers with 0 current tiers.
1027
- initialNumberOfTiers = bound(initialNumberOfTiers, 0, 15);
1028
- numberTiersToAdd = bound(numberTiersToAdd, 1, 15);
1029
-
1030
- JB721TierConfig[] memory tierConfigs = new JB721TierConfig[](initialNumberOfTiers);
1031
- JB721Tier[] memory tiers = new JB721Tier[](initialNumberOfTiers);
1032
- for (uint256 i; i < initialNumberOfTiers; i++) {
1033
- tierConfigs[i] = JB721TierConfig({
1034
- price: uint104((i + 1) * 10),
1035
- initialSupply: uint32(100),
1036
- votingUnits: uint16(0),
1037
- // forge-lint: disable-next-line(unsafe-typecast)
1038
- reserveFrequency: uint16(i),
1039
- reserveBeneficiary: reserveBeneficiary,
1040
- encodedIPFSUri: tokenUris[0],
1041
- category: uint24(100),
1042
- discountPercent: uint8(0),
1043
- flags: JB721TierConfigFlags({
1044
- allowOwnerMint: false,
1045
- useReserveBeneficiaryAsDefault: false,
1046
- transfersPausable: false,
1047
- useVotingUnits: true,
1048
- cantBeRemoved: false,
1049
- cantIncreaseDiscountPercent: false,
1050
- cantBuyWithCredits: false
1051
- }),
1052
- splitPercent: 0,
1053
- splits: new JBSplit[](0)
1054
- });
1055
- tiers[i] = JB721Tier({
1056
- // forge-lint: disable-next-line(unsafe-typecast)
1057
- id: uint32(i + 1),
1058
- price: tierConfigs[i].price,
1059
- remainingSupply: tierConfigs[i].initialSupply,
1060
- initialSupply: tierConfigs[i].initialSupply,
1061
- votingUnits: tierConfigs[i].votingUnits,
1062
- reserveFrequency: tierConfigs[i].reserveFrequency,
1063
- reserveBeneficiary: tierConfigs[i].reserveBeneficiary,
1064
- encodedIPFSUri: tierConfigs[i].encodedIPFSUri,
1065
- category: tierConfigs[i].category,
1066
- discountPercent: tierConfigs[i].discountPercent,
1067
- flags: JB721TierFlags({
1068
- allowOwnerMint: tierConfigs[i].flags.allowOwnerMint,
1069
- transfersPausable: tierConfigs[i].flags.transfersPausable,
1070
- cantBeRemoved: tierConfigs[i].flags.cantBeRemoved,
1071
- cantIncreaseDiscountPercent: tierConfigs[i].flags.cantIncreaseDiscountPercent,
1072
- cantBuyWithCredits: tierConfigs[i].flags.cantBuyWithCredits
1073
- }),
1074
- splitPercent: 0,
1075
- resolvedUri: ""
1076
- });
1077
- }
1078
- ForTest_JB721TiersHookStore store = new ForTest_JB721TiersHookStore();
1079
- ForTest_JB721TiersHook hook = new ForTest_JB721TiersHook(
1080
- ForTest_JB721TiersHook.ForTestInitConfig({
1081
- projectId: projectId,
1082
- name: name,
1083
- symbol: symbol,
1084
- baseUri: baseUri,
1085
- tokenUriResolver: IJB721TokenUriResolver(mockTokenUriResolver),
1086
- contractUri: contractUri,
1087
- tiers: tierConfigs,
1088
- flags: JB721TiersHookFlags({
1089
- preventOverspending: false,
1090
- issueTokensForSplits: false,
1091
- noNewTiersWithReserves: true, // <-- This is the flag we're testing.
1092
- noNewTiersWithVotes: false,
1093
- noNewTiersWithOwnerMinting: true
1094
- })
1095
- }),
1096
- IJBDirectory(mockJBDirectory),
1097
- IJBPrices(mockJBPrices),
1098
- IJBRulesets(mockJBRulesets),
1099
- IJB721TiersHookStore(address(store)),
1100
- IJBSplits(mockJBSplits)
1101
- );
1102
- hook.transferOwnership(owner);
1103
- JB721TierConfig[] memory tierConfigsToAdd = new JB721TierConfig[](numberTiersToAdd);
1104
- JB721Tier[] memory tiersAdded = new JB721Tier[](numberTiersToAdd);
1105
- for (uint256 i; i < numberTiersToAdd; i++) {
1106
- tierConfigsToAdd[i] = JB721TierConfig({
1107
- price: uint104((i + 1) * 100),
1108
- initialSupply: uint32(100),
1109
- votingUnits: uint16(0),
1110
- // forge-lint: disable-next-line(unsafe-typecast)
1111
- reserveFrequency: uint16(i + 1),
1112
- reserveBeneficiary: reserveBeneficiary,
1113
- encodedIPFSUri: tokenUris[0],
1114
- category: uint24(100),
1115
- discountPercent: uint8(0),
1116
- flags: JB721TierConfigFlags({
1117
- allowOwnerMint: false,
1118
- useReserveBeneficiaryAsDefault: false,
1119
- transfersPausable: false,
1120
- useVotingUnits: true,
1121
- cantBeRemoved: false,
1122
- cantIncreaseDiscountPercent: false,
1123
- cantBuyWithCredits: false
1124
- }),
1125
- splitPercent: 0,
1126
- splits: new JBSplit[](0)
1127
- });
1128
- tiersAdded[i] = JB721Tier({
1129
- id: uint32(tiers.length + (i + 1)),
1130
- price: tierConfigsToAdd[i].price,
1131
- remainingSupply: tierConfigsToAdd[i].initialSupply,
1132
- initialSupply: tierConfigsToAdd[i].initialSupply,
1133
- votingUnits: tierConfigsToAdd[i].votingUnits,
1134
- reserveFrequency: tierConfigsToAdd[i].reserveFrequency,
1135
- reserveBeneficiary: tierConfigsToAdd[i].reserveBeneficiary,
1136
- encodedIPFSUri: tierConfigsToAdd[i].encodedIPFSUri,
1137
- category: tierConfigsToAdd[i].category,
1138
- discountPercent: tierConfigsToAdd[i].discountPercent,
1139
- flags: JB721TierFlags({
1140
- allowOwnerMint: tierConfigsToAdd[i].flags.allowOwnerMint,
1141
- transfersPausable: tierConfigsToAdd[i].flags.transfersPausable,
1142
- cantBeRemoved: tierConfigsToAdd[i].flags.cantBeRemoved,
1143
- cantIncreaseDiscountPercent: tierConfigsToAdd[i].flags.cantIncreaseDiscountPercent,
1144
- cantBuyWithCredits: tierConfigsToAdd[i].flags.cantBuyWithCredits
1145
- }),
1146
- splitPercent: 0,
1147
- resolvedUri: ""
1148
- });
1149
- }
1150
-
1151
- // Expect the `adjustTiers` call to revert because of the `noNewTiersWithReserves` flag.
1152
- vm.expectRevert(
1153
- abi.encodeWithSelector(
1154
- JB721TiersHookStore.JB721TiersHookStore_ReserveFrequencyNotAllowed.selector, initialNumberOfTiers + 1
1155
- )
1156
- );
1157
- vm.prank(owner);
1158
- hook.adjustTiers(tierConfigsToAdd, new uint256[](0));
1159
- }
1160
-
1161
- function test_adjustTiers_revertIfCannotRemoveTier() public {
1162
- uint256 initialNumberOfTiers = 2;
1163
- uint256 numberOfTiersToRemove = 1;
1164
- uint256[] memory tierIdsToRemove = new uint256[](numberOfTiersToRemove);
1165
- tierIdsToRemove[0] = 1;
1166
- // Initial tiers configs and data.
1167
- JB721TierConfig[] memory tierConfigs = new JB721TierConfig[](initialNumberOfTiers);
1168
- tierConfigs[0] = JB721TierConfig({
1169
- price: 10,
1170
- initialSupply: uint32(100),
1171
- votingUnits: uint16(0),
1172
- reserveFrequency: uint16(0),
1173
- reserveBeneficiary: reserveBeneficiary,
1174
- encodedIPFSUri: tokenUris[0],
1175
- category: uint24(100),
1176
- discountPercent: uint8(0),
1177
- flags: JB721TierConfigFlags({
1178
- allowOwnerMint: false,
1179
- useReserveBeneficiaryAsDefault: false,
1180
- transfersPausable: false,
1181
- useVotingUnits: true,
1182
- cantBeRemoved: true,
1183
- cantIncreaseDiscountPercent: false,
1184
- cantBuyWithCredits: false
1185
- }),
1186
- splitPercent: 0,
1187
- splits: new JBSplit[](0)
1188
- });
1189
- tierConfigs[1] = JB721TierConfig({
1190
- price: 10,
1191
- initialSupply: uint32(100),
1192
- votingUnits: uint16(0),
1193
- reserveFrequency: uint16(0),
1194
- reserveBeneficiary: reserveBeneficiary,
1195
- encodedIPFSUri: tokenUris[0],
1196
- category: uint24(100),
1197
- discountPercent: uint8(0),
1198
- flags: JB721TierConfigFlags({
1199
- allowOwnerMint: false,
1200
- useReserveBeneficiaryAsDefault: false,
1201
- transfersPausable: false,
1202
- useVotingUnits: true,
1203
- cantBeRemoved: false,
1204
- cantIncreaseDiscountPercent: false,
1205
- cantBuyWithCredits: false
1206
- }),
1207
- splitPercent: 0,
1208
- splits: new JBSplit[](0)
1209
- });
1210
- // Deploy the hook and its store with the initial tiers.
1211
- vm.etch(hook_i, address(hook).code);
1212
- JB721TiersHook hook = JB721TiersHook(hook_i);
1213
- hook.initialize(
1214
- projectId,
1215
- name,
1216
- symbol,
1217
- baseUri,
1218
- IJB721TokenUriResolver(mockTokenUriResolver),
1219
- contractUri,
1220
- JB721InitTiersConfig({
1221
- tiers: tierConfigs, currency: uint32(uint160(JBConstants.NATIVE_TOKEN)), decimals: 18
1222
- }),
1223
- JB721TiersHookFlags({
1224
- preventOverspending: false,
1225
- issueTokensForSplits: false,
1226
- noNewTiersWithReserves: false,
1227
- noNewTiersWithVotes: false,
1228
- noNewTiersWithOwnerMinting: true
1229
- })
1230
- );
1231
- hook.transferOwnership(owner);
1232
-
1233
- // Expect the `adjustTiers` call to revert because cannot remove tier.
1234
- vm.expectRevert(abi.encodeWithSelector(JB721TiersHookStore.JB721TiersHookStore_CantRemoveTier.selector, 1));
1235
- vm.prank(owner);
1236
- hook.adjustTiers(new JB721TierConfig[](0), tierIdsToRemove);
1237
- }
1238
-
1239
- function test_adjustTiers_revertIfEmptyQuantity(uint256 initialNumberOfTiers, uint256 numberTiersToAdd) public {
1240
- // Include adding X new tiers with 0 current tiers.
1241
- initialNumberOfTiers = bound(initialNumberOfTiers, 0, 15);
1242
- numberTiersToAdd = bound(numberTiersToAdd, 1, 15);
1243
-
1244
- JB721TierConfig[] memory tierConfigs = new JB721TierConfig[](initialNumberOfTiers);
1245
- JB721Tier[] memory tiers = new JB721Tier[](initialNumberOfTiers);
1246
- for (uint256 i; i < initialNumberOfTiers; i++) {
1247
- tierConfigs[i] = JB721TierConfig({
1248
- price: uint104((i + 1) * 10),
1249
- initialSupply: uint32(100),
1250
- votingUnits: uint16(0),
1251
- // forge-lint: disable-next-line(unsafe-typecast)
1252
- reserveFrequency: uint16(i),
1253
- reserveBeneficiary: reserveBeneficiary,
1254
- encodedIPFSUri: tokenUris[0],
1255
- category: uint24(100),
1256
- discountPercent: uint8(0),
1257
- flags: JB721TierConfigFlags({
1258
- allowOwnerMint: false,
1259
- useReserveBeneficiaryAsDefault: false,
1260
- transfersPausable: false,
1261
- useVotingUnits: true,
1262
- cantBeRemoved: false,
1263
- cantIncreaseDiscountPercent: false,
1264
- cantBuyWithCredits: false
1265
- }),
1266
- splitPercent: 0,
1267
- splits: new JBSplit[](0)
1268
- });
1269
- tiers[i] = JB721Tier({
1270
- // forge-lint: disable-next-line(unsafe-typecast)
1271
- id: uint32(i + 1),
1272
- price: tierConfigs[i].price,
1273
- remainingSupply: tierConfigs[i].initialSupply,
1274
- initialSupply: tierConfigs[i].initialSupply,
1275
- votingUnits: tierConfigs[i].votingUnits,
1276
- reserveFrequency: tierConfigs[i].reserveFrequency,
1277
- reserveBeneficiary: tierConfigs[i].reserveBeneficiary,
1278
- encodedIPFSUri: tierConfigs[i].encodedIPFSUri,
1279
- category: tierConfigs[i].category,
1280
- discountPercent: tierConfigs[i].discountPercent,
1281
- flags: JB721TierFlags({
1282
- allowOwnerMint: tierConfigs[i].flags.allowOwnerMint,
1283
- transfersPausable: tierConfigs[i].flags.transfersPausable,
1284
- cantBeRemoved: tierConfigs[i].flags.cantBeRemoved,
1285
- cantIncreaseDiscountPercent: tierConfigs[i].flags.cantIncreaseDiscountPercent,
1286
- cantBuyWithCredits: tierConfigs[i].flags.cantBuyWithCredits
1287
- }),
1288
- splitPercent: 0,
1289
- resolvedUri: ""
1290
- });
1291
- }
1292
- ForTest_JB721TiersHookStore store = new ForTest_JB721TiersHookStore();
1293
- ForTest_JB721TiersHook hook = new ForTest_JB721TiersHook(
1294
- ForTest_JB721TiersHook.ForTestInitConfig({
1295
- projectId: projectId,
1296
- name: name,
1297
- symbol: symbol,
1298
- baseUri: baseUri,
1299
- tokenUriResolver: IJB721TokenUriResolver(mockTokenUriResolver),
1300
- contractUri: contractUri,
1301
- tiers: tierConfigs,
1302
- flags: JB721TiersHookFlags({
1303
- preventOverspending: false,
1304
- issueTokensForSplits: false,
1305
- noNewTiersWithReserves: false,
1306
- noNewTiersWithVotes: false,
1307
- noNewTiersWithOwnerMinting: true
1308
- })
1309
- }),
1310
- IJBDirectory(mockJBDirectory),
1311
- IJBPrices(mockJBPrices),
1312
- IJBRulesets(mockJBRulesets),
1313
- IJB721TiersHookStore(address(store)),
1314
- IJBSplits(mockJBSplits)
1315
- );
1316
- hook.transferOwnership(owner);
1317
- JB721TierConfig[] memory tierConfigsToAdd = new JB721TierConfig[](numberTiersToAdd);
1318
- JB721Tier[] memory tiersAdded = new JB721Tier[](numberTiersToAdd);
1319
- for (uint256 i; i < numberTiersToAdd; i++) {
1320
- tierConfigsToAdd[i] = JB721TierConfig({
1321
- price: uint104((i + 1) * 100),
1322
- initialSupply: uint32(0), // <-- This is the value we're testing.
1323
- votingUnits: uint16(0),
1324
- reserveFrequency: uint16(0),
1325
- reserveBeneficiary: reserveBeneficiary,
1326
- encodedIPFSUri: tokenUris[0],
1327
- category: uint24(100),
1328
- discountPercent: uint8(0),
1329
- flags: JB721TierConfigFlags({
1330
- allowOwnerMint: false,
1331
- useReserveBeneficiaryAsDefault: false,
1332
- transfersPausable: false,
1333
- useVotingUnits: false,
1334
- cantBeRemoved: false,
1335
- cantIncreaseDiscountPercent: false,
1336
- cantBuyWithCredits: false
1337
- }),
1338
- splitPercent: 0,
1339
- splits: new JBSplit[](0)
1340
- });
1341
- tiersAdded[i] = JB721Tier({
1342
- id: uint32(tiers.length + (i + 1)),
1343
- price: tierConfigsToAdd[i].price,
1344
- remainingSupply: tierConfigsToAdd[i].initialSupply,
1345
- initialSupply: tierConfigsToAdd[i].initialSupply,
1346
- votingUnits: tierConfigsToAdd[i].votingUnits,
1347
- reserveFrequency: tierConfigsToAdd[i].reserveFrequency,
1348
- reserveBeneficiary: tierConfigsToAdd[i].reserveBeneficiary,
1349
- encodedIPFSUri: tierConfigsToAdd[i].encodedIPFSUri,
1350
- category: tierConfigsToAdd[i].category,
1351
- discountPercent: tierConfigsToAdd[i].discountPercent,
1352
- flags: JB721TierFlags({
1353
- allowOwnerMint: tierConfigsToAdd[i].flags.allowOwnerMint,
1354
- transfersPausable: tierConfigsToAdd[i].flags.transfersPausable,
1355
- cantBeRemoved: tierConfigsToAdd[i].flags.cantBeRemoved,
1356
- cantIncreaseDiscountPercent: tierConfigsToAdd[i].flags.cantIncreaseDiscountPercent,
1357
- cantBuyWithCredits: tierConfigsToAdd[i].flags.cantBuyWithCredits
1358
- }),
1359
- splitPercent: 0,
1360
- resolvedUri: ""
1361
- });
1362
- }
1363
-
1364
- // Expect the `adjustTiers` call to revert because of the `initialSupply` of 0.
1365
- vm.expectRevert(
1366
- abi.encodeWithSelector(
1367
- JB721TiersHookStore.JB721TiersHookStore_ZeroInitialSupply.selector, initialNumberOfTiers + 1
1368
- )
1369
- );
1370
- vm.prank(owner);
1371
- hook.adjustTiers(tierConfigsToAdd, new uint256[](0));
1372
- }
1373
-
1374
- function test_adjustTiers_revertIfInvalidCategorySortOrder(
1375
- uint256 initialNumberOfTiers,
1376
- uint256 numberTiersToAdd
1377
- )
1378
- public
1379
- {
1380
- initialNumberOfTiers = bound(initialNumberOfTiers, 0, 15);
1381
- numberTiersToAdd = bound(numberTiersToAdd, 2, 15);
1382
-
1383
- ForTest_JB721TiersHook hook = _initializeForTestHook(initialNumberOfTiers);
1384
-
1385
- JB721TierConfig[] memory tierConfigsToAdd = new JB721TierConfig[](numberTiersToAdd);
1386
- for (uint256 i; i < numberTiersToAdd; i++) {
1387
- tierConfigsToAdd[i] = JB721TierConfig({
1388
- price: uint104((i + 1) * 100),
1389
- initialSupply: uint32(100),
1390
- votingUnits: uint16(0),
1391
- // forge-lint: disable-next-line(unsafe-typecast)
1392
- reserveFrequency: uint16(i),
1393
- reserveBeneficiary: reserveBeneficiary,
1394
- encodedIPFSUri: tokenUris[0],
1395
- category: uint24(100),
1396
- discountPercent: uint8(0),
1397
- flags: JB721TierConfigFlags({
1398
- allowOwnerMint: false,
1399
- useReserveBeneficiaryAsDefault: false,
1400
- transfersPausable: false,
1401
- useVotingUnits: true,
1402
- cantBeRemoved: false,
1403
- cantIncreaseDiscountPercent: false,
1404
- cantBuyWithCredits: false
1405
- }),
1406
- splitPercent: 0,
1407
- splits: new JBSplit[](0)
1408
- });
1409
- }
1410
- // Set the second to last tier to have a category of `99`, which is less than the last tier's category of `100`.
1411
- tierConfigsToAdd[numberTiersToAdd - 1].category = uint8(99);
1412
-
1413
- // Expect the `adjustTiers` call to revert because of the invalid category sort order.
1414
- vm.expectRevert(
1415
- abi.encodeWithSelector(JB721TiersHookStore.JB721TiersHookStore_InvalidCategorySortOrder.selector, 99, 100)
1416
- );
1417
- vm.prank(owner);
1418
- hook.adjustTiers(tierConfigsToAdd, new uint256[](0));
1419
- }
1420
-
1421
- function test_adjustTiers_revertIfMoreVotingUnitsNotAllowedWithPriceChange(
1422
- uint256 initialNumberOfTiers,
1423
- uint256 numberTiersToAdd
1424
- )
1425
- public
1426
- {
1427
- // Include adding X new tiers with 0 current tiers.
1428
- initialNumberOfTiers = bound(initialNumberOfTiers, 0, 15);
1429
- numberTiersToAdd = bound(numberTiersToAdd, 1, 15);
1430
-
1431
- JB721TierConfig[] memory tierConfigs = new JB721TierConfig[](initialNumberOfTiers);
1432
- for (uint256 i; i < initialNumberOfTiers; i++) {
1433
- tierConfigs[i] = JB721TierConfig({
1434
- price: uint104((i + 1) * 10),
1435
- initialSupply: uint32(100),
1436
- votingUnits: uint16(0),
1437
- // forge-lint: disable-next-line(unsafe-typecast)
1438
- reserveFrequency: uint16(i),
1439
- reserveBeneficiary: reserveBeneficiary,
1440
- encodedIPFSUri: tokenUris[0],
1441
- category: uint24(100),
1442
- discountPercent: uint8(0),
1443
- flags: JB721TierConfigFlags({
1444
- allowOwnerMint: false,
1445
- useReserveBeneficiaryAsDefault: false,
1446
- transfersPausable: false,
1447
- useVotingUnits: false,
1448
- cantBeRemoved: false,
1449
- cantIncreaseDiscountPercent: false,
1450
- cantBuyWithCredits: false
1451
- }),
1452
- splitPercent: 0,
1453
- splits: new JBSplit[](0)
1454
- });
1455
- }
1456
-
1457
- ForTest_JB721TiersHookStore store = new ForTest_JB721TiersHookStore();
1458
- ForTest_JB721TiersHook hook = new ForTest_JB721TiersHook(
1459
- ForTest_JB721TiersHook.ForTestInitConfig({
1460
- projectId: projectId,
1461
- name: name,
1462
- symbol: symbol,
1463
- baseUri: baseUri,
1464
- tokenUriResolver: IJB721TokenUriResolver(mockTokenUriResolver),
1465
- contractUri: contractUri,
1466
- tiers: tierConfigs,
1467
- flags: JB721TiersHookFlags({
1468
- preventOverspending: false,
1469
- issueTokensForSplits: false,
1470
- noNewTiersWithReserves: false,
1471
- noNewTiersWithVotes: true, // <-- This is the flag we're testing.
1472
- noNewTiersWithOwnerMinting: true
1473
- })
1474
- }),
1475
- IJBDirectory(mockJBDirectory),
1476
- IJBPrices(mockJBPrices),
1477
- IJBRulesets(mockJBRulesets),
1478
- IJB721TiersHookStore(address(store)),
1479
- IJBSplits(mockJBSplits)
1480
- );
1481
- hook.transferOwnership(owner);
1482
-
1483
- JB721TierConfig[] memory tierConfigsToAdd = new JB721TierConfig[](numberTiersToAdd);
1484
- for (uint256 i; i < numberTiersToAdd; i++) {
1485
- tierConfigsToAdd[i] = JB721TierConfig({
1486
- price: uint104((i + 1) * 100),
1487
- initialSupply: uint32(100),
1488
- votingUnits: uint16(0),
1489
- // forge-lint: disable-next-line(unsafe-typecast)
1490
- reserveFrequency: uint16(i),
1491
- reserveBeneficiary: reserveBeneficiary,
1492
- encodedIPFSUri: tokenUris[0],
1493
- category: uint24(100),
1494
- discountPercent: uint8(0),
1495
- flags: JB721TierConfigFlags({
1496
- allowOwnerMint: false,
1497
- useReserveBeneficiaryAsDefault: false,
1498
- transfersPausable: false,
1499
- useVotingUnits: false, // <-- If false, voting power is based on tier price
1500
- cantBeRemoved: false,
1501
- cantIncreaseDiscountPercent: false,
1502
- cantBuyWithCredits: false
1503
- }),
1504
- splitPercent: 0,
1505
- splits: new JBSplit[](0)
1506
- });
1507
- }
1508
-
1509
- // Expect the `adjustTiers` call to revert because of the `noNewTiersWithVotes` flag.
1510
- vm.expectRevert(
1511
- abi.encodeWithSelector(
1512
- JB721TiersHookStore.JB721TiersHookStore_VotingUnitsNotAllowed.selector, initialNumberOfTiers + 1
1513
- )
1514
- );
1515
- vm.prank(owner);
1516
- hook.adjustTiers(tierConfigsToAdd, new uint256[](0));
1517
- }
1518
-
1519
- function test_adjustTiers_revertIfMoreVotingUnitsNotAllowedUsingVotingUnits(
1520
- uint256 initialNumberOfTiers,
1521
- uint256 numberTiersToAdd
1522
- )
1523
- public
1524
- {
1525
- // Include adding X new tiers with 0 current tiers.
1526
- initialNumberOfTiers = bound(initialNumberOfTiers, 0, 15);
1527
- numberTiersToAdd = bound(numberTiersToAdd, 1, 15);
1528
-
1529
- JB721TierConfig[] memory tierConfigs = new JB721TierConfig[](initialNumberOfTiers);
1530
- for (uint256 i; i < initialNumberOfTiers; i++) {
1531
- tierConfigs[i] = JB721TierConfig({
1532
- price: uint104((i + 1) * 10),
1533
- initialSupply: uint32(100),
1534
- votingUnits: uint16(0),
1535
- // forge-lint: disable-next-line(unsafe-typecast)
1536
- reserveFrequency: uint16(i),
1537
- reserveBeneficiary: reserveBeneficiary,
1538
- encodedIPFSUri: tokenUris[0],
1539
- category: uint24(100),
1540
- discountPercent: uint8(0),
1541
- flags: JB721TierConfigFlags({
1542
- allowOwnerMint: false,
1543
- useReserveBeneficiaryAsDefault: false,
1544
- transfersPausable: false,
1545
- useVotingUnits: true,
1546
- cantBeRemoved: false,
1547
- cantIncreaseDiscountPercent: false,
1548
- cantBuyWithCredits: false
1549
- }),
1550
- splitPercent: 0,
1551
- splits: new JBSplit[](0)
1552
- });
1553
- }
1554
-
1555
- ForTest_JB721TiersHookStore store = new ForTest_JB721TiersHookStore();
1556
- ForTest_JB721TiersHook hook = new ForTest_JB721TiersHook(
1557
- ForTest_JB721TiersHook.ForTestInitConfig({
1558
- projectId: projectId,
1559
- name: name,
1560
- symbol: symbol,
1561
- baseUri: baseUri,
1562
- tokenUriResolver: IJB721TokenUriResolver(mockTokenUriResolver),
1563
- contractUri: contractUri,
1564
- tiers: tierConfigs,
1565
- flags: JB721TiersHookFlags({
1566
- preventOverspending: false,
1567
- issueTokensForSplits: false,
1568
- noNewTiersWithReserves: false,
1569
- noNewTiersWithVotes: true, // <-- This is the flag we're testing.
1570
- noNewTiersWithOwnerMinting: true
1571
- })
1572
- }),
1573
- IJBDirectory(mockJBDirectory),
1574
- IJBPrices(mockJBPrices),
1575
- IJBRulesets(mockJBRulesets),
1576
- IJB721TiersHookStore(address(store)),
1577
- IJBSplits(mockJBSplits)
1578
- );
1579
- hook.transferOwnership(owner);
1580
-
1581
- JB721TierConfig[] memory tierConfigsToAdd = new JB721TierConfig[](numberTiersToAdd);
1582
- for (uint256 i; i < numberTiersToAdd; i++) {
1583
- tierConfigsToAdd[i] = JB721TierConfig({
1584
- price: uint104((i + 1) * 100),
1585
- initialSupply: uint32(100),
1586
- votingUnits: uint16(0),
1587
- // forge-lint: disable-next-line(unsafe-typecast)
1588
- reserveFrequency: uint16(i),
1589
- reserveBeneficiary: reserveBeneficiary,
1590
- encodedIPFSUri: tokenUris[0],
1591
- category: uint24(100),
1592
- discountPercent: uint8(0),
1593
- flags: JB721TierConfigFlags({
1594
- allowOwnerMint: false,
1595
- useReserveBeneficiaryAsDefault: false,
1596
- transfersPausable: false,
1597
- useVotingUnits: true, // <-- If false, voting power is based on tier price
1598
- cantBeRemoved: false,
1599
- cantIncreaseDiscountPercent: false,
1600
- cantBuyWithCredits: false
1601
- }),
1602
- splitPercent: 0,
1603
- splits: new JBSplit[](0)
1604
- });
1605
- }
1606
-
1607
- // One new tier has a non-0 voting power
1608
- tierConfigsToAdd[numberTiersToAdd - 1].votingUnits = uint16(1);
1609
-
1610
- // Expect the `adjustTiers` call to revert because of the `noNewTiersWithVotes` flag.
1611
- vm.expectRevert(
1612
- abi.encodeWithSelector(
1613
- JB721TiersHookStore.JB721TiersHookStore_VotingUnitsNotAllowed.selector,
1614
- initialNumberOfTiers + numberTiersToAdd
1615
- )
1616
- );
1617
- vm.prank(owner);
1618
- hook.adjustTiers(tierConfigsToAdd, new uint256[](0));
1619
- }
1620
-
1621
- function test_cleanTiers_removeInactiveTiers(
1622
- uint256 initialNumberOfTiers,
1623
- uint256 seed,
1624
- uint256 numberOfTiersToRemove
1625
- )
1626
- public
1627
- {
1628
- // Include adding X new tiers with 0 current tiers.
1629
- initialNumberOfTiers = bound(initialNumberOfTiers, 1, 15);
1630
- numberOfTiersToRemove = bound(numberOfTiersToRemove, 0, initialNumberOfTiers - 1);
1631
-
1632
- // Create random tiers to remove.
1633
- uint256[] memory tiersToRemove = new uint256[](numberOfTiersToRemove);
1634
- // Use `seed` to generate new random tiers, and iterate on `i` to fill the `tiersToRemove` array.
1635
- for (uint256 i; i < numberOfTiersToRemove;) {
1636
- uint256 newTierCandidate = uint256(keccak256(abi.encode(seed))) % initialNumberOfTiers;
1637
- bool invalidTier;
1638
- if (newTierCandidate != 0) {
1639
- for (uint256 j; j < numberOfTiersToRemove; j++) {
1640
- // Same value twice?
1641
- if (newTierCandidate == tiersToRemove[j]) {
1642
- invalidTier = true;
1643
- break;
1644
- }
1645
- }
1646
- if (!invalidTier) {
1647
- tiersToRemove[i] = newTierCandidate;
1648
- i++;
1649
- }
1650
- }
1651
- // Overflow to loop over (the seed is fuzzed, and may start at max(uint256)).
1652
- unchecked {
1653
- seed++;
1654
- }
1655
- }
1656
- // Order the tiers to remove for event matching (which are ordered too).
1657
- tiersToRemove = _sortArray(tiersToRemove);
1658
- JB721TierConfig[] memory tierConfigs = new JB721TierConfig[](initialNumberOfTiers);
1659
- JB721Tier[] memory tiers = new JB721Tier[](initialNumberOfTiers);
1660
- for (uint256 i; i < initialNumberOfTiers; i++) {
1661
- tierConfigs[i] = JB721TierConfig({
1662
- price: uint104((i + 1) * 10),
1663
- initialSupply: uint32(100),
1664
- votingUnits: uint16(0),
1665
- // forge-lint: disable-next-line(unsafe-typecast)
1666
- reserveFrequency: uint16(i),
1667
- reserveBeneficiary: reserveBeneficiary,
1668
- encodedIPFSUri: tokenUris[0],
1669
- category: uint24(100),
1670
- discountPercent: uint8(0),
1671
- flags: JB721TierConfigFlags({
1672
- allowOwnerMint: false,
1673
- useReserveBeneficiaryAsDefault: false,
1674
- transfersPausable: false,
1675
- useVotingUnits: false,
1676
- cantBeRemoved: false,
1677
- cantIncreaseDiscountPercent: false,
1678
- cantBuyWithCredits: false
1679
- }),
1680
- splitPercent: 0,
1681
- splits: new JBSplit[](0)
1682
- });
1683
- tiers[i] = JB721Tier({
1684
- // forge-lint: disable-next-line(unsafe-typecast)
1685
- id: uint32(i + 1),
1686
- price: tierConfigs[i].price,
1687
- remainingSupply: tierConfigs[i].initialSupply,
1688
- initialSupply: tierConfigs[i].initialSupply,
1689
- votingUnits: tierConfigs[i].price,
1690
- reserveFrequency: tierConfigs[i].reserveFrequency,
1691
- reserveBeneficiary: i == 0 ? address(0) : tierConfigs[i].reserveBeneficiary,
1692
- encodedIPFSUri: tierConfigs[i].encodedIPFSUri,
1693
- category: tierConfigs[i].category,
1694
- discountPercent: tierConfigs[i].discountPercent,
1695
- flags: JB721TierFlags({
1696
- allowOwnerMint: tierConfigs[i].flags.allowOwnerMint,
1697
- transfersPausable: tierConfigs[i].flags.transfersPausable,
1698
- cantBeRemoved: tierConfigs[i].flags.cantBeRemoved,
1699
- cantIncreaseDiscountPercent: tierConfigs[i].flags.cantIncreaseDiscountPercent,
1700
- cantBuyWithCredits: tierConfigs[i].flags.cantBuyWithCredits
1701
- }),
1702
- splitPercent: 0,
1703
- resolvedUri: ""
1704
- });
1705
- }
1706
- ForTest_JB721TiersHookStore store = new ForTest_JB721TiersHookStore();
1707
- ForTest_JB721TiersHook hook = new ForTest_JB721TiersHook(
1708
- ForTest_JB721TiersHook.ForTestInitConfig({
1709
- projectId: projectId,
1710
- name: name,
1711
- symbol: symbol,
1712
- baseUri: baseUri,
1713
- tokenUriResolver: IJB721TokenUriResolver(mockTokenUriResolver),
1714
- contractUri: contractUri,
1715
- tiers: tierConfigs,
1716
- flags: JB721TiersHookFlags({
1717
- preventOverspending: false,
1718
- issueTokensForSplits: false,
1719
- noNewTiersWithReserves: false,
1720
- noNewTiersWithVotes: false,
1721
- noNewTiersWithOwnerMinting: true
1722
- })
1723
- }),
1724
- IJBDirectory(mockJBDirectory),
1725
- IJBPrices(mockJBPrices),
1726
- IJBRulesets(mockJBRulesets),
1727
- IJB721TiersHookStore(address(store)),
1728
- IJBSplits(mockJBSplits)
1729
- );
1730
- hook.transferOwnership(owner);
1731
- // Will be resized later
1732
- JB721TierConfig[] memory tierConfigsRemaining = new JB721TierConfig[](initialNumberOfTiers);
1733
- JB721Tier[] memory tiersRemaining = new JB721Tier[](initialNumberOfTiers);
1734
- for (uint256 i; i < tiers.length; i++) {
1735
- tierConfigsRemaining[i] = tierConfigs[i];
1736
- tiersRemaining[i] = tiers[i];
1737
- }
1738
-
1739
- // Iterate through the remaining tiers and remove the ones in `tiersToRemove`.
1740
- // Do this by "swapping" the tier to remove with the last element in the array, and then "popping" that last
1741
- // element.
1742
- for (uint256 i; i < tiersRemaining.length;) {
1743
- bool swappedAndPopped;
1744
- for (uint256 j; j < tiersToRemove.length; j++) {
1745
- if (tiersRemaining[i].id == tiersToRemove[j]) {
1746
- // Swap and pop tiers removed
1747
- tiersRemaining[i] = tiersRemaining[tiersRemaining.length - 1];
1748
- tierConfigsRemaining[i] = tierConfigsRemaining[tierConfigsRemaining.length - 1];
1749
- // Remove the last elelment / reduce array length by 1
1750
- assembly ("memory-safe") {
1751
- mstore(tiersRemaining, sub(mload(tiersRemaining), 1))
1752
- mstore(tierConfigsRemaining, sub(mload(tierConfigsRemaining), 1))
1753
- }
1754
- swappedAndPopped = true;
1755
- break;
1756
- }
1757
- }
1758
- if (!swappedAndPopped) i++;
1759
- }
1760
-
1761
- vm.prank(owner);
1762
- hook.adjustTiers(new JB721TierConfig[](0), tiersToRemove);
1763
- JB721Tier[] memory tiersListDump = hook.test_store().ForTest_dumpTiersList(address(hook));
1764
- // Check: are all of the tiers are still in the linked list (both active and inactive)?
1765
- assertTrue(_isIn(tiers, tiersListDump));
1766
- // Check: does the linked list include only the tiers (active and inactives)?
1767
- assertTrue(_isIn(tiersListDump, tiers));
1768
- // Check: was the correct event emitted?
1769
- vm.expectEmit(true, false, false, true, address(hook.test_store()));
1770
- emit CleanTiers(address(hook), beneficiary);
1771
- vm.startPrank(beneficiary);
1772
- hook.test_store().cleanTiers(address(hook));
1773
- vm.stopPrank();
1774
- tiersListDump = hook.test_store().ForTest_dumpTiersList(address(hook));
1775
- // Check: is the correct number of tiers remaining?
1776
- assertEq(tiersListDump.length, initialNumberOfTiers - numberOfTiersToRemove);
1777
- // Check: are all of the remaining tiers in the dump?
1778
- assertTrue(_isIn(tiersRemaining, tiersListDump));
1779
- // Check: does the dump include any tiers which shouldn't be remaining?
1780
- assertTrue(_isIn(tiersListDump, tiersRemaining));
1781
- }
1782
-
1783
- function test_tiersOf_emptyArrayIfNoInitializedTiers(uint256 size) public {
1784
- // Initialize a hook without default tiers.
1785
- JB721TiersHook hook = _initHookDefaultTiers(0);
1786
-
1787
- // Try to get `size` tiers
1788
- JB721Tier[] memory intialTiers = hook.STORE().tiersOf(address(hook), new uint256[](0), false, 0, size);
1789
-
1790
- // Check: does the array have a length of 0?
1791
- assertEq(intialTiers.length, 0, "Length mismatch.");
1792
- }
1793
-
1794
- function test_setDiscountPercentOf_revertIfCannotIncreaseDiscount() public {
1795
- // Initial tier config and data.
1796
- JB721TierConfig[] memory tierConfigs = new JB721TierConfig[](1);
1797
- tierConfigs[0] = JB721TierConfig({
1798
- price: 10,
1799
- initialSupply: uint32(100),
1800
- votingUnits: uint16(0),
1801
- reserveFrequency: uint16(0),
1802
- reserveBeneficiary: reserveBeneficiary,
1803
- encodedIPFSUri: tokenUris[0],
1804
- category: uint24(100),
1805
- discountPercent: uint8(0),
1806
- flags: JB721TierConfigFlags({
1807
- allowOwnerMint: false,
1808
- useReserveBeneficiaryAsDefault: false,
1809
- transfersPausable: false,
1810
- useVotingUnits: true,
1811
- cantBeRemoved: true,
1812
- cantIncreaseDiscountPercent: true,
1813
- cantBuyWithCredits: false
1814
- }),
1815
- splitPercent: 0,
1816
- splits: new JBSplit[](0)
1817
- });
1818
- // Deploy the hook and its store with the initial tiers.
1819
- vm.etch(hook_i, address(hook).code);
1820
- JB721TiersHook hook = JB721TiersHook(hook_i);
1821
- hook.initialize(
1822
- projectId,
1823
- name,
1824
- symbol,
1825
- baseUri,
1826
- IJB721TokenUriResolver(mockTokenUriResolver),
1827
- contractUri,
1828
- JB721InitTiersConfig({
1829
- tiers: tierConfigs, currency: uint32(uint160(JBConstants.NATIVE_TOKEN)), decimals: 18
1830
- }),
1831
- JB721TiersHookFlags({
1832
- preventOverspending: false,
1833
- issueTokensForSplits: false,
1834
- noNewTiersWithReserves: false,
1835
- noNewTiersWithVotes: false,
1836
- noNewTiersWithOwnerMinting: true
1837
- })
1838
- );
1839
- hook.transferOwnership(owner);
1840
-
1841
- // Expect the `setDiscountPercentOf` call to revert because of the flag.
1842
- vm.expectRevert(
1843
- abi.encodeWithSelector(
1844
- JB721TiersHookStore.JB721TiersHookStore_DiscountPercentIncreaseNotAllowed.selector, 100, 0
1845
- )
1846
- );
1847
- vm.prank(owner);
1848
- // Attempt to increase the discount of the first tier to 100%
1849
- hook.setDiscountPercentOf(1, 100);
1850
- }
1851
-
1852
- function test_setDiscountPercentsOf_revertIfCannotIncreaseDiscounts() public {
1853
- // Initial tier config and data.
1854
- JB721TierConfig[] memory initialConfig = new JB721TierConfig[](2);
1855
- initialConfig[0] = JB721TierConfig({
1856
- price: 10,
1857
- initialSupply: uint32(100),
1858
- votingUnits: uint16(0),
1859
- reserveFrequency: uint16(0),
1860
- reserveBeneficiary: reserveBeneficiary,
1861
- encodedIPFSUri: tokenUris[0],
1862
- category: uint24(100),
1863
- discountPercent: uint8(0),
1864
- flags: JB721TierConfigFlags({
1865
- allowOwnerMint: false,
1866
- useReserveBeneficiaryAsDefault: false,
1867
- transfersPausable: false,
1868
- useVotingUnits: true,
1869
- cantBeRemoved: true,
1870
- cantIncreaseDiscountPercent: true,
1871
- cantBuyWithCredits: false
1872
- }),
1873
- splitPercent: 0,
1874
- splits: new JBSplit[](0)
1875
- });
1876
- initialConfig[1] = JB721TierConfig({
1877
- price: 10,
1878
- initialSupply: uint32(100),
1879
- votingUnits: uint16(0),
1880
- reserveFrequency: uint16(0),
1881
- reserveBeneficiary: reserveBeneficiary,
1882
- encodedIPFSUri: tokenUris[0],
1883
- category: uint24(100),
1884
- discountPercent: uint8(0),
1885
- flags: JB721TierConfigFlags({
1886
- allowOwnerMint: false,
1887
- useReserveBeneficiaryAsDefault: false,
1888
- transfersPausable: false,
1889
- useVotingUnits: true,
1890
- cantBeRemoved: true,
1891
- cantIncreaseDiscountPercent: false,
1892
- cantBuyWithCredits: false
1893
- }),
1894
- splitPercent: 0,
1895
- splits: new JBSplit[](0)
1896
- });
1897
-
1898
- // Deploy the hook and its store with the initial tiers.
1899
- vm.etch(hook_i, address(hook).code);
1900
- JB721TiersHook hook = JB721TiersHook(hook_i);
1901
- hook.initialize(
1902
- projectId,
1903
- name,
1904
- symbol,
1905
- baseUri,
1906
- IJB721TokenUriResolver(mockTokenUriResolver),
1907
- contractUri,
1908
- JB721InitTiersConfig({
1909
- tiers: initialConfig, currency: uint32(uint160(JBConstants.NATIVE_TOKEN)), decimals: 18
1910
- }),
1911
- JB721TiersHookFlags({
1912
- preventOverspending: false,
1913
- issueTokensForSplits: false,
1914
- noNewTiersWithReserves: false,
1915
- noNewTiersWithVotes: false,
1916
- noNewTiersWithOwnerMinting: true
1917
- })
1918
- );
1919
- hook.transferOwnership(owner);
1920
-
1921
- // Build calldata for increasing multiple tier discounts at once.
1922
- JB721TiersSetDiscountPercentConfig[] memory discountCalldata = new JB721TiersSetDiscountPercentConfig[](2);
1923
- discountCalldata[0] = JB721TiersSetDiscountPercentConfig({
1924
- tierId: 1,
1925
- discountPercent: 100 // invalid
1926
- });
1927
-
1928
- discountCalldata[1] = JB721TiersSetDiscountPercentConfig({
1929
- tierId: 2,
1930
- discountPercent: 100 // valid
1931
- });
1932
-
1933
- // Expect the `setDiscountPercentsOf` call to revert because of the flag.
1934
- vm.expectRevert(
1935
- abi.encodeWithSelector(
1936
- JB721TiersHookStore.JB721TiersHookStore_DiscountPercentIncreaseNotAllowed.selector, 100, 0
1937
- )
1938
- );
1939
- vm.prank(owner);
1940
- hook.setDiscountPercentsOf(discountCalldata);
1941
- }
1942
- }