@croptop/core-v6 0.0.32 → 0.0.34

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 CHANGED
@@ -1,174 +1,94 @@
1
1
  # Administration
2
2
 
3
- Admin privileges and their scope in croptop-core-v6.
4
-
5
3
  ## At A Glance
6
4
 
7
5
  | Item | Details |
8
- |------|---------|
9
- | Scope | Croptop project deployment, posting-criteria management, publisher permissions, and project burn-lock ownership flows. |
10
- | Operators | Project owners, hook owners and delegates, the `CTDeployer`, `CTPublisher`, optional `CTProjectOwner`, and the configured sucker registry. |
11
- | Highest-risk actions | Sending a project NFT to `CTProjectOwner`, misconfiguring posting criteria, or relying on the write-once `dataHookOf` mapping without validating the hook first. |
12
- | Recovery posture | Ownership burn-lock mistakes are not recoverable in place. Operational fixes usually mean updating criteria if the project is still controlled, or deploying a new project/hook if it is not. |
13
-
14
- ## Routine Operations
15
-
16
- - Configure posting criteria before broad publisher access, because `mintFrom()` enforces those rules for every post.
17
- - Use `claimCollectionOwnershipOf()` when the project should own its hook directly instead of leaving hook control with the deployer path.
18
- - Treat sucker deployment as a project extension that still depends on the Croptop data-hook proxy remaining correct for the project.
19
- - Avoid transferring project NFTs to `CTProjectOwner` unless the intention is to burn human control permanently.
6
+ | --- | --- |
7
+ | Scope | Croptop deployment flow, publish-policy administration, and irreversible project owner sink behavior |
8
+ | Control posture | Mixed deployer-managed and project-local control |
9
+ | Highest-risk actions | Burn-locking a project into `CTProjectOwner`, misconfiguring posting criteria, and deploying suckers with the wrong authority assumptions |
10
+ | Recovery posture | Posting policy can often be changed, but burn-lock and some deployer wiring choices require replacement flows |
20
11
 
21
- ## One-Way Or High-Risk Actions
12
+ ## Purpose
22
13
 
23
- - `CTProjectOwner` permanently locks any JBProjects NFT it receives.
24
- - `dataHookOf[projectId]` is set during deployment and has no later setter.
25
- - Constructor-time wildcard permissions granted by `CTDeployer` are structural and cannot be revoked from within the deployer.
14
+ `croptop-core-v6` has two distinct control planes: project-local publishing control and deployer-level structural wiring. The high-risk surfaces are posting criteria, hook ownership, publisher permissions, and the irreversible `CTProjectOwner` burn-lock path.
26
15
 
27
- ## Recovery Notes
16
+ ## Control Model
28
17
 
29
- - If posting rules are wrong but the project still controls the hook, fix them through the hook-owner surface.
30
- - If ownership was accidentally burned into `CTProjectOwner` or the wrong hook path was deployed, recovery generally means abandoning that control path and redeploying the project or hook composition.
18
+ - `CTPublisher` enforces publish policy but does not own the project.
19
+ - `CTDeployer` is both a deployment helper and a live ruleset data-hook wrapper.
20
+ - The initial project owner receives direct hook-management permissions from `CTDeployer` at deployment time.
21
+ - Project owners or delegates administer publishing through the hook owner and `JBPermissions`.
22
+ - `CTProjectOwner` is an irreversible ownership sink for projects that want Croptop-mediated control.
23
+ - `SUCKER_REGISTRY` and `PUBLISHER` receive structural permissions from `CTDeployer`.
31
24
 
32
25
  ## Roles
33
26
 
