@bananapus/721-hook-v6 0.0.16 → 0.0.18

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 (63) hide show
  1. package/ADMINISTRATION.md +1 -1
  2. package/ARCHITECTURE.md +2 -2
  3. package/AUDIT_INSTRUCTIONS.md +340 -0
  4. package/CHANGE_LOG.md +345 -0
  5. package/README.md +1 -1
  6. package/RISKS.md +77 -312
  7. package/SKILLS.md +5 -4
  8. package/USER_JOURNEYS.md +503 -0
  9. package/package.json +5 -5
  10. package/src/JB721TiersHook.sol +7 -3
  11. package/src/JB721TiersHookStore.sol +52 -35
  12. package/src/abstract/ERC721.sol +3 -0
  13. package/src/interfaces/IJB721TiersHook.sol +23 -3
  14. package/src/libraries/JB721TiersHookLib.sol +182 -118
  15. package/test/TestAuditGaps.sol +903 -0
  16. package/test/TestSafeTransferReentrancy.t.sol +305 -0
  17. package/test/TestVotingUnitsLifecycle.t.sol +313 -0
  18. package/test/regression/BrokenTerminalDoesNotDos.t.sol +301 -0
  19. package/test/regression/ProjectDeployerRulesets.t.sol +355 -0
  20. package/test/regression/SplitDistributionBugs.t.sol +751 -0
  21. package/test/unit/redeem_Unit.t.sol +4 -0
  22. package/test/unit/splitHookDistribution_Unit.t.sol +604 -0
  23. package/docs/book.css +0 -13
  24. package/docs/book.toml +0 -12
  25. package/docs/solidity.min.js +0 -74
  26. package/docs/src/README.md +0 -253
  27. package/docs/src/SUMMARY.md +0 -38
  28. package/docs/src/src/JB721TiersHook.sol/contract.JB721TiersHook.md +0 -645
  29. package/docs/src/src/JB721TiersHookDeployer.sol/contract.JB721TiersHookDeployer.md +0 -99
  30. package/docs/src/src/JB721TiersHookProjectDeployer.sol/contract.JB721TiersHookProjectDeployer.md +0 -288
  31. package/docs/src/src/JB721TiersHookStore.sol/contract.JB721TiersHookStore.md +0 -1096
  32. package/docs/src/src/README.md +0 -11
  33. package/docs/src/src/abstract/ERC721.sol/abstract.ERC721.md +0 -430
  34. package/docs/src/src/abstract/JB721Hook.sol/abstract.JB721Hook.md +0 -309
  35. package/docs/src/src/abstract/README.md +0 -5
  36. package/docs/src/src/interfaces/IJB721Hook.sol/interface.IJB721Hook.md +0 -29
  37. package/docs/src/src/interfaces/IJB721TiersHook.sol/interface.IJB721TiersHook.md +0 -203
  38. package/docs/src/src/interfaces/IJB721TiersHookDeployer.sol/interface.IJB721TiersHookDeployer.md +0 -25
  39. package/docs/src/src/interfaces/IJB721TiersHookProjectDeployer.sol/interface.IJB721TiersHookProjectDeployer.md +0 -64
  40. package/docs/src/src/interfaces/IJB721TiersHookStore.sol/interface.IJB721TiersHookStore.md +0 -265
  41. package/docs/src/src/interfaces/IJB721TokenUriResolver.sol/interface.IJB721TokenUriResolver.md +0 -12
  42. package/docs/src/src/interfaces/README.md +0 -9
  43. package/docs/src/src/libraries/JB721Constants.sol/library.JB721Constants.md +0 -14
  44. package/docs/src/src/libraries/JB721TiersRulesetMetadataResolver.sol/library.JB721TiersRulesetMetadataResolver.md +0 -68
  45. package/docs/src/src/libraries/JBBitmap.sol/library.JBBitmap.md +0 -82
  46. package/docs/src/src/libraries/JBIpfsDecoder.sol/library.JBIpfsDecoder.md +0 -61
  47. package/docs/src/src/libraries/README.md +0 -7
  48. package/docs/src/src/structs/JB721InitTiersConfig.sol/struct.JB721InitTiersConfig.md +0 -27
  49. package/docs/src/src/structs/JB721Tier.sol/struct.JB721Tier.md +0 -59
  50. package/docs/src/src/structs/JB721TierConfig.sol/struct.JB721TierConfig.md +0 -60
  51. package/docs/src/src/structs/JB721TiersHookFlags.sol/struct.JB721TiersHookFlags.md +0 -26
  52. package/docs/src/src/structs/JB721TiersMintReservesConfig.sol/struct.JB721TiersMintReservesConfig.md +0 -16
  53. package/docs/src/src/structs/JB721TiersRulesetMetadata.sol/struct.JB721TiersRulesetMetadata.md +0 -20
  54. package/docs/src/src/structs/JB721TiersSetDiscountPercentConfig.sol/struct.JB721TiersSetDiscountPercentConfig.md +0 -16
  55. package/docs/src/src/structs/JBBitmapWord.sol/struct.JBBitmapWord.md +0 -19
  56. package/docs/src/src/structs/JBDeploy721TiersHookConfig.sol/struct.JBDeploy721TiersHookConfig.md +0 -34
  57. package/docs/src/src/structs/JBLaunchProjectConfig.sol/struct.JBLaunchProjectConfig.md +0 -23
  58. package/docs/src/src/structs/JBLaunchRulesetsConfig.sol/struct.JBLaunchRulesetsConfig.md +0 -22
  59. package/docs/src/src/structs/JBPayDataHookRulesetConfig.sol/struct.JBPayDataHookRulesetConfig.md +0 -51
  60. package/docs/src/src/structs/JBPayDataHookRulesetMetadata.sol/struct.JBPayDataHookRulesetMetadata.md +0 -66
  61. package/docs/src/src/structs/JBQueueRulesetsConfig.sol/struct.JBQueueRulesetsConfig.md +0 -21
  62. package/docs/src/src/structs/JBStored721Tier.sol/struct.JBStored721Tier.md +0 -42
  63. package/docs/src/src/structs/README.md +0 -18
