@bananapus/router-terminal-v6 0.0.26 → 0.0.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/ADMINISTRATION.md CHANGED
@@ -1,152 +1,80 @@
1
1
  # Administration
2
2
 
3
- Admin privileges and their scope in nana-router-terminal-v6.
4
-
5
3
  ## At A Glance
6
4
 
7
5
  | Item | Details |
8
- |------|---------|
9
- | Scope | Registry-level selection of router terminal implementations plus immutable router-terminal swap and forwarding behavior. |
10
- | Operators | Registry owner for the global allowlist/default, project owners or `SET_ROUTER_TERMINAL` delegates for per-project selection, and users who must supply valid routing metadata. |
11
- | Highest-risk actions | Locking a project to the wrong terminal, changing the default terminal without understanding who inherits it, or assuming the router is an accounting-truth surface when it is not. |
12
- | Recovery posture | Unlocked projects can switch terminals. Locked projects keep the stored terminal choice, so recovery requires moving the project to a different admin path outside the registry. |
13
-
14
- ## Routine Operations
15
-
16
- - Keep the registry allowlist limited to router terminal implementations you actually want projects to choose from.
17
- - Change the default terminal only when you are comfortable affecting every project that still relies on fallback resolution.
18
- - Encourage projects to lock their terminal only after verifying the resolved terminal address and expected routing behavior.
19
- - For credit-cashout routing, verify the payer has granted the router terminal the needed `TRANSFER_CREDITS` permission before relying on that path.
6
+ | --- | --- |
7
+ | Scope | Global router terminal allowlisting and project-local terminal selection |
8
+ | Control posture | Mixed registry-owner and project-local delegated control |
9
+ | Highest-risk actions | Changing the default terminal, locking a project to the wrong terminal, and relying on misconfigured credit-cashout routing |
10
+ | Recovery posture | Unlocked projects can move; locked projects and immutable router wiring limit recovery |
20
11
 
21
- ## One-Way Or High-Risk Actions
12
+ ## Purpose
22
13
 
23
- - `lockTerminalFor` is irreversible.
24
- - The current default terminal cannot be disallowed; the registry owner must move the default first before removing the old implementation from the allowlist.
14
+ `nana-router-terminal-v6` splits administration between a global registry and project-local terminal selection. The router logic itself is mostly immutable. The mutable control plane lives in `JBRouterTerminalRegistry`.
25
15
 
26
- ## Recovery Notes
16
+ ## Control Model
27
17
 
28
- - If the default terminal is wrong, update the registry quickly before more projects snapshot it through `lockTerminalFor`.
29
- - If a project already locked the wrong terminal, the registry cannot unlock it; recovery has to happen by migrating the project's broader terminal setup elsewhere.
18
+ - `JBRouterTerminalRegistry` is globally `Ownable`
19
+ - project owners or delegates choose and can lock their router terminal
20
+ - `JBRouterTerminal` has immutable routing dependencies and no owner-controlled strategy knobs
21
+ - some transaction paths depend on project-local `JBPermissions`, such as `TRANSFER_CREDITS`
30
22
 
31
23
  ## Roles
32
24
 
33
- ### 1. Registry Owner (Ownable)
34
-
35
- **Contract**: `JBRouterTerminalRegistry`
36
- **Assigned via**: Constructor parameter `owner`, transferable via `Ownable.transferOwnership()`.
37
- **Scope**: Global -- controls which router terminals can be used by any project and sets the system-wide default terminal.
38
-
39
- ### 2. Project Owner / SET_ROUTER_TERMINAL Delegate
40
-
41
- **Contract**: `JBRouterTerminalRegistry`
42
- **Assigned via**: Ownership of the project's ERC-721 NFT (via `JBProjects.ownerOf(projectId)`), or delegation via `JBPermissions` with permission ID `SET_ROUTER_TERMINAL` (30).
43
- **Scope**: Per-project -- controls which router terminal a specific project uses, and can permanently lock that choice.
44
-
45
- ### 3. Credit Cashout Payer (Implicit)
46
-
47
- **Contract**: `JBRouterTerminal`
48
- **Required permission**: `TRANSFER_CREDITS` (permission ID 13) -- must be granted by the payer to the router terminal address for the source project via `JBPermissions`.
49
- **Scope**: Per-transaction. Required only when using the `cashOutSource` metadata key to route payments through credit cashouts.
50
-
51
- ## Terminal Resolution
25
+ | Role | How Assigned | Scope | Notes |
26
+ | --- | --- | --- | --- |
27
+ | Registry owner | `Ownable(owner)` | Global | Controls allowlist and default terminal |
28
+ | Project owner | `JBProjects.ownerOf(projectId)` | Per project | May delegate `SET_ROUTER_TERMINAL` |
29
+ | Terminal delegate | `JBPermissions` grant | Per project | Usually `SET_ROUTER_TERMINAL` |
30
+ | Payer | Per transaction | Per payment | May need `TRANSFER_CREDITS` for credit-cashout routing |
52
31
 
