@bananapus/omnichain-deployers-v6 0.0.7 → 0.0.8

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
@@ -30,9 +30,10 @@ Admin privileges and their scope in nana-omnichain-deployers-v6.
30
30
  | `launchProjectFor` | Anyone | Creates a new project with suckers. The ERC-721 is minted to the specified `owner`. |
31
31
  | `launch721ProjectFor` | Anyone | Creates a new project with a 721 tiers hook and suckers. The ERC-721 is minted to the specified `owner`. |
32
32
  | `beforePayRecordedWith` | JBMultiTerminal (via controller) | View function: forwards pay data to the stored data hook, or passes through if none configured. |
33
- | `beforeCashOutRecordedWith` | JBMultiTerminal (via controller) | View function: returns 0% cash-out tax for registered suckers, otherwise forwards to stored data hook. |
34
- | `hasMintPermissionFor` | JBController | View function: returns true for registered suckers, otherwise forwards to stored data hook. |
35
- | `dataHookOf` | Anyone | View function: returns the stored data hook config for a project/ruleset pair. |
33
+ | `beforeCashOutRecordedWith` | JBMultiTerminal (via controller) | View function: returns 0% cash-out tax for registered suckers. Checks 721 hook (from `_tiered721HookOf`) then custom hook (from `_extraDataHookOf`) — the first with `useDataHookForCashOut: true` handles it. If neither has the flag set, returns original values. |
34
+ | `hasMintPermissionFor` | JBController | View function: returns true for registered suckers, otherwise checks the custom hook in `_extraDataHookOf`. |
35
+ | `extraDataHookOf` | Anyone | View function: returns the stored `JBDeployerHookConfig` for a project/ruleset pair (the custom data hook). |
36
+ | `tiered721HookOf` | Anyone | View function: returns the stored 721 hook and `useDataHookForCashOut` flag for a project/ruleset pair. |
36
37
 
37
38
  ## Deployment Administration
38
39
 
@@ -75,7 +76,7 @@ These values are set at deployment and cannot be changed:
75
76
  | Trusted forwarder | `address` | The ERC-2771 trusted forwarder for meta-transactions. |
76
77
  | MAP_SUCKER_TOKEN grant | Permission | Granted to SUCKER_REGISTRY at construction for all projects (projectId=0). Cannot be revoked by this contract. |
77
78
 
78
- **Data hook mappings** (`_dataHookOf[projectId][rulesetId]`) are write-once per ruleset ID. They are set during `_setup` and never updated or deleted.
79
+ **Data hook mappings** (`_tiered721HookOf[projectId][rulesetId]` and `_extraDataHookOf[projectId][rulesetId]`) are write-once per ruleset ID. They are set during `_setup` / `_setup721` and never updated or deleted.
79
80
 
80
81
  ## Admin Boundaries
81
82
 
@@ -83,10 +84,10 @@ What admins **cannot** do:
83
84
 
84
85
  - **Cannot upgrade the deployer.** JBOmnichainDeployer has no upgrade mechanism, proxy pattern, or self-destruct.
85
86
  - **Cannot change immutable references.** PROJECTS, HOOK_DEPLOYER, SUCKER_REGISTRY, PERMISSIONS, and the trusted forwarder are all immutable.
86
- - **Cannot modify stored data hooks.** Once a ruleset's data hook config is stored in `_dataHookOf`, it cannot be changed. New rulesets can use different hooks, but existing mappings are permanent.
87
+ - **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.
87
88
  - **Cannot bypass permission checks.** All post-deployment admin functions require JBPermissions verification against the project owner.
88
89
  - **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.
89
- - **Cannot set the deployer as its own data hook.** The `_setup` function explicitly reverts with `JBOmnichainDeployer_InvalidHook` if `metadata.dataHook == address(this)`.
90
+ - **Cannot set the deployer as its own data hook.** Both `_setup` and `_setup721` explicitly revert with `JBOmnichainDeployer_InvalidHook` if a hook is `address(this)`.
90
91
  - **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.
91
92
  - **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.
92
93
  - **Cannot drain funds.** The deployer never holds or manages token balances. It only orchestrates configuration.
package/ARCHITECTURE.md CHANGED
@@ -8,11 +8,12 @@ Omnichain project deployer for Juicebox V6. Wraps the project deployment flow to
8
8
 
9
9
  ```
10
10
  src/
11
- ├── JBOmnichainDeployer.sol — Deploys projects with sucker integration, acts as data hook
11
+ ├── JBOmnichainDeployer.sol — Deploys projects with sucker integration, acts as data hook wrapper
12
12
  ├── interfaces/
13
13
  │ └── IJBOmnichainDeployer.sol — Interface
14
14
  └── structs/
15
- ├── JBDeployerHookConfig.sol — Hook configuration for deployment
15
+ ├── JBDeployerHookConfig.sol — Custom hook configuration for deployment
16
+ ├── JBTiered721HookConfig.sol — Per-ruleset 721 hook configuration
16
17
  └── JBSuckerDeploymentConfig.sol — Sucker deployment parameters
17
18
  ```
18
19
 
@@ -31,12 +32,18 @@ Deployer → JBOmnichainDeployer.deployProjectFor()
31
32
  ### Data Hook Behavior
32
33
  ```
33
34
  Payment → JBOmnichainDeployer.beforePayRecordedWith()
34
- Pass through (no modification to pay behavior)
35
- Return empty pay hook specifications
35
+ Calls 721 hook first (from _tiered721HookOf) for specs/split amounts
36
+ Calls custom hook from _extraDataHookOf (if useDataHookForPay=true)
37
+ → Custom hook receives reduced amount (payment - splitAmount)
38
+ → Adjusts weight proportionally for splits
39
+ → Merges both hook specs (721 first, then custom)
36
40
 
37
41
  Cash Out → JBOmnichainDeployer.beforeCashOutRecordedWith()
38
- → If caller is a registered sucker: return 0% cash-out tax
39
- Otherwise: return configured cash-out tax rate
42
+ → If caller is a registered sucker: return 0% cash-out tax (early return)
43
+ Checks 721 hook (from _tiered721HookOf, if useDataHookForCashOut=true)
44
+ → Then checks custom hook (from _extraDataHookOf, if useDataHookForCashOut=true)
45
+ → If 721 hook has flag=true and reverts (fungible cashout): revert propagates
46
+ → If neither hook has the flag set: return original values
40
47
  ```
