@bananapus/router-terminal-v6 0.0.58 → 0.0.60
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 +1 -1
- package/package.json +1 -1
- package/src/JBRouterTerminal.sol +107 -22
- package/src/JBRouterTerminalRegistry.sol +36 -31
package/README.md
CHANGED
|
@@ -26,7 +26,7 @@ It can route through:
|
|
|
26
26
|
- Uniswap V3 or V4 swaps
|
|
27
27
|
- recursive Juicebox token cash outs when the input is itself a project token
|
|
28
28
|
|
|
29
|
-
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)`).
|
|
29
|
+
Projects can use the registry to choose, and optionally lock, a project-specific router terminal or fall back to the registry's default. The first `setDefaultTerminal` serves every project that already existed when it was called — including the canonical fee project (ID 1) — so those pre-existing projects can route tokens through the default. After that, 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)`).
|
|
30
30
|
|
|
31
31
|
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.
|
|
32
32
|
|
package/package.json
CHANGED
package/src/JBRouterTerminal.sol
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
pragma solidity 0.8.28;
|
|
3
3
|
|
|
4
4
|
import {IJBCashOutTerminal} from "@bananapus/core-v6/src/interfaces/IJBCashOutTerminal.sol";
|
|
5
|
+
import {IJBController} from "@bananapus/core-v6/src/interfaces/IJBController.sol";
|
|
5
6
|
import {IJBDirectory} from "@bananapus/core-v6/src/interfaces/IJBDirectory.sol";
|
|
6
7
|
import {IJBPermitTerminal} from "@bananapus/core-v6/src/interfaces/IJBPermitTerminal.sol";
|
|
7
8
|
import {IJBTerminal} from "@bananapus/core-v6/src/interfaces/IJBTerminal.sol";
|
|
@@ -78,6 +79,7 @@ contract JBRouterTerminal is
|
|
|
78
79
|
error JBRouterTerminal_CashOutDidNotDeliver(address sourceToken, address tokenToReclaim, uint256 cashOutCount);
|
|
79
80
|
error JBRouterTerminal_CashOutLoopLimit(uint256 maxIterations);
|
|
80
81
|
error JBRouterTerminal_InsufficientTwapHistory(address pool, uint256 twapWindow, uint256 minTwapWindow);
|
|
82
|
+
error JBRouterTerminal_ManipulationResistantQuoteRequired(PoolId poolId);
|
|
81
83
|
error JBRouterTerminal_NoCashOutPath(uint256 sourceProjectId, uint256 destProjectId);
|
|
82
84
|
error JBRouterTerminal_NoLiquidity(address pool, PoolId poolId);
|
|
83
85
|
error JBRouterTerminal_NoMsgValueAllowed(uint256 value);
|
|
@@ -190,6 +192,19 @@ contract JBRouterTerminal is
|
|
|
190
192
|
// ---------------------- internal stored properties ----------------- //
|
|
191
193
|
//*********************************************************************//
|
|
192
194
|
|
|
195
|
+
//*********************************************************************//
|
|
196
|
+
// ------------------- transient stored properties ------------------- //
|
|
197
|
+
//*********************************************************************//
|
|
198
|
+
|
|
199
|
+
/// @notice Transient flag: when set, a quote-less swap may NOT fall back to a manipulable spot price — if the
|
|
200
|
+
/// selected pool exposes no manipulation-resistant oracle (a vanilla V4 pool) and the caller supplied no `pay`
|
|
201
|
+
/// quote, `_getV4SpotQuote` reverts and the caller must provide a quote.
|
|
202
|
+
/// @dev Set true by `addToBalanceOf` (whose swap has no downstream `minReturnedTokens` backstop) and false by
|
|
203
|
+
/// `pay` (whose top-level `minReturnedTokens` guards the entire routed result end-to-end). Read at quote time,
|
|
204
|
+
/// which is synchronous and always precedes the swap's pool callbacks, so it reflects the originating entrypoint.
|
|
205
|
+
/// Off-chain previews leave it at its default (false), so estimates still resolve. Transient, so it auto-clears.
|
|
206
|
+
bool internal transient _strictSwapQuote;
|
|
207
|
+
|
|
193
208
|
//*********************************************************************//
|
|
194
209
|
// -------------------------- constructor ---------------------------- //
|
|
195
210
|
//*********************************************************************//
|
|
@@ -265,6 +280,11 @@ contract JBRouterTerminal is
|
|
|
265
280
|
payable
|
|
266
281
|
override
|
|
267
282
|
{
|
|
283
|
+
// This leg settles via `addToBalanceOf`, which has no `minReturnedTokens` (or any downstream) backstop, so a
|
|
284
|
+
// quote-less swap must not silently price against a manipulable spot. Require a manipulation-resistant source
|
|
285
|
+
// (a canonical-hook V4 oracle, a V3 TWAP) or an explicit `pay` quote — see `_getV4SpotQuote`.
|
|
286
|
+
_strictSwapQuote = true;
|
|
287
|
+
|
|
268
288
|
// Keep a reference to the terminal that will ultimately receive the routed funds.
|
|
269
289
|
IJBTerminal destTerminal;
|
|
270
290
|
|
|
@@ -354,6 +374,10 @@ contract JBRouterTerminal is
|
|
|
354
374
|
override
|
|
355
375
|
returns (uint256 beneficiaryTokenCount)
|
|
356
376
|
{
|
|
377
|
+
// The top-level `minReturnedTokens` guards the entire routed result end-to-end (a bad intermediate swap
|
|
378
|
+
// yields fewer final tokens and reverts here), so this leg may use the bounded spot-quote convenience path.
|
|
379
|
+
_strictSwapQuote = false;
|
|
380
|
+
|
|
357
381
|
// Keep a reference to the terminal that will receive the routed payment.
|
|
358
382
|
IJBTerminal destTerminal;
|
|
359
383
|
|
|
@@ -598,7 +622,9 @@ contract JBRouterTerminal is
|
|
|
598
622
|
return pool;
|
|
599
623
|
}
|
|
600
624
|
|
|
601
|
-
/// @notice
|
|
625
|
+
/// @notice The best Uniswap V3 pool for a token pair, useful for off-chain queries.
|
|
626
|
+
/// @dev V3-only by design: returns the deepest V3 pool whenever one exists, independent of whether a deeper V4
|
|
627
|
+
/// pool exists for the same pair. Reverts only when no V3 pool exists at all.
|
|
602
628
|
/// @param normalizedTokenIn The input token (wrapped if native).
|
|
603
629
|
/// @param normalizedTokenOut The output token (wrapped if native).
|
|
604
630
|
/// @return pool The V3 pool with the highest liquidity.
|
|
@@ -611,12 +637,10 @@ contract JBRouterTerminal is
|
|
|
611
637
|
override
|
|
612
638
|
returns (IUniswapV3Pool pool)
|
|
613
639
|
{
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
if (!info.isV4 && address(info.v3Pool) == address(0)) {
|
|
640
|
+
pool = _discoverV3Pool({normalizedTokenIn: normalizedTokenIn, normalizedTokenOut: normalizedTokenOut});
|
|
641
|
+
if (address(pool) == address(0)) {
|
|
617
642
|
revert JBRouterTerminal_NoPoolFound({tokenIn: normalizedTokenIn, tokenOut: normalizedTokenOut});
|
|
618
643
|
}
|
|
619
|
-
if (!info.isV4) pool = info.v3Pool;
|
|
620
644
|
}
|
|
621
645
|
|
|
622
646
|
/// @notice Preview a payment by simulating the router's routing logic in view context.
|
|
@@ -1180,6 +1204,8 @@ contract JBRouterTerminal is
|
|
|
1180
1204
|
sourceProjectId: sourceProjectId, destProjectId: destProjectId, preferredToken: preferredToken
|
|
1181
1205
|
});
|
|
1182
1206
|
|
|
1207
|
+
_claimRouterCreditsFor({projectId: sourceProjectId});
|
|
1208
|
+
|
|
1183
1209
|
uint256 cashOutCount = amount;
|
|
1184
1210
|
uint256 balanceBefore = _balanceOf({token: tokenToReclaim, account: address(this)});
|
|
1185
1211
|
|
|
@@ -1235,6 +1261,20 @@ contract JBRouterTerminal is
|
|
|
1235
1261
|
revert JBRouterTerminal_CashOutLoopLimit({maxIterations: _MAX_CASHOUT_ITERATIONS});
|
|
1236
1262
|
}
|
|
1237
1263
|
|
|
1264
|
+
/// @notice Converts this router's internal project-token credits into ERC-20s before a source cash-out.
|
|
1265
|
+
/// @dev Core burns holder credits before ERC-20 balances. Normalizing first keeps a source cash-out scoped to
|
|
1266
|
+
/// transferable token balances already visible to the router.
|
|
1267
|
+
/// @param projectId The Juicebox project whose tokens are being cashed out.
|
|
1268
|
+
function _claimRouterCreditsFor(uint256 projectId) internal {
|
|
1269
|
+
uint256 creditCount = TOKENS.creditBalanceOf({holder: address(this), projectId: projectId});
|
|
1270
|
+
if (creditCount == 0) return;
|
|
1271
|
+
|
|
1272
|
+
IJBController controller = IJBController(address(DIRECTORY.controllerOf(projectId)));
|
|
1273
|
+
controller.claimTokensFor({
|
|
1274
|
+
holder: address(this), projectId: projectId, tokenCount: creditCount, beneficiary: address(this)
|
|
1275
|
+
});
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1238
1278
|
/// @notice Convert tokenIn to tokenOut. No-op if same, wrap/unwrap for native/wrapped-native, or swap via Uniswap.
|
|
1239
1279
|
/// @param tokenIn The token to convert from.
|
|
1240
1280
|
/// @param tokenOut The token to convert into.
|
|
@@ -1893,9 +1933,49 @@ contract JBRouterTerminal is
|
|
|
1893
1933
|
view
|
|
1894
1934
|
returns (PoolInfo memory bestPool)
|
|
1895
1935
|
{
|
|
1936
|
+
// Search V3 for the deepest pool and its liquidity.
|
|
1896
1937
|
uint128 bestLiquidity;
|
|
1938
|
+
(bestPool.v3Pool, bestLiquidity) =
|
|
1939
|
+
_discoverV3PoolAndLiquidity({normalizedTokenIn: normalizedTokenIn, normalizedTokenOut: normalizedTokenOut});
|
|
1940
|
+
|
|
1941
|
+
// Search V4, promoting a V4 pool only when it is deeper than the best V3 pool found above.
|
|
1942
|
+
bestPool = _discoverV4Pool({
|
|
1943
|
+
normalizedTokenIn: normalizedTokenIn,
|
|
1944
|
+
normalizedTokenOut: normalizedTokenOut,
|
|
1945
|
+
currentBestLiquidity: bestLiquidity,
|
|
1946
|
+
bestPool: bestPool
|
|
1947
|
+
});
|
|
1948
|
+
}
|
|
1949
|
+
|
|
1950
|
+
/// @notice Find the highest-liquidity Uniswap V3 pool for a token pair across the common fee tiers.
|
|
1951
|
+
/// @param normalizedTokenIn The input token (wrapped if native).
|
|
1952
|
+
/// @param normalizedTokenOut The output token (wrapped if native).
|
|
1953
|
+
/// @return pool The V3 pool with the highest liquidity, or address(0) if none exists.
|
|
1954
|
+
function _discoverV3Pool(
|
|
1955
|
+
address normalizedTokenIn,
|
|
1956
|
+
address normalizedTokenOut
|
|
1957
|
+
)
|
|
1958
|
+
internal
|
|
1959
|
+
view
|
|
1960
|
+
returns (IUniswapV3Pool pool)
|
|
1961
|
+
{
|
|
1962
|
+
(pool,) =
|
|
1963
|
+
_discoverV3PoolAndLiquidity({normalizedTokenIn: normalizedTokenIn, normalizedTokenOut: normalizedTokenOut});
|
|
1964
|
+
}
|
|
1897
1965
|
|
|
1898
|
-
|
|
1966
|
+
/// @notice Find the highest-liquidity Uniswap V3 pool for a token pair along with its liquidity.
|
|
1967
|
+
/// @param normalizedTokenIn The input token (wrapped if native).
|
|
1968
|
+
/// @param normalizedTokenOut The output token (wrapped if native).
|
|
1969
|
+
/// @return pool The V3 pool with the highest liquidity, or address(0) if none exists.
|
|
1970
|
+
/// @return liquidity The liquidity of the returned pool, or 0 if none exists.
|
|
1971
|
+
function _discoverV3PoolAndLiquidity(
|
|
1972
|
+
address normalizedTokenIn,
|
|
1973
|
+
address normalizedTokenOut
|
|
1974
|
+
)
|
|
1975
|
+
internal
|
|
1976
|
+
view
|
|
1977
|
+
returns (IUniswapV3Pool pool, uint128 liquidity)
|
|
1978
|
+
{
|
|
1899
1979
|
for (uint256 i; i < 4;) {
|
|
1900
1980
|
address poolAddr = _getPool({tokenA: normalizedTokenIn, tokenB: normalizedTokenOut, fee: _feeTier(i)});
|
|
1901
1981
|
|
|
@@ -1908,23 +1988,15 @@ contract JBRouterTerminal is
|
|
|
1908
1988
|
|
|
1909
1989
|
uint128 poolLiquidity = IUniswapV3Pool(poolAddr).liquidity();
|
|
1910
1990
|
|
|
1911
|
-
if (poolLiquidity >
|
|
1912
|
-
|
|
1913
|
-
|
|
1991
|
+
if (poolLiquidity > liquidity) {
|
|
1992
|
+
liquidity = poolLiquidity;
|
|
1993
|
+
pool = IUniswapV3Pool(poolAddr);
|
|
1914
1994
|
}
|
|
1915
1995
|
|
|
1916
1996
|
unchecked {
|
|
1917
1997
|
++i;
|
|
1918
1998
|
}
|
|
1919
1999
|
}
|
|
1920
|
-
|
|
1921
|
-
// Search V4.
|
|
1922
|
-
bestPool = _discoverV4Pool({
|
|
1923
|
-
normalizedTokenIn: normalizedTokenIn,
|
|
1924
|
-
normalizedTokenOut: normalizedTokenOut,
|
|
1925
|
-
currentBestLiquidity: bestLiquidity,
|
|
1926
|
-
bestPool: bestPool
|
|
1927
|
-
});
|
|
1928
2000
|
}
|
|
1929
2001
|
|
|
1930
2002
|
/// @notice Search supported V4 pools and update the best pool candidate if a deeper V4 pool exists.
|
|
@@ -2247,9 +2319,12 @@ contract JBRouterTerminal is
|
|
|
2247
2319
|
}
|
|
2248
2320
|
|
|
2249
2321
|
/// @notice Get an automatic V4 quote with dynamic slippage.
|
|
2250
|
-
/// @dev Prefers a hook-provided geomean/TWAP quote when available.
|
|
2251
|
-
///
|
|
2252
|
-
///
|
|
2322
|
+
/// @dev Prefers a hook-provided geomean/TWAP quote when available. For a pool with no such oracle it falls back to
|
|
2323
|
+
/// the pool's spot tick ONLY when `_strictSwapQuote` is false (the `pay` leg — backstopped end-to-end by
|
|
2324
|
+
/// `minReturnedTokens` — and off-chain previews). When `_strictSwapQuote` is true (the `addToBalanceOf` and
|
|
2325
|
+
/// cash-out-swap legs, which have no downstream backstop) it instead reverts
|
|
2326
|
+
/// `JBRouterTerminal_ManipulationResistantQuoteRequired`, forcing the caller to supply a `pay` quote. The spot
|
|
2327
|
+
/// fallback is a bounded-convenience path, not a fully manipulation-resistant one.
|
|
2253
2328
|
///
|
|
2254
2329
|
/// SECURITY NOTE: The spot price read from `poolManager.getSlot0(id)` is an instantaneous value
|
|
2255
2330
|
/// that can be manipulated within the same block (e.g. via sandwich attacks or flash loans). Unlike V3 pools,
|
|
@@ -2274,11 +2349,15 @@ contract JBRouterTerminal is
|
|
|
2274
2349
|
/// scales up to the 88% ceiling via a continuous sigmoid curve.
|
|
2275
2350
|
/// 4. Pool discovery (`_discoverPool`) may select a V3 pool with TWAP if it has more liquidity, avoiding
|
|
2276
2351
|
/// this V4 spot-price path altogether.
|
|
2352
|
+
/// 5. On legs with no downstream `minReturnedTokens` backstop (`addToBalanceOf`, and cash-out routes that
|
|
2353
|
+
/// settle via add-to-balance), `_strictSwapQuote` is set and this spot fallback is REFUSED: the call reverts
|
|
2354
|
+
/// `JBRouterTerminal_ManipulationResistantQuoteRequired` unless the caller supplied a `pay` quote.
|
|
2277
2355
|
///
|
|
2278
2356
|
/// Despite these mitigations, the spot-based fallback does NOT provide full MEV protection. Integrators and
|
|
2279
2357
|
/// front-ends should supply `pay` swap-quote metadata for V4 swaps whenever possible so the user's slippage
|
|
2280
2358
|
/// tolerance reflects a recent, off-chain-verified price. When no external quote can be provided, this fallback
|
|
2281
|
-
///
|
|
2359
|
+
/// remains available as a bounded convenience path ONLY for the backstopped `pay` leg and off-chain previews; the
|
|
2360
|
+
/// un-backstopped `addToBalanceOf` and cash-out-swap legs refuse it (mitigation 5).
|
|
2282
2361
|
/// @param key The V4 pool key describing the pool to quote against.
|
|
2283
2362
|
/// @param normalizedTokenIn The normalized token to sell into the pool.
|
|
2284
2363
|
/// @param normalizedTokenOut The normalized token to buy from the pool.
|
|
@@ -2331,8 +2410,14 @@ contract JBRouterTerminal is
|
|
|
2331
2410
|
} catch {}
|
|
2332
2411
|
}
|
|
2333
2412
|
|
|
2334
|
-
// If no TWAP was available (no hook, or hook doesn't implement observe),
|
|
2413
|
+
// If no TWAP was available (no hook, or hook doesn't implement observe), there is no manipulation-resistant
|
|
2414
|
+
// price source for this pool. For legs with a downstream backstop (pay's `minReturnedTokens`) or for off-chain
|
|
2415
|
+
// previews, fall back to the instantaneous spot tick (a bounded-convenience path). For legs with NO downstream
|
|
2416
|
+
// backstop (`addToBalanceOf`, and cash-out routes that settle via add-to-balance), refuse: a self-referential
|
|
2417
|
+
// spot floor against an attacker-initialized vanilla pool offers no real protection, so require the caller to
|
|
2418
|
+
// supply a `pay` quote instead.
|
|
2335
2419
|
if (!usedTwap) {
|
|
2420
|
+
if (_strictSwapQuote) revert JBRouterTerminal_ManipulationResistantQuoteRequired({poolId: id});
|
|
2336
2421
|
(, tick,,) = _getSlot0(id);
|
|
2337
2422
|
}
|
|
2338
2423
|
|
|
@@ -74,9 +74,11 @@ contract JBRouterTerminalRegistry is IJBRouterTerminalRegistry, JBPermissioned,
|
|
|
74
74
|
/// @notice The `PROJECTS.count()` snapshot at the moment of the last `setDefaultTerminal` call.
|
|
75
75
|
/// Projects with `ID <= defaultTerminalProjectIdThreshold` (i.e. already existing when the most
|
|
76
76
|
/// recent default was set) DO NOT pick up `defaultTerminal` on fall-through; instead they
|
|
77
|
-
/// resolve against the historical entry in `_defaultTerminalHistory` that covers their ID.
|
|
77
|
+
/// resolve against the historical entry in `_defaultTerminalHistory` that covers their ID. The
|
|
78
|
+
/// first default's segment covers every project that already existed when it was set (so those
|
|
79
|
+
/// projects route through it), while later segments pin each outgoing default to its own cohort.
|
|
78
80
|
/// This prevents the registry owner from silently rerouting payments for already-deployed
|
|
79
|
-
/// projects via a default change.
|
|
81
|
+
/// projects via a later default change.
|
|
80
82
|
uint256 public override defaultTerminalProjectIdThreshold;
|
|
81
83
|
|
|
82
84
|
/// @notice Whether the terminal for a given project has been locked against future updates.
|
|
@@ -91,10 +93,11 @@ contract JBRouterTerminalRegistry is IJBRouterTerminalRegistry, JBPermissioned,
|
|
|
91
93
|
// --------------------- internal stored properties ------------------ //
|
|
92
94
|
//*********************************************************************//
|
|
93
95
|
|
|
94
|
-
/// @notice Append-only history of
|
|
95
|
-
/// applies to projectIds in `[<previous threshold> + 1, segment[i].maxProjectId]`. Resolution walks
|
|
96
|
-
/// forward and returns the first segment whose `maxProjectId` covers the queried `projectId`.
|
|
97
|
-
/// the
|
|
96
|
+
/// @notice Append-only history of default-terminal cohorts captured at each `setDefaultTerminal` call. Each
|
|
97
|
+
/// `segment[i]` applies to projectIds in `[<previous threshold> + 1, segment[i].maxProjectId]`. Resolution walks
|
|
98
|
+
/// the array forward and returns the first segment whose `maxProjectId` covers the queried `projectId`. The first
|
|
99
|
+
/// call records the projects that already existed mapped to the new default; later calls push the outgoing default
|
|
100
|
+
/// onto this history before updating `defaultTerminal`.
|
|
98
101
|
DefaultTerminalSegment[] internal _defaultTerminalHistory;
|
|
99
102
|
|
|
100
103
|
/// @notice The terminal explicitly configured for a project before default-terminal fallback is applied.
|
|
@@ -360,10 +363,10 @@ contract JBRouterTerminalRegistry is IJBRouterTerminalRegistry, JBPermissioned,
|
|
|
360
363
|
if (projectId > defaultTerminalProjectIdThreshold) return defaultTerminal;
|
|
361
364
|
|
|
362
365
|
// Older projects walk the history. Each segment covers a half-open range
|
|
363
|
-
// `(minProjectIdExclusive, maxProjectId]
|
|
364
|
-
// was
|
|
365
|
-
//
|
|
366
|
-
//
|
|
366
|
+
// `(minProjectIdExclusive, maxProjectId]`. The first segment covers every project that already existed when the
|
|
367
|
+
// first default was set (mapped to that first default, so they route through it); later segments each cover the
|
|
368
|
+
// cohort issued while their terminal was the active default. A project only resolves to `address(0)` here when
|
|
369
|
+
// no default has ever been set.
|
|
367
370
|
uint256 len = _defaultTerminalHistory.length;
|
|
368
371
|
for (uint256 i; i < len; ++i) {
|
|
369
372
|
DefaultTerminalSegment storage segment = _defaultTerminalHistory[i];
|
|
@@ -388,7 +391,7 @@ contract JBRouterTerminalRegistry is IJBRouterTerminalRegistry, JBPermissioned,
|
|
|
388
391
|
}
|
|
389
392
|
|
|
390
393
|
/// @notice Resolve the effective terminal for call paths that need to forward into a real terminal.
|
|
391
|
-
/// @dev `terminalOf`/`defaultTerminalFor`
|
|
394
|
+
/// @dev `terminalOf`/`defaultTerminalFor` return zero only when no default has ever been set. Transactional
|
|
392
395
|
/// and passthrough view paths must fail before accepting funds or calling address(0).
|
|
393
396
|
/// @param projectId The project to resolve the terminal for.
|
|
394
397
|
/// @return terminal The project-specific terminal or threshold-resolved default.
|
|
@@ -623,12 +626,14 @@ contract JBRouterTerminalRegistry is IJBRouterTerminalRegistry, JBPermissioned,
|
|
|
623
626
|
|
|
624
627
|
/// @notice Change the registry-wide default terminal for projects created AFTER this call.
|
|
625
628
|
/// @dev Only the registry owner can call this. Automatically allowlists the new default.
|
|
626
|
-
///
|
|
627
|
-
///
|
|
628
|
-
///
|
|
629
|
-
/// their
|
|
630
|
-
///
|
|
631
|
-
///
|
|
629
|
+
/// The very first call also maps every project that already existed onto the new default (via a
|
|
630
|
+
/// history segment) so those pre-existing projects — including the canonical fee project (ID 1) —
|
|
631
|
+
/// can route tokens through it. Existing projects (ID <= current `PROJECTS.count()` at call time)
|
|
632
|
+
/// keep their historical default on later changes — the previous `defaultTerminal` is pushed onto
|
|
633
|
+
/// `_defaultTerminalHistory` so fall-through resolution for those projects continues to return what
|
|
634
|
+
/// was current when their cohort was last addressed. This means a later default change never
|
|
635
|
+
/// silently reroutes payments for already-deployed projects that never set an explicit
|
|
636
|
+
/// `_terminalOf` override.
|
|
632
637
|
/// @param terminal The terminal to set as the default for future projects.
|
|
633
638
|
function setDefaultTerminal(IJBTerminal terminal) external onlyOwner {
|
|
634
639
|
if (address(terminal) == address(0)) revert JBRouterTerminalRegistry_ZeroAddress(address(terminal));
|
|
@@ -641,20 +646,20 @@ contract JBRouterTerminalRegistry is IJBRouterTerminalRegistry, JBPermissioned,
|
|
|
641
646
|
// default is what unconfigured (and all-future) projects will resolve to.
|
|
642
647
|
_requireNonCircularTerminalFor({projectId: count, terminal: terminal});
|
|
643
648
|
|
|
644
|
-
//
|
|
645
|
-
//
|
|
646
|
-
//
|
|
647
|
-
//
|
|
648
|
-
// the
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
)
|
|
657
|
-
|
|
649
|
+
// Record a history segment for the cohort whose IDs fall in the half-open range `(prevThreshold,
|
|
650
|
+
// currentCount]`. On the first call ever (`defaultTerminal == 0`) the segment maps the projects that already
|
|
651
|
+
// existed — including the canonical fee project (ID 1) — onto the NEW default so they can route tokens
|
|
652
|
+
// through
|
|
653
|
+
// it instead of resolving to nothing. On every later call the segment instead pins the OUTGOING default to its
|
|
654
|
+
// own cohort, so projects whose IDs were issued while it was active keep resolving to it and a default change
|
|
655
|
+
// never silently reroutes an already-deployed project.
|
|
656
|
+
_defaultTerminalHistory.push(
|
|
657
|
+
DefaultTerminalSegment({
|
|
658
|
+
minProjectIdExclusive: defaultTerminalProjectIdThreshold,
|
|
659
|
+
maxProjectId: count,
|
|
660
|
+
terminal: address(defaultTerminal) != address(0) ? defaultTerminal : terminal
|
|
661
|
+
})
|
|
662
|
+
);
|
|
658
663
|
|
|
659
664
|
defaultTerminal = terminal;
|
|
660
665
|
defaultTerminalProjectIdThreshold = count;
|