@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/RISKS.md
CHANGED
|
@@ -22,6 +22,7 @@ This file focuses on the publishing, fee-routing, and hook-composition risks tha
|
|
|
22
22
|
- **Trusted forwarder.** ERC-2771 `_msgSender()` is trusted in both CTPublisher and CTDeployer for permission checks, allowlist validation, and payment routing. A compromised forwarder can post as any allowed address, deploy projects as any owner, and redirect payments.
|
|
23
23
|
- **CTDeployer as permanent data hook proxy.** `CTDeployer` sets itself as the data hook for projects it deploys. `dataHookOf[projectId]` is set once during `deployProjectFor` and has no setter to update it. If the underlying data hook needs to change, there is no mechanism to do so without redeploying.
|
|
24
24
|
- **Sucker registry.** `CTDeployer.beforeCashOutRecordedWith` trusts `SUCKER_REGISTRY.isSuckerOf()` for 0% tax cashouts, same risk as the omnichain deployer.
|
|
25
|
+
- **Sucker deployment is intended to be fail-open at launch time.** `deployProjectFor` calls `SUCKER_REGISTRY.deploySuckersFor` only when a non-zero deployment salt is configured, and the current deployment path is documented as allowing launch to continue on chains where the configured sucker deployer cascade cannot complete. Operators should not assume a successful Croptop launch implies omnichain support is live.
|
|
25
26
|
- **CTProjectOwner as burn target.** Projects transferred to `CTProjectOwner` grant `ADJUST_721_TIERS` to `PUBLISHER`. The project NFT cannot be recovered -- this is intentional but irreversible.
|
|
26
27
|
- **JBDirectory / Terminal resolution.** `CTPublisher.mintFrom` resolves terminals via `DIRECTORY.primaryTerminalOf()`. A compromised directory could redirect payment and fee flows.
|
|
27
28
|
- **721 hook store.** `_setupPosts` calls `hook.STORE().tierOf()` and `hook.STORE().isTierRemoved()`. The store is trusted to return accurate tier data. A malicious hook returning a fake store can report manipulated prices, supply limits, and removal status, causing `_setupPosts` to miscalculate `totalPrice` or skip duplicate detection.
|
|
@@ -59,6 +60,7 @@ This file focuses on the publishing, fee-routing, and hook-composition risks tha
|
|
|
59
60
|
|
|
60
61
|
- **CTDeployer forwards pay/cashout calls to `dataHookOf` with null check.** `beforePayRecordedWith` and `beforeCashOutRecordedWith` check for a null `dataHookOf` and return defaults (context weight, empty specs) instead of reverting. If a non-null data hook reverts, payments/cashouts for the project are still blocked.
|
|
61
62
|
- **No mechanism for hook migration.** `dataHookOf` is written once in `deployProjectFor` and never updated. If the data hook becomes compromised, there is no governance path to replace it without deploying a new project.
|
|
63
|
+
- **Sucker support can be absent even when deployment requested it.** Launch does not treat successful sucker setup as part of the Croptop app's core publish flow. A project can come online with the publisher, hook, and main terminal flow active while no suckers were actually deployed yet. Monitoring and deployment tooling should verify the returned sucker set explicitly instead of inferring success from project launch.
|
|
62
64
|
- **Tier ID prediction.** `_setupPosts` predicts new tier IDs as `maxTierIdOf(hook) + 1 + i`. If another transaction adds tiers between `maxTierIdOf` read and `adjustTiers` execution, tier IDs shift and the wrong tiers are minted. This is a race condition in concurrent posting.
|
|
63
65
|
- **CTProjectOwner accepts any project NFT.** `onERC721Received` grants `ADJUST_721_TIERS` to `PUBLISHER` for whatever tokenId is received. If a non-Croptop project is accidentally transferred to `CTProjectOwner`, the publisher gains tier adjustment permission for it.
|
|
64
66
|
- **Fee payment destination.** Fees are routed to `FEE_PROJECT_ID` via its primary terminal. If the fee project changes its terminal or token acceptance incompatibly, `mintFrom` attempts to refund the fee to `_msgSender()`. If the caller cannot receive ETH, the mint reverts.
|
package/SKILLS.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
## Use This File For
|
|
4
4
|
|
|
5
5
|
- Use this file when the task touches Croptop publishing, project deployment, data-hook forwarding, fee routing, or burn-locked ownership behavior.
|
|
6
|
-
- Start here, then
|
|
6
|
+
- Start here, then decide whether the issue is posting-policy validation, tier reuse/content identity, deployer-packaged project shape, or burn-locked ownership. Those concerns interact, but they are not the same subsystem.
|
|
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
|
|
|
@@ -37,5 +39,8 @@ Permissioned publishing layer for Juicebox 721 projects. Project owners define p
|
|
|
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 allowed addresses, supply bounds, or split caps alter what can be published, not just how it is paid for.
|
|
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 string-formatting issue.
|
|
45
|
+
- Duplicate-post and tier-reuse behavior are first-class runtime semantics. Do not treat them like cacheable 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,67 +1,118 @@
|
|
|
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.
|
|
6
|
+
It owns post validation, Croptop fee routing, and the deployment packaging that turns a project into a Croptop-managed
|
|
7
|
+
publisher. It does not own the base terminal accounting or the underlying 721 tier mechanics it wraps.
|
|
8
|
+
|
|
9
|
+
## Primary Actors
|
|
10
|
+
|
|
11
|
+
- project owners creating a Croptop publishing surface
|
|
12
|
+
- publishers minting posts into an existing Croptop project
|
|
13
|
+
- auditors reviewing fee routing, posting policy, and owner-lock semantics
|
|
14
|
+
|
|
15
|
+
## Key Surfaces
|
|
16
|
+
|
|
17
|
+
- `CTPublisher`: validates posts, adjusts tiers, mints the first copy, and routes Croptop fees
|
|
18
|
+
- `CTDeployer`: launches a Croptop-shaped project and can compose omnichain deployment
|
|
19
|
+
- `CTProjectOwner`: owner helper that can burn-lock administration into Croptop
|
|
20
|
+
- `mintFrom(...)`: main publishing entrypoint for new content
|
|
8
21
|
|
|
9
22
|
## Journey 1: Turn A Project Into A Croptop Publisher
|
|
10
23
|
|
|
11
|
-
**
|
|
24
|
+
**Actor:** project owner.
|
|
25
|
+
|
|
26
|
+
**Intent:** install Croptop publishing policy on a project.
|
|
27
|
+
|
|
28
|
+
**Preconditions**
|
|
29
|
+
- the project already exists or will be launched through `CTDeployer`
|
|
30
|
+
- the owner has chosen category rules and the expected 721 hook shape
|
|
31
|
+
|
|
32
|
+
**Main Flow**
|
|
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**
|
|
38
|
+
- category rules do not match the intended publishing product
|
|
39
|
+
- teams assume Croptop replaces the need to audit the underlying 721 hook
|
|
14
40
|
|
|
15
|
-
**
|
|
16
|
-
|
|
17
|
-
2. Install or verify the 721 hook shape the project expects.
|
|
18
|
-
3. Route the project through Croptop's publisher logic so future post creation is policy-checked instead of free-form tier editing.
|
|
41
|
+
**Postconditions**
|
|
42
|
+
- the project now routes publishing through Croptop policy rather than direct free-form tier creation
|
|
19
43
|
|
|
20
44
|
## Journey 2: Publish Content Into An Existing Croptop Project
|
|
21
45
|
|
|
22
|
-
**
|
|
46
|
+
**Actor:** publisher.
|
|
23
47
|
|
|
24
|
-
**
|
|
48
|
+
**Intent:** publish one post into a Croptop project and mint the first copy.
|
|
25
49
|
|
|
26
|
-
**
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
50
|
+
**Preconditions**
|
|
51
|
+
- the post satisfies the target project's category policy
|
|
52
|
+
- the caller can receive ETH if the fee refund fallback is needed
|
|
53
|
+
- duplicate-content and stale-tier implications are understood
|
|
30
54
|
|
|
31
|
-
**
|
|
55
|
+
**Main Flow**
|
|
56
|
+
1. Call `mintFrom(...)` with the content URI and pricing data.
|
|
57
|
+
2. `CTPublisher` validates the post against category and fee policy.
|
|
58
|
+
3. It creates or reuses the underlying tier, mints the first copy, and routes project revenue plus the Croptop fee.
|
|
59
|
+
|
|
60
|
+
**Failure Modes**
|
|
61
|
+
- duplicate URIs or stale tier mappings
|
|
62
|
+
- publisher inputs satisfy the base 721 hook but violate Croptop's stricter rules
|
|
63
|
+
- the fee terminal rejects the fee payment and `_msgSender()` cannot receive the refund
|
|
64
|
+
|
|
65
|
+
**Postconditions**
|
|
66
|
+
- the post is minted or reused as a tier under Croptop policy and the fee path is accounted for
|
|
32
67
|
|
|
33
68
|
## Journey 3: Launch A New Croptop Project End To End
|
|
34
69
|
|
|
35
|
-
**
|
|
70
|
+
**Actor:** product team or deployer.
|
|
71
|
+
|
|
72
|
+
**Intent:** launch a project already wired for Croptop publishing.
|
|
36
73
|
|
|
37
|
-
**
|
|
74
|
+
**Preconditions**
|
|
75
|
+
- the team has project config, posting rules, and any omnichain requirements ready
|
|
76
|
+
- the correct `FEE_PROJECT_ID` is known for deployment
|
|
38
77
|
|
|
39
|
-
**Flow**
|
|
40
|
-
1. Use `CTDeployer` with project config, posting rules, and
|
|
41
|
-
2. The deployer launches the
|
|
42
|
-
3. The project is ready for publishers without a manual post-launch setup
|
|
78
|
+
**Main Flow**
|
|
79
|
+
1. Use `CTDeployer` with project config, posting rules, and optional omnichain config.
|
|
80
|
+
2. The deployer launches the project, configures Croptop ownership assumptions, and wires publisher behavior.
|
|
81
|
+
3. The resulting project is ready for publishers without a manual post-launch setup gap.
|
|
82
|
+
|
|
83
|
+
**Failure Modes**
|
|
84
|
+
- the fee project is misconfigured or omitted
|
|
85
|
+
- teams treat `CTDeployer` as packaging only and miss its policy implications
|
|
86
|
+
|
|
87
|
+
**Postconditions**
|
|
88
|
+
- the resulting project is ready for Croptop publishers without a post-launch wiring gap
|
|
43
89
|
|
|
44
90
|
## Journey 4: Lock Administration Into Croptop's Owner Surface
|
|
45
91
|
|
|
46
|
-
**
|
|
92
|
+
**Actor:** project owner.
|
|
93
|
+
|
|
94
|
+
**Intent:** keep governance inside Croptop's constrained owner surface.
|
|
47
95
|
|
|
48
|
-
**
|
|
96
|
+
**Preconditions**
|
|
97
|
+
- the owner wants irreversible product-shaping constraints, not ordinary owner flexibility
|
|
49
98
|
|
|
50
|
-
**Flow**
|
|
51
|
-
1. Transfer or configure ownership so
|
|
99
|
+
**Main Flow**
|
|
100
|
+
1. Transfer or configure ownership so `CTProjectOwner` controls the relevant admin surface.
|
|
52
101
|
2. Restrict future edits to the paths Croptop intentionally exposes.
|
|
53
|
-
3. Accept that this is
|
|
102
|
+
3. Accept that this is an ownership-model decision, not cosmetic packaging.
|
|
54
103
|
|
|
55
|
-
|
|
104
|
+
**Failure Modes**
|
|
105
|
+
- teams burn-lock before validating the publishing policy in production-like conditions
|
|
106
|
+
- reviewers miss that prior owner discretion no longer exists directly
|
|
56
107
|
|
|
57
|
-
**
|
|
108
|
+
**Postconditions**
|
|
109
|
+
- future administration is constrained to the Croptop owner surface instead of ordinary owner discretion
|
|
58
110
|
|
|
59
|
-
|
|
111
|
+
## Trust Boundaries
|
|
60
112
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
3. The swapped beneficiary is forwarded to the downstream data hook.
|
|
113
|
+
- this repo is trusted for publishing policy and fee routing
|
|
114
|
+
- the underlying 721 hook remains trusted for tier issuance and lower-level NFT accounting
|
|
115
|
+
- Croptop fee behavior depends on the fee project and its terminal remaining correctly configured
|
|
65
116
|
|
|
66
117
|
## Hand-Offs
|
|
67
118
|
|
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.34",
|
|
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/CTDeployer.sol
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
pragma solidity 0.8.28;
|
|
3
3
|
|
|
4
|
-
import {ERC2771Context} from "@openzeppelin/contracts/metatx/ERC2771Context.sol";
|
|
5
|
-
import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
|
|
6
|
-
import {Context} from "@openzeppelin/contracts/utils/Context.sol";
|
|
7
4
|
import {IJB721TiersHook} from "@bananapus/721-hook-v6/src/interfaces/IJB721TiersHook.sol";
|
|
8
5
|
import {IJB721TiersHookDeployer} from "@bananapus/721-hook-v6/src/interfaces/IJB721TiersHookDeployer.sol";
|
|
9
6
|
import {IJB721TokenUriResolver} from "@bananapus/721-hook-v6/src/interfaces/IJB721TokenUriResolver.sol";
|
|
@@ -28,7 +25,9 @@ import {JBRulesetConfig} from "@bananapus/core-v6/src/structs/JBRulesetConfig.so
|
|
|
28
25
|
import {JBOwnable} from "@bananapus/ownable-v6/src/JBOwnable.sol";
|
|
29
26
|
import {JBPermissionIds} from "@bananapus/permission-ids-v6/src/JBPermissionIds.sol";
|
|
30
27
|
import {IJBSuckerRegistry} from "@bananapus/suckers-v6/src/interfaces/IJBSuckerRegistry.sol";
|
|
31
|
-
import {
|
|
28
|
+
import {ERC2771Context} from "@openzeppelin/contracts/metatx/ERC2771Context.sol";
|
|
29
|
+
import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
|
|
30
|
+
import {Context} from "@openzeppelin/contracts/utils/Context.sol";
|
|
32
31
|
|
|
33
32
|
import {ICTDeployer} from "./interfaces/ICTDeployer.sol";
|
|
34
33
|
import {ICTPublisher} from "./interfaces/ICTPublisher.sol";
|
|
@@ -118,127 +117,6 @@ contract CTDeployer is ERC2771Context, JBPermissioned, IJBRulesetDataHook, IERC7
|
|
|
118
117
|
PERMISSIONS.setPermissionsFor({account: address(this), permissionsData: permissionData});
|
|
119
118
|
}
|
|
120
119
|
|
|
121
|
-
//*********************************************************************//
|
|
122
|
-
// ------------------------- external views -------------------------- //
|
|
123
|
-
//*********************************************************************//
|
|
124
|
-
|
|
125
|
-
/// @notice Allow cash outs from suckers without a tax.
|
|
126
|
-
/// @dev This function is part of `IJBRulesetDataHook`, and gets called before the revnet processes a cash out.
|
|
127
|
-
/// @param context Standard Juicebox cash out context. See `JBBeforeCashOutRecordedContext`.
|
|
128
|
-
/// @return cashOutTaxRate The cash out tax rate, which influences the amount of terminal tokens which get cashed
|
|
129
|
-
/// out.
|
|
130
|
-
/// @return cashOutCount The number of project tokens that are cashed out.
|
|
131
|
-
/// @return totalSupply The total project token supply.
|
|
132
|
-
/// @return hookSpecifications The amount of funds and the data to send to cash out hooks (this contract).
|
|
133
|
-
function beforeCashOutRecordedWith(JBBeforeCashOutRecordedContext calldata context)
|
|
134
|
-
external
|
|
135
|
-
view
|
|
136
|
-
override
|
|
137
|
-
returns (
|
|
138
|
-
uint256 cashOutTaxRate,
|
|
139
|
-
uint256 cashOutCount,
|
|
140
|
-
uint256 totalSupply,
|
|
141
|
-
JBCashOutHookSpecification[] memory hookSpecifications
|
|
142
|
-
)
|
|
143
|
-
{
|
|
144
|
-
// If the cash out is from a sucker, return the full cash out amount without taxes or fees.
|
|
145
|
-
if (SUCKER_REGISTRY.isSuckerOf({projectId: context.projectId, addr: context.holder})) {
|
|
146
|
-
return (0, context.cashOutCount, context.totalSupply, hookSpecifications);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// If the ruleset has a data hook, forward the call to the datahook.
|
|
150
|
-
IJBRulesetDataHook hook = dataHookOf[context.projectId];
|
|
151
|
-
if (address(hook) == address(0)) {
|
|
152
|
-
return (context.cashOutTaxRate, context.cashOutCount, context.totalSupply, hookSpecifications);
|
|
153
|
-
}
|
|
154
|
-
// slither-disable-next-line unused-return
|
|
155
|
-
return hook.beforeCashOutRecordedWith(context);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/// @notice Forward the call to the original data hook.
|
|
159
|
-
/// @dev This function is part of `IJBRulesetDataHook`, and gets called before the revnet processes a payment.
|
|
160
|
-
/// @param context Standard Juicebox payment context. See `JBBeforePayRecordedContext`.
|
|
161
|
-
/// @return weight The weight which project tokens are minted relative to. This can be used to customize how many
|
|
162
|
-
/// tokens get minted by a payment.
|
|
163
|
-
/// @return hookSpecifications Amounts (out of what's being paid in) to be sent to pay hooks instead of being paid
|
|
164
|
-
/// into the project. Useful for automatically routing funds from a treasury as payments come in.
|
|
165
|
-
function beforePayRecordedWith(JBBeforePayRecordedContext calldata context)
|
|
166
|
-
external
|
|
167
|
-
view
|
|
168
|
-
override
|
|
169
|
-
returns (uint256 weight, JBPayHookSpecification[] memory hookSpecifications)
|
|
170
|
-
{
|
|
171
|
-
// Forward the call to the data hook.
|
|
172
|
-
IJBRulesetDataHook hook = dataHookOf[context.projectId];
|
|
173
|
-
if (address(hook) == address(0)) {
|
|
174
|
-
return (context.weight, hookSpecifications);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// Resolve the relay beneficiary — if the payer is a sucker with relay metadata,
|
|
178
|
-
// swap the beneficiary so downstream hooks see the real user.
|
|
179
|
-
address effectiveBeneficiary = JBRelayBeneficiary.resolve({
|
|
180
|
-
payer: context.payer,
|
|
181
|
-
beneficiary: context.beneficiary,
|
|
182
|
-
projectId: context.projectId,
|
|
183
|
-
metadata: context.metadata,
|
|
184
|
-
registry: SUCKER_REGISTRY
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
// If the beneficiary was swapped, create a memory copy with the new beneficiary.
|
|
188
|
-
if (effectiveBeneficiary != context.beneficiary) {
|
|
189
|
-
JBBeforePayRecordedContext memory hookContext = context;
|
|
190
|
-
hookContext.beneficiary = effectiveBeneficiary;
|
|
191
|
-
return hook.beforePayRecordedWith(hookContext);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
// slither-disable-next-line unused-return
|
|
195
|
-
return hook.beforePayRecordedWith(context);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/// @notice A flag indicating whether an address has permission to mint a project's tokens on-demand.
|
|
199
|
-
/// @dev A project's data hook can allow any address to mint its tokens.
|
|
200
|
-
/// @param projectId The ID of the project whose token can be minted.
|
|
201
|
-
/// @param addr The address to check the token minting permission of.
|
|
202
|
-
/// @return flag A flag indicating whether the address has permission to mint the project's tokens on-demand.
|
|
203
|
-
function hasMintPermissionFor(uint256 projectId, JBRuleset memory, address addr) external view returns (bool flag) {
|
|
204
|
-
// If the address is a sucker for this project.
|
|
205
|
-
return SUCKER_REGISTRY.isSuckerOf({projectId: projectId, addr: addr});
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
/// @dev Make sure only mints can be received.
|
|
209
|
-
function onERC721Received(
|
|
210
|
-
address operator,
|
|
211
|
-
address from,
|
|
212
|
-
uint256 tokenId,
|
|
213
|
-
bytes calldata data
|
|
214
|
-
)
|
|
215
|
-
external
|
|
216
|
-
view
|
|
217
|
-
returns (bytes4)
|
|
218
|
-
{
|
|
219
|
-
data;
|
|
220
|
-
tokenId;
|
|
221
|
-
operator;
|
|
222
|
-
|
|
223
|
-
// Make sure the 721 received is the JBProjects contract.
|
|
224
|
-
if (msg.sender != address(PROJECTS)) revert();
|
|
225
|
-
// Make sure the 721 is being received as a mint.
|
|
226
|
-
if (from != address(0)) revert();
|
|
227
|
-
return IERC721Receiver.onERC721Received.selector;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
//*********************************************************************//
|
|
231
|
-
// -------------------------- public views --------------------------- //
|
|
232
|
-
//*********************************************************************//
|
|
233
|
-
|
|
234
|
-
/// @notice Indicates if this contract adheres to the specified interface.
|
|
235
|
-
/// @dev See `IERC165.supportsInterface`.
|
|
236
|
-
/// @return A flag indicating if the provided interface ID is supported.
|
|
237
|
-
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
|
|
238
|
-
return interfaceId == type(ICTDeployer).interfaceId || interfaceId == type(IJBRulesetDataHook).interfaceId
|
|
239
|
-
|| interfaceId == type(IERC721Receiver).interfaceId;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
120
|
//*********************************************************************//
|
|
243
121
|
// ---------------------- external transactions ---------------------- //
|
|
244
122
|
//*********************************************************************//
|
|
@@ -408,6 +286,118 @@ contract CTDeployer is ERC2771Context, JBPermissioned, IJBRulesetDataHook, IERC7
|
|
|
408
286
|
});
|
|
409
287
|
}
|
|
410
288
|
|
|
289
|
+
//*********************************************************************//
|
|
290
|
+
// ------------------------- external views -------------------------- //
|
|
291
|
+
//*********************************************************************//
|
|
292
|
+
|
|
293
|
+
/// @notice Allow cash outs from suckers without a tax.
|
|
294
|
+
/// @dev This function is part of `IJBRulesetDataHook`, and gets called before the revnet processes a cash out.
|
|
295
|
+
/// @param context Standard Juicebox cash out context. See `JBBeforeCashOutRecordedContext`.
|
|
296
|
+
/// @return cashOutTaxRate The cash out tax rate, which influences the amount of terminal tokens which get cashed
|
|
297
|
+
/// out.
|
|
298
|
+
/// @return cashOutCount The number of project tokens that are cashed out.
|
|
299
|
+
/// @return totalSupply The total project token supply.
|
|
300
|
+
/// @return surplusValue The surplus value to use for the bonding curve calculation.
|
|
301
|
+
/// @return hookSpecifications The amount of funds and the data to send to cash out hooks (this contract).
|
|
302
|
+
function beforeCashOutRecordedWith(JBBeforeCashOutRecordedContext calldata context)
|
|
303
|
+
external
|
|
304
|
+
view
|
|
305
|
+
override
|
|
306
|
+
returns (
|
|
307
|
+
uint256 cashOutTaxRate,
|
|
308
|
+
uint256 cashOutCount,
|
|
309
|
+
uint256 totalSupply,
|
|
310
|
+
uint256 surplusValue,
|
|
311
|
+
JBCashOutHookSpecification[] memory hookSpecifications
|
|
312
|
+
)
|
|
313
|
+
{
|
|
314
|
+
// If the cash out is from a sucker, return the full cash out amount without taxes or fees.
|
|
315
|
+
if (SUCKER_REGISTRY.isSuckerOf({projectId: context.projectId, addr: context.holder})) {
|
|
316
|
+
return (0, context.cashOutCount, context.totalSupply, context.surplus.value, hookSpecifications);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// If the ruleset has a data hook, forward the call to the datahook.
|
|
320
|
+
IJBRulesetDataHook hook = dataHookOf[context.projectId];
|
|
321
|
+
if (address(hook) == address(0)) {
|
|
322
|
+
return (
|
|
323
|
+
context.cashOutTaxRate,
|
|
324
|
+
context.cashOutCount,
|
|
325
|
+
context.totalSupply,
|
|
326
|
+
context.surplus.value,
|
|
327
|
+
hookSpecifications
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
// slither-disable-next-line unused-return
|
|
331
|
+
return hook.beforeCashOutRecordedWith(context);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/// @notice Forward the call to the original data hook.
|
|
335
|
+
/// @dev This function is part of `IJBRulesetDataHook`, and gets called before the revnet processes a payment.
|
|
336
|
+
/// @param context Standard Juicebox payment context. See `JBBeforePayRecordedContext`.
|
|
337
|
+
/// @return weight The weight which project tokens are minted relative to. This can be used to customize how many
|
|
338
|
+
/// tokens get minted by a payment.
|
|
339
|
+
/// @return hookSpecifications Amounts (out of what's being paid in) to be sent to pay hooks instead of being paid
|
|
340
|
+
/// into the project. Useful for automatically routing funds from a treasury as payments come in.
|
|
341
|
+
function beforePayRecordedWith(JBBeforePayRecordedContext calldata context)
|
|
342
|
+
external
|
|
343
|
+
view
|
|
344
|
+
override
|
|
345
|
+
returns (uint256 weight, JBPayHookSpecification[] memory hookSpecifications)
|
|
346
|
+
{
|
|
347
|
+
// Forward the call to the data hook.
|
|
348
|
+
IJBRulesetDataHook hook = dataHookOf[context.projectId];
|
|
349
|
+
if (address(hook) == address(0)) {
|
|
350
|
+
return (context.weight, hookSpecifications);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// slither-disable-next-line unused-return
|
|
354
|
+
return hook.beforePayRecordedWith(context);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/// @notice A flag indicating whether an address has permission to mint a project's tokens on-demand.
|
|
358
|
+
/// @dev A project's data hook can allow any address to mint its tokens.
|
|
359
|
+
/// @param projectId The ID of the project whose token can be minted.
|
|
360
|
+
/// @param addr The address to check the token minting permission of.
|
|
361
|
+
/// @return flag A flag indicating whether the address has permission to mint the project's tokens on-demand.
|
|
362
|
+
function hasMintPermissionFor(uint256 projectId, JBRuleset memory, address addr) external view returns (bool flag) {
|
|
363
|
+
// If the address is a sucker for this project.
|
|
364
|
+
return SUCKER_REGISTRY.isSuckerOf({projectId: projectId, addr: addr});
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/// @dev Make sure only mints can be received.
|
|
368
|
+
function onERC721Received(
|
|
369
|
+
address operator,
|
|
370
|
+
address from,
|
|
371
|
+
uint256 tokenId,
|
|
372
|
+
bytes calldata data
|
|
373
|
+
)
|
|
374
|
+
external
|
|
375
|
+
view
|
|
376
|
+
returns (bytes4)
|
|
377
|
+
{
|
|
378
|
+
data;
|
|
379
|
+
tokenId;
|
|
380
|
+
operator;
|
|
381
|
+
|
|
382
|
+
// Make sure the 721 received is the JBProjects contract.
|
|
383
|
+
if (msg.sender != address(PROJECTS)) revert();
|
|
384
|
+
// Make sure the 721 is being received as a mint.
|
|
385
|
+
if (from != address(0)) revert();
|
|
386
|
+
return IERC721Receiver.onERC721Received.selector;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
//*********************************************************************//
|
|
390
|
+
// -------------------------- public views --------------------------- //
|
|
391
|
+
//*********************************************************************//
|
|
392
|
+
|
|
393
|
+
/// @notice Indicates if this contract adheres to the specified interface.
|
|
394
|
+
/// @dev See `IERC165.supportsInterface`.
|
|
395
|
+
/// @return A flag indicating if the provided interface ID is supported.
|
|
396
|
+
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
|
|
397
|
+
return interfaceId == type(ICTDeployer).interfaceId || interfaceId == type(IJBRulesetDataHook).interfaceId
|
|
398
|
+
|| interfaceId == type(IERC721Receiver).interfaceId;
|
|
399
|
+
}
|
|
400
|
+
|
|
411
401
|
//*********************************************************************//
|
|
412
402
|
// --------------------- internal transactions ----------------------- //
|
|
413
403
|
//*********************************************************************//
|
|
@@ -451,7 +441,7 @@ contract CTDeployer is ERC2771Context, JBPermissioned, IJBRulesetDataHook, IERC7
|
|
|
451
441
|
}
|
|
452
442
|
|
|
453
443
|
//*********************************************************************//
|
|
454
|
-
//
|
|
444
|
+
// -------------------------- internal views ------------------------- //
|
|
455
445
|
//*********************************************************************//
|
|
456
446
|
|
|
457
447
|
/// @dev ERC-2771 specifies the context as being a single address (20 bytes).
|