@bananapus/router-terminal-v6 0.0.63 → 0.0.64

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/README.md CHANGED
@@ -32,7 +32,7 @@ Use this repo when UX requires "pay with many tokens, settle into the right one.
32
32
 
33
33
  This repo is best understood as an execution router attached to Juicebox, not as a new accounting model.
34
34
 
35
- ## Key Contracts
35
+ ## Key contracts
36
36
 
37
37
  | Contract | Role |
38
38
  | --- | --- |
@@ -40,7 +40,7 @@ This repo is best understood as an execution router attached to Juicebox, not as
40
40
  | `JBRouterTerminalRegistry` | Registry and proxy surface that lets a project choose and optionally lock its preferred router terminal. |
41
41
  | `JBPayRouteResolver` | Helper that evaluates pay-route candidates and selects the strongest route preview the router can resolve. |
42
42
 
43
- ## Mental Model
43
+ ## Mental model
44
44
 
45
45
  There are three separate decisions on the payment path:
46
46
 
@@ -56,21 +56,21 @@ The shortest useful reading order is:
56
56
  2. `JBRouterTerminalRegistry`
57
57
  3. the downstream terminal selected through `JBDirectory`
58
58
 
59
- ## Read These Files First
59
+ ## Read these files first
60
60
 
61
61
  1. `src/JBRouterTerminal.sol`
62
62
  2. `src/JBRouterTerminalRegistry.sol`
63
63
  3. `src/libraries/JBSwapLib.sol`
64
64
  4. the downstream terminal implementation in `nana-core-v6`
65
65
 
66
- ## Integration Traps
66
+ ## Integration traps
67
67
 
68
68
  - projects that expose a router terminal still settle into ordinary Juicebox terminals underneath
69
69
  - route discovery and route execution are related but not identical, especially when liquidity or caller-supplied quote data moves
70
70
  - using JB project tokens as router input creates recursive path complexity that frontends and integrators should model explicitly
71
71
  - the registry changes which router a project uses, but not what downstream terminal ultimately settles the payment
72
72
 
73
- ## Where State Lives
73
+ ## Where state lives
74
74
 
75
75
  - route-selection logic: `JBRouterTerminal`
76
76
  - per-project router choice and lock status: `JBRouterTerminalRegistry`
@@ -78,7 +78,7 @@ The shortest useful reading order is:
78
78
 
79
79
  That separation is why a successful route can still end in downstream terminal behavior you did not expect.
80
80
 
81
- ## High-Signal Tests
81
+ ## High-signal tests
82
82
 
83
83
  1. `test/RouterTerminal.t.sol`
84
84
  2. `test/RouterTerminalPreviewFork.t.sol`
@@ -105,11 +105,11 @@ Useful scripts:
105
105
  - `npm run deploy:mainnets`
106
106
  - `npm run deploy:testnets`
107
107
 
108
- ## Deployment Notes
108
+ ## Deployment notes
109
109
 
110
110
  This package depends on core, address-registry, and permission-ID packages plus Uniswap V3, V4, and Permit2 integrations. It is meant to sit in front of canonical Juicebox terminals, not replace them.
111
111
 
112
- ## Repository Layout
112
+ ## Repository layout
113
113
 
114
114
  ```text
115
115
  src/
@@ -125,7 +125,7 @@ script/
125
125
  helpers/
126
126
  ```
127
127
 
128
- ## Risks And Notes
128
+ ## Risks and notes
129
129
 
130
130
  - the router synthesizes accounting context for discovery and should not be treated as an accounting-truth surface
131
131
  - swap previews are best-effort estimates and depend on current pool state plus caller-supplied quote data
@@ -135,7 +135,7 @@ script/
135
135
 
136
136
  The most common reader mistake here is to stop at the router and forget to inspect the terminal that actually receives the value.
137
137
 
138
- ## For AI Agents
138
+ ## For AI agents
139
139
 
140
140
  - Do not claim the router is the accounting source of truth after forwarding.
141
141
  - Read the preview, recursive cash-out, and registry tests before summarizing path selection behavior.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bananapus/router-terminal-v6",