53
- When a payment is forwarded through the registry, the terminal is resolved as follows:
32
+ ## Privileged Surfaces
54
33
 
55
- 1. If the project has called `setTerminalFor(projectId, terminal)`, that explicit terminal is used.
56
- 2. If no explicit terminal is set, the registry's `defaultTerminal` is used.
57
- 3. If neither exists, the forwarding reverts.
34
+ | Contract | Function | Who Can Call | Effect |
35
+ | --- | --- | --- | --- |
36
+ | `JBRouterTerminalRegistry` | `allowTerminal(...)`, `disallowTerminal(...)`, `setDefaultTerminal(...)` | Registry owner | Controls global terminal availability and fallback |
37
+ | `JBRouterTerminalRegistry` | `setTerminalFor(...)` | Project owner or `SET_ROUTER_TERMINAL` delegate | Sets a project's explicit router terminal |
38
+ | `JBRouterTerminalRegistry` | `lockTerminalFor(...)` | Project owner or `SET_ROUTER_TERMINAL` delegate | Irreversibly locks the resolved terminal for a project |
58
39
 
59
- **Lock semantics:** When `lockTerminalFor()` is called on a project with no explicit terminal, the current default is snapshot into `_terminalOf[projectId]` before locking. The project becomes independent of future default changes.
40
+ ## Immutable And One-Way
60
41
 
61
- **Disallow interaction:** The registry owner cannot disallow the current default terminal. `disallowTerminal()` reverts with `JBRouterTerminalRegistry_CannotDisallowDefaultTerminal` until `setDefaultTerminal()` has moved the default elsewhere first. Projects relying on the default therefore keep a valid fallback terminal unless the owner explicitly changes that default.
42
+ - `lockTerminalFor(...)` is irreversible
43
+ - constructor dependencies on the router are immutable
44
+ - the current default terminal must move before the old default can be disallowed
62
45
 
63
- ## Privileged Functions
46
+ ## Operational Notes
64
47
 
65
- ### JBRouterTerminalRegistry
48
+ - keep the terminal allowlist small and explicit
49
+ - change the default terminal carefully because unconfigured projects inherit it
50
+ - encourage projects to lock only after validating the resolved terminal and routing behavior
51
+ - review credit-cashout routing permissions before relying on that path
52
+ - distinguish configuration risk from quote-quality risk
66
53
 
67
- | Function | Required Role | Permission ID | Scope | What It Does |
68
- |----------|--------------|---------------|-------|--------------|
69
- | `allowTerminal(terminal)` | Registry Owner | `onlyOwner` | Global | Adds a terminal to the allowlist (`isTerminalAllowed[terminal] = true`). Projects can only use allowlisted terminals. |
70
- | `disallowTerminal(terminal)` | Registry Owner | `onlyOwner` | Global | Removes a terminal from the allowlist. Reverts if `terminal` is the current `defaultTerminal`, so the owner must move the default first. Does NOT affect projects that have already locked their terminal or explicitly set another terminal. |
71
- | `setDefaultTerminal(terminal)` | Registry Owner | `onlyOwner` | Global | Sets the default terminal for all projects that have not set a project-specific terminal. Also auto-allows the terminal. |
72
- | `setTerminalFor(projectId, terminal)` | Project Owner or Delegate | `SET_ROUTER_TERMINAL` (30) | Per-project | Routes a specific project to a specific allowed terminal. Reverts if the terminal is not allowlisted or if the project's terminal is locked. |
73
- | `lockTerminalFor(projectId, expectedTerminal)` | Project Owner or Delegate | `SET_ROUTER_TERMINAL` (30) | Per-project | Permanently locks the terminal choice for a project. If no explicit terminal is set, snapshots the current default into `_terminalOf[projectId]`. Reverts with `TerminalMismatch` if the resolved terminal differs from `expectedTerminal` (race condition protection). **Irreversible.** |
54
+ ## Machine Notes
74
55
 
75
- ### Implicit Permission Requirements (not `onlyOwner`, but enforced by external contracts)
56
+ - do not treat registry ownership as authority to override a locked project choice
57
+ - inspect `src/JBRouterTerminalRegistry.sol` and `src/JBRouterTerminal.sol` separately; they govern different control boundaries
58
+ - if effective terminal resolution and the documented default differ, resolve registry state before further actions
59
+ - if route previews are falling back to weaker discovery or quote paths, do not describe the router as offering uniform oracle-quality guarantees
76
60
 