41
48
 
42
49
  ### Ruleset Management
package/README.md CHANGED
@@ -8,11 +8,12 @@ Deploy Juicebox projects with cross-chain suckers and optional 721 tiers hooks i
8
8
 
9
9
  Launching a cross-chain Juicebox project normally takes several steps: deploy the project, configure rulesets, set up terminals, deploy suckers, and wire up a data hook that exempts suckers from cash out taxes. `JBOmnichainDeployer` collapses all of this into one transaction.
10
10
 
11
- It works by inserting itself as the data hook on every ruleset it touches, storing the project's custom data hook in a mapping keyed by `(projectId, rulesetId)` and any 721 tiers hook separately in `tiered721HookOf`. When the protocol calls data hook functions during payments and cash outs, the deployer:
11
+ It works by inserting itself as the data hook on every ruleset it touches, storing hooks in two separate mappings: the 721 tiers hook (if any) is stored per-ruleset in `_tiered721HookOf[projectId][rulesetId]` with its own `useDataHookForCashOut` flag, and an optional custom data hook (e.g., buyback hook) is stored per-ruleset in `_extraDataHookOf[projectId][rulesetId]` with `useDataHookForPay` and `useDataHookForCashOut` flags. When the protocol calls data hook functions during payments and cash outs, the deployer:
12
12
 
13
13
  - **Checks if the holder is a sucker** -- if so, returns 0% cash out tax and grants mint permission. This early return means suckers can always bridge tokens without interference, even if the project's hooks would revert.
14
- - **Composes the 721 hook and custom data hook** for payments -- the 721 hook is called first to get its specs (including split fund amounts), then the custom data hook is called with a reduced amount context (payment minus split amount) so it only considers the available funds. The deployer adjusts the returned weight proportionally for splits, ensuring the terminal only mints tokens for the amount that actually enters the project treasury. For cash outs, the 721 hook takes priority if present.
15
- - **Forwards to the custom data hook** if no 721 hook is set, or returns default values if neither is set.
14
+ - **Composes the 721 hook and custom data hook** for payments -- the 721 hook is called first (via `tiered721HookOf`) to get its specs (including split fund amounts), then the custom hook from `_extraDataHookOf` (if `useDataHookForPay: true`) is called with a reduced amount context (payment minus split amount) so it only considers the available funds. The deployer adjusts the returned weight proportionally for splits, ensuring the terminal only mints tokens for the amount that actually enters the project treasury.
15
+ - **Checks hooks for cash outs** -- the 721 hook is checked first (if `useDataHookForCashOut: true`), then the custom hook. The first with the flag set handles the cash out. If the 721 hook has `useDataHookForCashOut: true` and reverts (e.g., for fungible-only cashouts), that revert propagates. Set `useDataHookForCashOut: false` on the 721 metadata to skip it and let the custom hook handle cashouts instead.
16
+ - **Returns default values** if neither hook has the relevant flag set.
16
17
 
17
18
  This wrapping is invisible to the project and its users. The project's hooks (buyback hook, 721 hook, etc.) work exactly as configured, and can be composed together.
18
19
 
@@ -42,20 +43,18 @@ sequenceDiagram
42
43
  participant Terminal
43
44
  participant Deployer as JBOmnichainDeployer
44
45
  participant Registry as JBSuckerRegistry
45
- participant Hook as Real Data Hook
46
+ participant Hook as 721 / Custom Hook
46
47
 
47
48
  Terminal->>Deployer: beforeCashOutRecordedWith(context)
48
49
  Deployer->>Registry: isSuckerOf(projectId, holder)?
49
50
  alt Holder is a sucker
50
51
  Deployer-->>Terminal: 0% tax (early return)
51
- else 721 hook exists
52
- Deployer->>721Hook: beforeCashOutRecordedWith(context)
53
- 721Hook-->>Deployer: taxRate, count, supply, specs
54
- Deployer-->>Terminal: forward 721 hook response
55
- else Custom data hook exists
52
+ else 721 or custom hook with useDataHookForCashOut=true
56
53
  Deployer->>Hook: beforeCashOutRecordedWith(context)
57
54
  Hook-->>Deployer: taxRate, count, supply, specs
58
- Deployer-->>Terminal: forward custom hook response
55
+ Deployer-->>Terminal: forward hook response
56
+ else Neither hook has useDataHookForCashOut=true
57
+ Deployer-->>Terminal: original values (default)
59
58
  end
60
59
  ```
61
60
 
@@ -64,12 +63,12 @@ sequenceDiagram
64
63
  The `launch721*` and `queue721*` variants deploy a tiered ERC-721 hook alongside the project. The deployer:
65
64
 
66
65
  1. Deploys the 721 hook via `HOOK_DEPLOYER`
67
- 2. Stores the 721 hook in `tiered721HookOf[projectId]` (separate from the custom data hook)
66
+ 2. Stores the 721 hook per-ruleset in `_tiered721HookOf[projectId][rulesetId]` with its `useDataHookForCashOut` flag
68
67
  3. Converts 721-specific ruleset configs (`JBPayDataHookRulesetConfig`) to standard configs, enforcing `useDataHookForPay = true` and `allowSetCustomToken = false`
69
- 4. Stores the optional custom data hook (e.g., buyback hook) in `_dataHookOf[projectId][rulesetId]`
68
+ 4. Stores the optional custom hook (e.g., buyback hook) separately in `_extraDataHookOf[projectId][rulesetId]` with its own per-hook flags
70
69
  5. Transfers hook ownership to the project via `JBOwnable.transferOwnershipToProject()`
71
70
 
72
- This separation means a project can have both a 721 hook (for NFT minting on payments) and a custom data hook (for buyback, custom weight logic, etc.) running simultaneously. During payments, both hooks' specifications are merged. During cash outs, the 721 hook takes priority.
71
+ This means a project can have both a 721 hook (for NFT minting on payments) and a custom data hook (for buyback, custom weight logic, etc.) running simultaneously. During payments, both hooks' specifications are merged. During cash outs, the 721 hook is checked first (if `useDataHookForCashOut: true`), then the custom hook.
73
72
 
74
73
  ### Deterministic Cross-Chain Addresses
75
74
 
@@ -100,9 +99,10 @@ The `queueRulesetsOf` and `queue721RulesetsOf` functions guard against predictio
100
99
 
101
100
  | Type | Description |
102
101
  |------|-------------|
103
- | `JBDeployerHookConfig` | Per-ruleset config storing the custom data hook address and its `useDataHookForPay`/`useDataHookForCashOut` flags. The 721 hook is stored separately in `tiered721HookOf`. |
102
+ | `JBDeployerHookConfig` | Per-hook config with `dataHook`, `useDataHookForPay`, and `useDataHookForCashOut` flags. Stored as a single value per `(projectId, rulesetId)` in `_extraDataHookOf` for the custom data hook. |
103
+ | `JBTiered721HookConfig` | Per-ruleset 721 hook config with `hook` (the `IJB721TiersHook`) and `useDataHookForCashOut` flag. Stored per `(projectId, rulesetId)` in `_tiered721HookOf`. |
104
104
  | `JBSuckerDeploymentConfig` | Wraps an array of `JBSuckerDeployerConfig` with a `bytes32` salt for deterministic cross-chain addresses. |
105
- | `IJBOmnichainDeployer` | Interface for all deployer entry points and the `dataHookOf` view. |
105
+ | `IJBOmnichainDeployer` | Interface for all deployer entry points and the `extraDataHookOf` view. |
106
106
 
107
107
  ## Install
108
108
 
@@ -137,7 +137,7 @@ Add to `remappings.txt`:
137
137
  # foundry.toml
138
138
  [profile.default]
139
139
  solc = '0.8.26'
140
- evm_version = 'paris'
140
+ evm_version = 'cancun'
141
141
  optimizer_runs = 100000
142
142
 
143
143
  [fuzz]
@@ -148,19 +148,23 @@ runs = 4096
148
148
 
149
149
  ```