3
- "version": "0.0.63",
3
+ "version": "0.0.64",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1,12 +1,12 @@
1
1
  # Router Terminal Operations
2
2
 
3
- ## Configuration Surface
3
+ ## Configuration surface
4
4
 
5
5
  - [`src/JBRouterTerminalRegistry.sol`](../src/JBRouterTerminalRegistry.sol) is the first stop for per-project terminal choice, default terminal behavior, allowlisting, and locking.
6
6
  - [`src/JBRouterTerminal.sol`](../src/JBRouterTerminal.sol) owns the metadata-driven route selection and execution logic.
7
7
  - [`script/Deploy.s.sol`](../script/Deploy.s.sol) is the deployment entry point when the task is about current deployment wiring rather than core routing logic.
8
8
 
9
- ## Change Checklist
9
+ ## Change checklist
10
10
 
11
11
  - If you edit route discovery, verify both direct acceptance and swap-based routes.
12
12
  - If you edit the cash-out loop, check project-token cash-out flows and fork tests, not only simple payments.
@@ -15,7 +15,7 @@
15
15
  - If you edit refund or partial-fill handling, verify baseline snapshots and destination-terminal receipt enforcement together.
16
16
  - If you touch Permit2 or metadata parsing, verify the corresponding interfaces and structs in `src/interfaces/` and `src/structs/` together with the fork tests.
17
17
 
18
- ## Common Failure Modes
18
+ ## Common failure modes
19
19
 
20
20
  - Router behavior looks wrong, but the real issue is the downstream terminal's accepted-token or accounting behavior.
21
21
  - Preview output drifts from execution because quote and execution paths were edited independently.
@@ -23,7 +23,7 @@
23
23
  - Metadata overrides force an output token or cash-out source that the caller did not intend.
24
24
  - On `addToBalanceOf` paths, a terminal-facing ERC-20 receipt mismatch indicates a non-standard final-hop token path.
25
25
 
26
- ## Useful Proof Points
26
+ ## Useful proof points
27
27
 
28
28
  - [`test/RouterTerminalRegistry.t.sol`](../test/RouterTerminalRegistry.t.sol) for registry rules.
29
29
  - [`test/RouterTerminalERC2771.t.sol`](../test/RouterTerminalERC2771.t.sol) for trusted-forwarder behavior.
@@ -1,12 +1,12 @@
1
1
  # Router Terminal Runtime
2
2
 
3
- ## Contract Roles
3
+ ## Contract roles
4
4
 
5
5
  - [`src/JBRouterTerminal.sol`](../src/JBRouterTerminal.sol) is the main execution surface. It accepts input tokens, discovers the output token, performs conversion, and forwards settlement to the downstream terminal.
6
6
  - [`src/JBRouterTerminalRegistry.sol`](../src/JBRouterTerminalRegistry.sol) selects a per-project router terminal or falls back to the default one, while enforcing allowlist and lock rules.
7
7
  - Helper logic in [`src/JBPayRouteResolver.sol`](../src/JBPayRouteResolver.sol) and the repo's interfaces/structs define how pay-route resolution and metadata-driven routing fit together.
8
8
 
9
- ## Runtime Path
9
+ ## Runtime path
10
10
 
11
11
  1. The router accepts native tokens, ERC-20s, or claimed Juicebox project-token ERC-20s.
12
12
  2. If the input is a Juicebox project token, the router may enter a cash-out loop first.
@@ -14,7 +14,7 @@
14
14
  4. It converts value through direct forwarding, wrap/unwrap, Uniswap V3, or Uniswap V4.
15
15
  5. It forwards the final asset to the destination project's canonical terminal.
16
16
 
17
- ## High-Risk Areas
17
+ ## High-risk areas
18
18
 
19
19
  - Preview and execution parity: changes to quote selection or route discovery should be checked against both preview and mutative paths.
20
20
  - V4 discovery scope: the router searches both vanilla V4 pools and pools using the configured canonical `UNIV4_HOOK`.
@@ -25,7 +25,7 @@
25
25
  - Final terminal-facing ERC-20 receipt enforcement: `addToBalanceOf` rejects lossy terminal pulls, while `pay` does not enforce receipt deltas because pay hooks can consume tokens during settlement. The registry does not independently enforce receipts; it relies on the router path it forwards into.
