@bananapus/omnichain-deployers-v6 0.0.25 → 0.0.27

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
@@ -1,156 +1,78 @@
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 project launch, hook composition, sucker deployment, and the deployer's data-hook proxy behavior. |
10
- | Operators | Project owners and delegates, the `JBOmnichainDeployer`, and the configured `JBSuckerRegistry` with its wildcard token-mapping grant. |
11
- | Highest-risk actions | Launching a project with the wrong hook composition, terminal configuration, or cross-chain setup, then assuming it can be rewritten later. |
12
- | Recovery posture | The deployer's immutable dependencies cannot be edited in place. Project-level recovery usually means launching corrected rulesets or redeploying the broader project path. |
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.
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 |
19
11
 
20
- ## One-Way Or High-Risk Actions
12
+ ## Purpose
21
13
 
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.
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.
25
15
 
26
- ## Recovery Notes
16
+ ## Control Model
27
17
 
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.
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.
30
23
 
31
24
  ## Roles
32
25
 
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.
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 |
64
32
 
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`.
33
+ ## Privileged Surfaces
72
34
 
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.
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 |
79
41
 
80
- ## Cross-Chain Controls
42
+ ## Immutable And One-Way
81
43
 
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` |
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.
89
48
 
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.
49
+ ## Operational Notes
91
50
 
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.
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.
93
55
 
94
- ## Data Hook Proxy Pattern
56
+ ## Machine Notes
95
57
 
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:
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.
97
61
 
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.
62
+ ## Recovery
100
63
 
101
- ### Call flow for `beforePayRecordedWith`:
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.
102
66
 
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
- ```
124
-
125
- **`useDataHookForCashOut` / `useDataHookForPay` flags:** These flags control whether each hook participates in a given operation. For the **extra data hook**, the flags are stored per-ruleset in the `JBDeployerHookConfig` struct -- if the flag is `false`, that hook is skipped entirely and the original values are returned unchanged for that hook's portion. The **721 hook** behaves differently: it is **always** called during `beforePayRecordedWith` when its address is non-zero (no `useDataHookForPay` check), but for `beforeCashOutRecordedWith` it respects the `useDataHookForCashOut` flag stored in `JBTiered721HookConfig`.
126
-
127
- **Write-once storage:** Both `_tiered721HookOf` and `_extraDataHookOf` mappings are written once during `_setup721()` and never updated. New rulesets can reference different hooks, but existing ruleset-to-hook mappings are permanent.
128
-
129
- ## Immutable Configuration
130
-
131
- These values are set at deployment and cannot be changed:
132
-
133
- | Property | Type | What It Is |
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. |
67
+ ## Admin Boundaries
141
68
 
142
- **Data hook mappings** (`_tiered721HookOf[projectId][rulesetId]` and `_extraDataHookOf[projectId][rulesetId]`) are write-once per ruleset ID. They are set during `_setup721()` and never updated or deleted.
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.
143
72
 
144
- ## Admin Boundaries
73
+ ## Source Map
145
74
 
146
- What admins **cannot** do:
147
-
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.
75
+ - `src/JBOmnichainDeployer.sol`
76
+ - `src/interfaces/IJBOmnichainDeployer.sol`
77
+ - `src/structs/`
78
+ - `test/`
package/ARCHITECTURE.md CHANGED
@@ -2,79 +2,109 @@
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 also acts as a wrapper data hook so it can compose a 721 hook with an optional extra data hook while granting suckers tax-free cash outs and mint permission.
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.
6
6
 
7
- ## Boundaries
7
+ ## System Overview
8
8
 
9
- - `JBOmnichainDeployer` is both a deployer and a live hook wrapper. Those two roles are inseparable.
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` 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.
12
10
 
13
- ## Main Components
11
+ ## Core Invariants
14
12
 
15
- | Component | Responsibility |
16
- | --- | --- |
17
- | `JBOmnichainDeployer` | Project launch, ruleset queueing, hook composition, and sucker-safe cash-out policy |
18
- | config structs | 721 hook config, extra hook config, and sucker deployment config |
19
- | `IJBOmnichainDeployer` | Public deployer and inspection interface |
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
20
 
21
- ## Runtime Model
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 |
28
+
29
+ ## Trust Boundaries
30
+
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
22
37
 
23
38
  ### Launch
24
39
 
25
40
  ```text
26
41
  caller
27
- -> launch project or queue rulesets through the deployer
42
+ -> launches a project or queues rulesets through the deployer
28
43
  -> 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
44
+ -> deploys or carries forward the 721 hook
45
+ -> optionally deploys sucker pairs with deterministic salts
46
+ -> transfers project ownership to the intended owner
32
47
  ```
33
48
 
34
- ### 721 Hook Carry-Forward (Queue Path)
35
-
36
- When `queueRulesetsOf` is called without new tiers, the deployer carries the existing 721 hook forward. The source ruleset is chosen with this precedence:
49
+ ### Queue With Carry-Forward
37
50
 
38
- 1. **Latest queued ruleset** — if its approval status is `Approved` or `Empty` (no approval hook) and it has a hook config stored in the deployer.
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.
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
+ ```
42
58
 
43
- ### Pay And Cash-Out Wrapping
59
+ ### Runtime Wrapping
44
60
 
45
61
  ```text