77
- | Operation | Required By | Permission | What Happens |
78
- |-----------|------------|------------|--------------|
79
- | Credit cashout via `cashOutSource` metadata | Payer | `TRANSFER_CREDITS` (13) granted to router terminal | `TOKENS.transferCreditsFrom()` pulls credits from payer. Reverts if payer has not granted the permission. |
80
- | Cashout execution | Router terminal (as holder) | None (terminal holds the tokens) | `IJBCashOutTerminal.cashOutTokensOf()` is called with the router as the holder. The router already holds the tokens from the credit transfer or prior cashout step. |
61
+ ## Recovery
81
62
 
82
- ## Immutable Configuration
63
+ - unlocked projects can switch to another allowlisted terminal
64
+ - locked projects cannot be unlocked by the registry
65
+ - bad immutable router behavior means replacement infrastructure, not in-place edits
66
+ - quote-path weakness is usually mitigated operationally with better pool choice, external quoting, or replacement routing infrastructure
83
67
 
84
- The following values are set at deploy time and cannot be changed:
85
-
86
- ### JBRouterTerminal
87
-
88
- | Property | Set At | Mutable? | Description |
89
- |----------|--------|----------|-------------|
90
- | `Trusted forwarder` | Constructor | No | ERC-2771 trusted forwarder for meta-transactions |
91
- | `DIRECTORY` | Constructor | No | JB directory for terminal/controller lookups |
92
- | `TOKENS` | Constructor | No | JB token manager for credit transfers and project token lookups |
93
- | `FACTORY` | Constructor | No | Uniswap V3 factory for pool discovery and callback verification |
94
- | `POOL_MANAGER` | Constructor | No | Uniswap V4 PoolManager (can be `address(0)` to disable V4) |
95
- | `PERMIT2` | Constructor | No | Permit2 contract for gasless approvals |
96
- | `WETH` | Constructor | No | Wrapped ETH contract |
97
- | `BUYBACK_HOOK` | Constructor | No | Canonical buyback hook whose metadata this router understands |
98
- | `UNIV4_HOOK` | Constructor | No | Canonical Uniswap V4 hook address searched during hooked-pool discovery |
99
- | `DEFAULT_TWAP_WINDOW` | Compile-time constant | No | 10 minutes (600 seconds) |
100
- | `SLIPPAGE_DENOMINATOR` | Compile-time constant | No | 10,000 (basis points) |
101
- | `_FEE_TIERS` | Storage (initialized) | No | `[3000, 500, 10000, 100]` -- V3 fee tiers |
102
- | `_V4_FEES` / `_V4_TICK_SPACINGS` | Storage (initialized) | No | V4 pool parameters |
103
- | `_MAX_CASHOUT_ITERATIONS` | Compile-time constant | No | 20 iterations |
104
-
105
- ### JBRouterTerminalRegistry
106
-
107
- | Property | Set At | Mutable? | Description |
108
- |----------|--------|----------|-------------|
109
- | `PERMISSIONS` | Constructor | No | JB permissions registry for permission checks |
110
- | `Trusted forwarder` | Constructor | No | ERC-2771 trusted forwarder for meta-transactions |
111
- | `PROJECTS` | Constructor | No | JB project NFT registry |
112
- | `PERMIT2` | Constructor | No | Permit2 contract for gasless approvals |
113
-
114
- ### JBPayRouteResolver
115
-
116
- | Property | Set At | Mutable? | Description |
117
- |----------|--------|----------|-------------|
118
- | `DIRECTORY` | Constructor | No | JB directory for terminal and accounting-context lookups, cached from the router at construction time |
119
- | `WETH` | Constructor | No | Wrapped native token used for token normalization, cached from the router at construction time |
120
-
121
- ### JBSwapLib
68
+ ## Admin Boundaries
122
69
 
123
- | Constant | Value | Description |
124
- |----------|-------|-------------|
125
- | `MAX_SLIPPAGE` | 8,800 (88%) | Maximum slippage tolerance ceiling |
126
- | `IMPACT_PRECISION` | 1e18 | Precision for impact calculations |
127
- | `SIGMOID_K` | 5e16 | Sigmoid curve shape parameter |
70
+ - the registry owner cannot unlock or override a locked project terminal
71
+ - project operators cannot set a terminal that the registry does not allow
72
+ - router maintainers cannot tune routing heuristics or constructor immutables post-deploy
73
+ - there is no pause surface in the registry or router
128
74
 
