@bananapus/router-terminal-v6 0.0.22 → 0.0.23

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bananapus/router-terminal-v6",
3
- "version": "0.0.22",
3
+ "version": "0.0.23",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -62,32 +62,38 @@ contract DeployScript is Script, Sphinx {
62
62
  } else if (block.chainid == 10) {
63
63
  weth = 0x4200000000000000000000000000000000000006;
64
64
  factory = 0x1F98431c8aD98523631AE4a59f267346ea31F984;
65
- poolManager = 0x000000000004444c5dc75cB358380D2e3dE08A90;
65
+ // https://docs.uniswap.org/contracts/v4/deployments
66
+ poolManager = 0x9a13F98Cb987694C9F086b1F5eB990EeA8264Ec3;
66
67
  // Base Mainnet
67
68
  } else if (block.chainid == 8453) {
68
69
  weth = 0x4200000000000000000000000000000000000006;
69
70
  factory = 0x33128a8fC17869897dcE68Ed026d694621f6FDfD;
70
- poolManager = 0x000000000004444c5dc75cB358380D2e3dE08A90;
71
+ // https://docs.uniswap.org/contracts/v4/deployments
72
+ poolManager = 0x498581fF718922c3f8e6A244956aF099B2652b2b;
71
73
  // Optimism Sepolia
72
74
  } else if (block.chainid == 11_155_420) {
73
75
  weth = 0x4200000000000000000000000000000000000006;
74
76
  factory = 0x4752ba5DBc23f44D87826276BF6Fd6b1C372aD24;
75
- poolManager = 0xE03A1074c86CFeDd5C142C4F04F1a1536e203543;
77
+ // https://docs.uniswap.org/contracts/v4/deployments
78
+ poolManager = 0x1390B1276c3C0dd59E0e666d4cF97e30267E72E0;
76
79
  // BASE Sepolia
77
80
  } else if (block.chainid == 84_532) {
78
81
  weth = 0x4200000000000000000000000000000000000006;
79
82
  factory = 0x4752ba5DBc23f44D87826276BF6Fd6b1C372aD24;
80
- poolManager = 0xE03A1074c86CFeDd5C142C4F04F1a1536e203543;
83
+ // https://docs.uniswap.org/contracts/v4/deployments
84
+ poolManager = 0x05E73354cFDd6745C338b50BcFDfA3Aa6fA03408;
81
85
  // Arbitrum Mainnet
82
86
  } else if (block.chainid == 42_161) {
83
87
  weth = 0x82aF49447D8a07e3bd95BD0d56f35241523fBab1;
84
88
  factory = 0x1F98431c8aD98523631AE4a59f267346ea31F984;
85
- poolManager = 0x000000000004444c5dc75cB358380D2e3dE08A90;
89
+ // https://docs.uniswap.org/contracts/v4/deployments
90
+ poolManager = 0x360E68faCcca8cA495c1B759Fd9EEe466db9FB32;
86
91
  // Arbitrum Sepolia
87
92
  } else if (block.chainid == 421_614) {
88
93
  weth = 0x980B62Da83eFf3D4576C647993b0c1D7faf17c73;
89
94
  factory = 0x248AB79Bbb9bC29bB72f7Cd42F17e054Fc40188e;
90
- poolManager = 0xE03A1074c86CFeDd5C142C4F04F1a1536e203543;
95
+ // https://docs.uniswap.org/contracts/v4/deployments
96
+ poolManager = 0xFB3e0C6F74eB1a21CC1Da29aeC80D2Dfe6C9a317;
91
97
  } else {
92
98
  revert("Invalid RPC / no juice contracts deployed on this network");
93
99
  }
@@ -543,9 +543,18 @@ contract JBRouterTerminal is
543
543
 
544
544
  (uint256 sourceProjectId, uint256 creditAmount) = abi.decode(creditData, (uint256, uint256));
545
545
 
546
- // Pull credits from the payer (requires payer to have granted TRANSFER_CREDITS to this contract).
546
+ // Resolve the actual credit holder. When called via an intermediary (e.g. JBRouterTerminalRegistry),
547
+ // _msgSender() is the intermediary — use its originalPayer to find the true holder.
548
+ address holder = _msgSender();
549
+ if (msg.sender.code.length > 0) {
550
+ try IJBPayerTracker(msg.sender).originalPayer() returns (address payer) {
551
+ if (payer != address(0)) holder = payer;
552
+ } catch {}
553
+ }
554
+
555
+ // Pull credits from the holder (requires holder to have granted TRANSFER_CREDITS to this contract).
547
556
  TOKENS.transferCreditsFrom({
548
- holder: _msgSender(), projectId: sourceProjectId, recipient: address(this), count: creditAmount
557
+ holder: holder, projectId: sourceProjectId, recipient: address(this), count: creditAmount
549
558
  });
