@croptop/core-v6 0.0.33 → 0.0.35
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 +58 -62
- package/README.md +56 -24
- package/RISKS.md +40 -53
- package/SKILLS.md +10 -5
- package/USER_JOURNEYS.md +104 -28
- package/foundry.toml +2 -0
- package/package.json +31 -31
- package/references/operations.md +2 -2
- package/references/runtime.md +3 -3
- package/src/CTPublisher.sol +0 -4
- 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/README.md
CHANGED
|
@@ -1,26 +1,27 @@
|
|
|
1
1
|
# Croptop Core
|
|
2
2
|
|
|
3
|
-
Croptop turns a Juicebox project with a 721 hook into a permissioned publishing marketplace. Project owners define posting
|
|
3
|
+
Croptop turns a Juicebox project with a 721 hook into a permissioned publishing marketplace. Project owners define posting rules, then anyone who meets those rules can publish new NFT tiers and mint the first copy of each post.
|
|
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
|
|
|
11
16
|
Croptop is built around three ideas:
|
|
12
17
|
|
|
13
|
-
- project owners set category-level posting
|
|
18
|
+
- project owners set category-level posting rules such as price floors, supply bounds, split limits, and optional allowlists
|
|
14
19
|
- publishers call `mintFrom` to create or reuse 721 tiers that represent their post
|
|
15
|
-
- a one-click deployer can create a full Juicebox project, its 721 hook
|
|
20
|
+
- a one-click deployer can create a full Juicebox project, its 721 hook config, and its posting rules in one transaction
|
|
16
21
|
|
|
17
|
-
Every mint collects a 5% Croptop fee unless the target project is itself the fee project. If the
|
|
18
|
-
terminal rejects that fee payment, Croptop refunds the fee portion to `_msgSender()` and still lets the publish
|
|
19
|
-
continue. If `_msgSender()` cannot receive ETH, the mint reverts.
|
|
22
|
+
Every mint collects a 5% Croptop fee unless the target project is itself the fee project. If the fee terminal rejects that fee payment, Croptop refunds the fee portion to `_msgSender()` and still lets the publish continue. If `_msgSender()` cannot receive ETH, the mint reverts.
|
|
20
23
|
|
|
21
|
-
Use this repo when the product is
|
|
22
|
-
|
|
23
|
-
If a bug looks like ordinary tier issuance or terminal accounting, start in the 721 hook or core repo first. Croptop is where posting policy, fee routing, and publishing-specific project wiring begin.
|
|
24
|
+
Use this repo when the product is permissioned publishing on top of a Juicebox project. Do not use it for plain 721 tier sales.
|
|
24
25
|
|
|
25
26
|
## Key Contracts
|
|
26
27
|
|
|
@@ -28,16 +29,46 @@ If a bug looks like ordinary tier issuance or terminal accounting, start in the
|
|
|
28
29
|
| --- | --- |
|
|
29
30
|
| `CTPublisher` | Validates posts, adjusts 721 tiers, mints the first copy, and routes protocol and project payments. |
|
|
30
31
|
| `CTDeployer` | Launches a project, configures Croptop posting rules, and can wire in omnichain sucker deployments. |
|
|
31
|
-
| `CTProjectOwner` |
|
|
32
|
+
| `CTProjectOwner` | Ownership sink that can permanently hold a project NFT while still delegating the posting permissions Croptop needs. |
|
|
32
33
|
|
|
33
34
|
## Mental Model
|
|
34
35
|
|
|
35
36
|
There are two separate concerns here:
|
|
36
37
|
|
|
37
|
-
1. `CTPublisher`
|
|
38
|
-
2. `CTDeployer`
|
|
38
|
+
1. `CTPublisher` decides whether a post is allowed and how it becomes a tier
|
|
39
|
+
2. `CTDeployer` decides how a Croptop-flavored project is packaged and launched
|
|
40
|
+
|
|
41
|
+
Many Croptop bugs are really deployment-shape bugs or posting-policy bugs, not generic 721 bugs.
|
|
42
|
+
|
|
43
|
+
## Read These Files First
|
|
44
|
+
|
|
45
|
+
1. `src/CTPublisher.sol`
|
|
46
|
+
2. `src/CTDeployer.sol`
|
|
47
|
+
3. `src/CTProjectOwner.sol`
|
|
48
|
+
4. `test/CTPublisher.t.sol`
|
|
49
|
+
5. `test/ClaimCollectionOwnership.t.sol`
|
|
50
|
+
|
|
51
|
+
## High-Signal Tests
|
|
52
|
+
|
|
53
|
+
1. `test/CTPublisher.t.sol`
|
|
54
|
+
2. `test/CTDeployer.t.sol`
|
|
55
|
+
3. `test/ClaimCollectionOwnership.t.sol`
|
|
56
|
+
4. `test/audit/FeeFallbackBlackhole.t.sol`
|
|
57
|
+
5. `test/regression/DuplicateUriFeeEvasion.t.sol`
|
|
39
58
|
|
|
40
|
-
|
|
59
|
+
## Integration Traps
|
|
60
|
+
|
|
61
|
+
- Croptop publishing policy is separate from ordinary 721 tier issuance
|
|
62
|
+
- fee routing is part of the publish path and its fallback behavior matters
|
|
63
|
+
- `CTProjectOwner` intentionally changes the ownership model and should be reviewed as part of the trust model
|
|
64
|
+
- duplicate-content, stale-tier, and fee-evasion edge cases are runtime behavior, not just UI concerns
|
|
65
|
+
|
|
66
|
+
## Where State Lives
|
|
67
|
+
|
|
68
|
+
- posting criteria and publish-side enforcement live in `CTPublisher`
|
|
69
|
+
- deployment-time project wiring lives in `CTDeployer`
|
|
70
|
+
- ownership-sink behavior lives in `CTProjectOwner`
|
|
71
|
+
- actual tier issuance and treasury accounting still live in sibling Juicebox repos
|
|
41
72
|
|
|
42
73
|
## Install
|
|
43
74
|
|
|
@@ -62,10 +93,7 @@ Useful scripts:
|
|
|
62
93
|
|
|
63
94
|
## Deployment Notes
|
|
64
95
|
|
|
65
|
-
Deployments are handled through Sphinx
|
|
66
|
-
|
|
67
|
-
The deploy script now expects an explicit nonzero `FEE_PROJECT_ID` for canonical deployments. It does not safely
|
|
68
|
-
autodiscover a fee project by scanning existing project IDs.
|
|
96
|
+
Deployments are handled through Sphinx. `CTDeployer` can also compose cross-chain sucker deployments when a nonzero sucker configuration is supplied. The deploy script expects an explicit nonzero `FEE_PROJECT_ID` for production-style deployments.
|
|
69
97
|
|
|
70
98
|
## Repository Layout
|
|
71
99
|
|
|
@@ -87,9 +115,13 @@ script/
|
|
|
87
115
|
## Risks And Notes
|
|
88
116
|
|
|
89
117
|
- posting criteria are only as safe as the project owner configures them
|
|
90
|
-
- fee routing depends on the
|
|
91
|
-
|
|
92
|
-
-
|
|
93
|
-
-
|
|
94
|
-
|
|
95
|
-
|
|
118
|
+
- fee routing depends on the fee project staying correctly configured
|
|
119
|
+
- parking a project in `CTProjectOwner` is effectively irreversible
|
|
120
|
+
- after routing ownership into `CTProjectOwner`, the old owner no longer holds the project NFT directly
|
|
121
|
+
- duplicate-content and stale-tier edge cases are economically relevant, not cosmetic
|
|
122
|
+
|
|
123
|
+
## For AI Agents
|
|
124
|
+
|
|
125
|
+
- Do not describe Croptop as a generic 721 marketplace.
|
|
126
|
+
- Read `CTPublisher` before `CTDeployer` when the question is about publish eligibility or fee behavior.
|
|
127
|
+
- If the issue is basic tier minting or accounting, move to `nana-721-hook-v6` or `nana-core-v6`.
|
package/RISKS.md
CHANGED
|
@@ -4,88 +4,75 @@ This file focuses on the publishing, fee-routing, and hook-composition risks tha
|
|
|
4
4
|
|
|
5
5
|
## How to use this file
|
|
6
6
|
|
|
7
|
-
- Read `Priority risks` first
|
|
7
|
+
- Read `Priority risks` first.
|
|
8
8
|
- Use the detailed sections for contract-level reasoning about posting criteria, fee routing, and deployer composition.
|
|
9
|
-
- Treat `Accepted Behaviors` and `Invariants to Verify` as the
|
|
9
|
+
- Treat `Accepted Behaviors` and `Invariants to Verify` as the boundary between intentional tradeoffs and defects.
|
|
10
10
|
|
|
11
11
|
## Priority risks
|
|
12
12
|
|
|
13
13
|
| Priority | Risk | Why it matters | Primary controls |
|
|
14
14
|
|----------|------|----------------|------------------|
|
|
15
15
|
| P0 | Hook/store and terminal trust | `mintFrom` depends on hook storage and directory terminal resolution; a bad integration can misprice posts or redirect value. | Audit integration assumptions, verify hook/store pairings, and monitor terminal configuration. |
|
|
16
|
-
| P1 | Tier ID race during concurrent posting | `_setupPosts` predicts future tier IDs before `adjustTiers`; concurrent writes can shift those IDs and break the batch. | Application-layer ordering, atomic reverts on mismatch, and operator awareness
|
|
17
|
-
| P1 | Fee-path degradation without mint failure | The fee terminal is fail-open via try/catch, so
|
|
18
|
-
|
|
16
|
+
| P1 | Tier ID race during concurrent posting | `_setupPosts` predicts future tier IDs before `adjustTiers`; concurrent writes can shift those IDs and break the batch. | Application-layer ordering, atomic reverts on mismatch, and operator awareness. |
|
|
17
|
+
| P1 | Fee-path degradation without mint failure | The fee terminal is fail-open via try/catch, so publishing continues even if the fee project temporarily stops receiving revenue. | Terminal health monitoring, fallback-beneficiary handling, and explicit fee-routing checks. |
|
|
19
18
|
|
|
20
19
|
## 1. Trust Assumptions
|
|
21
20
|
|
|
22
|
-
- **Trusted forwarder.** ERC-2771 `_msgSender()` is trusted in both
|
|
23
|
-
- **CTDeployer as permanent data
|
|
24
|
-
- **Sucker registry.** `CTDeployer.beforeCashOutRecordedWith` trusts `SUCKER_REGISTRY.isSuckerOf()` for 0% tax
|
|
25
|
-
- **
|
|
26
|
-
- **
|
|
27
|
-
- **
|
|
21
|
+
- **Trusted forwarder.** ERC-2771 `_msgSender()` is trusted in both publisher and deployer for permission checks, allowlists, and payment routing.
|
|
22
|
+
- **CTDeployer as permanent data-hook proxy.** `CTDeployer` sets itself as the data hook for projects it deploys. `dataHookOf[projectId]` is set once and has no setter.
|
|
23
|
+
- **Sucker registry.** `CTDeployer.beforeCashOutRecordedWith` trusts `SUCKER_REGISTRY.isSuckerOf()` for 0% tax cash outs.
|
|
24
|
+
- **Sucker deployment is fail-open at launch time.** Launch can continue on chains where the configured sucker deployer cascade cannot complete.
|
|
25
|
+
- **CTProjectOwner as burn target.** Projects transferred to `CTProjectOwner` cannot be recovered.
|
|
26
|
+
- **JBDirectory / terminal resolution.** `CTPublisher.mintFrom` trusts `DIRECTORY.primaryTerminalOf()`.
|
|
27
|
+
- **721 hook store.** `_setupPosts` trusts the hook store for tier state, removal checks, and prices.
|
|
28
28
|
|
|
29
|
-
## 2. Economic
|
|
29
|
+
## 2. Economic And Manipulation Risks
|
|
30
30
|
|
|
31
|
-
- **Fee evasion via duplicate posts across hooks.**
|
|
32
|
-
- **Fee calculation rounding.** Fee is `totalPrice /
|
|
33
|
-
- **
|
|
34
|
-
- **Fee terminal fallback refunds the caller.** If the
|
|
35
|
-
- **Split percent manipulation.** Posters can
|
|
31
|
+
- **Fee evasion via duplicate posts across hooks.** Duplicate-content checks are keyed per hook, so the same URI can be reused across different hooks.
|
|
32
|
+
- **Fee calculation rounding.** Fee is `totalPrice / 20`, so integer division truncates small amounts.
|
|
33
|
+
- **Fee is computed from `msg.value`.** Force-sent ETH does not affect the fee calculation.
|
|
34
|
+
- **Fee terminal fallback refunds the caller.** If the fee project cannot accept the fee, Croptop refunds `_msgSender()`. Relayers or contracts that cannot receive ETH will make the mint revert.
|
|
35
|
+
- **Split percent manipulation.** Posters can direct large shares of tier revenue away from the project if `maximumSplitPercent` is configured high.
|
|
36
36
|
|
|
37
37
|
## 3. Access Control
|
|
38
38
|
|
|
39
|
-
- **Allowlist is O(n)
|
|
40
|
-
- **Categories cannot be disabled.** Once
|
|
41
|
-
- **CTDeployer grants broad permissions.**
|
|
42
|
-
-
|
|
43
|
-
-
|
|
39
|
+
- **Allowlist is O(n).** `_isAllowed` linearly scans the full allowlist.
|
|
40
|
+
- **Categories cannot be disabled cleanly.** Once configured, a category can only be made impractical through stricter bounds.
|
|
41
|
+
- **CTDeployer grants broad permissions.** Wildcard permissions to the sucker registry and publisher apply to all projects deployed by that deployer instance.
|
|
42
|
+
- **`deployProjectFor` is permissionless for new projects.** Anyone can create a project with arbitrary owners.
|
|
43
|
+
- **`claimCollectionOwnershipOf` only checks current NFT ownership.** After claiming, the project owner must still grant `CTPublisher` the needed tier-adjust permission or publishing stops working.
|
|
44
44
|
|
|
45
45
|
## 4. DoS Vectors
|
|
46
46
|
|
|
47
|
-
- **Large batch posts.** `_setupPosts`
|
|
48
|
-
- **External hook calls in loops.**
|
|
49
|
-
- **Terminal resolution failure.** If `DIRECTORY.primaryTerminalOf()` returns `address(0)
|
|
50
|
-
-
|
|
47
|
+
- **Large batch posts.** `_setupPosts` does O(n^2) duplicate detection within a batch.
|
|
48
|
+
- **External hook calls in loops.** Tier-store calls inside the post loop can revert or become gas-heavy.
|
|
49
|
+
- **Terminal resolution failure.** If `DIRECTORY.primaryTerminalOf()` returns `address(0)`, payment calls revert.
|
|
50
|
+
- **`adjustTiers` revert.** Hook-level tier rules can block the whole `mintFrom` call.
|
|
51
51
|
|
|
52
52
|
## 5. Reentrancy Surface
|
|
53
53
|
|
|
54
|
-
- **`mintFrom` external call chain.**
|
|
55
|
-
- **Fee payment ordering.** The fee is sent
|
|
56
|
-
- **No `ReentrancyGuard`.** The publisher relies on independent local state per call. This is safe for the current implementation but fragile if mutable contract storage is added in future versions.
|
|
54
|
+
- **`mintFrom` external call chain.** The function calls into the hook and terminals. It currently relies on local-call state isolation rather than a `ReentrancyGuard`.
|
|
55
|
+
- **Fee payment ordering.** The fee is sent after the main payment. This is safe under the current `msg.value`-based accounting model, but future mutable storage in the publisher would make the surface riskier.
|
|
57
56
|
|
|
58
57
|
## 6. Integration Risks
|
|
59
58
|
|
|
60
|
-
- **
|
|
61
|
-
- **No
|
|
62
|
-
- **
|
|
63
|
-
- **
|
|
64
|
-
- **
|
|
59
|
+
- **Null data-hook forwarding in deployer.** `beforePayRecordedWith` and `beforeCashOutRecordedWith` return defaults when `dataHookOf` is null.
|
|
60
|
+
- **No hook migration path.** `dataHookOf` is written once and never updated.
|
|
61
|
+
- **Sucker support can be absent even when requested.** A launch can complete while omnichain support is still missing.
|
|
62
|
+
- **Tier ID prediction.** `_setupPosts` predicts new tier IDs ahead of the actual `adjustTiers` call.
|
|
63
|
+
- **CTProjectOwner accepts any project NFT.** Accidentally transferring a non-Croptop project there still grants publisher permissions.
|
|
64
|
+
- **Fee payment destination.** If the fee project changes terminal behavior incompatibly, mints fall back to refund or revert.
|
|
65
65
|
|
|
66
66
|
## 7. Accepted Behaviors
|
|
67
67
|
|
|
68
|
-
### 7.1 O(n^2) duplicate detection
|
|
69
|
-
|
|
70
|
-
`_setupPosts` uses an inner loop (`j < i`) to detect duplicate `encodedIPFSUri` values within a single batch. This is O(n^2) in the number of posts. For typical batch sizes (1-20 posts), gas cost is negligible (~2k gas per comparison). At 100 posts, the quadratic cost adds ~10M gas. The practical limit is ~150 posts per batch before approaching block gas limits. No mitigation is needed because: (1) the quadratic detection prevents duplicate NFT tiers which would corrupt tier ID tracking, (2) real-world posting batches are small (marketplace UX limits), and (3) the gas cost is borne by the poster, not the protocol.
|
|
71
|
-
|
|
72
|
-
### 7.2 Tier ID prediction assumes no concurrent transactions
|
|
68
|
+
### 7.1 O(n^2) duplicate detection is accepted
|
|
73
69
|
|
|
74
|
-
|
|
70
|
+
Duplicate detection within a batch is quadratic, but expected real-world batch sizes are small enough that this tradeoff is acceptable.
|
|
75
71
|
|
|
76
|
-
### 7.
|
|
72
|
+
### 7.2 Tier ID prediction assumes no concurrent tier writes
|
|
77
73
|
|
|
78
|
-
|
|
79
|
-
collection directly. That means the owner can bypass `CTPublisher`'s policy and fee path until ownership is moved into
|
|
80
|
-
another authority surface or those permissions are narrowed. This is an accepted product tradeoff and should be treated
|
|
81
|
-
as part of the trust model, not as a hidden invariant enforced by `CTPublisher`.
|
|
74
|
+
This is a known race. The mitigation is application-layer ordering and the fact that a bad prediction reverts the whole batch cleanly.
|
|
82
75
|
|
|
83
|
-
|
|
76
|
+
### 7.3 Project owners can bypass the publisher path while they still have direct hook permissions
|
|
84
77
|
|
|
85
|
-
|
|
86
|
-
- `totalPrice` accumulated in `_setupPosts` equals the sum of prices for all posts (new tier price for new posts, existing tier price for existing posts).
|
|
87
|
-
- Fee amount: `msg.value - payValue == totalPrice / FEE_DIVISOR` (within 19 wei rounding).
|
|
88
|
-
- For every configured category, `minimumTotalSupply <= maximumTotalSupply` and `minimumTotalSupply > 0`.
|
|
89
|
-
- Packed allowance encoding/decoding round-trips correctly for all valid input ranges.
|
|
90
|
-
- After `CTDeployer.deployProjectFor`, the project NFT is owned by `owner`, and `dataHookOf[projectId]` is the deployed 721 hook.
|
|
91
|
-
- `CTProjectOwner` only grants `ADJUST_721_TIERS` permission, never broader permissions.
|
|
78
|
+
`CTDeployer.deployProjectFor` intentionally grants the initial owner enough hook permissions to manage the collection directly. That is part of the trust model until ownership is moved into a narrower surface.
|
package/SKILLS.md
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
## Use This File For
|
|
4
4
|
|
|
5
|
-
- Use this file when the task touches Croptop publishing, project deployment, data-hook forwarding, fee routing, or burn-locked ownership
|
|
6
|
-
- Start here, then
|
|
5
|
+
- Use this file when the task touches Croptop publishing, project deployment, data-hook forwarding, fee routing, or burn-locked ownership.
|
|
6
|
+
- Start here, then decide whether the issue is posting-policy validation, tier reuse and content identity, deployer-packaged project shape, or burn-locked ownership.
|
|
7
7
|
|
|
8
8
|
## Read This Next
|
|
9
9
|
|
|
@@ -13,7 +13,9 @@
|
|
|
13
13
|
| Publishing and metadata behavior | [`src/CTPublisher.sol`](./src/CTPublisher.sol) |
|
|
14
14
|
| Deployment and fee-project wiring | [`src/CTDeployer.sol`](./src/CTDeployer.sol), [`script/Deploy.s.sol`](./script/Deploy.s.sol), [`script/ConfigureFeeProject.s.sol`](./script/ConfigureFeeProject.s.sol) |
|
|
15
15
|
| Ownership burn-lock behavior | [`src/CTProjectOwner.sol`](./src/CTProjectOwner.sol) |
|
|
16
|
-
|
|
|
16
|
+
| Runtime and operational invariants | [`references/runtime.md`](./references/runtime.md), [`references/operations.md`](./references/operations.md) |
|
|
17
|
+
| Publishing, metadata, and attack coverage | [`test/CTPublisher.t.sol`](./test/CTPublisher.t.sol), [`test/Test_MetadataGeneration.t.sol`](./test/Test_MetadataGeneration.t.sol), [`test/CroptopAttacks.t.sol`](./test/CroptopAttacks.t.sol) |
|
|
18
|
+
| Deployment, ownership, and fork coverage | [`test/CTDeployer.t.sol`](./test/CTDeployer.t.sol), [`test/CTProjectOwner.t.sol`](./test/CTProjectOwner.t.sol), [`test/ClaimCollectionOwnership.t.sol`](./test/ClaimCollectionOwnership.t.sol), [`test/Fork.t.sol`](./test/Fork.t.sol), [`test/TestAuditGaps.sol`](./test/TestAuditGaps.sol) |
|
|
17
19
|
|
|
18
20
|
## Repo Map
|
|
19
21
|
|
|
@@ -30,12 +32,15 @@ Permissioned publishing layer for Juicebox 721 projects. Project owners define p
|
|
|
30
32
|
|
|
31
33
|
## Reference Files
|
|
32
34
|
|
|
33
|
-
- Open [`references/runtime.md`](./references/runtime.md)
|
|
34
|
-
- Open [`references/operations.md`](./references/operations.md)
|
|
35
|
+
- Open [`references/runtime.md`](./references/runtime.md) for publisher behavior, fee routing, data-hook forwarding, and the main invariants around posting criteria and tier reuse.
|
|
36
|
+
- Open [`references/operations.md`](./references/operations.md) for deployer behavior, burn-lock implications, script breadcrumbs, and common stale assumptions.
|
|
35
37
|
|
|
36
38
|
## Working Rules
|
|
37
39
|
|
|
38
40
|
- Start in [`src/CTPublisher.sol`](./src/CTPublisher.sol) for posting-rule and fee behavior, but check [`src/CTDeployer.sol`](./src/CTDeployer.sol) when the bug might come from project shape or hook forwarding.
|
|
39
41
|
- Treat posting criteria, fee routing, and duplicate-content handling as treasury-sensitive and product-sensitive at the same time.
|
|
42
|
+
- Category policy is part of the product surface. Changes to allowlists, supply bounds, or split caps change what can be published.
|
|
40
43
|
- If the task mentions project immutability or admin recovery, inspect [`src/CTProjectOwner.sol`](./src/CTProjectOwner.sol) before changing deployer or publisher code.
|
|
44
|
+
- Metadata bugs can be publishing bugs, resolver-shape bugs, or duplicate-content bugs. Check all three before assuming a simple formatting issue.
|
|
45
|
+
- Duplicate-post and tier-reuse behavior are runtime semantics, not convenience logic.
|
|
41
46
|
- When a bug looks like generic 721 issuance, confirm it is not actually in `nana-721-hook-v6`.
|
package/USER_JOURNEYS.md
CHANGED
|
@@ -1,56 +1,132 @@
|
|
|
1
1
|
# User Journeys
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## Repo Purpose
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
This repo turns a Juicebox 721 project into a permissioned publishing system. It owns post validation, Croptop fee routing, and the deployment packaging that turns a project into a Croptop-managed publisher. It does not own base terminal accounting or the underlying 721 tier mechanics.
|
|
6
|
+
|
|
7
|
+
## Primary Actors
|
|
8
|
+
|
|
9
|
+
- project owners creating a Croptop publishing surface
|
|
10
|
+
- publishers minting posts into an existing Croptop project
|
|
11
|
+
- auditors reviewing fee routing, posting policy, and owner-lock semantics
|
|
12
|
+
|
|
13
|
+
## Key Surfaces
|
|
14
|
+
|
|
15
|
+
- `CTPublisher`: validates posts, adjusts tiers, mints the first copy, and routes Croptop fees
|
|
16
|
+
- `CTDeployer`: launches a Croptop-shaped project and can compose omnichain deployment
|
|
17
|
+
- `CTProjectOwner`: owner helper that can burn-lock administration into Croptop
|
|
18
|
+
- `mintFrom(...)`: main publishing entrypoint
|
|
8
19
|
|
|
9
20
|
## Journey 1: Turn A Project Into A Croptop Publisher
|
|
10
21
|
|
|
11
|
-
**
|
|
22
|
+
**Actor:** project owner.
|
|
23
|
+
|
|
24
|
+
**Intent:** install Croptop publishing policy on a project.
|
|
25
|
+
|
|
26
|
+
**Preconditions**
|
|
27
|
+
|
|
28
|
+
- the project already exists or will be launched through `CTDeployer`
|
|
29
|
+
- the owner has chosen category rules and the expected 721 hook shape
|
|
30
|
+
|
|
31
|
+
**Main Flow**
|
|
32
|
+
|
|
33
|
+
1. Configure category-level constraints such as price floor, supply, splits, and allowlists.
|
|
34
|
+
2. Install or verify the expected 721 hook setup.
|
|
35
|
+
3. Route publishing through Croptop so future posts are policy-checked instead of free-form tier edits.
|
|
12
36
|
|
|
13
|
-
**
|
|
37
|
+
**Failure Modes**
|
|
14
38
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
39
|
+
- category rules do not match the intended publishing product
|
|
40
|
+
- teams assume Croptop replaces the need to audit the underlying 721 hook
|
|
41
|
+
|
|
42
|
+
**Postconditions**
|
|
43
|
+
|
|
44
|
+
- the project now routes publishing through Croptop policy instead of direct free-form tier creation
|
|
19
45
|
|
|
20
46
|
## Journey 2: Publish Content Into An Existing Croptop Project
|
|
21
47
|
|
|
22
|
-
**
|
|
48
|
+
**Actor:** publisher.
|
|
49
|
+
|
|
50
|
+
**Intent:** publish one post into a Croptop project and mint the first copy.
|
|
51
|
+
|
|
52
|
+
**Preconditions**
|
|
53
|
+
|
|
54
|
+
- the post satisfies the target project's category policy
|
|
55
|
+
- the caller can receive ETH if the fee refund fallback is needed
|
|
56
|
+
- duplicate-content and stale-tier implications are understood
|
|
57
|
+
|
|
58
|
+
**Main Flow**
|
|
59
|
+
|
|
60
|
+
1. Call `mintFrom(...)` with the content URI and pricing data.
|
|
61
|
+
2. `CTPublisher` validates the post against category and fee policy.
|
|
62
|
+
3. It creates or reuses the underlying tier, mints the first copy, and routes project revenue plus the Croptop fee.
|
|
63
|
+
|
|
64
|
+
**Failure Modes**
|
|
23
65
|
|
|
24
|
-
|
|
66
|
+
- duplicate URIs or stale tier mappings
|
|
67
|
+
- publisher inputs satisfy the base 721 hook but violate Croptop's stricter rules
|
|
68
|
+
- the fee terminal rejects the fee payment and `_msgSender()` cannot receive the refund
|
|
25
69
|
|
|
26
|
-
**
|
|
27
|
-
1. The publisher calls `mintFrom(...)` or the equivalent publishing surface with the content URI and pricing data.
|
|
28
|
-
2. `CTPublisher` checks the post against category rules and fee policy.
|
|
29
|
-
3. It creates or reuses the underlying 721 tier, mints the first copy, and routes both project revenue and the Croptop fee. If the fee terminal is unavailable, the fee is refunded to `_msgSender()` instead.
|
|
70
|
+
**Postconditions**
|
|
30
71
|
|
|
31
|
-
|
|
72
|
+
- the post is minted or reused as a tier under Croptop policy and the fee path is accounted for
|
|
32
73
|
|
|
33
74
|
## Journey 3: Launch A New Croptop Project End To End
|
|
34
75
|
|
|
35
|
-
**
|
|
76
|
+
**Actor:** product team or deployer.
|
|
36
77
|
|
|
37
|
-
**
|
|
78
|
+
**Intent:** launch a project already wired for Croptop publishing.
|
|
38
79
|
|
|
39
|
-
**
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
80
|
+
**Preconditions**
|
|
81
|
+
|
|
82
|
+
- the team has project config, posting rules, and any omnichain requirements ready
|
|
83
|
+
- the correct `FEE_PROJECT_ID` is known
|
|
84
|
+
|
|
85
|
+
**Main Flow**
|
|
86
|
+
|
|
87
|
+
1. Use `CTDeployer` with project config, posting rules, and optional omnichain config.
|
|
88
|
+
2. The deployer launches the project, configures Croptop ownership assumptions, and wires publisher behavior.
|
|
89
|
+
3. The resulting project is ready for publishers without a manual post-launch setup gap.
|
|
90
|
+
|
|
91
|
+
**Failure Modes**
|
|
92
|
+
|
|
93
|
+
- the fee project is misconfigured or omitted
|
|
94
|
+
- teams treat `CTDeployer` as packaging only and miss its policy implications
|
|
95
|
+
|
|
96
|
+
**Postconditions**
|
|
97
|
+
|
|
98
|
+
- the project is ready for Croptop publishers without a post-launch wiring gap
|
|
43
99
|
|
|
44
100
|
## Journey 4: Lock Administration Into Croptop's Owner Surface
|
|
45
101
|
|
|
46
|
-
**
|
|
102
|
+
**Actor:** project owner.
|
|
103
|
+
|
|
104
|
+
**Intent:** keep governance inside Croptop's constrained owner surface.
|
|
47
105
|
|
|
48
|
-
**
|
|
106
|
+
**Preconditions**
|
|
49
107
|
|
|
50
|
-
|
|
51
|
-
|
|
108
|
+
- the owner wants irreversible product-shaping constraints, not ordinary owner flexibility
|
|
109
|
+
|
|
110
|
+
**Main Flow**
|
|
111
|
+
|
|
112
|
+
1. Transfer or configure ownership so `CTProjectOwner` controls the relevant admin surface.
|
|
52
113
|
2. Restrict future edits to the paths Croptop intentionally exposes.
|
|
53
|
-
3. Accept that this is
|
|
114
|
+
3. Accept that this is an ownership-model decision, not cosmetic packaging.
|
|
115
|
+
|
|
116
|
+
**Failure Modes**
|
|
117
|
+
|
|
118
|
+
- teams burn-lock before validating the publishing policy in production-like conditions
|
|
119
|
+
- reviewers miss that prior owner discretion no longer exists directly
|
|
120
|
+
|
|
121
|
+
**Postconditions**
|
|
122
|
+
|
|
123
|
+
- future administration is constrained to the Croptop owner surface instead of ordinary owner discretion
|
|
124
|
+
|
|
125
|
+
## Trust Boundaries
|
|
126
|
+
|
|
127
|
+
- this repo is trusted for publishing policy and fee routing
|
|
128
|
+
- the underlying 721 hook remains trusted for tier issuance and lower-level NFT accounting
|
|
129
|
+
- Croptop fee behavior depends on the fee project and its terminal remaining correctly configured
|
|
54
130
|
|
|
55
131
|
## Hand-Offs
|
|
56
132
|
|
package/foundry.toml
CHANGED
package/package.json
CHANGED
|
@@ -1,33 +1,33 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
2
|
+
"name": "@croptop/core-v6",
|
|
3
|
+
"version": "0.0.35",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/mejango/croptop-core-v6"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "forge test",
|
|
11
|
+
"coverage:integration": "forge coverage --match-path \"./src/*.sol\" --report lcov --report summary",
|
|
12
|
+
"deploy:mainnets": "source ./.env && npx sphinx propose ./script/Deploy.s.sol --networks mainnets",
|
|
13
|
+
"deploy:mainnets:project": "source ./.env && export START_TIME=$(date +%s) && npx sphinx propose ./script/ConfigureFeeProject.s.sol --networks mainnets",
|
|
14
|
+
"deploy:testnets": "source ./.env && npx sphinx propose ./script/Deploy.s.sol --networks testnets",
|
|
15
|
+
"deploy:testnets:project": "source ./.env && export START_TIME=$(date +%s) && npx sphinx propose ./script/ConfigureFeeProject.s.sol --networks testnets",
|
|
16
|
+
"artifacts": "source ./.env && npx sphinx artifacts --org-id 'ea165b21-7cdc-4d7b-be59-ecdd4c26bee4' --project-name 'croptop-core-v5'"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@bananapus/721-hook-v6": "^0.0.35",
|
|
20
|
+
"@bananapus/buyback-hook-v6": "^0.0.27",
|
|
21
|
+
"@bananapus/core-v6": "^0.0.34",
|
|
22
|
+
"@bananapus/ownable-v6": "^0.0.17",
|
|
23
|
+
"@bananapus/permission-ids-v6": "^0.0.17",
|
|
24
|
+
"@bananapus/router-terminal-v6": "^0.0.26",
|
|
25
|
+
"@bananapus/suckers-v6": "^0.0.25",
|
|
26
|
+
"@openzeppelin/contracts": "^5.6.1"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@bananapus/address-registry-v6": "^0.0.17",
|
|
30
|
+
"@rev-net/core-v6": "^0.0.32",
|
|
31
|
+
"@sphinx-labs/plugins": "^0.33.3"
|
|
32
|
+
}
|
|
33
33
|
}
|
package/references/operations.md
CHANGED
|
@@ -21,5 +21,5 @@
|
|
|
21
21
|
|
|
22
22
|
## Useful Proof Points
|
|
23
23
|
|
|
24
|
-
- [`test/
|
|
25
|
-
- [`script/
|
|
24
|
+
- [`test/Fork.t.sol`](../test/Fork.t.sol) when deployment shape matters.
|
|
25
|
+
- [`script/ConfigureFeeProject.s.sol`](../script/ConfigureFeeProject.s.sol) if the issue is really script/config assembly.
|
package/references/runtime.md
CHANGED
|
@@ -22,6 +22,6 @@
|
|
|
22
22
|
|
|
23
23
|
## Tests To Trust First
|
|
24
24
|
|
|
25
|
-
- [`test/
|
|
26
|
-
- [`test/
|
|
27
|
-
- [`test
|
|
25
|
+
- [`test/CTPublisher.t.sol`](../test/CTPublisher.t.sol) and [`test/Test_MetadataGeneration.t.sol`](../test/Test_MetadataGeneration.t.sol) for content and metadata behavior.
|
|
26
|
+
- [`test/CTDeployer.t.sol`](../test/CTDeployer.t.sol) and [`test/Fork.t.sol`](../test/Fork.t.sol) for live deployment assumptions.
|
|
27
|
+
- [`test/CroptopAttacks.t.sol`](../test/CroptopAttacks.t.sol) and [`test/TestAuditGaps.sol`](../test/TestAuditGaps.sol) when the issue could be in publisher or deployer behavior rather than one isolated function.
|
package/src/CTPublisher.sol
CHANGED
|
@@ -27,7 +27,6 @@ contract CTPublisher is JBPermissioned, ERC2771Context, ICTPublisher {
|
|
|
27
27
|
// --------------------------- custom errors ------------------------- //
|
|
28
28
|
//*********************************************************************//
|
|
29
29
|
|
|
30
|
-
// forge-lint: disable-next-line(mixed-case-variable)
|
|
31
30
|
error CTPublisher_DuplicatePost(bytes32 encodedIPFSUri);
|
|
32
31
|
error CTPublisher_EmptyEncodedIPFSUri();
|
|
33
32
|
error CTPublisher_InsufficientEthSent(uint256 expected, uint256 sent);
|
|
@@ -66,7 +65,6 @@ contract CTPublisher is JBPermissioned, ERC2771Context, ICTPublisher {
|
|
|
66
65
|
/// @notice The ID of the tier that an IPFS metadata has been saved to.
|
|
67
66
|
/// @custom:param hook The hook for which the tier ID applies.
|
|
68
67
|
/// @custom:param encodedIPFSUri The IPFS URI.
|
|
69
|
-
// forge-lint: disable-next-line(mixed-case-variable)
|
|
70
68
|
mapping(address hook => mapping(bytes32 encodedIPFSUri => uint256)) public override tierIdForEncodedIPFSUriOf;
|
|
71
69
|
|
|
72
70
|
//*********************************************************************//
|
|
@@ -324,7 +322,6 @@ contract CTPublisher is JBPermissioned, ERC2771Context, ICTPublisher {
|
|
|
324
322
|
/// is returned.
|
|
325
323
|
function tiersFor(
|
|
326
324
|
address hook,
|
|
327
|
-
// forge-lint: disable-next-line(mixed-case-variable)
|
|
328
325
|
bytes32[] memory encodedIPFSUris
|
|
329
326
|
)
|
|
330
327
|
external
|
|
@@ -332,7 +329,6 @@ contract CTPublisher is JBPermissioned, ERC2771Context, ICTPublisher {
|
|
|
332
329
|
override
|
|
333
330
|
returns (JB721Tier[] memory tiers)
|
|
334
331
|
{
|
|
335
|
-
// forge-lint: disable-next-line(mixed-case-variable)
|
|
336
332
|
uint256 numberOfEncodedIPFSUris = encodedIPFSUris.length;
|
|
337
333
|
|
|
338
334
|
// Initialize the tier array being returned.
|