package/CHANGE_LOG.md ADDED
@@ -0,0 +1,345 @@
1
+ # nana-721-hook-v6 Changelog (v5 → v6)
2
+
3
+ This document describes all changes between `nana-721-hook` (v5) and `nana-721-hook-v6` (v6).
4
+
5
+ ---
6
+
7
+ ## 1. Breaking Changes
8
+
9
+ ### 1.1 `IJB721TiersHook` — Changed Function Signatures
10
+
11
+ | Function | v5 Signature | v6 Signature | Notes |
12
+ |----------|-------------|-------------|-------|
13
+ | `pricingContext()` | `returns (uint256, uint256, IJBPrices)` | `returns (uint256 currency, uint256 decimals)` | `IJBPrices` removed from return; now a separate `PRICES()` getter |
14
+ | `setMetadata(...)` | `(string baseUri, string contractMetadataUri, IJB721TokenUriResolver, uint256, bytes32)` | `(string name, string symbol, string baseUri, string contractUri, IJB721TokenUriResolver, uint256, bytes32)` | Added `name` and `symbol` parameters; `contractMetadataUri` renamed to `contractUri` |
15
+
16
+ ### 1.2 `JB721Hook` (abstract) — Changed Function Signatures
17
+
18
+ | Function | v5 Signature | v6 Signature | Notes |
19
+ |----------|-------------|-------------|-------|
20
+ | `cashOutWeightOf(...)` | `(uint256[] memory, JBBeforeCashOutRecordedContext calldata) returns (uint256)` | `(uint256[] memory) returns (uint256)` | Removed `JBBeforeCashOutRecordedContext` parameter |
21
+ | `totalCashOutWeight(...)` | `(JBBeforeCashOutRecordedContext calldata) returns (uint256)` | `() returns (uint256)` | Removed `JBBeforeCashOutRecordedContext` parameter |
22
+ | `afterPayRecordedWith(...)` | Reverts if `msg.value != 0` | No `msg.value` check | v6 removes the `msg.value != 0` revert condition from pay hook validation |
23
+
24
+ ### 1.3 `JB721TiersHook` — Changed Constructor
25
+
26
+ | Parameter | v5 | v6 | Notes |
27
+ |-----------|----|----|-------|
28
+ | `prices` | Not a parameter (packed in `_packedPricingContext`) | `IJBPrices prices` | Now an immutable constructor parameter |
29
+ | `splits` | Not present | `IJBSplits splits` | New immutable for tier split distribution |
30
+
31
+ ### 1.4 `JB721InitTiersConfig` — Removed Field
32
+
33
+ | Field | v5 | v6 | Notes |
34
+ |-------|----|----|-------|
35
+ | `prices` | `IJBPrices prices` | Removed | Prices contract moved to `JB721TiersHook` constructor as an immutable |
36
+
37
+ ### 1.5 `JB721Constants` — Renamed Constant
38
+
39
+ | v5 | v6 | Notes |
40
+ |----|----|-------|
41
+ | `MAX_DISCOUNT_PERCENT` | `DISCOUNT_DENOMINATOR` | Same value (`200`), renamed for clarity |
42
+
43
+ ### 1.6 `JBStored721Tier` — Replaced Field
44
+
45
+ | Field | v5 | v6 | Notes |
46
+ |-------|----|----|-------|
47
+ | `votingUnits` (`uint32`) | Present | Removed | Replaced by `splitPercent` |
48
+ | `splitPercent` (`uint32`) | Not present | Added | Percentage of tier price routed to splits |
49
+
50
+ ### 1.7 Error Signature Changes (Store)
51
+
52
+ Several store errors gained a `tierId` parameter for better debugging:
53
+
54
+ | Error | v5 | v6 |
55
+ |-------|----|----|
56
+ | `CantMintManually` | `()` | `(uint256 tierId)` |
57
+ | `CantRemoveTier` | `()` | `(uint256 tierId)` |
58
+ | `InsufficientSupplyRemaining` | `()` | `(uint256 tierId)` |
59
+ | `ManualMintingNotAllowed` | `()` | `(uint256 tierId)` |
60
+ | `ReserveFrequencyNotAllowed` | `()` | `(uint256 tierId)` |
61
+ | `UnrecognizedTier` | `()` | `(uint256 tierId)` |
62
+ | `VotingUnitsNotAllowed` | `()` | `(uint256 tierId)` |
63
+ | `ZeroInitialSupply` | `()` | `(uint256 tierId)` |
64
+
65
+ ### 1.8 Solidity Version Change
66
+
67
+ All contracts upgraded from `pragma solidity 0.8.23` to `pragma solidity 0.8.26`.
68
+
69
+ ---
70
+
71
+ ## 2. New Features
72
+
73
+ ### 2.1 Tier Splits System
74
+
75
+ The largest new feature in v6. Each tier can now define a `splitPercent` and a set of `JBSplit[]` recipients. When an NFT is minted from a tier with splits configured, a portion of the payment is routed to the tier's split group recipients instead of entering the project's balance.
76
+
77
+ **New immutables on `JB721TiersHook`:**
78
+
79
+ | Name | Type | Description |
80
+ |------|------|-------------|
81
+ | `PRICES()` | `IJBPrices` | Price feed contract (was previously packed in `_packedPricingContext`) |
82
+ | `SPLITS()` | `IJBSplits` | Splits contract for reading/writing tier split groups |
83
+
84
+ **New library: `JB721TiersHookLib`** (entirely new in v6)
85
+
86
+ Contains functions extracted from `JB721TiersHook` to stay within the EIP-170 contract size limit:
87
+
88
+ | Function | Description |
89
+ |----------|-------------|
90
+ | `adjustTiersFor(...)` | Handles tier removal, addition, event emission, and split group setting |
91
+ | `recordAddTiersFor(...)` | Records new tiers and sets their split groups (used during initialization) |
92
+ | `normalizePaymentValue(...)` | Normalizes payment value based on pricing context |
93
+ | `calculateSplitAmounts(...)` | Calculates per-tier split amounts for a pay event |
94
+ | `convertSplitAmounts(...)` | Converts split amounts between currencies |
95
+ | `calculateWeight(...)` | Adjusts minting weight to account for split amounts |
96
+ | `distributeAll(...)` | Pulls tokens and distributes forwarded funds to tier split recipients |
97
+ | `resolveTokenURI(...)` | Resolves the token URI (moved IPFS decoding out of hook) |
98
+
99
+ **New flag on `JB721TiersHookFlags`:**
100
+
101
+ | Field | Type | Description |
102
+ |-------|------|-------------|
103
+ | `issueTokensForSplits` | `bool` | When `true`, payers receive full token credit even for the portion routed to splits. When `false` (default), weight is reduced proportionally. |
104
+
105
+ ### 2.2 Mutable Collection Name and Symbol
106
+
107
+ The ERC721 collection `name` and `symbol` can now be changed after initialization via `setMetadata(...)`.
108
+
109
+ **New internal functions on `ERC721` (abstract):**
110
+
111
+ | Function | Description |
112
+ |----------|-------------|
113
+ | `_setName(string memory)` | Updates the token collection name |
114
+ | `_setSymbol(string memory)` | Updates the token collection symbol |
115
+
116
+ ### 2.3 `split.hook` Support in Tier Distribution
117
+
118
+ Tier split payouts now support the `split.hook` field (`IJBSplitHook`). The distribution priority follows the same order as `JBMultiTerminal`: hook > projectId > beneficiary. In v5, tier splits did not exist. In v6, this ensures full parity with core split behavior.
119
+
120
+ ### 2.4 DOS Protection on Split Distribution
121
+
122
+ All external calls during tier split distribution (split hooks, terminal `pay`/`addToBalanceOf`, and leftover distribution) are wrapped in try-catch. A reverting split recipient or terminal cannot brick payments to the project. On failure, funds are returned to the project balance and a `SplitPayoutReverted` or `AddToBalanceReverted` event is emitted with the revert reason. For ERC-20 split hook failures, tokens are transferred first and the hook callback is best-effort; for ERC-20 terminal failures, the approval is reset to zero.
123
+
124
+ ### 2.5 `splitPercent` Bounds Validation
125
+
126
+ `JB721TiersHookStore.recordAddTiers` now validates that each tier's `splitPercent` does not exceed `JBConstants.SPLITS_TOTAL_PERCENT`. If it does, it reverts with `JB721TiersHookStore_SplitPercentExceedsBounds(uint256 percent, uint256 limit)`.
127
+
128
+ ### 2.6 `beforePayRecordedWith` Override in `JB721TiersHook`
129
+
130
+ v6 overrides `beforePayRecordedWith` in `JB721TiersHook` (not just the base `JB721Hook`). The override calculates per-tier split amounts and adjusts the minting weight so the terminal only mints project tokens for the portion of the payment that actually enters the project. It also sets the `JBPayHookSpecification.amount` to the total split amount so the terminal forwards those funds to the hook for distribution.
131
+
132
+ ### 2.7 Pricing Decimals Validation
133
+
134
+ `initialize(...)` now reverts with `JB721TiersHook_InvalidPricingDecimals` if `tiersConfig.decimals > 18`.
135
+
136
+ ### 2.8 `allowSetCustomToken` Pass-Through
137
+
138
+ The `JBPayDataHookRulesetMetadata` struct gained an `allowSetCustomToken` field. In v5, the project deployer hardcoded this to `false`. In v6, it passes through the value from the config.
139
+
140
+ ---
141
+
142
+ ## 3. Event Changes
143
+
144
+ ### 3.1 New Events on `IJB721TiersHook`
145
+
146
+ | Event | Signature | Description |
147
+ |-------|-----------|-------------|
148
+ | `AddToBalanceReverted` | `AddToBalanceReverted(uint256 indexed projectId, address token, uint256 amount, bytes reason)` | Emitted when leftover distribution's `addToBalanceOf` call fails. Funds remain in the hook contract. |
149
+ | `SetName` | `SetName(string indexed name, address caller)` | Emitted when the collection name is changed |
150
+ | `SetSymbol` | `SetSymbol(string indexed symbol, address caller)` | Emitted when the collection symbol is changed |
151
+ | `SplitPayoutReverted` | `SplitPayoutReverted(uint256 indexed projectId, JBSplit split, uint256 amount, bytes reason, address caller)` | Emitted when a split payout reverts during distribution. The failed split's funds route to the project balance. |
152
+
153
+ ### 3.2 New Event on `IJB721TiersHookStore`
154
+
155
+ | Event | Signature | Description |
156
+ |-------|-----------|-------------|
157
+ | `SetDefaultReserveBeneficiary` | `SetDefaultReserveBeneficiary(address indexed hook, address indexed newBeneficiary, address caller)` | Emitted when the global default reserve beneficiary is changed via `useReserveBeneficiaryAsDefault` |
158
+
159
+ ### 3.3 Events Unchanged
160
+
161
+ All other events (`AddPayCredits`, `AddTier`, `Mint`, `MintReservedNft`, `RemoveTier`, `SetBaseUri`, `SetContractUri`, `SetDiscountPercent`, `SetEncodedIPFSUri`, `SetTokenUriResolver`, `UsePayCredits`, `CleanTiers`, `HookDeployed`) remain unchanged.
162
+
163
+ ---
164
+
165
+ ## 4. Error Changes
166
+
167
+ ### 4.1 New Errors on `JB721TiersHook`
168
+
169
+ | Error | Signature | Description |
170
+ |-------|-----------|-------------|
171
+ | `JB721TiersHook_CurrencyMismatch` | `(uint256 paymentCurrency, uint256 tierCurrency)` | Reserved for currency mismatch detection |
172
+ | `JB721TiersHook_InvalidPricingDecimals` | `(uint256 decimals)` | Reverts when `tiersConfig.decimals > 18` during initialization |
173
+
174
+ ### 4.2 New Error on `JB721TiersHookStore`
175
+
176
+ | Error | Signature | Description |
177
+ |-------|-----------|-------------|
178
+ | `JB721TiersHookStore_SplitPercentExceedsBounds` | `(uint256 percent, uint256 limit)` | Reverts when a tier's `splitPercent` exceeds `JBConstants.SPLITS_TOTAL_PERCENT` during `recordAddTiers` |
179
+
180
+ ### 4.3 Store Errors with Added `tierId` Parameter
181
+
182
+ See Section 1.7 above. Eight store errors gained a `tierId` parameter for improved debuggability.
183
+
184
+ ### 4.4 Errors Unchanged
185
+
186
+ All other errors (`JB721Hook_InvalidPay`, `JB721Hook_InvalidCashOut`, `JB721Hook_UnauthorizedToken`, `JB721Hook_UnexpectedTokenCashedOut`, `JB721TiersHook_AlreadyInitialized`, `JB721TiersHook_NoProjectId`, `JB721TiersHook_Overspending`, `JB721TiersHook_MintReserveNftsPaused`, `JB721TiersHook_TierTransfersPaused`) remain unchanged.
187
+
188
+ ---
189
+
190
+ ## 5. Struct Changes
191
+
192
+ ### 5.1 `JB721Tier` — Added Field
193
+
194
+ | Field | Type | Description |
195
+ |-------|------|-------------|
196
+ | `splitPercent` | `uint32` | Percentage of the tier's price routed to the tier's split group (out of `JBConstants.SPLITS_TOTAL_PERCENT`). Inserted between `cannotIncreaseDiscountPercent` and `resolvedUri`. |
197
+
198
+ ### 5.2 `JB721TierConfig` — Added Fields
199
+
200
+ | Field | Type | Description |
201
+ |-------|------|-------------|
202
+ | `splitPercent` | `uint32` | Percentage of payment routed to splits when minting from this tier |
203
+ | `splits` | `JBSplit[]` | The split recipients for this tier's split group |
204
+
205
+ ### 5.3 `JBStored721Tier` — Replaced Field
206
+
207
+ The `votingUnits` (`uint32`) field was replaced by `splitPercent` (`uint32`) in the storage layout. Voting units are still tracked but no longer stored in the packed tier struct (they use the existing `_tierVotingUnitsOf` mapping).
208
+
209
+ ### 5.4 `JB721InitTiersConfig` — Removed Field
210
+
211
+ | Field | Type | Description |
212
+ |-------|------|-------------|
213
+ | `prices` | `IJBPrices` | Removed. The prices contract is now an immutable on the hook itself. |
214
+
215
+ ### 5.5 `JB721TiersHookFlags` — Added Field
216
+
217
+ | Field | Type | Description |
218
+ |-------|------|-------------|
219
+ | `issueTokensForSplits` | `bool` | Whether payers receive token credit for the split portion of their payment |
220
+
221
+ ### 5.6 `JBPayDataHookRulesetMetadata` — Added Field
222
+
223
+ | Field | Type | Description |
224
+ |-------|------|-------------|
225
+ | `allowSetCustomToken` | `bool` | Whether the project owner can set a custom ERC-20 token. Inserted between `allowOwnerMinting` and `allowTerminalMigration`. |
226
+
227
+ ### 5.7 Structs Unchanged
228
+
229
+ `JB721TiersMintReservesConfig`, `JB721TiersSetDiscountPercentConfig`, `JB721TiersRulesetMetadata`, `JBBitmapWord`, `JBDeploy721TiersHookConfig`, `JBLaunchProjectConfig`, `JBLaunchRulesetsConfig`, `JBQueueRulesetsConfig`, `JBPayDataHookRulesetConfig` remain structurally identical (import paths updated from core-v5 to core-v6).
230
+
231
+ ---
232
+
233
+ ## 6. Implementation Changes (Non-Interface)
234
+
235
+ ### 6.1 `JB721TiersHook._processPayment` — Split Distribution
236
+
237
+ After minting NFTs and updating pay credits, v6 checks `context.hookMetadata` and `context.forwardedAmount.value`. If both are non-zero, it calls `JB721TiersHookLib.distributeAll(...)` to distribute the forwarded funds to tier split recipients. This is the core of the tier splits flow.
238
+
239
+ ### 6.2 `JB721TiersHook.adjustTiers` — Delegated to Library
240
+
241
+ In v5, `adjustTiers` directly called `STORE.recordRemoveTierIds()` and `STORE.recordAddTiers()` with inline event emission. In v6, the entire operation is delegated to `JB721TiersHookLib.adjustTiersFor(...)`, which also handles setting split groups for newly added tiers in `JBSplits`.
242
+
243
+ ### 6.3 `JB721TiersHook.initialize` — Split Group Setup
244
+
245
+ During initialization, v6 uses `JB721TiersHookLib.recordAddTiersFor(...)` instead of calling `STORE.recordAddTiers()` directly. This also sets up split groups for tiers that have splits configured.
246
+
247
+ ### 6.4 `JB721TiersHook.tokenURI` — Delegated to Library
248
+
249
+ In v5, `tokenURI` resolved the URI inline (checking the resolver, then falling back to IPFS decoding). In v6, this is delegated to `JB721TiersHookLib.resolveTokenURI(...)` to reduce contract bytecode size.
250
+
251
+ ### 6.5 `JB721TiersHook._processPayment` — Payment Normalization Delegated
252
+
253
+ In v5, the payment value normalization (currency conversion via `IJBPrices`) was done inline. In v6, it is delegated to `JB721TiersHookLib.normalizePaymentValue(...)`.
254
+
255
+ ### 6.6 `JB721TiersHook._packedPricingContext` — Reduced Packing
256
+
257
+ In v5, `_packedPricingContext` packed three values: currency (bits 0-31), decimals (bits 32-39), and prices contract address (bits 40-199). In v6, it only packs currency and decimals (bits 0-39) since the prices contract is now a separate immutable.
258
+
259
+ ### 6.7 `JB721Hook.afterPayRecordedWith` — Removed `msg.value` Check
260
+
261
+ v5 reverted if `msg.value != 0` in `afterPayRecordedWith`. v6 removes this check because the hook now accepts forwarded native token payments for tier split distribution.
262
+
263
+ ### 6.8 `JB721TiersHookLib._sendPayoutToSplit` — `split.hook` Priority
264
+
265
+ Split payouts follow the same priority order as `JBMultiTerminal`: if `split.hook` is set, funds are sent to the hook via `processSplitWith`; otherwise if `split.projectId` is set, funds go to the project's primary terminal; otherwise funds go to `split.beneficiary`. For ERC-20 payments to hooks, tokens are transferred first and the hook callback is best-effort (failure does not revert).
266
+
267
+ ### 6.9 `JB721TiersHookLib` — Try-Catch on All External Calls
268
+
269
+ All external calls during split distribution (split hooks, terminal `pay`, terminal `addToBalanceOf`, and leftover `addToBalanceOf`) are wrapped in try-catch. On failure: native token calls return false (funds stay in the hook), ERC-20 terminal call approvals are reset to zero, and `SplitPayoutReverted` or `AddToBalanceReverted` events are emitted with the revert reason.
270
+
271
+ ### 6.10 `JB721TiersHookLib.calculateSplitAmounts` — Discount Applied Before Splits
272
+
273
+ Split amounts are calculated on the discount-adjusted (effective) tier price rather than the base price. This ensures the split amount matches the actual price the minter pays when a tier discount is active.
274
+
275
+ ### 6.11 `JB721TiersHookStore.recordAddTiers` — Validates `splitPercent`
276
+
277
+ `recordAddTiers` now reverts with `JB721TiersHookStore_SplitPercentExceedsBounds` if a tier's `splitPercent` exceeds `JBConstants.SPLITS_TOTAL_PERCENT`.
278
+
279
+ ### 6.12 `JB721TiersHookStore.recordAddTiers` — Stores `splitPercent`
280
+
281
+ The store now stores `splitPercent` in the `JBStored721Tier` packed struct (replacing the `votingUnits` field in storage). The `votingUnits` value continues to be stored in the `_tierVotingUnitsOf` mapping.
282
+
283
+ ### 6.13 `JB721TiersHookStore.recordAddTiers` — Emits `SetDefaultReserveBeneficiary`
284
+
285
+ When a tier config has `useReserveBeneficiaryAsDefault` set and the beneficiary differs from the current default, v6 emits the new `SetDefaultReserveBeneficiary` event.
286
+
287
+ ### 6.14 `JB721TiersHookProjectDeployer` — `allowSetCustomToken` Pass-Through
288
+
289
+ In v5, the project deployer hardcoded `allowSetCustomToken: false` when constructing `JBRulesetMetadata`. In v6, it passes through `payDataRulesetConfig.metadata.allowSetCustomToken`.
290
+
291
+ ---
292
+
293
+ ## 7. Migration Table
294
+
295
+ ### 7.1 Interfaces
296
+
297
+ | v5 | v6 | Notes |
298
+ |----|----|-------|
299
+ | `IJB721Hook` | `IJB721Hook` | Unchanged (import path updated) |
300
+ | `IJB721TiersHook` | `IJB721TiersHook` | `pricingContext()` return changed; `setMetadata()` signature changed; added `PRICES()`, `SPLITS()`, `SetName`, `SetSymbol`, `SplitPayoutReverted`, `AddToBalanceReverted` |
301
+ | `IJB721TiersHookDeployer` | `IJB721TiersHookDeployer` | Unchanged |
302
+ | `IJB721TiersHookProjectDeployer` | `IJB721TiersHookProjectDeployer` | Unchanged |
303
+ | `IJB721TiersHookStore` | `IJB721TiersHookStore` | Added `SetDefaultReserveBeneficiary` event; NatSpec added |
304
+ | `IJB721TokenUriResolver` | `IJB721TokenUriResolver` | Unchanged |
305
+
306
+ ### 7.2 Contracts
307
+
308
+ | v5 | v6 | Notes |
309
+ |----|----|-------|
310
+ | `JB721TiersHook` | `JB721TiersHook` | Constructor gains `prices` and `splits`; tier splits system; code extracted to library |
311
+ | `JB721TiersHookDeployer` | `JB721TiersHookDeployer` | Unchanged (import paths updated) |
312
+ | `JB721TiersHookProjectDeployer` | `JB721TiersHookProjectDeployer` | `allowSetCustomToken` pass-through |
313
+ | `JB721TiersHookStore` | `JB721TiersHookStore` | `splitPercent` storage and validation; error params added; `SplitPercentExceedsBounds` error; `SetDefaultReserveBeneficiary` event |
314
+ | `JB721Hook` (abstract) | `JB721Hook` (abstract) | `cashOutWeightOf`/`totalCashOutWeight` signatures simplified; `msg.value` check removed from `afterPayRecordedWith` |
315
+ | `ERC721` (abstract) | `ERC721` (abstract) | Added `_setName()` and `_setSymbol()` |
316
+
317
+ ### 7.3 Libraries
318
+
319
+ | v5 | v6 | Notes |
320
+ |----|----|-------|
321
+ | `JB721Constants` | `JB721Constants` | `MAX_DISCOUNT_PERCENT` renamed to `DISCOUNT_DENOMINATOR` |
322
+ | `JB721TiersRulesetMetadataResolver` | `JB721TiersRulesetMetadataResolver` | Unchanged |
323
+ | `JBBitmap` | `JBBitmap` | Unchanged |
324
+ | `JBIpfsDecoder` | `JBIpfsDecoder` | Unchanged |
325
+ | _(not present)_ | `JB721TiersHookLib` | New library for tier splits, payment normalization, weight calculation, and token URI resolution |
326
+
327
+ ### 7.4 Structs
328
+
329
+ | v5 | v6 | Notes |
330
+ |----|----|-------|
331
+ | `JB721Tier` | `JB721Tier` | Added `splitPercent` field |
332
+ | `JB721TierConfig` | `JB721TierConfig` | Added `splitPercent` and `splits` fields |
333
+ | `JB721TiersHookFlags` | `JB721TiersHookFlags` | Added `issueTokensForSplits` field |
334
+ | `JB721InitTiersConfig` | `JB721InitTiersConfig` | Removed `prices` field |
335
+ | `JBStored721Tier` | `JBStored721Tier` | `votingUnits` replaced by `splitPercent` |
336
+ | `JBPayDataHookRulesetMetadata` | `JBPayDataHookRulesetMetadata` | Added `allowSetCustomToken` field |
337
+ | `JB721TiersMintReservesConfig` | `JB721TiersMintReservesConfig` | Unchanged |
338
+ | `JB721TiersSetDiscountPercentConfig` | `JB721TiersSetDiscountPercentConfig` | Unchanged |
339
+ | `JB721TiersRulesetMetadata` | `JB721TiersRulesetMetadata` | Unchanged |
340
+ | `JBBitmapWord` | `JBBitmapWord` | Unchanged |
341
+ | `JBDeploy721TiersHookConfig` | `JBDeploy721TiersHookConfig` | Unchanged |
342
+ | `JBLaunchProjectConfig` | `JBLaunchProjectConfig` | Unchanged |
343
+ | `JBLaunchRulesetsConfig` | `JBLaunchRulesetsConfig` | Unchanged |
344
+ | `JBQueueRulesetsConfig` | `JBQueueRulesetsConfig` | Unchanged |
345
+ | `JBPayDataHookRulesetConfig` | `JBPayDataHookRulesetConfig` | Unchanged |
package/README.md CHANGED
@@ -235,7 +235,7 @@ Each tier has the following properties:
235
235
  - Voting units (optional). By default, each NFT's voting power equals its tier price. If `useVotingUnits` is true, a custom `votingUnits` value is used instead.