150
150
  src/
151
- JBOmnichainDeployer.sol # Main contract (719 lines)
151
+ JBOmnichainDeployer.sol # Main contract (~893 lines)
152
152
  interfaces/
153
153
  IJBOmnichainDeployer.sol # Public interface
154
154
  structs/
155
- JBDeployerHookConfig.sol # Stored data hook config
155
+ JBDeployerHookConfig.sol # Custom hook config (dataHook + flags)
156
+ JBTiered721HookConfig.sol # Per-ruleset 721 hook config
156
157
  JBSuckerDeploymentConfig.sol # Sucker deployment params
157
158
  test/
158
159
  JBOmnichainDeployer.t.sol # Unit tests
159
160
  JBOmnichainDeployerGuard.t.sol # Ruleset ID prediction tests
160
161
  OmnichainDeployerAttacks.t.sol # Adversarial security tests
161
- Tiered721HookComposition.t.sol # 721 hook + custom hook composition tests
162
+ OmnichainDeployerEdgeCases.t.sol # Edge case tests (weight, cashout, mint)
163
+ OmnichainDeployerReentrancy.t.sol # Reentrancy tests
164
+ Tiered721HookComposition.t.sol # 721 hook + custom hook composition tests
165
+ fork/ # Fork tests against mainnet
162
166
  regression/
163
- H20_HookOwnershipTransfer.t.sol # Hook ownership transfer regression
167
+ HookOwnershipTransfer.t.sol # Hook ownership transfer regression
164
168
  script/
165
169
  Deploy.s.sol # Sphinx deployment script
166
170
  helpers/
@@ -180,7 +184,7 @@ Note: `launchProjectFor` and `launch721ProjectFor` require no permissions -- any
180
184
 
181
185
  ## Risks
182
186
 
183
- - **Ruleset ID mismatch**: If `_setup()` predictions are wrong (e.g., due to same-block queuing from another source), the stored hook configs will be keyed to the wrong rulesets. The `queueRulesetsOf` guard prevents this, but `launchProjectFor` relies on `PROJECTS.count()` being accurate at call time.
184
- - **Reverting real hook**: If the project's real data hook reverts on `beforePayRecordedWith`, payments are blocked. Suckers are immune to this for cash outs (early return), but not for payments.
187
+ - **Ruleset ID mismatch**: If `_setup()` / `_setup721()` predictions are wrong (e.g., due to same-block queuing from another source), the stored hook configs will be keyed to the wrong rulesets. The `queueRulesetsOf` guard prevents this, but `launchProjectFor` relies on `PROJECTS.count()` being accurate at call time.
188
+ - **Reverting real hook**: If any stored hook reverts on `beforePayRecordedWith`, payments are blocked. If the 721 hook has `useDataHookForCashOut: true`, its revert for fungible cashouts propagates. Suckers are immune to this for cash outs (early return), but not for payments.
185
189
  - **Hook forwarding is view-only**: The deployer's data hook functions are `view`, so any real hook that requires state changes in `beforePayRecordedWith` or `beforeCashOutRecordedWith` will fail.
186
190
  - **Meta-transaction trust**: ERC2771 `_msgSender()` is used for salt hashing. A compromised trusted forwarder could impersonate senders and create suckers at unexpected addresses.
package/RISKS.md CHANGED
@@ -13,7 +13,7 @@
13
13
  | Risk | Description | Mitigation |
14
14
  |------|-------------|------------|
15
15
  | Sucker privilege abuse | Any registered sucker gets 0% cashout tax | Sucker registration requires DEPLOY_SUCKERS permission |
16
- | Data hook centralization | Deployer is the data hook for all omnichain projects | Simple pass-through logic minimizes attack surface |
16
+ | Data hook centralization | Deployer is the data hook for all omnichain projects | Simple pass-through logic minimizes attack surface. The 721 hook (in `_tiered721HookOf`) and custom hook (in `_extraDataHookOf`) each have their own `useDataHookForCashOut` flag — set to `false` on the 721 metadata to skip it for fungible cashouts. Suckers always get the early return regardless of hook configuration. |
17
17
  | Controller mismatch | Reverts if provided controller doesn't match project's actual controller | Explicit validation via `JBOmnichainDeployer_ControllerMismatch` |
18
18
  | Invalid self-hook | Reverts if someone tries to set deployer as hook for deployer itself | `JBOmnichainDeployer_InvalidHook` check |
19
19
  | Ownership transfer | Project ownership transferred during deployment | Ownership returned to caller after setup |
package/SKILLS.md CHANGED
@@ -2,13 +2,13 @@
2
2
 
3
3
  ## Purpose
4
4
 
