@bananapus/721-hook-v6 0.0.28 → 0.0.30

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 (46) hide show
  1. package/ADMINISTRATION.md +38 -11
  2. package/ARCHITECTURE.md +53 -99
  3. package/AUDIT_INSTRUCTIONS.md +84 -383
  4. package/CHANGELOG.md +71 -0
  5. package/README.md +79 -225
  6. package/RISKS.md +28 -11
  7. package/SKILLS.md +29 -296
  8. package/STYLE_GUIDE.md +57 -18
  9. package/USER_JOURNEYS.md +57 -501
  10. package/package.json +1 -1
  11. package/references/operations.md +28 -0
  12. package/references/runtime.md +32 -0
  13. package/script/Deploy.s.sol +5 -4
  14. package/src/JB721TiersHook.sol +1 -1
  15. package/src/JB721TiersHookDeployer.sol +1 -1
  16. package/src/JB721TiersHookProjectDeployer.sol +1 -1
  17. package/src/JB721TiersHookStore.sol +23 -17
  18. package/src/libraries/JB721Constants.sol +1 -1
  19. package/src/libraries/JB721TiersRulesetMetadataResolver.sol +1 -1
  20. package/src/libraries/JBBitmap.sol +1 -1
  21. package/src/libraries/JBIpfsDecoder.sol +1 -1
  22. package/src/structs/JB721Tier.sol +5 -11
  23. package/src/structs/JB721TierConfig.sol +5 -20
  24. package/src/structs/JB721TierConfigFlags.sol +26 -0
  25. package/src/structs/JB721TierFlags.sol +17 -0
  26. package/test/721HookAttacks.t.sol +22 -17
  27. package/test/E2E/Pay_Mint_Redeem_E2E.t.sol +19 -14
  28. package/test/Fork.t.sol +69 -54
  29. package/test/TestAuditGaps.sol +73 -56
  30. package/test/TestSafeTransferReentrancy.t.sol +4 -4
  31. package/test/TestVotingUnitsLifecycle.t.sol +11 -11
  32. package/test/audit/CodexPayCreditsBypassTierSplits.t.sol +10 -7
  33. package/test/audit/CodexSplitCreditsMismatch.t.sol +10 -7
  34. package/test/fork/ERC20CashOutFork.t.sol +37 -28
  35. package/test/fork/ERC20TierSplitFork.t.sol +28 -21
  36. package/test/fork/IssueTokensForSplitsFork.t.sol +10 -7
  37. package/test/invariants/handlers/TierLifecycleHandler.sol +10 -7
  38. package/test/invariants/handlers/TierStoreHandler.sol +10 -7
  39. package/test/regression/ProjectDeployerRulesets.t.sol +10 -7
  40. package/test/regression/ReserveBeneficiaryOverwrite.t.sol +6 -6
  41. package/test/unit/AuditFixes_Unit.t.sol +37 -28
  42. package/test/unit/adjustTier_Unit.t.sol +268 -202
  43. package/test/unit/getters_constructor_Unit.t.sol +20 -14
  44. package/test/unit/mintFor_mintReservesFor_Unit.t.sol +2 -2
  45. package/test/unit/pay_Unit.t.sol +1 -1
  46. package/CHANGE_LOG.md +0 -359
package/test/Fork.t.sol CHANGED
@@ -82,6 +82,7 @@ import "../src/structs/JB721TiersRulesetMetadata.sol";
82
82
  import "../src/libraries/JB721TiersRulesetMetadataResolver.sol";
83
83
  // forge-lint: disable-next-line(unaliased-plain-import)
84
84
  import "../src/libraries/JB721Constants.sol";
85
+ import {JB721TierConfigFlags} from "../src/structs/JB721TierConfigFlags.sol";
85
86
 
86
87
  /// @title Fork_721Hook_Test
87
88
  /// @notice Comprehensive fork tests for JB721TiersHook: lifecycle, features, flags, and adversarial conditions.
