@croptop/core-v6 0.0.26 → 0.0.28

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@croptop/core-v6",
3
- "version": "0.0.26",
3
+ "version": "0.0.28",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -16,7 +16,7 @@
16
16
  "artifacts": "source ./.env && npx sphinx artifacts --org-id 'ea165b21-7cdc-4d7b-be59-ecdd4c26bee4' --project-name 'croptop-core-v5'"
17
17
  },
18
18
  "dependencies": {
19
- "@bananapus/721-hook-v6": "^0.0.26",
19
+ "@bananapus/721-hook-v6": "^0.0.30",
20
20
  "@bananapus/address-registry-v6": "^0.0.16",
21
21
  "@bananapus/buyback-hook-v6": "^0.0.24",
22
22
  "@bananapus/core-v6": "^0.0.30",
@@ -27,7 +27,7 @@
27
27
  "@openzeppelin/contracts": "^5.6.1"
28
28
  },
29
29
  "devDependencies": {
30
- "@rev-net/core-v6": "^0.0.17",
30
+ "@rev-net/core-v6": "^0.0.24",
31
31
  "@sphinx-labs/plugins": "^0.33.1"
32
32
  }
33
33
  }
@@ -0,0 +1,25 @@
1
+ # Croptop Operations
2
+
3
+ ## Deployment Surface
4
+
5
+ - [`src/CTDeployer.sol`](../src/CTDeployer.sol) is the first stop for project launch shape, hook forwarding, and optional sucker integration.
6
+ - [`script/Deploy.s.sol`](../script/Deploy.s.sol) and [`script/ConfigureFeeProject.s.sol`](../script/ConfigureFeeProject.s.sol) cover the current deployment and fee-project wiring.
7
+ - [`src/structs/`](../src/structs/) contains config types that often drift from memory.
8
+
9
+ ## Change Checklist
10
+
11
+ - If you edit posting criteria, verify both direct publisher calls and deployer-created project flows.
12
+ - If you edit fee behavior, check both the designated fee project path and any exemption behavior.
13
+ - If you edit burn-lock ownership assumptions, confirm the intended irreversibility still holds.
14
+ - If you edit data-hook forwarding, re-check sucker-related fee-free cash-out behavior.
15
+
16
+ ## Common Failure Modes
17
+
18
+ - Publishing bug is blamed on the publisher when the deployer packaged the project or hook incorrectly.
19
+ - Immutable-owner expectations are missed after ownership moves into [`src/CTProjectOwner.sol`](../src/CTProjectOwner.sol).
20
+ - Content reuse or duplicate-post behavior changes and silently alters user-facing publishing semantics.
21
+
22
+ ## Useful Proof Points
23
+
24
+ - [`test/fork/`](../test/fork/) when deployment shape matters.
25
+ - [`script/helpers/`](../script/helpers/) if the issue is really script/config assembly.
@@ -0,0 +1,27 @@
1
+ # Croptop Runtime
2
+
3
+ ## Contract Roles
4
+
5
+ - [`src/CTPublisher.sol`](../src/CTPublisher.sol) validates posts, configures or reuses tiers, mints first copies, and routes Croptop fees.
6
+ - [`src/CTDeployer.sol`](../src/CTDeployer.sol) packages project deployment, hook forwarding, and optional sucker support.
7
+ - [`src/CTProjectOwner.sol`](../src/CTProjectOwner.sol) is the burn-lock ownership helper for immutable administration patterns.
8
+
9
+ ## Runtime Path
10
+
11
+ 1. A project is deployed or configured with Croptop posting rules.
12
+ 2. Publishers call into [`src/CTPublisher.sol`](../src/CTPublisher.sol) with content, supply, and pricing data.
13
+ 3. The publisher validates category-level rules, creates or reuses tiers, mints the first copy, and routes fees and proceeds.
14
+ 4. If the project uses the deployer wrapper, data-hook calls forward through [`src/CTDeployer.sol`](../src/CTDeployer.sol).
15
+
16
+ ## High-Risk Areas
17
+
18
+ - Posting criteria: category rules are the policy surface that protects the project from bad content or bad economics.
19
+ - Fee routing: fee-project assumptions and fee exemptions are operationally important.
20
+ - Tier reuse and duplicate content: content identity is part of runtime behavior, not just metadata.
21
+ - Burn-lock ownership: once ownership moves into the lock helper, reversibility expectations change drastically.
22
+
23
+ ## Tests To Trust First
24
+
25
+ - [`test/regression/`](../test/regression/) for pinned content and tier edge cases.
26
+ - [`test/fork/`](../test/fork/) for live integration assumptions.
27
+ - [`test/`](../test/) broadly when the issue could be in publisher or deployer behavior rather than one isolated function.
@@ -332,7 +332,6 @@ contract ConfigureFeeProjectScript is Script, Sphinx {
332
332
  tiersConfig: JB721InitTiersConfig({
333
333
  tiers: new JB721TierConfig[](0), currency: ETH_CURRENCY, decimals: DECIMALS
334
334
  }),
335
- reserveBeneficiary: address(0),
336
335
  flags: REV721TiersHookFlags({
337
336
  noNewTiersWithReserves: false,
338
337
  noNewTiersWithVotes: true,
@@ -5,6 +5,7 @@ import {IJB721TiersHook} from "@bananapus/721-hook-v6/src/interfaces/IJB721Tiers
5
5
  import {IJB721TiersHookStore} from "@bananapus/721-hook-v6/src/interfaces/IJB721TiersHookStore.sol";
6
6
  import {JB721Tier} from "@bananapus/721-hook-v6/src/structs/JB721Tier.sol";
7
7
  import {JB721TierConfig} from "@bananapus/721-hook-v6/src/structs/JB721TierConfig.sol";
8
+ import {JB721TierConfigFlags} from "@bananapus/721-hook-v6/src/structs/JB721TierConfigFlags.sol";
8
9
  import {JBPermissioned} from "@bananapus/core-v6/src/abstract/JBPermissioned.sol";
9
10
  import {IJBDirectory} from "@bananapus/core-v6/src/interfaces/IJBDirectory.sol";
10
11
  import {IJBPermissions} from "@bananapus/core-v6/src/interfaces/IJBPermissions.sol";
@@ -569,13 +570,15 @@ contract CTPublisher is JBPermissioned, ERC2771Context, ICTPublisher {
569
570
  encodedIPFSUri: post.encodedIPFSUri,
570
571
  category: post.category,
571
572
  discountPercent: 0,
572
- allowOwnerMint: false,
573
- useReserveBeneficiaryAsDefault: false,
574
- transfersPausable: false,
575
- useVotingUnits: true,
576
- cannotBeRemoved: false,
577
- cannotIncreaseDiscountPercent: false,
578
- cantBuyWithCredits: false,
573
+ flags: JB721TierConfigFlags({
574
+ allowOwnerMint: false,
575
+ useReserveBeneficiaryAsDefault: false,
576
+ transfersPausable: false,
577
+ useVotingUnits: true,
578
+ cantBeRemoved: false,
579
+ cantIncreaseDiscountPercent: false,
580
+ cantBuyWithCredits: false
581
+ }),
579
582
  splitPercent: post.splitPercent,
580
583
  splits: post.splits
581
584
  });
@@ -15,6 +15,7 @@ import {JBConstants} from "@bananapus/core-v6/src/libraries/JBConstants.sol";
15
15
  import {JBSplit} from "@bananapus/core-v6/src/structs/JBSplit.sol";
16
16
 
17
17
  import {JB721TierConfig} from "@bananapus/721-hook-v6/src/structs/JB721TierConfig.sol";
18
+ import {JB721TierConfigFlags} from "@bananapus/721-hook-v6/src/structs/JB721TierConfigFlags.sol";
18
19
 
19
20
  import {CTPublisher} from "../src/CTPublisher.sol";
20
21
  import {CTAllowedPost} from "../src/structs/CTAllowedPost.sol";
@@ -809,13 +810,15 @@ contract TestCTPublisher is Test {
809
810
  encodedIPFSUri: keccak256("split-beneficiary-test"),
810
811
  category: 5,
811
812
  discountPercent: 0,
812
- allowOwnerMint: false,
813
- useReserveBeneficiaryAsDefault: false,
814
- transfersPausable: false,
815
- useVotingUnits: true,
816
- cannotBeRemoved: false,
817
- cannotIncreaseDiscountPercent: false,
818
- cantBuyWithCredits: false,
813
+ flags: JB721TierConfigFlags({
814
+ allowOwnerMint: false,
815
+ useReserveBeneficiaryAsDefault: false,
816
+ transfersPausable: false,
817
+ useVotingUnits: true,
818
+ cantBeRemoved: false,
819
+ cantIncreaseDiscountPercent: false,
820
+ cantBuyWithCredits: false
821
+ }),
819
822
  splitPercent: 250_000_000,
820
823
  splits: splits
821
824
  });
@@ -11,6 +11,7 @@ import {IJB721Hook} from "@bananapus/721-hook-v6/src/interfaces/IJB721Hook.sol";
11
11
  import {IJB721TiersHook} from "@bananapus/721-hook-v6/src/interfaces/IJB721TiersHook.sol";
12
12
  import {IJB721TiersHookStore} from "@bananapus/721-hook-v6/src/interfaces/IJB721TiersHookStore.sol";
13
13
  import {JB721Tier} from "@bananapus/721-hook-v6/src/structs/JB721Tier.sol";
14
+ import {JB721TierFlags} from "@bananapus/721-hook-v6/src/structs/JB721TierFlags.sol";
14
15
  import {JBSplit} from "@bananapus/core-v6/src/structs/JBSplit.sol";
15
16
 
16
17
  import {CTPublisher} from "../../src/CTPublisher.sol";
@@ -110,11 +111,13 @@ contract H19_FeeEvasion is Test {
110
111
  encodedIPFSUri: TEST_URI,
111
112
  category: 5,
112
113
  discountPercent: 0,
113
- allowOwnerMint: false,
114
- transfersPausable: false,
115
- cannotBeRemoved: false,
116
- cannotIncreaseDiscountPercent: false,
117
- cantBuyWithCredits: false,
114
+ flags: JB721TierFlags({
115
+ allowOwnerMint: false,
116
+ transfersPausable: false,
117
+ cantBeRemoved: false,
118
+ cantIncreaseDiscountPercent: false,
119
+ cantBuyWithCredits: false
120
+ }),
118
121
  splitPercent: 0,
119
122
  resolvedUri: ""
120
123
  });