46
62
  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
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
51
68
  ```
52
69
 
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.
70
+ ## Accounting Model
60
71
 
61
- ## Where Complexity Lives
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.
62
73
 
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.
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.
66
75
 
67
- ## Dependencies
76
+ ## Security Model
68
77
 
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
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.
73
84
 
74
85
  ## Safe Change Guide
75
86
 
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.
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`
103
+
104
+ ## Source Map
105
+
106
+ - `src/JBOmnichainDeployer.sol`
107
+ - `src/structs/`
108
+ - `test/Tiered721HookComposition.t.sol`
109
+ - `test/audit/CarryForwardRejectedHook.t.sol`
110
+ - `test/invariants/OmnichainDeployerInvariant.t.sol`
@@ -2,7 +2,7 @@
2
2
 
3
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.
4
4
 
5
- ## Objective
5
+ ## Audit Objective
6
6
 
7
7
  Find issues that:
8
8
  - launch projects with incorrect rulesets, terminals, or hook ownership
@@ -24,7 +24,13 @@ Key dependencies:
24
24
  - `nana-721-hook-v6`
25
25
  - `nana-suckers-v6`
26
26
 
27
- ## System Model
27
+ ## Start Here
28
+
29
+ 1. `src/JBOmnichainDeployer.sol`
30
+ 2. `script/Deploy.s.sol`
31
+ 3. `script/helpers/DeployersDeploymentLib.sol`
32
+
33
+ ## Security Model
28
34
 
29
35
  `JBOmnichainDeployer` is a launch surface that can:
30
36
  - create a new Juicebox project
@@ -33,6 +39,22 @@ Key dependencies:
33
39
  - deploy suckers and register them for the project
34
40
  - participate in pay or cash-out accounting as a data hook where needed
35
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
+
36
58
  ## Critical Invariants
37
59
 
38
60
  1. Launch configuration is faithful
@@ -47,26 +69,16 @@ If the deployer proxies or modifies hook outputs, the resulting project token is
47
69
  4. Ownership transfer is complete
48
70
  Deployer-created hooks and helper contracts must not retain silent control after initialization.
49
71
 
50
- ## Threat Model
72
+ ## Attack Surfaces
51
73
 
52
- Prioritize:
53
- - empty or malformed ruleset configurations
54
- - hook ownership transfer races
74
+ - malformed launch configuration
75
+ - hook and sucker ownership transfer
55
76
  - registry-based privilege spoofing
56
- - reentrancy around project launch and initialization
57
- - stale assumptions when optional sucker deployment is skipped
77
+ - reentrancy around launch and initialization
78
+ - local-only launches versus omnichain launches with optional components disabled
58
79
 
59
- ## Build And Verification
80
+ ## Verification
60
81
 
61
- Standard workflow:
62
82
  - `npm install`
63
83
  - `forge build`
64
84
  - `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,10 +17,10 @@ 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
- - grant tax-free and mint-safe behavior to project suckers
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 suckers can bridge without being blocked by project-specific cash-out tax logic while the project still keeps its own data hooks.
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
 
@@ -42,6 +47,20 @@ This repo owns orchestration plus runtime wrapping:
42
47
  3. `nana-721-hook-v6/src/JB721TiersHook.sol`
43
48
  4. the extra hook repo, if the deployment composes one
44
49
 
50
+ ## Integration Traps
51
+
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
56
+
57
+ ## Where State Lives
58
+
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
63
+
45
64
  ## Install
46
65
 
47
66
  ```bash
@@ -85,3 +104,8 @@ script/
85
104
  - hook composition order matters because the 721 hook runs before any extra custom hook
86
105
  - using the default empty-tier 721 config is convenient, but teams should still decide explicitly whether the hook participates in cash-out behavior
87
106
  - deterministic salts help with cross-chain address alignment, but only when the sender and configuration match exactly
107
+
108
+ ## For AI Agents
109
+
110
+ - Describe this repo as an orchestration and wrapper layer, not as the source of sucker or 721 runtime behavior.
111
+ - 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`. Any other NFTs sent to this contract are permanently lost.
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
@@ -3,7 +3,7 @@
3
3
  ## Use This File For
4
4
 
5
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 open the deployer or composition-focused tests depending on whether the question is about setup, permissions, hook wrapping, or runtime delegation.
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.
7
7
 
8
8
  ## Read This Next
9
9
 
@@ -11,8 +11,10 @@
11
11
  |---|---|
12
12
  | Repo overview and launch model | [`README.md`](./README.md), [`ARCHITECTURE.md`](./ARCHITECTURE.md) |
13
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) |
14
15
  | Input and output types | [`src/structs/`](./src/structs/), [`src/interfaces/`](./src/interfaces/) |
15
- | Guard rails, attacks, or hook composition behavior | [`test/JBOmnichainDeployerGuard.t.sol`](./test/JBOmnichainDeployerGuard.t.sol), [`test/OmnichainDeployerAttacks.t.sol`](./test/OmnichainDeployerAttacks.t.sol), [`test/Tiered721HookComposition.t.sol`](./test/Tiered721HookComposition.t.sol), [`test/regression/`](./test/regression/) |
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) |
16
18
 
17
19
  ## Repo Map
18
20
 
@@ -36,5 +38,7 @@ Single-transaction deployment and wrapper layer for Juicebox projects that need
36
38
 
37
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.
38
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.
39
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.
40
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.