@@ -358,13 +359,15 @@ contract Fork_721Hook_Test is Test {
358
359
  encodedIPFSUri: IPFS_URI,
359
360
  category: uint24(100),
360
361
  discountPercent: 0,
361
- allowOwnerMint: allowOwnerMint,
362
- useReserveBeneficiaryAsDefault: false,
363
- transfersPausable: false,
364
- useVotingUnits: false,
365
- cantBeRemoved: false,
366
- cantIncreaseDiscountPercent: false,
367
- cantBuyWithCredits: false,
362
+ flags: JB721TierConfigFlags({
363
+ allowOwnerMint: allowOwnerMint,
364
+ useReserveBeneficiaryAsDefault: false,
365
+ transfersPausable: false,
366
+ useVotingUnits: false,
367
+ cantBeRemoved: false,
368
+ cantIncreaseDiscountPercent: false,
369
+ cantBuyWithCredits: false
370
+ }),
368
371
  splitPercent: 0,
369
372
  splits: new JBSplit[](0)
370
373
  });
@@ -789,13 +792,15 @@ contract Fork_721Hook_Test is Test {
789
792
  encodedIPFSUri: IPFS_URI,
790
793
  category: 200,
791
794
  discountPercent: 0,
792
- allowOwnerMint: false,
793
- useReserveBeneficiaryAsDefault: false,
794
- transfersPausable: false,
795
- useVotingUnits: false,
796
- cantBeRemoved: false,
797
- cantIncreaseDiscountPercent: false,
798
- cantBuyWithCredits: false,
795
+ flags: JB721TierConfigFlags({
796
+ allowOwnerMint: false,
797
+ useReserveBeneficiaryAsDefault: false,
798
+ transfersPausable: false,
799
+ useVotingUnits: false,
800
+ cantBeRemoved: false,
801
+ cantIncreaseDiscountPercent: false,
802
+ cantBuyWithCredits: false
803
+ }),
799
804
  splitPercent: 0,
800
805
  splits: new JBSplit[](0)
801
806
  });
@@ -822,13 +827,15 @@ contract Fork_721Hook_Test is Test {
822
827
  encodedIPFSUri: IPFS_URI,
823
828
  category: 200,
824
829
  discountPercent: 0,
825
- allowOwnerMint: true,
826
- useReserveBeneficiaryAsDefault: false,
827
- transfersPausable: false,
828
- useVotingUnits: false,
829
- cantBeRemoved: false,
830
- cantIncreaseDiscountPercent: false,
831
- cantBuyWithCredits: false,
830
+ flags: JB721TierConfigFlags({
831
+ allowOwnerMint: true,
832
+ useReserveBeneficiaryAsDefault: false,
833
+ transfersPausable: false,
834
+ useVotingUnits: false,
835
+ cantBeRemoved: false,
836
+ cantIncreaseDiscountPercent: false,
837
+ cantBuyWithCredits: false
838
+ }),
832
839
  splitPercent: 0,
833
840
  splits: new JBSplit[](0)
834
841
  });
