@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/ADMINISTRATION.md +28 -1
- package/ARCHITECTURE.md +48 -157
- package/AUDIT_INSTRUCTIONS.md +69 -485
- package/CHANGELOG.md +57 -0
- package/README.md +48 -137
- package/RISKS.md +18 -1
- package/SKILLS.md +28 -187
- package/STYLE_GUIDE.md +56 -17
- package/USER_JOURNEYS.md +37 -708
- package/package.json +3 -3
- package/references/operations.md +25 -0
- package/references/runtime.md +27 -0
- package/script/ConfigureFeeProject.s.sol +0 -1
- package/src/CTPublisher.sol +10 -7
- package/test/CTPublisher.t.sol +10 -7
- package/test/regression/FeeEvasion.t.sol +15 -10
- package/test/regression/StaleTierIdMapping.t.sol +8 -5
- package/CHANGE_LOG.md +0 -273
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@croptop/core-v6",
|
|
3
|
-
"version": "0.0.
|
|
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.
|
|
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.
|
|
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,
|
package/src/CTPublisher.sol
CHANGED
|
@@ -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
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
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
|
});
|
package/test/CTPublisher.t.sol
CHANGED
|
@@ -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
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
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
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
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
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
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.
|