@bananapus/omnichain-deployers-v6 0.0.26 → 0.0.28
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 +14 -141
- package/ARCHITECTURE.md +17 -66
- package/AUDIT_INSTRUCTIONS.md +12 -55
- package/README.md +30 -8
- package/RISKS.md +5 -1
- package/SKILLS.md +10 -25
- package/USER_JOURNEYS.md +38 -45
- package/foundry.toml +2 -0
- package/package.json +31 -31
- package/references/operations.md +1 -1
- package/references/runtime.md +1 -1
- package/src/JBOmnichainDeployer.sol +7 -2
- package/src/structs/JBDeployerHookConfig.sol +0 -1
- package/src/structs/JBOmnichain721Config.sol +0 -1
- package/src/structs/JBSuckerDeploymentConfig.sol +0 -1
- package/src/structs/JBTiered721HookConfig.sol +0 -1
- package/test/audit/CodexNemesisAudit.t.sol +13 -5
package/ADMINISTRATION.md
CHANGED
|
@@ -1,156 +1,29 @@
|
|
|
1
1
|
# Administration
|
|
2
2
|
|
|
3
|
-
Admin privileges and their scope in nana-omnichain-deployers-v6.
|
|
4
|
-
|
|
5
3
|
## At A Glance
|
|
6
4
|
|
|
7
5
|
| Item | Details |
|
|
8
|
-
|
|
9
|
-
| Scope | Omnichain
|
|
10
|
-
|
|
|
11
|
-
| Highest-risk actions |
|
|
12
|
-
| Recovery posture |
|
|
13
|
-
|
|
14
|
-
## Routine Operations
|
|
15
|
-
|
|
16
|
-
- Validate all deploy-time hook choices, 721 settings, and sucker configuration before using `launchProjectFor` or `launchRulesetsFor`.
|
|
17
|
-
- Keep the distinction clear between per-ruleset composed hooks and the deployer's permanent proxy role.
|
|
18
|
-
- Use the deployer when you want its tax-free sucker and mint-permission behavior; otherwise, do not assume it is a drop-in replacement for arbitrary hook wiring.
|
|
19
|
-
|
|
20
|
-
## One-Way Or High-Risk Actions
|
|
21
|
-
|
|
22
|
-
- Constructor-time wildcard permissions and immutable references on the deployer cannot be changed afterward.
|
|
23
|
-
- Launch-time hook composition choices determine how future pay and cash-out flows are merged for that ruleset.
|
|
24
|
-
- A bad omnichain deployment can leave a project with a cross-chain shape that is expensive to unwind operationally.
|
|
25
|
-
|
|
26
|
-
## Recovery Notes
|
|
27
|
-
|
|
28
|
-
- If the project is still administratively flexible, queue new rulesets or use project-level migration paths to move to corrected hook composition.
|
|
29
|
-
- If the deployer's own immutable assumptions are wrong, recovery means deploying a new deployer path rather than trying to hot-fix the existing one.
|
|
30
|
-
|
|
31
|
-
## Roles
|
|
32
|
-
|
|
33
|
-
| Role | How Assigned | Scope |
|
|
34
|
-
|------|-------------|-------|
|
|
35
|
-
| Project owner | Holds the project's ERC-721 (minted by `JBProjects`) | Per-project. Can delegate via `JBPermissions`. |
|
|
36
|
-
| Permitted operator | Granted specific permission IDs by the project owner through `JBPermissions` | Per-project, per-permission. ROOT (1) grants all. Wildcard projectId=0 grants across all projects. |
|
|
37
|
-
| Registered sucker | Deployed via `JBSuckerRegistry.deploySuckersFor` (requires DEPLOY_SUCKERS permission) | Per-project. Gets 0% cash-out tax and mint permission automatically. |
|
|
38
|
-
| JBSuckerRegistry | Set at construction, granted MAP_SUCKER_TOKEN for all projects (projectId=0 wildcard) | Protocol-wide. Maps tokens for sucker bridging. |
|
|
39
|
-
|
|
40
|
-
## Privileged Functions
|
|
41
|
-
|
|
42
|
-
### JBOmnichainDeployer
|
|
43
|
-
|
|
44
|
-
| Function | Required Role | Permission ID | Scope | What It Does |
|
|
45
|
-
|----------|--------------|---------------|-------|--------------|
|
|
46
|
-
| `deploySuckersFor` | Project owner or operator | `DEPLOY_SUCKERS` | Per-project | Deploys new cross-chain suckers for an existing project via the sucker registry. The same operation also applies token mappings on the new suckers, so existing projects must already have the registry arranged as an authorized `MAP_SUCKER_TOKEN` operator for that project. |
|
|
47
|
-
| `launchRulesetsFor` | Project owner or operator | `LAUNCH_RULESETS` + `SET_TERMINALS` | Per-project | Deploys a 721 tiers hook, launches new rulesets with terminal configuration for an existing project. Has a simplified overload without `deploy721Config`. |
|
|
48
|
-
| `queueRulesetsOf` | Project owner or operator | `QUEUE_RULESETS` | Per-project | Queues new rulesets for an existing project. If tiers provided, deploys a new 721 hook. Otherwise, carries forward the 721 hook from the latest ruleset. Has a simplified overload without `deploy721Config`. |
|
|
49
|
-
|
|
50
|
-
### Permissionless Functions
|
|
51
|
-
|
|
52
|
-
| Function | Who Can Call | What It Does |
|
|
53
|
-
|----------|-------------|--------------|
|
|
54
|
-
| `launchProjectFor` | Anyone | Creates a new project with a 721 tiers hook (even with 0 tiers) and suckers. The ERC-721 is minted to the specified `owner`. Returns `(projectId, hook, suckers)`. Has a simplified overload without `deploy721Config` that uses a default empty-tier 721 config. |
|
|
55
|
-
| `beforePayRecordedWith` | JBMultiTerminal (via controller) | View function: always calls the 721 hook (when its address is non-zero) for specs, then calls the custom hook (if configured and `useDataHookForPay` is set) with the reduced amount. Merges results. |
|
|
56
|
-
| `beforeCashOutRecordedWith` | JBMultiTerminal (via controller) | View function: returns 0% cash-out tax for registered suckers. Calls 721 hook first (from `_tiered721HookOf`, if `useDataHookForCashOut: true`), then calls custom hook (from `_extraDataHookOf`, if `useDataHookForCashOut: true`) with the updated values from the 721 hook. Both hooks' specifications are merged. If neither has the flag set, returns original values. |
|
|
57
|
-
| `hasMintPermissionFor` | JBController | View function: returns true for registered suckers, otherwise checks the custom hook in `_extraDataHookOf`. |
|
|
58
|
-
| `extraDataHookOf` | Anyone | View function: returns the stored `JBDeployerHookConfig` for a project/ruleset pair (the custom data hook). |
|
|
59
|
-
| `tiered721HookOf` | Anyone | View function: returns the stored 721 hook and `useDataHookForCashOut` flag for a project/ruleset pair. |
|
|
60
|
-
|
|
61
|
-
## Deployment Administration
|
|
62
|
-
|
|
63
|
-
**Who can deploy omnichain projects:** Anyone. The `launchProjectFor` function is permissionless. The caller specifies an `owner` address that receives the project ERC-721.
|
|
64
|
-
|
|
65
|
-
**Deployment flow:**
|
|
66
|
-
1. The deployer deploys a 721 tiers hook via `HOOK_DEPLOYER` (even with 0 tiers).
|
|
67
|
-
2. It configures rulesets via `_setup721()`, sets itself as the data hook wrapper.
|
|
68
|
-
3. It calls `controller.launchProjectFor`, which mints the project ERC-721 to `address(this)`.
|
|
69
|
-
4. It transfers 721 hook ownership to the project (requires project NFT to exist).
|
|
70
|
-
5. It optionally deploys suckers via the sucker registry.
|
|
71
|
-
6. It transfers the project ERC-721 to the specified `owner`.
|
|
72
|
-
|
|
73
|
-
**Configurable parameters at deployment:**
|
|
74
|
-
- Ruleset configurations (duration, weight, decay, approval hooks, splits, fund access limits, metadata flags).
|
|
75
|
-
- Terminal configurations (which terminals accept which tokens).
|
|
76
|
-
- 721 tiers hook configuration (tier pricing, supply, metadata, categories — can be empty for 0 tiers).
|
|
77
|
-
- Sucker deployment configuration (which chains, which deployers, token mappings).
|
|
78
|
-
- Salt for deterministic cross-chain address matching.
|
|
79
|
-
|
|
80
|
-
## Cross-Chain Controls
|
|
81
|
-
|
|
82
|
-
| Action | Who | Mechanism |
|
|
83
|
-
|--------|-----|-----------|
|
|
84
|
-
| Deploy suckers for existing project | Project owner or DEPLOY_SUCKERS operator | `deploySuckersFor` calls `SUCKER_REGISTRY.deploySuckersFor`; because the registry also applies the initial mappings, existing projects must pair this with a project-level `MAP_SUCKER_TOKEN` arrangement for the registry |
|
|
85
|
-
| Deploy suckers during project launch | Project deployer (anyone) | Included in `launchProjectFor` if `salt != bytes32(0)` |
|
|
86
|
-
| Map sucker tokens | JBSuckerRegistry | Granted MAP_SUCKER_TOKEN at construction with projectId=0 wildcard |
|
|
87
|
-
| Grant 0% cash-out tax to suckers | Automatic | `beforeCashOutRecordedWith` checks `SUCKER_REGISTRY.isSuckerOf` |
|
|
88
|
-
| Grant mint permission to suckers | Automatic | `hasMintPermissionFor` checks `SUCKER_REGISTRY.isSuckerOf` |
|
|
89
|
-
|
|
90
|
-
**Existing-project operator note:** `deploySuckersFor` looks like a deployment-only action at the top level, but it is intentionally a deploy-and-map flow. If an existing project delegates `DEPLOY_SUCKERS` without also arranging `MAP_SUCKER_TOKEN` for the registry, the transaction will fail once the registry reaches the mapping step.
|
|
91
|
-
|
|
92
|
-
**Cross-chain determinism:** The salt for sucker deployment is combined with `_msgSender()` (`keccak256(abi.encode(salt, _msgSender()))`). Deploying from the same sender address with the same salt on each chain produces matching sucker addresses.
|
|
93
|
-
|
|
94
|
-
## Data Hook Proxy Pattern
|
|
95
|
-
|
|
96
|
-
`JBOmnichainDeployer` acts as a data hook proxy. When set as a project's `dataHook` in ruleset metadata, it wraps up to two inner hooks:
|
|
97
|
-
|
|
98
|
-
1. **721 tiers hook** (`_tiered721HookOf[projectId][rulesetId]`): Handles NFT-based pay/cashout logic.
|
|
99
|
-
2. **Extra data hook** (`_extraDataHookOf[projectId][rulesetId]`): An optional custom hook for additional pay/cashout logic.
|
|
100
|
-
|
|
101
|
-
### Call flow for `beforePayRecordedWith`:
|
|
102
|
-
|
|
103
|
-
```
|
|
104
|
-
Terminal -> Controller -> JBOmnichainDeployer.beforePayRecordedWith()
|
|
105
|
-
1. Call 721 hook's beforePayRecordedWith (always, when its address is non-zero)
|
|
106
|
-
-> Get pay hook specifications and the total split amount
|
|
107
|
-
2. Call extra hook's beforePayRecordedWith (if useDataHookForPay is set on extra hook config)
|
|
108
|
-
-> Amount is reduced by what the 721 hook already allocated
|
|
109
|
-
3. Scale the extra hook's weight proportionally to the project's share of the payment
|
|
110
|
-
4. Merge both hooks' specifications and return
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
### Call flow for `beforeCashOutRecordedWith`:
|
|
114
|
-
|
|
115
|
-
```
|
|
116
|
-
Terminal -> Controller -> JBOmnichainDeployer.beforeCashOutRecordedWith()
|
|
117
|
-
1. Check if caller is a registered sucker -> return 0% cash-out tax (fee-free bridging)
|
|
118
|
-
2. Call 721 hook's beforeCashOutRecordedWith (if useDataHookForCashOut is set on 721 config)
|
|
119
|
-
-> Get cashout hook specifications and adjusted values
|
|
120
|
-
3. Call extra hook's beforeCashOutRecordedWith (if useDataHookForCashOut is set on extra config)
|
|
121
|
-
-> Receives updated values from 721 hook
|
|
122
|
-
4. Merge both hooks' specifications and return
|
|
123
|
-
```
|
|
6
|
+
| --- | --- |
|
|
7
|
+
| Scope | Omnichain launch orchestration and wrapper behavior |
|
|
8
|
+
| Control posture | Mixed deployer logic, project permissions, and registry trust |
|
|
9
|
+
| Highest-risk actions | Wrong hook composition, wrong sucker wiring, and bad registry trust |
|
|
10
|
+
| Recovery posture | Often requires redeploying or re-launching around bad wiring |
|
|
124
11
|
|
|
125
|
-
|
|
12
|
+
## Purpose
|
|
126
13
|
|
|
127
|
-
|
|
14
|
+
This repo controls how omnichain projects are launched and wrapped, not the low-level runtime logic of suckers or 721 hooks.
|
|
128
15
|
|
|
129
|
-
##
|
|
16
|
+
## Control Model
|
|
130
17
|
|
|
131
|
-
|
|
18
|
+
- launch paths are largely permissionless for new projects
|
|
19
|
+
- later ruleset changes depend on project permissions
|
|
20
|
+
- registry and sucker trust surfaces can widen authority if misconfigured
|
|
132
21
|
|
|
133
|
-
|
|
134
|
-
|----------|------|-----------|
|
|
135
|
-
| `PROJECTS` | `IJBProjects` | The ERC-721 contract for project ownership. |
|
|
136
|
-
| `HOOK_DEPLOYER` | `IJB721TiersHookDeployer` | The deployer for 721 tiers hooks. |
|
|
137
|
-
| `SUCKER_REGISTRY` | `IJBSuckerRegistry` | The registry for deploying and tracking suckers. |
|
|
138
|
-
| `PERMISSIONS` | `IJBPermissions` | The permissions contract (inherited from JBPermissioned). |
|
|
139
|
-
| Trusted forwarder | `address` | The ERC-2771 trusted forwarder for meta-transactions. |
|
|
140
|
-
| MAP_SUCKER_TOKEN grant | Permission | Granted to SUCKER_REGISTRY at construction for all projects (projectId=0). Cannot be revoked by this contract. |
|
|
22
|
+
## Recovery
|
|
141
23
|
|
|
142
|
-
|
|
24
|
+
- bad launch wiring usually means a new deployment path rather than a local patch
|
|
143
25
|
|
|
144
26
|
## Admin Boundaries
|
|
145
27
|
|
|
146
|
-
|
|
28
|
+
- this repo does not override locked runtime behavior in sibling repos
|
|
147
29
|
|
|
148
|
-
- **Cannot upgrade the deployer.** JBOmnichainDeployer has no upgrade mechanism, proxy pattern, or self-destruct.
|
|
149
|
-
- **Cannot change immutable references.** PROJECTS, HOOK_DEPLOYER, SUCKER_REGISTRY, PERMISSIONS, and the trusted forwarder are all immutable.
|
|
150
|
-
- **Cannot modify stored data hooks.** Once a ruleset's hooks are stored in `_tiered721HookOf` and `_extraDataHookOf`, they cannot be changed. New rulesets can use different hooks, but existing mappings are permanent.
|
|
151
|
-
- **Cannot bypass permission checks.** All post-deployment admin functions require JBPermissions verification against the project owner.
|
|
152
|
-
- **Cannot revoke sucker privileges.** Once a sucker is registered in JBSuckerRegistry, it automatically gets 0% cash-out tax and mint permission for its project. Revocation must happen at the registry level.
|
|
153
|
-
- **Cannot set the deployer as its own data hook.** `_setup721()` explicitly reverts with `JBOmnichainDeployer_InvalidHook` if a hook is `address(this)`.
|
|
154
|
-
- **Cannot use a controller that doesn't match the project.** `_validateController` reverts with `JBOmnichainDeployer_ControllerMismatch` if the provided controller is not the project's actual controller in the directory.
|
|
155
|
-
- **Cannot steal project ownership during deployment.** The deployer holds the project ERC-721 only transiently and transfers it to the specified owner in the same transaction.
|
|
156
|
-
- **Cannot drain funds.** The deployer never holds or manages token balances. It only orchestrates configuration.
|
package/ARCHITECTURE.md
CHANGED
|
@@ -2,79 +2,30 @@
|
|
|
2
2
|
|
|
3
3
|
## Purpose
|
|
4
4
|
|
|
5
|
-
`nana-omnichain-deployers-v6`
|
|
5
|
+
`nana-omnichain-deployers-v6` packages a Juicebox project, a 721 hook, and sucker deployment into one omnichain launch surface.
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## System Overview
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
- The repo composes `nana-721-hook-v6` and `nana-suckers-v6`; it should not duplicate their internal logic.
|
|
11
|
-
- Project accounting still happens in the core protocol.
|
|
9
|
+
`JBOmnichainDeployer` launches the project, stores per-ruleset hook composition, and wraps sucker behavior so bridge-triggered flows can bypass project-specific logic where intended.
|
|
12
10
|
|
|
13
|
-
##
|
|
11
|
+
## Core Invariants
|
|
14
12
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
| `IJBOmnichainDeployer` | Public deployer and inspection interface |
|
|
13
|
+
- launch wiring must match the intended omnichain project shape
|
|
14
|
+
- hook composition must stay consistent with the created ruleset IDs
|
|
15
|
+
- sucker-specific privileged paths must remain limited to trusted suckers
|
|
16
|
+
- project NFT ownership and hook ownership must end in the intended place
|
|
20
17
|
|
|
21
|
-
##
|
|
18
|
+
## Trust Boundaries
|
|
22
19
|
|
|
23
|
-
|
|
20
|
+
- bridge runtime trust lives in `nana-suckers-v6`
|
|
21
|
+
- 721 runtime trust lives in `nana-721-hook-v6`
|
|
22
|
+
- this repo mainly owns orchestration and wrapper semantics
|
|
24
23
|
|
|
25
|
-
|
|
26
|
-
caller
|
|
27
|
-
-> launch project or queue rulesets through the deployer
|
|
28
|
-
-> deployer installs itself as the ruleset data hook
|
|
29
|
-
-> deployer deploys or carries forward the 721 hook
|
|
30
|
-
-> deployer optionally deploys sucker pairs with deterministic salts
|
|
31
|
-
-> project ownership is transferred to the intended owner
|
|
32
|
-
```
|
|
24
|
+
## Security Model
|
|
33
25
|
|
|
34
|
-
|
|
26
|
+
- the main risks are hook composition, ruleset ID prediction, and registry-trusted sucker bypasses
|
|
27
|
+
- this repo is not the source of underlying bridge or 721 behavior, but it can wire them together incorrectly
|
|
35
28
|
|
|
36
|
-
|
|
29
|
+
## Source Map
|
|
37
30
|
|
|
38
|
-
|
|
39
|
-
2. **Current active ruleset** — fallback when no qualifying queued ruleset exists.
|
|
40
|
-
|
|
41
|
-
This ensures that a recently queued (and approved) ruleset's hook config takes precedence over a potentially stale active ruleset. The `useDataHookForCashOut` flag is also preserved from whichever source ruleset is selected.
|
|
42
|
-
|
|
43
|
-
### Pay And Cash-Out Wrapping
|
|
44
|
-
|
|
45
|
-
```text
|
|
46
|
-
runtime callback
|
|
47
|
-
-> if the actor is a registered sucker, return the special tax-free / mint-enabled path
|
|
48
|
-
-> otherwise call the 721 hook first when configured
|
|
49
|
-
-> then call the extra data hook when configured
|
|
50
|
-
-> merge hook specs in order and return the combined result
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
## Critical Invariants
|
|
54
|
-
|
|
55
|
-
- Suckers must be able to bridge without getting trapped behind custom cash-out policies.
|
|
56
|
-
- Hook order matters: the 721 hook runs first, and the extra hook receives the updated context.
|
|
57
|
-
- The deployer's predicted ruleset IDs must stay aligned with `JBRulesets` behavior; the storage keys depend on it.
|
|
58
|
-
- Every project launched through this repo gets a 721 hook surface, even if it starts with zero tiers.
|
|
59
|
-
- Carry-forward must prefer the latest approved queued ruleset over the current ruleset to avoid losing hook config from a recently queued update.
|
|
60
|
-
|
|
61
|
-
## Where Complexity Lives
|
|
62
|
-
|
|
63
|
-
- This repo hides composition complexity behind a simple launch surface, which makes stale assumptions dangerous.
|
|
64
|
-
- Ruleset ID prediction is a subtle but central storage keying mechanism.
|
|
65
|
-
- The sucker exception path intentionally short-circuits normal hook composition and must stay easy to reason about.
|
|
66
|
-
|
|
67
|
-
## Dependencies
|
|
68
|
-
|
|
69
|
-
- `nana-core-v6` for project launch and hook interfaces
|
|
70
|
-
- `nana-721-hook-v6` for tiered NFT behavior
|
|
71
|
-
- `nana-suckers-v6` for cross-chain transport
|
|
72
|
-
- `nana-ownable-v6` for project-following hook ownership
|
|
73
|
-
|
|
74
|
-
## Safe Change Guide
|
|
75
|
-
|
|
76
|
-
- Review launch-time logic and runtime-hook logic together. This repo is easy to break by fixing only one side.
|
|
77
|
-
- When changing hook composition, verify both payment and cash-out ordering.
|
|
78
|
-
- If you touch ruleset ID prediction, test same-block and queued-ruleset edge cases explicitly.
|
|
79
|
-
- Keep deterministic salt handling stable across chains; address predictability is part of the feature.
|
|
80
|
-
- Treat "transparent wrapper" claims as something to prove continuously, not assume.
|
|
31
|
+
- `src/JBOmnichainDeployer.sol`
|
package/AUDIT_INSTRUCTIONS.md
CHANGED
|
@@ -1,72 +1,29 @@
|
|
|
1
1
|
# Audit Instructions
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Audit this repo as an orchestration layer that composes 721 hooks, suckers, and optional extra data hooks.
|
|
4
4
|
|
|
5
|
-
## Objective
|
|
5
|
+
## Audit Objective
|
|
6
6
|
|
|
7
7
|
Find issues that:
|
|
8
|
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
- create
|
|
8
|
+
|
|
9
|
+
- launch the wrong project shape
|
|
10
|
+
- miscompose hooks or wrapper behavior
|
|
11
|
+
- grant privileged sucker behavior to the wrong addresses
|
|
12
|
+
- create cross-chain drift or bad deterministic assumptions
|
|
13
13
|
|
|
14
14
|
## Scope
|
|
15
15
|
|
|
16
16
|
In scope:
|
|
17
|
-
- `src/JBOmnichainDeployer.sol`
|
|
18
|
-
- `src/interfaces/`
|
|
19
|
-
- `src/structs/`
|
|
20
|
-
- deployment scripts in `script/`
|
|
21
|
-
|
|
22
|
-
Key dependencies:
|
|
23
|
-
- `nana-core-v6`
|
|
24
|
-
- `nana-721-hook-v6`
|
|
25
|
-
- `nana-suckers-v6`
|
|
26
|
-
|
|
27
|
-
## System Model
|
|
28
|
-
|
|
29
|
-
`JBOmnichainDeployer` is a launch surface that can:
|
|
30
|
-
- create a new Juicebox project
|
|
31
|
-
- deploy and configure a 721 hook
|
|
32
|
-
- configure rulesets and terminals
|
|
33
|
-
- deploy suckers and register them for the project
|
|
34
|
-
- participate in pay or cash-out accounting as a data hook where needed
|
|
35
|
-
|
|
36
|
-
## Critical Invariants
|
|
37
17
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
2. Sucker privileges stay restricted
|
|
42
|
-
Zero-tax or mint-permission behavior intended for legitimate suckers must not be reachable by arbitrary contracts or stale registry entries.
|
|
43
|
-
|
|
44
|
-
3. Weight and accounting scaling are correct
|
|
45
|
-
If the deployer proxies or modifies hook outputs, the resulting project token issuance and reclaim math must still match intended economics.
|
|
46
|
-
|
|
47
|
-
4. Ownership transfer is complete
|
|
48
|
-
Deployer-created hooks and helper contracts must not retain silent control after initialization.
|
|
18
|
+
- `src/JBOmnichainDeployer.sol`
|
|
19
|
+
- related tests under `test/`
|
|
49
20
|
|
|
50
|
-
##
|
|
21
|
+
## Start Here
|
|
51
22
|
|
|
52
|
-
|
|
53
|
-
- empty or malformed ruleset configurations
|
|
54
|
-
- hook ownership transfer races
|
|
55
|
-
- registry-based privilege spoofing
|
|
56
|
-
- reentrancy around project launch and initialization
|
|
57
|
-
- stale assumptions when optional sucker deployment is skipped
|
|
23
|
+
1. `src/JBOmnichainDeployer.sol`
|
|
58
24
|
|
|
59
|
-
##
|
|
25
|
+
## Verification
|
|
60
26
|
|
|
61
|
-
Standard workflow:
|
|
62
27
|
- `npm install`
|
|
63
28
|
- `forge build`
|
|
64
29
|
- `forge test`
|
|
65
|
-
|
|
66
|
-
Existing tests emphasize:
|
|
67
|
-
- reentrancy and attack paths
|
|
68
|
-
- hook composition
|
|
69
|
-
- weight scaling
|
|
70
|
-
- omnichain fork and stress scenarios
|
|
71
|
-
|
|
72
|
-
High-value findings typically show either a bad project launch state or a non-sucker actor receiving omnichain-only privileges.
|
package/README.md
CHANGED
|
@@ -3,7 +3,12 @@
|
|
|
3
3
|
`@bananapus/omnichain-deployers-v6` launches Juicebox projects with cross-chain suckers and a 721 hook already wired in. It is the package you use when the default project shape should be omnichain from day one.
|
|
4
4
|
|
|
5
5
|
Docs: <https://docs.juicebox.money>
|
|
6
|
-
Architecture: [ARCHITECTURE.md](./ARCHITECTURE.md)
|
|
6
|
+
Architecture: [ARCHITECTURE.md](./ARCHITECTURE.md)
|
|
7
|
+
User journeys: [USER_JOURNEYS.md](./USER_JOURNEYS.md)
|
|
8
|
+
Skills: [SKILLS.md](./SKILLS.md)
|
|
9
|
+
Risks: [RISKS.md](./RISKS.md)
|
|
10
|
+
Administration: [ADMINISTRATION.md](./ADMINISTRATION.md)
|
|
11
|
+
Audit instructions: [AUDIT_INSTRUCTIONS.md](./AUDIT_INSTRUCTIONS.md)
|
|
7
12
|
|
|
8
13
|
## Overview
|
|
9
14
|
|
|
@@ -12,15 +17,13 @@ The deployer wraps multiple launch concerns into one surface:
|
|
|
12
17
|
- deploy or carry forward a tiered 721 hook
|
|
13
18
|
- install itself as the project's data-hook wrapper
|
|
14
19
|
- compose the 721 hook with an optional extra custom hook
|
|
15
|
-
-
|
|
20
|
+
- wrap sucker flows so project-specific cash-out taxation and mint-permission logic can be handled safely
|
|
16
21
|
- deploy suckers deterministically across chains
|
|
17
22
|
|
|
18
|
-
The wrapper exists so
|
|
23
|
+
The wrapper exists so sucker-triggered flows can be exempted from project-specific cash-out taxation and related mint-gating logic where needed while the project still keeps its own inner data hooks.
|
|
19
24
|
|
|
20
25
|
Use this repo when the default project shape is "Juicebox project plus 721 hook plus cross-chain bridge." Do not use it when a project is single-chain or does not need the wrapper semantics around suckers.
|
|
21
26
|
|
|
22
|
-
If the question is "how do suckers bridge?" start in `nana-suckers-v6`. If the question is "how does a 721 hook behave?" start in `nana-721-hook-v6`. This repo is where those components are packaged together and wrapped.
|
|
23
|
-
|
|
24
27
|
## Key Contract
|
|
25
28
|
|
|
26
29
|
| Contract | Role |
|
|
@@ -42,6 +45,20 @@ This repo owns orchestration plus runtime wrapping:
|
|
|
42
45
|
3. `nana-721-hook-v6/src/JB721TiersHook.sol`
|
|
43
46
|
4. the extra hook repo, if the deployment composes one
|
|
44
47
|
|
|
48
|
+
## Integration Traps
|
|
49
|
+
|
|
50
|
+
- this repo wraps hooks and bridge flows together, so ownership and hook-order assumptions matter as much as deployment salt
|
|
51
|
+
- ruleset ID prediction is a real implementation dependency
|
|
52
|
+
- the deployer can carry forward an existing 721 hook shape, so stale hook assumptions can leak across deployments
|
|
53
|
+
- bridge-safe wrapper behavior is part of the runtime trust model
|
|
54
|
+
|
|
55
|
+
## Where State Lives
|
|
56
|
+
|
|
57
|
+
- orchestration and wrapper logic: `JBOmnichainDeployer`
|
|
58
|
+
- bridge runtime state: `nana-suckers-v6`
|
|
59
|
+
- 721 tier state: `nana-721-hook-v6`
|
|
60
|
+
- extra hook behavior: the additional repo composed into the deployment
|
|
61
|
+
|
|
45
62
|
## Install
|
|
46
63
|
|
|
47
64
|
```bash
|
|
@@ -81,7 +98,12 @@ script/
|
|
|
81
98
|
|
|
82
99
|
## Risks And Notes
|
|
83
100
|
|
|
84
|
-
- ruleset ID prediction is part of the implementation strategy
|
|
101
|
+
- ruleset ID prediction is part of the implementation strategy
|
|
85
102
|
- hook composition order matters because the 721 hook runs before any extra custom hook
|
|
86
|
-
-
|
|
87
|
-
- deterministic salts help with cross-chain address alignment
|
|
103
|
+
- default empty-tier 721 config is convenient, but teams should still decide explicitly whether the hook participates in cash-out behavior
|
|
104
|
+
- deterministic salts only help with cross-chain address alignment when sender and configuration match exactly
|
|
105
|
+
|
|
106
|
+
## For AI Agents
|
|
107
|
+
|
|
108
|
+
- Describe this repo as an orchestration and wrapper layer, not as the source of sucker or 721 runtime behavior.
|
|
109
|
+
- Start with `JBOmnichainDeployer`, then inspect the sibling repo that owns the behavior being questioned.
|
package/RISKS.md
CHANGED
|
@@ -29,6 +29,7 @@ This file focuses on the risks in the deployer layer that launches Juicebox proj
|
|
|
29
29
|
- **Sucker cashout bypass.** Any address registered as a sucker for a project gets 0% cashout tax rate and full reclaim. If a malicious sucker is registered (via compromised `SUCKER_REGISTRY`), it can drain the project's surplus.
|
|
30
30
|
- **Weight manipulation via extra data hook.** `beforePayRecordedWith` forwards to the extra data hook, which can return any `weight`. A malicious hook can inflate token minting or set weight=0 to block minting.
|
|
31
31
|
- **721 hook amount splitting.** The deployer computes `projectAmount = context.amount.value - totalSplitAmount`. The 721 hook's returned weight (already adjusted for splits via `JB721TiersHookLib.calculateWeight`) is used directly -- no proportional scaling is applied. If the 721 hook returns a `totalSplitAmount >= context.amount.value`, `projectAmount` is set to 0 and weight becomes 0 -- no tokens are minted for the payment.
|
|
32
|
+
- **Cross-chain sender dependence in sucker deployment salts.** `deploySuckersFor` salts deployments with `keccak256(abi.encode(userSalt, _msgSender()))`. This prevents replay collisions, but it also means the same logical project deployed by different operators on different chains will not get matching deterministic sucker addresses.
|
|
32
33
|
|
|
33
34
|
## 3. Access Control
|
|
34
35
|
|
|
@@ -41,6 +42,7 @@ This file focuses on the risks in the deployer layer that launches Juicebox proj
|
|
|
41
42
|
- **Ruleset ID collision.** `_setup721` stores hook configs at `block.timestamp + i`. If `latestRulesetIdOf >= block.timestamp` (rulesets already queued this block), `queueRulesetsOf` reverts with `RulesetIdsUnpredictable`. An attacker who queues rulesets in the same block as the legitimate owner can front-run and block their queue attempt. Gas impact: `queueRulesetsOf` costs ~200-400k gas per ruleset queued. The collision only occurs when two transactions queue rulesets in the same block for the same project — race condition window is one block (~12 seconds on L1, 2 seconds on L2).
|
|
42
43
|
- **External hook revert.** `beforePayRecordedWith` and `beforeCashOutRecordedWith` call external hooks without try-catch. A reverting hook blocks all payments or cashouts for that project/ruleset. For cash-outs, if the 721 hook reverts, the custom hook is never reached (the revert propagates before it). Gas impact: the direct call into the extra data hook has no gas limit, so a gas-griefing hook can consume the entire transaction gas. The 721 hook call is similarly unbounded.
|
|
43
44
|
- **721 hook deployment revert.** `HOOK_DEPLOYER.deployHookFor` is called without try-catch. A failing deployment blocks the entire project launch.
|
|
45
|
+
- **Unexpected safe NFT receipt reverts, but non-safe transfers can still strand assets.** `onERC721Received` only accepts `PROJECTS` NFTs. Safe transfers of any other ERC-721 revert instead of being accepted. The narrower residual risk is `transferFrom` or other non-safe delivery into the deployer, which bypasses the receiver hook and has no generalized rescue path.
|
|
44
46
|
|
|
45
47
|
## 5. Reentrancy Surface
|
|
46
48
|
|
|
@@ -53,7 +55,8 @@ This file focuses on the risks in the deployer layer that launches Juicebox proj
|
|
|
53
55
|
|
|
54
56
|
- **Hook config keyed by predicted rulesetId.** Configs stored at `block.timestamp + i` must match the actual rulesetId assigned by the controller. If the controller assigns different IDs (e.g., due to approval hook delays), the stored configs become unreachable -- payments/cashouts fall through to default behavior (no 721 handling, no extra hook).
|
|
55
57
|
- **Carried-forward 721 hook on queue.** When `tiers.length == 0`, `queueRulesetsOf` carries forward the hook from a previous ruleset. The source is selected by first checking `latestQueuedOf(projectId)` — if the queued ruleset's approval status is `Approved` or `Empty` and it has a stored hook config, that config is used. Otherwise it falls back to `currentOf(projectId)`. If neither has a hook deployed through this deployer, the mapping is empty and the call reverts with `JBOmnichainDeployer_InvalidHook`. The `useDataHookForCashOut` flag is also preserved from whichever source ruleset is selected.
|
|
56
|
-
- **ERC721Receiver restriction.** `onERC721Received` only accepts from `PROJECTS`.
|
|
58
|
+
- **ERC721Receiver restriction.** `onERC721Received` only accepts from `PROJECTS`. Non-project NFTs sent via `safeTransferFrom` revert cleanly; non-safe transfers can still become stuck because the deployer has no generalized rescue function.
|
|
59
|
+
- **Empty simplified launch config reverts.** The convenience overload that derives a default 721 config from `rulesetConfigurations[0]` reverts with `JBOmnichainDeployer_NoRulesetConfigurations()` when called with an empty ruleset array.
|
|
57
60
|
- **Cross-reference: sucker registration.** The deployer grants `MAP_SUCKER_TOKEN` to `SUCKER_REGISTRY` with `projectId=0` (wildcard). This means the registry can map tokens for ALL projects deployed through this deployer. See [nana-suckers-v6 RISKS.md](../nana-suckers-v6/RISKS.md) for the full sucker lifecycle risks.
|
|
58
61
|
- **Cross-reference: core reentrancy.** The deployer delegates to `JBController` and `JBMultiTerminal` for all fund operations. See [nana-core-v6 RISKS.md](../nana-core-v6/RISKS.md) section 3 for the reentrancy surface of these contracts.
|
|
59
62
|
|
|
@@ -65,6 +68,7 @@ This file focuses on the risks in the deployer layer that launches Juicebox proj
|
|
|
65
68
|
- `beforePayRecordedWith` uses the 721 hook's weight directly (already split-adjusted by `JB721TiersHookLib.calculateWeight`), so no additional scaling is applied.
|
|
66
69
|
- Self-reference prevention: `rulesetConfigurations[i].metadata.dataHook` cannot be `address(this)` after `_setup721`.
|
|
67
70
|
- Project NFT ownership: after `_launchProjectFor`, the project NFT is owned by `owner`, not the deployer.
|
|
71
|
+
- `deploySuckersFor` uses the same `_msgSender()` on every chain when deterministic cross-chain peer symmetry is expected.
|
|
68
72
|
|
|
69
73
|
## 8. Accepted Behaviors
|
|
70
74
|
|
package/SKILLS.md
CHANGED
|
@@ -2,39 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
## Use This File For
|
|
4
4
|
|
|
5
|
-
- Use this file when the task involves
|
|
6
|
-
- Start here, then
|
|
5
|
+
- Use this file when the task involves omnichain project launch, sucker deployment, or wrapped 721-hook composition.
|
|
6
|
+
- Start here, then decide whether the issue is in launch orchestration, hook composition, or bridge-specific runtime behavior.
|
|
7
7
|
|
|
8
8
|
## Read This Next
|
|
9
9
|
|
|
10
10
|
| If you need... | Open this next |
|
|
11
11
|
|---|---|
|
|
12
|
-
| Repo overview and
|
|
13
|
-
|
|
|
14
|
-
|
|
|
15
|
-
|
|
|
16
|
-
|
|
17
|
-
## Repo Map
|
|
18
|
-
|
|
19
|
-
| Area | Where to look |
|
|
20
|
-
|---|---|
|
|
21
|
-
| Main contract | [`src/JBOmnichainDeployer.sol`](./src/JBOmnichainDeployer.sol) |
|
|
22
|
-
| Types | [`src/structs/`](./src/structs/), [`src/interfaces/`](./src/interfaces/) |
|
|
23
|
-
| Scripts | [`script/`](./script/) |
|
|
24
|
-
| Tests | [`test/`](./test/) |
|
|
12
|
+
| Repo overview and architecture | [`README.md`](./README.md), [`ARCHITECTURE.md`](./ARCHITECTURE.md) |
|
|
13
|
+
| Main deployer | [`src/JBOmnichainDeployer.sol`](./src/JBOmnichainDeployer.sol) |
|
|
14
|
+
| Bridge runtime | [`../nana-suckers-v6/src/JBSucker.sol`](../nana-suckers-v6/src/JBSucker.sol) |
|
|
15
|
+
| 721 hook runtime | [`../nana-721-hook-v6/src/JB721TiersHook.sol`](../nana-721-hook-v6/src/JB721TiersHook.sol) |
|
|
25
16
|
|
|
26
17
|
## Purpose
|
|
27
18
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
## Reference Files
|
|
31
|
-
|
|
32
|
-
- Open [`references/runtime.md`](./references/runtime.md) when you need hook-composition order, sucker exemptions, per-ruleset storage behavior, or the main runtime invariants.
|
|
33
|
-
- Open [`references/operations.md`](./references/operations.md) when you need launch and queue behavior, permission and salt assumptions, test breadcrumbs, or the common stale-data traps.
|
|
19
|
+
Orchestration and wrapper layer for launching projects with suckers and a 721 hook already wired in.
|
|
34
20
|
|
|
35
21
|
## Working Rules
|
|
36
22
|
|
|
37
|
-
- Start in [`src/JBOmnichainDeployer.sol`](./src/JBOmnichainDeployer.sol)
|
|
38
|
-
- Treat ruleset ID prediction
|
|
39
|
-
-
|
|
40
|
-
- If a task mentions 721 or custom-hook behavior, confirm whether the issue lives here or in the composed repo before patching wrapper logic.
|
|
23
|
+
- Start in [`src/JBOmnichainDeployer.sol`](./src/JBOmnichainDeployer.sol).
|
|
24
|
+
- Treat ruleset ID prediction as a real implementation dependency.
|
|
25
|
+
- Keep wrapper behavior and the underlying 721 or sucker behavior separate in your reasoning.
|
package/USER_JOURNEYS.md
CHANGED
|
@@ -1,70 +1,63 @@
|
|
|
1
1
|
# User Journeys
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## Repo Purpose
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
- deployers composing a tiered 721 hook with sucker support and optional extra hooks
|
|
7
|
-
- operators evolving rulesets without breaking sucker-safe wrapper behavior
|
|
5
|
+
This repo launches projects that are omnichain from the start.
|
|
8
6
|
|
|
9
|
-
##
|
|
7
|
+
## Primary Actors
|
|
10
8
|
|
|
11
|
-
|
|
9
|
+
- operators launching omnichain projects
|
|
10
|
+
- teams composing 721 hooks with extra data hooks
|
|
11
|
+
- auditors checking bridge-wrapper behavior
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
## Journey 1: Launch An Omnichain Project
|
|
14
14
|
|
|
15
|
-
**
|
|
16
|
-
1. Call `JBOmnichainDeployer` with the project launch config, 721 config, sucker deployment config, and any extra hook composition.
|
|
17
|
-
2. The deployer launches the base Juicebox project and either deploys or wires the tiered 721 hook.
|
|
18
|
-
3. It wraps the hook composition so future rulesets can keep sucker-safe behavior while still preserving project-specific hook logic.
|
|
19
|
-
4. Deterministic salts are used so sibling-chain deployments can be coordinated with confidence.
|
|
15
|
+
**Actor:** deployer.
|
|
20
16
|
|
|
21
|
-
|
|
17
|
+
**Intent:** launch a project with a 721 hook and suckers already wired in.
|
|
22
18
|
|
|
23
|
-
**
|
|
19
|
+
**Main Flow**
|
|
20
|
+
1. Build the intended ruleset and hook composition.
|
|
21
|
+
2. Launch the project through `JBOmnichainDeployer`.
|
|
22
|
+
3. Deploy or wire the sucker pair and transfer control to the intended owner.
|
|
24
23
|
|
|
25
|
-
**
|
|
24
|
+
**Failure Modes**
|
|
25
|
+
- wrong hook composition
|
|
26
|
+
- cross-chain deployment drift
|
|
27
|
+
- wrong sucker peer wiring
|
|
26
28
|
|
|
27
|
-
|
|
28
|
-
1. Fix the deployer inputs that drive deterministic addresses for suckers and hook packaging.
|
|
29
|
-
2. Reuse those inputs consistently on each chain.
|
|
30
|
-
3. Validate that the controller, hook ownership, and sucker expectations still line up across the resulting deployments.
|
|
29
|
+
## Journey 2: Deploying Suckers for an Existing Project
|
|
31
30
|
|
|
32
|
-
|
|
31
|
+
**Actor:** project owner who wants to add cross-chain sucker bridges to an already-launched project.
|
|
33
32
|
|
|
34
|
-
**
|
|
33
|
+
**Intent:** deploy suckers via `JBOmnichainDeployer.deploySuckersFor()` for a project that was not originally launched through the omnichain deployer (or needs additional suckers after launch).
|
|
35
34
|
|
|
36
|
-
**
|
|
35
|
+
**Background**
|
|
37
36
|
|
|
38
|
-
|
|
39
|
-
1. Queue the next ruleset through the deployer using the path that reuses the existing 721 hook (pass zero tiers).
|
|
40
|
-
2. The deployer selects the source hook: it first checks the latest queued ruleset (if approved or with no approval hook), then falls back to the current active ruleset. This prevents losing a recently queued hook config.
|
|
41
|
-
3. The `useDataHookForCashOut` flag is preserved from whichever source ruleset is selected.
|
|
42
|
-
4. Validate the controller and queued ruleset inputs before relying on the result.
|
|
43
|
-
5. Confirm the queued ruleset now points at the carried-forward hook rather than accidentally dropping the 721 layer.
|
|
37
|
+
When a project is freshly launched through the omnichain deployer, the deployer contract temporarily holds the project NFT. This means it automatically satisfies the `DEPLOY_SUCKERS` permission check because it is the project owner at that moment. After sucker deployment, it transfers the NFT to the intended owner.
|
|
44
38
|
|
|
45
|
-
|
|
39
|
+
For projects that already exist and are owned by someone else, the deployer no longer holds the project NFT. The permission check in `deploySuckersFor()` requires that the caller has `DEPLOY_SUCKERS` permission from the project owner. Since the omnichain deployer enforces this check internally via `_requirePermissionFrom`, the project owner must explicitly grant this permission before calling the function.
|
|
46
40
|
|
|
47
|
-
**
|
|
41
|
+
**Main Flow**
|
|
42
|
+
1. The project owner calls `JBPermissions.setPermissionsFor()` to grant the `DEPLOY_SUCKERS` permission (from `JBPermissionIds`) to the `JBOmnichainDeployer` contract address, scoped to their project ID.
|
|
43
|
+
2. The project owner (or any caller with the appropriate permission) calls `JBOmnichainDeployer.deploySuckersFor()` with the project ID and sucker deployment configuration.
|
|
44
|
+
3. The deployer verifies that the caller has `DEPLOY_SUCKERS` permission from the project owner.
|
|
45
|
+
4. The deployer deploys the suckers via the sucker registry and returns their addresses.
|
|
48
46
|
|
|
49
|
-
**
|
|
47
|
+
**Failure Modes**
|
|
48
|
+
- Calling `deploySuckersFor()` without first granting the deployer `DEPLOY_SUCKERS` permission will revert with a permission error.
|
|
49
|
+
- Granting the permission to the wrong address (e.g., the caller's EOA instead of the deployer contract) will not satisfy the check, since the deployer enforces permission on behalf of the project owner against its own address.
|
|
50
|
+
- Forgetting to scope the permission to the correct project ID will cause the call to fail.
|
|
50
51
|
|
|
51
|
-
**
|
|
52
|
-
1. Provide the extra-hook config when launching through `JBOmnichainDeployer`.
|
|
53
|
-
2. Let the deployer remember which composition belongs to each ruleset.
|
|
54
|
-
3. Make sure bridge flows still bypass or special-case the right tax and data-hook behavior.
|
|
52
|
+
**Key Difference from Journey 1**
|
|
55
53
|
|
|
56
|
-
|
|
54
|
+
In Journey 1 (fresh launch), permission is implicit because the deployer owns the project NFT during deployment. In Journey 2 (existing project), permission must be explicitly granted via `JBPermissions` before sucker deployment can proceed.
|
|
57
55
|
|
|
58
|
-
|
|
56
|
+
## Trust Boundaries
|
|
59
57
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
**Flow**
|
|
63
|
-
1. Queue the next ruleset through the omnichain-aware deployer surface.
|
|
64
|
-
2. Keep track of which hook stack should apply for that future ruleset.
|
|
65
|
-
3. Confirm that sucker flows still land on the mint-safe and tax-safe path the wrapper was designed to preserve.
|
|
58
|
+
- this repo wraps runtime behavior but does not replace the underlying 721 or sucker repos
|
|
66
59
|
|
|
67
60
|
## Hand-Offs
|
|
68
61
|
|
|
69
|
-
- Use
|
|
70
|
-
- Use
|
|
62
|
+
- Use `nana-suckers-v6` for bridge runtime behavior.
|
|
63
|
+
- Use `nana-721-hook-v6` for tiered NFT runtime behavior.
|
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": "@bananapus/omnichain-deployers-v6",
|
|
3
|
+
"version": "0.0.28",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/Bananapus/nana-omnichain-deployers-v6"
|
|
8
|
+
},
|
|
9
|
+
"engines": {
|
|
10
|
+
"node": ">=20.0.0"
|
|
11
|
+
},
|
|
12
|
+
"scripts": {
|
|
13
|
+
"test": "forge test",
|
|
14
|
+
"coverage": "forge coverage --match-path \"./src/*.sol\" --report lcov --report summary",
|
|
15
|
+
"deploy:mainnets": "source ./.env && export START_TIME=$(date +%s) && npx sphinx propose ./script/Deploy.s.sol --networks mainnets",
|
|
16
|
+
"deploy:testnets": "source ./.env && export START_TIME=$(date +%s) && npx sphinx propose ./script/Deploy.s.sol --networks testnets",
|
|
17
|
+
"artifacts": "source ./.env && npx sphinx artifacts --org-id 'ea165b21-7cdc-4d7b-be59-ecdd4c26bee4' --project-name 'nana-omnichain-deployers-v6'"
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"@bananapus/721-hook-v6": "^0.0.35",
|
|
21
|
+
"@bananapus/buyback-hook-v6": "^0.0.27",
|
|
22
|
+
"@bananapus/core-v6": "^0.0.34",
|
|
23
|
+
"@bananapus/ownable-v6": "^0.0.17",
|
|
24
|
+
"@bananapus/permission-ids-v6": "^0.0.17",
|
|
25
|
+
"@bananapus/suckers-v6": "^0.0.25",
|
|
26
|
+
"@openzeppelin/contracts": "^5.6.1",
|
|
27
|
+
"@uniswap/v4-core": "^1.0.2"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@bananapus/address-registry-v6": "^0.0.17",
|
|
31
|
+
"@sphinx-labs/plugins": "^0.33.2"
|
|
32
|
+
}
|
|
33
33
|
}
|
package/references/operations.md
CHANGED
|
@@ -25,4 +25,4 @@
|
|
|
25
25
|
|
|
26
26
|
- [`test/JBOmnichainDeployer.t.sol`](../test/JBOmnichainDeployer.t.sol) for baseline deploy and queue flows.
|
|
27
27
|
- [`test/TestAuditGaps.sol`](../test/TestAuditGaps.sol) for pinned edge cases.
|
|
28
|
-
- [`test/
|
|
28
|
+
- [`test/Tiered721HookComposition.t.sol`](../test/Tiered721HookComposition.t.sol) when cross-repo integration behavior matters more than isolated unit logic.
|
package/references/runtime.md
CHANGED
|
@@ -25,4 +25,4 @@
|
|
|
25
25
|
- [`test/Tiered721HookComposition.t.sol`](../test/Tiered721HookComposition.t.sol) for hook-composition behavior.
|
|
26
26
|
- [`test/JBOmnichainDeployerGuard.t.sol`](../test/JBOmnichainDeployerGuard.t.sol) and [`test/OmnichainDeployerAttacks.t.sol`](../test/OmnichainDeployerAttacks.t.sol) for safety properties.
|
|
27
27
|
- [`test/OmnichainDeployerReentrancy.t.sol`](../test/OmnichainDeployerReentrancy.t.sol) for wrapper security assumptions.
|
|
28
|
-
- [`test/OmnichainDeployerEdgeCases.t.sol`](../test/OmnichainDeployerEdgeCases.t.sol), [`test/
|
|
28
|
+
- [`test/OmnichainDeployerEdgeCases.t.sol`](../test/OmnichainDeployerEdgeCases.t.sol), [`test/JBOmnichainDeployer.t.sol`](../test/JBOmnichainDeployer.t.sol), and [`test/TestAuditGaps.sol`](../test/TestAuditGaps.sol) for edge behavior.
|
|
@@ -412,7 +412,9 @@ contract JBOmnichainDeployer is
|
|
|
412
412
|
// This prevents disproportionate reclaim when tokens bridge away but surplus stays.
|
|
413
413
|
effectiveSurplusValue = context.surplus.value
|
|
414
414
|
+ SUCKER_REGISTRY.remoteSurplusOf({
|
|
415
|
-
projectId: context.projectId,
|
|
415
|
+
projectId: context.projectId,
|
|
416
|
+
decimals: context.surplus.decimals,
|
|
417
|
+
currency: uint256(context.surplus.currency)
|
|
416
418
|
});
|
|
417
419
|
|
|
418
420
|
// Will hold the 721 hook's cash out specifications (always 0 or 1 element).
|
|
@@ -444,6 +446,7 @@ contract JBOmnichainDeployer is
|
|
|
444
446
|
hookContext.cashOutTaxRate = cashOutTaxRate;
|
|
445
447
|
hookContext.cashOutCount = cashOutCount;
|
|
446
448
|
hookContext.totalSupply = totalSupply;
|
|
449
|
+
hookContext.surplus.value = effectiveSurplusValue;
|
|
447
450
|
|
|
448
451
|
// Forward to the extra hook. It may further change the tax rate, count, and return hook specs.
|
|
449
452
|
// We discard the inner hook's effectiveSurplusValue — this contract computes the cross-chain values.
|
|
@@ -923,7 +926,9 @@ contract JBOmnichainDeployer is
|
|
|
923
926
|
/// @param projectId The ID of the project to validate the controller for.
|
|
924
927
|
/// @param controller The controller to validate.
|
|
925
928
|
function _validateController(uint256 projectId, IJBController controller) internal view {
|
|
926
|
-
|
|
929
|
+
address current = address(controller.DIRECTORY().controllerOf(projectId));
|
|
930
|
+
// Allow address(0) for fresh projects that haven't launched rulesets yet.
|
|
931
|
+
if (current != address(0) && current != address(controller)) {
|
|
927
932
|
revert JBOmnichainDeployer_ControllerMismatch();
|
|
928
933
|
}
|
|
929
934
|
}
|
|
@@ -8,7 +8,6 @@ import {JBDeploy721TiersHookConfig} from "@bananapus/721-hook-v6/src/structs/JBD
|
|
|
8
8
|
/// @param useDataHookForCashOut Whether the 721 hook should handle cash outs (via beforeCashOutRecordedWith).
|
|
9
9
|
/// @param salt A salt to use for the deterministic 721 hook deployment. Combined with `msg.sender` internally, so
|
|
10
10
|
/// cross-chain deterministic addresses require the same sender on each chain.
|
|
11
|
-
// forge-lint: disable-next-line(pascal-case-struct)
|
|
12
11
|
struct JBOmnichain721Config {
|
|
13
12
|
JBDeploy721TiersHookConfig deployTiersHookConfig;
|
|
14
13
|
bool useDataHookForCashOut;
|
|
@@ -5,7 +5,6 @@ import {JBSuckerDeployerConfig} from "@bananapus/suckers-v6/src/structs/JBSucker
|
|
|
5
5
|
|
|
6
6
|
/// @custom:member deployerConfigurations The information for how to suck tokens to other chains.
|
|
7
7
|
/// @custom:member salt The salt to use for creating suckers so that they use the same address across chains.
|
|
8
|
-
// forge-lint: disable-next-line(pascal-case-struct)
|
|
9
8
|
struct JBSuckerDeploymentConfig {
|
|
10
9
|
JBSuckerDeployerConfig[] deployerConfigurations;
|
|
11
10
|
bytes32 salt;
|
|
@@ -57,27 +57,35 @@ contract CodexNemesisAudit is Test {
|
|
|
57
57
|
vm.mockCall(hookAddr, abi.encodeWithSelector(IJBOwnable.transferOwnershipToProject.selector), abi.encode());
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
function
|
|
60
|
+
function test_poc_launchRulesetsFor_succeedsWhenProjectHasNoControllerYet() public {
|
|
61
61
|
JBOmnichainDeployer deployer =
|
|
62
62
|
new JBOmnichainDeployer(mockSuckerRegistry, hookDeployer, permissions, projects, address(0));
|
|
63
63
|
|
|
64
64
|
vm.mockCall(
|
|
65
65
|
address(controller), abi.encodeWithSelector(IJBController.DIRECTORY.selector), abi.encode(directory)
|
|
66
66
|
);
|
|
67
|
-
// A freshly created project with no controller yet
|
|
68
|
-
// but the deployer rejects it before the controller can set itself as first controller.
|
|
67
|
+
// A freshly created project with no controller yet — the M-14 fix allows address(0) through.
|
|
69
68
|
vm.mockCall(
|
|
70
69
|
address(directory),
|
|
71
70
|
abi.encodeWithSelector(IJBDirectory.controllerOf.selector, PROJECT_ID),
|
|
72
71
|
abi.encode(IERC165(address(0)))
|
|
73
72
|
);
|
|
73
|
+
// Mock controller.launchRulesetsFor to return a rulesetId.
|
|
74
|
+
vm.mockCall(
|
|
75
|
+
address(controller),
|
|
76
|
+
abi.encodeWithSelector(IJBController.launchRulesetsFor.selector),
|
|
77
|
+
abi.encode(uint256(1))
|
|
78
|
+
);
|
|
74
79
|
|
|
75
80
|
JBRulesetConfig[] memory configs = new JBRulesetConfig[](1);
|
|
76
81
|
configs[0] = _makeRulesetConfig();
|
|
77
82
|
|
|
78
83
|
vm.prank(projectOwner);
|
|
79
|
-
|
|
80
|
-
|
|
84
|
+
(uint256 rulesetId,) =
|
|
85
|
+
deployer.launchRulesetsFor(PROJECT_ID, configs, new JBTerminalConfig[](0), "memo", controller);
|
|
86
|
+
|
|
87
|
+
// Verify the call succeeded and returned a valid rulesetId.
|
|
88
|
+
assertGt(rulesetId, 0, "launchRulesetsFor should succeed for fresh project with no controller");
|
|
81
89
|
}
|
|
82
90
|
|
|
83
91
|
function test_poc_deploySuckersFor_requiresHiddenPermissionForDeployerItself() public {
|