@@ -207,11 +210,13 @@ contract H19_FeeEvasion is Test {
207
210
  encodedIPFSUri: TEST_URI,
208
211
  category: 5,
209
212
  discountPercent: 0,
210
- allowOwnerMint: false,
211
- transfersPausable: false,
212
- cannotBeRemoved: false,
213
- cannotIncreaseDiscountPercent: false,
214
- cantBuyWithCredits: false,
213
+ flags: JB721TierFlags({
214
+ allowOwnerMint: false,
215
+ transfersPausable: false,
216
+ cantBeRemoved: false,
217
+ cantIncreaseDiscountPercent: false,
218
+ cantBuyWithCredits: false
219
+ }),
215
220
  splitPercent: 0,
216
221
  resolvedUri: ""
217
222
  });
@@ -11,6 +11,7 @@ import {IJB721Hook} from "@bananapus/721-hook-v6/src/interfaces/IJB721Hook.sol";
11
11
  import {IJB721TiersHook} from "@bananapus/721-hook-v6/src/interfaces/IJB721TiersHook.sol";
12
12
  import {IJB721TiersHookStore} from "@bananapus/721-hook-v6/src/interfaces/IJB721TiersHookStore.sol";
13
13
  import {JB721Tier} from "@bananapus/721-hook-v6/src/structs/JB721Tier.sol";