@@ -841,7 +848,7 @@ contract Fork_721Hook_Test is Test {
841
848
  /// @notice cantBeRemoved: removing an immutable tier should revert.
842
849
  function test_fork_cantBeRemoved_reverts() public {
843
850
  JB721TierConfig[] memory tierConfigs = _makeStandardTiers(1, 10, false);
844
- tierConfigs[0].cantBeRemoved = true;
851
+ tierConfigs[0].flags.cantBeRemoved = true;
845
852
  JB721TiersHookFlags memory flags = _defaultFlags();
846
853
  (, address hook) = _launchProject(tierConfigs, flags, 5000, true, 0x00);
847
854
 
@@ -906,7 +913,7 @@ contract Fork_721Hook_Test is Test {
906
913
  function test_fork_cannotIncreaseDiscount() public {
907
914
  JB721TierConfig[] memory tierConfigs = _makeStandardTiers(1, 10, false);
908
915
  tierConfigs[0].discountPercent = 50;
909
- tierConfigs[0].cantIncreaseDiscountPercent = true;
916
+ tierConfigs[0].flags.cantIncreaseDiscountPercent = true;
910
917
  JB721TiersHookFlags memory flags = _defaultFlags();
911
918
  (, address hook) = _launchProject(tierConfigs, flags, 5000, true, 0x00);
912
919
 
@@ -997,7 +1004,7 @@ contract Fork_721Hook_Test is Test {
997
1004
  /// @notice transfersPausable tier flag + ruleset metadata pauses transfers.
998
1005
  function test_fork_transfersPaused_reverts() public {
999
1006
  JB721TierConfig[] memory tierConfigs = _makeStandardTiers(1, 10, false);
1000
- tierConfigs[0].transfersPausable = true;
1007
+ tierConfigs[0].flags.transfersPausable = true;
1001
1008
  JB721TiersHookFlags memory flags = _defaultFlags();
1002
1009
 
1003
1010
  // Pack 721 metadata: bit 0 = pauseTransfers = true.
@@ -1022,7 +1029,7 @@ contract Fork_721Hook_Test is Test {
1022
1029
  /// @notice Transfer works when transfersPausable=true but ruleset metadata doesn't pause.
1023
1030
  function test_fork_transfersPausable_notPaused_works() public {
1024
1031
  JB721TierConfig[] memory tierConfigs = _makeStandardTiers(1, 10, false);
1025
- tierConfigs[0].transfersPausable = true;
1032
+ tierConfigs[0].flags.transfersPausable = true;
1026
1033
  JB721TiersHookFlags memory flags = _defaultFlags();
1027
1034
 
1028
1035
  // 721 metadata: transfers NOT paused.
@@ -1130,13 +1137,15 @@ contract Fork_721Hook_Test is Test {
1130
1137
  encodedIPFSUri: IPFS_URI,
1131
1138
  category: 200,
1132
1139
  discountPercent: 0,
1133
- allowOwnerMint: false,
1134
- useReserveBeneficiaryAsDefault: false,
1135
- transfersPausable: false,
1136
- useVotingUnits: false,
1137
- cantBeRemoved: false,
1138
- cantIncreaseDiscountPercent: false,
1139
- cantBuyWithCredits: false,
1140
+ flags: JB721TierConfigFlags({
1141
+ allowOwnerMint: false,
1142
+ useReserveBeneficiaryAsDefault: false,
1143
+ transfersPausable: false,
1144
+ useVotingUnits: false,
1145
+ cantBeRemoved: false,
1146
+ cantIncreaseDiscountPercent: false,
1147
+ cantBuyWithCredits: false
1148
+ }),
1140
1149
  splitPercent: 0,
1141
1150
  splits: new JBSplit[](0)
1142
1151
  });
@@ -1298,7 +1307,7 @@ contract Fork_721Hook_Test is Test {
1298
1307
  /// @notice Custom voting units (useVotingUnits=true) uses specified value.
1299
1308
  function test_fork_customVotingUnits() public {
1300
1309
  JB721TierConfig[] memory tierConfigs = _makeStandardTiers(1, 10, false);
1301
- tierConfigs[0].useVotingUnits = true;
1310
+ tierConfigs[0].flags.useVotingUnits = true;
1302
1311
  tierConfigs[0].votingUnits = 42;
1303
1312
  JB721TiersHookFlags memory flags = _defaultFlags();
1304
1313
  (uint256 projectId, address hook) = _launchProject(tierConfigs, flags, 5000, true, 0x00);
@@ -1524,13 +1533,15 @@ contract Fork_721Hook_Test is Test {
1524
1533
  encodedIPFSUri: IPFS_URI,
1525
1534
  category: 200,
1526
1535
  discountPercent: 0,
1527
- allowOwnerMint: false,
1528
- useReserveBeneficiaryAsDefault: false,
1529
- transfersPausable: false,
1530
- useVotingUnits: false,
1531
- cantBeRemoved: false,
1532
- cantIncreaseDiscountPercent: false,
1533
- cantBuyWithCredits: false,
1536
+ flags: JB721TierConfigFlags({
1537
+ allowOwnerMint: false,
1538
+ useReserveBeneficiaryAsDefault: false,
1539
+ transfersPausable: false,
1540
+ useVotingUnits: false,
1541
+ cantBeRemoved: false,
1542
+ cantIncreaseDiscountPercent: false,
1543
+ cantBuyWithCredits: false
1544
+ }),
1534
1545
  splitPercent: 0,
1535
1546
  splits: new JBSplit[](0)
1536
1547
  });
@@ -1771,13 +1782,15 @@ contract Fork_721Hook_Test is Test {
1771
1782
  encodedIPFSUri: IPFS_URI,
1772
1783
  category: uint24((i + 1) * 100), // Categories: 100, 200, 300
1773
1784
  discountPercent: 0,
1774
- allowOwnerMint: false,
1775
- useReserveBeneficiaryAsDefault: false,
1776
- transfersPausable: false,
1777
- useVotingUnits: false,
1778
- cantBeRemoved: false,
1779
- cantIncreaseDiscountPercent: false,
1780
- cantBuyWithCredits: false,
1785
+ flags: JB721TierConfigFlags({
1786
+ allowOwnerMint: false,
1787
+ useReserveBeneficiaryAsDefault: false,
1788
+ transfersPausable: false,
1789
+ useVotingUnits: false,
1790
+ cantBeRemoved: false,
1791
+ cantIncreaseDiscountPercent: false,
1792
+ cantBuyWithCredits: false
1793
+ }),
1781
1794
  splitPercent: 0,
1782
1795
  splits: new JBSplit[](0)
1783
1796
  });
@@ -2047,13 +2060,15 @@ contract Fork_721Hook_Test is Test {
2047
2060
  encodedIPFSUri: IPFS_URI,
2048
2061
  category: category,
2049
2062
  discountPercent: 0,
2050
- allowOwnerMint: false,
2051
- useReserveBeneficiaryAsDefault: false,
2052
- transfersPausable: false,
2053
- useVotingUnits: false,
2054
- cantBeRemoved: false,
2055
- cantIncreaseDiscountPercent: false,
2056
- cantBuyWithCredits: false,
2063
+ flags: JB721TierConfigFlags({
2064
+ allowOwnerMint: false,
2065
+ useReserveBeneficiaryAsDefault: false,
2066
+ transfersPausable: false,
2067
+ useVotingUnits: false,
2068
+ cantBeRemoved: false,
2069
+ cantIncreaseDiscountPercent: false,
2070
+ cantBuyWithCredits: false
2071
+ }),
2057
2072
  splitPercent: splitPct,
2058
2073
  splits: splits
2059
2074
  });
@@ -10,6 +10,7 @@ import {IJBSplitHook} from "@bananapus/core-v6/src/interfaces/IJBSplitHook.sol";
10
10
  import {IJBSplits} from "@bananapus/core-v6/src/interfaces/IJBSplits.sol";
11
11
  import {IJBDirectory} from "@bananapus/core-v6/src/interfaces/IJBDirectory.sol";
12
12
  import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
13
+ import {JB721TierConfigFlags} from "../src/structs/JB721TierConfigFlags.sol";
13
14
 
14
15
  // =====================================================================
15
16
  // Malicious split hook that attempts reentrancy during fund distribution
@@ -450,13 +451,15 @@ contract TestAuditGaps_GasLimits is UnitTestSetup {
450
451
  // forge-lint: disable-next-line(unsafe-typecast)
451
452
  category: uint24(i + 1), // Ascending categories
452
453
  discountPercent: 0,
453
- allowOwnerMint: false,
454
- useReserveBeneficiaryAsDefault: false,
455
- transfersPausable: false,
456
- cantBeRemoved: false,
457
- cantIncreaseDiscountPercent: false,
458
- cantBuyWithCredits: false,
459
- useVotingUnits: false,
454
+ flags: JB721TierConfigFlags({
455
+ allowOwnerMint: false,
456
+ useReserveBeneficiaryAsDefault: false,
457
+ transfersPausable: false,
458
+ useVotingUnits: false,
459
+ cantBeRemoved: false,
460
+ cantIncreaseDiscountPercent: false,
461
+ cantBuyWithCredits: false
462
+ }),
460
463
  splitPercent: 0,
461
464
  splits: new JBSplit[](0)
462
465
  });
@@ -504,13 +507,15 @@ contract TestAuditGaps_GasLimits is UnitTestSetup {
504
507
  // forge-lint: disable-next-line(unsafe-typecast)
505
508
  category: uint24(i + 1),
506
509
  discountPercent: 0,
507
- allowOwnerMint: false,
508
- useReserveBeneficiaryAsDefault: false,
509
- transfersPausable: false,
510
- cantBeRemoved: false,
511
- cantIncreaseDiscountPercent: false,
512
- cantBuyWithCredits: false,
513
- useVotingUnits: false,
510
+ flags: JB721TierConfigFlags({
511
+ allowOwnerMint: false,
512
+ useReserveBeneficiaryAsDefault: false,
513
+ transfersPausable: false,
514
+ useVotingUnits: false,
515
+ cantBeRemoved: false,
516
+ cantIncreaseDiscountPercent: false,
517
+ cantBuyWithCredits: false
518
+ }),
514
519
  splitPercent: 0,
515
520
  splits: new JBSplit[](0)
516
521
  });
@@ -560,13 +565,15 @@ contract TestAuditGaps_GasLimits is UnitTestSetup {
560
565
  // forge-lint: disable-next-line(unsafe-typecast)
561
566
  category: uint24(i + 1),
562
567
  discountPercent: 0,
563
- allowOwnerMint: false,
564
- useReserveBeneficiaryAsDefault: false,
565
- transfersPausable: false,
566
- cantBeRemoved: false,
567
- cantIncreaseDiscountPercent: false,
568
- cantBuyWithCredits: false,
569
- useVotingUnits: false,
568
+ flags: JB721TierConfigFlags({
569
+ allowOwnerMint: false,
570
+ useReserveBeneficiaryAsDefault: false,
571
+ transfersPausable: false,
572
+ useVotingUnits: false,
573
+ cantBeRemoved: false,
574
+ cantIncreaseDiscountPercent: false,
575
+ cantBuyWithCredits: false
576
+ }),
570
577
  splitPercent: 0,
571
578
  splits: new JBSplit[](0)
572
579
  });
@@ -662,13 +669,15 @@ contract TestAuditGaps_GasLimits is UnitTestSetup {
662
669
  // forge-lint: disable-next-line(unsafe-typecast)
663
670
  category: uint24(i + 1),
664
671
  discountPercent: 0,
665
- allowOwnerMint: false,
666
- useReserveBeneficiaryAsDefault: false,
667
- transfersPausable: false,
668
- cantBeRemoved: false,
669
- cantIncreaseDiscountPercent: false,
670
- cantBuyWithCredits: false,
671
- useVotingUnits: false,
672
+ flags: JB721TierConfigFlags({
673
+ allowOwnerMint: false,
674
+ useReserveBeneficiaryAsDefault: false,
675
+ transfersPausable: false,
676
+ useVotingUnits: false,
677
+ cantBeRemoved: false,
678
+ cantIncreaseDiscountPercent: false,
679
+ cantBuyWithCredits: false
680
+ }),
672
681
  splitPercent: 0,
673
682
  splits: new JBSplit[](0)
674
683
  });
@@ -712,13 +721,15 @@ contract TestAuditGaps_GasLimits is UnitTestSetup {
712
721
  // forge-lint: disable-next-line(unsafe-typecast)
713
722
  category: uint24(i + 1),
714
723
  discountPercent: 0,
715
- allowOwnerMint: false,
716
- useReserveBeneficiaryAsDefault: false,
717
- transfersPausable: false,
718
- cantBeRemoved: false,
719
- cantIncreaseDiscountPercent: false,
720
- cantBuyWithCredits: false,
721
- useVotingUnits: false,
724
+ flags: JB721TierConfigFlags({
725
+ allowOwnerMint: false,
726
+ useReserveBeneficiaryAsDefault: false,
727
+ transfersPausable: false,
728
+ useVotingUnits: false,
729
+ cantBeRemoved: false,
730
+ cantIncreaseDiscountPercent: false,
731
+ cantBuyWithCredits: false
732
+ }),
722
733
  splitPercent: 0,
723
734
  splits: new JBSplit[](0)
724
735
  });
@@ -774,13 +785,15 @@ contract TestAuditGaps_GasLimits is UnitTestSetup {
774
785
  // forge-lint: disable-next-line(unsafe-typecast)
775
786
  category: uint24(i + 1),
776
787
  discountPercent: 0,
777
- allowOwnerMint: false,
778
- useReserveBeneficiaryAsDefault: false,
779
- transfersPausable: false,
780
- cantBeRemoved: false,
781
- cantIncreaseDiscountPercent: false,
782
- cantBuyWithCredits: false,
783
- useVotingUnits: false,
788
+ flags: JB721TierConfigFlags({
789
+ allowOwnerMint: false,
790
+ useReserveBeneficiaryAsDefault: false,
791
+ transfersPausable: false,
792
+ useVotingUnits: false,
793
+ cantBeRemoved: false,
794
+ cantIncreaseDiscountPercent: false,
795
+ cantBuyWithCredits: false
796
+ }),
784
797
  splitPercent: 0,
785
798
  splits: new JBSplit[](0)
786
799
  });
