@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
@@ -203,19 +203,19 @@ interface IJB721TiersHookStore {
203
203
 
204
204
  /// @notice Record newly added tiers.
205
205
  /// @param tiersToAdd The tiers to add.
206
- /// @return tierIds The IDs of the tiers being added.
206
+ /// @return tierIds The IDs of the tiers added.
207
207
  function recordAddTiers(JB721TierConfig[] calldata tiersToAdd) external returns (uint256[] memory tierIds);
208
208
 
209
- /// @notice Record 721 burns.
209
+ /// @notice Records the burning of tiered 721 tokens, updating supply tracking for the affected tiers.
210
210
  /// @param tokenIds The token IDs of the NFTs to burn.
211
211
  function recordBurn(uint256[] calldata tokenIds) external;
212
212
 
213
- /// @notice Record newly set flags.
213
+ /// @notice Stores updated ruleset metadata flags that control minting, transferring, and tier behavior.
214
214
  /// @param flags The flags to set.
215
215
  function recordFlags(JB721TiersHookFlags calldata flags) external;
216
216
 
217
217
  /// @notice Record 721 mints from the provided tiers.
218
- /// @param amount The amount being spent on NFTs.
218
+ /// @param amount The amount to spend on NFTs.
219
219
  /// @param tierIds The IDs of the tiers to mint from.
220
220
  /// @param isOwnerMint Whether this is a direct owner mint.
221
221
  /// @return tokenIds The token IDs of the NFTs which were minted.
@@ -235,13 +235,13 @@ interface IJB721TiersHookStore {
235
235
  /// @return tokenIds The token IDs of the reserve NFTs which were minted.
236
236
  function recordMintReservesFor(uint256 tierId, uint256 count) external returns (uint256[] memory tokenIds);
237
237
 
238
- /// @notice Record tiers being removed.
239
- /// @param tierIds The IDs of the tiers being removed.
238
+ /// @notice Record tiers to remove.
239
+ /// @param tierIds The IDs of the tiers to remove.
240
240
  function recordRemoveTierIds(uint256[] calldata tierIds) external;
241
241
 
242
242
  /// @notice Record the setting of a discount for a tier.
243
243
  /// @param tierId The ID of the tier to set the discount of.
244
- /// @param discountPercent The new discount percent being applied.
244
+ /// @param discountPercent The new discount percent to apply.
245
245
  function recordSetDiscountPercentOf(uint256 tierId, uint256 discountPercent) external;
246
246
 
247
247
  /// @notice Record a new encoded IPFS URI for a tier.
@@ -254,9 +254,9 @@ interface IJB721TiersHookStore {
254
254
  /// @param resolver The resolver to set.
255
255
  function recordSetTokenUriResolver(IJB721TokenUriResolver resolver) external;
256
256
 
257
- /// @notice Record an 721 transfer.
258
- /// @param tierId The ID of the tier that the 721 being transferred belongs to.
259
- /// @param from The address that the 721 is being transferred from.
260
- /// @param to The address that the 721 is being transferred to.
257
+ /// @notice Records a 721 token transfer, updating the first-owner tracking for the receiving address.
258
+ /// @param tierId The ID of the tier that the 721 to transfer belongs to.
259
+ /// @param from The address to transfer the 721 from.
260
+ /// @param to The address to transfer the 721 to.
261
261
  function recordTransferForTier(uint256 tierId, address from, address to) external;
262
262
  }
@@ -113,7 +113,7 @@ library JB721TiersHookLib {
113
113
  /// @param splits The splits contract to read tier split groups from.
114
114
  /// @param projectId The project ID of the hook.
115
115
  /// @param hookAddress The hook address (for computing split group IDs).
116
- /// @param token The token being distributed.
116
+ /// @param token The token to distribute.
117
117
  /// @param amount The total amount to distribute.
118
118
  /// @param decimals The token decimals.
119
119
  /// @param encodedSplitData The encoded per-tier breakdown from hookMetadata.
@@ -8,7 +8,7 @@ pragma solidity ^0.8.0;
8
8
  /// @custom:member noNewTiersWithOwnerMinting A boolean indicating whether attempts to add new tiers with
9
9
  /// `allowOwnerMint` set to true will revert.
10
10
  /// @custom:member preventOverspending A boolean indicating whether payments attempting to spend more than the price of
11
- /// the NFTs being minted will revert.
11
+ /// the NFTs to mint will revert.
12
12
  /// @custom:member issueTokensForSplits A boolean indicating whether payers receive token credit for the portion of
13
13
  /// their payment that is routed to tier splits. When false (default), weight is reduced proportionally.
14
14
  struct JB721TiersHookFlags {
@@ -23,7 +23,7 @@ pragma solidity ^0.8.0;
23
23
  /// between its tokens.
24
24
  /// @custom:member holdFees A flag indicating if fees should be held during this ruleset.
25
25
  /// @custom:member useTotalSurplusForCashOut A flag indicating if cash outs should use the project's balance held
26
- /// in all terminals instead of the project's local terminal balance from which the cash out is being fulfilled.
26
+ /// in all terminals instead of the project's local terminal balance from which the cash out is fulfilled.
27
27
  /// @custom:member useDataHookForCashOuts A flag indicating if the data hook should be used for cash out transactions
28
28
  /// during
29
29
  /// this ruleset.
@@ -0,0 +1,49 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity 0.8.28;
3
+
4
+ // forge-lint: disable-next-line(unaliased-plain-import)
5
+ import "@bananapus/core-v6/src/libraries/JBCurrencyIds.sol";
6
+ // forge-lint: disable-next-line(unaliased-plain-import)
7
+ import "@bananapus/core-v6/src/libraries/JBConstants.sol";
8
+
9
+ contract AccessJBLib {
10
+ // forge-lint: disable-next-line(mixed-case-function)
11
+ function NATIVE() external pure returns (uint256) {
12
+ return uint32(uint160(JBConstants.NATIVE_TOKEN));
13
+ }
14
+
15
+ // forge-lint: disable-next-line(mixed-case-function)
16
+ function USD() external pure returns (uint256) {
17
+ return JBCurrencyIds.USD;
18
+ }
19
+
20
+ // forge-lint: disable-next-line(mixed-case-function)
21
+ function NATIVE_TOKEN() external pure returns (address) {
22
+ return JBConstants.NATIVE_TOKEN;
23
+ }
24
+
25
+ // forge-lint: disable-next-line(mixed-case-function)
26
+ function MAX_FEE() external pure returns (uint256) {
27
+ return JBConstants.MAX_FEE;
28
+ }
29
+
30
+ // forge-lint: disable-next-line(mixed-case-function)
31
+ function MAX_RESERVED_PERCENT() external pure returns (uint256) {
32
+ return JBConstants.MAX_RESERVED_PERCENT;
33
+ }
34
+
35
+ // forge-lint: disable-next-line(mixed-case-function)
36
+ function MAX_CASH_OUT_TAX_RATE() external pure returns (uint256) {
37
+ return JBConstants.MAX_CASH_OUT_TAX_RATE;
38
+ }
39
+
40
+ // forge-lint: disable-next-line(mixed-case-function)
41
+ function MAX_WEIGHT_CUT_PERCENT() external pure returns (uint256) {
42
+ return JBConstants.MAX_WEIGHT_CUT_PERCENT;
43
+ }
44
+
45
+ // forge-lint: disable-next-line(mixed-case-function)
46
+ function SPLITS_TOTAL_PERCENT() external pure returns (uint256) {
47
+ return JBConstants.SPLITS_TOTAL_PERCENT;
48
+ }
49
+ }
@@ -0,0 +1,246 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity 0.8.28;
3
+
4
+ // forge-lint: disable-next-line(unaliased-plain-import)
5
+ import "../../src/interfaces/IJB721TiersHook.sol";
6
+ import {JB721TierFlags} from "../../src/structs/JB721TierFlags.sol";
7
+
8
+ // forge-lint: disable-next-line(unaliased-plain-import)
9
+ import "../../src/JB721TiersHook.sol";
10
+ // forge-lint: disable-next-line(unaliased-plain-import)
11
+ import "../../src/JB721TiersHookStore.sol";
12
+
13
+ import {JB721CheckpointsDeployer} from "../../src/JB721CheckpointsDeployer.sol";
14
+ import {IJB721CheckpointsDeployer} from "../../src/interfaces/IJB721CheckpointsDeployer.sol";
15
+
16
+ // forge-lint: disable-next-line(unaliased-plain-import)
17
+ import "../../src/structs/JBBitmapWord.sol";
18
+
19
+ // forge-lint: disable-next-line(unaliased-plain-import)
20
+ import "@bananapus/core-v6/src/structs/JBRulesetMetadata.sol";
21
+ // forge-lint: disable-next-line(unaliased-plain-import)
22
+ import "@bananapus/core-v6/src/interfaces/IJBPermissioned.sol";
23
+ import {MetadataResolverHelper} from "@bananapus/core-v6/test/helpers/MetadataResolverHelper.sol";
24
+
25
+ // forge-lint: disable-next-line(unaliased-plain-import)
26
+ import "@bananapus/core-v6/src/libraries/JBConstants.sol";
27
+
28
+ // forge-lint: disable-next-line(unaliased-plain-import)
29
+ import "./UnitTestSetup.sol"; // Only used to get the `PAY_HOOK_ID` and `CASH_OUT_HOOK_ID` constants.
30
+
31
+ interface IJB721TiersHookStore_ForTest is IJB721TiersHookStore {
32
+ // forge-lint: disable-next-line(mixed-case-function)
33
+ function ForTest_dumpTiersList(address nft) external view returns (JB721Tier[] memory tiers);
34
+ // forge-lint: disable-next-line(mixed-case-function)
35
+ function ForTest_setTier(address hook, uint256 index, JBStored721Tier calldata newTier) external;
36
+ // forge-lint: disable-next-line(mixed-case-function)
37
+ function ForTest_setTierVotingUnits(address hook, uint256 tierId, uint32 votingUnits) external;
38
+ // forge-lint: disable-next-line(mixed-case-function)
39
+ function ForTest_setBalanceOf(address hook, address holder, uint256 tier, uint256 balance) external;
40
+ // forge-lint: disable-next-line(mixed-case-function)
41
+ function ForTest_setReservesMintedFor(address hook, uint256 tier, uint256 amount) external;
42
+ // forge-lint: disable-next-line(mixed-case-function)
43
+ function ForTest_setIsTierRemoved(address hook, uint256 tokenId) external;
44
+ // forge-lint: disable-next-line(mixed-case-function)
45
+ function ForTest_packBools(
46
+ bool allowOwnerMint,
47
+ bool transfersPausable,
48
+ bool useVotingUnits,
49
+ bool cantBeRemoved,
50
+ bool cantIncreaseDiscountPercent,
51
+ bool cantBuyWithCredits
52
+ )
53
+ external
54
+ returns (uint8);
55
+ }
56
+
57
+ // A customized 721 tiers hook for testing purposes.
58
+ contract ForTest_JB721TiersHook is JB721TiersHook {
59
+ // forge-lint: disable-next-line(mixed-case-variable)
60
+ IJB721TiersHookStore_ForTest public test_store;
61
+ MetadataResolverHelper metadataHelper;
62
+
63
+ uint256 constant SURPLUS = 10e18;
64
+ uint256 constant CASH_OUT_TAX_RATE = JBConstants.MAX_CASH_OUT_TAX_RATE; // 40%
65
+ address _trustedForwarder = address(123_456);
66
+
67
+ /// @dev Bundles ForTest_JB721TiersHook constructor args to avoid stack-too-deep.
68
+ struct ForTestInitConfig {
69
+ uint256 projectId;
70
+ string name;
71
+ string symbol;
72
+ string baseUri;
73
+ IJB721TokenUriResolver tokenUriResolver;
74
+ string contractUri;
75
+ JB721TierConfig[] tiers;
76
+ JB721TiersHookFlags flags;
77
+ }
78
+
79
+ constructor(
80
+ ForTestInitConfig memory config,
81
+ IJBDirectory directory,
82
+ IJBPrices prices,
83
+ IJBRulesets rulesets,
84
+ IJB721TiersHookStore store,
85
+ IJBSplits splits
86
+ )
87
+ // The directory is also `IJBPermissioned`.
88
+ JB721TiersHook(
89
+ directory,
90
+ IJBPermissioned(address(directory)).PERMISSIONS(),
91
+ prices,
92
+ rulesets,
93
+ store,
94
+ splits,
95
+ IJB721CheckpointsDeployer(address(new JB721CheckpointsDeployer(store))),
96
+ _trustedForwarder
97
+ )
98
+ {
99
+ // Reset the _initialized flag set by the parent constructor so this test contract can initialize itself.
100
+ _initialized = false;
101
+ JB721TiersHook.initialize(
102
+ config.projectId,
103
+ config.name,
104
+ config.symbol,
105
+ config.baseUri,
106
+ config.tokenUriResolver,
107
+ config.contractUri,
108
+ JB721InitTiersConfig({
109
+ tiers: config.tiers, currency: uint32(uint160(JBConstants.NATIVE_TOKEN)), decimals: 18
110
+ }),
111
+ config.flags
112
+ );
113
+ test_store = IJB721TiersHookStore_ForTest(address(store));
114
+
115
+ metadataHelper = new MetadataResolverHelper();
116
+ }
117
+
118
+ // forge-lint: disable-next-line(mixed-case-function)
119
+ function ForTest_setOwnerOf(uint256 tokenId, address owner) public {
120
+ _owners[tokenId] = owner;
121
+ }
122
+
123
+ function burn(uint256[] memory tokenIds) public {
124
+ for (uint256 i; i < tokenIds.length; i++) {
125
+ _burn(tokenIds[i]);
126
+ }
127
+ STORE.recordBurn(tokenIds);
128
+ }
129
+ }
130
+
131
+ // A customized 721 tiers hook store for testing purposes.
132
+ contract ForTest_JB721TiersHookStore is JB721TiersHookStore, IJB721TiersHookStore_ForTest {
133
+ using JBBitmap for mapping(uint256 => uint256);
134
+ using JBBitmap for JBBitmapWord;
135
+
136
+ // forge-lint: disable-next-line(mixed-case-function)
137
+ function ForTest_dumpTiersList(address nft) public view override returns (JB721Tier[] memory tiers) {
138
+ // Keep a reference to the max tier ID.
139
+ uint256 maxTierId = maxTierIdOf[nft];
140
+ // Initialize an array with the appropriate length.
141
+ tiers = new JB721Tier[](maxTierId);
142
+ // Count the number of included tiers.
143
+ uint256 numberOfIncludedTiers;
144
+ // Get a reference to the sorted index being iterated on, starting with the first one.
145
+ uint256 currentSortIndex = _firstSortedTierIdOf(nft, 0);
146
+ // Keep a reference to the tier being iterated on.
147
+ JBStored721Tier memory storedTier;
148
+ // Make the sorted array.
149
+ while (currentSortIndex != 0 && numberOfIncludedTiers < maxTierId) {
150
+ storedTier = _storedTierOf[nft][currentSortIndex];
151
+
152
+ // Unpack stored tier.
153
+ (bool allowOwnerMint, bool transfersPausable,,,,) = _unpackBools(storedTier.packedBools);
154
+
155
+ // Add the tier to the array being returned.
156
+ tiers[numberOfIncludedTiers++] = JB721Tier({
157
+ // forge-lint: disable-next-line(unsafe-typecast)
158
+ id: uint32(currentSortIndex),
159
+ price: storedTier.price,
160
+ remainingSupply: storedTier.remainingSupply,
161
+ initialSupply: storedTier.initialSupply,
162
+ votingUnits: storedTier.price,
163
+ reserveFrequency: storedTier.reserveFrequency,
164
+ reserveBeneficiary: reserveBeneficiaryOf(nft, currentSortIndex),
165
+ encodedIPFSUri: encodedIPFSUriOf[nft][currentSortIndex],
166
+ category: storedTier.category,
167
+ discountPercent: storedTier.discountPercent,
168
+ flags: JB721TierFlags({
169
+ allowOwnerMint: allowOwnerMint,
170
+ transfersPausable: transfersPausable,
171
+ cantBeRemoved: false,
172
+ cantIncreaseDiscountPercent: false,
173
+ cantBuyWithCredits: false
174
+ }),
175
+ splitPercent: storedTier.splitPercent,
176
+ resolvedUri: ""
177
+ });
178
+ // Set the next sort index.
179
+ currentSortIndex = _nextSortedTierIdOf(nft, currentSortIndex, maxTierId);
180
+ }
181
+ // Drop the empty tiers at the end of the array.
182
+ // The array's size is based on `maxTierIdOf`, which *might* exceed the actual number of tiers.
183
+ for (uint256 i = tiers.length - 1; i >= 0; i--) {
184
+ if (tiers[i].id == 0) {
185
+ assembly ("memory-safe") {
186
+ mstore(tiers, sub(mload(tiers), 1))
187
+ }
188
+ } else {
189
+ break;
190
+ }
191
+ }
192
+ }
193
+
194
+ // forge-lint: disable-next-line(mixed-case-function)
195
+ function ForTest_setTier(address hook, uint256 index, JBStored721Tier calldata newTier) public override {
196
+ _storedTierOf[address(hook)][index] = newTier;
197
+ }
198
+
199
+ // forge-lint: disable-next-line(mixed-case-function)
200
+ function ForTest_setTierVotingUnits(address hook, uint256 tierId, uint32 votingUnits) public override {
201
+ _tierVotingUnitsOf[address(hook)][tierId] = votingUnits;
202
+ }
203
+
204
+ // forge-lint: disable-next-line(mixed-case-function)
205
+ function ForTest_setBalanceOf(address hook, address holder, uint256 tier, uint256 balance) public override {
206
+ tierBalanceOf[address(hook)][holder][tier] = balance;
207
+ }
208
+
209
+ // forge-lint: disable-next-line(mixed-case-function)
210
+ function ForTest_setReservesMintedFor(address hook, uint256 tier, uint256 amount) public override {
211
+ numberOfReservesMintedFor[address(hook)][tier] = amount;
212
+ }
213
+
214
+ // forge-lint: disable-next-line(mixed-case-function)
215
+ function ForTest_setIsTierRemoved(address hook, uint256 tokenId) public override {
216
+ _removedTiersBitmapWordOf[hook].removeTier(tokenId);
217
+ }
218
+
219
+ // forge-lint: disable-next-line(mixed-case-function)
220
+ function ForTest_packBools(
221
+ bool allowOwnerMint,
222
+ bool transfersPausable,
223
+ bool useVotingUnits,
224
+ bool cantBeRemoved,
225
+ bool cantIncreaseDiscountPercent,
226
+ bool cantBuyWithCredits
227
+ )
228
+ public
229
+ pure
230
+ returns (uint8)
231
+ {
232
+ return _packBools(
233
+ allowOwnerMint,
234
+ transfersPausable,
235
+ useVotingUnits,
236
+ cantBeRemoved,
237
+ cantIncreaseDiscountPercent,
238
+ cantBuyWithCredits
239
+ );
240
+ }
241
+
242
+ // forge-lint: disable-next-line(mixed-case-function)
243
+ function ForTest_unpackBools(uint8 packed) public pure returns (bool, bool, bool, bool, bool, bool) {
244
+ return _unpackBools(packed);
245
+ }
246
+ }
@@ -0,0 +1,213 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity 0.8.28;
3
+
4
+ // forge-lint: disable-next-line(unaliased-plain-import)
5
+ import "@bananapus/core-v6/src/JBController.sol";
6
+ // forge-lint: disable-next-line(unaliased-plain-import)
7
+ import "@bananapus/core-v6/src/JBDirectory.sol";
8
+ // forge-lint: disable-next-line(unaliased-plain-import)
9
+ import "@bananapus/core-v6/src/JBMultiTerminal.sol";
10
+ // forge-lint: disable-next-line(unaliased-plain-import)
11
+ import "@bananapus/core-v6/src/JBFundAccessLimits.sol";
12
+ // forge-lint: disable-next-line(unaliased-plain-import)
13
+ import "@bananapus/core-v6/src/JBFeelessAddresses.sol";
14
+ // forge-lint: disable-next-line(unaliased-plain-import)
15
+ import "@bananapus/core-v6/src/JBTerminalStore.sol";
16
+ // forge-lint: disable-next-line(unaliased-plain-import)
17
+ import "@bananapus/core-v6/src/JBRulesets.sol";
18
+ // forge-lint: disable-next-line(unaliased-plain-import)
19
+ import "@bananapus/core-v6/src/JBPermissions.sol";
20
+ // forge-lint: disable-next-line(unaliased-plain-import)
21
+ import "@bananapus/core-v6/src/JBPrices.sol";
22
+ import {JBProjects} from "@bananapus/core-v6/src/JBProjects.sol";
23
+ // forge-lint: disable-next-line(unaliased-plain-import)
24
+ import "@bananapus/core-v6/src/JBSplits.sol";
25
+ // forge-lint: disable-next-line(unaliased-plain-import)
26
+ import "@bananapus/core-v6/src/JBERC20.sol";
27
+ // forge-lint: disable-next-line(unaliased-plain-import)
28
+ import "@bananapus/core-v6/src/JBTokens.sol";
29
+
30
+ // forge-lint: disable-next-line(unaliased-plain-import)
31
+ import "@bananapus/core-v6/src/structs/JBAfterPayRecordedContext.sol";
32
+ // forge-lint: disable-next-line(unaliased-plain-import)
33
+ import "@bananapus/core-v6/src/structs/JBAfterCashOutRecordedContext.sol";
34
+ // forge-lint: disable-next-line(unaliased-plain-import)
35
+ import "@bananapus/core-v6/src/structs/JBFee.sol";
36
+ // forge-lint: disable-next-line(unaliased-plain-import)
37
+ import "@bananapus/core-v6/src/structs/JBFundAccessLimitGroup.sol";
38
+ // forge-lint: disable-next-line(unaliased-plain-import)
39
+ import "@bananapus/core-v6/src/structs/JBRuleset.sol";
40
+ // forge-lint: disable-next-line(unaliased-plain-import)
41
+ import "@bananapus/core-v6/src/structs/JBRulesetConfig.sol";
42
+ // forge-lint: disable-next-line(unaliased-plain-import)
43
+ import "@bananapus/core-v6/src/structs/JBRulesetMetadata.sol";
44
+ // forge-lint: disable-next-line(unaliased-plain-import)
45
+ import "@bananapus/core-v6/src/structs/JBPermissionsData.sol";
46
+ // forge-lint: disable-next-line(unaliased-plain-import)
47
+ import "@bananapus/core-v6/src/structs/JBBeforePayRecordedContext.sol";
48
+ // forge-lint: disable-next-line(unaliased-plain-import)
49
+ import "@bananapus/core-v6/src/structs/JBBeforeCashOutRecordedContext.sol";
50
+ // forge-lint: disable-next-line(unaliased-plain-import)
51
+ import "@bananapus/core-v6/src/structs/JBSplit.sol";
52
+
53
+ // forge-lint: disable-next-line(unaliased-plain-import)
54
+ import "@bananapus/core-v6/src/interfaces/IJBTerminal.sol";
55
+ // forge-lint: disable-next-line(unaliased-plain-import)
56
+ import "@bananapus/core-v6/src/interfaces/IJBToken.sol";
57
+
58
+ // forge-lint: disable-next-line(unaliased-plain-import)
59
+ import "@bananapus/permission-ids-v6/src/JBPermissionIds.sol";
60
+ // forge-lint: disable-next-line(unaliased-plain-import)
61
+ import "@bananapus/core-v6/src/libraries/JBRulesetMetadataResolver.sol";
62
+
63
+ // forge-lint: disable-next-line(unused-import)
64
+ import {mulDiv} from "@prb/math/src/Common.sol";
65
+
66
+ // forge-lint: disable-next-line(unaliased-plain-import)
67
+ import "forge-std/Test.sol";
68
+
69
+ // forge-lint: disable-next-line(unaliased-plain-import)
70
+ import "./AccessJBLib.sol";
71
+
72
+ // forge-lint: disable-next-line(unaliased-plain-import)
73
+ import "../../src/structs/JBPayDataHookRulesetConfig.sol";
74
+ // forge-lint: disable-next-line(unaliased-plain-import)
75
+ import "../../src/structs/JBPayDataHookRulesetMetadata.sol";
76
+
77
+ /// @notice Base contract for Juicebox system tests.
78
+ /// @dev Provides common functionality, such as deploying contracts on test setup.
79
+ contract TestBaseWorkflow is Test {
80
+ //*********************************************************************//
81
+ // --------------------- internal stored properties ------------------- //
82
+ //*********************************************************************//
83
+
84
+ address internal projectOwner = address(123);
85
+ address internal beneficiary = address(69_420);
86
+ address internal caller = address(696_969);
87
+
88
+ JBPermissions internal jbPermissions;
89
+ JBProjects internal jbProjects;
90
+ JBPrices internal jbPrices;
91
+ JBDirectory internal jbDirectory;
92
+ JBRulesets internal jbRulesets;
93
+ JBTokens internal jbTokens;
94
+ JBFundAccessLimits internal jbFundAccessLimits;
95
+ JBFeelessAddresses internal jbFeelessAddresses;
96
+ JBSplits internal jbSplits;
97
+ JBController internal jbController;
98
+ JBTerminalStore internal jbTerminalStore;
99
+ JBMultiTerminal internal jbMultiTerminal;
100
+ string internal projectUri;
101
+ IJBToken internal tokenV2;
102
+
103
+ // forge-lint: disable-next-line(mixed-case-variable)
104
+ AccessJBLib internal accessJBLib;
105
+
106
+ //*********************************************************************//
107
+ // --------------------------- test setup ---------------------------- //
108
+ //*********************************************************************//
109
+
110
+ // Deploys and initializes contracts for testing.
111
+ function setUp() public virtual {
112
+ // ---- Set up project ---- //
113
+ jbPermissions = new JBPermissions(address(0));
114
+ vm.label(address(jbPermissions), "JBPermissions");
115
+
116
+ jbProjects = new JBProjects(projectOwner, address(0), address(0));
117
+ vm.label(address(jbProjects), "JBProjects");
118
+
119
+ jbDirectory = new JBDirectory(jbPermissions, jbProjects, projectOwner);
120
+ vm.label(address(jbDirectory), "JBDirectory");
121
+
122
+ jbPrices = new JBPrices(jbDirectory, jbPermissions, jbProjects, projectOwner, address(0));
123
+ vm.label(address(jbPrices), "JBPrices");
124
+
125
+ jbRulesets = new JBRulesets(jbDirectory);
126
+ vm.label(address(jbRulesets), "JBRulesets");
127
+
128
+ jbFundAccessLimits = new JBFundAccessLimits(jbDirectory);
129
+ vm.label(address(jbFundAccessLimits), "JBFundAccessLimits");
130
+
131
+ jbFeelessAddresses = new JBFeelessAddresses(address(69));
132
+ vm.label(address(jbFeelessAddresses), "JBFeelessAddresses");
133
+
134
+ jbTokens = new JBTokens(jbDirectory, new JBERC20(jbPermissions, jbProjects));
135
+ vm.label(address(jbTokens), "JBTokens");
136
+
137
+ jbSplits = new JBSplits(jbDirectory);
138
+ vm.label(address(jbSplits), "JBSplits");
139
+
140
+ jbController = new JBController(
141
+ jbDirectory,
142
+ jbFundAccessLimits,
143
+ jbPermissions,
144
+ jbPrices,
145
+ jbProjects,
146
+ jbRulesets,
147
+ jbSplits,
148
+ jbTokens,
149
+ address(0),
150
+ address(0)
151
+ );
152
+ vm.label(address(jbController), "JBController");
153
+
154
+ vm.prank(projectOwner);
155
+ jbDirectory.setIsAllowedToSetFirstController(address(jbController), true);
156
+
157
+ jbTerminalStore = new JBTerminalStore(jbDirectory, jbPrices, jbRulesets);
158
+ vm.label(address(jbTerminalStore), "JBTerminalStore");
159
+
160
+ accessJBLib = new AccessJBLib();
161
+
162
+ jbMultiTerminal = new JBMultiTerminal(
163
+ jbFeelessAddresses,
164
+ jbPermissions,
165
+ jbProjects,
166
+ jbSplits,
167
+ jbTerminalStore,
168
+ jbTokens,
169
+ IPermit2(address(0)),
170
+ address(0)
171
+ );
172
+ vm.label(address(jbMultiTerminal), "JBMultiTerminal");
173
+
174
+ projectUri = "myIPFSHash";
175
+
176
+ // ---- general setup ---- //
177
+ vm.deal(beneficiary, 100 ether);
178
+ vm.deal(projectOwner, 100 ether);
179
+ vm.deal(caller, 100 ether);
180
+
181
+ vm.label(projectOwner, "projectOwner");
182
+ vm.label(beneficiary, "beneficiary");
183
+ vm.label(caller, "caller");
184
+ }
185
+
186
+ //https://ethereum.stackexchange.com/questions/24248/how-to-calculate-an-ethereum-contracts-address-during-its-creation-using-the-so
187
+ function addressFrom(address origin, uint256 nonce) internal pure returns (address addr) {
188
+ bytes memory data;
189
+ if (nonce == 0x00) {
190
+ data = abi.encodePacked(bytes1(0xd6), bytes1(0x94), origin, bytes1(0x80));
191
+ } else if (nonce <= 0x7f) {
192
+ // forge-lint: disable-next-line(unsafe-typecast)
193
+ data = abi.encodePacked(bytes1(0xd6), bytes1(0x94), origin, uint8(nonce));
194
+ } else if (nonce <= 0xff) {
195
+ // forge-lint: disable-next-line(unsafe-typecast)
196
+ data = abi.encodePacked(bytes1(0xd7), bytes1(0x94), origin, bytes1(0x81), uint8(nonce));
197
+ } else if (nonce <= 0xffff) {
198
+ // forge-lint: disable-next-line(unsafe-typecast)
199
+ data = abi.encodePacked(bytes1(0xd8), bytes1(0x94), origin, bytes1(0x82), uint16(nonce));
200
+ } else if (nonce <= 0xffffff) {
201
+ // forge-lint: disable-next-line(unsafe-typecast)
202
+ data = abi.encodePacked(bytes1(0xd9), bytes1(0x94), origin, bytes1(0x83), uint24(nonce));
203
+ } else {
204
+ // forge-lint: disable-next-line(unsafe-typecast)
205
+ data = abi.encodePacked(bytes1(0xda), bytes1(0x94), origin, bytes1(0x84), uint32(nonce));
206
+ }
207
+ bytes32 hash = keccak256(data);
208
+ assembly ("memory-safe") {
209
+ mstore(0, hash)
210
+ addr := mload(0)
211
+ }
212
+ }
213
+ }