14
+ import {JB721TierFlags} from "@bananapus/721-hook-v6/src/structs/JB721TierFlags.sol";
14
15
  import {JBSplit} from "@bananapus/core-v6/src/structs/JBSplit.sol";
15
16
 
16
17
  import {CTPublisher} from "../../src/CTPublisher.sol";
@@ -170,11 +171,13 @@ contract L52_StaleTierIdMapping is Test {
170
171
  encodedIPFSUri: TEST_URI,
171
172
  category: 5,
172
173
  discountPercent: 0,
173
- allowOwnerMint: false,
174
- transfersPausable: false,
175
- cannotBeRemoved: false,
176
- cannotIncreaseDiscountPercent: false,
177
- cantBuyWithCredits: false,
174
+ flags: JB721TierFlags({
175
+ allowOwnerMint: false,
176
+ transfersPausable: false,
177
+ cantBeRemoved: false,
178
+ cantIncreaseDiscountPercent: false,
179
+ cantBuyWithCredits: false
180
+ }),
178
181
  splitPercent: 0,
179
182
  resolvedUri: ""
180
183
  });
package/CHANGE_LOG.md DELETED
@@ -1,273 +0,0 @@
1
- # croptop-core-v6 Changelog (v5 → v6)
2
-
3
- This document describes all changes between `croptop-core` (v5) and `croptop-core-v6` (v6).
4
-
5
- ## Summary
6
-
7
- - **Data hook proxy activated**: `CTDeployer` now sets itself as the data hook (`metadata.dataHook = address(this)`) instead of pointing directly to the 721 hook — enables sucker cashouts at 0% tax rate for cross-chain operations.
8
- - **Split support for posts**: `CTPost` gained `splitPercent` and `splits` fields, allowing poster-defined payment routing per NFT tier (bounded by `maximumSplitPercent`).
9
- - **Fee evasion fixes**: Existing tier mints now use on-chain price (not user-supplied), and duplicate posts within a batch are rejected.
10
- - **Stale tier recovery**: Externally-removed tiers are detected and re-created instead of silently failing.
11
- - **`projectId` cast widened**: `uint56` → `uint64` to match v6 `JBPermissionsData`.
12
-
13
- ---
14
-
15
- ## 1. Breaking Changes
16
-
17
- ### Solidity Version
18
- - Compiler version bumped from `0.8.23` to `0.8.28` across all implementation contracts (`CTDeployer`, `CTProjectOwner`, `CTPublisher`).
19
-
20
- ### Dependency Namespace Migration
21
- All imports updated from v5 to v6 namespaces:
22
- - `@bananapus/core-v5` → `@bananapus/core-v6`
23
- - `@bananapus/721-hook-v5` → `@bananapus/721-hook-v6`
24
- - `@bananapus/ownable-v5` → `@bananapus/ownable-v6`
25
- - `@bananapus/permission-ids-v5` → `@bananapus/permission-ids-v6`
26
- - `@bananapus/suckers-v5` → `@bananapus/suckers-v6`
27
-
28
- ### `ICTPublisher.allowanceFor` Return Signature Changed
29
- - **v5:** Returns 4 values — `(uint256 minimumPrice, uint256 minimumTotalSupply, uint256 maximumTotalSupply, address[] memory allowedAddresses)`
30
- - **v6:** Returns 5 values — `(uint256 minimumPrice, uint256 minimumTotalSupply, uint256 maximumTotalSupply, uint256 maximumSplitPercent, address[] memory allowedAddresses)`
31
- - The new `maximumSplitPercent` return value is inserted before `allowedAddresses`. Any consumer destructuring this return value will break.
32
-
33
- ### `ICTPublisher.mintFrom` Parameter Data Location Changed
34
- - **v5:** `CTPost[] memory posts`
35
- - **v6:** `CTPost[] calldata posts`
36
-
37
- ### `CTPost` Struct Has New Fields
38
- - **v5:** `{ bytes32 encodedIPFSUri, uint32 totalSupply, uint104 price, uint24 category }`
39
- - **v6:** `{ bytes32 encodedIPFSUri, uint32 totalSupply, uint104 price, uint24 category, uint32 splitPercent, JBSplit[] splits }`
40
- - Adds `splitPercent` (uint32) and `splits` (JBSplit[] from `@bananapus/core-v6`). This changes the ABI encoding of `CTPost` and all functions that accept it.
41
-
42
- ### `CTAllowedPost` Struct Has New Field
43
- - **v5:** `{ address hook, uint24 category, uint104 minimumPrice, uint32 minimumTotalSupply, uint32 maximumTotalSupply, address[] allowedAddresses }`
44
- - **v6:** `{ address hook, uint24 category, uint104 minimumPrice, uint32 minimumTotalSupply, uint32 maximumTotalSupply, uint32 maximumSplitPercent, address[] allowedAddresses }`
45
- - Adds `maximumSplitPercent` (uint32) before `allowedAddresses`. This changes the ABI encoding of `CTAllowedPost` and all functions that accept it.
46
-
47
- ### `CTDeployerAllowedPost` Struct Has New Field
48
- - **v5:** `{ uint24 category, uint104 minimumPrice, uint32 minimumTotalSupply, uint32 maximumTotalSupply, address[] allowedAddresses }`
49
- - **v6:** `{ uint24 category, uint104 minimumPrice, uint32 minimumTotalSupply, uint32 maximumTotalSupply, uint32 maximumSplitPercent, address[] allowedAddresses }`
50
- - Adds `maximumSplitPercent` (uint32) before `allowedAddresses`, mirroring `CTAllowedPost`.
51
-
52
- ### `CTProjectOwner.onERC721Received` — `projectId` Cast Width Changed
53
- - **v5:** `projectId: uint56(tokenId)`
54
- - **v6:** `projectId: uint64(tokenId)`
55
- - This aligns with the v6 `JBPermissionsData` struct which uses `uint64` for `projectId` (was `uint56` in v5).
56
-
57
- ### `CTDeployer.deployProjectFor` — Data Hook and Cash Out Behavior Changed
58
- - **v5:** Sets `metadata.dataHook = address(hook)` (the 721 hook itself is the data hook). Does NOT set `cashOutTaxRate` or `useDataHookForCashOut`.
59
- - **v6:** Sets `metadata.dataHook = address(this)` (the CTDeployer itself is the data hook). Sets `metadata.cashOutTaxRate = JBConstants.MAX_CASH_OUT_TAX_RATE` and `metadata.useDataHookForCashOut = true`.
60
- - The CTDeployer now acts as a data hook proxy, forwarding pay/cashout calls to the stored `dataHookOf[projectId]`, while intercepting sucker cash outs to grant 0% tax rate. This is a fundamental architectural change.
61
-
62
- > **Why this change**: In v5, the CTDeployer already had the proxy methods (`beforePayRecordedWith`, `beforeCashOutRecordedWith`, `hasMintPermissionFor`) and the `dataHookOf` mapping, but `deployProjectFor` pointed `metadata.dataHook` directly at the 721 hook, bypassing the proxy entirely. v6 activates the proxy so the deployer can intercept sucker cashouts (verified via `SUCKER_REGISTRY.isSuckerOf`) and return a 0% tax rate for cross-chain operations. Without this, cross-chain token bridging via suckers would incur the full `MAX_CASH_OUT_TAX_RATE`, making omnichain projects economically unviable.
63
-
64
- ### `JB721InitTiersConfig` — `prices` Field Removed
65
- - **v5:** `JB721InitTiersConfig({ tiers, currency, decimals, prices: controller.PRICES() })`
66
- - **v6:** `JB721InitTiersConfig({ tiers, currency, decimals })` — the `prices` field no longer exists in the v6 721 hook config struct.
67
-
68
- ### `JB721TiersHookFlags` — New `issueTokensForSplits` Flag
69
- - **v5:** `JB721TiersHookFlags({ noNewTiersWithReserves, noNewTiersWithVotes, noNewTiersWithOwnerMinting, preventOverspending })`
70
- - **v6:** Adds `issueTokensForSplits: false` as a fifth flag.
71
-
72
- ### `ICTDeployer.deployProjectFor` — Parameter Renamed
73
- - **v5:** `projectConfigurations` parameter name
74
- - **v6:** `projectConfig` parameter name
75
-
76
- ---
77
-
78
- ## 2. New Features
79
-
80
- ### Split Percent Support for Posts
81
- Posts can now include a `splitPercent` and an array of `splits` (JBSplit[]) that route a percentage of the tier's price to specified recipients when the NFT is minted. This is enforced against a per-category `maximumSplitPercent` configured by the project owner.
82
-
83
- - `CTPost.splitPercent` — percent of tier price to route to splits (out of `JBConstants.SPLITS_TOTAL_PERCENT`).
84
- - `CTPost.splits` — the split recipients for the tier.
85
- - `CTAllowedPost.maximumSplitPercent` — the maximum split percent a poster can set (0 = splits not allowed).
86
- - `CTDeployerAllowedPost.maximumSplitPercent` — same as above, for deployer-configured posts.
87
- - `JB721TierConfig` in v6 now accepts `splitPercent` and `splits` fields, which are populated from the post.
88
-
89
- ### Duplicate Post Detection
90
- - v6 adds an explicit duplicate check within `_setupPosts`: if two posts in the same batch share the same `encodedIPFSUri`, the transaction reverts with `CTPublisher_DuplicatePost`. This prevents fee evasion by submitting duplicate URIs in a single `mintFrom` call.
91
-
92
- ### Stale Tier Cleanup
93
- - v6 adds logic to detect when a tier referenced by `tierIdForEncodedIPFSUriOf` has been removed externally (via `adjustTiers`). If `hook.STORE().isTierRemoved()` returns true, the stale mapping is deleted and a new tier is created for that URI.
94
-
95
- ### Fee Evasion Prevention for Existing Tiers
96
- - **v5:** When minting from an existing tier, `totalPrice` was accumulated using `post.price` (user-supplied).
97
- - **v6:** When minting from an existing tier, `totalPrice` is accumulated using the actual tier price fetched from `store.tierOf()`. This prevents a caller from passing `price=0` for an existing tier to evade fees.
98
-
99
- ### CTDeployer Data Hook Proxy Activated
100
- - The CTDeployer implemented the data hook proxy pattern in v5 as well -- it had `beforePayRecordedWith`, `beforeCashOutRecordedWith`, `hasMintPermissionFor`, and the `dataHookOf` mapping -- but `deployProjectFor` set `metadata.dataHook = address(hook)` (the 721 hook directly), so the proxy methods were never called. In v6, `deployProjectFor` sets `metadata.dataHook = address(this)`, `cashOutTaxRate = MAX_CASH_OUT_TAX_RATE`, and `useDataHookForCashOut = true`, activating the proxy. This routes all pay and cash out data hook calls through CTDeployer, which forwards them to the stored `dataHookOf[projectId]` while intercepting sucker cash outs (verified via `SUCKER_REGISTRY.isSuckerOf`) to return a 0% tax rate for cross-chain operations.
101
-
102
- ---
103
-
104
- ## 3. Event Changes
105
-
106
- Indexer note:
107
- - event names are stable, but embedded struct payloads changed ABI shape;
108
- - if your graph decodes `ConfigurePostingCriteria` or `Mint`, update the event-decoding schema for the new `maximumSplitPercent`, `splitPercent`, and `splits` fields.
109
-
110
- No event signatures were changed. Both versions emit the same two events:
111
- - `ConfigurePostingCriteria(address indexed hook, CTAllowedPost allowedPost, address caller)` — note that the `CTAllowedPost` struct gained a `maximumSplitPercent` field, which changes the ABI encoding of this event's data.
112
- - `Mint(uint256 indexed projectId, IJB721TiersHook indexed hook, address indexed nftBeneficiary, address feeBeneficiary, CTPost[] posts, uint256 postValue, uint256 txValue, address caller)` — note that the `CTPost` struct gained `splitPercent` and `splits` fields, which changes the ABI encoding of this event's data.
113
-
114
- ---
115
-
116
- ## 4. Error Changes
117
-
118
- ### New Errors
119
-
120
- | Error | Contract | Description |
121
- |-------|----------|-------------|
122
- | `CTPublisher_DuplicatePost(bytes32 encodedIPFSUri)` | `CTPublisher` | Reverts when two posts in the same `mintFrom` batch share the same encoded IPFS URI. |
123
- | `CTPublisher_SplitPercentExceedsMaximum(uint256 splitPercent, uint256 maximumSplitPercent)` | `CTPublisher` | Reverts when a post's split percent exceeds the category's configured maximum. |
124
-
125
- ### Unchanged Errors
126
- - `CTDeployer_NotOwnerOfProject(uint256 projectId, address hook, address caller)` — unchanged.
127
- - `CTPublisher_EmptyEncodedIPFSUri()` — unchanged.
128
- - `CTPublisher_InsufficientEthSent(uint256 expected, uint256 sent)` — signature unchanged; v6 adds an explicit fee validation check before the subtraction (`if (payValue < fee) revert`) so this error now fires with a descriptive message instead of a panic on underflow.
129
- - `CTPublisher_MaxTotalSupplyLessThanMin(uint256 min, uint256 max)` — unchanged.
130
- - `CTPublisher_NotInAllowList(address addr, address[] allowedAddresses)` — unchanged.
131
- - `CTPublisher_PriceTooSmall(uint256 price, uint256 minimumPrice)` — unchanged.
132
- - `CTPublisher_TotalSupplyTooBig(uint256 totalSupply, uint256 maximumTotalSupply)` — unchanged.
133
- - `CTPublisher_TotalSupplyTooSmall(uint256 totalSupply, uint256 minimumTotalSupply)` — unchanged.
134
- - `CTPublisher_UnauthorizedToPostInCategory()` — unchanged.
135
- - `CTPublisher_ZeroTotalSupply()` — unchanged.
136
-
137
- ---
138
-
139
- ## 5. Struct Changes
140
-
141
- ### `CTPost`
142
- | Field | v5 | v6 |
143
- |-------|----|----|
144
- | `encodedIPFSUri` | `bytes32` | `bytes32` |
145
- | `totalSupply` | `uint32` | `uint32` |
146
- | `price` | `uint104` | `uint104` |
147
- | `category` | `uint24` | `uint24` |
148
- | `splitPercent` | -- | `uint32` (new) |
149
- | `splits` | -- | `JBSplit[]` (new) |
150
-
151
- New import: `JBSplit` from `@bananapus/core-v6/src/structs/JBSplit.sol`.
152
-
153
- ### `CTAllowedPost`
154
- | Field | v5 | v6 |
155
- |-------|----|----|
156
- | `hook` | `address` | `address` |
157
- | `category` | `uint24` | `uint24` |
158
- | `minimumPrice` | `uint104` | `uint104` |
159
- | `minimumTotalSupply` | `uint32` | `uint32` |
160
- | `maximumTotalSupply` | `uint32` | `uint32` |
161
- | `maximumSplitPercent` | -- | `uint32` (new) |
162
- | `allowedAddresses` | `address[]` | `address[]` |
163
-
164
- ### `CTDeployerAllowedPost`
165
- | Field | v5 | v6 |
166
- |-------|----|----|
167
- | `category` | `uint24` | `uint24` |
168
- | `minimumPrice` | `uint104` | `uint104` |
169
- | `minimumTotalSupply` | `uint32` | `uint32` |
170
- | `maximumTotalSupply` | `uint32` | `uint32` |
171
- | `maximumSplitPercent` | -- | `uint32` (new) |
172
- | `allowedAddresses` | `address[]` | `address[]` |
173
-
174
- ### `CTProjectConfig`
175
- No field changes. Import path updated from `@bananapus/core-v5` to `@bananapus/core-v6` for `JBTerminalConfig`.
176
-
177
- ### `CTSuckerDeploymentConfig`
178
- No field changes. Import path updated from `@bananapus/suckers-v5` to `@bananapus/suckers-v6` for `JBSuckerDeployerConfig`.
179
-
180
- ---
181
-
182
- ## 6. Implementation Changes (Non-Interface)
183
-
184
- ### `CTPublisher._setupPosts`
185
-
186
- #### Store Reference Caching
187
- - **v5:** Calls `hook.STORE().maxTierIdOf(...)` inline, accessing the store through the hook each time.
188
- - **v6:** Caches `IJB721TiersHookStore store = hook.STORE()` once and reuses it. Also imports `IJB721TiersHookStore` explicitly.
189
-
190
- #### Duplicate Post Detection
191
- - **v6 only:** Adds an O(n^2) check at the start of each post iteration that scans all previous posts for matching `encodedIPFSUri`. Reverts with `CTPublisher_DuplicatePost` on match.
192
-
193
- #### Stale Tier Recovery
194
- - **v5:** If `tierIdForEncodedIPFSUriOf` returns a nonzero tier ID, it is used unconditionally.
195
- - **v6:** Checks `hook.STORE().isTierRemoved(address(hook), tierId)`. If removed, deletes the stale mapping and falls through to create a new tier.
196
-
197
- #### Fee-Accurate Price for Existing Tiers
198
- - **v5:** `totalPrice += post.price` for all posts (new and existing).
199
- - **v6:** For existing tiers, `totalPrice += store.tierOf(...).price` (uses actual on-chain price). For new tiers, `totalPrice += post.price`.
200
-
201
- #### Split Validation
202
- - **v6 only:** Checks `post.splitPercent > maximumSplitPercent` and reverts with `CTPublisher_SplitPercentExceedsMaximum` if exceeded.
203
-
204
- #### `JB721TierConfig` Construction
205
- - **v5:** 14 fields in `JB721TierConfig`.
206
- - **v6:** 16 fields — adds `splitPercent: post.splitPercent` and `splits: post.splits`.
207
-
208
- ### `CTPublisher.allowanceFor` — Packed Storage Layout Extended
209
- - **v5:** Packs 3 fields into `_packedAllowanceFor`: bits 0-103 (minimumPrice), 104-135 (minimumTotalSupply), 136-167 (maximumTotalSupply). Total: 168 bits.
210
- - **v6:** Packs 4 fields: bits 0-103, 104-135, 136-167 as before, plus bits 168-199 (maximumSplitPercent, 32 bits). Total: 200 bits.
211
-
212
- ### `CTPublisher.configurePostingCriteriaFor` — Packs `maximumSplitPercent`
213
- - **v6 only:** Adds `packed |= uint256(allowedPost.maximumSplitPercent) << 168;` when storing allowance data.
214
-
215
- ### `CTDeployer._configurePostingCriteriaFor` — Passes `maximumSplitPercent`
216
- - **v5:** `CTAllowedPost` construction has 6 fields.
217
- - **v6:** `CTAllowedPost` construction has 7 fields — adds `maximumSplitPercent: post.maximumSplitPercent`.
218
-
219
- ### `CTDeployer.deployProjectFor` — Ruleset Configuration Changes
220
- - **v5:** Sets `metadata.dataHook = address(hook)` and `metadata.useDataHookForPay = true`.
221
- - **v6:** Sets `metadata.cashOutTaxRate = JBConstants.MAX_CASH_OUT_TAX_RATE`, `metadata.dataHook = address(this)`, `metadata.useDataHookForPay = true`, and `metadata.useDataHookForCashOut = true`. Imports `JBConstants` for this.
222
-
223
- ### `CTDeployer.deployProjectFor` — Named Arguments in Function Calls
224
- - **v6:** Uses named arguments consistently (e.g., `PROJECTS.transferFrom({from: ..., to: ..., tokenId: ...})` instead of positional arguments).
225
-
226
- ### `CTDeployer` — Function Ordering
227
- - **v5:** `beforePayRecordedWith` appears before `beforeCashOutRecordedWith` in source.
228
- - **v6:** `beforeCashOutRecordedWith` appears before `beforePayRecordedWith`. Similarly, `claimCollectionOwnershipOf` appears before `deployProjectFor` in v6 (reversed from v5).
229
-
230
- ### `CTDeployer.beforeCashOutRecordedWith` — Named Arguments
231
- - **v5:** `SUCKER_REGISTRY.isSuckerOf(context.projectId, context.holder)`
232
- - **v6:** `SUCKER_REGISTRY.isSuckerOf({projectId: context.projectId, addr: context.holder})`
233
-
234
- ### `CTDeployer.hasMintPermissionFor` — Named Arguments
235
- - **v5:** `SUCKER_REGISTRY.isSuckerOf(projectId, addr)`
236
- - **v6:** `SUCKER_REGISTRY.isSuckerOf({projectId: projectId, addr: addr})`
237
-
238
- ### `CTPublisher.tiersFor` — Named Arguments
239
- - **v5:** `IJB721TiersHook(hook).STORE().tierOf(hook, tierId, false)`
240
- - **v6:** `IJB721TiersHook(hook).STORE().tierOf({hook: hook, id: tierId, includeResolvedUri: false})`
241
-
242
- ### `CTPublisher.mintFrom` — Named Arguments
243
- - **v6:** Uses named arguments for `DIRECTORY.primaryTerminalOf(...)`, `hook.adjustTiers(...)`, `JBMetadataResolver.getId(...)`, and `_isAllowed(...)`.
244
-
245
- ### `CTPublisher.mintFrom` — Fee Payment Resilience (Audit Remediation)
246
- - **Previous:** Fee terminal payment was a bare `feeTerminal.pay{value}()` call. If the fee terminal reverted, the entire `mintFrom()` transaction reverted, blocking all mints.
247
- - **Current:** Fee amount is pre-computed as `msg.value - payValue` (no longer relies on `address(this).balance`). The fee terminal payment is wrapped in try-catch. On failure, the fee is sent to `feeBeneficiary` via low-level call. If that also fails, the fee is sent to `msg.sender`. A broken fee terminal never blocks mints.
248
-
249
- ### NatDoc / Comments
250
- - **v6:** Adds extensive NatDoc comments to all interface functions, events, and struct fields. Adds `forge-lint` disable comments for mixed-case variables. Adds explanatory comments for design decisions (e.g., fee rounding behavior, force-sent ETH handling, category irrevocability, linear scan scaling).
251
-
252
- ---
253
-
254
- ## 7. Migration Table
255
-
256
- | v5 Identifier | v6 Identifier | Change |
257
- |---------------|---------------|--------|
258
- | `CTPost.{4 fields}` | `CTPost.{6 fields}` | Added `splitPercent`, `splits` |
259
- | `CTAllowedPost.{6 fields}` | `CTAllowedPost.{7 fields}` | Added `maximumSplitPercent` |
260
- | `CTDeployerAllowedPost.{5 fields}` | `CTDeployerAllowedPost.{6 fields}` | Added `maximumSplitPercent` |
261
- | `ICTPublisher.allowanceFor` (4 returns) | `ICTPublisher.allowanceFor` (5 returns) | Added `maximumSplitPercent` return |
262
- | `ICTPublisher.mintFrom(... CTPost[] memory ...)` | `ICTPublisher.mintFrom(... CTPost[] calldata ...)` | `memory` → `calldata` |
263
- | `CTProjectOwner`: `uint56(tokenId)` | `CTProjectOwner`: `uint64(tokenId)` | Cast width for projectId |
264
- | `CTDeployer`: `dataHook = address(hook)` | `CTDeployer`: `dataHook = address(this)` | CTDeployer is now the data hook |
265
- | `CTDeployer`: no cashout config | `CTDeployer`: `cashOutTaxRate = MAX`, `useDataHookForCashOut = true` | Enables sucker 0% tax cashout |
266
- | `JB721InitTiersConfig`: has `prices` | `JB721InitTiersConfig`: no `prices` | Field removed in v6 721 hook |
267
- | `JB721TiersHookFlags`: 4 flags | `JB721TiersHookFlags`: 5 flags | Added `issueTokensForSplits` |
268
- | -- | `CTPublisher_DuplicatePost` | New error |
269
- | -- | `CTPublisher_SplitPercentExceedsMaximum` | New error |
270
- | Solidity `0.8.23` | Solidity `0.8.28` | Compiler bump |
271
- | `@bananapus/*-v5` | `@bananapus/*-v6` | All dependency namespaces |
272
-
273
- > **Cross-repo impact**: The `CTPost.splitPercent` and `splits` fields feed directly into `nana-721-hook-v6`'s tier splits system. `nana-suckers-v6` suckers are detected via `SUCKER_REGISTRY.isSuckerOf` for the 0% tax cashout path. `nana-permission-ids-v6` `uint64` projectId width change drove the `CTProjectOwner` cast update.