@bananapus/omnichain-deployers-v6 0.0.27 → 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 +10 -59
- package/ARCHITECTURE.md +11 -90
- package/AUDIT_INSTRUCTIONS.md +8 -63
- package/README.md +11 -13
- package/SKILLS.md +10 -29
- package/USER_JOURNEYS.md +31 -110
- package/package.json +1 -1
- package/src/JBOmnichainDeployer.sol +7 -2
- package/test/audit/CodexNemesisAudit.t.sol +13 -5
package/ADMINISTRATION.md
CHANGED
|
@@ -4,75 +4,26 @@
|
|
|
4
4
|
|
|
5
5
|
| Item | Details |
|
|
6
6
|
| --- | --- |
|
|
7
|
-
| Scope | Omnichain
|
|
8
|
-
| Control posture | Mixed deployer
|
|
9
|
-
| Highest-risk actions |
|
|
10
|
-
| Recovery posture |
|
|
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 |
|
|
11
11
|
|
|
12
12
|
## Purpose
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
This repo controls how omnichain projects are launched and wrapped, not the low-level runtime logic of suckers or 721 hooks.
|
|
15
15
|
|
|
16
16
|
## Control Model
|
|
17
17
|
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
- Sucker deployment requires project-local `DEPLOY_SUCKERS` permission.
|
|
22
|
-
- Hook composition data is stored by ruleset and then used at runtime.
|
|
23
|
-
|
|
24
|
-
## Roles
|
|
25
|
-
|
|
26
|
-
| Role | How Assigned | Scope | Notes |
|
|
27
|
-
| --- | --- | --- | --- |
|
|
28
|
-
| Project owner | `JBProjects.ownerOf(projectId)` | Per project | Can delegate through `JBPermissions` |
|
|
29
|
-
| Project operator | `JBPermissions` grant | Per project | Often `DEPLOY_SUCKERS`, `QUEUE_RULESETS`, `LAUNCH_RULESETS`, `SET_TERMINALS` |
|
|
30
|
-
| `JBOmnichainDeployer` | Immutable singleton | Global | Launch helper and runtime wrapper |
|
|
31
|
-
| `SUCKER_REGISTRY` | Immutable dependency | Global | Receives wildcard `MAP_SUCKER_TOKEN` from the deployer |
|
|
32
|
-
|
|
33
|
-
## Privileged Surfaces
|
|
34
|
-
|
|
35
|
-
| Contract | Function | Who Can Call | Effect |
|
|
36
|
-
| --- | --- | --- | --- |
|
|
37
|
-
| `JBOmnichainDeployer` | `deploySuckersFor(...)` | Project owner or `DEPLOY_SUCKERS` delegate | Extends an existing project with suckers |
|
|
38
|
-
| `JBOmnichainDeployer` | `launchProjectFor(...)` | Anyone | Launches a new omnichain-shaped project |
|
|
39
|
-
| `JBOmnichainDeployer` | `launchRulesetsFor(...)` | Project owner or relevant delegates | Launches rulesets for an existing project |
|
|
40
|
-
| `JBOmnichainDeployer` | `queueRulesetsOf(...)` | Project owner or `QUEUE_RULESETS` delegate | Queues rulesets and stores runtime wrapper config |
|
|
41
|
-
|
|
42
|
-
## Immutable And One-Way
|
|
43
|
-
|
|
44
|
-
- Constructor dependencies are immutable.
|
|
45
|
-
- Ruleset-keyed hook configuration becomes the runtime source of truth once stored.
|
|
46
|
-
- Deterministic sucker deployment assumptions depend on stable salts and deployer config.
|
|
47
|
-
- The deployer's wildcard grant to `SUCKER_REGISTRY` is structural.
|
|
48
|
-
|
|
49
|
-
## Operational Notes
|
|
50
|
-
|
|
51
|
-
- Review launch and runtime-wrapper behavior together for every admin change.
|
|
52
|
-
- Validate controller matching on existing-project flows before launch or queue operations.
|
|
53
|
-
- Treat hook-order changes as runtime behavior changes, not just deployment metadata changes.
|
|
54
|
-
- Arrange token-mapping authority for the registry before using the end-to-end sucker deployment path.
|
|
55
|
-
|
|
56
|
-
## Machine Notes
|
|
57
|
-
|
|
58
|
-
- Do not treat this repo as deployment-only; queued wrapper config is a live runtime input.
|
|
59
|
-
- Inspect `src/JBOmnichainDeployer.sol` alongside project rulesets before assuming current behavior.
|
|
60
|
-
- If directory/controller state and stored wrapper config diverge, stop and resolve the mismatch before further admin actions.
|
|
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
|
|
61
21
|
|
|
62
22
|
## Recovery
|
|
63
23
|
|
|
64
|
-
-
|
|
65
|
-
- If the wrong deterministic deployment assumptions or constructor dependencies were used, recover with replacement infra.
|
|
24
|
+
- bad launch wiring usually means a new deployment path rather than a local patch
|
|
66
25
|
|
|
67
26
|
## Admin Boundaries
|
|
68
27
|
|
|
69
|
-
-
|
|
70
|
-
- It cannot mutate constructor immutables after deployment.
|
|
71
|
-
- It does not own core treasury accounting or project ownership semantics outside the flows it wraps.
|
|
72
|
-
|
|
73
|
-
## Source Map
|
|
28
|
+
- this repo does not override locked runtime behavior in sibling repos
|
|
74
29
|
|
|
75
|
-
- `src/JBOmnichainDeployer.sol`
|
|
76
|
-
- `src/interfaces/IJBOmnichainDeployer.sol`
|
|
77
|
-
- `src/structs/`
|
|
78
|
-
- `test/`
|
package/ARCHITECTURE.md
CHANGED
|
@@ -2,109 +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
|
-
`JBOmnichainDeployer`
|
|
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.
|
|
10
10
|
|
|
11
11
|
## Core Invariants
|
|
12
12
|
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
- This wrapper always becomes the on-chain ruleset data hook and forces both pay and cash-out callbacks through itself before delegating internally.
|
|
18
|
-
- Extra pay hooks must see only the post-721 project amount and the 721-adjusted weight, not the raw terminal payment context.
|
|
19
|
-
- Queue-path carry-forward must prefer the latest approved queued ruleset over the current active ruleset when recovering hook config.
|
|
20
|
-
|
|
21
|
-
## Modules
|
|
22
|
-
|
|
23
|
-
| Module | Responsibility | Notes |
|
|
24
|
-
| --- | --- | --- |
|
|
25
|
-
| `JBOmnichainDeployer` | Launch, queue rulesets, compose hooks, and grant sucker-safe behavior | Deployer and runtime wrapper |
|
|
26
|
-
| config structs | 721 config, extra hook config, and sucker deployment config | Launch-time inputs |
|
|
27
|
-
| `IJBOmnichainDeployer` | External launch and inspection interface | Public surface |
|
|
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
|
|
28
17
|
|
|
29
18
|
## Trust Boundaries
|
|
30
19
|
|
|
31
|
-
-
|
|
32
|
-
-
|
|
33
|
-
-
|
|
34
|
-
- Project-following ownership behavior comes from `nana-ownable-v6`.
|
|
35
|
-
|
|
36
|
-
## Critical Flows
|
|
37
|
-
|
|
38
|
-
### Launch
|
|
39
|
-
|
|
40
|
-
```text
|
|
41
|
-
caller
|
|
42
|
-
-> launches a project or queues rulesets through the deployer
|
|
43
|
-
-> deployer installs itself as the ruleset data hook
|
|
44
|
-
-> deploys or carries forward the 721 hook
|
|
45
|
-
-> optionally deploys sucker pairs with deterministic salts
|
|
46
|
-
-> transfers project ownership to the intended owner
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
### Queue With Carry-Forward
|
|
50
|
-
|
|
51
|
-
```text
|
|
52
|
-
caller
|
|
53
|
-
-> queues a new ruleset without new tiers
|
|
54
|
-
-> deployer selects carry-forward hook config from the latest approved queued ruleset when present
|
|
55
|
-
-> otherwise falls back to the current active ruleset
|
|
56
|
-
-> preserves useDataHookForCashOut from the chosen source ruleset
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
### Runtime Wrapping
|
|
60
|
-
|
|
61
|
-
```text
|
|
62
|
-
runtime callback
|
|
63
|
-
-> if the actor is a registered sucker, return the bridge-safe tax-free and mint-enabled path
|
|
64
|
-
-> otherwise run the 721 hook first when configured
|
|
65
|
-
-> pass the extra pay hook only the post-split project amount and 721-adjusted weight
|
|
66
|
-
-> then run the optional extra hook
|
|
67
|
-
-> merge and return the resulting specs
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
## Accounting Model
|
|
71
|
-
|
|
72
|
-
This repo does not own the treasury ledger. Its critical state is hook configuration and ruleset-keyed carry-forward data that determine how downstream accounting hooks are composed.
|
|
73
|
-
|
|
74
|
-
The wrapper also computes cross-chain cash-out context for non-sucker paths by augmenting local supply and surplus with remote sucker snapshots. Inner hooks may adjust tax rate or counts, but this repo keeps the omnichain supply/surplus view authoritative.
|
|
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
|
|
75
23
|
|
|
76
24
|
## Security Model
|
|
77
25
|
|
|
78
|
-
-
|
|
79
|
-
-
|
|
80
|
-
- The sucker exception path intentionally short-circuits normal composition and should stay easy to reason about.
|
|
81
|
-
- Suckers have two privileged behaviors by design: tax-free bridge cash-outs and unconditional mint permission. Those exceptions must remain tightly scoped to registry-recognized sucker addresses.
|
|
82
|
-
- Salt derivation includes `_msgSender()` for replay protection. Cross-chain deterministic matching therefore depends on using the same sender on each chain.
|
|
83
|
-
- Because this repo is both deployer and runtime hook, permission or hook-order changes can break already-launched projects, not just future launches.
|
|
84
|
-
|
|
85
|
-
## Safe Change Guide
|
|
86
|
-
|
|
87
|
-
- Review launch logic and runtime wrapper logic together.
|
|
88
|
-
- If hook composition changes, test payment and cash-out ordering explicitly.
|
|
89
|
-
- If ruleset prediction changes, test same-block and queued-ruleset edge cases.
|
|
90
|
-
- If sucker exceptions or mint-permission behavior change, re-check bridge flows against normal custom-hook policy.
|
|
91
|
-
- If salt derivation changes, re-check deterministic cross-chain deployment expectations and replay resistance together.
|
|
92
|
-
- If wrapper metadata behavior changes, re-check the forced `useDataHookForPay/useDataHookForCashOut` install path and the extra-hook context shaping together.
|
|
93
|
-
- Keep salt handling stable across chains when deterministic address expectations matter.
|
|
94
|
-
|
|
95
|
-
## Canonical Checks
|
|
96
|
-
|
|
97
|
-
- hook ordering and composition correctness:
|
|
98
|
-
`test/Tiered721HookComposition.t.sol`
|
|
99
|
-
- carry-forward and queued-ruleset recovery behavior:
|
|
100
|
-
`test/audit/CarryForwardRejectedHook.t.sol`
|
|
101
|
-
- wrapper invariants under adversarial sequences:
|
|
102
|
-
`test/invariants/OmnichainDeployerInvariant.t.sol`
|
|
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
|
|
103
28
|
|
|
104
29
|
## Source Map
|
|
105
30
|
|
|
106
31
|
- `src/JBOmnichainDeployer.sol`
|
|
107
|
-
- `src/structs/`
|
|
108
|
-
- `test/Tiered721HookComposition.t.sol`
|
|
109
|
-
- `test/audit/CarryForwardRejectedHook.t.sol`
|
|
110
|
-
- `test/invariants/OmnichainDeployerInvariant.t.sol`
|
package/AUDIT_INSTRUCTIONS.md
CHANGED
|
@@ -1,81 +1,26 @@
|
|
|
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
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
17
|
|
|
22
|
-
|
|
23
|
-
- `
|
|
24
|
-
- `nana-721-hook-v6`
|
|
25
|
-
- `nana-suckers-v6`
|
|
18
|
+
- `src/JBOmnichainDeployer.sol`
|
|
19
|
+
- related tests under `test/`
|
|
26
20
|
|
|
27
21
|
## Start Here
|
|
28
22
|
|
|
29
23
|
1. `src/JBOmnichainDeployer.sol`
|
|
30
|
-
2. `script/Deploy.s.sol`
|
|
31
|
-
3. `script/helpers/DeployersDeploymentLib.sol`
|
|
32
|
-
|
|
33
|
-
## Security Model
|
|
34
|
-
|
|
35
|
-
`JBOmnichainDeployer` is a launch surface that can:
|
|
36
|
-
- create a new Juicebox project
|
|
37
|
-
- deploy and configure a 721 hook
|
|
38
|
-
- configure rulesets and terminals
|
|
39
|
-
- deploy suckers and register them for the project
|
|
40
|
-
- participate in pay or cash-out accounting as a data hook where needed
|
|
41
|
-
|
|
42
|
-
## Roles And Privileges
|
|
43
|
-
|
|
44
|
-
| Role | Powers | How constrained |
|
|
45
|
-
|------|--------|-----------------|
|
|
46
|
-
| Launch caller | Supply desired project configuration | Should receive exactly the requested state |
|
|
47
|
-
| Omnichain deployer | Create hooks, projects, and sucker composition | Must relinquish setup authority after launch |
|
|
48
|
-
| Sucker registry | Grant omnichain-specific privileges | Must not bless arbitrary contracts |
|
|
49
|
-
|
|
50
|
-
## Integration Assumptions
|
|
51
|
-
|
|
52
|
-
| Dependency | Assumption | What breaks if wrong |
|
|
53
|
-
|------------|------------|----------------------|
|
|
54
|
-
| `nana-core-v6` | Launch and ruleset surfaces are authentic | Deployed economics drift from requested config |
|
|
55
|
-
| `nana-721-hook-v6` | Hook ownership and tier setup complete correctly | Collection state or authority is wrong |
|
|
56
|
-
| `nana-suckers-v6` | Registry identifies genuine peers | Fee or mint exemptions widen incorrectly |
|
|
57
|
-
|
|
58
|
-
## Critical Invariants
|
|
59
|
-
|
|
60
|
-
1. Launch configuration is faithful
|
|
61
|
-
The deployed project must end up with the exact hook, ruleset, and ownership configuration the caller requested.
|
|
62
|
-
|
|
63
|
-
2. Sucker privileges stay restricted
|
|
64
|
-
Zero-tax or mint-permission behavior intended for legitimate suckers must not be reachable by arbitrary contracts or stale registry entries.
|
|
65
|
-
|
|
66
|
-
3. Weight and accounting scaling are correct
|
|
67
|
-
If the deployer proxies or modifies hook outputs, the resulting project token issuance and reclaim math must still match intended economics.
|
|
68
|
-
|
|
69
|
-
4. Ownership transfer is complete
|
|
70
|
-
Deployer-created hooks and helper contracts must not retain silent control after initialization.
|
|
71
|
-
|
|
72
|
-
## Attack Surfaces
|
|
73
|
-
|
|
74
|
-
- malformed launch configuration
|
|
75
|
-
- hook and sucker ownership transfer
|
|
76
|
-
- registry-based privilege spoofing
|
|
77
|
-
- reentrancy around launch and initialization
|
|
78
|
-
- local-only launches versus omnichain launches with optional components disabled
|
|
79
24
|
|
|
80
25
|
## Verification
|
|
81
26
|
|
package/README.md
CHANGED
|
@@ -24,8 +24,6 @@ The wrapper exists so sucker-triggered flows can be exempted from project-specif
|
|
|
24
24
|
|
|
25
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.
|
|
26
26
|
|
|
27
|
-
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.
|
|
28
|
-
|
|
29
27
|
## Key Contract
|
|
30
28
|
|
|
31
29
|
| Contract | Role |
|
|
@@ -49,17 +47,17 @@ This repo owns orchestration plus runtime wrapping:
|
|
|
49
47
|
|
|
50
48
|
## Integration Traps
|
|
51
49
|
|
|
52
|
-
- this repo wraps hooks and bridge flows together, so ownership and hook-order assumptions matter as much as
|
|
53
|
-
- ruleset ID prediction is
|
|
54
|
-
- the deployer can carry forward an existing 721 hook shape, so stale
|
|
55
|
-
- bridge-safe wrapper behavior is part of the runtime trust model
|
|
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
|
|
56
54
|
|
|
57
55
|
## Where State Lives
|
|
58
56
|
|
|
59
|
-
- orchestration and wrapper logic
|
|
60
|
-
- bridge runtime state
|
|
61
|
-
- 721 tier state
|
|
62
|
-
-
|
|
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
|
|
63
61
|
|
|
64
62
|
## Install
|
|
65
63
|
|
|
@@ -100,10 +98,10 @@ script/
|
|
|
100
98
|
|
|
101
99
|
## Risks And Notes
|
|
102
100
|
|
|
103
|
-
- ruleset ID prediction is part of the implementation strategy
|
|
101
|
+
- ruleset ID prediction is part of the implementation strategy
|
|
104
102
|
- hook composition order matters because the 721 hook runs before any extra custom hook
|
|
105
|
-
-
|
|
106
|
-
- 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
|
|
107
105
|
|
|
108
106
|
## For AI Agents
|
|
109
107
|
|
package/SKILLS.md
CHANGED
|
@@ -2,43 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
## Use This File For
|
|
4
4
|
|
|
5
|
-
- Use this file when the task involves
|
|
6
|
-
- Start here, then decide whether the
|
|
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
|
-
| Guard rails, attacks, and hook-composition behavior | [`test/JBOmnichainDeployerGuard.t.sol`](./test/JBOmnichainDeployerGuard.t.sol), [`test/OmnichainDeployerAttacks.t.sol`](./test/OmnichainDeployerAttacks.t.sol), [`test/OmnichainDeployerReentrancy.t.sol`](./test/OmnichainDeployerReentrancy.t.sol), [`test/Tiered721HookComposition.t.sol`](./test/Tiered721HookComposition.t.sol) |
|
|
17
|
-
| Baseline deployment and pinned edge cases | [`test/JBOmnichainDeployer.t.sol`](./test/JBOmnichainDeployer.t.sol), [`test/OmnichainDeployerEdgeCases.t.sol`](./test/OmnichainDeployerEdgeCases.t.sol), [`test/TestAuditGaps.sol`](./test/TestAuditGaps.sol) |
|
|
18
|
-
|
|
19
|
-
## Repo Map
|
|
20
|
-
|
|
21
|
-
| Area | Where to look |
|
|
22
|
-
|---|---|
|
|
23
|
-
| Main contract | [`src/JBOmnichainDeployer.sol`](./src/JBOmnichainDeployer.sol) |
|
|
24
|
-
| Types | [`src/structs/`](./src/structs/), [`src/interfaces/`](./src/interfaces/) |
|
|
25
|
-
| Scripts | [`script/`](./script/) |
|
|
26
|
-
| 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) |
|
|
27
16
|
|
|
28
17
|
## Purpose
|
|
29
18
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
## Reference Files
|
|
33
|
-
|
|
34
|
-
- Open [`references/runtime.md`](./references/runtime.md) when you need hook-composition order, sucker exemptions, per-ruleset storage behavior, or the main runtime invariants.
|
|
35
|
-
- 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.
|
|
36
20
|
|
|
37
21
|
## Working Rules
|
|
38
22
|
|
|
39
|
-
- Start in [`src/JBOmnichainDeployer.sol`](./src/JBOmnichainDeployer.sol)
|
|
40
|
-
- Treat ruleset ID prediction
|
|
41
|
-
-
|
|
42
|
-
- When a bug mentions suckers, tax-free cash outs, or mint permission, verify whether the early-return wrapper behavior is the real cause before changing downstream hooks.
|
|
43
|
-
- Existing-project sucker deployment is not just “deploy clones”; it also depends on token-mapping authority landing correctly through the registry path.
|
|
44
|
-
- 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
|
@@ -2,141 +2,62 @@
|
|
|
2
2
|
|
|
3
3
|
## Repo Purpose
|
|
4
4
|
|
|
5
|
-
This repo
|
|
6
|
-
It owns deployment-time composition of core launch config, 721 hook config, sucker deployment, and wrapper behavior
|
|
7
|
-
that keeps later bridge paths safe. It does not own the lower-level bridge runtime once suckers are deployed.
|
|
5
|
+
This repo launches projects that are omnichain from the start.
|
|
8
6
|
|
|
9
7
|
## Primary Actors
|
|
10
8
|
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
9
|
+
- operators launching omnichain projects
|
|
10
|
+
- teams composing 721 hooks with extra data hooks
|
|
11
|
+
- auditors checking bridge-wrapper behavior
|
|
14
12
|
|
|
15
|
-
##
|
|
13
|
+
## Journey 1: Launch An Omnichain Project
|
|
16
14
|
|
|
17
|
-
|
|
15
|
+
**Actor:** deployer.
|
|
18
16
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
**Actor:** launch team or deployer.
|
|
22
|
-
|
|
23
|
-
**Intent:** launch a project with its 721 hook, sucker package, and wrapper behavior already wired in.
|
|
24
|
-
|
|
25
|
-
**Preconditions**
|
|
26
|
-
- the team has base launch config, 721 config, and sucker deployment config ready
|
|
27
|
-
- the team wants omnichain packaging instead of separate post-launch setup
|
|
28
|
-
|
|
29
|
-
**Main Flow**
|
|
30
|
-
1. Call `JBOmnichainDeployer` with the project launch config, 721 config, sucker deployment config, and any extra hook composition.
|
|
31
|
-
2. The deployer launches the base Juicebox project and either deploys or wires the tiered 721 hook.
|
|
32
|
-
3. It wraps the hook composition so future rulesets can keep sucker-safe behavior while still preserving project-specific hook logic.
|
|
33
|
-
4. Deterministic salts are used so sibling-chain deployments can be coordinated with confidence.
|
|
34
|
-
|
|
35
|
-
**Failure Modes**
|
|
36
|
-
- deterministic inputs differ across chains and break address or wrapper expectations
|
|
37
|
-
- teams treat the wrapper as cosmetic and miss its effect on later bridge-safe ruleset evolution
|
|
38
|
-
|
|
39
|
-
**Postconditions**
|
|
40
|
-
- the project launches with its omnichain-capable shape already installed
|
|
41
|
-
- future bridge and ruleset questions should move to the wrapper, sucker, or 721 surfaces this deployer packaged
|
|
42
|
-
|
|
43
|
-
## Journey 2: Coordinate Deterministic Deployment Inputs Across Chains
|
|
44
|
-
|
|
45
|
-
**Actor:** deployment operator.
|
|
46
|
-
|
|
47
|
-
**Intent:** keep addresses, salts, and wrapper behavior predictable across chains.
|
|
48
|
-
|
|
49
|
-
**Preconditions**
|
|
50
|
-
- the deployment will happen on more than one chain
|
|
51
|
-
- the team can reuse the same structured inputs where determinism matters
|
|
17
|
+
**Intent:** launch a project with a 721 hook and suckers already wired in.
|
|
52
18
|
|
|
53
19
|
**Main Flow**
|
|
54
|
-
1.
|
|
55
|
-
2.
|
|
56
|
-
3.
|
|
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.
|
|
57
23
|
|
|
58
24
|
**Failure Modes**
|
|
59
|
-
-
|
|
60
|
-
-
|
|
61
|
-
|
|
62
|
-
**Postconditions**
|
|
63
|
-
- teams can predict addresses, salts, and wrapper behavior before doing the live rollout
|
|
64
|
-
|
|
65
|
-
## Journey 3: Carry Forward An Existing 721 Hook While Queueing New Omnichain Rulesets
|
|
25
|
+
- wrong hook composition
|
|
26
|
+
- cross-chain deployment drift
|
|
27
|
+
- wrong sucker peer wiring
|
|
66
28
|
|
|
67
|
-
|
|
29
|
+
## Journey 2: Deploying Suckers for an Existing Project
|
|
68
30
|
|
|
69
|
-
**
|
|
31
|
+
**Actor:** project owner who wants to add cross-chain sucker bridges to an already-launched project.
|
|
70
32
|
|
|
71
|
-
**
|
|
72
|
-
- the project already has a 721 hook
|
|
73
|
-
- the operator wants omnichain-aware future rulesets without replacing that hook
|
|
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).
|
|
74
34
|
|
|
75
|
-
**
|
|
76
|
-
1. Queue the next ruleset through the deployer using the path that reuses the existing 721 hook (pass zero tiers).
|
|
77
|
-
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.
|
|
78
|
-
3. The `useDataHookForCashOut` flag is preserved from whichever source ruleset is selected.
|
|
79
|
-
4. Validate the controller and queued ruleset inputs before relying on the result.
|
|
80
|
-
5. Confirm the queued ruleset now points at the carried-forward hook rather than accidentally dropping the 721 layer.
|
|
81
|
-
|
|
82
|
-
**Failure Modes**
|
|
83
|
-
- a newly queued but not yet active hook configuration is accidentally ignored
|
|
84
|
-
- operators assume zero tiers means "no 721 behavior" rather than "reuse the existing hook"
|
|
85
|
-
|
|
86
|
-
**Postconditions**
|
|
87
|
-
- the existing 721 hook is carried forward, stored against the queued ruleset, and kept bridge-safe
|
|
35
|
+
**Background**
|
|
88
36
|
|
|
89
|
-
|
|
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.
|
|
90
38
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
**Intent:** add extra hook behavior without breaking the omnichain wrapper or the standard 721 hook.
|
|
94
|
-
|
|
95
|
-
**Preconditions**
|
|
96
|
-
- the project needs both standard tiered NFTs and additional product logic
|
|
97
|
-
- the extra hook is understood well enough to evaluate its effect on bridge paths
|
|
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.
|
|
98
40
|
|
|
99
41
|
**Main Flow**
|
|
100
|
-
1.
|
|
101
|
-
2.
|
|
102
|
-
3.
|
|
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.
|
|
103
46
|
|
|
104
47
|
**Failure Modes**
|
|
105
|
-
-
|
|
106
|
-
-
|
|
107
|
-
|
|
108
|
-
**Postconditions**
|
|
109
|
-
- the extra logic composes with the 721 hook and sucker wrapper instead of overriding them unsafely
|
|
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.
|
|
110
51
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
**Actor:** live-project operator.
|
|
114
|
-
|
|
115
|
-
**Intent:** change future project behavior without breaking the special wrapper assumptions suckers rely on.
|
|
116
|
-
|
|
117
|
-
**Preconditions**
|
|
118
|
-
- the project is already live
|
|
119
|
-
- future rulesets need new metadata, hook composition, or payout behavior
|
|
120
|
-
|
|
121
|
-
**Main Flow**
|
|
122
|
-
1. Queue the next ruleset through the omnichain-aware deployer surface.
|
|
123
|
-
2. Keep track of which hook stack should apply for that future ruleset.
|
|
124
|
-
3. Confirm that sucker flows still land on the mint-safe and tax-safe path the wrapper was designed to preserve.
|
|
125
|
-
|
|
126
|
-
**Failure Modes**
|
|
127
|
-
- operators queue rulesets through a non-omnichain path and silently lose wrapper guarantees
|
|
128
|
-
- teams change payout or hook assumptions without rechecking bridge-special-case behavior
|
|
52
|
+
**Key Difference from Journey 1**
|
|
129
53
|
|
|
130
|
-
|
|
131
|
-
- ruleset changes preserve the wrapper assumptions that let suckers bridge cleanly
|
|
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.
|
|
132
55
|
|
|
133
56
|
## Trust Boundaries
|
|
134
57
|
|
|
135
|
-
- this repo
|
|
136
|
-
- `JBSuckerRegistry` and deployed suckers remain trusted for the actual bridge runtime
|
|
137
|
-
- the underlying 721 hook and any extra hook still need to be audited as part of the composed product
|
|
58
|
+
- this repo wraps runtime behavior but does not replace the underlying 721 or sucker repos
|
|
138
59
|
|
|
139
60
|
## Hand-Offs
|
|
140
61
|
|
|
141
|
-
- Use
|
|
142
|
-
- Use
|
|
62
|
+
- Use `nana-suckers-v6` for bridge runtime behavior.
|
|
63
|
+
- Use `nana-721-hook-v6` for tiered NFT runtime behavior.
|
package/package.json
CHANGED
|
@@ -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
|
}
|
|
@@ -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 {
|