26
26
  - Preview normalization: buyback-hook metadata can improve the user-visible preview outcome, so route ranking must normalize hook-returned hints consistently across candidates.
27
27
 
28
- ## Tests To Trust First
28
+ ## Tests to trust first
29
29
 
30
30
  - [`test/RouterTerminalPreviewFork.t.sol`](../test/RouterTerminalPreviewFork.t.sol) for preview-path behavior.
31
31
  - [`test/RouterTerminalCashOutFork.t.sol`](../test/RouterTerminalCashOutFork.t.sol) and [`test/RouterTerminalFeeCashOutFork.t.sol`](../test/RouterTerminalFeeCashOutFork.t.sol) for project-token cash-out routing.
@@ -23,6 +23,8 @@ contract JBPayRouteResolver is IJBPayRouteResolver {
23
23
  // --------------------------- custom errors ------------------------- //
24
24
  //*********************************************************************//
25
25
 
26
+ /// @notice Thrown when no usable, non-circular terminal route can be resolved to pay the project with the input
27
+ /// token.
26
28
  error JBRouterTerminal_NoRouteFound(uint256 projectId, address tokenIn);
27
29
 
28
30
  //*********************************************************************//
@@ -843,7 +845,22 @@ contract JBPayRouteResolver is IJBPayRouteResolver {
843
845
  // ------------------------- external views -------------------------- //
844
846
  //*********************************************************************//
845
847
 
846
- /// @inheritdoc IJBPayRouteResolver
848
+ /// @notice Preview the best pay route for a router terminal.
849
+ /// @param router The router terminal whose preview helpers to use.
850
+ /// @param wrappedNativeToken The router's wrapped-native-token address, passed in so the resolver does not have to
851
+ /// call back into the router for it on every normalization step.
852
+ /// @param projectId The destination project that would receive the payment.
853
+ /// @param tokenIn The token currently available to route.
854
+ /// @param amount The amount of `tokenIn` to preview.
855
+ /// @param beneficiary The address whose minted token count to optimize.
856
+ /// @param metadata Metadata forwarded into route and pay previews.
857
+ /// @return destTerminal The terminal chosen for the best previewed route.
858
+ /// @return tokenOut The token `destTerminal` would receive.
859
+ /// @return amountOut The amount of `tokenOut` that would be paid.
860
+ /// @return ruleset The ruleset returned by the chosen terminal preview.
861
+ /// @return beneficiaryTokenCount The effective beneficiary token count for the chosen route.
862
+ /// @return reservedTokenCount The effective reserved token count for the chosen route.
863
+ /// @return hookSpecifications The hook specifications returned by the chosen terminal preview.
847
864
  function previewBestPayRoute(
848
865
  IJBPayRoutePreviewer router,
849
866
  address wrappedNativeToken,
@@ -1128,7 +1145,14 @@ contract JBPayRouteResolver is IJBPayRouteResolver {
1128
1145
  });
1129
1146
  }
1130
1147
 
1131
- /// @inheritdoc IJBPayRouteResolver
1148
+ /// @notice Determine what output token a project accepts for a given input token.
1149
+ /// @param router The router whose view helpers to use.
1150
+ /// @param wrappedNativeToken The router's wrapped-native-token address.
1151
+ /// @param projectId The destination project to pay.
1152
+ /// @param tokenIn The input token to route.
1153
+ /// @param metadata Metadata forwarded into route-token resolution.
1154
+ /// @return tokenOut The token the project accepts.
1155
+ /// @return destTerminal The terminal that accepts `tokenOut`.
1132
1156
  function resolveTokenOut(
1133
1157
  IJBPayRoutePreviewer router,
1134
1158
  address wrappedNativeToken,
@@ -1149,7 +1173,11 @@ contract JBPayRouteResolver is IJBPayRouteResolver {
1149
1173
  });
1150
1174
  }
1151
1175
 
