@bananapus/router-terminal-v6 0.0.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Bananapus
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,145 @@
1
+ # nana-router-terminal-v6
2
+
3
+ A Juicebox terminal that accepts payments in any token, dynamically discovers what token each destination project accepts, and routes the payment there — via direct forwarding, Uniswap swap, JB token cashout, or a combination. Supports both Uniswap V3 and V4 pools, choosing whichever offers better liquidity.
4
+
5
+ _If you're having trouble understanding this contract, take a look at the [core protocol contracts](https://github.com/Bananapus/nana-core-v6) and the [documentation](https://docs.juicebox.money/) first. If you have questions, reach out on [Discord](https://discord.com/invite/ErQYmth4dS)._
6
+
7
+ ## Architecture
8
+
9
+ | Contract | Description |
10
+ |----------|-------------|
11
+ | `JBRouterTerminal` | Core terminal. Accepts any token via `pay` or `addToBalanceOf`, discovers the destination project's accepted token, and routes there — swapping through Uniswap V3 or V4 pools if needed. Uses TWAP oracle for automatic slippage protection when the caller doesn't provide a quote. |
12
+ | `JBRouterTerminalRegistry` | A proxy terminal that delegates `pay` and `addToBalanceOf` to a per-project or default `JBRouterTerminal` instance. Allows project owners to choose (and lock) which router terminal implementation they use. |
13
+
14
+ ## How It Works
15
+
16
+ 1. A payer calls `pay(projectId, token, amount, ...)` with any token.
17
+ 2. The terminal accepts the token (supports ERC-20 approvals and Permit2).
18
+ 3. It discovers the destination project's accepted token by querying the directory.
19
+ 4. If the input token differs from the accepted token, it converts it — by cashing out JB project tokens, swapping through the best Uniswap V3 or V4 pool across multiple fee tiers, or both.
20
+ 5. Slippage protection: the caller can pass a minimum output quote in metadata (`quoteForSwap` key), or the terminal calculates one from the pool's TWAP oracle with dynamic slippage tolerance.
21
+ 6. The output tokens are forwarded to the project's primary terminal via `terminal.pay(...)` or `terminal.addToBalanceOf(...)`.
22
+
23
+ ```mermaid
24
+ sequenceDiagram
25
+ participant Frontend client
26
+ participant JBDirectory
27
+ participant JBRouterTerminal
28
+ participant Uniswap V3/V4
29
+ participant JBMultiTerminal
30
+ Note left of Frontend client: User pays project with USDC
31
+ Frontend client->>JBDirectory: Checks project's primary USDC terminal
32
+ JBDirectory->>Frontend client: Returns JBRouterTerminal
33
+ Frontend client->>JBRouterTerminal: Calls pay(...) with USDC (and optional quote)
34
+ JBRouterTerminal->>JBDirectory: Discovers project's accepted token (ETH)
35
+ JBRouterTerminal->>Uniswap V3/V4: Finds best pool, swaps USDC for ETH
36
+ JBRouterTerminal->>JBMultiTerminal: Pays ETH to project's terminal
37
+ Note right of JBMultiTerminal: Mint tokens for original beneficiary
38
+ ```
39
+
40
+ ### Routing Strategies
41
+
42
+ The terminal can route payments via multiple strategies:
43
+
44
+ - **Direct forwarding** — If the input token is already accepted by the destination terminal.
45
+ - **Uniswap V3 swap** — Through the highest-liquidity V3 pool across fee tiers (0.01%, 0.05%, 0.3%, 1%).
46
+ - **Uniswap V4 swap** — Through V4 pools, also searching multiple fee/tick-spacing configurations.
47
+ - **JB token cashout** — Redeeming JB project tokens to get the needed output token.
48
+ - **Combination** — Chaining strategies when no single route works.
49
+
50
+ ## Install
51
+
52
+ For projects using `npm` to manage dependencies (recommended):
53
+
54
+ ```bash
55
+ npm install @bananapus/router-terminal-v6
56
+ ```
57
+
58
+ For projects using `forge` to manage dependencies:
59
+
60
+ ```bash
61
+ forge install Bananapus/nana-router-terminal-v6
62
+ ```
63
+
64
+ If you're using `forge`, add `@bananapus/router-terminal-v6/=lib/nana-router-terminal-v6/` to `remappings.txt`.
65
+
66
+ ## Develop
67
+
68
+ `nana-router-terminal-v6` uses [npm](https://www.npmjs.com/) (version >=20.0.0) for package management and [Foundry](https://github.com/foundry-rs/foundry) for builds and tests.
69
+
70
+ ```bash
71
+ npm ci && forge install
72
+ ```
73
+
74
+ | Command | Description |
75
+ |---------|-------------|
76
+ | `forge build` | Compile the contracts and write artifacts to `out`. |
77
+ | `forge test` | Run the tests. |
78
+ | `forge fmt` | Lint. |
79
+ | `forge build --sizes` | Get contract sizes. |
80
+ | `forge coverage` | Generate a test coverage report. |
81
+ | `forge clean` | Remove the build artifacts and cache directories. |
82
+
83
+ ### Scripts
84
+
85
+ | Command | Description |
86
+ |---------|-------------|
87
+ | `npm test` | Run local tests. |
88
+ | `npm run coverage` | Generate an LCOV test coverage report. |
89
+
90
+ ### Configuration
91
+
92
+ Key `foundry.toml` settings:
93
+
94
+ - `solc = '0.8.26'`
95
+ - `evm_version = 'cancun'` (required for Uniswap V4's transient storage)
96
+ - `optimizer_runs = 100000000`
97
+
98
+ ## Repository Layout
99
+
100
+ ```
101
+ nana-router-terminal-v6/
102
+ ├── src/
103
+ │ ├── JBRouterTerminal.sol # Core router terminal
104
+ │ ├── JBRouterTerminalRegistry.sol # Per-project terminal routing
105
+ │ ├── interfaces/
106
+ │ │ ├── IJBRouterTerminal.sol # Router terminal interface
107
+ │ │ ├── IJBRouterTerminalRegistry.sol # Registry interface
108
+ │ │ └── IWETH9.sol # WETH wrapper interface
109
+ │ ├── libraries/
110
+ │ │ └── JBSwapLib.sol # Swap math and pool discovery
111
+ │ └── structs/
112
+ │ └── PoolInfo.sol # Pool metadata struct
113
+ ├── script/
114
+ │ ├── Deploy.s.sol # Deployment script
115
+ │ └── helpers/
116
+ │ └── RouterTerminalDeploymentLib.sol # Deployment address loader
117
+ └── test/
118
+ ├── RouterTerminal.t.sol # Terminal tests
119
+ └── RouterTerminalRegistry.t.sol # Registry tests
120
+ ```
121
+
122
+ ## Payment Metadata
123
+
124
+ The `JBRouterTerminal` accepts encoded `metadata` in its `pay(...)` function. Metadata is decoded using `JBMetadataResolver`:
125
+
126
+ ```solidity
127
+ (bool exists, bytes memory quote) =
128
+ JBMetadataResolver.getDataFor(JBMetadataResolver.getId("quoteForSwap"), metadata);
129
+
130
+ if (exists) {
131
+ (minAmountOut) = abi.decode(quote, (uint256));
132
+ }
133
+ ```
134
+
135
+ If no quote is provided, the terminal calculates one from the pool's TWAP oracle with a dynamic slippage tolerance based on the estimated price impact of the swap.
136
+
137
+ The terminal also supports Permit2 metadata (key: `"permit2"`) for gasless token approvals.
138
+
139
+ ## Risks
140
+
141
+ - The terminal never holds a token balance. After every swap, all output tokens are forwarded and leftover input tokens are returned to the payer.
142
+ - Pool discovery is dynamic — the terminal searches V3 and V4 pools at runtime. If pool liquidity changes between discovery and execution, slippage protection prevents losses.
143
+ - TWAP fallback: when no TWAP observations exist, the terminal falls back to the pool's current spot tick rather than reverting.
144
+ - The `receive()` function only accepts ETH from the WETH contract (during unwrap). All other senders revert.
145
+ - Uniswap V4 requires `cancun` EVM version (transient storage opcodes). This terminal will not work on chains without EIP-1153 support.
package/SKILLS.md ADDED
@@ -0,0 +1,101 @@
1
+ # nana-router-terminal-v6
2
+
3
+ ## Purpose
4
+
5
+ Accept payments in any ERC-20 token (or native ETH), dynamically discover what token the destination project accepts, and route there — via Uniswap V3/V4 swap, direct forwarding, JB token cashout, or a combination.
6
+
7
+ ## Contracts
8
+
9
+ | Contract | Role |
10
+ |----------|------|
11
+ | `JBRouterTerminal` | Core terminal: accepts any token, discovers the best route to the destination project's accepted token, swaps via Uniswap V3 or V4, forwards to primary terminal. Implements `IJBTerminal`, `IJBPermitTerminal`, `IUniswapV3SwapCallback`, `IUnlockCallback`. |
12
+ | `JBRouterTerminalRegistry` | Proxy terminal routing `pay`/`addToBalanceOf` to a per-project or default `JBRouterTerminal`. Implements `IJBTerminal`. |
13
+
14
+ ## Key Functions
15
+
16
+ | Function | Contract | What it does |
17
+ |----------|----------|--------------|
18
+ | `pay(projectId, token, amount, beneficiary, minReturnedTokens, memo, metadata)` | `JBRouterTerminal` | Accept any token, discover destination's accepted token, swap if needed via best V3/V4 pool, forward to project's primary terminal. Returns project token count. |
19
+ | `addToBalanceOf(projectId, token, amount, shouldReturnHeldFees, memo, metadata)` | `JBRouterTerminal` | Same routing flow but calls `terminal.addToBalanceOf(...)` instead of `terminal.pay(...)`. |
20
+ | `discoverPool(normalizedTokenIn, normalizedTokenOut)` | `JBRouterTerminal` | Search V3 factory for highest liquidity pool across 4 fee tiers (0.01%, 0.05%, 0.3%, 1%). |
21
+ | `discoverBestPool(normalizedTokenIn, normalizedTokenOut)` | `JBRouterTerminal` | Discover best pool across both V3 and V4, comparing liquidity. Returns `PoolInfo` with version, key, and liquidity. |
22
+ | `setTerminalFor(projectId, terminal)` | `JBRouterTerminalRegistry` | Route a project to a specific allowed router terminal. Requires `SET_ROUTER_TERMINAL` permission. |
23
+ | `lockTerminalFor(projectId)` | `JBRouterTerminalRegistry` | Lock the terminal choice for a project (irreversible). |
24
+ | `allowTerminal(terminal)` | `JBRouterTerminalRegistry` | Owner-only: add a terminal to the allowlist. |
25
+ | `setDefaultTerminal(terminal)` | `JBRouterTerminalRegistry` | Owner-only: set the default terminal for projects without a custom choice. |
26
+
27
+ ## Integration Points
28
+
29
+ | Dependency | Import | Used For |
30
+ |------------|--------|----------|
31
+ | `nana-core-v6` | `IJBDirectory`, `IJBTerminal`, `IJBProjects`, `IJBPermissions`, `IJBTokens` | Directory lookups (`primaryTerminalOf`), project ownership, permission checks, token discovery |
32
+ | `nana-core-v6` | `JBMetadataResolver` | Parsing `quoteForSwap` and `permit2` metadata from calldata |
33
+ | `nana-core-v6` | `JBAccountingContext`, `JBSingleAllowance` | Token accounting and Permit2 allowance structs |
34
+ | `nana-permission-ids-v6` | `JBPermissionIds` | Permission ID constants (`SET_ROUTER_TERMINAL`) |
35
+ | `@uniswap/v3-core` | `IUniswapV3Pool`, `IUniswapV3Factory`, `TickMath` | V3 pool swaps, factory pool discovery, tick math |
36
+ | `@uniswap/v3-periphery` | `OracleLibrary` | TWAP oracle consultation (`consult`, `getQuoteAtTick`, `getOldestObservationSecondsAgo`) |
37
+ | `@uniswap/v4-core` | `IPoolManager`, `PoolKey`, `Currency`, `StateLibrary` | V4 pool swaps and liquidity queries |
38
+ | `@uniswap/permit2` | `IPermit2`, `IAllowanceTransfer` | Gasless token approvals |
39
+ | `@openzeppelin/contracts` | `Ownable`, `ERC2771Context`, `SafeERC20` | Access control, meta-transactions, safe transfers |
40
+
41
+ ## Key Types
42
+
43
+ | Struct/Enum | Key Fields | Used In |
44
+ |-------------|------------|---------|
45
+ | `PoolInfo` | `isV4`, `v3Pool`, `v4Key`, `liquidity` | Returned by `discoverBestPool`. Indicates whether the best route is V3 or V4 and stores the pool details. |
46
+ | `JBAccountingContext` | `token`, `decimals`, `currency` | Token accounting contexts for accepted tokens. |
47
+ | `JBSingleAllowance` | `sigDeadline`, `amount`, `expiration`, `nonce`, `signature` | Decoded from `permit2` metadata key for gasless approvals. |
48
+
49
+ ## Constants
50
+
51
+ | Constant | Value | Purpose |
52
+ |----------|-------|---------|
53
+ | `DEFAULT_TWAP_WINDOW` | `10 minutes` | Default TWAP oracle window for auto-discovered pools |
54
+ | `FEE_TIERS` | `[3000, 500, 10000, 100]` | V3 fee tiers to search (0.3%, 0.05%, 1%, 0.01%) |
55
+ | `V4_FEES` | `[3000, 500, 10000, 100]` | V4 fee tiers to search |
56
+ | `V4_TICK_SPACINGS` | `[60, 10, 200, 1]` | V4 tick spacings paired with fee tiers |
57
+ | `SLIPPAGE_DENOMINATOR` | `10,000` | Basis points denominator for slippage |
58
+
59
+ ## Gotchas
60
+
61
+ - The terminal never holds a token balance. After every swap, all output tokens are forwarded and leftover input tokens are returned to the payer.
62
+ - Unlike the swap terminal which had a fixed `TOKEN_OUT`, the router terminal dynamically discovers what token each project accepts. This makes it a universal entry point.
63
+ - Pool discovery runs at call time — it searches V3 and V4 pools across multiple fee tiers. The best pool (by liquidity) wins. This is gas-intensive but ensures optimal routing.
64
+ - When `tokenIn == NATIVE_TOKEN`, the terminal wraps ETH to WETH before swapping. When the output is `NATIVE_TOKEN`, it unwraps WETH after swapping.
65
+ - The `receive()` function only accepts ETH from the WETH contract (during unwrap). All other senders revert.
66
+ - TWAP fallback: when no observations exist (`oldestObservation == 0`), the terminal falls back to the pool's current spot tick and liquidity rather than reverting.
67
+ - Uniswap V4 requires `cancun` EVM version (transient storage). Chains without EIP-1153 cannot use V4 routing — the terminal falls back to V3.
68
+ - The `JBRouterTerminalRegistry` handles token custody during delegation — it transfers tokens from the payer to itself, then to the underlying terminal.
69
+ - Metadata keys: `"quoteForSwap"` for the minimum output amount, `"permit2"` for gasless approvals.
70
+ - `_msgSender()` (ERC-2771) is used instead of `msg.sender` for meta-transaction compatibility.
71
+ - The `JBSwapLib` library contains the core swap execution logic, extracted for contract size management.
72
+
73
+ ## Example Integration
74
+
75
+ ```solidity
76
+ import {JBRouterTerminal} from "@bananapus/router-terminal-v6/src/JBRouterTerminal.sol";
77
+ import {JBRouterTerminalRegistry} from "@bananapus/router-terminal-v6/src/JBRouterTerminalRegistry.sol";
78
+
79
+ // The registry is the entry point for payments.
80
+ // It delegates to per-project or default JBRouterTerminal instances.
81
+
82
+ // Pay project 1 with USDC — the router terminal discovers that
83
+ // project 1 accepts ETH, finds the best USDC/WETH pool across
84
+ // V3 and V4, swaps, and forwards ETH to the project's terminal.
85
+ IERC20(usdc).approve(address(registry), 1000e6);
86
+ registry.pay{value: 0}(
87
+ 1, // projectId
88
+ usdc, // token (USDC)
89
+ 1000e6, // amount (1000 USDC)
90
+ beneficiary, // who receives project tokens
91
+ 0, // minReturnedTokens
92
+ "Payment via router",
93
+ "" // metadata (empty = use TWAP quote)
94
+ );
95
+
96
+ // Project owners can choose a specific router terminal:
97
+ registry.setTerminalFor(projectId, preferredTerminal);
98
+
99
+ // And lock it permanently:
100
+ registry.lockTerminalFor(projectId);
101
+ ```
package/foundry.toml ADDED
@@ -0,0 +1,22 @@
1
+ [profile.default]
2
+ solc = '0.8.26'
3
+ evm_version = 'cancun'
4
+ optimizer_runs = 100000000
5
+ libs = ["node_modules", "lib"]
6
+ fs_permissions = [{ access = "read-write", path = "./"}]
7
+
8
+ [profile.ci_sizes]
9
+ optimizer_runs = 200
10
+
11
+ [fuzz]
12
+ runs = 4096
13
+
14
+ [invariant]
15
+ runs = 1024
16
+ depth = 100
17
+ fail_on_revert = false
18
+
19
+ [fmt]
20
+ number_underscore = "thousands"
21
+ multiline_func_header = "all"
22
+ wrap_comments = true
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@bananapus/router-terminal-v6",
3
+ "version": "0.0.1",
4
+ "license": "MIT",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/Bananapus/nana-router-terminal-v6"
8
+ },
9
+ "engines": {
10
+ "node": ">=20.0.0"
11
+ },
12
+ "scripts": {
13
+ "test": "forge test",
14
+ "coverage": "forge coverage --match-path \"./src/*.sol\" --report lcov --report summary",
15
+ "deploy:mainnets": "source ./.env && npx sphinx propose ./script/Deploy.s.sol --networks mainnets",
16
+ "deploy:testnets": "source ./.env && npx sphinx propose ./script/Deploy.s.sol --networks testnets",
17
+ "artifacts": "source ./.env && npx sphinx artifacts --org-id 'ea165b21-7cdc-4d7b-be59-ecdd4c26bee4' --project-name 'nana-router-terminal-v6'"
18
+ },
19
+ "dependencies": {
20
+ "@bananapus/core-v6": "^0.0.4",
21
+ "@openzeppelin/contracts": "^5.2.0",
22
+ "@uniswap/permit2": "github:Uniswap/permit2",
23
+ "@uniswap/v3-core": "github:Uniswap/v3-core#0.8",
24
+ "@uniswap/v3-periphery": "github:Uniswap/v3-periphery#0.8",
25
+ "@uniswap/v4-core": "^1.0.0"
26
+ },
27
+ "devDependencies": {
28
+ "@sphinx-labs/plugins": "^0.33.2"
29
+ }
30
+ }
@@ -0,0 +1,130 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity 0.8.26;
3
+
4
+ import "@bananapus/core-v6/script/helpers/CoreDeploymentLib.sol";
5
+
6
+ import {Sphinx} from "@sphinx-labs/contracts/SphinxPlugin.sol";
7
+ import {Script} from "forge-std/Script.sol";
8
+
9
+ import {JBRouterTerminal} from "../src/JBRouterTerminal.sol";
10
+ import {JBRouterTerminalRegistry} from "../src/JBRouterTerminalRegistry.sol";
11
+ import {IWETH9} from "../src/interfaces/IWETH9.sol";
12
+ import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
13
+ import {IUniswapV3Factory} from "@uniswap/v3-core/contracts/interfaces/IUniswapV3Factory.sol";
14
+ import {IPermit2} from "@uniswap/permit2/src/interfaces/IPermit2.sol";
15
+
16
+ contract DeployScript is Script, Sphinx {
17
+ /// @notice tracks the deployment of the core contracts for the chain we are deploying to.
18
+ CoreDeployment core;
19
+
20
+ /// @notice the salts that are used to deploy the contracts.
21
+ bytes32 ROUTER_TERMINAL = "JBRouterTerminalV6";
22
+ bytes32 ROUTER_TERMINAL_REGISTRY = "JBRouterTerminalRegistryV6";
23
+
24
+ /// @notice tracks the addresses that are required for the chain we are deploying to.
25
+ address weth;
26
+ address factory;
27
+ address poolManager;
28
+ address permit2;
29
+ address trustedForwarder;
30
+
31
+ function configureSphinx() public override {
32
+ sphinxConfig.projectName = "nana-router-terminal-v6";
33
+ sphinxConfig.mainnets = ["ethereum", "optimism", "base", "arbitrum"];
34
+ sphinxConfig.testnets = ["ethereum_sepolia", "optimism_sepolia", "base_sepolia", "arbitrum_sepolia"];
35
+ }
36
+
37
+ function run() public {
38
+ // Get the deployment addresses for the nana CORE for this chain.
39
+ // We want to do this outside of the `sphinx` modifier.
40
+ core = CoreDeploymentLib.getDeployment(
41
+ vm.envOr("NANA_CORE_DEPLOYMENT_PATH", string("node_modules/@bananapus/core-v6/deployments/"))
42
+ );
43
+
44
+ trustedForwarder = core.permissions.trustedForwarder();
45
+
46
+ // Permit2 is deployed at the same address on all chains.
47
+ permit2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3;
48
+
49
+ // Ethereum Mainnet
50
+ if (block.chainid == 1) {
51
+ weth = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
52
+ factory = 0x1F98431c8aD98523631AE4a59f267346ea31F984;
53
+ poolManager = 0x000000000004444c5dc75cB358380D2e3dE08A90;
54
+ // Ethereum Sepolia
55
+ } else if (block.chainid == 11_155_111) {
56
+ weth = 0x7b79995e5f793A07Bc00c21412e50Ecae098E7f9;
57
+ factory = 0x0227628f3F023bb0B980b67D528571c95c6DaC1c;
58
+ poolManager = 0xE03A1074c86CFeDd5C142C4F04F1a1536e203543;
59
+ // Optimism Mainnet
60
+ } else if (block.chainid == 10) {
61
+ weth = 0x4200000000000000000000000000000000000006;
62
+ factory = 0x1F98431c8aD98523631AE4a59f267346ea31F984;
63
+ poolManager = 0x000000000004444c5dc75cB358380D2e3dE08A90;
64
+ // Base Mainnet
65
+ } else if (block.chainid == 8453) {
66
+ weth = 0x4200000000000000000000000000000000000006;
67
+ factory = 0x33128a8fC17869897dcE68Ed026d694621f6FDfD;
68
+ poolManager = 0x000000000004444c5dc75cB358380D2e3dE08A90;
69
+ // Optimism Sepolia
70
+ } else if (block.chainid == 11_155_420) {
71
+ weth = 0x4200000000000000000000000000000000000006;
72
+ factory = 0x4752ba5DBc23f44D87826276BF6Fd6b1C372aD24;
73
+ poolManager = 0xE03A1074c86CFeDd5C142C4F04F1a1536e203543;
74
+ // BASE Sepolia
75
+ } else if (block.chainid == 84_532) {
76
+ weth = 0x4200000000000000000000000000000000000006;
77
+ factory = 0x4752ba5DBc23f44D87826276BF6Fd6b1C372aD24;
78
+ poolManager = 0xE03A1074c86CFeDd5C142C4F04F1a1536e203543;
79
+ // Arbitrum Mainnet
80
+ } else if (block.chainid == 42_161) {
81
+ weth = 0x82aF49447D8a07e3bd95BD0d56f35241523fBab1;
82
+ factory = 0x1F98431c8aD98523631AE4a59f267346ea31F984;
83
+ poolManager = 0x000000000004444c5dc75cB358380D2e3dE08A90;
84
+ // Arbitrum Sepolia
85
+ } else if (block.chainid == 421_614) {
86
+ weth = 0x980B62Da83eFf3D4576C647993b0c1D7faf17c73;
87
+ factory = 0x248AB79Bbb9bC29bB72f7Cd42F17e054Fc40188e;
88
+ poolManager = 0xE03A1074c86CFeDd5C142C4F04F1a1536e203543;
89
+ } else {
90
+ revert("Invalid RPC / no juice contracts deployed on this network");
91
+ }
92
+
93
+ // Perform the deployment transactions.
94
+ deploy();
95
+ }
96
+
97
+ function deploy() public sphinx {
98
+ JBRouterTerminalRegistry registry = new JBRouterTerminalRegistry{salt: ROUTER_TERMINAL_REGISTRY}(
99
+ core.permissions, core.projects, IPermit2(permit2), safeAddress(), trustedForwarder
100
+ );
101
+
102
+ JBRouterTerminal terminal = new JBRouterTerminal{salt: ROUTER_TERMINAL}(
103
+ core.directory,
104
+ core.permissions,
105
+ core.projects,
106
+ core.tokens,
107
+ IPermit2(permit2),
108
+ safeAddress(),
109
+ IWETH9(weth),
110
+ IUniswapV3Factory(factory),
111
+ IPoolManager(poolManager),
112
+ trustedForwarder
113
+ );
114
+
115
+ // Set the terminal as the default for the registry.
116
+ registry.setDefaultTerminal(terminal);
117
+ }
118
+
119
+ function _isDeployed(bytes32 salt, bytes memory creationCode, bytes memory arguments) internal view returns (bool) {
120
+ address _deployedTo = vm.computeCreate2Address({
121
+ salt: salt,
122
+ initCodeHash: keccak256(abi.encodePacked(creationCode, arguments)),
123
+ // Arachnid/deterministic-deployment-proxy address.
124
+ deployer: address(0x4e59b44847b379578588920cA78FbF26c0B4956C)
125
+ });
126
+
127
+ // Return if code is already present at this address.
128
+ return address(_deployedTo).code.length != 0;
129
+ }
130
+ }
@@ -0,0 +1,76 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity 0.8.26;
3
+
4
+ import {stdJson} from "forge-std/Script.sol";
5
+ import {Vm} from "forge-std/Vm.sol";
6
+
7
+ import {SphinxConstants, NetworkInfo} from "@sphinx-labs/contracts/SphinxConstants.sol";
8
+
9
+ import {IJBRouterTerminal} from "../../src/interfaces/IJBRouterTerminal.sol";
10
+ import {IJBRouterTerminalRegistry} from "../../src/interfaces/IJBRouterTerminalRegistry.sol";
11
+
12
+ struct RouterTerminalDeployment {
13
+ IJBRouterTerminal terminal;
14
+ IJBRouterTerminalRegistry registry;
15
+ }
16
+
17
+ library RouterTerminalDeploymentLib {
18
+ // Cheat code address, 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D.
19
+ address internal constant VM_ADDRESS = address(uint160(uint256(keccak256("hevm cheat code"))));
20
+ Vm internal constant vm = Vm(VM_ADDRESS);
21
+
22
+ function getDeployment(string memory path) internal returns (RouterTerminalDeployment memory deployment) {
23
+ // get chainId for which we need to get the deployment.
24
+ uint256 chainId = block.chainid;
25
+
26
+ // Deploy to get the constants.
27
+ // TODO: get constants without deploy.
28
+ SphinxConstants sphinxConstants = new SphinxConstants();
29
+ NetworkInfo[] memory networks = sphinxConstants.getNetworkInfoArray();
30
+
31
+ for (uint256 _i; _i < networks.length; _i++) {
32
+ if (networks[_i].chainId == chainId) {
33
+ return getDeployment(path, networks[_i].name);
34
+ }
35
+ }
36
+
37
+ revert("ChainID is not (currently) supported by Sphinx.");
38
+ }
39
+
40
+ function getDeployment(
41
+ string memory path,
42
+ string memory network_name
43
+ )
44
+ internal
45
+ view
46
+ returns (RouterTerminalDeployment memory deployment)
47
+ {
48
+ deployment.terminal = IJBRouterTerminal(
49
+ _getDeploymentAddress(path, "nana-router-terminal-v6", network_name, "JBRouterTerminal")
50
+ );
51
+
52
+ deployment.registry = IJBRouterTerminalRegistry(
53
+ _getDeploymentAddress(path, "nana-router-terminal-v6", network_name, "JBRouterTerminalRegistry")
54
+ );
55
+ }
56
+
57
+ /// @notice Get the address of a contract that was deployed by the Deploy script.
58
+ /// @dev Reverts if the contract was not found.
59
+ /// @param path The path to the deployment file.
60
+ /// @param contractName The name of the contract to get the address of.
61
+ /// @return The address of the contract.
62
+ function _getDeploymentAddress(
63
+ string memory path,
64
+ string memory project_name,
65
+ string memory network_name,
66
+ string memory contractName
67
+ )
68
+ internal
69
+ view
70
+ returns (address)
71
+ {
72
+ string memory deploymentJson =
73
+ vm.readFile(string.concat(path, project_name, "/", network_name, "/", contractName, ".json"));
74
+ return stdJson.readAddress(deploymentJson, ".address");
75
+ }
76
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "detectors_to_exclude": "timestamp,uninitialized-local,naming-convention,solc-version,shadowing-local",
3
+ "exclude_informational": true,
4
+ "exclude_low": false,
5
+ "exclude_medium": false,
6
+ "exclude_high": false,
7
+ "disable_color": false,
8
+ "filter_paths": "(mocks/|test/|node_modules/|lib/)",
9
+ "legacy_ast": false
10
+ }