129
- ## Admin Boundaries
75
+ ## Source Map
130
76
 
131
- What admins **cannot** do:
132
-
133
- ### Registry Owner Cannot:
134
- - **Unlock a locked terminal.** `lockTerminalFor` is irreversible -- there is no `unlockTerminalFor` function.
135
- - **Override a project's locked terminal choice.** Once locked, the terminal is permanently stored in `_terminalOf[projectId]` and `hasLockedTerminal[projectId]` is permanently `true`.
136
- - **Force a project to use a specific terminal.** Only the project owner (or delegate) can call `setTerminalFor`.
137
- - **Access project funds.** The registry is a pass-through; it holds funds transiently during forwarding only.
138
- - **Modify swap parameters, slippage, or routing logic.** These are controlled by the `JBRouterTerminal` contract, not the registry.
139
- - **Pause payments.** There is no pause mechanism.
140
-
141
- ### Router Terminal Maintainers Cannot:
142
- - **Modify swap slippage parameters.** The TWAP window, sigmoid constants, fee tiers, and max slippage are all immutable.
143
- - **Redirect funds.** The terminal is stateless between transactions and routes payments to whichever terminal the JB directory specifies.
144
- - **Change the Uniswap factory or PoolManager.** These are immutable constructor parameters.
145
- - **Override user-provided quotes.** The `quoteForSwap` metadata is decoded and used as-is.
146
- - **Prevent specific users from paying.** There is no blocklist mechanism.
147
- - **Extract stuck funds.** There is no sweep or rescue function. The terminal relies on completing all token movements within a single transaction.
148
-
149
- ### Project Owner / Delegate Cannot:
150
- - **Change the terminal after locking.** The `setTerminalFor` function reverts with `TerminalLocked` if the terminal is locked.
151
- - **Set a disallowed terminal.** `setTerminalFor` reverts with `TerminalNotAllowed` if the terminal is not on the registry owner's allowlist.
152
- - **Affect other projects' routing.** Permission checks are scoped to the specific `projectId`.
77
+ - `src/JBRouterTerminalRegistry.sol`
78
+ - `src/JBRouterTerminal.sol`
79
+ - `src/JBPayRouteResolver.sol`
80
+ - `test/`
package/ARCHITECTURE.md CHANGED
@@ -2,67 +2,93 @@
2
2
 
3
3
  ## Purpose
4
4
 
5
- `nana-router-terminal-v6` lets a payer fund a Juicebox project with a token the project does not directly accept. It discovers the destination token, unwraps or wraps native assets when needed, optionally cashes out upstream JB project tokens, and swaps through the deepest available Uniswap V3 or V4 route before forwarding the final asset to the destination terminal.
6
- The router is intentionally heuristic: it does not exhaustively search every viable pool for the absolute best execution price.
5
+ `nana-router-terminal-v6` lets a payer fund a Juicebox project with a token the project does not directly accept. It discovers the destination token, wraps or unwraps native assets when needed, can recursively cash out upstream JB project tokens, and swaps through bounded Uniswap V3 or V4 routes before forwarding value to the destination terminal.
7
6
 
8
- ## Boundaries
7
+ The router is intentionally heuristic. It does not search every possible route for a globally optimal price.
9
8
 
10
- - The router is a terminal-shaped payment adapter, not a canonical accounting terminal.
11
- - It owns routing, swapping, quoting, and refund behavior.
12
- - Final accounting still occurs at the destination terminal that actually accepts the routed token.
13
- - Pool selection is optimized for simple, bounded route discovery, not full best-execution search across all candidate pools.
9
+ ## System Overview
14
10
 
15
- ## Main Components
11
+ `JBRouterTerminal` is a terminal-shaped adapter, not an accounting source of truth. `JBRouterTerminalRegistry` is both a registry and a stable project-facing proxy surface: projects can point at the registry while the registry resolves, and can later lock, the actual router terminal implementation to use. `JBPayRouteResolver` expands preview candidates without forcing the main router contract to carry all preview complexity inline.
16
12
 
17
- | Component | Responsibility |
18
- | --- | --- |
19
- | `JBRouterTerminal` | Source-token intake, route discovery, swapping, and forwarding |
20
- | `JBRouterTerminalRegistry` | Project-level selection and locking of router terminal instances |
21
- | `JBPayRouteResolver` | Helper contract that evaluates pay-route preview candidates without bloating router runtime size |
22
- | `JBSwapLib` | Pool discovery, quoting, and slippage helpers |
23
- | `PoolInfo`, `CashOutPathCandidates`, and interfaces | Typed routing metadata and registry/payer integration surfaces |
13
+ Final accounting still happens in the downstream terminal selected through `nana-core-v6`.
24
14
 