5
- Single-transaction deployment of Juicebox projects with cross-chain suckers and optional 721 tiers hooks. Wraps the project's data hook to give suckers tax-free cash outs and mint permission without interfering with custom hooks. Supports composing a 721 hook alongside a custom data hook (e.g., buyback hook) — both run on every payment.
5
+ Single-transaction deployment of Juicebox projects with cross-chain suckers and optional 721 tiers hooks. Wraps the project's data hooks to give suckers tax-free cash outs and mint permission without interfering with custom hooks. Stores the 721 hook per-ruleset in `_tiered721HookOf` and the optional custom hook per-ruleset in `_extraDataHookOf`, each with their own `useDataHookForCashOut` flag. Supports composing a 721 hook alongside a custom data hook (e.g., buyback hook) — both run on every payment.
6
6
 
7
7
  ## Contracts
8
8
 
9
9
  | Contract | Role |
10
10
  |----------|------|
11
- | `JBOmnichainDeployer` | Deploys projects/rulesets/suckers, wraps data hooks for sucker tax exemption. Implements `IJBRulesetDataHook`, `IERC721Receiver`, `ERC2771Context`, `JBPermissioned`. |
11
+ | `JBOmnichainDeployer` | Deploys projects/rulesets/suckers, wraps data hooks for sucker tax exemption. Stores 721 hook per-ruleset in `_tiered721HookOf` and custom hook per-ruleset in `_extraDataHookOf`. Implements `IJBRulesetDataHook`, `IERC721Receiver`, `ERC2771Context`, `JBPermissioned`. |
12
12
 
13
13
  ## Key Functions
14
14
 
@@ -17,27 +17,27 @@ Single-transaction deployment of Juicebox projects with cross-chain suckers and
17
17
  | Function | What it does |
18
18
  |----------|-------------|
19
19
  | `launchProjectFor(owner, projectUri, rulesetConfigs, terminalConfigs, memo, suckerConfig, controller)` | Creates a new project with rulesets, terminals, and suckers in one tx. Temporarily holds the project NFT. Returns `(projectId, suckers)`. |
20
- | `launch721ProjectFor(owner, deployTiersHookConfig, launchProjectConfig, suckerConfig, controller, dataHook, salt)` | Same as above but also deploys a 721 tiers hook. Pass a custom `dataHook` (e.g., buyback hook) to compose alongside the 721 hook, or `address(0)` for none. Returns `(projectId, hook, suckers)`. |
20
+ | `launch721ProjectFor(owner, deployTiersHookConfig, launchProjectConfig, suckerConfig, controller, dataHookConfig, salt)` | Same as above but also deploys a 721 tiers hook. Pass a `JBDeployerHookConfig` with a custom data hook (e.g., buyback hook) to compose alongside the 721 hook, or `dataHook: address(0)` for none. Each hook gets its own `useDataHookForPay`/`useDataHookForCashOut` flags. Returns `(projectId, hook, suckers)`. |
21
21
  | `launchRulesetsFor(projectId, rulesetConfigs, terminalConfigs, memo, controller)` | Launches new rulesets + terminals for an existing project. Requires `QUEUE_RULESETS` + `SET_TERMINALS`. |
22
- | `launch721RulesetsFor(projectId, deployTiersHookConfig, launchRulesetsConfig, controller, dataHook, salt)` | Launches rulesets with a new 721 tiers hook + optional custom data hook. Requires `QUEUE_RULESETS` + `SET_TERMINALS`. |
22
+ | `launch721RulesetsFor(projectId, deployTiersHookConfig, launchRulesetsConfig, controller, dataHookConfig, salt)` | Launches rulesets with a new 721 tiers hook + optional custom data hook config. Requires `QUEUE_RULESETS` + `SET_TERMINALS`. |
23
23
  | `queueRulesetsOf(projectId, rulesetConfigs, memo, controller)` | Queues future rulesets. Requires `QUEUE_RULESETS`. Reverts if rulesets were already queued in the same block. |
24
- | `queue721RulesetsOf(projectId, deployTiersHookConfig, queueRulesetsConfig, controller, dataHook, salt)` | Queues rulesets with a new 721 tiers hook + optional custom data hook. Same same-block guard. |
24
+ | `queue721RulesetsOf(projectId, deployTiersHookConfig, queueRulesetsConfig, controller, dataHookConfig, salt)` | Queues rulesets with a new 721 tiers hook + optional custom data hook config. Same same-block guard. |
25
25
  | `deploySuckersFor(projectId, suckerConfig)` | Deploys new suckers for an existing project. Requires `DEPLOY_SUCKERS`. |
26
26
 
27
27
  ### Data Hook (IJBRulesetDataHook)
28
28
 
29
29
  | Function | What it does |
30
30
  |----------|-------------|
31
- | `beforePayRecordedWith(context)` | Calls the 721 hook first for its specs (including split amounts), then calls the custom data hook with a reduced amount context (payment minus split amount) for weight + specs. Adjusts the returned weight proportionally so the terminal only mints tokens for the amount entering the project (`weight = mulDiv(weight, amount - splitAmount, amount)`). Merges both (721 hook specs first, then custom hook specs). |
32
- | `beforeCashOutRecordedWith(context)` | If holder is a sucker: returns 0% tax immediately. If 721 hook exists: delegates to it (takes priority). Otherwise forwards to the custom data hook, or returns defaults. |
33
- | `hasMintPermissionFor(projectId, ruleset, addr)` | Returns `true` for registered suckers, OR if the custom data hook grants permission, OR if the 721 hook grants permission. Returns `false` only if none grant it. |
31
+ | `beforePayRecordedWith(context)` | Calls the 721 hook first (via `_tiered721HookOf`) for its specs (including split amounts), then calls the custom hook from `_extraDataHookOf` (if `useDataHookForPay: true`) with a reduced amount context (payment minus split amount) for weight + specs. Adjusts the returned weight proportionally so the terminal only mints tokens for the amount entering the project (`weight = mulDiv(weight, amount - splitAmount, amount)`). Merges both (721 hook specs first, then custom hook specs). |
32
+ | `beforeCashOutRecordedWith(context)` | If holder is a sucker: returns 0% tax immediately. Checks 721 hook first (if `useDataHookForCashOut: true`), then custom hook from `_extraDataHookOf`. The first with the flag set handles it. If the 721 hook has the flag set and reverts (e.g., fungible cashout), the revert propagates. If neither has the flag set, returns original values. |
33
+ | `hasMintPermissionFor(projectId, ruleset, addr)` | Returns `true` for registered suckers, OR if the custom hook in `_extraDataHookOf` grants permission. Returns `false` only if it doesn't grant it. |
34
34
 
