@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 +65 -145
- package/ARCHITECTURE.md +68 -43
- package/AUDIT_INSTRUCTIONS.md +54 -62
- package/README.md +47 -6
- package/RISKS.md +2 -0
- package/SKILLS.md +7 -2
- package/USER_JOURNEYS.md +86 -35
- package/foundry.toml +2 -0
- package/package.json +31 -31
- package/references/operations.md +2 -2
- package/references/runtime.md +3 -3
- package/src/CTDeployer.sol +116 -126
- package/src/CTPublisher.sol +134 -138
- package/src/interfaces/ICTPublisher.sol +1 -2
- package/src/structs/CTAllowedPost.sol +0 -1
- package/src/structs/CTDeployerAllowedPost.sol +0 -1
- package/src/structs/CTPost.sol +0 -2
- package/src/structs/CTProjectConfig.sol +0 -1
- package/src/structs/CTSuckerDeploymentConfig.sol +0 -1
- package/test/CTDeployer.t.sol +5 -4
- package/test/Fork.t.sol +6 -3
- package/test/TestAuditGaps.sol +9 -8
- package/test/audit/CodexNemesisPoCs.t.sol +263 -0
- package/test/fork/PublishFork.t.sol +6 -3
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
|
|
10
|
-
|
|
|
11
|
-
| Highest-risk actions |
|
|
12
|
-
| Recovery posture |
|
|
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
|
-
##
|
|
12
|
+
## Purpose
|
|
22
13
|
|
|
23
|
-
-
|
|
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
|
-
##
|
|
16
|
+
## Control Model
|
|
28
17
|
|
|
29
|
-
-
|
|
30
|
-
-
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
|
|
36
|
+
## Privileged Surfaces
|
|
73
37
|
|
|
74
|
-
|
|
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
|
-
|
|
47
|
+
The important nuance is:
|
|
77
48
|
|
|
78
|
-
|
|
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
|
-
|
|
52
|
+
## Immutable And One-Way
|
|
81
53
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
|
|
59
|
+
## Operational Notes
|
|
89
60
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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
|
-
|
|
67
|
+
## Machine Notes
|
|
96
68
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
-
|
|
73
|
+
## Recovery
|
|
102
74
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
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
|
-
|
|
87
|
+
## Source Map
|
|
173
88
|
|
|
174
|
-
|
|
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
|
|
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
|
-
##
|
|
7
|
+
## System Overview
|
|
8
8
|
|
|
9
|
-
|
|
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
|
-
##
|
|
11
|
+
## Core Invariants
|
|
15
12
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
##
|
|
20
|
+
## Modules
|
|
24
21
|
|
|
25
|
-
|
|
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(
|
|
30
|
-
-> publisher validates each post against project
|
|
31
|
-
-> publisher
|
|
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
|
|
34
|
-
-> first copy of each
|
|
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
|
-
###
|
|
49
|
+
### Launch
|
|
38
50
|
|
|
39
51
|
```text
|
|
40
52
|
creator
|
|
41
|
-
-> CTDeployer launches the project
|
|
42
|
-
->
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
-
-
|
|
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
|
-
##
|
|
65
|
+
## Security Model
|
|
60
66
|
|
|
61
|
-
-
|
|
62
|
-
- `
|
|
63
|
-
- `
|
|
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
|
-
-
|
|
68
|
-
-
|
|
69
|
-
-
|
|
70
|
-
-
|
|
71
|
-
-
|
|
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`
|
package/AUDIT_INSTRUCTIONS.md
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
# Audit Instructions
|
|
2
2
|
|
|
3
|
-
Croptop is a publishing layer on top of Juicebox projects and the 721
|
|
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
|
-
-
|
|
12
|
-
-
|
|
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
|
|
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
|
-
##
|
|
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
|
-
|
|
47
|
-
|
|
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
|
-
|
|
50
|
-
|
|
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
|
-
|
|
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
|
-
|
|
56
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
-
|
|
67
|
-
|
|
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
|
-
##
|
|
68
|
+
## Attack Surfaces
|
|
71
69
|
|
|
72
|
-
-
|
|
73
|
-
-
|
|
74
|
-
- tier creation
|
|
75
|
-
-
|
|
76
|
-
- permission grants
|
|
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
|
-
##
|
|
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` |
|
|
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
|
|
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
|
|
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
|
-
-
|
|
93
|
-
- after
|
|
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`.
|