@bananapus/router-terminal-v6 0.0.13 → 0.0.15
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/ARCHITECTURE.md +17 -1
- package/CHANGE_LOG.md +13 -6
- package/README.md +21 -4
- package/RISKS.md +7 -0
- package/SKILLS.md +7 -3
- package/USER_JOURNEYS.md +80 -0
- package/foundry.toml +1 -0
- package/package.json +2 -2
- package/src/JBRouterTerminal.sol +353 -132
- package/src/JBRouterTerminalRegistry.sol +48 -5
- package/src/interfaces/IJBRouterTerminal.sol +2 -1
- package/test/RouterTerminal.t.sol +751 -3
- package/test/RouterTerminalCashOutFork.t.sol +556 -0
- package/test/RouterTerminalERC2771.t.sol +17 -0
- package/test/RouterTerminalMultihopFork.t.sol +355 -0
- package/test/RouterTerminalPreviewFork.t.sol +336 -0
- package/test/RouterTerminalRegistry.t.sol +62 -0
- package/test/TestAuditGaps.sol +6 -0
package/ARCHITECTURE.md
CHANGED
|
@@ -12,6 +12,7 @@ src/
|
|
|
12
12
|
├── JBRouterTerminalRegistry.sol — Registry mapping projects to router terminal configs
|
|
13
13
|
├── interfaces/
|
|
14
14
|
│ ├── IJBRouterTerminal.sol
|
|
15
|
+
│ ├── IJBRouterTerminalRegistry.sol
|
|
15
16
|
│ └── IWETH9.sol
|
|
16
17
|
├── libraries/
|
|
17
18
|
│ └── JBSwapLib.sol — Uniswap V3/V4 swap helpers, pool discovery
|
|
@@ -29,10 +30,18 @@ Payer → JBRouterTerminal.pay(projectId, token, amount)
|
|
|
29
30
|
→ If different token:
|
|
30
31
|
→ Compare V3 and V4 pool quotes
|
|
31
32
|
→ Swap via better pool
|
|
32
|
-
|
|
33
|
+
→ Forward swapped tokens to project's terminal
|
|
33
34
|
→ Return token count from destination payment
|
|
34
35
|
```
|
|
35
36
|
|
|
37
|
+
### Preview Routing
|
|
38
|
+
```
|
|
39
|
+
Caller → JBRouterTerminal.previewPayFor(projectId, token, amount)
|
|
40
|
+
→ Mirror source-of-funds and routing logic in view context
|
|
41
|
+
→ If direct, wrap-unwrap, or exact cashout route: forward preview to destination terminal
|
|
42
|
+
→ If swap route: estimate output using the same quote-selection logic used for execution bounds
|
|
43
|
+
```
|
|
44
|
+
|
|
36
45
|
## Extension Points
|
|
37
46
|
|
|
38
47
|
| Point | Interface | Purpose |
|
|
@@ -41,6 +50,13 @@ Payer → JBRouterTerminal.pay(projectId, token, amount)
|
|
|
41
50
|
| Registry | `IJBRouterTerminalRegistry` | Maps projects to routing configs |
|
|
42
51
|
| Permit | `IJBPermitTerminal` | Permit2 token approval support |
|
|
43
52
|
|
|
53
|
+
## Composition Boundary
|
|
54
|
+
|
|
55
|
+
The router terminal exposes the `IJBTerminal` surface because it needs to participate in Juicebox routing, but its
|
|
56
|
+
accounting context is intentionally synthetic. `accountingContextForTokenOf()` returns `decimals = 18` for any token,
|
|
57
|
+
and the registry forwards that value unchanged. Treat the router layer as a payment router only, not as an
|
|
58
|
+
accounting-sensitive terminal source for loan sizing, debt normalization, or any other decimals-dependent logic.
|
|
59
|
+
|
|
44
60
|
## Dependencies
|
|
45
61
|
- `@bananapus/core-v6` — Terminal, directory, permissions
|
|
46
62
|
- `@uniswap/v3-core` + `v3-periphery` — V3 swap routing
|
package/CHANGE_LOG.md
CHANGED
|
@@ -21,7 +21,7 @@ The main contract was renamed from `JBSwapTerminal` (and `JBSwapTerminal5_1`) to
|
|
|
21
21
|
|
|
22
22
|
### 1.4 `IJBSwapTerminal` Interface Removed, Replaced by `IJBRouterTerminal`
|
|
23
23
|
- **v5 `IJBSwapTerminal`** exposed: `DEFAULT_PROJECT_ID()`, `MAX_TWAP_WINDOW()`, `MIN_TWAP_WINDOW()`, `MIN_DEFAULT_POOL_CARDINALITY()`, `UNCERTAIN_SLIPPAGE_TOLERANCE()`, `SLIPPAGE_DENOMINATOR()`, `twapWindowOf()`, `addDefaultPool()`, `addTwapParamsFor()`.
|
|
24
|
-
- **v6 `IJBRouterTerminal`** exposes
|
|
24
|
+
- **v6 `IJBRouterTerminal`** exposes: `discoverBestPool()`, `discoverPool()`, and `previewPayFor()`.
|
|
25
25
|
- All pool/TWAP configuration functions are removed (see section 1.5).
|
|
26
26
|
|
|
27
27
|
### 1.5 Removed: Per-Project Pool Configuration and TWAP Management
|
|
@@ -44,7 +44,7 @@ The `SLIPPAGE_DENOMINATOR` constant was kept but changed from `uint160` to `uint
|
|
|
44
44
|
|
|
45
45
|
### 1.7 `supportsInterface` Changed
|
|
46
46
|
- **v5:** Reported support for `IJBTerminal`, `IJBPermitTerminal`, `IERC165`, `IUniswapV3SwapCallback`, `IJBPermissioned`, `IJBSwapTerminal`.
|
|
47
|
-
- **v6:** Reports support for `IJBTerminal`, `IJBPermitTerminal`, `IERC165`, `IJBPermissioned
|
|
47
|
+
- **v6:** Reports support for `IJBTerminal`, `IJBPermitTerminal`, `IJBRouterTerminal`, `IERC165`, and `IJBPermissioned`. It no longer advertises `IUniswapV3SwapCallback`.
|
|
48
48
|
|
|
49
49
|
### 1.8 Permission ID Changed (Registry)
|
|
50
50
|
- **v5:** `lockTerminalFor()` and `setTerminalFor()` used `JBPermissionIds.ADD_SWAP_TERMINAL_POOL`.
|
|
@@ -121,18 +121,25 @@ New external view functions on `IJBRouterTerminal` for off-chain queries:
|
|
|
121
121
|
- `discoverBestPool(tokenIn, tokenOut)` — returns a `PoolInfo` (V3 or V4).
|
|
122
122
|
- `discoverPool(tokenIn, tokenOut)` — returns only the V3 pool (backwards-compatible helper).
|
|
123
123
|
|
|
124
|
-
### 2.10 `
|
|
124
|
+
### 2.10 `previewPayFor()` Added
|
|
125
|
+
New external view functions on the router surfaces:
|
|
126
|
+
- `JBRouterTerminal.previewPayFor(projectId, token, amount, beneficiary, metadata)` mirrors the router's payment routing logic and forwards the preview to the terminal that would ultimately receive the payment.
|
|
127
|
+
- `JBRouterTerminalRegistry.previewPayFor(projectId, token, amount, beneficiary, metadata)` resolves the router terminal for a project and forwards the preview.
|
|
128
|
+
- Direct routes and wrap-unwrap routes are previewable exactly.
|
|
129
|
+
- Swap routes return best-effort estimates using the same pool discovery and quote-selection logic used for execution bounds.
|
|
130
|
+
|
|
131
|
+
### 2.11 `Permit2AllowanceFailed` Event
|
|
125
132
|
In v6, when a Permit2 allowance call fails during `_acceptFundsFor()`, an event `Permit2AllowanceFailed(token, owner, reason)` is emitted (inherited from `IJBPermitTerminal`), and the payment continues using fallback transfer. In v5, the failure was silently swallowed with an empty `catch`.
|
|
126
133
|
|
|
127
|
-
### 2.
|
|
134
|
+
### 2.12 Fee-on-Transfer Token Handling
|
|
128
135
|
- **v5 `_acceptFundsFor`:** Returned `IERC20(token).balanceOf(address(this))` after transfer — would include any pre-existing balance.
|
|
129
136
|
- **v6 `_acceptFundsFor`:** Uses balance-delta pattern (`balanceAfter - balanceBefore`) to accurately measure tokens received.
|
|
130
137
|
|
|
131
|
-
### 2.
|
|
138
|
+
### 2.13 Partial-Fill Leftover Handling via Balance Delta
|
|
132
139
|
- **v5 `_handleTokenTransfersAndSwap`:** Measured leftovers as the full `balanceOf(normalizedTokenIn)` — could include pre-existing balances.
|
|
133
140
|
- **v6 `_handleSwap`:** Snapshots `balanceBefore` and uses `balanceAfter - balanceBefore` for accurate leftover calculation.
|
|
134
141
|
|
|
135
|
-
### 2.
|
|
142
|
+
### 2.14 `PERMIT2` Exposed on `IJBRouterTerminalRegistry` Interface
|
|
136
143
|
- **v5:** `PERMIT2` was an immutable on the registry contract but not exposed on the `IJBSwapTerminalRegistry` interface.
|
|
137
144
|
- **v6:** `PERMIT2()` is declared on the `IJBRouterTerminalRegistry` interface.
|
|
138
145
|
|
package/README.md
CHANGED
|
@@ -2,25 +2,40 @@
|
|
|
2
2
|
|
|
3
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
4
|
|
|
5
|
+
This contract family is a routing surface, not an accounting-truth surface. `JBRouterTerminal` synthesizes
|
|
6
|
+
best-effort `JBAccountingContext` values for routing discovery: native tokens use `18` decimals, ERC-20s probe
|
|
7
|
+
`IERC20Metadata.decimals()` when available, and broken or non-standard tokens fall back to `18`. The registry simply
|
|
8
|
+
forwards that context. Do not reuse the router terminal or router terminal registry as an accounting-sensitive terminal
|
|
9
|
+
source for lending or other logic that relies on real token decimals.
|
|
10
|
+
|
|
5
11
|
_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
12
|
|
|
7
13
|
## Architecture
|
|
8
14
|
|
|
9
15
|
| Contract | Description |
|
|
10
16
|
|----------|-------------|
|
|
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, cashing out JB project tokens if the input is a project token, or forwarding directly if the token is already accepted. Uses TWAP oracle (V3) or spot price (V4) for automatic slippage protection when the caller does not provide a quote. Implements `IJBTerminal`, `IJBPermitTerminal`, `IUniswapV3SwapCallback`, and `
|
|
12
|
-
| `JBRouterTerminalRegistry` | A proxy terminal that delegates `pay` and `addToBalanceOf` to a per-project or default `JBRouterTerminal` instance. Project owners can choose which router terminal they use, and optionally lock that choice permanently. Implements `IJBTerminal` via `IJBRouterTerminalRegistry`. |
|
|
17
|
+
| `JBRouterTerminal` | Core terminal. Accepts any token via `pay` or `addToBalanceOf`, previews payment routes via `previewPayFor`, discovers the destination project's accepted token, and routes there -- swapping through Uniswap V3 or V4 pools if needed, cashing out JB project tokens if the input is a project token, or forwarding directly if the token is already accepted. Uses TWAP oracle (V3) or spot price (V4) for automatic slippage protection when the caller does not provide a quote. Implements `IJBTerminal`, `IJBPermitTerminal`, `IUniswapV3SwapCallback`, `IUnlockCallback`, and `IJBRouterTerminal`. |
|
|
18
|
+
| `JBRouterTerminalRegistry` | A proxy terminal that delegates `pay`, `previewPayFor`, and `addToBalanceOf` to a per-project or default `JBRouterTerminal` instance. Project owners can choose which router terminal they use, and optionally lock that choice permanently. Implements `IJBTerminal` and the extra registry management surface via `IJBRouterTerminalRegistry`. |
|
|
13
19
|
|
|
14
20
|
## How It Works
|
|
15
21
|
|
|
16
22
|
1. A payer calls `pay(projectId, token, amount, ...)` with any token.
|
|
17
23
|
2. The terminal accepts the token (supports ERC-20 approvals, Permit2, and credit transfers).
|
|
18
24
|
3. If the input is a JB project token (ERC-20 or credits), it recursively cashes out through the source project's terminals until reaching a base token.
|
|
19
|
-
4. It resolves which token the destination project accepts, checking: metadata override (`routeTokenOut`), direct acceptance, NATIVE/WETH equivalence, then dynamic pool discovery across all terminals.
|
|
25
|
+
4. It resolves which token the destination project accepts, checking: metadata override (`routeTokenOut`), direct acceptance only if the chosen terminal exposes non-empty accounting contexts for the project, NATIVE/WETH equivalence, then dynamic pool discovery across all terminals.
|
|
20
26
|
5. If the resolved token differs from the input, it converts -- wrapping/unwrapping ETH/WETH, or swapping through the best Uniswap V3 or V4 pool.
|
|
21
27
|
6. Slippage protection: the caller can pass a minimum output quote in metadata (`quoteForSwap` key), or the terminal calculates one using TWAP (V3) or spot price (V4) with a dynamic sigmoid slippage tolerance based on estimated price impact.
|
|
22
28
|
7. The output tokens are forwarded to the project's primary terminal via `terminal.pay(...)` or `terminal.addToBalanceOf(...)`.
|
|
23
29
|
|
|
30
|
+
### Previewing Payments
|
|
31
|
+
|
|
32
|
+
`previewPayFor(...)` mirrors the router's payment routing logic and forwards the preview to the terminal that would ultimately receive the payment. When a swap is required, it returns the router's best estimate using the same pool-discovery and quote-selection logic used to derive execution bounds.
|
|
33
|
+
|
|
34
|
+
- Direct routes are exact.
|
|
35
|
+
- Native/WETH wrap-unwrap routes are exact.
|
|
36
|
+
- Cashout-only routes are exact when the downstream cashout terminal exposes an exact preview surface.
|
|
37
|
+
- Swap routes return best-effort estimates based on current pool state and any caller-provided `quoteForSwap` metadata.
|
|
38
|
+
|
|
24
39
|
```mermaid
|
|
25
40
|
sequenceDiagram
|
|
26
41
|
participant Frontend client
|
|
@@ -104,6 +119,7 @@ Key `foundry.toml` settings:
|
|
|
104
119
|
- `solc = '0.8.26'`
|
|
105
120
|
- `evm_version = 'cancun'` (required for Uniswap V4's transient storage)
|
|
106
121
|
- `optimizer_runs = 200`
|
|
122
|
+
- `via_ir = true` (required for `JBRouterTerminal` to fit under EIP-170)
|
|
107
123
|
- `fuzz.runs = 4096`
|
|
108
124
|
- `invariant.runs = 1024`, `invariant.depth = 100`
|
|
109
125
|
|
|
@@ -129,7 +145,8 @@ nana-router-terminal-v6/
|
|
|
129
145
|
└── test/
|
|
130
146
|
├── RouterTerminal.t.sol # Unit tests (mocked dependencies)
|
|
131
147
|
├── RouterTerminalRegistry.t.sol # Registry unit tests
|
|
132
|
-
|
|
148
|
+
├── RouterTerminalFork.t.sol # Fork tests against mainnet Uniswap pools
|
|
149
|
+
└── RouterTerminalPreviewFork.t.sol # Fork parity tests for previewPayFor
|
|
133
150
|
```
|
|
134
151
|
|
|
135
152
|
## Payment Metadata
|
package/RISKS.md
CHANGED
|
@@ -23,6 +23,13 @@
|
|
|
23
23
|
- **Fee-on-transfer tokens unsupported.** `_acceptFundsFor` in the terminal uses balance-delta, but the registry does NOT. Fee-on-transfer tokens through the registry will mismatch.
|
|
24
24
|
- **Credit cashout path.** `_acceptFundsFor` processes `cashOutSource` metadata to transfer credits from `_msgSender()`. Requires `TRANSFER_CREDITS` permission. If a user has this permission set broadly, any caller through the trusted forwarder could drain their credits.
|
|
25
25
|
- **Registry owner.** Controls which terminals are allowlisted and sets the global default. Disallowing a terminal clears the default if it matches but does NOT clear per-project terminal settings already set to the disallowed terminal.
|
|
26
|
+
- **Synthetic accounting contexts.** `JBRouterTerminal.accountingContextForTokenOf()` uses best-effort decimals for
|
|
27
|
+
routing discovery: native tokens use `18`, ERC-20s probe `IERC20Metadata.decimals()` when available, and broken or
|
|
28
|
+
non-standard tokens fall back to `18`. `JBRouterTerminalRegistry` simply forwards that context. This is safe for
|
|
29
|
+
routing discovery but unsafe for integrations that treat the router or registry as a truthful accounting source for
|
|
30
|
+
non-18-decimal assets. Lending and debt-normalization flows must point at a real terminal, not the router layer.
|
|
31
|
+
The router now refuses to treat a primary terminal as direct acceptance unless that terminal also exposes non-empty
|
|
32
|
+
accounting contexts for the project, so router-stack terminals do not win the direct-forward fast path.
|
|
26
33
|
|
|
27
34
|
## 4. DoS Vectors
|
|
28
35
|
|
package/SKILLS.md
CHANGED
|
@@ -8,8 +8,8 @@ Accept payments in any ERC-20 token (or native ETH), dynamically discover what t
|
|
|
8
8
|
|
|
9
9
|
| Contract | Role |
|
|
10
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, cashes out JB project tokens, forwards to the primary terminal. Implements `IJBTerminal`, `IJBPermitTerminal`, `IUniswapV3SwapCallback`, `IUnlockCallback`, `IJBRouterTerminal`. |
|
|
12
|
-
| `JBRouterTerminalRegistry` | Proxy terminal routing `pay
|
|
11
|
+
| `JBRouterTerminal` | Core terminal: accepts any token, previews payment routes with `previewPayFor`, discovers the best route to the destination project's accepted token, swaps via Uniswap V3 or V4, cashes out JB project tokens, and forwards to the primary terminal. Implements `IJBTerminal`, `IJBPermitTerminal`, `IUniswapV3SwapCallback`, `IUnlockCallback`, `IJBRouterTerminal`. |
|
|
12
|
+
| `JBRouterTerminalRegistry` | Proxy terminal routing `pay`, `previewPayFor`, and `addToBalanceOf` to a per-project or default `JBRouterTerminal`. Project owners can set and lock their terminal choice. Implements `IJBTerminal` via `IJBRouterTerminalRegistry`. |
|
|
13
13
|
|
|
14
14
|
## Key Functions
|
|
15
15
|
|
|
@@ -18,6 +18,7 @@ Accept payments in any ERC-20 token (or native ETH), dynamically discover what t
|
|
|
18
18
|
| Function | What it does |
|
|
19
19
|
|----------|--------------|
|
|
20
20
|
| `pay(projectId, token, amount, beneficiary, minReturnedTokens, memo, metadata)` | Accept any token, route to the destination project's accepted token (cashout, swap, wrap/unwrap, or direct), forward to the project's primary terminal. Returns project token count. |
|
|
21
|
+
| `previewPayFor(projectId, token, amount, beneficiary, metadata)` | Preview the terminal and mint result that the current payment route would produce. Swap routes return best-effort estimates using the router's quote-selection logic. |
|
|
21
22
|
| `addToBalanceOf(projectId, token, amount, shouldReturnHeldFees, memo, metadata)` | Same routing flow as `pay` but calls `terminal.addToBalanceOf(...)` instead of `terminal.pay(...)`. |
|
|
22
23
|
| `discoverPool(normalizedTokenIn, normalizedTokenOut) -> IUniswapV3Pool` | Search V3 factory for highest-liquidity pool across 4 fee tiers. Returns the V3 pool (or zero address if V4 was better). |
|
|
23
24
|
| `discoverBestPool(normalizedTokenIn, normalizedTokenOut) -> PoolInfo` | Discover best pool across both V3 and V4, comparing in-range liquidity. Returns `PoolInfo` indicating protocol version and pool details. |
|
|
@@ -26,13 +27,14 @@ Accept payments in any ERC-20 token (or native ETH), dynamically discover what t
|
|
|
26
27
|
| `currentSurplusOf(...) -> uint256` | Always returns 0. This terminal holds no surplus. |
|
|
27
28
|
| `uniswapV3SwapCallback(amount0Delta, amount1Delta, data)` | V3 swap callback. Verifies caller is a legitimate pool via the factory. Wraps ETH if needed and transfers input tokens to the pool. |
|
|
28
29
|
| `unlockCallback(data) -> bytes` | V4 swap callback. Called by PoolManager during `unlock()`. Executes the swap, settles input, takes output, checks slippage. |
|
|
29
|
-
| `supportsInterface(interfaceId) -> bool` | Returns true for `IJBTerminal`, `IJBPermitTerminal`, `IERC165`, `IJBPermissioned`. |
|
|
30
|
+
| `supportsInterface(interfaceId) -> bool` | Returns true for `IJBTerminal`, `IJBPermitTerminal`, `IJBRouterTerminal`, `IERC165`, `IJBPermissioned`. |
|
|
30
31
|
|
|
31
32
|
### JBRouterTerminalRegistry
|
|
32
33
|
|
|
33
34
|
| Function | What it does |
|
|
34
35
|
|----------|--------------|
|
|
35
36
|
| `pay(projectId, token, amount, beneficiary, minReturnedTokens, memo, metadata)` | Resolves the terminal for the project (per-project or default), accepts funds, forwards payment. |
|
|
37
|
+
| `previewPayFor(projectId, token, amount, beneficiary, metadata)` | Resolves the terminal for the project (per-project or default) and forwards the payment preview. |
|
|
36
38
|
| `addToBalanceOf(projectId, token, amount, shouldReturnHeldFees, memo, metadata)` | Same resolution and forwarding but for balance additions. |
|
|
37
39
|
| `terminalOf(projectId) -> IJBTerminal` | Returns the terminal for the project, or `defaultTerminal` if none is set. |
|
|
38
40
|
| `setTerminalFor(projectId, terminal)` | Route a project to a specific allowed router terminal. Requires `SET_ROUTER_TERMINAL` permission (ID 28). Reverts if locked or terminal not allowed. |
|
|
@@ -50,6 +52,7 @@ Accept payments in any ERC-20 token (or native ETH), dynamically discover what t
|
|
|
50
52
|
| Function | What it does |
|
|
51
53
|
|----------|--------------|
|
|
52
54
|
| `_route(destProjectId, tokenIn, amount, metadata)` | Core routing logic. Detects JB project tokens, runs `_cashOutLoop` if needed, then resolves output token and converts. |
|
|
55
|
+
| `_previewRoute(destProjectId, tokenIn, amount, metadata)` | View mirror of `_route`. Returns the terminal, token, and amount that the current payment route would use, estimating swap outputs from current quote data when needed. |
|
|
53
56
|
| `_resolveTokenOut(projectId, tokenIn, metadata)` | Priority: 1) `routeTokenOut` metadata override, 2) direct acceptance, 3) NATIVE/WETH equivalence, 4) `_discoverAcceptedToken`. |
|
|
54
57
|
| `_discoverAcceptedToken(projectId, tokenIn)` | Iterates all terminals and their accounting contexts for a project. Finds the accepted token with the deepest Uniswap pool. Falls back to the first accepted token if no pool exists. |
|
|
55
58
|
| `_convert(tokenIn, tokenOut, amount, projectId, metadata)` | No-op if same token, wrap/unwrap for NATIVE/WETH, or swap via `_handleSwap`. |
|
|
@@ -175,6 +178,7 @@ Accept payments in any ERC-20 token (or native ETH), dynamically discover what t
|
|
|
175
178
|
- **V3 TWAP**: Reverts with `JBRouterTerminal_NoObservationHistory()` when a V3 pool has no observation history. The TWAP window is capped by the pool's oldest observation if shorter than 10 minutes.
|
|
176
179
|
- **V4 spot price**: V4 vanilla pools have no built-in TWAP oracle. The terminal uses the current spot tick with the same sigmoid slippage formula.
|
|
177
180
|
- **V4 requires cancun EVM**: Chains without EIP-1153 (transient storage) cannot use V4 routing. If `POOL_MANAGER` is `address(0)`, V4 discovery is skipped entirely.
|
|
181
|
+
- **Preview estimates**: `previewPayFor()` returns exact values for direct and wrap-unwrap routes, and best-effort estimates for swap routes using current pool state or caller-provided quotes.
|
|
178
182
|
- The `JBRouterTerminalRegistry` handles token custody during delegation -- it transfers tokens from the payer to itself, then approves and forwards to the underlying terminal.
|
|
179
183
|
- `_msgSender()` (ERC-2771) is used instead of `msg.sender` for meta-transaction compatibility in both contracts.
|
|
180
184
|
- The `JBSwapLib` library contains slippage tolerance math (sigmoid formula), price impact estimation, and V3-compatible `sqrtPriceLimitX96` calculation. It does not contain swap execution logic.
|
package/USER_JOURNEYS.md
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
Every path a user or integrated contract can take through the router terminal.
|
|
4
4
|
|
|
5
|
+
The journeys below describe payment routing behavior. They should not be read as proof that the router terminal or
|
|
6
|
+
registry is a truthful accounting source for arbitrary tokens. Both surfaces report synthetic accounting contexts with
|
|
7
|
+
`decimals = 18`, so any integration that depends on real token decimals must query a real terminal instead.
|
|
8
|
+
|
|
5
9
|
---
|
|
6
10
|
|
|
7
11
|
## Journey 1: Pay a Project Through the Router Terminal
|
|
@@ -162,6 +166,46 @@ Same as Journey 1. Additionally:
|
|
|
162
166
|
|
|
163
167
|
---
|
|
164
168
|
|
|
169
|
+
## Journey 2A: Preview a Payment Through the Router Terminal
|
|
170
|
+
|
|
171
|
+
The caller asks what a payment would do without moving funds.
|
|
172
|
+
|
|
173
|
+
### Entry Point
|
|
174
|
+
|
|
175
|
+
```solidity
|
|
176
|
+
function previewPayFor(
|
|
177
|
+
uint256 projectId,
|
|
178
|
+
address token,
|
|
179
|
+
uint256 amount,
|
|
180
|
+
address beneficiary,
|
|
181
|
+
bytes calldata metadata
|
|
182
|
+
)
|
|
183
|
+
external
|
|
184
|
+
view
|
|
185
|
+
returns (
|
|
186
|
+
JBRuleset memory ruleset,
|
|
187
|
+
uint256 beneficiaryTokenCount,
|
|
188
|
+
uint256 reservedTokenCount,
|
|
189
|
+
JBPayHookSpecification[] memory hookSpecifications
|
|
190
|
+
)
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Behavior
|
|
194
|
+
|
|
195
|
+
1. `_previewAcceptFundsFor()` mirrors the router's source-of-funds logic in view context.
|
|
196
|
+
2. `_previewRoute()` mirrors `_route()` and determines which terminal would ultimately receive the payment.
|
|
197
|
+
3. If the route is direct or wrap-unwrap, the router forwards an exact preview to that destination terminal.
|
|
198
|
+
4. If the route requires a swap, the router estimates the output using current quote data, then forwards that best-effort preview to the destination terminal.
|
|
199
|
+
|
|
200
|
+
### Exactness Boundary
|
|
201
|
+
|
|
202
|
+
- **Direct forwarding:** exact
|
|
203
|
+
- **Native/WETH wrap-unwrap:** exact
|
|
204
|
+
- **Cashout-only routes:** exact when the downstream cashout terminal exposes an exact preview surface
|
|
205
|
+
- **Swap routes:** best-effort estimates using current pool state and any caller-provided `quoteForSwap`
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
165
209
|
## Journey 3: Pay a Project Through the Registry
|
|
166
210
|
|
|
167
211
|
The registry resolves which router terminal instance a project uses, then forwards the payment.
|
|
@@ -196,6 +240,42 @@ function pay(
|
|
|
196
240
|
|
|
197
241
|
---
|
|
198
242
|
|
|
243
|
+
## Journey 3A: Preview a Payment Through the Registry
|
|
244
|
+
|
|
245
|
+
The registry resolves which router terminal instance a project uses, then forwards the preview.
|
|
246
|
+
|
|
247
|
+
### Entry Point
|
|
248
|
+
|
|
249
|
+
```solidity
|
|
250
|
+
// On JBRouterTerminalRegistry:
|
|
251
|
+
function previewPayFor(
|
|
252
|
+
uint256 projectId,
|
|
253
|
+
address token,
|
|
254
|
+
uint256 amount,
|
|
255
|
+
address beneficiary,
|
|
256
|
+
bytes calldata metadata
|
|
257
|
+
)
|
|
258
|
+
external
|
|
259
|
+
view
|
|
260
|
+
returns (
|
|
261
|
+
JBRuleset memory ruleset,
|
|
262
|
+
uint256 beneficiaryTokenCount,
|
|
263
|
+
uint256 reservedTokenCount,
|
|
264
|
+
JBPayHookSpecification[] memory hookSpecifications
|
|
265
|
+
)
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### State Changes
|
|
269
|
+
|
|
270
|
+
None. The registry resolves `_terminalOf[projectId]`, falling back to `defaultTerminal`, then forwards `previewPayFor()` to the resolved router terminal.
|
|
271
|
+
|
|
272
|
+
### Edge Cases
|
|
273
|
+
|
|
274
|
+
- **No terminal set and no default:** The preview will revert when forwarded to `address(0)`.
|
|
275
|
+
- **Estimated downstream route:** The resolved router terminal may return a best-effort swap estimate rather than an execution-exact value.
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
199
279
|
## Journey 4: Add to a Project's Balance Through the Registry
|
|
200
280
|
|
|
201
281
|
### Entry Point
|
package/foundry.toml
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bananapus/router-terminal-v6",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.15",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"artifacts": "source ./.env && npx sphinx artifacts --org-id 'ea165b21-7cdc-4d7b-be59-ecdd4c26bee4' --project-name 'nana-router-terminal-v6'"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@bananapus/core-v6": "^0.0.
|
|
20
|
+
"@bananapus/core-v6": "^0.0.23",
|
|
21
21
|
"@bananapus/permission-ids-v6": "^0.0.10",
|
|
22
22
|
"@openzeppelin/contracts": "^5.6.1",
|
|
23
23
|
"@uniswap/permit2": "github:Uniswap/permit2",
|