35
35
  ### Views
36
36
 
37
37
  | Function | What it does |
38
38
  |----------|-------------|
39
- | `dataHookOf(projectId, rulesetId)` | Returns the stored `(useDataHookForPay, useDataHookForCashOut, dataHook)` for a given project and ruleset. This is the custom data hook, not the 721 hook. |
40
- | `tiered721HookOf(projectId)` | Returns the project's 721 tiers hook, stored separately from the custom data hook. Returns `address(0)` if no 721 hook was deployed. |
39
+ | `extraDataHookOf(projectId, rulesetId)` | Returns the stored `JBDeployerHookConfig` for a given project and ruleset. Contains the custom data hook (e.g., buyback hook) with its per-hook flags. Returns empty struct if none configured. |
40
+ | `tiered721HookOf(projectId, rulesetId)` | Returns the 721 tiers hook and its `useDataHookForCashOut` flag for a given project and ruleset. Returns `(address(0), false)` if no 721 hook was deployed. |
41
41
  | `supportsInterface(interfaceId)` | Returns `true` for `IJBOmnichainDeployer`, `IJBRulesetDataHook`, `IERC721Receiver`, `IERC165`. |
42
42
  | `onERC721Received(...)` | Accepts project NFTs from `PROJECTS` only. Reverts for any other NFT contract. |
43
43
 
@@ -56,7 +56,8 @@ Single-transaction deployment of Juicebox projects with cross-chain suckers and
56
56
 
57
57
  | Struct | Key Fields | Used In |
58
58
  |--------|------------|---------|
59
- | `JBDeployerHookConfig` | `bool useDataHookForPay`, `bool useDataHookForCashOut`, `IJBRulesetDataHook dataHook` | `_dataHookOf` mapping keyed by `(projectId, rulesetId)`. Stores the custom data hook only — the 721 hook is in `tiered721HookOf`. |
59
+ | `JBDeployerHookConfig` | `IJBRulesetDataHook dataHook`, `bool useDataHookForPay`, `bool useDataHookForCashOut` | `_extraDataHookOf` mapping keyed by `(projectId, rulesetId)` single custom hook config. |
60
+ | `JBTiered721HookConfig` | `IJB721TiersHook hook`, `bool useDataHookForCashOut` | `_tiered721HookOf` mapping keyed by `(projectId, rulesetId)` → per-ruleset 721 hook config. |
60
61
  | `JBSuckerDeploymentConfig` | `JBSuckerDeployerConfig[] deployerConfigurations`, `bytes32 salt` | All launch and deploy functions |
61
62
 
62
63
  ## Permission IDs
@@ -72,7 +73,7 @@ Single-transaction deployment of Juicebox projects with cross-chain suckers and
72
73
 
73
74
  | Error | When |
74
75
  |-------|------|
75
- | `JBOmnichainDeployer_InvalidHook` | `_setup()` detects `rulesetConfig.metadata.dataHook == address(this)` -- prevents infinite forwarding loops |
76
+ | `JBOmnichainDeployer_InvalidHook` | `_setup()` or `_setup721()` detects the hook is `address(this)` -- prevents infinite forwarding loops |
76
77
  | `JBOmnichainDeployer_UnexpectedNFTReceived` | `onERC721Received` called by a contract other than `PROJECTS` |
77
78
  | `JBOmnichainDeployer_RulesetIdsUnpredictable` | `queueRulesetsOf`/`queue721RulesetsOf` called when `latestRulesetIdOf(projectId) >= block.timestamp` -- ruleset ID prediction would fail |
78
79
  | `JBOmnichainDeployer_ProjectIdMismatch` | `launchProjectFor`/`launch721ProjectFor` -- the project ID returned by the controller does not match the predicted `PROJECTS.count() + 1` |
@@ -82,25 +83,25 @@ Single-transaction deployment of Juicebox projects with cross-chain suckers and
82
83
 
83
84
  1. `launchProjectFor` and `launch721ProjectFor` require **no permissions** -- anyone can launch a project to any owner address.
84
85
  2. `queueRulesetsOf` and `queue721RulesetsOf` **revert if called in the same block** as a previous ruleset queue (whether via deployer or directly). The `launch*` functions don't have this guard because they predict IDs from `PROJECTS.count()`, which is always 0 for a new project.
85
- 3. Ruleset IDs in `_dataHookOf` are keyed by `block.timestamp + i`. If the controller assigns different IDs than predicted, the stored hook config will be orphaned and the deployer will behave as if no hook was set (returning default values).
86
+ 3. Ruleset IDs in `_extraDataHookOf` are keyed by `block.timestamp + i`. If the controller assigns different IDs than predicted, the stored hook configs will be orphaned and the deployer will behave as if no hooks were set (returning default values).
86
87
  4. Sucker deployment salts are hashed with `_msgSender()`: `keccak256(abi.encode(salt, _msgSender()))`. Cross-chain deterministic addresses require using the **same sender** on each chain. For `launch721ProjectFor`, the 721 hook salt uses `keccak256(abi.encode(_msgSender(), salt))` (reversed order).
87
88
  5. `salt = bytes32(0)` **skips sucker deployment entirely**. Use a nonzero salt to deploy suckers.
88
- 6. The deployer **always forces `useDataHookForCashOut = true`** on every ruleset it touches, even if the original config had it as `false`. This is required so the deployer can intercept cash outs to check for suckers.
89
- 7. Suckers get an **early return** in `beforeCashOutRecordedWith` -- they bypass both the 721 hook and custom data hook entirely. This means suckers can cash out even if either hook would revert.
90
- 8. If no hooks are stored, `hasMintPermissionFor` returns `false` for non-suckers. It does **not** return the default `true`. Both the custom data hook and 721 hook are checked — either one can grant permission.
89
+ 6. The deployer **always forces `useDataHookForCashOut = true`** at the protocol level so it can intercept cash outs for sucker tax exemption. However, the 721 hook's `useDataHookForCashOut` flag (stored in `_tiered721HookOf`) and the custom hook's flag (stored in `_extraDataHookOf`) each control whether that hook processes cash outs. Set `useDataHookForCashOut: false` on the 721 metadata to skip it for fungible cashouts (it reverts with `JB721Hook_UnexpectedTokenCashedOut` otherwise).
90
+ 7. Suckers get an **early return** in `beforeCashOutRecordedWith` -- they bypass all stored hooks entirely. This means suckers can cash out even if any hook would revert.
91
+ 8. If no custom hook is stored or it doesn't grant permission, `hasMintPermissionFor` returns `false` for non-suckers. Only the custom hook in `_extraDataHookOf` is checked — the 721 hook is not consulted.
91
92
  9. 721 ruleset config conversion enforces `useDataHookForPay = true` and `allowSetCustomToken = false`. These cannot be overridden.