236
236
  - A flag to specify whether the NFTs in the tier can always be transferred, or if transfers can be paused depending on the project's ruleset.
237
237
  - A flag to specify whether the contract's owner can mint NFTs from the tier on-demand.
238
- - A split percent and a set of splits (optional). Each tier can route a percentage of its mint price to configured split recipients (other projects, addresses, etc.) every time an NFT from the tier is purchased. The remaining funds stay in the project's balance. The `splitPercent` is out of `JBConstants.SPLITS_TOTAL_PERCENT` (1,000,000,000). When splits are active, the hook adjusts the returned weight so the terminal only mints tokens proportional to the amount that actually enters the project treasury (e.g., a 50% split on a 1 ETH payment results in half the normal token issuance). This weight adjustment can be disabled with the `issueTokensForSplits` flag, which gives payers full token credit regardless of where the funds go.
238
+ - A split percent and a set of splits (optional). Each tier can route a percentage of its mint price to configured split recipients every time an NFT from the tier is purchased. The remaining funds stay in the project's balance. The `splitPercent` is out of `JBConstants.SPLITS_TOTAL_PERCENT` (1,000,000,000). Split recipients follow the same priority as JBMultiTerminal: `split.hook` (receives funds via `IJBSplitHook.processSplitWith` with full context including token, amount, decimals, project ID, and group ID) > `split.projectId` (routed via the project's primary terminal) > `split.beneficiary` (direct transfer). When splits are active, the hook adjusts the returned weight so the terminal only mints tokens proportional to the amount that actually enters the project treasury (e.g., a 50% split on a 1 ETH payment results in half the normal token issuance). This weight adjustment can be disabled with the `issueTokensForSplits` flag, which gives payers full token credit regardless of where the funds go.
239
239
  - A set of flags which restrict tiers added in the future (the votes/reserved frequency/on-demand minting/overspending/issueTokensForSplits flags noted above).
240
240
 
241
241
  Additional notes: