@croptop/core-v6 0.0.20 → 0.0.21

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/SKILLS.md CHANGED
@@ -10,7 +10,7 @@ Permissioned NFT publishing system that lets anyone post content as 721 tiers to
10
10
  |----------|------|
11
11
  | `CTPublisher` | Core publishing engine. Validates posts against bit-packed allowances, creates 721 tiers on hooks, mints first copies to posters, and routes fees. Inherits `JBPermissioned`, `ERC2771Context`. |
12
12
  | `CTDeployer` | Factory that deploys a Juicebox project + 721 hook + posting criteria in one transaction. Also acts as `IJBRulesetDataHook` proxy that forwards pay/cash-out calls to the underlying hook while granting fee-free cash outs to suckers. |
13
- | `CTProjectOwner` | Receives project ownership NFT and grants `CTPublisher` the `ADJUST_721_TIERS` permission permanently. Locks ownership while keeping posting enabled. |
13
+ | `CTProjectOwner` | Burn-lock contract: receives the project ownership NFT and grants `CTPublisher` the `ADJUST_721_TIERS` permission permanently. Since `CTProjectOwner` has no `transferFrom`, `reconfigure`, or `withdraw` functions, ownership is effectively burned -- no one can reclaim the project NFT, change rulesets, or modify posting criteria after transfer. Use this when you want a fully autonomous project where only community posting (within pre-set criteria) is possible. If you need to retain the ability to reconfigure the project, adjust tiers, or withdraw funds, keep ownership yourself (or use a multisig) instead. Configure all desired posting criteria **before** transferring the project NFT here, as they become immutable once ownership moves. Accepts both mints and `safeTransferFrom` calls originating from the `PROJECTS` contract. |
14
14
 
15
15
  ## Key Functions
16
16
 
@@ -40,15 +40,15 @@ Permissioned NFT publishing system that lets anyone post content as 721 tiers to
40
40
 
41
41
  | Function | What it does |
42
42
  |----------|-------------|
43
- | `CTDeployer.beforePayRecordedWith(context)` | Forwards pay context to the stored `dataHookOf[projectId]` (typically the 721 tiers hook). |
44
- | `CTDeployer.beforeCashOutRecordedWith(context)` | Returns zero tax rate for sucker addresses (fee-free cross-chain cash outs). Otherwise forwards to the stored data hook. |
43
+ | `CTDeployer.beforePayRecordedWith(context)` | Forwards pay context to the stored `dataHookOf[projectId]` (typically the 721 tiers hook). Hook specifications returned include a `noop` field — the 721 hook always returns `noop: false`. |
44
+ | `CTDeployer.beforeCashOutRecordedWith(context)` | Returns zero tax rate for sucker addresses (fee-free cross-chain cash outs). Otherwise forwards to the stored data hook. Forwarded hook specifications preserve the inner hook's `noop` flag. |
45
45
  | `CTDeployer.hasMintPermissionFor(projectId, ruleset, addr)` | Returns `true` if `addr` is a sucker for the project. |
46
46
 
47
47
  ### Burn-Lock Ownership
48
48
 
49
49
  | Function | What it does |
50
50
  |----------|-------------|
51
- | `CTProjectOwner.onERC721Received(operator, from, tokenId, data)` | On receiving the project NFT, grants `CTPublisher` the `ADJUST_721_TIERS` permission for that project. Only accepts mints from `PROJECTS` (rejects direct transfers). |
51
+ | `CTProjectOwner.onERC721Received(operator, from, tokenId, data)` | On receiving the project NFT, grants `CTPublisher` the `ADJUST_721_TIERS` permission for that project. Accepts both mints and transfers from `PROJECTS` (reverts if `msg.sender` is not `PROJECTS`). |
52
52
 
53
53
  ## Integration Points
54
54
 
@@ -65,10 +65,10 @@ Permissioned NFT publishing system that lets anyone post content as 721 tiers to
65
65
 
66
66
  | Struct | Key Fields | Used In |
67
67
  |--------|------------|---------|
68
- | `CTAllowedPost` | `hook`, `category` (uint24), `minimumPrice` (uint104), `minimumTotalSupply` (uint32), `maximumTotalSupply` (uint32), `maximumSplitPercent` (uint32), `allowedAddresses[]` | `configurePostingCriteriaFor` |
68
+ | `CTAllowedPost` | `hook`, `category` (uint24), `minimumPrice` (uint104), `minimumTotalSupply` (uint32), `maximumTotalSupply` (uint32), `maximumSplitPercent` (uint32), `allowedAddresses[]` | `configurePostingCriteriaFor` -- used when calling `CTPublisher` directly on an existing hook |
69
+ | `CTDeployerAllowedPost` | Same fields as `CTAllowedPost` minus `hook` | `CTProjectConfig.allowedPosts` -- used during `deployProjectFor` because the hook address is not yet known; `CTDeployer._configurePostingCriteriaFor` fills in the `hook` field automatically after deploying the hook |
69
70
  | `CTPost` | `encodedIPFSUri` (bytes32), `totalSupply` (uint32), `price` (uint104), `category` (uint24), `splitPercent` (uint32), `splits[]` (JBSplit[]) | `mintFrom` |
70
71
  | `CTProjectConfig` | `terminalConfigurations`, `projectUri`, `allowedPosts` (CTDeployerAllowedPost[]), `contractUri`, `name`, `symbol`, `salt` | `deployProjectFor` |
71
- | `CTDeployerAllowedPost` | Same as `CTAllowedPost` minus `hook` (inferred during deployment) | `CTProjectConfig.allowedPosts` |
72
72
  | `CTSuckerDeploymentConfig` | `deployerConfigurations` (JBSuckerDeployerConfig[]), `salt` | `deployProjectFor`, `deploySuckersFor` |
73
73
 
74
74
  ## Events
@@ -92,6 +92,7 @@ Permissioned NFT publishing system that lets anyone post content as 721 tiers to
92
92
  | `CTPublisher_TotalSupplyTooBig` | Post `totalSupply > maximumTotalSupply` (when max > 0) |
93
93
  | `CTPublisher_UnauthorizedToPostInCategory` | Category unconfigured (`minSupply == 0`) |
94
94
  | `CTPublisher_ZeroTotalSupply` | `configurePostingCriteriaFor` with `minimumTotalSupply == 0` |
95
+ | `CTPublisher_DuplicatePost(bytes32 encodedIPFSUri)` | Same `encodedIPFSUri` appears more than once within the same `mintFrom` batch |
95
96
  | `CTDeployer_NotOwnerOfProject` | `claimCollectionOwnershipOf` called by non-owner |
96
97
 
97
98
  ## Constants
@@ -103,12 +104,23 @@ Permissioned NFT publishing system that lets anyone post content as 721 tiers to
103
104
 
104
105
  ## Storage
105
106
 
106
- | Mapping | Type | Purpose |
107
- |---------|------|---------|
107
+ ### CTPublisher
108
+
109
+ | Variable | Type | Purpose |
110
+ |----------|------|---------|
108
111
  | `tierIdForEncodedIPFSUriOf` | `hook => encodedIPFSUri => uint256` | Maps IPFS URI to existing tier ID (prevents duplicates) |
109
112
  | `_packedAllowanceFor` | `hook => category => uint256` | Bit-packed allowance: price (0-103), minSupply (104-135), maxSupply (136-167), maxSplitPercent (168-199) |
110
113
  | `_allowedAddresses` | `hook => category => address[]` | Per-category address allowlist |
111
- | `dataHookOf` | `projectId => IJBRulesetDataHook` | Stores original data hook (CTDeployer proxy pattern) |
114
+
115
+ ### CTDeployer
116
+
117
+ | Variable | Type | Purpose |
118
+ |----------|------|---------|
119
+ | `PROJECTS` | `IJBProjects` (immutable) | ERC-721 contract for Juicebox project ownership |
120
+ | `DEPLOYER` | `IJB721TiersHookDeployer` (immutable) | Factory for deploying 721 tiers hooks |
121
+ | `PUBLISHER` | `ICTPublisher` (immutable) | CTPublisher instance used for posting |
122
+ | `SUCKER_REGISTRY` | `IJBSuckerRegistry` (immutable) | Registry for cross-chain sucker deployment and lookup |
123
+ | `dataHookOf` | `projectId => IJBRulesetDataHook` | Stores original data hook per project (CTDeployer proxy pattern) |
112
124
 