92
93
  10. Hook ownership is transferred to the **project** (not the owner) via `JBOwnable.transferOwnershipToProject(projectId)`. The project owner controls the hook through project ownership.
93
94
  11. The deployer holds the project NFT temporarily during launch. If the controller's `launchProjectFor` reverts, the entire transaction reverts -- no stuck NFTs.
94
95
  12. The constructor grants `MAP_SUCKER_TOKEN` permission to `SUCKER_REGISTRY` with `projectId=0`, meaning the registry can map tokens for **any project** deployed through this deployer.
95
96
  13. All data hook functions (`beforePayRecordedWith`, `beforeCashOutRecordedWith`, `hasMintPermissionFor`) are `view`. If the project's real hook needs to modify state in these functions, it will fail.
96
- 14. Setting a ruleset's `dataHook` to `address(this)` (the deployer itself) reverts with `JBOmnichainDeployer_InvalidHook`. This prevents infinite forwarding loops.
97
+ 14. Setting a hook's `dataHook` to `address(this)` (the deployer itself) reverts with `JBOmnichainDeployer_InvalidHook` in both `_setup()` and `_setup721()`. This prevents infinite forwarding loops.
97
98
  15. `onERC721Received` only accepts NFTs from the `PROJECTS` contract. Sending any other ERC-721 to the deployer will revert.
98
99
  16. ERC2771 meta-transaction support allows gasless deployments via a trusted forwarder. Salt hashing uses `_msgSender()` (not `msg.sender`), so forwarder-relayed transactions use the original sender's address for deterministic sucker addresses.
99
100
  17. **Prefer `launch721ProjectFor` over `launchProjectFor` even with empty tiers.** Using `launch721ProjectFor` with an empty tiers array wires up the 721 hook from the start, so the project owner can add and sell NFTs later without needing to reconfigure the data hook in a new ruleset. `launchProjectFor` skips hook deployment entirely.
100
- 18. The 721 hook is stored **per-project** in `tiered721HookOf[projectId]`, not per-ruleset. It persists across all rulesets. The custom data hook is stored **per-ruleset** in `_dataHookOf[projectId][rulesetId]`.
101
- 19. For payments, `beforePayRecordedWith` calls the 721 hook first to get its specs (including split fund amounts and tier metadata), then calls the custom data hook with a reduced amount context (payment minus split amount) so the buyback hook only considers the available amount. The deployer then adjusts the weight proportionally for splits (`weight = mulDiv(weight, amount - splitAmount, amount)`). The 721 hook's specs come first in the merged result.
102
- 20. For cash outs, the 721 hook **takes priority** over the custom data hook. If a 721 hook exists, `beforeCashOutRecordedWith` delegates entirely to it, ignoring the custom data hook.
103
- 21. The `launch721*` and `queue721*` functions now accept a `dataHook` parameter (type `address`) for the custom data hook to compose alongside the 721 hook. Pass `address(0)` for no custom hook.
101
+ 18. The 721 hook is stored per-ruleset in `_tiered721HookOf[projectId][rulesetId]` with its `useDataHookForCashOut` flag. The custom data hook (if any) is stored separately in `_extraDataHookOf[projectId][rulesetId]`. They are never in the same array.
102
+ 19. For payments, `beforePayRecordedWith` calls the 721 hook first (via `_tiered721HookOf`) to get its specs (including split fund amounts and tier metadata), then calls the custom hook from `_extraDataHookOf` (if `useDataHookForPay: true`) with a reduced amount context (payment minus split amount) so the buyback hook only considers the available amount. The deployer then adjusts the weight proportionally for splits (`weight = mulDiv(weight, amount - splitAmount, amount)`). The 721 hook's specs come first in the merged result.
103
+ 20. For cash outs, `beforeCashOutRecordedWith` checks the 721 hook first (from `_tiered721HookOf`, if `useDataHookForCashOut: true`), then the custom hook (from `_extraDataHookOf`). If the 721 hook has the flag set and reverts (e.g., `JB721Hook_UnexpectedTokenCashedOut` for fungible cashouts), the revert propagates. Set `useDataHookForCashOut: false` on the 721 metadata to skip it and let the custom hook handle cashouts.
104
+ 21. The `launch721*` and `queue721*` functions accept a `dataHookConfig` parameter (type `JBDeployerHookConfig`) for the custom data hook to compose alongside the 721 hook. Each hook gets its own `useDataHookForPay`/`useDataHookForCashOut` flags. Pass `dataHook: address(0)` for no custom hook.
104
105
 
105
106
  ## Example Integration
106
107
 
@@ -149,7 +150,11 @@ address[] memory newSuckers = omnichainDeployer.deploySuckersFor({
149
150
  deployTiersHookConfig: tiersHookConfig,
150
151
  queueRulesetsConfig: queueConfig,
151
152
  controller: controller,
152
- dataHook: address(buybackHook), // custom data hook (or address(0) for none)
153
+ dataHookConfig: JBDeployerHookConfig({
154
+ dataHook: IJBRulesetDataHook(address(buybackHook)),
155
+ useDataHookForPay: true,
156
+ useDataHookForCashOut: false
157
+ }),
153
158
  salt: bytes32("my-hook-salt")
154
159
  });
155
160
  ```
package/STYLE_GUIDE.md CHANGED
@@ -314,14 +314,11 @@ Standard config across all repos:
314
314
  ```toml
315
315
  [profile.default]
316
316
  solc = '0.8.26'
317
- evm_version = 'paris'
317
+ evm_version = 'cancun'
318
318
  optimizer_runs = 200
319
319
  libs = ["node_modules", "lib"]
320
320
  fs_permissions = [{ access = "read-write", path = "./"}]
321
321
 
322
- [profile.ci_sizes]
323
- optimizer_runs = 200
324
-
325
322
  [fuzz]
326
323
  runs = 4096
327
324
 
@@ -336,12 +333,14 @@ multiline_func_header = "all"
336
333
  wrap_comments = true