25
- ## Runtime Model
15
+ ## Core Invariants
16
+
17
+ - the router's own accounting context is synthetic and must not be treated as the project ledger
18
+ - preview route discovery and live execution must stay aligned
19
+ - refund behavior is part of correctness, not just UX
20
+ - registry locking prevents silent migration to untrusted router implementations
21
+ - final terminal-facing ERC-20 hops only support standard, non-lossy transfers
22
+ - recursive project-token cashout routing is intentionally bounded
23
+ - caller reclaim minima only apply to the first cashout hop, because later hops may change token units
24
+ - circular `router -> registry -> same router` forwarding remains blocked in the registry
25
+
26
+ ## Modules
27
+
28
+ | Module | Responsibility | Notes |
29
+ | --- | --- | --- |
30
+ | `JBRouterTerminal` | Intake, route discovery, swap execution, forwarding, and refunds | Main runtime surface |
31
+ | `JBRouterTerminalRegistry` | Project-level router selection, locking, and proxy forwarding to the resolved router terminal | Governance, safety, and proxy surface |
32
+ | `JBPayRouteResolver` | Preview candidate evaluation | Helper to keep runtime size bounded |
33
+ | `JBSwapLib` and routing structs | Pool discovery, quoting, and route metadata | Shared routing logic |
34
+
35
+ ## Trust Boundaries
36
+
37
+ - final accounting remains in the downstream terminal selected through `JBDirectory`
38
+ - the router trusts Uniswap V3, Uniswap V4, Permit2, and optional payer trackers for routing-side behavior
39
+ - fee-on-transfer tokens are only tolerated on ingress where received-balance deltas can be reconciled
40
+ - the registry is trusted to resolve and forward into the intended router implementation for a project
41
+
42
+ ## Critical Flows
43
+
44
+ ### Route And Pay
26
45
 
27
46
  ```text
28
47
  router pay call
29
48
  -> accept native, ERC-20, or JB-token-like input
30
49
  -> if input is a project token, recursively cash it out first
31
- -> resolve the destination token the project can actually receive
32
- -> pick the best direct, wrap/unwrap, or swap route
33
- -> execute the route and forward the result to the destination terminal
34
- -> return any leftover input to the original payer when possible
50
+ -> resolve the destination token the project terminal actually accepts
51
+ -> choose the best direct, wrap/unwrap, or swap path under the router's bounded candidate-discovery heuristic
52
+ -> execute the route and forward the result to the downstream terminal
53
+ -> refund leftover input when possible
35
54
  ```
36
55
 
37
- ## Critical Invariants
38
-
39
- - The router's own accounting context is synthetic. Consumers should not treat it as the source of truth for project accounting.
40
- - Pool discovery and quote logic must stay aligned between preview and execution paths.
41
- - Refund resolution is part of correctness, not ergonomics. Partial fills without correct refunds create value leaks.
42
- - Registry locking is a security feature; it prevents projects from being silently switched to untrusted router implementations.
43
- - Final forwarded ERC-20 hops are only supported for standard tokens whose destination-terminal pull transfers the full nominal amount without transfer fees or burns.
44
- - Circular `router -> registry -> same router` forwarding is blocked in the registry, not by teaching the router about registry internals.
56
+ ## Accounting Model
45
57
 
46
- ## Where Complexity Lives
58
+ The router does not own project balances. It owns transient route accounting: input reconciliation, swap execution, forwarded amount, and refund resolution.
47
59
 
48
- - The router composes multiple route families: direct, wrap/unwrap, recursive JB cash-out, and DEX swaps.
49
- - Native-asset handling and refund handling are the most failure-prone parts of the implementation.
50
- - Liquidity discovery across V3 and V4 is simple to describe but easy to desynchronize between preview and live execution.
51
- - V4 discovery intentionally searches both vanilla pools and pools using the canonical `UNIV4_HOOK`, because buyback-hook and LP-split integrations rely on that hook-backed oracle path.
52
- - “Best route” in this system means the best route under the router's discovery heuristic, not a guarantee of globally optimal output across every live pool.
53
- - Fee-on-transfer or otherwise lossy ERC-20s are only tolerated on ingress where the router can reconcile the received balance delta. They are rejected on the router's final terminal-facing hop. The registry does not perform independent receipt enforcement; it relies on the downstream router terminal to reject lossy transfers.
54
- - The preview candidate fanout lives in `JBPayRouteResolver`, but downstream `previewPayFor(...)` calls still originate from the router so payer-sensitive previews match execution context.
60
+ Preview and execution share the same conceptual route shape: optional recursive cashout first, then destination-token resolution, then final conversion and forwarding.
55
61
 