113
125
  ## Gotchas
114
126
 
@@ -119,12 +131,12 @@ Permissioned NFT publishing system that lets anyone post content as 721 tiers to
119
131
  5. **Tier reuse by IPFS URI.** If an encoded IPFS URI was already minted on the hook, the existing tier ID is reused instead of creating a new tier. The poster still gets a mint of the existing tier. The fee is calculated from the actual tier price stored on-chain (not from `post.price`), preventing fee evasion (H-19 fix).
120
132
  6. **Stale tier mapping cleanup.** If a tier was removed externally via `adjustTiers()`, the `tierIdForEncodedIPFSUriOf` mapping is automatically cleared when the same IPFS URI is posted again, allowing a new tier to be created (L-52 fix).
121
133
  7. **Array resizing via assembly.** `_setupPosts` resizes `tiersToAdd` via inline assembly when some posts reuse existing tiers. The `tierIdsToMint` array is NOT resized and may contain zeros for pre-existing tiers.
122
- 8. **CTProjectOwner only accepts mints.** `onERC721Received` reverts if `from != address(0)` -- it only accepts tokens minted by `PROJECTS`, not transferred directly. But external project NFT transfers (where `from` is the previous owner) DO work since the hook is on `CTProjectOwner`, not `CTDeployer`.
134
+ 8. **CTProjectOwner only accepts from PROJECTS contract.** `onERC721Received` reverts if `msg.sender != address(PROJECTS)`. It does NOT check the `from` address, so both mints and transfers through `PROJECTS.safeTransferFrom` are accepted.
123
135
  9. **CTDeployer rejects direct transfers.** `CTDeployer.onERC721Received` reverts if `from != address(0)`. It only accepts mints from `PROJECTS`.
124
136
  10. **Temporary ownership during deployment.** `CTDeployer` owns the project NFT temporarily during `deployProjectFor` (to configure permissions and hooks), then transfers it to the specified `owner`. If the transfer reverts, the entire deployment fails.
125
137
  11. **Data hook proxy pattern.** `CTDeployer` wraps itself as the data hook, forwarding to `dataHookOf[projectId]`. This is needed to intercept cash-out calls and grant fee-free cash outs to suckers. Both `useDataHookForPay` and `useDataHookForCashOut` are enabled (M-37 fix).
126
138
  12. **Sucker registry trust.** `CTDeployer.beforeCashOutRecordedWith` trusts `SUCKER_REGISTRY.isSuckerOf` to determine fee exemption. If the registry is compromised, any address could cash out without tax.
127
- 13. **Allowlist uses linear scan.** `_isAllowed()` iterates the full allowlist array. Acceptable for <100 addresses; gas cost scales linearly with list size.
139
+ 13. **Allowlist uses linear scan.** `_isAllowed()` iterates the full allowlist array. Acceptable for <100 addresses; gas cost scales linearly with list size. This also affects `mintFrom` with large post batches: each post in the batch triggers a separate allowlist scan, so gas scales as `O(posts * allowlistSize)`. Keep batches under ~20 posts with allowlists under ~50 addresses to stay within block gas limits.
128
140
  14. **Referral ID in metadata.** `FEE_PROJECT_ID` is stored in the first 32 bytes of mint metadata (via assembly `mstore`), allowing the fee terminal to track referrals.
129
141
  15. **Deterministic deployment.** Hook salt is `keccak256(abi.encode(projectConfig.salt, msg.sender))` and sucker salt is `keccak256(abi.encode(suckerConfig.salt, msg.sender))`. Different callers with the same salt get different addresses.
130
142
  16. **Default project weight.** `CTDeployer` deploys projects with `weight = 1_000_000 * 10^18`, ETH currency, and `maxCashOutTaxRate`. These defaults are hardcoded.
package/STYLE_GUIDE.md CHANGED
@@ -21,7 +21,7 @@ One contract/interface/struct/enum per file. Name the file after the type it con
21
21
 
22
22
  ```solidity
23
23
  // Contracts — pin to exact version
24
- pragma solidity 0.8.26;
24
+ pragma solidity ^0.8.26;
25
25
 
26
26
  // Interfaces, structs, enums — caret for forward compatibility
27
27
  pragma solidity ^0.8.0;