337
334
  ```
338
335
 
339
- **Variations:**
340
- - `evm_version = 'cancun'` for repos using transient storage (buyback-hook, router-terminal, univ4-router)
341
- - `via_ir = true` for repos hitting stack-too-deep (buyback-hook, banny-retail, univ4-lp-split-hook, deploy-all)
342
- - `optimizer = false` only for deploy-all-v6 (stack-too-deep with optimization)
336
+ **Optional sections (add only when needed):**
337
+ - `[rpc_endpoints]` repos with fork tests. Maps named endpoints to env vars (e.g. `ethereum = "${RPC_ETHEREUM_MAINNET}"`).
338
+ - `[profile.ci_sizes]` only when CI needs different optimizer settings than defaults for the size check step (e.g. `optimizer_runs = 200` when the default profile uses a lower value).
343
339
 
344
- **This repo's deviations:** `[profile.fork]` with `via_ir = true` and `evm_version = 'cancun'` for fork tests. `[lint] lint_on_build = false`.
340
+ **Common variations:**
341
+ - `via_ir = true` when hitting stack-too-deep
342
+ - `optimizer = false` when optimization causes stack-too-deep
343
+ - `optimizer_runs` reduced when deep struct nesting causes stack-too-deep at 200 runs
345
344
 
346
345
  ### CI Workflows
347
346
 
@@ -374,7 +373,7 @@ jobs:
374
373
  env:
375
374
  RPC_ETHEREUM_MAINNET: ${{ secrets.RPC_ETHEREUM_MAINNET }}
376
375
  - name: Check contract sizes
377
- run: FOUNDRY_PROFILE=ci_sizes forge build --sizes --skip "*/test/**" --skip "*/script/**" --skip SphinxUtils
376
+ run: forge build --sizes --skip "*/test/**" --skip "*/script/**" --skip SphinxUtils
378
377
  ```
379
378
 
380
379
  **lint.yml:**
@@ -396,11 +395,60 @@ jobs:
396
395
  run: forge fmt --check
397
396
  ```
398
397
 
398
+ **slither.yml** (repos with `src/` contracts only):
399
+ ```yaml
400
+ name: slither
401
+ on:
402
+ pull_request:
403
+ branches:
404
+ - main
405
+ push:
406
+ branches:
407
+ - main
408
+ jobs:
409
+ analyze:
410
+ runs-on: ubuntu-latest
411
+ steps:
412
+ - uses: actions/checkout@v4
413
+ with:
414
+ submodules: recursive
415
+ - uses: actions/setup-node@v4
416
+ with:
417
+ node-version: latest
418
+ - name: Install npm dependencies
419
+ run: npm install --omit=dev
420
+ - name: Install Foundry
421
+ uses: foundry-rs/foundry-toolchain@v1
422
+ - name: Run slither
423
+ uses: crytic/slither-action@v0.3.1
424
+ with:
425
+ slither-config: slither-ci.config.json
426
+ fail-on: medium
427
+ ```
428
+
429
+ **slither-ci.config.json:**
430
+ ```json
431
+ {
432
+ "detectors_to_exclude": "timestamp,uninitialized-local,naming-convention,solc-version,shadowing-local",
433
+ "exclude_informational": true,
434
+ "exclude_low": false,
435
+ "exclude_medium": false,
436
+ "exclude_high": false,
437
+ "disable_color": false,
438
+ "filter_paths": "(mocks/|test/|node_modules/|lib/)",
439
+ "legacy_ast": false
440
+ }
441
+ ```
442
+
443
+ **Variations:**
444
+ - Deployer-only repos (no `src/`, only `script/`) skip slither entirely — the action's internal `forge build` skips `test/` and `script/` by default, leaving nothing to compile.
445
+ - Use inline `// slither-disable-next-line <detector>` to suppress known false positives rather than adding to `detectors_to_exclude` in the config. The comment must be on the line immediately before the flagged expression.
446
+
399
447
  ### package.json
400
448
 
