@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.
- package/ADMINISTRATION.md +38 -11
- package/ARCHITECTURE.md +53 -99
- package/AUDIT_INSTRUCTIONS.md +84 -383
- package/CHANGELOG.md +71 -0
- package/README.md +79 -225
- package/RISKS.md +28 -11
- package/SKILLS.md +29 -296
- package/STYLE_GUIDE.md +57 -18
- package/USER_JOURNEYS.md +57 -501
- package/package.json +1 -1
- package/references/operations.md +28 -0
- package/references/runtime.md +32 -0
- package/script/Deploy.s.sol +5 -4
- package/src/JB721TiersHook.sol +1 -1
- package/src/JB721TiersHookDeployer.sol +1 -1
- package/src/JB721TiersHookProjectDeployer.sol +1 -1
- package/src/JB721TiersHookStore.sol +23 -17
- package/src/libraries/JB721Constants.sol +1 -1
- package/src/libraries/JB721TiersRulesetMetadataResolver.sol +1 -1
- package/src/libraries/JBBitmap.sol +1 -1
- package/src/libraries/JBIpfsDecoder.sol +1 -1
- package/src/structs/JB721Tier.sol +5 -11
- package/src/structs/JB721TierConfig.sol +5 -20
- package/src/structs/JB721TierConfigFlags.sol +26 -0
- package/src/structs/JB721TierFlags.sol +17 -0
- package/test/721HookAttacks.t.sol +22 -17
- package/test/E2E/Pay_Mint_Redeem_E2E.t.sol +19 -14
- package/test/Fork.t.sol +69 -54
- package/test/TestAuditGaps.sol +73 -56
- package/test/TestSafeTransferReentrancy.t.sol +4 -4
- package/test/TestVotingUnitsLifecycle.t.sol +11 -11
- package/test/audit/CodexPayCreditsBypassTierSplits.t.sol +10 -7
- package/test/audit/CodexSplitCreditsMismatch.t.sol +10 -7
- package/test/fork/ERC20CashOutFork.t.sol +37 -28
- package/test/fork/ERC20TierSplitFork.t.sol +28 -21
- package/test/fork/IssueTokensForSplitsFork.t.sol +10 -7
- package/test/invariants/handlers/TierLifecycleHandler.sol +10 -7
- package/test/invariants/handlers/TierStoreHandler.sol +10 -7
- package/test/regression/ProjectDeployerRulesets.t.sol +10 -7
- package/test/regression/ReserveBeneficiaryOverwrite.t.sol +6 -6
- package/test/unit/AuditFixes_Unit.t.sol +37 -28
- package/test/unit/adjustTier_Unit.t.sol +268 -202
- package/test/unit/getters_constructor_Unit.t.sol +20 -14
- package/test/unit/mintFor_mintReservesFor_Unit.t.sol +2 -2
- package/test/unit/pay_Unit.t.sol +1 -1
- package/CHANGE_LOG.md +0 -359
|
@@ -3,6 +3,8 @@ pragma solidity 0.8.28;
|
|
|
3
3
|
|
|
4
4
|
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
5
5
|
import "../utils/UnitTestSetup.sol";
|
|
6
|
+
import {JB721TierConfigFlags} from "../../src/structs/JB721TierConfigFlags.sol";
|
|
7
|
+
import {JB721TierFlags} from "../../src/structs/JB721TierFlags.sol";
|
|
6
8
|
|
|
7
9
|
contract Test_Getters_Constructor_Unit is UnitTestSetup {
|
|
8
10
|
using stdStorage for StdStorage;
|
|
@@ -151,11 +153,13 @@ contract Test_Getters_Constructor_Unit is UnitTestSetup {
|
|
|
151
153
|
encodedIPFSUri: bytes32(0),
|
|
152
154
|
category: uint24(100),
|
|
153
155
|
discountPercent: uint8(0),
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
156
|
+
flags: JB721TierFlags({
|
|
157
|
+
allowOwnerMint: false,
|
|
158
|
+
transfersPausable: false,
|
|
159
|
+
cantBeRemoved: false,
|
|
160
|
+
cantIncreaseDiscountPercent: false,
|
|
161
|
+
cantBuyWithCredits: false
|
|
162
|
+
}),
|
|
159
163
|
splitPercent: 0,
|
|
160
164
|
resolvedUri: ""
|
|
161
165
|
})
|
|
@@ -265,7 +269,7 @@ contract Test_Getters_Constructor_Unit is UnitTestSetup {
|
|
|
265
269
|
votingUnits = bound(votingUnits, 1, type(uint32).max);
|
|
266
270
|
balances = bound(balances, 1, type(uint32).max);
|
|
267
271
|
|
|
268
|
-
defaultTierConfig.useVotingUnits = true;
|
|
272
|
+
defaultTierConfig.flags.useVotingUnits = true;
|
|
269
273
|
// forge-lint: disable-next-line(unsafe-typecast)
|
|
270
274
|
defaultTierConfig.votingUnits = uint32(votingUnits);
|
|
271
275
|
ForTest_JB721TiersHook hook = _initializeForTestHook(numberOfTiers);
|
|
@@ -474,7 +478,7 @@ contract Test_Getters_Constructor_Unit is UnitTestSetup {
|
|
|
474
478
|
vm.assume(newOwner != trustedForwarder);
|
|
475
479
|
vm.assume(previousOwner != trustedForwarder);
|
|
476
480
|
|
|
477
|
-
defaultTierConfig.allowOwnerMint = true;
|
|
481
|
+
defaultTierConfig.flags.allowOwnerMint = true;
|
|
478
482
|
defaultTierConfig.reserveFrequency = 0;
|
|
479
483
|
ForTest_JB721TiersHook hook = _initializeForTestHook(10);
|
|
480
484
|
|
|
@@ -546,13 +550,15 @@ contract Test_Getters_Constructor_Unit is UnitTestSetup {
|
|
|
546
550
|
encodedIPFSUri: tokenUris[0],
|
|
547
551
|
category: uint24(100),
|
|
548
552
|
discountPercent: uint8(0),
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
553
|
+
flags: JB721TierConfigFlags({
|
|
554
|
+
allowOwnerMint: false,
|
|
555
|
+
useReserveBeneficiaryAsDefault: false,
|
|
556
|
+
transfersPausable: false,
|
|
557
|
+
useVotingUnits: true,
|
|
558
|
+
cantBeRemoved: false,
|
|
559
|
+
cantIncreaseDiscountPercent: false,
|
|
560
|
+
cantBuyWithCredits: false
|
|
561
|
+
}),
|
|
556
562
|
splitPercent: 0,
|
|
557
563
|
splits: new JBSplit[](0)
|
|
558
564
|
});
|
|
@@ -414,7 +414,7 @@ contract Test_mintFor_mintReservesFor_Unit is UnitTestSetup {
|
|
|
414
414
|
function test_mintFor_mintArrayOfTiers() public {
|
|
415
415
|
uint256 numberOfTiers = 3;
|
|
416
416
|
|
|
417
|
-
defaultTierConfig.allowOwnerMint = true;
|
|
417
|
+
defaultTierConfig.flags.allowOwnerMint = true;
|
|
418
418
|
defaultTierConfig.reserveFrequency = 0;
|
|
419
419
|
ForTest_JB721TiersHook hook = _initializeForTestHook(numberOfTiers);
|
|
420
420
|
|
|
@@ -454,7 +454,7 @@ contract Test_mintFor_mintReservesFor_Unit is UnitTestSetup {
|
|
|
454
454
|
}
|
|
455
455
|
|
|
456
456
|
// Set the `allowOwnerMint` flag to false and initialize the hook.
|
|
457
|
-
defaultTierConfig.allowOwnerMint = false;
|
|
457
|
+
defaultTierConfig.flags.allowOwnerMint = false;
|
|
458
458
|
ForTest_JB721TiersHook hook = _initializeForTestHook(numberOfTiers);
|
|
459
459
|
|
|
460
460
|
vm.prank(owner);
|
package/test/unit/pay_Unit.t.sol
CHANGED
|
@@ -1309,7 +1309,7 @@ contract Test_afterPayRecorded_Unit is UnitTestSetup {
|
|
|
1309
1309
|
// as long as the `transfersPausable` flag must be true.
|
|
1310
1310
|
// Transfers involving the zero address (minting and burning) are not affected.
|
|
1311
1311
|
function test_transferFrom_revertTransferIfPausedInRuleset() public {
|
|
1312
|
-
defaultTierConfig.transfersPausable = true;
|
|
1312
|
+
defaultTierConfig.flags.transfersPausable = true;
|
|
1313
1313
|
JB721TiersHook hook = _initHookDefaultTiers(10);
|
|
1314
1314
|
|
|
1315
1315
|
// Mock the directory call.
|
package/CHANGE_LOG.md
DELETED
|
@@ -1,359 +0,0 @@
|
|
|
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
|
-
## Summary
|
|
6
|
-
|
|
7
|
-
- **Tier splits system**: Each NFT tier can now route a percentage of payments to split recipients instead of the project treasury, with full DOS protection via try-catch on all external calls.
|
|
8
|
-
- **`votingUnits` replaced by `splitPercent`** in packed tier storage — voting units moved to a separate mapping to free the storage slot for the more widely-needed split feature.
|
|
9
|
-
- **New `JB721TiersHookLib` library**: Code extracted from the hook to stay within the EIP-170 contract size limit (24KB).
|
|
10
|
-
- **Mutable collection metadata**: `name` and `symbol` can now be changed post-deployment via `setMetadata()`.
|
|
11
|
-
- **Prices moved to constructor immutable**: `IJBPrices` is now a constructor parameter instead of being packed in tier config.
|
|
12
|
-
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
## 1. Breaking Changes
|
|
16
|
-
|
|
17
|
-
### 1.1 `IJB721TiersHook` — Changed Function Signatures
|
|
18
|
-
|
|
19
|
-
| Function | v5 Signature | v6 Signature | Notes |
|
|
20
|
-
|----------|-------------|-------------|-------|
|
|
21
|
-
| `pricingContext()` | `returns (uint256, uint256, IJBPrices)` | `returns (uint256 currency, uint256 decimals)` | `IJBPrices` removed from return; now a separate `PRICES()` getter |
|
|
22
|
-
| `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` |
|
|
23
|
-
|
|
24
|
-
### 1.2 `JB721Hook` (abstract) — Changed Function Signatures
|
|
25
|
-
|
|
26
|
-
| Function | v5 Signature | v6 Signature | Notes |
|
|
27
|
-
|----------|-------------|-------------|-------|
|
|
28
|
-
| `cashOutWeightOf(...)` | `(uint256[] memory, JBBeforeCashOutRecordedContext calldata) returns (uint256)` | `(uint256[] memory) returns (uint256)` | Removed `JBBeforeCashOutRecordedContext` parameter |
|
|
29
|
-
| `totalCashOutWeight(...)` | `(JBBeforeCashOutRecordedContext calldata) returns (uint256)` | `() returns (uint256)` | Removed `JBBeforeCashOutRecordedContext` parameter |
|
|
30
|
-
| `afterPayRecordedWith(...)` | Reverts if `msg.value != 0` | No `msg.value` check | v6 removes the `msg.value != 0` revert condition from pay hook validation |
|
|
31
|
-
|
|
32
|
-
### 1.3 `JB721TiersHook` — Changed Constructor
|
|
33
|
-
|
|
34
|
-
| Parameter | v5 | v6 | Notes |
|
|
35
|
-
|-----------|----|----|-------|
|
|
36
|
-
| `prices` | Not a parameter (packed in `_packedPricingContext`) | `IJBPrices prices` | Now an immutable constructor parameter |
|
|
37
|
-
| `splits` | Not present | `IJBSplits splits` | New immutable for tier split distribution |
|
|
38
|
-
|
|
39
|
-
### 1.4 `JB721InitTiersConfig` — Removed Field
|
|
40
|
-
|
|
41
|
-
| Field | v5 | v6 | Notes |
|
|
42
|
-
|-------|----|----|-------|
|
|
43
|
-
| `prices` | `IJBPrices prices` | Removed | Prices contract moved to `JB721TiersHook` constructor as an immutable |
|
|
44
|
-
|
|
45
|
-
### 1.5 `JB721Constants` — Renamed Constant
|
|
46
|
-
|
|
47
|
-
| v5 | v6 | Notes |
|
|
48
|
-
|----|----|-------|
|
|
49
|
-
| `MAX_DISCOUNT_PERCENT` | `DISCOUNT_DENOMINATOR` | Same value (`200`), renamed for clarity |
|
|
50
|
-
|
|
51
|
-
### 1.6 `JBStored721Tier` — Replaced Field
|
|
52
|
-
|
|
53
|
-
| Field | v5 | v6 | Notes |
|
|
54
|
-
|-------|----|----|-------|
|
|
55
|
-
| `votingUnits` (`uint32`) | Present | Removed | Replaced by `splitPercent` |
|
|
56
|
-
| `splitPercent` (`uint32`) | Not present | Added | Percentage of tier price routed to splits |
|
|
57
|
-
|
|
58
|
-
> **Why this change**: On-chain voting units were rarely used by projects, while per-tier payment splits were the most requested feature. Reusing the same `uint32` storage slot avoided expanding the packed struct and kept gas costs unchanged. Voting units are still available via the `_tierVotingUnitsOf` mapping.
|
|
59
|
-
|
|
60
|
-
### 1.7 Error Signature Changes (Store)
|
|
61
|
-
|
|
62
|
-
Several store errors gained a `tierId` parameter for better debugging:
|
|
63
|
-
|
|
64
|
-
| Error | v5 | v6 |
|
|
65
|
-
|-------|----|----|
|
|
66
|
-
| `CantMintManually` | `()` | `(uint256 tierId)` |
|
|
67
|
-
| `CantRemoveTier` | `()` | `(uint256 tierId)` |
|
|
68
|
-
| `InsufficientSupplyRemaining` | `()` | `(uint256 tierId)` |
|
|
69
|
-
| `ManualMintingNotAllowed` | `()` | `(uint256 tierId)` |
|
|
70
|
-
| `ReserveFrequencyNotAllowed` | `()` | `(uint256 tierId)` |
|
|
71
|
-
| `UnrecognizedTier` | `()` | `(uint256 tierId)` |
|
|
72
|
-
| `VotingUnitsNotAllowed` | `()` | `(uint256 tierId)` |
|
|
73
|
-
| `ZeroInitialSupply` | `()` | `(uint256 tierId)` |
|
|
74
|
-
|
|
75
|
-
### 1.8 Solidity Version Change
|
|
76
|
-
|
|
77
|
-
All contracts upgraded from `pragma solidity 0.8.23` to `pragma solidity 0.8.28`.
|
|
78
|
-
|
|
79
|
-
### 1.9 Cross-Repo Impact
|
|
80
|
-
|
|
81
|
-
> The tier splits system affects `revnet-core-v6`, `croptop-core-v6`, and `nana-omnichain-deployers-v6`, which all deploy 721 hooks and must account for the new `splitPercent`/`splits` fields in `JB721TierConfig`. The `issueTokensForSplits` flag in `JB721TiersHookFlags` is force-set to `false` by `revnet-core-v6` to prevent dilution.
|
|
82
|
-
|
|
83
|
-
---
|
|
84
|
-
|
|
85
|
-
## 2. New Features
|
|
86
|
-
|
|
87
|
-
### 2.1 Tier Splits System
|
|
88
|
-
|
|
89
|
-
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.
|
|
90
|
-
|
|
91
|
-
**New immutables on `JB721TiersHook`:**
|
|
92
|
-
|
|
93
|
-
| Name | Type | Description |
|
|
94
|
-
|------|------|-------------|
|
|
95
|
-
| `PRICES()` | `IJBPrices` | Price feed contract (was previously packed in `_packedPricingContext`) |
|
|
96
|
-
| `SPLITS()` | `IJBSplits` | Splits contract for reading/writing tier split groups |
|
|
97
|
-
|
|
98
|
-
**New library: `JB721TiersHookLib`** (entirely new in v6)
|
|
99
|
-
|
|
100
|
-
Contains functions extracted from `JB721TiersHook` to stay within the EIP-170 contract size limit:
|
|
101
|
-
|
|
102
|
-
| Function | Description |
|
|
103
|
-
|----------|-------------|
|
|
104
|
-
| `adjustTiersFor(...)` | Handles tier removal, addition, event emission, and split group setting |
|
|
105
|
-
| `recordAddTiersFor(...)` | Records new tiers and sets their split groups (used during initialization) |
|
|
106
|
-
| `normalizePaymentValue(...)` | Normalizes payment value based on pricing context |
|
|
107
|
-
| `calculateSplitAmounts(...)` | Calculates per-tier split amounts for a pay event |
|
|
108
|
-
| `convertAndCapSplitAmounts(...)` | Converts split amounts between currencies, capped at payment value |
|
|
109
|
-
| `calculateWeight(...)` | Adjusts minting weight to account for split amounts |
|
|
110
|
-
| `distributeAll(...)` | Pulls tokens and distributes forwarded funds to tier split recipients |
|
|
111
|
-
| `resolveTokenURI(...)` | Resolves the token URI (moved IPFS decoding out of hook) |
|
|
112
|
-
|
|
113
|
-
**New flag on `JB721TiersHookFlags`:**
|
|
114
|
-
|
|
115
|
-
| Field | Type | Description |
|
|
116
|
-
|-------|------|-------------|
|
|
117
|
-
| `issueTokensForSplits` | `bool` | When `true`, payers receive full token credit even for the portion routed to splits. When `false` (default), weight is reduced proportionally. |
|
|
118
|
-
|
|
119
|
-
### 2.2 Mutable Collection Name and Symbol
|
|
120
|
-
|
|
121
|
-
The ERC721 collection `name` and `symbol` can now be changed after initialization via `setMetadata(...)`.
|
|
122
|
-
|
|
123
|
-
**New internal functions on `ERC721` (abstract):**
|
|
124
|
-
|
|
125
|
-
| Function | Description |
|
|
126
|
-
|----------|-------------|
|
|
127
|
-
| `_setName(string memory)` | Updates the token collection name |
|
|
128
|
-
| `_setSymbol(string memory)` | Updates the token collection symbol |
|
|
129
|
-
|
|
130
|
-
### 2.3 `split.hook` Support in Tier Distribution
|
|
131
|
-
|
|
132
|
-
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.
|
|
133
|
-
|
|
134
|
-
### 2.4 DOS Protection on Split Distribution
|
|
135
|
-
|
|
136
|
-
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, the failed amount is accumulated separately and routed to the project balance after the distribution loop completes. This proportional redistribution ensures that later split recipients receive only their configured share, not an inflated share from the failed recipient's funds. 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.
|
|
137
|
-
|
|
138
|
-
### 2.5 `splitPercent` Bounds Validation
|
|
139
|
-
|
|
140
|
-
`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)`.
|
|
141
|
-
|
|
142
|
-
### 2.6 `beforePayRecordedWith` Override in `JB721TiersHook`
|
|
143
|
-
|
|
144
|
-
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.
|
|
145
|
-
|
|
146
|
-
### 2.7 Pricing Decimals Validation
|
|
147
|
-
|
|
148
|
-
`initialize(...)` now reverts with `JB721TiersHook_InvalidPricingDecimals` if `tiersConfig.decimals > 18`.
|
|
149
|
-
|
|
150
|
-
### 2.8 `allowSetCustomToken` Pass-Through
|
|
151
|
-
|
|
152
|
-
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.
|
|
153
|
-
|
|
154
|
-
---
|
|
155
|
-
|
|
156
|
-
## 3. Event Changes
|
|
157
|
-
|
|
158
|
-
### 3.1 New Events on `IJB721TiersHook`
|
|
159
|
-
|
|
160
|
-
| Event | Signature | Description |
|
|
161
|
-
|-------|-----------|-------------|
|
|
162
|
-
| `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. |
|
|
163
|
-
| `SetName` | `SetName(string indexed name, address caller)` | Emitted when the collection name is changed |
|
|
164
|
-
| `SetSymbol` | `SetSymbol(string indexed symbol, address caller)` | Emitted when the collection symbol is changed |
|
|
165
|
-
| `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. |
|
|
166
|
-
|
|
167
|
-
### 3.2 New Event on `IJB721TiersHookStore`
|
|
168
|
-
|
|
169
|
-
| Event | Signature | Description |
|
|
170
|
-
|-------|-----------|-------------|
|
|
171
|
-
| `SetDefaultReserveBeneficiary` | `SetDefaultReserveBeneficiary(address indexed hook, address indexed newBeneficiary, address caller)` | Emitted when the global default reserve beneficiary is changed via `useReserveBeneficiaryAsDefault` |
|
|
172
|
-
|
|
173
|
-
### 3.3 Events Unchanged
|
|
174
|
-
|
|
175
|
-
All other events (`AddPayCredits`, `AddTier`, `Mint`, `MintReservedNft`, `RemoveTier`, `SetBaseUri`, `SetContractUri`, `SetDiscountPercent`, `SetEncodedIPFSUri`, `SetTokenUriResolver`, `UsePayCredits`, `CleanTiers`, `HookDeployed`) remain unchanged.
|
|
176
|
-
|
|
177
|
-
---
|
|
178
|
-
|
|
179
|
-
## 4. Error Changes
|
|
180
|
-
|
|
181
|
-
### 4.1 New Errors on `JB721TiersHook`
|
|
182
|
-
|
|
183
|
-
| Error | Signature | Description |
|
|
184
|
-
|-------|-----------|-------------|
|
|
185
|
-
| `JB721TiersHook_CurrencyMismatch` | `(uint256 paymentCurrency, uint256 tierCurrency)` | Reserved for currency mismatch detection |
|
|
186
|
-
| `JB721TiersHook_InvalidPricingDecimals` | `(uint256 decimals)` | Reverts when `tiersConfig.decimals > 18` during initialization |
|
|
187
|
-
|
|
188
|
-
### 4.2 New Error on `JB721TiersHookStore`
|
|
189
|
-
|
|
190
|
-
| Error | Signature | Description |
|
|
191
|
-
|-------|-----------|-------------|
|
|
192
|
-
| `JB721TiersHookStore_SplitPercentExceedsBounds` | `(uint256 percent, uint256 limit)` | Reverts when a tier's `splitPercent` exceeds `JBConstants.SPLITS_TOTAL_PERCENT` during `recordAddTiers` |
|
|
193
|
-
|
|
194
|
-
### 4.3 Store Errors with Added `tierId` Parameter
|
|
195
|
-
|
|
196
|
-
See Section 1.7 above. Eight store errors gained a `tierId` parameter for improved debuggability.
|
|
197
|
-
|
|
198
|
-
### 4.4 Errors Unchanged
|
|
199
|
-
|
|
200
|
-
All other errors (`JB721Hook_InvalidPay`, `JB721Hook_InvalidCashOut`, `JB721Hook_UnauthorizedToken`, `JB721Hook_UnexpectedTokenCashedOut`, `JB721TiersHook_AlreadyInitialized`, `JB721TiersHook_NoProjectId`, `JB721TiersHook_Overspending`, `JB721TiersHook_MintReserveNftsPaused`, `JB721TiersHook_TierTransfersPaused`) remain unchanged.
|
|
201
|
-
|
|
202
|
-
---
|
|
203
|
-
|
|
204
|
-
## 5. Struct Changes
|
|
205
|
-
|
|
206
|
-
### 5.1 `JB721Tier` — Added Field
|
|
207
|
-
|
|
208
|
-
| Field | Type | Description |
|
|
209
|
-
|-------|------|-------------|
|
|
210
|
-
| `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`. |
|
|
211
|
-
|
|
212
|
-
### 5.2 `JB721TierConfig` — Added Fields
|
|
213
|
-
|
|
214
|
-
| Field | Type | Description |
|
|
215
|
-
|-------|------|-------------|
|
|
216
|
-
| `splitPercent` | `uint32` | Percentage of payment routed to splits when minting from this tier |
|
|
217
|
-
| `splits` | `JBSplit[]` | The split recipients for this tier's split group |
|
|
218
|
-
|
|
219
|
-
### 5.3 `JBStored721Tier` — Replaced Field
|
|
220
|
-
|
|
221
|
-
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).
|
|
222
|
-
|
|
223
|
-
### 5.4 `JB721InitTiersConfig` — Removed Field
|
|
224
|
-
|
|
225
|
-
| Field | Type | Description |
|
|
226
|
-
|-------|------|-------------|
|
|
227
|
-
| `prices` | `IJBPrices` | Removed. The prices contract is now an immutable on the hook itself. |
|
|
228
|
-
|
|
229
|
-
### 5.5 `JB721TiersHookFlags` — Added Field
|
|
230
|
-
|
|
231
|
-
| Field | Type | Description |
|
|
232
|
-
|-------|------|-------------|
|
|
233
|
-
| `issueTokensForSplits` | `bool` | Whether payers receive token credit for the split portion of their payment |
|
|
234
|
-
|
|
235
|
-
### 5.6 `JBPayDataHookRulesetMetadata` — Added Field
|
|
236
|
-
|
|
237
|
-
| Field | Type | Description |
|
|
238
|
-
|-------|------|-------------|
|
|
239
|
-
| `allowSetCustomToken` | `bool` | Whether the project owner can set a custom ERC-20 token. Inserted between `allowOwnerMinting` and `allowTerminalMigration`. |
|
|
240
|
-
|
|
241
|
-
### 5.7 Structs Unchanged
|
|
242
|
-
|
|
243
|
-
`JB721TiersMintReservesConfig`, `JB721TiersSetDiscountPercentConfig`, `JB721TiersRulesetMetadata`, `JBBitmapWord`, `JBDeploy721TiersHookConfig`, `JBLaunchProjectConfig`, `JBLaunchRulesetsConfig`, `JBQueueRulesetsConfig`, `JBPayDataHookRulesetConfig` remain structurally identical (import paths updated from core-v5 to core-v6).
|
|
244
|
-
|
|
245
|
-
---
|
|
246
|
-
|
|
247
|
-
## 6. Implementation Changes (Non-Interface)
|
|
248
|
-
|
|
249
|
-
### 6.1 `JB721TiersHook._processPayment` — Split Distribution
|
|
250
|
-
|
|
251
|
-
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.
|
|
252
|
-
|
|
253
|
-
### 6.2 `JB721TiersHook.adjustTiers` — Delegated to Library
|
|
254
|
-
|
|
255
|
-
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`.
|
|
256
|
-
|
|
257
|
-
### 6.3 `JB721TiersHook.initialize` — Split Group Setup
|
|
258
|
-
|
|
259
|
-
During initialization, v6 uses `JB721TiersHookLib.recordAddTiersFor(...)` instead of calling `STORE.recordAddTiers()` directly. This also sets up split groups for tiers that have splits configured.
|
|
260
|
-
|
|
261
|
-
### 6.4 `JB721TiersHook.tokenURI` — Delegated to Library
|
|
262
|
-
|
|
263
|
-
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.
|
|
264
|
-
|
|
265
|
-
### 6.5 `JB721TiersHook._processPayment` — Payment Normalization Delegated
|
|
266
|
-
|
|
267
|
-
In v5, the payment value normalization (currency conversion via `IJBPrices`) was done inline. In v6, it is delegated to `JB721TiersHookLib.normalizePaymentValue(...)`.
|
|
268
|
-
|
|
269
|
-
### 6.6 `JB721TiersHook._packedPricingContext` — Reduced Packing
|
|
270
|
-
|
|
271
|
-
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.
|
|
272
|
-
|
|
273
|
-
### 6.7 `JB721Hook.afterPayRecordedWith` — Removed `msg.value` Check
|
|
274
|
-
|
|
275
|
-
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.
|
|
276
|
-
|
|
277
|
-
### 6.8 `JB721TiersHookLib._sendPayoutToSplit` — `split.hook` Priority
|
|
278
|
-
|
|
279
|
-
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).
|
|
280
|
-
|
|
281
|
-
### 6.9 `JB721TiersHookLib` — Try-Catch on All External Calls
|
|
282
|
-
|
|
283
|
-
All external calls during split distribution (split hooks, terminal `pay`, terminal `addToBalanceOf`, and leftover `addToBalanceOf`) are wrapped in try-catch. On failure: the failed amount is accumulated separately and routed to the project balance after the distribution loop, ensuring proportional (not equal) redistribution among remaining recipients. 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.
|
|
284
|
-
|
|
285
|
-
### 6.10 `JB721TiersHookLib.calculateSplitAmounts` — Discount Applied Before Splits
|
|
286
|
-
|
|
287
|
-
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.
|
|
288
|
-
|
|
289
|
-
### 6.11 `JB721TiersHookStore.recordAddTiers` — Validates `splitPercent`
|
|
290
|
-
|
|
291
|
-
`recordAddTiers` now reverts with `JB721TiersHookStore_SplitPercentExceedsBounds` if a tier's `splitPercent` exceeds `JBConstants.SPLITS_TOTAL_PERCENT`.
|
|
292
|
-
|
|
293
|
-
### 6.12 `JB721TiersHookStore.recordAddTiers` — Stores `splitPercent`
|
|
294
|
-
|
|
295
|
-
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.
|
|
296
|
-
|
|
297
|
-
### 6.13 `JB721TiersHookStore.recordAddTiers` — Emits `SetDefaultReserveBeneficiary`
|
|
298
|
-
|
|
299
|
-
When a tier config has `useReserveBeneficiaryAsDefault` set and the beneficiary differs from the current default, v6 emits the new `SetDefaultReserveBeneficiary` event.
|
|
300
|
-
|
|
301
|
-
### 6.14 `JB721TiersHookProjectDeployer` — `allowSetCustomToken` Pass-Through
|
|
302
|
-
|
|
303
|
-
In v5, the project deployer hardcoded `allowSetCustomToken: false` when constructing `JBRulesetMetadata`. In v6, it passes through `payDataRulesetConfig.metadata.allowSetCustomToken`.
|
|
304
|
-
|
|
305
|
-
---
|
|
306
|
-
|
|
307
|
-
## 7. Migration Table
|
|
308
|
-
|
|
309
|
-
### 7.1 Interfaces
|
|
310
|
-
|
|
311
|
-
| v5 | v6 | Notes |
|
|
312
|
-
|----|----|-------|
|
|
313
|
-
| `IJB721Hook` | `IJB721Hook` | Unchanged (import path updated) |
|
|
314
|
-
| `IJB721TiersHook` | `IJB721TiersHook` | `pricingContext()` return changed; `setMetadata()` signature changed; added `PRICES()`, `SPLITS()`, `SetName`, `SetSymbol`, `SplitPayoutReverted`, `AddToBalanceReverted` |
|
|
315
|
-
| `IJB721TiersHookDeployer` | `IJB721TiersHookDeployer` | Unchanged |
|
|
316
|
-
| `IJB721TiersHookProjectDeployer` | `IJB721TiersHookProjectDeployer` | Unchanged |
|
|
317
|
-
| `IJB721TiersHookStore` | `IJB721TiersHookStore` | Added `SetDefaultReserveBeneficiary` event; NatSpec added |
|
|
318
|
-
| `IJB721TokenUriResolver` | `IJB721TokenUriResolver` | Unchanged |
|
|
319
|
-
|
|
320
|
-
### 7.2 Contracts
|
|
321
|
-
|
|
322
|
-
| v5 | v6 | Notes |
|
|
323
|
-
|----|----|-------|
|
|
324
|
-
| `JB721TiersHook` | `JB721TiersHook` | Constructor gains `prices` and `splits`; tier splits system; code extracted to library |
|
|
325
|
-
| `JB721TiersHookDeployer` | `JB721TiersHookDeployer` | Unchanged (import paths updated) |
|
|
326
|
-
| `JB721TiersHookProjectDeployer` | `JB721TiersHookProjectDeployer` | `allowSetCustomToken` pass-through |
|
|
327
|
-
| `JB721TiersHookStore` | `JB721TiersHookStore` | `splitPercent` storage and validation; error params added; `SplitPercentExceedsBounds` error; `SetDefaultReserveBeneficiary` event |
|
|
328
|
-
| `JB721Hook` (abstract) | `JB721Hook` (abstract) | `cashOutWeightOf`/`totalCashOutWeight` signatures simplified; `msg.value` check removed from `afterPayRecordedWith` |
|
|
329
|
-
| `ERC721` (abstract) | `ERC721` (abstract) | Added `_setName()` and `_setSymbol()` |
|
|
330
|
-
|
|
331
|
-
### 7.3 Libraries
|
|
332
|
-
|
|
333
|
-
| v5 | v6 | Notes |
|
|
334
|
-
|----|----|-------|
|
|
335
|
-
| `JB721Constants` | `JB721Constants` | `MAX_DISCOUNT_PERCENT` renamed to `DISCOUNT_DENOMINATOR` |
|
|
336
|
-
| `JB721TiersRulesetMetadataResolver` | `JB721TiersRulesetMetadataResolver` | Unchanged |
|
|
337
|
-
| `JBBitmap` | `JBBitmap` | Unchanged |
|
|
338
|
-
| `JBIpfsDecoder` | `JBIpfsDecoder` | Unchanged |
|
|
339
|
-
| _(not present)_ | `JB721TiersHookLib` | New library for tier splits, payment normalization, weight calculation, and token URI resolution |
|
|
340
|
-
|
|
341
|
-
### 7.4 Structs
|
|
342
|
-
|
|
343
|
-
| v5 | v6 | Notes |
|
|
344
|
-
|----|----|-------|
|
|
345
|
-
| `JB721Tier` | `JB721Tier` | Added `splitPercent` field |
|
|
346
|
-
| `JB721TierConfig` | `JB721TierConfig` | Added `splitPercent` and `splits` fields |
|
|
347
|
-
| `JB721TiersHookFlags` | `JB721TiersHookFlags` | Added `issueTokensForSplits` field |
|
|
348
|
-
| `JB721InitTiersConfig` | `JB721InitTiersConfig` | Removed `prices` field |
|
|
349
|
-
| `JBStored721Tier` | `JBStored721Tier` | `votingUnits` replaced by `splitPercent` |
|
|
350
|
-
| `JBPayDataHookRulesetMetadata` | `JBPayDataHookRulesetMetadata` | Added `allowSetCustomToken` field |
|
|
351
|
-
| `JB721TiersMintReservesConfig` | `JB721TiersMintReservesConfig` | Unchanged |
|
|
352
|
-
| `JB721TiersSetDiscountPercentConfig` | `JB721TiersSetDiscountPercentConfig` | Unchanged |
|
|
353
|
-
| `JB721TiersRulesetMetadata` | `JB721TiersRulesetMetadata` | Unchanged |
|
|
354
|
-
| `JBBitmapWord` | `JBBitmapWord` | Unchanged |
|
|
355
|
-
| `JBDeploy721TiersHookConfig` | `JBDeploy721TiersHookConfig` | Unchanged |
|
|
356
|
-
| `JBLaunchProjectConfig` | `JBLaunchProjectConfig` | Unchanged |
|
|
357
|
-
| `JBLaunchRulesetsConfig` | `JBLaunchRulesetsConfig` | Unchanged |
|
|
358
|
-
| `JBQueueRulesetsConfig` | `JBQueueRulesetsConfig` | Unchanged |
|
|
359
|
-
| `JBPayDataHookRulesetConfig` | `JBPayDataHookRulesetConfig` | Unchanged |
|