34
- ### 1. Project Owner
35
-
36
- **How assigned:** Receives the JBProjects ERC-721 NFT for the project. Initially set by the `owner` parameter in `CTDeployer.deployProjectFor()`. Can be transferred via standard ERC-721 transfer.
37
-
38
- **Scope:** Per-project. Controls posting rules and hook ownership for a single project.
39
-
40
- ### 2. Hook Owner
41
-
42
- **How assigned:** Determined by `JBOwnable(hook).owner()`. For CTDeployer-launched projects, the deployer initially owns the hook (via `DEPLOYER.deployHookFor()`). The project owner can later claim hook ownership via `claimCollectionOwnershipOf()`.
43
-
44
- **Scope:** Per-hook. The hook owner (or anyone with `ADJUST_721_TIERS` permission for that hook's project) can configure posting criteria.
45
-
46
- ### 3. CTDeployer (Contract)
47
-
48
- **How assigned:** Immutable singleton deployed at construction. Acts as the `IJBRulesetDataHook` for all CTDeployer-launched projects (set in `deployProjectFor()`).
49
-
50
- **Scope:** All Croptop-deployed projects. Proxies pay/cashout data hook calls, grants fee-free cashouts to suckers, and holds broad permissions on behalf of launched projects.
51
-
52
- ### 4. CTPublisher (Contract)
53
-
54
- **How assigned:** Immutable singleton deployed at construction. Receives `ADJUST_721_TIERS` permission from CTDeployer at construction.
55
-
56
- **Scope:** All hooks for which it has `ADJUST_721_TIERS` permission. Creates NFT tiers and mints first copies.
57
-
58
- ### 5. CTProjectOwner (Contract)
59
-
60
- **How assigned:** Optional burn-lock proxy. Receives project ownership when a project NFT is `safeTransferFrom`'d to it.
61
-
62
- **Scope:** Per-project. Grants `CTPublisher` permanent `ADJUST_721_TIERS` permission for the received project. Once the project is transferred here, human ownership is effectively burned.
63
-
64
- - **Important:** `onERC721Received()` accepts project NFTs from any transfer, not only mints. If a project owner accidentally transfers their project NFT to `CTProjectOwner`, it is permanently locked -- there is no recovery function. The only check is that `msg.sender` is the `PROJECTS` contract (ensuring it is a JBProjects NFT, not an arbitrary ERC-721).
65
-
66
- ### 6. Sucker Registry
67
-
68
- **How assigned:** Immutable dependency set at CTDeployer construction. Receives `MAP_SUCKER_TOKEN` permission at construction.
69
-
70
- **Scope:** All projects deployed via CTDeployer. Can map tokens for cross-chain bridging. Determines which addresses get fee-free cashouts.
27
+ | Role | How Assigned | Scope | Notes |
28
+ | --- | --- | --- | --- |
29
+ | Project owner | `JBProjects.ownerOf(projectId)` | Per project | May grant delegates through `JBPermissions` |
30
+ | Hook owner | `JBOwnable(hook).owner()` | Per hook | Often resolves to the project owner after claim |
31
+ | `CTDeployer` | Immutable singleton | Global | Launch helper and runtime wrapper |
32
+ | `CTPublisher` | Immutable singleton | Global runtime surface | Needs `ADJUST_721_TIERS` authority on relevant hooks |
33
+ | `CTProjectOwner` | Receives project NFT transfer | Per project | Burn-lock path; no return function |
34
+ | `SUCKER_REGISTRY` | Immutable dependency | Global | Holds wildcard `MAP_SUCKER_TOKEN` from the deployer |
71
35
 
72
- ### 7. Publishers (Poster Addresses)
36
+ ## Privileged Surfaces
73
37
 
74
- **How assigned:** Either any address (when allowlist is empty) or explicitly added to a per-hook per-category allowlist via `configurePostingCriteriaFor()`.
38
+ | Contract | Function | Who Can Call | Effect |
39
+ | --- | --- | --- | --- |
40
+ | `CTDeployer` | `deployProjectFor(...)` | Anyone | Launches a Croptop-shaped project and configures initial permissions |
41
+ | `CTDeployer` | `claimCollectionOwnershipOf(...)` | Current project owner | Transfers hook ownership from the deployer path to the project |
42
+ | `CTDeployer` | `deploySuckersFor(...)` | Project owner or `DEPLOY_SUCKERS` delegate | Extends a project with suckers |
43
+ | `CTPublisher` | `configurePostingCriteriaFor(...)` | Hook owner or `ADJUST_721_TIERS` delegate | Changes posting policy for a hook and category |
44
+ | `CTPublisher` | `mintFrom(...)` | Anyone subject to policy | Publishes posts, mints first copies, and routes the Croptop fee |
45
+ | `CTProjectOwner` | `onERC721Received(...)` | Any project NFT transfer into it | Locks the project into the Croptop owner helper and grants `CTPublisher` tier-adjust authority |
75
46
 
76
- **Scope:** Per-hook, per-category. Can create NFT tiers (posts) and mint first copies, subject to posting criteria.
47
+ The important nuance is:
77
48
 
78
- ## Privileged Functions
49
+ - after `deployProjectFor(...)`, the initial project owner can directly manage tiers, metadata, minting, and discount percent through permissions granted from `CTDeployer`
50
+ - this means the owner can bypass the publisher path until ownership is claimed away from `CTDeployer`
79
51
 
80
- ### CTDeployer
52
+ ## Immutable And One-Way
81
53
 
82
- | Function | Required Role | Permission ID | Scope | What It Does |
83
- |----------|--------------|---------------|-------|-------------|
84
- | `deployProjectFor()` | Anyone | None | Global | Deploys a new Juicebox project with 721 hook, configures posting rules, optionally deploys suckers, transfers ownership to `owner`. No access restriction -- anyone can deploy a project. |
85
- | `claimCollectionOwnershipOf()` | Project owner | None (direct `ownerOf` check) | Per-project | Transfers hook ownership to the project via `JBOwnable.transferOwnershipToProject()`. Caller must be `PROJECTS.ownerOf(projectId)`. |
86
- | `deploySuckersFor()` | Project owner or delegate | `JBPermissionIds.DEPLOY_SUCKERS` | Per-project | Deploys new cross-chain suckers for an existing project. Uses `_requirePermissionFrom()` against the project owner. |
54
+ - `CTDeployer`'s wildcard permission grants to `SUCKER_REGISTRY` and `CTPublisher` are structural.
55
+ - `dataHookOf[projectId]` is write-once through deployment flow.
56
+ - Sending a project NFT into `CTProjectOwner` is effectively irreversible.
57
+ - `FEE_PROJECT_ID` in `CTPublisher` is constructor-immutable.
87
58
 
88
- ### CTPublisher
59
+ ## Operational Notes
89
60
 
90
- | Function | Required Role | Permission ID | Scope | What It Does |
91
- |----------|--------------|---------------|-------|-------------|
92
- | `configurePostingCriteriaFor()` | Hook owner or delegate | `JBPermissionIds.ADJUST_721_TIERS` | Per-hook, per-category | Sets posting rules: minimum price, min/max supply, max split percent, address allowlist. Uses `_requirePermissionFrom()` against `JBOwnable(hook).owner()`. |
93
- | `mintFrom()` | Anyone (subject to allowlist) | None (enforced by allowlist in `_setupPosts`) | Per-hook | Publishes posts as 721 tiers, mints first copies, routes 5% fee to `FEE_PROJECT_ID`. Validates all posts against configured criteria. |
61
+ - Validate posting criteria before broad publisher access; the publisher enforces those rules on every post.
62
+ - Decide intentionally whether the project should keep the initial direct-management path or move to project-owned hook control with `claimCollectionOwnershipOf(...)`.
63
+ - Use `claimCollectionOwnershipOf(...)` when the project should own the hook directly instead of relying on the deployer as the ownership bridge.
64
+ - Treat the burn-lock path as governance finality, not convenience.
65
+ - Review Croptop deployer changes as both launch-time and runtime changes.
94
66
 
95
- ### CTProjectOwner
67
+ ## Machine Notes
96
68
 
97
- | Function | Required Role | Permission ID | Scope | What It Does |
98
- |----------|--------------|---------------|-------|-------------|
99
- | `onERC721Received()` | Anyone who transfers a JBProjects NFT | None | Per-project | On receiving a project NFT from `PROJECTS`, grants `CTPublisher` the `ADJUST_721_TIERS` permission for that project. The contract does not restrict this to mint receipts; any transferred JBProjects NFT will be accepted and effectively burn human ownership. |
69
+ - Do not treat `CTDeployer` as a passive script helper; it is also part of the live runtime path.
70
+ - Treat `src/CTPublisher.sol`, `src/CTDeployer.sol`, and `src/CTProjectOwner.sol` as the minimum source set for control-plane crawling.
71
+ - If a project NFT has already been sent to `CTProjectOwner`, stop assuming the original owner can recover it.
100
72
 
101
- ### Permissions Granted at CTDeployer Construction
73
+ ## Recovery
102
74
 
103
- These permissions are set in the CTDeployer constructor and apply to all projects it will ever deploy (wildcard `projectId: 0`):
104
-
105
- | Permission | Granted To | Purpose |
106
- |-----------|-----------|---------|
107
- | `MAP_SUCKER_TOKEN` | `SUCKER_REGISTRY` | Allows the sucker registry to map tokens for cross-chain bridging on any project owned by CTDeployer. |
108
- | `ADJUST_721_TIERS` | `PUBLISHER` (CTPublisher) | Allows CTPublisher to add tiers to any hook on any project owned by CTDeployer. |
109
-
110
- ### Permissions Granted During `deployProjectFor()`
111
-
112
- These permissions are set per-project during deployment:
113
-
114
- | Permission | Granted To | Purpose |
115
- |-----------|-----------|---------|
116
- | `ADJUST_721_TIERS` | `owner` | Allows the project owner to adjust 721 tiers. |
117
- | `SET_721_METADATA` | `owner` | Allows the project owner to update 721 metadata. |
118
- | `MINT_721` | `owner` | Allows the project owner to mint 721 tokens directly. |
119
- | `SET_721_DISCOUNT_PERCENT` | `owner` | Allows the project owner to set tier discount percentages. |
120
-
121
- ## Data Hook Proxy
122
-
123
- When deploying a project, `CTDeployer` sets itself as the project's `dataHook` in the ruleset metadata. It then proxies data hook calls to the project's actual 721 tiers hook:
124
-
125
- - **`beforePayRecordedWith`**: Calls `IJBRulesetDataHook(hook).beforePayRecordedWith(context)` where `hook = dataHookOf[context.projectId]`, then returns the 721 hook's specifications.
126
- - **`beforeCashOutRecordedWith`**: Checks if the caller is a registered sucker via `SUCKER_REGISTRY.isSuckerOf()`. If so, returns 0% cash-out tax (fee-free bridging). Otherwise, delegates to the 721 hook.
127
- - **`hasMintPermissionFor`**: Returns `true` for registered suckers, `false` for all other addresses. Does not delegate to the 721 hook.
128
-
129
- This proxy pattern exists so that CTDeployer can intercept cash-out calls to grant fee-free bridging to suckers while still supporting the 721 hook's NFT minting logic.
130
-
131
- The `dataHookOf[projectId]` mapping is write-once (set during `deployProjectFor`, no setter function). The proxy target cannot be changed after deployment.
132
-
133
- ## Immutable Configuration
134
-
135
- These values are set at deploy time and cannot be changed after deployment:
136
-
137
- | Value | Contract | Set At | Description |
138
- |-------|----------|--------|-------------|
139
- | `FEE_DIVISOR` | CTPublisher | Compile time (constant = 20) | Fee percentage: 5% (1/20). Hardcoded, not configurable. |
140
- | `FEE_PROJECT_ID` | CTPublisher | Constructor (immutable) | Project ID that receives all fees. Cannot be changed. |
141
- | `DIRECTORY` | CTPublisher | Constructor (immutable) | JBDirectory for project/terminal lookups. |
142
- | `PROJECTS` | CTDeployer | Constructor (immutable) | JBProjects NFT contract. |
143
- | `DEPLOYER` | CTDeployer | Constructor (immutable) | JB721TiersHookDeployer for hook creation. |
144
- | `PUBLISHER` | CTDeployer | Constructor (immutable) | CTPublisher contract reference. |
145
- | `SUCKER_REGISTRY` | CTDeployer | Constructor (immutable) | Sucker registry for cross-chain bridging. |
146
- | `PERMISSIONS` | CTDeployer, CTProjectOwner | Constructor (immutable) | JBPermissions contract for access control. |
147
- | `trustedForwarder` | CTDeployer, CTPublisher | Constructor (immutable via ERC2771Context) | Meta-transaction trusted forwarder address. |
148
- | `dataHookOf[projectId]` | CTDeployer | `deployProjectFor()` | Once set during deployment, the data hook for a project can never be changed. Write-once storage. |
149
- | Project weight | CTDeployer | `deployProjectFor()` | Hardcoded at `1_000_000 * 10^18` with ETH base currency and max cashout tax rate. |
150
- | Hook deploy salt | CTDeployer | `deployProjectFor()` | `keccak256(abi.encode(salt, msg.sender))` -- deterministic but caller-specific. |
75
+ - If posting policy is wrong but the project still controls the hook, fix it through `configurePostingCriteriaFor(...)`.
76
+ - If the wrong hook path or burn-lock path was chosen, recovery usually means a new project or new hook arrangement.
77
+ - `CTProjectOwner` is not a reversible safety valve.
151
78
 
152
79
  ## Admin Boundaries
153
80
 
154
- What admins CANNOT do:
155
-
156
- 1. **Project owners cannot change the fee rate.** `FEE_DIVISOR = 20` (5%) is a compile-time constant. No function exists to modify it.
157
-
158
- 2. **Project owners cannot change the fee recipient.** `FEE_PROJECT_ID` is immutable. Fees always route to the same project.
159
-
160
- 3. **Project owners cannot change the data hook.** `dataHookOf[projectId]` is write-once (set during `deployProjectFor`, no setter function). The data hook proxy pattern is permanent.
161
-
162
- 4. **Project owners cannot disable Croptop posting entirely for a category.** `configurePostingCriteriaFor()` requires `minimumTotalSupply > 0`. The workaround is to set an astronomically high `minimumPrice` with `minimumTotalSupply = maximumTotalSupply = 1`. See finding NM-006.
163
-
164
- 5. **Project owners cannot bypass posting criteria through `CTPublisher`, but they may still bypass the publisher surface entirely.** `mintFrom()` enforces all configured rules. Separately, the initial owner/operator can hold direct hook-management permissions from `CTDeployer`, which lets them adjust tiers or mint without going through `CTPublisher` until ownership is claimed away or permissions are narrowed.
165
-
166
- 6. **CTPublisher cannot mint without paying.** `mintFrom()` requires `msg.value >= totalPrice + fee`. There is no free-mint path through CTPublisher.
167
-
168
- 7. **CTProjectOwner cannot return project ownership.** Once a project NFT is transferred to CTProjectOwner, there is no function to transfer it back. Ownership is effectively burned.
169
-
170
- 8. **No admin can modify existing tier prices.** Once a tier is created via `_setupPosts()`, the price is set in the `JB721TiersHookStore`. CTPublisher uses the stored price for fee calculation on subsequent mints (not `post.price`). See H-19 fix.
81
+ - Neither project owners nor Croptop can change the fixed Croptop fee divisor in `CTPublisher`.
82
+ - `CTPublisher` cannot trap fee ETH intentionally; failed fee-terminal payments refund `_msgSender()` or revert.
83
+ - `CTProjectOwner` cannot return project ownership once it receives the NFT.
84
+ - `CTDeployer` cannot later rewrite `dataHookOf[projectId]` through a setter.
85
+ - `CTDeployer` does not stop the initial project owner from using the directly granted hook permissions before ownership is claimed away.
171
86
 
172
- 9. **No admin can drain CTPublisher funds.** CTPublisher has no `withdraw()` function and no `receive()` / `fallback()`. The only ETH that enters the contract is during `mintFrom()` and it is fully routed to the project terminal and fee terminal (or refunded to the caller if the fee terminal reverts) within the same transaction. If that refund also fails, the mint reverts rather than trapping ETH in the publisher.
87
+ ## Source Map
173
88
 
174
- 10. **Sucker registry trust is irrevocable.** The `MAP_SUCKER_TOKEN` permission is granted at CTDeployer construction with `projectId: 0` (wildcard). There is no function to revoke this permission from within CTDeployer.
89
+ - `src/CTPublisher.sol`
90
+ - `src/CTDeployer.sol`
91
+ - `src/CTProjectOwner.sol`
92
+ - `script/Deploy.s.sol`
93
+ - `script/helpers/CroptopDeploymentLib.sol`
94
+ - `test/TestAuditGaps.sol`
package/ARCHITECTURE.md CHANGED
@@ -2,70 +2,95 @@
2
2
 
3
3
  ## Purpose
4
4
 
5
- `croptop-core-v6` turns a Juicebox project with a 721 tiers hook into a permissioned publishing surface. Project owners define what kinds of posts are allowed, and third parties can mint new content tiers only if their post matches those rules. A fixed fee is routed to a designated fee project on every publish.
5
+ `croptop-core-v6` turns a Juicebox project with a 721 tiers hook into a permissioned publishing market. Project owners define what posts are valid, third parties publish content by minting or reusing tiers, and Croptop routes a fixed publish fee to the canonical fee project.
6
6
 
7
- ## Boundaries
7
+ ## System Overview
8
8
 
9
- - `CTPublisher` owns publishing policy and fee routing.
10
- - `CTDeployer` owns one-shot project creation and optional sucker setup.
11
- - The underlying 721 tier implementation remains in `nana-721-hook-v6`.
12
- - The repo does not reimplement terminal accounting; payments still settle through Juicebox terminals.
9
+ `CTPublisher` is the runtime policy and fee-routing surface. `CTDeployer` is the launch-time wrapper that can package a project, its 721 hook configuration, posting rules, and optional omnichain setup in one transaction. `CTProjectOwner` is the irreversible ownership helper for projects that want Croptop-mediated administration instead of a plain owner EOA.
13
10
 
14
- ## Main Components
11
+ ## Core Invariants
15
12
 
16
- | Component | Responsibility |
17
- | --- | --- |
18
- | `CTPublisher` | Validates posts, creates or reuses tiers, mints the first copy, and routes publish fees |
19
- | `CTDeployer` | Launches a Juicebox project plus hook configuration in one transaction and can proxy hook callbacks |
20
- | `CTProjectOwner` | Burn-lock helper that permanently delegates tier adjustment authority to Croptop |
21
- | `CTAllowedPost`, `CTPost`, related structs | Encode project-level publishing policy and publish requests |
13
+ - A post can only be published if it satisfies the configured category, pricing, supply, split, and allowlist rules.
14
+ - Publish fees must be computed from the call value, not from ambient contract balance.
15
+ - `CTPublisher` must not trap fee funds. If the fee-project payment fails, the fee is refunded to `_msgSender()`, and if that refund fails the publish reverts.
16
+ - Tier creation and minting must continue to respect `nana-721-hook-v6` invariants.
17
+ - `CTDeployer` intentionally creates a temporary owner-bypass period before collection ownership is claimed away from the deployer.
18
+ - `CTProjectOwner` is a burn-lock primitive, not a flexible admin panel.
22
19
 
23
- ## Runtime Model
20
+ ## Modules
24
21
 
25
- ### Publishing
22
+ | Module | Responsibility | Notes |
23
+ | --- | --- | --- |
24
+ | `CTPublisher` | Post validation, tier reuse or creation, first-copy minting, fee routing | Main runtime contract |
25
+ | `CTDeployer` | Project launch, hook wiring, optional sucker setup, wrapper behavior | Launch-time and runtime wrapper |
26
+ | `CTProjectOwner` | Irreversible ownership helper | Governance-sensitive |
27
+ | `CTAllowedPost`, `CTPost`, related structs | Publishing policy and request encoding | Shared config surface |
28
+
29
+ ## Trust Boundaries
30
+
31
+ - Tier storage and minting semantics live in `nana-721-hook-v6`.
32
+ - Terminal accounting and project ownership live in `nana-core-v6`.
33
+ - When omnichain setup is enabled, this repo composes deployer patterns from `nana-suckers-v6` and `nana-omnichain-deployers-v6` instead of reimplementing them.
34
+
35
+ ## Critical Flows
36
+
37
+ ### Publish
26
38
 
27
39
  ```text
28
40
  poster
29
- -> mintFrom(hook, posts, ...)
30
- -> publisher validates each post against project-defined criteria
31
- -> publisher calls the 721 hook to create or reuse tiers
41
+ -> calls mintFrom(...)
42
+ -> publisher validates each post against project policy
43
+ -> publisher creates or reuses 721 tiers
32
44
  -> project terminal receives the publish payment
33
- -> fee project receives the fixed fee slice, or `_msgSender()` is refunded that fee if the fee terminal rejects it
34
- -> first copy of each created tier is minted to the poster
45
+ -> fee project receives the fixed fee slice, or `_msgSender()` is refunded if that fee payment fails
46
+ -> first copy of each post tier is minted to the poster
35
47
  ```
36
48
 
37
- ### Project Launch
49
+ ### Launch
38
50
 
39
51
  ```text
40
52
  creator
41
- -> CTDeployer launches the project, hook, posting criteria, and optional suckers
42
- -> deployer can also stand in as a ruleset data-hook wrapper when needed
53
+ -> CTDeployer launches the project and 721-hook shape
54
+ -> configures Croptop posting rules
55
+ -> optionally wires omnichain sucker deployment
56
+ -> may remain in the flow as a runtime wrapper when hook composition is enabled
43
57
  ```
44
58
 
45
- ## Critical Invariants
46
-
47
- - A post is valid only if it satisfies the configured category, price, supply, split, and allowlist constraints.
48
- - Fee routing must be computed from the payment value, not transient contract balance, so forced ETH cannot distort the fee.
49
- - Fee routing must not strand ETH in `CTPublisher`. If the fee terminal rejects payment, the fee is refunded to `_msgSender()`; if that refund fails, the mint reverts.
50
- - `CTProjectOwner` only makes sense as a lock, not a flexible admin layer. Once a project is burn-locked, Croptop becomes the only intended tier-adjustment path.
51
- - Publishing should not bypass the 721 hook's own invariants around tier creation and minting.
59
+ ## Accounting Model
52
60
 
53
- ## Where Complexity Lives
61
+ This repo does not define treasury accounting. Its critical economic logic is publish-fee routing and the mapping from valid post data to 721 tier creation or reuse.
54
62
 
55
- - Post validation is spread across category rules, split limits, supply bounds, and optional allowlists.
56
- - `CTDeployer` is subtle because it is both a launch helper and, in some flows, a runtime hook proxy.
57
- - Fee routing is intentionally liveness-first: it prefers refunding `_msgSender()` over blocking the mint when the fee terminal is down, but still reverts if the refund itself cannot be delivered.
63
+ `CTPublisher` also relies on duplicate-content and pricing checks to stop fee evasion through batch composition or tier reuse. Those checks are part of economic correctness, not just content hygiene.
58
64
 
59
- ## Dependencies
65
+ ## Security Model
60
66
 
61
- - `nana-721-hook-v6` for tier storage and minting
62
- - `nana-suckers-v6` and `nana-omnichain-deployers-v6` patterns when omnichain deployment is enabled
63
- - `nana-core-v6` terminals, permissions, and project ownership
67
+ - Fee routing is liveness-first but still value-sensitive; fallback refunds must stay correct.
68
+ - `CTDeployer` has a larger review surface than a normal deployer because it can also participate at runtime.
69
+ - Croptop's product boundary is partly social: until collection ownership is claimed away from `CTDeployer`, the project owner can interact through the granted permissions rather than only through the publisher surface.
70
+ - Posting policy bugs are product-level authorization bugs, not just metadata bugs.
64
71
 
65
72
  ## Safe Change Guide
66
73
 
67
- - Keep publishing policy separate from 721 implementation details. If a change is generally useful for all tiered collections, it belongs downstream.
68
- - When modifying fee logic, reason through terminal payment ordering and failure fallback paths together.
69
- - Any change to `CTDeployer` should be reviewed as both a deployer and a live hook wrapper, because it participates in runtime flows after launch.
70
- - Changes to burn-lock semantics should be treated as governance changes, not UI conveniences.
71
- - Be wary of adding product-specific tier semantics here that really belong in the generic 721 hook.
74
+ - Put generic tier logic in `nana-721-hook-v6`, not here.
75
+ - If fee behavior changes, review payment ordering, fee-project fallback, and refund failure handling together.
76
+ - If deployer ownership or permission grants change, re-check the temporary bypass window and post-claim ownership behavior together.
77
+ - If `CTDeployer` changes, test both project launch and any wrapped hook flow it participates in.
78
+ - Treat `CTProjectOwner` changes as governance changes.
79
+
80
+ ## Canonical Checks
81
+
82
+ - publish-path fee routing and policy enforcement:
83
+ `test/CTPublisher.t.sol`
84
+ - fee fallback and refund safety:
85
+ `test/audit/FeeFallbackBlackhole.t.sol`
86
+ - duplicate-content and batch-fee-evasion resistance:
87
+ `test/regression/DuplicateUriFeeEvasion.t.sol`
88
+
89
+ ## Source Map
90
+
91
+ - `src/CTPublisher.sol`
92
+ - `src/CTDeployer.sol`
93
+ - `src/CTProjectOwner.sol`
94
+ - `test/CTPublisher.t.sol`
95
+ - `test/audit/FeeFallbackBlackhole.t.sol`
96
+ - `test/regression/DuplicateUriFeeEvasion.t.sol`
@@ -1,15 +1,15 @@
1
1
  # Audit Instructions
2
2
 
3
- Croptop is a publishing layer on top of Juicebox projects and the 721 hook stack. Audit it as a permissions and fee-routing system, not just a content app.
3
+ Croptop is a publishing layer on top of Juicebox projects and the tiered 721 stack. Audit it as a permissions, fee-routing, and project-launch system.
4
4
 
5
- ## Objective
5
+ ## Audit Objective
6
6
 
7
7
  Find issues that:
8
8
  - let publishers create or mint posts outside configured criteria
9
9
  - let users evade Croptop fees or route them incorrectly
10
10
  - grant fee-free or privileged cash-outs to the wrong actors
11
- - create stale, duplicate, or abusive tier reuse across posts
12
- - break ownership handoff or permanently lock a project in an unintended admin state
11
+ - make tier reuse bypass stale-content, fee, or policy checks
12
+ - leave a project in an unintended ownership or admin state
13
13
 
14
14
  ## Scope
15
15
 
@@ -17,76 +17,68 @@ In scope:
17
17
  - `src/CTPublisher.sol`
18
18
  - `src/CTDeployer.sol`
19
19
  - `src/CTProjectOwner.sol`
20
- - `src/interfaces/`
21
- - `src/structs/`
22
- - deployment scripts in `script/`
23
-
24
- External integrations that matter:
25
- - `nana-core-v6`
26
- - `nana-721-hook-v6`
27
- - `nana-ownable-v6`
28
- - `nana-suckers-v6`
29
-
30
- ## System Model
31
-
32
- Croptop has three roles:
33
- - `CTPublisher`: validates post configuration, creates or adjusts tiers, mints the first copy, and routes fees
34
- - `CTDeployer`: launches a project, wires hook ownership and post criteria, and acts as a data-hook proxy where required
35
- - `CTProjectOwner`: ownership helper for projects that want Croptop-controlled administration
36
-
37
- The system relies on project-specific posting criteria such as:
38
- - minimum price
39
- - supply bounds
40
- - category restrictions
41
- - split limits
42
- - optional address allowlists
20
+ - all interfaces in `src/interfaces/`
21
+ - all structs in `src/structs/`
22
+ - deployment helpers in `script/`
43
23
 
44
- ## Critical Invariants
24
+ ## Start Here
25
+
26
+ 1. `src/CTPublisher.sol`
27
+ 2. `src/CTDeployer.sol`
28
+ 3. `src/CTProjectOwner.sol`
29
+
30
+ ## Security Model
45
31
 
46
- 1. Post criteria are binding
47
- No publish path should bypass configured minimum price, total supply bounds, split caps, or allowlist restrictions.
32
+ Croptop composes several subsystems:
33
+ - `CTPublisher` enforces posting criteria, creates or adjusts tiers, and routes fees
34
+ - `CTDeployer` launches projects and wires hooks, criteria, and ownership helpers
35
+ - `CTProjectOwner` lets a project follow Croptop-specific admin rules instead of a fixed EOA
48
36
 
49
- 2. Fee collection is complete
50
- Each Croptop mint should either pay the configured fee or take the documented fallback path. Users must not be able to mint while underpaying Croptop.
37
+ Trust boundaries that matter:
38
+ - project owners choose policy, but should not be able to bypass the policy they configured
39
+ - fee recipients and external hooks may revert or reenter
40
+ - sucker-based privileges must be limited to genuine omnichain components
51
41
 
52
- 3. Tier reuse is safe
53
- Existing tiers must not be reusable in a way that evades fees, stale criteria, or duplicate-content protections.
42
+ ## Roles And Privileges
54
43
 
55
- 4. Sucker privileges stay narrow
56
- Any cash-out tax exemptions or mint permissions intended for legitimate suckers must not be reachable by arbitrary callers or spoofed registry state.
44
+ | Role | Powers | How constrained |
45
+ |------|--------|-----------------|
46
+ | Project owner | Choose policy and ownership mode | Must not bypass the active policy through helper paths |
47
+ | `CTPublisher` | Create or reuse tiers and route fees | Must stay within configured criteria |
48
+ | `CTDeployer` | Launch projects and wire helpers | Must not retain unexpected post-launch authority |
49
+ | Sucker integration | Access narrow omnichain-only paths | Must be backed by authentic registry state |
57
50
 
58
- 5. Ownership transitions are intentional
59
- Burn-lock or project-owner helper flows must not grant broader privileges than intended or accidentally strand project administration.
51
+ ## Integration Assumptions
60
52
 
61
- ## Threat Model
53
+ | Dependency | Assumption | What breaks if wrong |
54
+ |------------|------------|----------------------|
55
+ | `nana-721-hook-v6` | Tier state and tier adjustments match Croptop policy checks | Posting criteria and tier reuse safety break |
56
+ | `nana-core-v6` | Terminal and project routing are authentic | Fee routing and publish settlement drift |
57
+ | `nana-ownable-v6` | Ownership helper resolves the intended admin | Projects can end up misowned or stranded |
58
+ | `nana-suckers-v6` | Registry identifies genuine omnichain actors | Fee-free or privileged paths widen incorrectly |
59
+
60
+ ## Critical Invariants
62
61
 
63
- Prioritize:
64
- - malicious publishers choosing edge-case prices, split structures, or reused metadata
65
- - malicious project owners misconfiguring rules and then trying to escape them
66
- - fake or stale sucker registrations
67
- - fee-recipient failures that alter control flow
68
- - reentrancy through fee routing or tier-adjustment side effects
62
+ 1. Minimum price, supply bounds, split limits, category restrictions, and allowlists stay binding on every publish path.
63
+ 2. Every Croptop mint either pays the configured fee or takes the documented fallback path without underpaying Croptop.
64
+ 3. Existing tiers cannot be reused in a way that revives stale criteria or dodges fee collection.
65
+ 4. Sucker-only or fee-exempt paths cannot be reached through spoofed registry state or stale deployment wiring.
66
+ 5. Ownership handoff and burn-lock flows do not accidentally widen privileges or strand administration.
69
67
 
70
- ## Hotspots
68
+ ## Attack Surfaces
71
69
 
72
- - `CTPublisher.mintFrom` and its validation pipeline
73
- - any code path that computes fees from user-provided versus on-chain values
74
- - tier creation or adjustment against prior post state
75
- - `CTDeployer` data-hook behavior for pay and cash-out flows
76
- - permission grants made during deployment or project-owner handoff
77
- - any one-way lock or burn-based ownership design
70
+ - publish and mint entrypoints
71
+ - fee computation from user input versus on-chain state
72
+ - tier creation, adjustment, and reuse logic
73
+ - deployer-mediated pay or cash-out data-hook behavior
74
+ - permission grants during deployment and ownership transfer
78
75
 
79
- ## Build And Verification
76
+ ## Accepted Risks Or Behaviors
77
+
78
+ - Fee routing may degrade to a fallback path rather than block publishing entirely.
79
+
80
+ ## Verification
80
81
 
81
- Standard workflow:
82
82
  - `npm install`
83
83
  - `forge build`
84
84
  - `forge test`
85
-
86
- Current tests emphasize:
87
- - fee evasion
88
- - stale tier mappings
89
- - reentrancy and attacker-controlled publish flows
90
- - fork and omnichain composition
91
-
92
- Strong findings usually show either fee loss, unauthorized publishing power, or a project entering a control configuration it cannot safely escape.
package/README.md CHANGED
@@ -4,7 +4,12 @@ Croptop turns a Juicebox project with a 721 hook into a permissioned publishing
4
4
 
5
5
  Docs: <https://docs.juicebox.money>
6
6
  Site: <https://croptop.eth.limo>
7
- Architecture: [ARCHITECTURE.md](./ARCHITECTURE.md)
7
+ Architecture: [ARCHITECTURE.md](./ARCHITECTURE.md)
8
+ User journeys: [USER_JOURNEYS.md](./USER_JOURNEYS.md)
9
+ Skills: [SKILLS.md](./SKILLS.md)
10
+ Risks: [RISKS.md](./RISKS.md)
11
+ Administration: [ADMINISTRATION.md](./ADMINISTRATION.md)
12
+ Audit instructions: [AUDIT_INSTRUCTIONS.md](./AUDIT_INSTRUCTIONS.md)
8
13
 
9
14
  ## Overview
10
15
 
@@ -28,7 +33,7 @@ If a bug looks like ordinary tier issuance or terminal accounting, start in the
28
33
  | --- | --- |
29
34
  | `CTPublisher` | Validates posts, adjusts 721 tiers, mints the first copy, and routes protocol and project payments. |
30
35
  | `CTDeployer` | Launches a project, configures Croptop posting rules, and can wire in omnichain sucker deployments. |
31
- | `CTProjectOwner` | Burn-lock ownership helper that can permanently route project administration through Croptop's publishing surface. |
36
+ | `CTProjectOwner` | Ownership sink that can permanently hold a project NFT while still delegating the posting permissions Croptop needs. |
32
37
 
33
38
  ## Mental Model
34
39
 
@@ -39,6 +44,36 @@ There are two separate concerns here:
39
44
 
40
45
  That distinction matters because many "Croptop bugs" are deployment-shape bugs rather than publishing-rule bugs.
41
46
 
47
+ ## Read These Files First
48
+
49
+ 1. `src/CTPublisher.sol`
50
+ 2. `src/CTDeployer.sol`
51
+ 3. `src/CTProjectOwner.sol`
52
+ 4. `test/CTPublisher.t.sol`
53
+ 5. `test/ClaimCollectionOwnership.t.sol`
54
+
55
+ ## High-Signal Tests
56
+
57
+ 1. `test/CTPublisher.t.sol`
58
+ 2. `test/CTDeployer.t.sol`
59
+ 3. `test/ClaimCollectionOwnership.t.sol`
60
+ 4. `test/audit/FeeFallbackBlackhole.t.sol`
61
+ 5. `test/regression/DuplicateUriFeeEvasion.t.sol`
62
+
63
+ ## Integration Traps
64
+
65
+ - Croptop publishing policy is separate from ordinary 721 tier issuance, so readers often stop in the wrong repo
66
+ - fee routing is part of the publish path and has fallback behavior that affects who must be able to receive ETH
67
+ - `CTProjectOwner` intentionally changes the project's ownership shape and should be reviewed as part of the trust model
68
+ - duplicate-content, stale-tier, and fee-evasion edge cases are first-class surfaces, not only UI concerns
69
+
70
+ ## Where State Lives
71
+
72
+ - posting criteria and publish-side enforcement live in `CTPublisher`
73
+ - deployment-time project wiring lives in `CTDeployer`
74
+ - ownership-sink behavior lives in `CTProjectOwner`
75
+ - actual tier issuance and treasury accounting still live in sibling Juicebox repos
76
+
42
77
  ## Install
43
78
 
44
79
  ```bash
@@ -62,9 +97,9 @@ Useful scripts:
62
97
 
63
98
  ## Deployment Notes
64
99
 
65
- Deployments are handled through Sphinx using the environments configured in the repo scripts. `CTDeployer` can also compose cross-chain sucker deployments when the target publishing project needs omnichain support.
100
+ Deployments are handled through Sphinx using the environments configured in the repo scripts. `CTDeployer` can also compose cross-chain sucker deployments when a nonzero sucker configuration is supplied for the target publishing project.
66
101
 
67
- The deploy script now expects an explicit nonzero `FEE_PROJECT_ID` for canonical deployments. It does not safely
102
+ The deploy script now expects an explicit nonzero `FEE_PROJECT_ID` for production-style deployments. It does not safely
68
103
  autodiscover a fee project by scanning existing project IDs.
69
104
 
70
105
  ## Repository Layout
@@ -89,7 +124,13 @@ script/
89
124
  - posting criteria are only as safe as the project owner configures them
90
125
  - fee routing depends on the designated fee project remaining correctly configured; if its terminal rejects payments,
91
126
  Croptop refunds the fee to `_msgSender()` instead of trapping ETH in `CTPublisher`
92
- - burn-lock ownership is intentionally irreversible and should only be used when immutability is desired
93
- - after burn-locking into `CTProjectOwner`, the previous owner no longer holds the project NFT directly; control is
127
+ - parking a project in `CTProjectOwner` is intentionally irreversible in practice and should only be used when immutability is desired
128
+ - after routing ownership into `CTProjectOwner`, the previous owner no longer holds the project NFT directly; control is
94
129
  intentionally mediated through Croptop's owner helper and hook-admin surface instead of remaining a plain owner EOA
95
130
  - duplicate-content and stale-tier edge cases are guarded by tests, but integrations should still treat metadata reuse carefully
131
+
132
+ ## For AI Agents
133
+
134
+ - Do not describe Croptop as a generic 721 marketplace; it is a rules-driven publishing layer on top of Juicebox.
135
+ - Read `CTPublisher` before `CTDeployer` when the question is about publish eligibility or fee behavior.
136
+ - If the issue is basic tier minting or accounting, move to `nana-721-hook-v6` or `nana-core-v6`.