@bananapus/router-terminal-v6 0.0.37 → 0.0.40

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/CHANGELOG.md CHANGED
@@ -2,7 +2,25 @@
2
2
 
3
3
  ## Scope
4
4
 
5
- This file describes the verified change from `nana-swap-terminal-v5` to the current `nana-router-terminal-v6` repo.
5
+ This file describes the verified change from `nana-swap-terminal-v5` to the current `nana-router-terminal-v6` repo. In-v6 behavior changes that have semantic implications for integrators are also captured here.
6
+
7
+ ## In-v6 changes
8
+
9
+ ### Threshold-protected `setDefaultTerminal`
10
+
11
+ The registry owner's `setDefaultTerminal(IJBTerminal)` call now applies only to projects created AFTER the call. Existing projects without an explicit `setTerminalFor` override keep resolving to the default that was current when their project-ID cohort was active. The outgoing default is snapshotted into an append-only `_defaultTerminalHistory` array on every `setDefaultTerminal` call.
12
+
13
+ - New view: `defaultTerminalFor(uint256 projectId)` returns the default applicable to a specific project (walks history if the project is in a legacy cohort).
14
+ - New view: `defaultTerminalProjectIdThreshold()` returns `PROJECTS.count()` at the time of the most recent `setDefaultTerminal`.
15
+ - New views: `defaultTerminalHistoryAt(uint256 index)` and `defaultTerminalHistoryLength()` expose the snapshot history.
16
+ - New struct: `DefaultTerminalSegment { uint256 maxProjectId; IJBTerminal terminal; }` in `src/structs/`.
17
+ - `lockTerminalFor` now snapshots the *cohort-correct* default into `_terminalOf` (via `_defaultTerminalFor(projectId)`) when locking a project that has no explicit terminal — not the current registry-wide default.
18
+
19
+ Indexer impact: read `defaultTerminalFor(projectId)` rather than `defaultTerminal()` when computing the effective default for any specific project.
20
+
21
+ Admin impact: the registry owner can no longer silently reroute payments for already-deployed projects by changing the default. See `ADMINISTRATION.md` for the updated boundary description.
22
+
23
+
6
24
 
7
25
  ## Current v6 surface
8
26
 
package/README.md CHANGED
@@ -21,7 +21,7 @@ It can route through:
21
21
  - Uniswap V3 or V4 swaps
22
22
  - recursive Juicebox token cash outs when the input is itself a project token
23
23
 
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.
24
+ Projects can use the registry to choose, and optionally lock, a project-specific router terminal or fall back to the registry's default. The default is cohort-pinned: when the registry owner calls `setDefaultTerminal` again, the new default applies only to projects created after that call; existing projects continue to resolve against the default that was current when their ID range was active (see `defaultTerminalFor(projectId)`).
25
25
 
26
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.
27
27
 
@@ -78,8 +78,8 @@ That separation is why a successful route can still end in downstream terminal b
78
78
  1. `test/RouterTerminal.t.sol`
79
79
  2. `test/RouterTerminalPreviewFork.t.sol`
80
80
  3. `test/RouterTerminalCashOutFork.t.sol`
81
- 4. `test/audit/PreviewPrimaryTerminalMismatch.t.sol`
82
- 5. `test/codex/CashOutCircularPrimaryTerminal.t.sol`
81
+ 4. `test/regression/PreviewPrimaryTerminalMismatch.t.sol`
82
+ 5. `test/regression/CashOutCircularPrimaryTerminal.t.sol`
83
83
 
84
84
  ## Install
85
85
 
@@ -114,7 +114,7 @@ src/
114
114
  libraries/
115
115
  structs/
116
116
  test/
117
- unit, fork, registry, audit, invariant, and regression coverage
117
+ unit, fork, registry, review, invariant, and regression coverage
118
118
  script/
119
119
  Deploy.s.sol
120
120
  helpers/
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bananapus/router-terminal-v6",
3
- "version": "0.0.37",
3
+ "version": "0.0.40",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -26,10 +26,10 @@
26
26
  "artifacts": "source ./.env && npx sphinx artifacts --org-id 'ea165b21-7cdc-4d7b-be59-ecdd4c26bee4' --project-name 'nana-router-terminal-v6'"
27
27
  },