401
449
  ```json
402
450
  {
403
- "name": "@bananapus/omnichain-deployers-v6",
451
+ "name": "@bananapus/package-name-v6",
404
452
  "version": "x.x.x",
405
453
  "license": "MIT",
406
454
  "repository": { "type": "git", "url": "git+https://github.com/Org/repo.git" },
@@ -420,13 +468,62 @@ jobs:
420
468
 
421
469
  ### remappings.txt
422
470
 
423
- Every repo has a `remappings.txt`. Minimal content:
471
+ Every repo has a `remappings.txt` as the **single source of truth** for import remappings. Never add remappings to `foundry.toml`.
472
+
473
+ **Principle:** Import paths in Solidity source must match npm package names exactly. With `libs = ["node_modules", "lib"]`, Foundry auto-resolves `@scope/package/path/File.sol` → `node_modules/@scope/package/path/File.sol`. No remapping needed for packages installed as real directories.
474
+
475
+ **Note:** Auto-resolution does **not** work for symlinked packages (e.g. npm workspace links). Workspace repos like `deploy-all-v6` and `nana-cli-v6` need explicit `@scope/package/=node_modules/@scope/package/` remappings for each symlinked dependency.
476
+
477
+ **Minimal content** (most repos):
478
+
479
+ ```
480
+ forge-std/=lib/forge-std/src/
481
+ ```
482
+
483
+ Only add extra remappings for:
484
+ - **`forge-std`** — always needed (git submodule with `src/` subdirectory)
485
+ - **Repo-specific `lib/` submodules** that have no npm package (e.g., `hookmate/=lib/hookmate/src/`)
486
+ - **Symlinked npm packages** — need explicit `@scope/package/=node_modules/@scope/package/` entries
487
+ - **Nested transitive deps** — e.g., `@chainlink/contracts-ccip/` nested inside `@bananapus/suckers-v6/node_modules/`
488
+
489
+ **Never add remappings for:**
490
+ - npm packages that match their import path and are installed as real directories — they auto-resolve
491
+ - Short-form aliases (e.g., `@bananapus/core/` → `@bananapus/core-v6/src/`) — fix the import instead
492
+ - Packages available via npm that are also git submodules — remove the submodule, use npm
493
+
494
+ **Import path convention:**
495
+
496
+ | Package | Import path | Resolves to |
497
+ |---------|------------|-------------|
498
+ | `@bananapus/core-v6` | `@bananapus/core-v6/src/libraries/JBConstants.sol` | `node_modules/@bananapus/core-v6/src/...` |
499
+ | `@openzeppelin/contracts` | `@openzeppelin/contracts/token/ERC20/IERC20.sol` | `node_modules/@openzeppelin/contracts/...` |
500
+ | `@uniswap/v4-core` | `@uniswap/v4-core/src/interfaces/IPoolManager.sol` | `node_modules/@uniswap/v4-core/src/...` |
501
+
502
+ ### Linting
503
+
504
+ Solar (Foundry's built-in linter) runs automatically during `forge build`. It scans all `.sol` files in `libs` directories, including `node_modules`.
424
505
 
506
+ **All test helpers must use relative imports** (e.g. `../../src/structs/JBRuleset.sol`), not bare `src/` imports. This ensures solar can resolve paths when the helper is consumed via npm in downstream repos.
507
+
508
+ ### Fork Tests
509
+
510
+ Fork tests use named RPC endpoints defined in `[rpc_endpoints]` of `foundry.toml`. No skip guards — fork tests should hard-fail if the RPC endpoint is unavailable, making CI failures explicit.
511
+
512
+ ```solidity
513
+ function setUp() public {
514
+ vm.createSelectFork("ethereum");
515
+ // ... setup code
516
+ }
425
517
  ```
426
- @sphinx-labs/contracts/=lib/sphinx/packages/contracts/contracts/foundry
518
+
519
+ The endpoint name (e.g. `"ethereum"`) maps to an env var via `foundry.toml`:
520
+
521
+ ```toml
522
+ [rpc_endpoints]
523
+ ethereum = "${RPC_ETHEREUM_MAINNET}"
427
524
  ```
428
525
 
429
- Additional mappings as needed for repo-specific dependencies.
526
+ For multi-chain fork tests, add all needed endpoints.
430
527
 
431
528
  ### Formatting
432
529
 
@@ -437,15 +534,6 @@ Run `forge fmt` before committing. The `[fmt]` config in `foundry.toml` enforces
437
534
 
438
535
  CI checks formatting via `forge fmt --check`.
439
536
 
440
- ### CI Secrets
441
-
442
- | Secret | Purpose |
443
- |--------|--------|
444
- | `NPM_TOKEN` | npm publish access (used by `publish.yml`) |
445
- | `RPC_ETHEREUM_MAINNET` | Ethereum mainnet RPC URL for fork tests (used by `test.yml`) |
446
-
447
- Fork tests require `RPC_ETHEREUM_MAINNET` — they fail if it's missing.
448
-
449
537
  ### Branching
450
538
 
451
539
  - `main` is the primary branch
@@ -463,4 +551,8 @@ Fork tests require `RPC_ETHEREUM_MAINNET` — they fail if it's missing.
463
551
 
464
552
  ### Contract Size Checks
465
553
 
466
- CI runs `FOUNDRY_PROFILE=ci_sizes forge build --sizes` to catch contracts approaching the 24KB limit. The `ci_sizes` profile uses `optimizer_runs = 200` for realistic size measurement even when the default profile has different optimizer settings.
554
+ CI runs `forge build --sizes` to catch contracts approaching the 24KB limit. When the repo's default `optimizer_runs` differs from what you want for size checking, use `FOUNDRY_PROFILE=ci_sizes forge build --sizes` with a `[profile.ci_sizes]` section in `foundry.toml`.
555
+
556
+ ## Repo-Specific Deviations
557
+
558
+ None. This repo follows the standard configuration exactly.
package/foundry.toml CHANGED
@@ -18,3 +18,6 @@ fail_on_revert = false
18
18
  number_underscore = "thousands"
19
19
  multiline_func_header = "all"
20
20
  wrap_comments = true
21
+
22
+ [rpc_endpoints]
23
+ ethereum = "${RPC_ETHEREUM_MAINNET}"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bananapus/omnichain-deployers-v6",
3
- "version": "0.0.7",
3
+ "version": "0.0.8",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -17,12 +17,12 @@
17
17
  "artifacts": "source ./.env && npx sphinx artifacts --org-id 'ea165b21-7cdc-4d7b-be59-ecdd4c26bee4' --project-name 'nana-omnichain-deployers-v6'"
18
18
  },
19
19
  "dependencies": {
20
- "@bananapus/721-hook-v6": "^0.0.13",
20
+ "@bananapus/721-hook-v6": "^0.0.14",
21
21
  "@bananapus/buyback-hook-v6": "^0.0.10",
22
- "@bananapus/core-v6": "^0.0.14",
22
+ "@bananapus/core-v6": "^0.0.15",
23
23
  "@bananapus/ownable-v6": "^0.0.7",
24
- "@bananapus/permission-ids-v6": "^0.0.6",
25
- "@bananapus/suckers-v6": "^0.0.8",
24
+ "@bananapus/permission-ids-v6": "^0.0.7",
25
+ "@bananapus/suckers-v6": "^0.0.9",
26
26
  "@openzeppelin/contracts": "^5.6.1",
27
27
  "@uniswap/v4-core": "^1.0.2"
28
28
  },
package/remappings.txt CHANGED
@@ -1 +1 @@
1
- @sphinx-labs/contracts/=node_modules/@sphinx-labs/contracts/contracts/foundry
1
+ forge-std/=lib/forge-std/src/
@@ -1,7 +1,7 @@
1
1
  // SPDX-License-Identifier: MIT
2
2
  pragma solidity ^0.8.26;
3
3
 
4
- import "@sphinx-labs/contracts/SphinxPlugin.sol";
4
+ import "@sphinx-labs/contracts/contracts/foundry/SphinxPlugin.sol";
5
5
  import {Script, stdJson, VmSafe} from "forge-std/Script.sol";
6
6
 
7
7
  import "@bananapus/core-v6/script/helpers/CoreDeploymentLib.sol";
@@ -3,7 +3,7 @@ pragma solidity 0.8.26;
3
3
 
4
4
  import {stdJson} from "forge-std/Script.sol";
5
5
  import {Vm} from "forge-std/Vm.sol";
6
- import {SphinxConstants, NetworkInfo} from "@sphinx-labs/contracts/SphinxConstants.sol";
6
+ import {SphinxConstants, NetworkInfo} from "@sphinx-labs/contracts/contracts/foundry/SphinxConstants.sol";
7
7
 
8
8
  import {JBOmnichainDeployer} from "src/JBOmnichainDeployer.sol";
9
9