56
- ## Dependencies
62
+ ## Security Model
57
63
 
58
- - `nana-core-v6` terminal and directory surfaces
59
- - Uniswap V3, Uniswap V4, and Permit2
60
- - Optional `IJBPayerTracker` intermediaries for refund attribution
64
+ - native-asset handling and refunds are the most failure-prone paths
65
+ - V3 and V4 discovery must stay synchronized between preview and live execution
66
+ - V4 discovery intentionally considers both vanilla pools and pools using the canonical `UNIV4_HOOK`
67
+ - the router's "best route" claim is only as strong as its bounded discovery set and external-terminal safety checks
68
+ - recursive cashout behavior, preferred-token handling, and one-shot source overrides are tightly coupled
61
69
 
62
70
  ## Safe Change Guide
63
71
 
64
- - Keep route selection and execution semantics paired. If preview and execution diverge, frontends will misprice user flows.
65
- - Be cautious with native-token handling; wrap and unwrap edge cases are where routers usually leak value.
66
- - If you change recursive cash-out behavior, inspect the hop limit and failure modes together.
67
- - Do not promote the router into a stateful treasury layer.
68
- - Treat any new convenience path as a new asset-conservation proof obligation.
72
+ - keep route discovery and route execution semantics paired
73
+ - be conservative with native wrapping, unwrapping, and refund behavior
74
+ - if recursive cash-out logic changes, review hop limits and failure handling together
75
+ - if metadata semantics change, re-check first-hop reclaim minima, one-shot source overrides, and preferred-token routing together
76
+ - do not turn the router into a persistent treasury layer
77
+
78
+ ## Canonical Checks
79
+
80
+ - bounded recursive cash-out behavior:
81
+ `test/regression/CashOutLoopLimit.t.sol`
82
+ - preview versus execution terminal alignment:
83
+ `test/audit/PreviewPrimaryTerminalMismatch.t.sol`
84
+ - router-wide route and refund invariants:
85
+ `test/invariant/RouterTerminalInvariant.t.sol`
86
+
87
+ ## Source Map
88
+
89
+ - `src/JBRouterTerminal.sol`
90
+ - `src/JBRouterTerminalRegistry.sol`
91
+ - `src/JBPayRouteResolver.sol`
92
+ - `test/regression/CashOutLoopLimit.t.sol`
93
+ - `test/audit/PreviewPrimaryTerminalMismatch.t.sol`
94
+ - `test/invariant/RouterTerminalInvariant.t.sol`
@@ -2,9 +2,10 @@
2
2
 
3
3
  This repo accepts one token and routes value into whatever token a destination project actually accepts. Audit it as a stateless router whose mistakes show up as lost value, bad slippage control, or wrong-route accounting.
4
4
 
5
- ## Objective
5
+ ## Audit Objective
6
6
 
7
7
  Find issues that:
8
+
8
9
  - route user funds through an incorrect pool or protocol path
9
10
  - under-deliver relative to quoted or minimum-return semantics
10
11
  - refund leftovers to the wrong party or trap them in the router
@@ -14,6 +15,7 @@ Find issues that:
14
15
  ## Scope
15
16
 
16
17
  In scope:
18
+
17
19
  - `src/JBRouterTerminal.sol`
18
20
  - `src/JBRouterTerminalRegistry.sol`
19
21
  - `src/interfaces/`
@@ -22,43 +24,55 @@ In scope:
22
24
  - deployment scripts in `script/`
23
25
 
24
26
  Key dependencies:
27
+
25
28
  - `nana-core-v6`
26
29
  - Uniswap V3 and V4 integration surfaces
27
30
 
28
- ## System Model
31
+ ## Start Here
32
+
33
+ 1. `src/JBRouterTerminal.sol`
34
+ 2. `src/JBRouterTerminalRegistry.sol`
35
+ 3. `src/libraries/JBSwapLib.sol`
36
+
37
+ ## Security Model
29
38
 
30
39
  The router terminal:
31
- - discovers what token a project’s terminal accepts
40
+
41
+ - discovers what token a project's terminal accepts
32
42
  - decides whether to route via wrap/unwrap, V3, V4, Juicebox token cash-out, or a combination
33
43
  - forwards value into the destination terminal
34
44
  - optionally handles Permit2-funded transfers
35
45
 
36
46
  The registry chooses which router terminal instance a project uses and whether that choice is locked.
37
47
 
38
- ## Critical Invariants
39
-
40
- 1. User intent is preserved
41
- The actual destination project, beneficiary, minimum output semantics, and refund recipient must match the request and metadata.
48
+ ## Roles And Privileges
42
49
 