28
28
  "dependencies": {
29
- "@bananapus/buyback-hook-v6": "0.0.36",
30
- "@bananapus/core-v6": "0.0.39",
31
- "@bananapus/permission-ids-v6": "0.0.22",
32
- "@bananapus/univ4-router-v6": "0.0.22",
29
+ "@bananapus/buyback-hook-v6": "^0.0.36",
30
+ "@bananapus/core-v6": "^0.0.48",
31
+ "@bananapus/permission-ids-v6": "^0.0.22",
32
+ "@bananapus/univ4-router-v6": "^0.0.22",
33
33
  "@openzeppelin/contracts": "5.6.1",
34
34
  "@uniswap/permit2": "github:Uniswap/permit2#cc56ad0f3439c502c246fc5cfcc3db92bb8b7219",
35
35
  "@uniswap/v3-core": "github:Uniswap/v3-core#6562c52e8f75f0c10f9deaf44861847585fc8129",
@@ -28,5 +28,5 @@
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.
30
30
  - [`test/RouterTerminalSandwichFork.t.sol`](../test/RouterTerminalSandwichFork.t.sol) and [`test/RouterTerminalFeeCashOutFork.t.sol`](../test/RouterTerminalFeeCashOutFork.t.sol) for adversarial routing conditions.
31
- - [`test/audit/LeftoverRefund.t.sol`](../test/audit/LeftoverRefund.t.sol), [`test/audit/PreviewPrimaryTerminalMismatch.t.sol`](../test/audit/PreviewPrimaryTerminalMismatch.t.sol), and [`test/codex/CashOutCircularPrimaryTerminal.t.sol`](../test/codex/CashOutCircularPrimaryTerminal.t.sol) for the route-selection and refund traps most likely to regress.
32
- - [`test/TestAuditGaps.sol`](../test/TestAuditGaps.sol) for pinned edge cases.
31
+ - [`test/regression/LeftoverRefund.t.sol`](../test/regression/LeftoverRefund.t.sol), [`test/regression/PreviewPrimaryTerminalMismatch.t.sol`](../test/regression/PreviewPrimaryTerminalMismatch.t.sol), and [`test/regression/CashOutCircularPrimaryTerminal.t.sol`](../test/regression/CashOutCircularPrimaryTerminal.t.sol) for the route-selection and refund traps most likely to regress.
32
+ - [`test/TestRegressionGaps.sol`](../test/TestRegressionGaps.sol) for pinned edge cases.
@@ -31,4 +31,4 @@
31
31
  - [`test/RouterTerminalCashOutFork.t.sol`](../test/RouterTerminalCashOutFork.t.sol) and [`test/RouterTerminalCreditCashout.t.sol`](../test/RouterTerminalCreditCashout.t.sol) for cash-out routing.
32
32
  - [`test/RouterTerminalReentrancy.t.sol`](../test/RouterTerminalReentrancy.t.sol) for callback and reentrancy-sensitive behavior.
33
33
  - [`test/RouterTerminalFork.t.sol`](../test/RouterTerminalFork.t.sol), [`test/RouterTerminalMultihopFork.t.sol`](../test/RouterTerminalMultihopFork.t.sol), and [`test/invariant/RouterTerminalInvariant.t.sol`](../test/invariant/RouterTerminalInvariant.t.sol) for live routing assumptions.
34
- - [`test/codex/CashOutCircularPrimaryTerminal.t.sol`](../test/codex/CashOutCircularPrimaryTerminal.t.sol), [`test/codex/CashOutFallbackPrefersRecursiveLoop.t.sol`](../test/codex/CashOutFallbackPrefersRecursiveLoop.t.sol), [`test/audit/LeftoverRefund.t.sol`](../test/audit/LeftoverRefund.t.sol), and [`test/audit/PreviewPrimaryTerminalMismatch.t.sol`](../test/audit/PreviewPrimaryTerminalMismatch.t.sol) for the misdiagnosis-prone edge cases.
34
+ - [`test/regression/CashOutCircularPrimaryTerminal.t.sol`](../test/regression/CashOutCircularPrimaryTerminal.t.sol), [`test/regression/CashOutFallbackPrefersRecursiveLoop.t.sol`](../test/regression/CashOutFallbackPrefersRecursiveLoop.t.sol), [`test/regression/LeftoverRefund.t.sol`](../test/regression/LeftoverRefund.t.sol), and [`test/regression/PreviewPrimaryTerminalMismatch.t.sol`](../test/regression/PreviewPrimaryTerminalMismatch.t.sol) for the misdiagnosis-prone edge cases.
@@ -32,18 +32,18 @@ contract JBPayRouteResolver is IJBPayRouteResolver {
32
32
  /// @notice The directory storing project terminal relationships, cached from the router at construction time.
33
33
  IJBDirectory public immutable DIRECTORY;
34
34
 
35
- /// @notice The wrapped native token, cached from the router at construction time.
36
- IWETH9 public immutable WETH;
35
+ /// @notice The ERC-20 wrapper for the chain's native token, cached from the router at construction time.
36
+ IWETH9 public immutable WRAPPED_NATIVE_TOKEN;
37
37
 
38
38
  //*********************************************************************//
39
39
  // -------------------------- constructor ---------------------------- //
40
40
  //*********************************************************************//
41
41
 
42
42
  /// @param directory The directory storing project terminal relationships.
43
- /// @param weth The wrapped native token used for router token normalization.
43
+ /// @param weth The ERC-20 wrapper for the chain's native token, used for router token normalization.
44
44
  constructor(IJBDirectory directory, IWETH9 weth) {
45
45
  DIRECTORY = directory;
46
- WETH = weth;
46
+ WRAPPED_NATIVE_TOKEN = weth;
47
47
  }
48
48
 
49
49
  //*********************************************************************//
@@ -56,7 +56,6 @@ contract JBPayRouteResolver is IJBPayRouteResolver {
56
56
  /// @param terminals The terminal list already fetched for the destination project.
57
57
  /// @return tokens The unique candidate tokens that can be paid into the project.
58
58
  /// @return count The number of populated entries in `tokens`.
59
- // slither-disable-next-line calls-loop
60
59
  function _candidatePayRouteTokens(
61
60
  IJBDirectory directory,
62
61
  uint256 projectId,
@@ -145,7 +144,6 @@ contract JBPayRouteResolver is IJBPayRouteResolver {
145
144
  /// @param tokenIn The input token to find a route from.
146
145
  /// @return tokenOut The best accepted token found.
147
146
  /// @return destTerminal The terminal that accepts `tokenOut`.
148
- // slither-disable-next-line calls-loop
149
147
  function _discoverAcceptedToken(
150
148
  IJBPayRoutePreviewer router,
151
149
  uint256 projectId,
@@ -188,7 +186,7 @@ contract JBPayRouteResolver is IJBPayRouteResolver {
188
186
  // Pull the candidate token out of the accounting context being inspected.
189
187
  address candidateToken = contexts[j].token;
190
188
 
191
- // Normalize the candidate so native-vs-WETH comparisons behave the same as the router.
189
+ // Normalize the candidate so native-vs-wrapped comparisons behave the same as the router.
192
190
  address normalizedCandidate = _normalizedTokenOf(candidateToken);
193
191
 
194
192
  // Skip tokens that are equivalent to the input token because they do not require route discovery.
@@ -364,8 +362,6 @@ contract JBPayRouteResolver is IJBPayRouteResolver {
364
362
  pure
365
363
  returns (bool exists, bytes memory data)
366
364
  {
367
- // slither-disable-next-line unused-return
368
- // slither-disable-next-line unused-return
369
365
  return JBMetadataResolver.getDataFor({id: JBMetadataResolver.getId(key, address(router)), metadata: metadata});
370
366
  }
371
367
 
@@ -377,7 +373,7 @@ contract JBPayRouteResolver is IJBPayRouteResolver {
377
373
  // Treat exact-token matches as the same routing asset without extra normalization work.
378
374
  if (tokenA == tokenB) return true;
379
375
 
380
- // Otherwise compare normalized representations so ETH and WETH share one routing identity.
376
+ // Otherwise compare normalized representations so native and wrapped native tokens share one routing identity.
381
377
  return _normalizedTokenOf(tokenA) == _normalizedTokenOf(tokenB);
382
378
  }
383
379
 
@@ -404,7 +400,7 @@ contract JBPayRouteResolver is IJBPayRouteResolver {
404
400
  /// @param token The token to normalize.
405
401
  /// @return normalizedToken The normalized token address.
406
402
  function _normalizedTokenOf(address token) internal view returns (address normalizedToken) {
407
- return token == JBConstants.NATIVE_TOKEN ? address(WETH) : token;
403
+ return token == JBConstants.NATIVE_TOKEN ? address(WRAPPED_NATIVE_TOKEN) : token;
408
404
  }
409
405
 
410
406
  /// @notice Preview the amount that would be routed into a specific destination token.
@@ -602,7 +598,6 @@ contract JBPayRouteResolver is IJBPayRouteResolver {
602
598
  if (sourceProjectId == 0) return (resolvedTerminal, tokenIn, amount);
603
599
 
604
600
  // Otherwise reuse the router's own preview cashout loop so preview and execution stay aligned.
605
- // slither-disable-next-line unused-return
606
601
  return router.previewCashOutLoopOf({
607
602
  destProjectId: destProjectId,
608
603
  token: tokenIn,
@@ -647,7 +642,7 @@ contract JBPayRouteResolver is IJBPayRouteResolver {
647
642
  address(destTerminal) == address(0)
648
643
  || _isCircularTerminal({router: router, projectId: projectId, terminal: destTerminal})
649
644
  ) {
650
- revert JBRouterTerminal_NoRouteFound(projectId, tokenIn);
645
+ revert JBRouterTerminal_NoRouteFound({projectId: projectId, tokenIn: tokenIn});
651
646
  }
652
647
  return (tokenOut, destTerminal);
653
648
  }
@@ -663,8 +658,8 @@ contract JBPayRouteResolver is IJBPayRouteResolver {
663
658
  }
664
659
 
665
660
  // Then try the native-token and wrapped-native-token equivalent form before falling back to pool discovery.
666
- if (tokenIn == JBConstants.NATIVE_TOKEN || tokenIn == address(WETH)) {
667
- tokenOut = tokenIn == JBConstants.NATIVE_TOKEN ? address(WETH) : JBConstants.NATIVE_TOKEN;
661
+ if (tokenIn == JBConstants.NATIVE_TOKEN || tokenIn == address(WRAPPED_NATIVE_TOKEN)) {
662
+ tokenOut = tokenIn == JBConstants.NATIVE_TOKEN ? address(WRAPPED_NATIVE_TOKEN) : JBConstants.NATIVE_TOKEN;
668
663
  destTerminal = directory.primaryTerminalOf({projectId: projectId, token: tokenOut});
669
664
  if (
670
665
  address(destTerminal) != address(0)
@@ -682,7 +677,7 @@ contract JBPayRouteResolver is IJBPayRouteResolver {
682
677
  address(destTerminal) == address(0)
683
678
  || _isCircularTerminal({router: router, projectId: projectId, terminal: destTerminal})
684
679
  ) {
685
- revert JBRouterTerminal_NoRouteFound(projectId, tokenIn);
680
+ revert JBRouterTerminal_NoRouteFound({projectId: projectId, tokenIn: tokenIn});
686
681
  }
687
682
  }
688
683
 
@@ -828,7 +823,6 @@ contract JBPayRouteResolver is IJBPayRouteResolver {
828
823
  returns (IJBTerminal candidateTerminal)
829
824
  {
830
825
  // Resolve the primary terminal for the candidate token so fallback discovery agrees with preview/execution.
831
- // slither-disable-next-line calls-loop
832
826
  candidateTerminal = directory.primaryTerminalOf({projectId: projectId, token: candidateToken});
833
827
 
834
828
  // Drop candidates whose primary terminal disappeared or would route straight back into the router.
@@ -845,7 +839,6 @@ contract JBPayRouteResolver is IJBPayRouteResolver {
845
839
  //*********************************************************************//
846
840
 
847
841
  /// @inheritdoc IJBPayRouteResolver
848
- // slither-disable-next-line calls-loop
849
842
  function previewBestPayRoute(
850
843
  IJBPayRoutePreviewer router,
851
844
  uint256 projectId,
@@ -885,7 +878,7 @@ contract JBPayRouteResolver is IJBPayRouteResolver {
885
878
  address(destTerminal) == address(0)
886
879
  || _isCircularTerminal({router: router, projectId: projectId, terminal: destTerminal})
887
880
  ) {
888
- revert JBRouterTerminal_NoRouteFound(projectId, tokenIn);
881
+ revert JBRouterTerminal_NoRouteFound({projectId: projectId, tokenIn: tokenIn});
889
882
  }
890
883
 
891
884
  // Score the explicitly requested route directly instead of scanning every accepted token.