@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 CHANGED
@@ -4,75 +4,26 @@
4
4
 
5
5
  | Item | Details |
6
6
  | --- | --- |
7
- | Scope | Omnichain project launch, ruleset queuing, runtime wrapper config, and sucker deployment integration |
8
- | Control posture | Mixed deployer/wrapper control plus project-local delegated authority |
9
- | Highest-risk actions | Queuing rulesets with bad wrapper config, launching with the wrong controller assumptions, and misconfigured sucker deployment |
10
- | Recovery posture | Some config can be superseded with new rulesets; wrong immutable dependencies require replacement infra |
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
- `nana-omnichain-deployers-v6` combines deployment authority with runtime wrapper authority. The critical admin question is not just who can launch or queue rulesets, but also who controls the wrapped hook composition and registered sucker behavior afterward.
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
- - `JBOmnichainDeployer` is both deployer and live data-hook wrapper.
19
- - Project-local authority flows through project ownership and `JBPermissions`.
20
- - `SUCKER_REGISTRY` holds structural `MAP_SUCKER_TOKEN` authority granted to the deployer.
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
- - If a ruleset was queued with bad wrapper config, recover through new rulesets if the project still has the necessary authority.
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
- - The deployer cannot bypass project-local permission checks on existing-project launch, queue, or sucker deployment paths.
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` launches Juicebox projects that are ready for both tiered NFTs and cross-chain suckers from day one. It is also a live wrapper data hook that composes a 721 hook with an optional extra hook while giving registered suckers the bridge-safe path they need.
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` is both deployer and runtime wrapper; those roles are inseparable. At launch time it can deploy a project, install itself as the ruleset data hook, deploy or carry forward the 721 hook, and optionally deploy sucker pairs with deterministic salts. At runtime it composes hook specs and grants special behavior to registered suckers so bridging does not get trapped behind project-specific cash-out policy.
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
- - Registered suckers must be able to bridge without getting blocked by custom cash-out or mint policy.
14
- - Hook order is intentional: the 721 hook runs first, then the optional extra hook.
15
- - Ruleset ID prediction must stay aligned with `JBRulesets`; stored hook config keys depend on it.
16
- - Every project launched through this repo gets a 721-hook surface, even if it starts with zero tiers.
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
- - Accounting and project ownership transfer remain rooted in `nana-core-v6`.
32
- - Tier behavior comes from `nana-721-hook-v6`.
33
- - Cross-chain transport comes from `nana-suckers-v6`.
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
- - The largest risk is silent drift between deploy-time assumptions and runtime wrapper behavior.
79
- - Ruleset ID prediction is storage-key critical.
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`
@@ -1,81 +1,26 @@
1
1
  # Audit Instructions
2
2
 
3
- This repo launches projects that are immediately composed with 721 hooks and sucker deployments. Audit it as a privileged deployer and runtime data-hook participant.
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
- - launch projects with incorrect rulesets, terminals, or hook ownership
9
- - grant cash-out or mint privileges to non-suckers
10
- - mis-scale weight or tax behavior during omnichain-specific data-hook flows
11
- - leave deployed hook or sucker ownership in the wrong hands
12
- - create inconsistent behavior between local-only and omnichain project launches
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
- Key dependencies:
23
- - `nana-core-v6`
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 the deployment salt
53
- - ruleset ID prediction is an implementation dependency and should be reviewed as an actual invariant
54
- - the deployer can carry forward an existing 721 hook shape, so stale assumptions about hook config can leak across deployments
55
- - bridge-safe wrapper behavior is part of the runtime trust model, not just deployment ergonomics
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 live in `JBOmnichainDeployer`
60
- - bridge runtime state lives in `nana-suckers-v6`
61
- - 721 tier state lives in `nana-721-hook-v6`
62
- - any extra hook behavior lives in the additional repo composed into the deployment
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 and should be treated as a real assumption
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
- - using the default empty-tier 721 config is convenient, but teams should still decide explicitly whether the hook participates in cash-out behavior
106
- - deterministic salts help with cross-chain address alignment, but only when the sender and configuration match exactly
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 deploying core projects with suckers and optional 721 or custom data-hook composition in one flow.
6
- - Start here, then decide whether the question is about deterministic deployment, ruleset-to-hook storage, wrapper pay/cash-out delegation, or sucker deployment authority. Those are distinct sources of failure here.
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 launch model | [`README.md`](./README.md), [`ARCHITECTURE.md`](./ARCHITECTURE.md) |
13
- | Deployment implementation | [`src/JBOmnichainDeployer.sol`](./src/JBOmnichainDeployer.sol), [`script/Deploy.s.sol`](./script/Deploy.s.sol) |
14
- | Runtime wrapper and config assumptions | [`references/runtime.md`](./references/runtime.md), [`references/operations.md`](./references/operations.md) |
15
- | Input and output types | [`src/structs/`](./src/structs/), [`src/interfaces/`](./src/interfaces/) |
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
- Single-transaction deployment and wrapper layer for Juicebox projects that need a 721 hook, cross-chain sucker support, and optional extra data-hook composition from day one.
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) for both deployment and runtime wrapping behavior. This repo is orchestration plus a live data-hook wrapper, not a pure deploy script package.
40
- - Treat ruleset ID prediction, hook-carry-forward logic, and salt derivation as high-risk. Small changes there can orphan config or break deterministic deployment.
41
- - The deployer stores hook config per predicted ruleset ID. Same-block queue assumptions and carry-forward behavior are core correctness issues, not edge cases.
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 packages omnichain project launches and omnichain-aware ruleset evolution.
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
- - teams launching projects that should be cross-chain from day one
12
- - deployers composing a 721 hook, suckers, and optional extra hooks
13
- - operators evolving rulesets without breaking omnichain wrapper behavior
9
+ - operators launching omnichain projects
10
+ - teams composing 721 hooks with extra data hooks
11
+ - auditors checking bridge-wrapper behavior
14
12
 
15
- ## Key Surfaces
13
+ ## Journey 1: Launch An Omnichain Project
16
14
 
17
- - `JBOmnichainDeployer`: packaged omnichain launch and ruleset-evolution surface
15
+ **Actor:** deployer.
18
16
 
19
- ## Journey 1: Launch A Project Across Chains In One Flow
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. Fix the deployer inputs that drive deterministic addresses for suckers and hook packaging.
55
- 2. Reuse those inputs consistently on each chain.
56
- 3. Validate that the controller, hook ownership, and sucker expectations still line up across the resulting deployments.
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
- - teams reuse similar but not identical inputs and assume deterministic outputs still match
60
- - address prediction is checked for one chain only and not for the full deployment set
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
- **Actor:** operator evolving a live project.
29
+ ## Journey 2: Deploying Suckers for an Existing Project
68
30
 
69
- **Intent:** queue new omnichain-aware rulesets without dropping the existing 721 collection.
31
+ **Actor:** project owner who wants to add cross-chain sucker bridges to an already-launched project.
70
32
 
71
- **Preconditions**
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
- **Main Flow**
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
- ## Journey 4: Compose A Tiered 721 Hook With A Custom Extra Hook
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
- **Actor:** product team.
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. Provide the extra-hook config when launching through `JBOmnichainDeployer`.
101
- 2. Let the deployer remember which composition belongs to each ruleset.
102
- 3. Make sure bridge flows still bypass or special-case the right tax and data-hook behavior.
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
- - the extra hook overrides or bypasses wrapper assumptions the bridge path depends on
106
- - teams audit the extra hook in isolation and miss the composed behavior
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
- ## Journey 5: Evolve The Project After Launch Without Breaking Bridge Paths
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
- **Postconditions**
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 is trusted for wrapper composition and hook carry-forward decisions across rulesets
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 [nana-suckers-v6](../nana-suckers-v6/USER_JOURNEYS.md) for the bridge mechanics after the project has been launched with suckers.
142
- - Use [nana-721-hook-v6](../nana-721-hook-v6/USER_JOURNEYS.md) for the standard tier and resolver behavior that this repo packages into the omnichain launch.
62
+ - Use `nana-suckers-v6` for bridge runtime behavior.
63
+ - Use `nana-721-hook-v6` for tiered NFT runtime behavior.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bananapus/omnichain-deployers-v6",
3
- "version": "0.0.27",
3
+ "version": "0.0.28",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -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, decimals: 18, currency: uint256(uint160(context.surplus.token))
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
- if (address(controller.DIRECTORY().controllerOf(projectId)) != address(controller)) {
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 test_poc_launchRulesetsFor_revertsWhenProjectHasNoControllerYet() public {
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 is valid for core launchRulesetsFor,
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
- vm.expectRevert(JBOmnichainDeployer.JBOmnichainDeployer_ControllerMismatch.selector);
80
- deployer.launchRulesetsFor(PROJECT_ID, configs, new JBTerminalConfig[](0), "memo", controller);
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 {