@@ -847,13 +860,15 @@ contract TestAuditGaps_GasLimits is UnitTestSetup {
847
860
  // forge-lint: disable-next-line(unsafe-typecast)
848
861
  category: uint24(i + 1),
849
862
  discountPercent: 0,
850
- allowOwnerMint: false,
851
- useReserveBeneficiaryAsDefault: false,
852
- transfersPausable: false,
853
- cantBeRemoved: false,
854
- cantIncreaseDiscountPercent: false,
855
- cantBuyWithCredits: false,
856
- useVotingUnits: false,
863
+ flags: JB721TierConfigFlags({
864
+ allowOwnerMint: false,
865
+ useReserveBeneficiaryAsDefault: false,
866
+ transfersPausable: false,
867
+ useVotingUnits: false,
868
+ cantBeRemoved: false,
869
+ cantIncreaseDiscountPercent: false,
870
+ cantBuyWithCredits: false
871
+ }),
857
872
  splitPercent: 0,
858
873
  splits: new JBSplit[](0)
859
874
  });
@@ -1043,13 +1058,15 @@ contract TestAuditGaps_GasLimits is UnitTestSetup {
1043
1058
  // forge-lint: disable-next-line(unsafe-typecast)
1044
1059
  category: uint24(i + 1),
1045
1060
  discountPercent: 0,
1046
- allowOwnerMint: false,
1047
- useReserveBeneficiaryAsDefault: false,
1048
- transfersPausable: false,
1049
- cantBeRemoved: false,
1050
- cantIncreaseDiscountPercent: false,
1051
- cantBuyWithCredits: false,
1052
- useVotingUnits: false,
1061
+ flags: JB721TierConfigFlags({
1062
+ allowOwnerMint: false,
1063
+ useReserveBeneficiaryAsDefault: false,
1064
+ transfersPausable: false,
1065
+ useVotingUnits: false,
1066
+ cantBeRemoved: false,
1067
+ cantIncreaseDiscountPercent: false,
1068
+ cantBuyWithCredits: false
1069
+ }),
1053
1070
  splitPercent: 0,
1054
1071
  splits: new JBSplit[](0)
1055
1072
  });