1152
- /// @inheritdoc IJBPayRouteResolver
1176
+ /// @notice Resolve a project's primary terminal only when the router can safely forward into it.
1177
+ /// @param router The router whose forwarding-terminal rules to apply.
1178
+ /// @param projectId The project to check the primary terminal for.
1179
+ /// @param token The token that terminal should accept.
1180
+ /// @return terminal The usable primary terminal, or address(0) if none is usable.
1153
1181
  function usablePrimaryTerminalOf(
1154
1182
  IJBPayRoutePreviewer router,
1155
1183
  uint256 projectId,
@@ -72,25 +72,62 @@ contract JBRouterTerminal is
72
72
  // --------------------------- custom errors ------------------------- //
73
73
  //*********************************************************************//
74
74
 
75
+ /// @notice Thrown when the chain-specific constants have already been set and cannot be reconfigured.
75
76
  error JBRouterTerminal_AlreadyConfigured();
77
+
78
+ /// @notice Thrown when an amount exceeds the maximum width a swap quote or pool call can represent.
76
79
  error JBRouterTerminal_AmountOverflow(uint256 amount);
80
+
81
+ /// @notice Thrown when a Uniswap V3 swap callback caller is not the expected pool for the swapped token pair.
77
82
  error JBRouterTerminal_CallerNotPool(address caller);
83
+
84
+ /// @notice Thrown when an unlock callback caller is not the Uniswap V4 pool manager.
78
85
  error JBRouterTerminal_CallerNotPoolManager(address caller);
86
+
87
+ /// @notice Thrown when a non-zero cash-out hop reclaims no tokens, so the route cannot safely continue.
79
88
  error JBRouterTerminal_CashOutDidNotDeliver(address sourceToken, address tokenToReclaim, uint256 cashOutCount);
89
+
90
+ /// @notice Thrown when a cash-out route exceeds the maximum number of hops allowed.
80
91
  error JBRouterTerminal_CashOutLoopLimit(uint256 maxIterations);
92
+
93
+ /// @notice Thrown when a pool's TWAP history is shorter than the minimum window required to resist manipulation.
81
94
  error JBRouterTerminal_InsufficientTwapHistory(address pool, uint256 twapWindow, uint256 minTwapWindow);
95
+
96
+ /// @notice Thrown when a leg with no downstream slippage backstop has no manipulation-resistant TWAP and the
97
+ /// caller did not supply a `pay` quote.
82
98
  error JBRouterTerminal_ManipulationResistantQuoteRequired(PoolId poolId);
99
+
100
+ /// @notice Thrown when no terminal exposes a reclaimable token that can advance a cash-out route.
83
101
  error JBRouterTerminal_NoCashOutPath(uint256 sourceProjectId, uint256 destProjectId);
102
+
103
+ /// @notice Thrown when the chosen pool has no in-range liquidity to swap against.
84
104
  error JBRouterTerminal_NoLiquidity(address pool, PoolId poolId);
105
+
106
+ /// @notice Thrown when native tokens are sent on a call that does not accept them.
85
107
  error JBRouterTerminal_NoMsgValueAllowed(uint256 value);
108
+
109
+ /// @notice Thrown when a pool has no oracle observation history, so no TWAP can be formed.
86
110
  error JBRouterTerminal_NoObservationHistory(address pool);
111
+
112
+ /// @notice Thrown when no Uniswap V3 or V4 pool exists for the requested token pair.
87
113
  error JBRouterTerminal_NoPoolFound(address tokenIn, address tokenOut);
114
+
115
+ /// @notice Thrown when the amount a terminal actually received differs from the amount the router routed to it,
116
+ /// indicating a lossy or non-standard token path.
88
117
  error JBRouterTerminal_NonStandardTerminalToken(
89
118
  address terminal, address token, uint256 expectedAmount, uint256 actualAmount
90
119
  );
120
+
121
+ /// @notice Thrown when the payment amount exceeds the Permit2 allowance provided in the metadata.
91
122
  error JBRouterTerminal_PermitAllowanceNotEnough(uint256 amount, uint256 allowance);
123
+
124
+ /// @notice Thrown when the token a `pay` quote was bound to does not match the route's resolved output token.
92
125
  error JBRouterTerminal_QuoteTokenMismatch(address quotedTokenOut, address expectedTokenOut);
126
+
127
+ /// @notice Thrown when a swap or cash-out output falls below the caller's minimum acceptable amount.
93
128
  error JBRouterTerminal_SlippageExceeded(uint256 amountOut, uint256 minAmountOut);
129
+
130
+ /// @notice Thrown when the caller is not the deployer authorized to set chain-specific constants.
94
131
  error JBRouterTerminal_Unauthorized(address caller);
95
132
 
96
133
  //*********************************************************************//
@@ -40,19 +40,38 @@ contract JBRouterTerminalRegistry is IJBRouterTerminalRegistry, JBPermissioned,
40
40
  // --------------------------- custom errors ------------------------- //
41
41
  //*********************************************************************//
42
42
 
43
+ /// @notice Thrown when an amount exceeds the maximum width a forwarded terminal call can represent.
43
44
  error JBRouterTerminalRegistry_AmountOverflow(uint256 amount);
45
+
46
+ /// @notice Thrown when attempting to disallow the terminal that is currently set as the default.
44
47
  error JBRouterTerminalRegistry_CannotDisallowDefaultTerminal(IJBTerminal terminal);
48
+
49
+ /// @notice Thrown when a terminal would forward a project back into this registry, directly or transitively.
45
50
  error JBRouterTerminalRegistry_CircularForward(IJBTerminal terminal);
51
+
52
+ /// @notice Thrown when native tokens are sent on a call that does not accept them.
46
53
  error JBRouterTerminalRegistry_NoMsgValueAllowed(uint256 value);
54
+
55
+ /// @notice Thrown when the payment amount exceeds the Permit2 allowance provided in the metadata.
47
56
  error JBRouterTerminalRegistry_PermitAllowanceNotEnough(uint256 amount, uint256 allowanceAmount);
57
+
58
+ /// @notice Thrown when changing a project's terminal after its terminal choice has been permanently locked.
48
59
  error JBRouterTerminalRegistry_TerminalLocked(uint256 projectId);
60
+
61
+ /// @notice Thrown when the project's resolved terminal does not match the terminal the caller expected to lock.
49
62
  error JBRouterTerminalRegistry_TerminalMismatch(IJBTerminal currentTerminal, IJBTerminal expectedTerminal);
63
+
64
+ /// @notice Thrown when selecting a terminal that is not on the registry allowlist.
50
65
  error JBRouterTerminalRegistry_TerminalNotAllowed(IJBTerminal terminal);
66
+
67
+ /// @notice Thrown when a project has no explicit terminal and no default terminal has ever been set.
51
68
  error JBRouterTerminalRegistry_TerminalNotSet(uint256 projectId);
69
+
70
+ /// @notice Thrown when setting the default terminal to the zero address.
52
71
  error JBRouterTerminalRegistry_ZeroAddress(address terminal);
53
72
 
54
73
  //*********************************************************************//
55
- // -------------------- public immutable properties ------------------ //
74
+ // ---------------- public immutable stored properties --------------- //
56
75
  //*********************************************************************//
57
76
 
58
77
  /// @notice The Juicebox project registry used to verify project existence and ownership.
@@ -108,7 +127,7 @@ contract JBRouterTerminalRegistry is IJBRouterTerminalRegistry, JBPermissioned,
108
127
  // -------------------- transient stored properties ------------------ //
109
128
  //*********************************************************************//
110
129
 
111
- /// @inheritdoc IJBPayerTracker
130
+ /// @notice The original payer of the current transaction.
112
131
  address public transient override originalPayer;
113
132
 
114
133
  //*********************************************************************//
@@ -275,7 +294,9 @@ contract JBRouterTerminalRegistry is IJBRouterTerminalRegistry, JBPermissioned,
275
294
  });
276
295
  }
277
296
 
278
- /// @inheritdoc IJBForwardingTerminal
297
+ /// @notice The concrete terminal this forwarding layer would route a project's payment into.
298
+ /// @param projectId The project whose downstream terminal should be resolved.
299
+ /// @return terminal The concrete terminal the forwarder would call for `projectId`.
279
300
  function terminalOf(uint256 projectId) external view override returns (IJBTerminal terminal) {
280
301
  return _resolvedTerminalOf(projectId);
281
302
  }