@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 +52 -130
- package/ARCHITECTURE.md +79 -49
- package/AUDIT_INSTRUCTIONS.md +30 -18
- package/README.md +27 -3
- package/RISKS.md +5 -1
- package/SKILLS.md +6 -2
- package/USER_JOURNEYS.md +91 -19
- package/foundry.toml +2 -0
- package/package.json +31 -31
- package/references/operations.md +1 -1
- package/references/runtime.md +1 -1
- package/src/JBOmnichainDeployer.sol +364 -362
- package/src/structs/JBDeployerHookConfig.sol +0 -1
- package/src/structs/JBOmnichain721Config.sol +0 -1
- package/src/structs/JBSuckerDeploymentConfig.sol +0 -1
- package/src/structs/JBTiered721HookConfig.sol +0 -1
package/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,
|
|
10
|
-
|
|
|
11
|
-
| Highest-risk actions |
|
|
12
|
-
| Recovery posture |
|
|
13
|
-
|
|
14
|
-
## Routine Operations
|
|
15
|
-
|
|
16
|
-
- Validate all deploy-time hook choices, 721 settings, and sucker configuration before using `launchProjectFor` or `launchRulesetsFor`.
|
|
17
|
-
- Keep the distinction clear between per-ruleset composed hooks and the deployer's permanent proxy role.
|
|
18
|
-
- Use the deployer when you want its tax-free sucker and mint-permission behavior; otherwise, do not assume it is a drop-in replacement for arbitrary hook wiring.
|
|
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
|
-
##
|
|
12
|
+
## Purpose
|
|
21
13
|
|
|
22
|
-
-
|
|
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
|
-
##
|
|
16
|
+
## Control Model
|
|
27
17
|
|
|
28
|
-
-
|
|
29
|
-
-
|
|
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 |
|
|
36
|
-
|
|
|
37
|
-
|
|
|
38
|
-
|
|
|
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
|
-
|
|
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
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
-
##
|
|
42
|
+
## Immutable And One-Way
|
|
81
43
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
|
|
49
|
+
## Operational Notes
|
|
91
50
|
|
|
92
|
-
|
|
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
|
-
##
|
|
56
|
+
## Machine Notes
|
|
95
57
|
|
|
96
|
-
|
|
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
|
-
|
|
99
|
-
2. **Extra data hook** (`_extraDataHookOf[projectId][rulesetId]`): An optional custom hook for additional pay/cashout logic.
|
|
62
|
+
## Recovery
|
|
100
63
|
|
|
101
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
73
|
+
## Source Map
|
|
145
74
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
-
|
|
149
|
-
-
|
|
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
|
|
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
|
-
##
|
|
7
|
+
## System Overview
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
- The repo composes `nana-721-hook-v6` and `nana-suckers-v6`; it should not duplicate their internal logic.
|
|
11
|
-
- Project accounting still happens in the core protocol.
|
|
9
|
+
`JBOmnichainDeployer` 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
|
-
##
|
|
11
|
+
## Core Invariants
|
|
14
12
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
##
|
|
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
|
-
->
|
|
42
|
+
-> launches a project or queues rulesets through the deployer
|
|
28
43
|
-> deployer installs itself as the ruleset data hook
|
|
29
|
-
->
|
|
30
|
-
->
|
|
31
|
-
-> project ownership
|
|
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
|
-
###
|
|
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
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
-
###
|
|
59
|
+
### Runtime Wrapping
|
|
44
60
|
|
|
45
61
|
```text
|
|
46
62
|
runtime callback
|
|
47
|
-
-> if the actor is a registered sucker, return the
|
|
48
|
-
-> otherwise
|
|
49
|
-
->
|
|
50
|
-
->
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
-
-
|
|
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
|
-
##
|
|
76
|
+
## Security Model
|
|
68
77
|
|
|
69
|
-
-
|
|
70
|
-
-
|
|
71
|
-
-
|
|
72
|
-
-
|
|
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
|
|
77
|
-
-
|
|
78
|
-
- If
|
|
79
|
-
-
|
|
80
|
-
-
|
|
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`
|
package/AUDIT_INSTRUCTIONS.md
CHANGED
|
@@ -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
|
-
##
|
|
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
|
-
##
|
|
72
|
+
## Attack Surfaces
|
|
51
73
|
|
|
52
|
-
|
|
53
|
-
-
|
|
54
|
-
- hook ownership transfer races
|
|
74
|
+
- malformed launch configuration
|
|
75
|
+
- hook and sucker ownership transfer
|
|
55
76
|
- registry-based privilege spoofing
|
|
56
|
-
- reentrancy around
|
|
57
|
-
-
|
|
77
|
+
- reentrancy around launch and initialization
|
|
78
|
+
- local-only launches versus omnichain launches with optional components disabled
|
|
58
79
|
|
|
59
|
-
##
|
|
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
|
-
-
|
|
20
|
+
- wrap sucker flows so project-specific cash-out taxation and mint-permission logic can be handled safely
|
|
16
21
|
- deploy suckers deterministically across chains
|
|
17
22
|
|
|
18
|
-
The wrapper exists so
|
|
23
|
+
The wrapper exists so sucker-triggered flows can be exempted from project-specific cash-out taxation and related mint-gating logic where needed while the project still keeps its own inner data hooks.
|
|
19
24
|
|
|
20
25
|
Use this repo when the default project shape is "Juicebox project plus 721 hook plus cross-chain bridge." Do not use it when a project is single-chain or does not need the wrapper semantics around suckers.
|
|
21
26
|
|
|
@@ -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`.
|
|
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
|
|
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,
|
|
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.
|