43
- 2. No leftover value disappears
44
- Partial fills, failed paths, and overfunded inputs must either be forwarded or refunded to the intended party.
50
+ | Role | Powers | How constrained |
51
+ |------|--------|-----------------|
52
+ | User or relayer | Initiate routed payment with beneficiary and slippage intent | Must receive exact refund semantics requested |
53
+ | Registry controller | Set default or allowed router terminals | Must not redirect projects unexpectedly |
54
+ | Router terminal | Hold funds only transiently during routing | Must not retain leftovers across flows |
45
55
 
46
- 3. Pool discovery and settlement agree
47
- The quoted path, callback settlement, and final forwarded amount must all describe the same trade.
56
+ ## Integration Assumptions
48
57
 
49
- 4. Registry controls stay narrow
50
- Default terminals, allowed terminals, and lock semantics must not let an unexpected router instance take over project routing.
58
+ | Dependency | Assumption | What breaks if wrong |
59
+ |------------|------------|----------------------|
60
+ | `nana-core-v6` | Terminal discovery and pay semantics are accurate | Routed value lands in the wrong place |
61
+ | Uniswap V3 and V4 | Callback settlement and pool discovery are authentic | Slippage and final forwarded amount diverge |
62
+ | Permit2 | Allowances and deadlines reflect user intent | Unauthorized transfer or stuck routing behavior |
51
63
 
52
- ## Threat Model
64
+ ## Critical Invariants
53
65
 
54
- Prioritize:
55
- - V3 or V4 callback reentrancy
56
- - sandwiching around discovered pool liquidity
57
- - beneficiary versus payer refund mismatches
58
- - Permit2 allowance or deadline misuse
59
- - races around registry terminal locking
66
+ 1. User intent is preserved.
67
+ The actual destination project, beneficiary, minimum output semantics, and refund recipient must match the request and metadata.
68
+ 2. No leftover value disappears.
69
+ Partial fills, failed paths, and overfunded inputs must either be forwarded or refunded to the intended party.
70
+ 3. Pool discovery and settlement agree.
71
+ The quoted path, callback settlement, and final forwarded amount must all describe the same trade.
72
+ 4. Registry controls stay narrow.
73
+ Default terminals, allowed terminals, and lock semantics must not let an unexpected router instance take over project routing.
60
74
 
61
- ## Hotspots
75
+ ## Attack Surfaces
62
76
 
63
77
  - payment entrypoints and refund logic
64
78
  - V3 callback verification
@@ -66,18 +80,8 @@ Prioritize:
66
80
  - pool discovery and best-path selection
67
81
  - registry allowlist and lock behavior
68
82
 
69
- ## Build And Verification
83
+ ## Verification
70
84
 
71
- Standard workflow:
72
85
  - `npm install`
73
86
  - `forge build`
74
87
  - `forge test`
75
-
76
- Current tests focus on:
77
- - refund edge cases
78
- - payer tracking
79
- - Permit2 failure handling
80
- - cash-out-assisted routes
81
- - reentrancy and sandwich-sensitive fork cases
82
-
83
- Strong findings in this repo usually show the router holding onto value or satisfying user slippage checks with the wrong sign, recipient, or output token.
package/README.md CHANGED
@@ -1,9 +1,14 @@
1
1
  # Juicebox Router Terminal
2
2
 
3
- `@bananapus/router-terminal-v6` is a routing terminal for Juicebox V6. It accepts value in many input tokens, discovers what token the destination project actually accepts, and forwards the payment through the best available route.
3
+ `@bananapus/router-terminal-v6` is a routing terminal for Juicebox V6. It accepts value in many input tokens, discovers what token the destination project actually accepts, and forwards the payment through the best route it can resolve from the configured candidates.
4
4
 
5
- Docs: <https://docs.juicebox.money>
6
- Architecture: [ARCHITECTURE.md](./ARCHITECTURE.md)
5
+ Docs: <https://docs.juicebox.money>
6
+ Architecture: [ARCHITECTURE.md](./ARCHITECTURE.md)
7
+ User journeys: [USER_JOURNEYS.md](./USER_JOURNEYS.md)
8
+ Skills: [SKILLS.md](./SKILLS.md)
9
+ Risks: [RISKS.md](./RISKS.md)
10
+ Administration: [ADMINISTRATION.md](./ADMINISTRATION.md)
11
+ Audit instructions: [AUDIT_INSTRUCTIONS.md](./AUDIT_INSTRUCTIONS.md)
7
12
 
8
13
  ## Overview
9
14
 
@@ -14,9 +19,9 @@ It can route through:
14
19
  - direct forwarding when the destination already accepts the input token
