@bananapus/721-hook-v6 0.0.42 → 0.0.45

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 (86) 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 +61 -19
  6. package/src/JB721CheckpointsDeployer.sol +10 -5
  7. package/src/JB721TiersHook.sol +66 -53
  8. package/src/JB721TiersHookDeployer.sol +8 -5
  9. package/src/JB721TiersHookProjectDeployer.sol +87 -46
  10. package/src/JB721TiersHookStore.sol +137 -107
  11. package/src/abstract/JB721Hook.sol +8 -6
  12. package/src/interfaces/IJB721Checkpoints.sol +21 -14
  13. package/src/interfaces/IJB721CheckpointsDeployer.sol +7 -3
  14. package/src/interfaces/IJB721TiersHook.sol +3 -3
  15. package/src/interfaces/IJB721TiersHookProjectDeployer.sol +4 -2
  16. package/src/interfaces/IJB721TiersHookStore.sol +11 -11
  17. package/src/libraries/JB721TiersHookLib.sol +1 -1
  18. package/src/structs/JB721TiersHookFlags.sol +1 -1
  19. package/src/structs/JBPayDataHookRulesetMetadata.sol +1 -1
  20. package/test/utils/AccessJBLib.sol +49 -0
  21. package/test/utils/ForTest_JB721TiersHook.sol +246 -0
  22. package/test/utils/TestBaseWorkflow.sol +213 -0
  23. package/test/utils/UnitTestSetup.sol +805 -0
  24. package/.gas-snapshot +0 -152
  25. package/ADMINISTRATION.md +0 -87
  26. package/ARCHITECTURE.md +0 -98
  27. package/AUDIT_INSTRUCTIONS.md +0 -77
  28. package/RISKS.md +0 -118
  29. package/SKILLS.md +0 -43
  30. package/STYLE_GUIDE.md +0 -610
  31. package/USER_JOURNEYS.md +0 -121
  32. package/assets/findings/nana-721-hook-v6-pashov-ai-audit-report-20260330-091257.md +0 -83
  33. package/slither-ci.config.json +0 -10
  34. package/test/721HookAttacks.t.sol +0 -408
  35. package/test/E2E/Pay_Mint_Redeem_E2E.t.sol +0 -985
  36. package/test/Fork.t.sol +0 -2346
  37. package/test/TestAuditGaps.sol +0 -1075
  38. package/test/TestCheckpoints.t.sol +0 -341
  39. package/test/TestSafeTransferReentrancy.t.sol +0 -305
  40. package/test/TestVotingUnitsLifecycle.t.sol +0 -313
  41. package/test/audit/AuditRegressions.t.sol +0 -83
  42. package/test/audit/CodexNemesisReserveSellout.t.sol +0 -66
  43. package/test/audit/CrossCurrencySplitNoPrices.t.sol +0 -123
  44. package/test/audit/FreshAudit.t.sol +0 -197
  45. package/test/audit/FutureTierPoC.t.sol +0 -39
  46. package/test/audit/FutureTierRemoval.t.sol +0 -47
  47. package/test/audit/Pass12L18.t.sol +0 -80
  48. package/test/audit/PayCreditsBypassTierSplits.t.sol +0 -200
  49. package/test/audit/ProjectDeployerAuth.t.sol +0 -266
  50. package/test/audit/RepoFindings.t.sol +0 -195
  51. package/test/audit/ReserveActivation.t.sol +0 -87
  52. package/test/audit/ReserveSlotProtection.t.sol +0 -273
  53. package/test/audit/RetroactiveReserveBeneficiaryDilution.t.sol +0 -149
  54. package/test/audit/SameCurrencyDecimalMismatch.t.sol +0 -249
  55. package/test/audit/SplitCreditsMismatch.t.sol +0 -219
  56. package/test/audit/SplitFailureRedistribution.t.sol +0 -143
  57. package/test/audit/USDTVoidReturnCompat.t.sol +0 -301
  58. package/test/fork/ERC20CashOutFork.t.sol +0 -633
  59. package/test/fork/ERC20TierSplitFork.t.sol +0 -596
  60. package/test/fork/IssueTokensForSplitsFork.t.sol +0 -516
  61. package/test/invariants/TierLifecycleInvariant.t.sol +0 -188
  62. package/test/invariants/TieredHookStoreInvariant.t.sol +0 -86
  63. package/test/invariants/handlers/TierLifecycleHandler.sol +0 -300
  64. package/test/invariants/handlers/TierStoreHandler.sol +0 -165
  65. package/test/regression/BrokenTerminalDoesNotDos.t.sol +0 -277
  66. package/test/regression/CacheTierLookup.t.sol +0 -190
  67. package/test/regression/ProjectDeployerRulesets.t.sol +0 -358
  68. package/test/regression/ReserveBeneficiaryOverwrite.t.sol +0 -155
  69. package/test/regression/SplitDistributionBugs.t.sol +0 -751
  70. package/test/regression/SplitNoBeneficiary.t.sol +0 -140
  71. package/test/unit/AuditFixes_Unit.t.sol +0 -624
  72. package/test/unit/JB721CheckpointsDeployer_AccessControl.t.sol +0 -116
  73. package/test/unit/JB721TiersRulesetMetadataResolver.t.sol +0 -144
  74. package/test/unit/JBBitmap.t.sol +0 -170
  75. package/test/unit/JBIpfsDecoder.t.sol +0 -136
  76. package/test/unit/TierSupplyReserveCheck.t.sol +0 -221
  77. package/test/unit/adjustTier_Unit.t.sol +0 -1942
  78. package/test/unit/deployer_Unit.t.sol +0 -114
  79. package/test/unit/getters_constructor_Unit.t.sol +0 -593
  80. package/test/unit/mintFor_mintReservesFor_Unit.t.sol +0 -452
  81. package/test/unit/pay_CrossCurrency_Unit.t.sol +0 -530
  82. package/test/unit/pay_Unit.t.sol +0 -1661
  83. package/test/unit/redeem_Unit.t.sol +0 -473
  84. package/test/unit/relayBeneficiary_Unit.t.sol +0 -182
  85. package/test/unit/splitHookDistribution_Unit.t.sol +0 -604
  86. package/test/unit/tierSplitRouting_Unit.t.sol +0 -757