550
559
 
551
560
  return creditAmount;
@@ -678,6 +687,8 @@ contract JBRouterTerminal is
678
687
  // This propagates slippage protection through multi-hop cashouts instead of dropping it.
679
688
  if (minTokensReclaimed != 0 && previousExpectedAmount != 0) {
680
689
  minTokensReclaimed = mulDiv(minTokensReclaimed, amount, previousExpectedAmount);
690
+ // minTokensReclaimed may round to 0 here — that is intentional.
691
+ // A 0 minimum is valid and means no slippage protection for this hop.
681
692
  }
682
693
 
683
694
  // Update for next iteration.
@@ -1400,26 +1411,33 @@ contract JBRouterTerminal is
1400
1411
  returns (uint256 minAmountOut)
1401
1412
  {
1402
1413
  PoolId id = key.toId();
1414
+
1415
+ // The tick used for quoting — prefer TWAP over spot for MEV resistance.
1403
1416
  int24 tick;
1417
+
1418
+ // Track whether the oracle hook provided a TWAP so we know whether to fall back to spot.
1404
1419
  bool usedTwap;
1405
1420
 
1406
- // Try to use TWAP from the pool's oracle hook (if available) for manipulation resistance.
1421
+ // If the pool has a hook, try querying it as a geomean oracle (e.g., JBUniswapV4Hook implements this).
1407
1422
  if (address(key.hooks) != address(0)) {
1423
+ // Build the two-element lookback array: [_TWAP_WINDOW seconds ago, now].
1408
1424
  uint32[] memory secondsAgos = new uint32[](2);
1409
- secondsAgos[0] = _TWAP_WINDOW;
1410
- secondsAgos[1] = 0;
1425
+ secondsAgos[0] = _TWAP_WINDOW; // Start of the window (30 seconds ago).
1426
+ secondsAgos[1] = 0; // End of the window (current block).
1427
+
1428
+ // Ask the hook for cumulative tick data over the window. Silently catch if it doesn't support it.
1411
1429
  // slither-disable-next-line unused-return
1412
1430
  try IGeomeanOracle(address(key.hooks)).observe(key, secondsAgos) returns (
1413
1431
  int56[] memory tickCumulatives, uint160[] memory
1414
1432
  ) {
1415
- // Compute the arithmetic mean tick from the TWAP window.
1433
+ // Derive the arithmetic mean tick: (cumulative_now - cumulative_start) / elapsed_seconds.
1416
1434
  // forge-lint: disable-next-line(unsafe-typecast)
1417
1435
  tick = int24((tickCumulatives[1] - tickCumulatives[0]) / int56(int32(_TWAP_WINDOW)));
1418
1436
  usedTwap = true;
1419
1437
  } catch {}
1420
1438
  }
1421
1439
 
1422
- // Fall back to spot price if TWAP was not available.
1440
+ // If no TWAP was available (no hook, or hook doesn't implement observe), use the instantaneous spot tick.
1423
1441
  if (!usedTwap) {
1424
1442
  // slither-disable-next-line unused-return
1425
1443
  (, tick,,) = _getSlot0(id);
@@ -1509,6 +1527,12 @@ contract JBRouterTerminal is
1509
1527
 
1510
1528
  if (exists) {
1511
1529
  (minAmountOut) = abi.decode(quote, (uint256));
1530
+ }
1531
+
1532
+ // Treat a decoded value of 0 the same as "not provided" so that a stale or default-zero quote
1533
+ // does not silently disable slippage protection. Fall through to automatic quoting.
1534
+ if (minAmountOut != 0) {
1535
+ // User-provided quote is valid; skip automatic quoting.
1512
1536
  } else if (pool.isV4) {
1513
1537
  minAmountOut = _getV4SpotQuote({
1514
1538
  key: pool.v4Key,
@@ -1601,6 +1625,8 @@ contract JBRouterTerminal is
1601
1625
  // Scale the minimum proportionally for the next step based on the actual cashout ratio.
1602
1626
  if (minTokensReclaimed != 0 && previousExpectedAmount != 0) {
1603
1627
  minTokensReclaimed = mulDiv(minTokensReclaimed, amount, previousExpectedAmount);
1628
+ // minTokensReclaimed may round to 0 here — that is intentional.
1629
+ // A 0 minimum is valid and means no slippage protection for this hop.
1604
1630
  }
1605
1631
 
1606
1632
  // Continue previewing from the token reclaimed in this hop.