15
20
  - wrapping or unwrapping native ETH and WETH
16
21
  - Uniswap V3 or V4 swaps
17
- - recursive Juicebox token cash outs when the input itself is a project token
22
+ - recursive Juicebox token cash outs when the input is itself a project token
18
23
 
19
- Projects can use the registry contract to choose a project-specific router terminal or fall back to a default.
24
+ Projects can use the registry to choose, and optionally lock, a project-specific router terminal or fall back to the registry's default terminal.
20
25
 
21
26
  Use this repo when UX requires "pay with many tokens, settle into the right one." Do not use it as a replacement for downstream terminal accounting or as an authoritative decimal source.
22
27
 
@@ -28,7 +33,7 @@ This repo is best understood as an execution router attached to Juicebox, not as
28
33
  | --- | --- |
29
34
  | `JBRouterTerminal` | Main routing terminal that accepts many token types and forwards value to the destination terminal. |
30
35
  | `JBRouterTerminalRegistry` | Registry and proxy surface that lets a project choose and optionally lock its preferred router terminal. |
31
- | `JBPayRouteResolver` | Helper that evaluates pay-route candidates and selects the best route preview for the router terminal. |
36
+ | `JBPayRouteResolver` | Helper that evaluates pay-route candidates and selects the strongest route preview the router can resolve. |
32
37
 
33
38
  ## Mental Model
34
39
 
@@ -55,18 +60,26 @@ The shortest useful reading order is:
55
60
 
56
61
  ## Integration Traps
57
62
 
58
- - projects that expose a router terminal still settle into ordinary Juicebox terminals underneath; downstream semantics still matter
59
- - route discovery and route execution are related but not identical, especially when liquidity or metadata-supplied quotes move
63
+ - projects that expose a router terminal still settle into ordinary Juicebox terminals underneath
64
+ - route discovery and route execution are related but not identical, especially when liquidity or caller-supplied quote data moves
60
65
  - using JB project tokens as router input creates recursive path complexity that frontends and integrators should model explicitly
61
- - the registry layer changes who a project routes through, but not what the downstream terminal ultimately is
66
+ - the registry changes which router a project uses, but not what downstream terminal ultimately settles the payment
62
67
 
63
68
  ## Where State Lives
64
69
 
65
- - route-selection logic lives in `JBRouterTerminal`
66
- - per-project router choice and lock status live in `JBRouterTerminalRegistry`
67
- - accepted-token accounting and final balance changes live in the downstream terminal, usually in `nana-core-v6`
70
+ - route-selection logic: `JBRouterTerminal`
71
+ - per-project router choice and lock status: `JBRouterTerminalRegistry`
72
+ - accepted-token accounting and final balance changes: the downstream terminal, usually in `nana-core-v6`
68
73
 
69
- That separation is the reason a successful route can still end in a downstream terminal behavior you did not expect.
74
+ That separation is why a successful route can still end in downstream terminal behavior you did not expect.
75
+
76
+ ## High-Signal Tests
77
+
78
+ 1. `test/RouterTerminal.t.sol`
79
+ 2. `test/RouterTerminalPreviewFork.t.sol`
80
+ 3. `test/RouterTerminalCashOutFork.t.sol`
81
+ 4. `test/audit/PreviewPrimaryTerminalMismatch.t.sol`
82
+ 5. `test/codex/CashOutCircularPrimaryTerminal.t.sol`
70
83
 
71
84
  ## Install
72
85
 
@@ -112,7 +125,13 @@ script/
112
125
  - the router synthesizes accounting context for discovery and should not be treated as an accounting-truth surface
113
126
  - swap previews are best-effort estimates and depend on current pool state plus caller-supplied quote data
114
127
  - recursive cash-out routing increases complexity when the input token is itself a Juicebox project token
115
- - slippage and sandwich resistance depend on the quality of the quote path chosen for the route
116
- - final terminal-facing ERC-20 hops must be standard tokens; lossy terminal pulls are rejected on both router and registry paths
128
+ - slippage and sandwich resistance depend on the quality of the chosen quote path
129
+ - final terminal-facing ERC-20 hops must be standard tokens; lossy terminal pulls are rejected
117
130
 
118
131
  The most common reader mistake here is to stop at the router and forget to inspect the terminal that actually receives the value.
132
+
133
+ ## For AI Agents
134
+
135
+ - Do not claim the router is the accounting source of truth after forwarding.
136
+ - Read the preview, recursive cash-out, and registry tests before summarizing path selection behavior.
137
+ - If the route ends in surprising accounting, move to the downstream terminal in `nana-core-v6`.