@@ -84,7 +84,7 @@ contract TestSafeTransferReentrancy is UnitTestSetup {
84
84
  /// The re-entry fails because the receiver is not registered as a terminal.
85
85
  function test_safeTransferFrom_maliciousReceiver_cannotReenterAfterPay() public {
86
86
  // Set up hook with tiers and mint an NFT.
87
- defaultTierConfig.allowOwnerMint = true;
87
+ defaultTierConfig.flags.allowOwnerMint = true;
88
88
  defaultTierConfig.reserveFrequency = 0;
89
89
  ForTest_JB721TiersHook testHook = _initializeForTestHook(10);
90
90
 
@@ -161,7 +161,7 @@ contract TestSafeTransferReentrancy is UnitTestSetup {
161
161
  /// The call is blocked by permission checks.
162
162
  function test_safeTransferFrom_maliciousReceiver_cannotReenterAdjustTiers() public {
163
163
  // Set up hook with tiers and mint an NFT.
164
- defaultTierConfig.allowOwnerMint = true;
164
+ defaultTierConfig.flags.allowOwnerMint = true;
165
165
  defaultTierConfig.reserveFrequency = 0;
166
166
  ForTest_JB721TiersHook testHook = _initializeForTestHook(10);
167
167
 
@@ -205,7 +205,7 @@ contract TestSafeTransferReentrancy is UnitTestSetup {
205
205
  /// and a re-transfer should succeed with correct state.
206
206
  function test_safeTransferFrom_maliciousReceiver_retransferDuringCallback() public {
207
207
  // Set up hook with tiers and mint an NFT.
208
- defaultTierConfig.allowOwnerMint = true;
208
+ defaultTierConfig.flags.allowOwnerMint = true;
209
209
  defaultTierConfig.reserveFrequency = 0;
210
210
  ForTest_JB721TiersHook testHook = _initializeForTestHook(10);
211
211
 
@@ -258,7 +258,7 @@ contract TestSafeTransferReentrancy is UnitTestSetup {
258
258
  /// tier balances, voting units, and firstOwner tracking consistent.
259
259
  function test_safeTransferFrom_normalReceiver_stateConsistent() public {
260
260
  // Set up hook with tiers and mint an NFT.
261
- defaultTierConfig.allowOwnerMint = true;
261
+ defaultTierConfig.flags.allowOwnerMint = true;
262
262
  defaultTierConfig.reserveFrequency = 0;
263
263
  ForTest_JB721TiersHook testHook = _initializeForTestHook(10);
264
264
 
@@ -20,9 +20,9 @@ contract TestVotingUnitsLifecycle is UnitTestSetup {
20
20
  /// through the full lifecycle: mint to user A, transfer to user B, burn by user B.
21
21
  function test_votingUnits_mintTransferBurn_lifecycle() public {
22
22
  // Configure a tier with custom voting units.
23
- defaultTierConfig.allowOwnerMint = true;
23
+ defaultTierConfig.flags.allowOwnerMint = true;
24
24
  defaultTierConfig.reserveFrequency = 0;
25
- defaultTierConfig.useVotingUnits = true;
25
+ defaultTierConfig.flags.useVotingUnits = true;
26
26
  defaultTierConfig.votingUnits = 100;
27
27
 
28
28
  ForTest_JB721TiersHook testHook = _initializeForTestHook(1);
@@ -75,9 +75,9 @@ contract TestVotingUnitsLifecycle is UnitTestSetup {
75
75
  /// and update properly when NFTs are transferred between users.
76
76
  function test_votingUnits_multiTier_aggregation() public {
77
77
  // Configure tiers with different custom voting units.
78
- defaultTierConfig.allowOwnerMint = true;
78
+ defaultTierConfig.flags.allowOwnerMint = true;
79
79
  defaultTierConfig.reserveFrequency = 0;
80
- defaultTierConfig.useVotingUnits = true;
80
+ defaultTierConfig.flags.useVotingUnits = true;
81
81
 
82
82
  ForTest_JB721TiersHook testHook = _initializeForTestHook(3);
83
83
  IJB721TiersHookStore hookStore = testHook.STORE();
@@ -149,9 +149,9 @@ contract TestVotingUnitsLifecycle is UnitTestSetup {
149
149
  /// @notice Verifies that when useVotingUnits is false, the tier price is used as voting power.
150
150
  function test_votingUnits_priceBasedVoting_lifecycle() public {
151
151
  // Configure tiers WITHOUT custom voting units (price-based voting).
152
- defaultTierConfig.allowOwnerMint = true;
152
+ defaultTierConfig.flags.allowOwnerMint = true;
153
153
  defaultTierConfig.reserveFrequency = 0;
154
- defaultTierConfig.useVotingUnits = false;
154
+ defaultTierConfig.flags.useVotingUnits = false;
155
155
  defaultTierConfig.votingUnits = 0;
156
156
 
157
157
  ForTest_JB721TiersHook testHook = _initializeForTestHook(3);
@@ -200,9 +200,9 @@ contract TestVotingUnitsLifecycle is UnitTestSetup {
200
200
  /// @notice Verifies that voting units scale correctly when a user owns multiple NFTs from one tier.
201
201
  function test_votingUnits_multipleMintsSameTier() public {
202
202
  // Configure tier with custom voting units.
203
- defaultTierConfig.allowOwnerMint = true;
203
+ defaultTierConfig.flags.allowOwnerMint = true;
204
204
  defaultTierConfig.reserveFrequency = 0;
205
- defaultTierConfig.useVotingUnits = true;
205
+ defaultTierConfig.flags.useVotingUnits = true;
206
206
  defaultTierConfig.votingUnits = 50;
207
207
 
208
208
  ForTest_JB721TiersHook testHook = _initializeForTestHook(1);
@@ -242,7 +242,7 @@ contract TestVotingUnitsLifecycle is UnitTestSetup {
242
242
  // ---------------------------------------------------------------
243
243
  /// @notice Verifies that addresses with no NFTs always return 0 voting units.
244
244
  function test_votingUnits_zeroForNonHolders() public {
245
- defaultTierConfig.useVotingUnits = true;
245
+ defaultTierConfig.flags.useVotingUnits = true;
246
246
  defaultTierConfig.votingUnits = 100;
247
247
 
248
248
  ForTest_JB721TiersHook testHook = _initializeForTestHook(5);
@@ -260,9 +260,9 @@ contract TestVotingUnitsLifecycle is UnitTestSetup {
260
260
  /// @notice Verifies that voting units work correctly when some tiers use custom voting units
261
261
  /// and others use price-based voting. The tier with useVotingUnits=false should use price.
262
262
  function test_votingUnits_mixedTierConfigs() public {
263
- defaultTierConfig.allowOwnerMint = true;
263
+ defaultTierConfig.flags.allowOwnerMint = true;
264
264
  defaultTierConfig.reserveFrequency = 0;
265
- defaultTierConfig.useVotingUnits = true;
265
+ defaultTierConfig.flags.useVotingUnits = true;
266
266
  defaultTierConfig.votingUnits = 100;
267
267
 
268
268
  ForTest_JB721TiersHook testHook = _initializeForTestHook(3);
@@ -11,6 +11,7 @@ import {JBAfterPayRecordedContext} from "@bananapus/core-v6/src/structs/JBAfterP
11
11
  import {JBPayHookSpecification} from "@bananapus/core-v6/src/structs/JBPayHookSpecification.sol";
12
12
  import {JBSplit} from "@bananapus/core-v6/src/structs/JBSplit.sol";
13
13
  import {IJBSplitHook} from "@bananapus/core-v6/src/interfaces/IJBSplitHook.sol";
14
+ import {JB721TierConfigFlags} from "../../src/structs/JB721TierConfigFlags.sol";
14
15
 
15
16
  contract CodexPayCreditsBypassTierSplits is UnitTestSetup {
16
17
  address internal splitBeneficiary = makeAddr("splitBeneficiary");
@@ -118,13 +119,15 @@ contract CodexPayCreditsBypassTierSplits is UnitTestSetup {
118
119
  encodedIPFSUri: bytes32(uint256(0x1234)),
119
120
  category: 1,
120
121
  discountPercent: 0,
121
- allowOwnerMint: false,
122
- useReserveBeneficiaryAsDefault: false,
123
- transfersPausable: false,
124
- cantBeRemoved: false,
125
- cantIncreaseDiscountPercent: false,
126
- cantBuyWithCredits: false,
127
- useVotingUnits: false,
122
+ flags: JB721TierConfigFlags({
123
+ allowOwnerMint: false,
124
+ useReserveBeneficiaryAsDefault: false,
125
+ transfersPausable: false,
126
+ useVotingUnits: false,
127
+ cantBeRemoved: false,
128
+ cantIncreaseDiscountPercent: false,
129
+ cantBuyWithCredits: false
130
+ }),
128
131
  splitPercent: 1_000_000_000,
129
132
  splits: new JBSplit[](0)
130
133
  });
@@ -10,6 +10,7 @@ import {JBBeforePayRecordedContext} from "@bananapus/core-v6/src/structs/JBBefor
10
10
  import {JBPayHookSpecification} from "@bananapus/core-v6/src/structs/JBPayHookSpecification.sol";
11
11
  import {JBSplit} from "@bananapus/core-v6/src/structs/JBSplit.sol";
12
12
  import {IJBSplitHook} from "@bananapus/core-v6/src/interfaces/IJBSplitHook.sol";
13
+ import {JB721TierConfigFlags} from "../../src/structs/JB721TierConfigFlags.sol";
13
14
 
14
15
  /// @notice Regression test: split metadata is proportionally scaled when credits fund a split-bearing tier mint.
15
16
  /// @dev Previously (pre-fix), the per-tier split amounts were left at the uncapped value, trapping forwarded ETH.
@@ -121,13 +122,15 @@ contract CodexSplitCreditsMismatch is UnitTestSetup {
121
122
  encodedIPFSUri: bytes32(uint256(0x1234)),
122
123
  category: 1,
123
124
  discountPercent: 0,
124
- allowOwnerMint: false,
125
- useReserveBeneficiaryAsDefault: false,
126
- transfersPausable: false,
127
- cantBeRemoved: false,
128
- cantIncreaseDiscountPercent: false,
129
- cantBuyWithCredits: false,
130
- useVotingUnits: false,
125
+ flags: JB721TierConfigFlags({
126
+ allowOwnerMint: false,
127
+ useReserveBeneficiaryAsDefault: false,
128
+ transfersPausable: false,
129
+ useVotingUnits: false,
130
+ cantBeRemoved: false,
131
+ cantIncreaseDiscountPercent: false,
132
+ cantBuyWithCredits: false
133
+ }),
131
134
  splitPercent: 1_000_000_000,
132
135
  splits: new JBSplit[](0)
133
136
  });