@@ -1,452 +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
-
7
- contract Test_mintFor_mintReservesFor_Unit is UnitTestSetup {
8
- using stdStorage for StdStorage;
9
-
10
- function test_mintPendingReservesFor_mintsCorrectly() public {
11
- uint256 initialSupply = 200; // The number of NFTs available for each tier.
12
- uint256 totalMinted = 120; // The number of NFTs already minted for each tier (out of `initialSupply`).
13
- uint256 reservedMinted = 1; // The number of reserve NFTs already minted (out of `totalMinted`).
14
- uint256 reserveFrequency = 4000; // The frequency at which NFTs are reserved (4000/10000 = 40%).
15
- uint256 numberOfTiers = 3; // The number of tiers to set up.
16
-
17
- // With 120 total NFTs minted and 1 being a reserve mint, 119 are non-reserved.
18
- // With a 40% reserve frequency, 47 should be reserved.
19
- // Accounting for the 1 already minted, there should be 46 pending reserve mints.
20
-
21
- ForTest_JB721TiersHook hook = _initializeForTestHook(numberOfTiers);
22
-
23
- // Initialize `numberOfTiers` tiers.
24
- for (uint256 i; i < numberOfTiers; i++) {
25
- hook.test_store()
26
- .ForTest_setTier(
27
- address(hook),
28
- i + 1,
29
- JBStored721Tier({
30
- price: uint104((i + 1) * 10),
31
- // forge-lint: disable-next-line(unsafe-typecast)
32
- remainingSupply: uint32(initialSupply - totalMinted),
33
- // forge-lint: disable-next-line(unsafe-typecast)
34
- initialSupply: uint32(initialSupply),
35
- // forge-lint: disable-next-line(unsafe-typecast)
36
- reserveFrequency: uint16(reserveFrequency),
37
- category: uint24(100),
38
- discountPercent: uint8(0),
39
- packedBools: hook.test_store().ForTest_packBools(false, false, true, false, false, false),
40
- splitPercent: 0
41
- })
42
- );
43
- hook.test_store().ForTest_setReservesMintedFor(address(hook), i + 1, reservedMinted);
44
- }
45
-
46
- // Iterate through the tiers, minting the pending reserves,
47
- // and ensuring that the correct number of NFTs have been minted.
48
- for (uint256 tier = 1; tier <= numberOfTiers; tier++) {
49
- uint256 mintable = hook.test_store().numberOfPendingReservesFor(address(hook), tier);
50
-
51
- // Mint the reserve NFTs for the tier.
52
- for (uint256 token = 1; token <= mintable; token++) {
53
- vm.expectEmit(true, true, true, true, address(hook));
54
- emit MintReservedNft(_generateTokenId(tier, totalMinted + token), tier, reserveBeneficiary, owner);
55
- }
56
-
57
- vm.prank(owner);
58
- hook.mintPendingReservesFor(tier, mintable);
59
-
60
- // Check: does the reserve beneficiary have the correct number of NFTs?
61
- assertEq(hook.balanceOf(reserveBeneficiary), mintable * tier);
62
- }
63
- }
64
-
65
- // Todo case: initial 10, rr 3, minted 6, what happens? mint the reserves till there's no remaining. then if all
66
- // users burn, what happens?
67
- function test_mintPendingReservesFor_mintOddReservedTokens() public {
68
- uint256 initialSupply = 10; // The number of NFTs available for each tier.
69
- uint256 totalMinted = 6; // The number of NFTs already minted for each tier (out of `initialSupply`).
70
- uint256 reserveFrequency = 3; // The frequency at which NFTs are reserved (3/10 = 30%).
71
-
72
- ForTest_JB721TiersHook hook = _initializeForTestHook(1);
73
-
74
- // Initialize `numberOfTiers` tiers.
75
- hook.test_store()
76
- .ForTest_setTier(
77
- address(hook),
78
- 1,
79
- JBStored721Tier({
80
- price: uint104(10),
81
- // forge-lint: disable-next-line(unsafe-typecast)
82
- remainingSupply: uint32(initialSupply),
83
- // forge-lint: disable-next-line(unsafe-typecast)
84
- initialSupply: uint32(initialSupply),
85
- // forge-lint: disable-next-line(unsafe-typecast)
86
- reserveFrequency: uint16(reserveFrequency),
87
- category: uint24(100),
88
- discountPercent: uint8(0),
89
- packedBools: hook.test_store().ForTest_packBools(true, false, true, false, false, false),
90
- splitPercent: 0
91
- })
92
- );
93
-
94
- // Mint the initial tiers.
95
- uint16[] memory tiersToMint = new uint16[](totalMinted);
96
- for (uint256 i; i < totalMinted; i++) {
97
- tiersToMint[i] = 1;
98
- }
99
- vm.prank(owner);
100
- hook.mintFor(tiersToMint, beneficiary);
101
-
102
- // Iterate through the tiers, calculating how many reserve NFTs should be mintable.
103
- uint256 mintable = hook.test_store().numberOfPendingReservesFor(address(hook), 1);
104
- assertEq(mintable, 2, "Tier 1 should have 2 reserve NFTs mintable.");
105
-
106
- // Mint the next tier
107
- tiersToMint = new uint16[](1);
108
- tiersToMint[0] = 1;
109
- vm.prank(owner);
110
- hook.mintFor(tiersToMint, beneficiary);
111
-
112
- // Should have one more reserved.
113
- mintable = hook.test_store().numberOfPendingReservesFor(address(hook), 1);
114
- assertEq(mintable, 3, "Tier 1 should have 3 reserve NFTs mintable.");
115
-
116
- // Revert when minting the next.
117
- vm.expectRevert(
118
- abi.encodeWithSelector(JB721TiersHookStore.JB721TiersHookStore_InsufficientSupplyRemaining.selector, 1)
119
- );
120
- vm.prank(owner);
121
- hook.mintFor(tiersToMint, beneficiary);
122
-
123
- // Package reserves to mint.
124
- JB721TiersMintReservesConfig[] memory reservesToMint = new JB721TiersMintReservesConfig[](1);
125
- // forge-lint: disable-next-line(unsafe-typecast)
126
- reservesToMint[0] = JB721TiersMintReservesConfig({tierId: uint32(1), count: uint16(mintable)});
127
-
128
- // Mint the pending reserve NFTs.
129
- vm.prank(owner);
130
- hook.mintPendingReservesFor(reservesToMint);
131
-
132
- // Check: does the reserve beneficiary and beneficiary have the correct number of NFTs?
133
- assertEq(hook.balanceOf(reserveBeneficiary), 3);
134
- assertEq(hook.balanceOf(beneficiary), 7);
135
-
136
- mintable = hook.test_store().numberOfPendingReservesFor(address(hook), 1);
137
- assertEq(mintable, 0, "Tier 1 should have 0 reserve NFTs mintable.");
138
-
139
- // Burn them all.
140
- uint256[] memory tokenIdsToBurn = new uint256[](initialSupply);
141
- for (uint256 i; i < initialSupply; i++) {
142
- tokenIdsToBurn[i] = 1_000_000_000 + 1 + i;
143
- }
144
- vm.prank(address(hook));
145
- hook.burn(tokenIdsToBurn);
146
-
147
- // Check: does the reserve beneficiary and beneficiary have the correct number of NFTs?
148
- assertEq(hook.balanceOf(reserveBeneficiary), 0);
149
- assertEq(hook.balanceOf(beneficiary), 0);
150
-
151
- // No pending reserves still.
152
- mintable = hook.test_store().numberOfPendingReservesFor(address(hook), 1);
153
- assertEq(mintable, 0, "Tier 1 should have 0 reserve NFTs mintable.");
154
-
155
- // No remaining supply still.
156
- JB721Tier memory tier = hook.STORE().tierOf(address(hook), 1, false);
157
- assertEq(tier.remainingSupply, 0, "Tier 1 should have 0 remaining supply.");
158
-
159
- // Revert when minting the next.
160
- vm.expectRevert(
161
- abi.encodeWithSelector(JB721TiersHookStore.JB721TiersHookStore_InsufficientSupplyRemaining.selector, 1)
162
- );
163
- vm.prank(owner);
164
- hook.mintFor(tiersToMint, beneficiary);
165
- }
166
-
167
- function test_mintPendingReservesFor_mintMultipleReservedTokens() public {
168
- uint256 initialSupply = 200; // The number of NFTs available for each tier.
169
- uint256 totalMinted = 120; // The number of NFTs already minted for each tier (out of `initialSupply`).
170
- uint256 reservedMinted = 1; // The number of reserve NFTs already minted (out of `totalMinted`).
171
- uint256 reserveFrequency = 4000; // The frequency at which NFTs are reserved (4000/10000 = 40%).
172
- uint256 numberOfTiers = 3; // The number of tiers to set up.
173
-
174
- // With 120 total NFTs minted and 1 being a reserve mint, 119 are non-reserved.
175
- // With a 40% reserve frequency, 47 should be reserved.
176
- // Accounting for the 1 already minted, there should be 46 pending reserve mints.
177
-
178
- ForTest_JB721TiersHook hook = _initializeForTestHook(numberOfTiers);
179
-
180
- // Initialize `numberOfTiers` tiers.
181
- for (uint256 i; i < numberOfTiers; i++) {
182
- hook.test_store()
183
- .ForTest_setTier(
184
- address(hook),
185
- i + 1,
186
- JBStored721Tier({
187
- price: uint104((i + 1) * 10),
188
- // forge-lint: disable-next-line(unsafe-typecast)
189
- remainingSupply: uint32(initialSupply - totalMinted),
190
- // forge-lint: disable-next-line(unsafe-typecast)
191
- initialSupply: uint32(initialSupply),
192
- // forge-lint: disable-next-line(unsafe-typecast)
193
- reserveFrequency: uint16(reserveFrequency),
194
- category: uint24(100),
195
- discountPercent: uint8(0),
196
- packedBools: hook.test_store().ForTest_packBools(false, false, true, false, false, false),
197
- splitPercent: 0
198
- })
199
- );
200
-
201
- // Set the number of reserve NFTs already minted for the tier.
202
- hook.test_store().ForTest_setReservesMintedFor(address(hook), i + 1, reservedMinted);
203
- }
204
-
205
- uint256 totalMintable; // Keep a running counter of how many reserve NFTs should be mintable.
206
-
207
- JB721TiersMintReservesConfig[] memory reservesToMint = new JB721TiersMintReservesConfig[](numberOfTiers);
208
-
209
- // Iterate through the tiers, calculating how many reserve NFTs should be mintable.
210
- for (uint256 tier = 1; tier <= numberOfTiers; tier++) {
211
- uint256 mintable = hook.test_store().numberOfPendingReservesFor(address(hook), tier);
212
- // forge-lint: disable-next-line(unsafe-typecast)
213
- reservesToMint[tier - 1] = JB721TiersMintReservesConfig({tierId: uint32(tier), count: uint16(mintable)});
214
- totalMintable += mintable;
215
- for (uint256 token = 1; token <= mintable; token++) {
216
- uint256 tokenNonce = totalMinted + token; // Avoid stack too deep
217
- vm.expectEmit(true, true, true, true, address(hook));
218
- emit MintReservedNft(_generateTokenId(tier, tokenNonce), tier, reserveBeneficiary, owner);
219
- }
220
- }
221
-
222
- // Mint the pending reserve NFTs.
223
- vm.prank(owner);
224
- hook.mintPendingReservesFor(reservesToMint);
225
-
226
- // Check: does the reserve beneficiary has the correct number of NFTs?
227
- assertEq(hook.balanceOf(reserveBeneficiary), totalMintable);
228
- }
229
-
230
- function test_mintPendingReservesFor_revertIfReservedMintingIsPausedInRuleset() public {
231
- uint256 initialSupply = 200; // The number of NFTs available for each tier.
232
- uint256 totalMinted = 120; // The number of NFTs already minted for each tier (out of `initialSupply`).
233
- uint256 reservedMinted = 1; // The number of reserve NFTs already minted (out of `totalMinted`).
234
- uint256 reserveFrequency = 4000; // The frequency at which NFTs are reserved (4000/10000 = 40%).
235
- uint256 numberOfTiers = 3; // The number of tiers to set up.
236
-
237
- // Set up the ruleset to pause reserved minting.
238
- // This is done with the `JBRulesetMetadata.metadata` field.
239
- // The second bit in `JBRulesetMetadata.metadata` is the `mintPendingReservesPaused` bit.
240
- // See `JB721TiersRulesetMetadataResolver`.
241
- mockAndExpect(
242
- mockJBRulesets,
243
- abi.encodeCall(IJBRulesets.currentOf, projectId),
244
- abi.encode(
245
- JBRuleset({
246
- cycleNumber: 1,
247
- id: uint48(block.timestamp),
248
- basedOnId: 0,
249
- start: uint48(block.timestamp),
250
- duration: 600,
251
- weight: 10e18,
252
- weightCutPercent: 0,
253
- approvalHook: IJBRulesetApprovalHook(address(0)),
254
- metadata: JBRulesetMetadataResolver.packRulesetMetadata(
255
- JBRulesetMetadata({
256
- reservedPercent: 5000, //50%
257
- cashOutTaxRate: 5000, //50%
258
- baseCurrency: uint32(uint160(JBConstants.NATIVE_TOKEN)),
259
- pausePay: false,
260
- pauseCreditTransfers: false,
261
- allowOwnerMinting: true,
262
- allowSetCustomToken: false,
263
- allowTerminalMigration: false,
264
- allowSetTerminals: false,
265
- allowSetController: false,
266
- allowAddAccountingContext: false,
267
- allowAddPriceFeed: false,
268
- ownerMustSendPayouts: false,
269
- holdFees: false,
270
- useTotalSurplusForCashOuts: false,
271
- useDataHookForPay: true,
272
- useDataHookForCashOut: true,
273
- dataHook: address(0),
274
- metadata: 2
275
- })
276
- )
277
- })
278
- )
279
- );
280
-
281
- ForTest_JB721TiersHook hook = _initializeForTestHook(numberOfTiers);
282
-
283
- for (uint256 i; i < numberOfTiers; i++) {
284
- hook.test_store()
285
- .ForTest_setTier(
286
- address(hook),
287
- i + 1,
288
- JBStored721Tier({
289
- price: uint104((i + 1) * 10),
290
- // forge-lint: disable-next-line(unsafe-typecast)
291
- remainingSupply: uint32(initialSupply - totalMinted),
292
- // forge-lint: disable-next-line(unsafe-typecast)
293
- initialSupply: uint32(initialSupply),
294
- // forge-lint: disable-next-line(unsafe-typecast)
295
- reserveFrequency: uint16(reserveFrequency),
296
- category: uint24(100),
297
- discountPercent: uint8(0),
298
- packedBools: hook.test_store().ForTest_packBools(false, false, true, false, false, false),
299
- splitPercent: 0
300
- })
301
- );
302
- hook.test_store().ForTest_setReservesMintedFor(address(hook), i + 1, reservedMinted);
303
- }
304
-
305
- // Iterate through the tiers, attempting to mint the pending reserves.
306
- // Check: is the correct error thrown?
307
- for (uint256 tier = 1; tier <= numberOfTiers; tier++) {
308
- uint256 mintable = hook.test_store().numberOfPendingReservesFor(address(hook), tier);
309
- vm.prank(owner);
310
- vm.expectRevert(JB721TiersHook.JB721TiersHook_MintReserveNftsPaused.selector);
311
- hook.mintPendingReservesFor(tier, mintable);
312
- }
313
- }
314
-
315
- function test_mintPendingReservesFor_revertIfNotEnoughPendingReserves() public {
316
- uint256 initialSupply = 200; // The number of NFTs available for each tier.
317
- uint256 totalMinted = 120; // The number of NFTs already minted for each tier (out of `initialSupply`).
318
- uint256 reservedMinted = 1; // The number of reserve NFTs already minted (out of `totalMinted`).
319
- uint256 reserveFrequency = 4000; // The frequency at which NFTs are reserved (4000/10000 = 40%).
320
-
321
- ForTest_JB721TiersHook hook = _initializeForTestHook(10);
322
-
323
- // Initialize `numberOfTiers` tiers.
324
- for (uint256 i; i < 10; i++) {
325
- hook.test_store()
326
- .ForTest_setTier(
327
- address(hook),
328
- i + 1,
329
- JBStored721Tier({
330
- price: uint104((i + 1) * 10),
331
- // forge-lint: disable-next-line(unsafe-typecast)
332
- remainingSupply: uint32(initialSupply - totalMinted),
333
- // forge-lint: disable-next-line(unsafe-typecast)
334
- initialSupply: uint32(initialSupply),
335
- // forge-lint: disable-next-line(unsafe-typecast)
336
- reserveFrequency: uint16(reserveFrequency),
337
- category: uint24(100),
338
- discountPercent: uint8(0),
339
- packedBools: hook.test_store().ForTest_packBools(false, false, true, false, false, false),
340
- splitPercent: 0
341
- })
342
- );
343
- hook.test_store().ForTest_setReservesMintedFor(address(hook), i + 1, reservedMinted);
344
- }
345
-
346
- // Iterate through the tiers, attempting to mint more pending reserves than what is available.
347
- for (uint256 i = 1; i <= 10; i++) {
348
- // Get the number that we could mint successfully.
349
- uint256 amount = hook.test_store().numberOfPendingReservesFor(address(hook), i);
350
- // Increase it by 1 to cause an error, then attempt to mint.
351
- amount++;
352
- // Check: is the correct error thrown?
353
- vm.expectRevert(
354
- abi.encodeWithSelector(
355
- JB721TiersHookStore.JB721TiersHookStore_InsufficientPendingReserves.selector, amount, amount - 1
356
- )
357
- );
358
- vm.prank(owner);
359
- hook.mintPendingReservesFor(i, amount);
360
- }
361
- }
362
-
363
- /// @notice Tiers with reserveFrequency > 0 and no beneficiary are now rejected at creation time.
364
- /// This test verifies that the creation-time check prevents such tiers from existing.
365
- function test_numberOfPendingReservesFor_noReservesIfNoBeneficiarySet() public {
366
- // Build tier configs with reserveFrequency > 0 but reserveBeneficiary == address(0).
367
- JB721TierConfig[] memory tierConfigs = new JB721TierConfig[](1);
368
- tierConfigs[0] = JB721TierConfig({
369
- price: uint104(10),
370
- initialSupply: uint32(200),
371
- votingUnits: 0,
372
- reserveFrequency: uint16(9),
373
- reserveBeneficiary: address(0),
374
- encodedIPFSUri: bytes32(uint256(1)),
375
- category: uint24(100),
376
- discountPercent: 0,
377
- flags: JB721TierConfigFlags({
378
- allowOwnerMint: false,
379
- useReserveBeneficiaryAsDefault: false,
380
- transfersPausable: false,
381
- useVotingUnits: true,
382
- cantBeRemoved: false,
383
- cantIncreaseDiscountPercent: false,
384
- cantBuyWithCredits: false
385
- }),
386
- splitPercent: 0,
387
- splits: new JBSplit[](0)
388
- });
389
-
390
- // The creation-time check should reject a tier with reserves but no beneficiary.
391
- ForTest_JB721TiersHookStore hookStore = new ForTest_JB721TiersHookStore();
392
- vm.expectRevert(
393
- abi.encodeWithSelector(JB721TiersHookStore.JB721TiersHookStore_MissingReserveBeneficiary.selector, 1)
394
- );
395
- hookStore.recordAddTiers(tierConfigs);
396
- }
397
-
398
- function test_mintFor_mintArrayOfTiers() public {
399
- uint256 numberOfTiers = 3;
400
-
401
- defaultTierConfig.flags.allowOwnerMint = true;
402
- defaultTierConfig.reserveFrequency = 0;
403
- ForTest_JB721TiersHook hook = _initializeForTestHook(numberOfTiers);
404
-
405
- // Mint 6 NFTs, 2 from each tier.
406
- uint16[] memory tiersToMint = new uint16[](numberOfTiers * 2);
407
- for (uint256 i; i < numberOfTiers; i++) {
408
- // forge-lint: disable-next-line(unsafe-typecast)
409
- tiersToMint[i] = uint16(i) + 1;
410
- // forge-lint: disable-next-line(unsafe-typecast)
411
- tiersToMint[tiersToMint.length - 1 - i] = uint16(i) + 1;
412
- }
413
-
414
- vm.prank(owner);
415
- hook.mintFor(tiersToMint, beneficiary);
416
-
417
- // Check: does the beneficiary have the correct number of NFTs?
418
- assertEq(hook.balanceOf(beneficiary), 6);
419
-
420
- // Check: does the beneficiary own the correct NFTs?
421
- assertEq(hook.ownerOf(_generateTokenId(1, 1)), beneficiary);
422
- assertEq(hook.ownerOf(_generateTokenId(1, 2)), beneficiary);
423
- assertEq(hook.ownerOf(_generateTokenId(2, 1)), beneficiary);
424
- assertEq(hook.ownerOf(_generateTokenId(2, 2)), beneficiary);
425
- assertEq(hook.ownerOf(_generateTokenId(3, 1)), beneficiary);
426
- assertEq(hook.ownerOf(_generateTokenId(3, 2)), beneficiary);
427
- }
428
-
429
- function test_mintFor_revertIfManualMintNotAllowed() public {
430
- uint256 numberOfTiers = 10;
431
-
432
- uint16[] memory tiersToMint = new uint16[](numberOfTiers * 2);
433
- for (uint256 i; i < numberOfTiers; i++) {
434
- // forge-lint: disable-next-line(unsafe-typecast)
435
- tiersToMint[i] = uint16(i) + 1;
436
- // forge-lint: disable-next-line(unsafe-typecast)
437
- tiersToMint[tiersToMint.length - 1 - i] = uint16(i) + 1;
438
- }
439
-
440
- // Set the `allowOwnerMint` flag to false and initialize the hook.
441
- defaultTierConfig.flags.allowOwnerMint = false;
442
- ForTest_JB721TiersHook hook = _initializeForTestHook(numberOfTiers);
443
-
444
- vm.prank(owner);
445
-
446
- // Expect the function call to revert with the specified error message.
447
- vm.expectRevert(abi.encodeWithSelector(JB721TiersHookStore.JB721TiersHookStore_CantMintManually.selector, 1));
448
-
449
- // Call the `mintFor` function to trigger the revert.
450
- hook.mintFor(tiersToMint, beneficiary);
451
- }
452
- }