@panoptic-eng/sdk 1.0.68 → 1.0.70

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.
Files changed (34) hide show
  1. package/dist/cow/index.js +47 -2
  2. package/dist/cow/index.js.map +1 -1
  3. package/dist/{cow-CGgPXhY6.js → cow-sGuN0eas.js} +1 -1
  4. package/dist/{greeks-CGtgLUyh.js → greeks-DNPP24KL.js} +115 -9
  5. package/dist/index.d.ts +67 -25
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +161 -62
  8. package/dist/index.js.map +1 -1
  9. package/dist/{irm-BDxwCFDm.js → irm-6RzXXa1_.js} +927 -3
  10. package/dist/panoptic/v2/greeks/index.d.ts +7 -4
  11. package/dist/panoptic/v2/greeks/index.d.ts.map +1 -1
  12. package/dist/panoptic/v2/greeks/index.js +74 -7
  13. package/dist/panoptic/v2/greeks/index.js.map +1 -1
  14. package/dist/panoptic/v2/index.d.ts +244 -6
  15. package/dist/panoptic/v2/index.d.ts.map +1 -1
  16. package/dist/panoptic/v2/index.js +2554 -464
  17. package/dist/panoptic/v2/index.js.map +1 -1
  18. package/dist/panoptic/v2/react-public.d.ts +258 -9
  19. package/dist/panoptic/v2/react-public.d.ts.map +1 -1
  20. package/dist/panoptic/v2/react-public.js +4655 -2909
  21. package/dist/panoptic/v2/react-public.js.map +1 -1
  22. package/dist/{providers-DubVMHWU.js → providers-CbcNt27n.js} +2 -1
  23. package/dist/{rates-BHS-Qiu7.js → rates-DDK4ZimJ.js} +40 -4
  24. package/dist/{router-BW6TqwOO.js → router-njWsLvZc.js} +969 -39
  25. package/dist/rpc/index.d.ts +12 -1
  26. package/dist/rpc/index.d.ts.map +1 -1
  27. package/dist/rpc/index.js +65 -5
  28. package/dist/rpc/index.js.map +1 -1
  29. package/dist/uniswap/index.js +53 -13
  30. package/dist/uniswap/index.js.map +1 -1
  31. package/dist/{v2-Dgh6JxG_.js → v2-BrG4T_Gh.js} +681 -32
  32. package/dist/{writes-CG612Vuk.js → writes-CI6Haer-.js} +34 -3
  33. package/package.json +1 -1
  34. package/dist/router-IDtCENST.js +0 -942
@@ -1,9 +1,9 @@
1
- import { getAccountCollateral, getNetLiquidationValue, panopticQueryAbi, readBlockAndAggregate, requireReturnData } from "./irm-BDxwCFDm.js";
2
- import { AccountInsolventError, ChunkLimitError, DEFAULT_VEGOID, InvalidHistoryRangeError, MulticallResultMissingError, NotEnoughTokensError, PanopticError, PanopticValidationError, SafeModeError, StaleDataError, StorageDataNotFoundError, UnhealthyPoolError, WrongUniswapPoolError, applyMintBufferPerToken, availableToBorrow, collateralTrackerV2Abi, decodeLeftRightSigned, decodeLeftRightUnsigned, decodePositionBalance, getBlockMeta, getClosedPositionsKey, getPendingPositionsKey, getPool, getPoolMetaKey, getPoolMetadata, getPositionMetaKey, getPositionsKey, getTrackedChunksKey, getTrackedPositionIds, isPanopticErrorType, jsonSerializer, mintableAfterBuffer, panopticFactoryV3Abi, panopticFactoryV4Abi, panopticGuardianAbi, panopticLiquidatorAbi, panopticPoolV2Abi, panopticQueryAbi$1, parsePanopticError, riskEngineAbi, riskEngineMulticall, semiFungiblePositionManagerV3Abi, semiFungiblePositionManagerV4Abi, stateViewAbi, uniswapV3PoolAbi } from "./rates-BHS-Qiu7.js";
3
- import { MAX_TICK, MAX_TRACKED_CHUNKS, MIN_TICK, NO_LOWER_LIQUIDATION_TICK, NO_UPPER_LIQUIDATION_TICK, WAD, calculatePositionDelta, calculatePositionDeltaDebtOnly, calculatePositionGamma, calculatePositionValue, priceToTick, sqrtPriceX96ToTick, tickLimits, tickToPriceDecimalScaled, tickToSqrtPriceX96, toVaultFrameAtTick } from "./greeks-CGtgLUyh.js";
4
- import { addLegToTokenId, buildBatchDispatchArgs, buildUniqueCredit, buildUniqueLoan, countLegs, createTokenIdBuilder, decodeAllLegs, decodeTokenId, getCurrentPositionSizes, getPoolTokensForSimulation, loadCheckpoint, resolveTokenIndex, simulateDispatch, simulateOpenPosition, simulateWithTokenFlow } from "./writes-CG612Vuk.js";
5
- import { feesFromFeeGrowthDelta, getLpPositionFunding, getMaxLpPositionSize, getPriceHistory, preparePositionGamma } from "./router-BW6TqwOO.js";
6
- import { ContractFunctionExecutionError, decodeFunctionResult, encodeAbiParameters, encodeFunctionData, getAddress, hexToString, keccak256, parseAbi, parseEventLogs, trim, zeroAddress } from "viem";
1
+ import { UniswapV3PoolAbi, getAccountCollateral, getNetLiquidationValue, panopticQueryAbi, readBlockAndAggregate, requireReturnData } from "./irm-6RzXXa1_.js";
2
+ import { AccountInsolventError, ChunkLimitError, DEFAULT_VEGOID, InvalidHistoryRangeError, MulticallResultMissingError, NotEnoughTokensError, PanopticError, PanopticValidationError, SafeModeError, StaleDataError, StorageDataNotFoundError, UnhealthyPoolError, WrongUniswapPoolError, applyMintBufferPerToken, availableToBorrow, collateralTrackerV2Abi, decodeLeftRightSigned, decodeLeftRightUnsigned, decodePositionBalance, getBlockMeta, getClosedPositionsKey, getPendingPositionsKey, getPool, getPoolMetaKey, getPoolMetadata, getPositionMetaKey, getPositionsKey, getTrackedChunksKey, getTrackedPositionIds, isPanopticErrorType, jsonSerializer, mintableAfterBuffer, panopticFactoryV3Abi, panopticFactoryV4Abi, panopticGuardianAbi, panopticLiquidatorAbi, panopticPoolV2Abi, panopticQueryAbi$1, parsePanopticError, riskEngineAbi, riskEngineMulticall, semiFungiblePositionManagerV3Abi, semiFungiblePositionManagerV4Abi, stateViewAbi, uniswapV3PoolAbi } from "./rates-DDK4ZimJ.js";
3
+ import { MAX_TICK, MAX_TRACKED_CHUNKS, MIN_TICK, NO_LOWER_LIQUIDATION_TICK, NO_UPPER_LIQUIDATION_TICK, WAD, calculatePositionDelta, calculatePositionDeltaDebtOnly, calculatePositionGamma, calculatePositionValue, getLegLiquidityRange, priceToTick, sqrtPriceX96ToTick, tickLimits, tickToPriceDecimalScaled, tickToSqrtPriceX96, toVaultFrameAtTick } from "./greeks-DNPP24KL.js";
4
+ import { addLegToTokenId, buildBatchDispatchArgs, buildProtectedSettlePlan, buildSettleSequenceCalls, buildUniqueCredit, buildUniqueLoan, countLegs, createTokenIdBuilder, decodeAllLegs, decodeTokenId, getCurrentPositionSizes, getPoolTokensForSimulation, loadCheckpoint, resolveTokenIndex, simulateDispatch, simulateOpenPosition, simulateWithTokenFlow } from "./writes-CI6Haer-.js";
5
+ import { feesFromFeeGrowthDelta, getLpPositionFunding, getMaxLpPositionSize, getPriceHistory, preparePositionGamma, quoteSwapExactOutViaRouter, quoteSwapExactOutViaV3Router } from "./router-njWsLvZc.js";
6
+ import { ContractFunctionExecutionError, decodeFunctionResult, encodeAbiParameters, encodeFunctionData, getAddress, hexToString, isAddress, isAddressEqual, isHex, keccak256, parseAbi, parseEventLogs, trim, zeroAddress } from "viem";
7
7
  import { multicall } from "viem/actions";
8
8
  import Decimal from "decimal.js";
9
9
 
@@ -656,13 +656,15 @@ function getNotEnoughTokensError(error) {
656
656
  let current = error;
657
657
  while (current !== void 0 && current !== null && !visited.has(current)) {
658
658
  visited.add(current);
659
- const parsed = current instanceof NotEnoughTokensError ? current : parsePanopticError(current)?.error;
659
+ const decoded = parsePanopticError(current);
660
+ const parsed = current instanceof NotEnoughTokensError ? current : decoded?.error;
660
661
  const candidate = parsed instanceof NotEnoughTokensError ? parsed : null;
661
662
  if (candidate !== null) {
662
663
  const args = candidate;
663
- if (args.tokenAddress !== void 0 && args.assetsRequested !== void 0 && args.assetBalance !== void 0) return candidate;
664
+ if (typeof args.tokenAddress === "string" && isAddress(args.tokenAddress) && typeof args.assetsRequested === "bigint" && typeof args.assetBalance === "bigint") return candidate;
664
665
  }
665
- current = current instanceof Error && "cause" in current ? current.cause : void 0;
666
+ const innerData = decoded?.errorName === "ExecuteError" ? decoded.args[1] : void 0;
667
+ current = typeof innerData === "string" && isHex(innerData) ? { data: innerData } : current instanceof Error && "cause" in current ? current.cause : void 0;
666
668
  }
667
669
  return null;
668
670
  }
@@ -670,7 +672,7 @@ function maximumAmountIn(estimatedAmountIn, slippageBps) {
670
672
  if (slippageBps < 0n) throw new PanopticError("slippageBps must be non-negative");
671
673
  return (estimatedAmountIn * (BPS_DENOMINATOR$2 + slippageBps) + BPS_DENOMINATOR$2 - 1n) / BPS_DENOMINATOR$2;
672
674
  }
673
- function ceilDiv$2(numerator, denominator) {
675
+ function ceilDiv$3(numerator, denominator) {
674
676
  return (numerator + denominator - 1n) / denominator;
675
677
  }
676
678
  function getInputAmount(tokenFlow, tokenInIndex) {
@@ -775,7 +777,7 @@ async function quoteTokenShortfallRecovery(params) {
775
777
  const estimatedAmountIn = getInputAmount(swapSimulation.tokenFlow, tokenInIndex);
776
778
  const estimatedAmountOut = getOutputAmount(swapSimulation.tokenFlow, tokenOutIndex);
777
779
  if (estimatedAmountOut < requiredOutput) {
778
- creditInput = estimatedAmountOut > 0n ? ceilDiv$2(creditInput * requiredOutput, estimatedAmountOut) + 1n : creditInput * 2n;
780
+ creditInput = estimatedAmountOut > 0n ? ceilDiv$3(creditInput * requiredOutput, estimatedAmountOut) + 1n : creditInput * 2n;
779
781
  continue;
780
782
  }
781
783
  const recoveredDispatch = buildPrefixedExactInputRecoveryDispatch({
@@ -830,7 +832,7 @@ async function quoteTokenShortfallRecovery(params) {
830
832
  };
831
833
  const residual = remainingShortfall.assetsRequested - remainingShortfall.assetBalance;
832
834
  requiredOutput += residual > 0n ? residual : requiredOutput;
833
- creditInput = ceilDiv$2(creditInput * requiredOutput, estimatedAmountOut) + 1n;
835
+ creditInput = ceilDiv$3(creditInput * requiredOutput, estimatedAmountOut) + 1n;
834
836
  }
835
837
  return {
836
838
  available: false,
@@ -885,7 +887,7 @@ async function quoteTokenShortfallRecovery(params) {
885
887
  const maxAmountIn = maximumAmountIn(estimatedAmountIn, params.slippageBps);
886
888
  const swapOutput = getOutputAmount(swapSimulation.tokenFlow, tokenOutIndex);
887
889
  if (swapOutput < amountOut) {
888
- creditOutSize = swapOutput > 0n ? ceilDiv$2(creditOutSize * amountOut, swapOutput) + 1n : creditOutSize * 2n;
890
+ creditOutSize = swapOutput > 0n ? ceilDiv$3(creditOutSize * amountOut, swapOutput) + 1n : creditOutSize * 2n;
889
891
  continue;
890
892
  }
891
893
  const recoverySimulation = await simulateDispatch({
@@ -940,7 +942,7 @@ async function quoteTokenShortfallRecovery(params) {
940
942
  if (decodedShortfall >= shortfallError.assetsRequested - shortfallError.assetBalance) return quotePrefixedExactInput(amountOut);
941
943
  const additionalAmountOut = decodedShortfall > 0n ? decodedShortfall : amountOut;
942
944
  amountOut += additionalAmountOut;
943
- creditOutSize = swapOutput > 0n ? ceilDiv$2(creditOutSize * amountOut, swapOutput) + 1n : creditOutSize * 2n;
945
+ creditOutSize = swapOutput > 0n ? ceilDiv$3(creditOutSize * amountOut, swapOutput) + 1n : creditOutSize * 2n;
944
946
  }
945
947
  return quotePrefixedExactInput(shortfallError.assetsRequested - shortfallError.assetBalance);
946
948
  }
@@ -2248,7 +2250,7 @@ async function getItmAmounts(params) {
2248
2250
  * high-decimal amount can't masquerade as real flow.
2249
2251
  */
2250
2252
  const FLOW_NEUTRAL_DUST_THRESHOLD = 1000n;
2251
- const Q192$2 = 1n << 192n;
2253
+ const Q192$3 = 1n << 192n;
2252
2254
  const POOL_ID_MASK$1 = (1n << 64n) - 1n;
2253
2255
  /**
2254
2256
  * Floor integer square root for bigints (Newton's method). Exact for all
@@ -2284,7 +2286,7 @@ function buildNeutralLeg(tokenIndex, signedAmount, positionSize) {
2284
2286
  const absAmount = signedAmount < 0n ? -signedAmount : signedAmount;
2285
2287
  const legAsset = tokenIndex === 0n ? 1n : 0n;
2286
2288
  const legIsLong = signedAmount < 0n;
2287
- const sqrtKrawX96 = isqrt(absAmount * Q192$2 / positionSize);
2289
+ const sqrtKrawX96 = isqrt(absAmount * Q192$3 / positionSize);
2288
2290
  let signedTick;
2289
2291
  try {
2290
2292
  signedTick = sqrtPriceX96ToTick(sqrtKrawX96);
@@ -2542,7 +2544,7 @@ async function createFlowNeutralTokenId(params) {
2542
2544
  const sqrtPX96 = tickToSqrtPriceX96(tickBefore);
2543
2545
  const valueAbs = (index, amount) => {
2544
2546
  const abs$1 = amount < 0n ? -amount : amount;
2545
- return index === 0n ? abs$1 * sqrtPX96 * sqrtPX96 / Q192$2 : abs$1;
2547
+ return index === 0n ? abs$1 * sqrtPX96 * sqrtPX96 / Q192$3 : abs$1;
2546
2548
  };
2547
2549
  const flowFor = (index) => index === 0n ? credit.creditAmount0 : credit.creditAmount1;
2548
2550
  if (queryAddress !== void 0) {
@@ -3515,7 +3517,7 @@ async function getPoolLiquidities(params) {
3515
3517
 
3516
3518
  //#endregion
3517
3519
  //#region src/panoptic/v2/reads/accountGreeks.ts
3518
- const Q192$1 = 1n << 192n;
3520
+ const Q192$2 = 1n << 192n;
3519
3521
  /**
3520
3522
  * Calculate account-level greeks using stored position data.
3521
3523
  *
@@ -3636,8 +3638,8 @@ async function getAccountGreeks(params) {
3636
3638
  const assetBal = isAssetToken0 ? collateral.token0.assets : collateral.token1.assets;
3637
3639
  const otherBal = isAssetToken0 ? collateral.token1.assets : collateral.token0.assets;
3638
3640
  const sqrtPriceX96 = tickToSqrtPriceX96(BigInt(currentTick));
3639
- if (isAssetToken0) collateralValue = assetBal * sqrtPriceX96 * sqrtPriceX96 / Q192$1 + otherBal;
3640
- else collateralValue = assetBal * Q192$1 / (sqrtPriceX96 * sqrtPriceX96) + otherBal;
3641
+ if (isAssetToken0) collateralValue = assetBal * sqrtPriceX96 * sqrtPriceX96 / Q192$2 + otherBal;
3642
+ else collateralValue = assetBal * Q192$2 / (sqrtPriceX96 * sqrtPriceX96) + otherBal;
3641
3643
  collateralDelta = assetBal;
3642
3644
  }
3643
3645
  return {
@@ -3677,7 +3679,7 @@ function calculateAccountGreeksPure(params) {
3677
3679
  const totalDelta$1 = [];
3678
3680
  for (let i = 0; i < n; i++) {
3679
3681
  const sqrtPriceX96 = tickToSqrtPriceX96(atTicks[i]);
3680
- const assetValueInNumeraire = isAssetToken0 ? assetBal * sqrtPriceX96 * sqrtPriceX96 / Q192$1 : assetBal * Q192$1 / (sqrtPriceX96 * sqrtPriceX96);
3682
+ const assetValueInNumeraire = isAssetToken0 ? assetBal * sqrtPriceX96 * sqrtPriceX96 / Q192$2 : assetBal * Q192$2 / (sqrtPriceX96 * sqrtPriceX96);
3681
3683
  totalValue$1.push(assetValueInNumeraire + otherBal);
3682
3684
  totalDelta$1.push(assetBal);
3683
3685
  }
@@ -3727,7 +3729,7 @@ function calculateAccountGreeksPure(params) {
3727
3729
  }
3728
3730
  for (let i = 0; i < n; i++) {
3729
3731
  const sqrtPriceX96 = tickToSqrtPriceX96(atTicks[i]);
3730
- const assetValueInNumeraire = isAssetToken0 ? assetBal * sqrtPriceX96 * sqrtPriceX96 / Q192$1 : assetBal * Q192$1 / (sqrtPriceX96 * sqrtPriceX96);
3732
+ const assetValueInNumeraire = isAssetToken0 ? assetBal * sqrtPriceX96 * sqrtPriceX96 / Q192$2 : assetBal * Q192$2 / (sqrtPriceX96 * sqrtPriceX96);
3731
3733
  totalValue[i] += assetValueInNumeraire + otherBal;
3732
3734
  totalDelta[i] += assetBal;
3733
3735
  }
@@ -5321,10 +5323,10 @@ async function processClosedPositions(client, queryAddress, positions) {
5321
5323
  //#endregion
5322
5324
  //#region src/panoptic/v2/reads/hedgeCapacity.ts
5323
5325
  const SCALE$1 = 10000000n;
5324
- const FEE_SCALE = 10000n;
5325
- const Q192 = 1n << 192n;
5326
+ const FEE_SCALE$1 = 10000n;
5327
+ const Q192$1 = 1n << 192n;
5326
5328
  const MASK128$1 = (1n << 128n) - 1n;
5327
- const ceilDiv$1 = (value, divisor) => (value + divisor - 1n) / divisor;
5329
+ const ceilDiv$2 = (value, divisor) => (value + divisor - 1n) / divisor;
5328
5330
  function accountHedgeDelta({ positionIds, balances, collateralDelta, tick, tickSpacing, assetIndex }) {
5329
5331
  return positionIds.reduce((sum$1, id, index) => {
5330
5332
  const balance = balances[index];
@@ -5344,8 +5346,8 @@ function accountHedgeDelta({ positionIds, balances, collateralDelta, tick, tickS
5344
5346
  function buildCapacityHedge({ delta, assetIndex, tick, tickSpacing, poolId, notionalFee, swapFee }) {
5345
5347
  const borrowToken = delta >= 0n ? assetIndex : assetIndex === 0n ? 1n : 0n;
5346
5348
  const receiveToken = borrowToken === 0n ? 1n : 0n;
5347
- const amount = delta >= 0n ? delta : ceilDiv$1((toVaultFrameAtTick(-delta, assetIndex, borrowToken, tick) + 1n) * 1000000n * FEE_SCALE, (1000000n - swapFee) * (FEE_SCALE - notionalFee));
5348
- const commission = ceilDiv$1(amount * notionalFee, FEE_SCALE);
5349
+ const amount = delta >= 0n ? delta : ceilDiv$2((toVaultFrameAtTick(-delta, assetIndex, borrowToken, tick) + 1n) * 1000000n * FEE_SCALE$1, (1000000n - swapFee) * (FEE_SCALE$1 - notionalFee));
5350
+ const commission = ceilDiv$2(amount * notionalFee, FEE_SCALE$1);
5349
5351
  const proceeds = toVaultFrameAtTick((amount - commission) * (1000000n - swapFee) / 1000000n, borrowToken, receiveToken, tick);
5350
5352
  const tokenId = addLegToTokenId(poolId, {
5351
5353
  index: 0n,
@@ -5377,7 +5379,7 @@ function hedgeMargin({ tokenData, balanceChanges, crossRatios, tick, buffer }) {
5377
5379
  const surplus1 = balances[1] > required1 ? (balances[1] - required1) * crossRatios[1] / SCALE$1 : 0n;
5378
5380
  const sqrt = tickToSqrtPriceX96(tick);
5379
5381
  const price = sqrt * sqrt;
5380
- const pairs = sqrt < 1n << 96n ? [[balances[0] + surplus1 * Q192 / price, required0], [balances[1] * Q192 / price + surplus0, ceilDiv$1(required1 * Q192, price)]] : [[balances[0] * price / Q192 + surplus1, ceilDiv$1(required0 * price, Q192)], [balances[1] + surplus0 * price / Q192, required1]];
5382
+ const pairs = sqrt < 1n << 96n ? [[balances[0] + surplus1 * Q192$1 / price, required0], [balances[1] * Q192$1 / price + surplus0, ceilDiv$2(required1 * Q192$1, price)]] : [[balances[0] * price / Q192$1 + surplus1, ceilDiv$2(required0 * price, Q192$1)], [balances[1] + surplus0 * price / Q192$1, required1]];
5381
5383
  const headroom = pairs.map(([balance, required]) => balance - required);
5382
5384
  return {
5383
5385
  solvent: balances.every((balance) => balance >= 0n) && headroom.every((value) => value >= 0n),
@@ -5590,7 +5592,7 @@ async function getHedgeLimits({ client, poolAddress, account, positionIds, asset
5590
5592
  if (hedge.amount > 0n && BigInt(legCount) >= maxLegs) return unavailable("leg-limit");
5591
5593
  const totalAssets = hedge.borrowToken === 0n ? total0 : total1;
5592
5594
  if (hedge.amount > 0n && (hedge.amount > MAX_SIZE || hedge.amount > borrowCapacity[Number(hedge.borrowToken)] || totalAssets === 0n)) return unavailable("capacity");
5593
- const projectedUtilization = currentUtilizations.map((util, i) => hedge.amount > 0n && BigInt(i) === hedge.borrowToken ? ceilDiv$1(util * totalAssets + hedge.amount * 10000n, totalAssets) : util);
5595
+ const projectedUtilization = currentUtilizations.map((util, i) => hedge.amount > 0n && BigInt(i) === hedge.borrowToken ? ceilDiv$2(util * totalAssets + hedge.amount * 10000n, totalAssets) : util);
5594
5596
  if (hedge.amount > 0n && projectedUtilization.some((util) => util > 10000n)) return unavailable("capacity");
5595
5597
  const global = projectedUtilization.map((util, i) => util > globalUtilizations[i] ? util : globalUtilizations[i]);
5596
5598
  const syntheticBalance = hedge.amount | projectedUtilization[0] << 128n | projectedUtilization[1] << 144n;
@@ -5682,6 +5684,90 @@ async function getHedgeLimits({ client, poolAddress, account, positionIds, asset
5682
5684
  };
5683
5685
  }
5684
5686
 
5687
+ //#endregion
5688
+ //#region src/panoptic/v2/reads/positionMintFunding.ts
5689
+ const Q96 = 1n << 96n;
5690
+ const Q192 = 1n << 192n;
5691
+ const BPS = 10000n;
5692
+ const FEE_SCALE = 1000000n;
5693
+ const max = (a, b) => a > b ? a : b;
5694
+ const ceilDiv$1 = (n, d) => n > 0n ? (n + d - 1n) / d : n / d;
5695
+ /**
5696
+ * Estimate deposit-independent mint funding, including settlement and commission.
5697
+ * Swaps use spot conversion with fees and slippage allowance; execution still needs a live quote.
5698
+ * Received tokens cover post-mint margin but cannot fund the other tracker's upfront payment.
5699
+ * Each requiredValue is the starting balance when funding entirely in that token.
5700
+ */
5701
+ function getPositionMintFunding(params) {
5702
+ const { tokenId, positionSize, sqrtPriceX96, valuationSqrtPriceX96, swapAtMint, commissionRate, swapFee, slippageBps, required0, required1 } = params;
5703
+ if (positionSize < 0n || sqrtPriceX96 <= 0n || valuationSqrtPriceX96 <= 0n || commissionRate < 0n || swapFee < 0n || swapFee >= FEE_SCALE || slippageBps < 0n || slippageBps >= BPS || required0 < 0n || required1 < 0n) throw new RangeError("Invalid mint funding inputs");
5704
+ const { legs, tickSpacing } = decodeTokenId(tokenId);
5705
+ const firstLeg = legs[0];
5706
+ if (!firstLeg || tickSpacing <= 0n) throw new RangeError("Invalid position");
5707
+ let paid0 = 0n;
5708
+ let paid1 = 0n;
5709
+ let itm0 = 0n;
5710
+ let itm1 = 0n;
5711
+ let notional0 = 0n;
5712
+ let notional1 = 0n;
5713
+ for (const leg of legs) {
5714
+ const { sqrtLower, sqrtUpper, liquidity } = getLegLiquidityRange(leg.width === 0n ? {
5715
+ ...leg,
5716
+ width: 2n
5717
+ } : leg, positionSize, tickSpacing);
5718
+ const round = !leg.isLong || leg.width === 0n ? ceilDiv$1 : (n, d) => n / d;
5719
+ const moved0 = round((liquidity << 96n) * (sqrtUpper - sqrtLower), sqrtUpper * sqrtLower);
5720
+ const moved1 = round(liquidity * (sqrtUpper - sqrtLower), Q96);
5721
+ const sign = leg.isLong ? -1n : 1n;
5722
+ let amm0 = 0n;
5723
+ let amm1 = 0n;
5724
+ if (leg.width > 0n) {
5725
+ const price = max(sqrtLower, sqrtPriceX96 < sqrtUpper ? sqrtPriceX96 : sqrtUpper);
5726
+ amm0 = sign * round((liquidity << 96n) * (sqrtUpper - price), sqrtUpper * price);
5727
+ amm1 = sign * round(liquidity * (price - sqrtLower), Q96);
5728
+ itm0 += leg.tokenType === 1n ? amm0 : 0n;
5729
+ itm1 += leg.tokenType === 0n ? amm1 : 0n;
5730
+ } else {
5731
+ itm0 -= leg.tokenType === 0n ? sign * moved0 : 0n;
5732
+ itm1 -= leg.tokenType === 1n ? sign * moved1 : 0n;
5733
+ }
5734
+ paid0 += amm0 - (leg.tokenType === 0n ? sign * moved0 : 0n);
5735
+ paid1 += amm1 - (leg.tokenType === 1n ? sign * moved1 : 0n);
5736
+ notional0 += leg.tokenType === 0n ? moved0 : 0n;
5737
+ notional1 += leg.tokenType === 1n ? moved1 : 0n;
5738
+ }
5739
+ if (swapAtMint && (itm0 !== 0n || itm1 !== 0n)) {
5740
+ const swapToken0 = itm0 !== 0n && (itm1 === 0n || firstLeg.asset === 0n);
5741
+ const priceSquared$1 = sqrtPriceX96 * sqrtPriceX96;
5742
+ const itm = swapToken0 ? itm0 : itm1;
5743
+ const numerator = swapToken0 ? priceSquared$1 : Q192;
5744
+ const denominator = swapToken0 ? Q192 : priceSquared$1;
5745
+ const retained$1 = (FEE_SCALE - swapFee) * (BPS - slippageBps);
5746
+ const scale$1 = FEE_SCALE * BPS;
5747
+ const payment = itm > 0n ? ceilDiv$1(itm * numerator * scale$1, denominator * retained$1) : itm * numerator * retained$1 / (denominator * scale$1);
5748
+ paid0 += swapToken0 ? -itm : payment;
5749
+ paid1 += swapToken0 ? payment : -itm;
5750
+ }
5751
+ paid0 += ceilDiv$1(notional0 * commissionRate, BPS);
5752
+ paid1 += ceilDiv$1(notional1 * commissionRate, BPS);
5753
+ const priceSquared = valuationSqrtPriceX96 * valuationSqrtPriceX96;
5754
+ const inToken0 = (amount0, amount1) => amount0 + ceilDiv$1(amount1 * Q192, priceSquared);
5755
+ const inToken1 = (amount0, amount1) => amount1 + ceilDiv$1(amount0 * priceSquared, Q192);
5756
+ const retained = (FEE_SCALE - swapFee) * (BPS - slippageBps);
5757
+ const scale = FEE_SCALE * BPS;
5758
+ const spotSquared = sqrtPriceX96 * sqrtPriceX96;
5759
+ const upfront0 = max(paid0, 0n) + ceilDiv$1(max(paid1, 0n) * Q192 * scale, spotSquared * retained);
5760
+ const upfront1 = max(paid1, 0n) + ceilDiv$1(max(paid0, 0n) * spotSquared * scale, Q192 * retained);
5761
+ const requiredValue0 = upfront0 + max(0n, inToken0(required0 - max(-paid0, 0n), required1 - max(-paid1, 0n)));
5762
+ const requiredValue1 = upfront1 + max(0n, inToken1(required0 - max(-paid0, 0n), required1 - max(-paid1, 0n)));
5763
+ return {
5764
+ paid0,
5765
+ paid1,
5766
+ requiredValue0,
5767
+ requiredValue1
5768
+ };
5769
+ }
5770
+
5685
5771
  //#endregion
5686
5772
  //#region src/panoptic/v2/sync/getSyncStatus.ts
5687
5773
  /**
@@ -6503,6 +6589,108 @@ async function simulateClosePosition(params) {
6503
6589
  }
6504
6590
  }
6505
6591
 
6592
+ //#endregion
6593
+ //#region src/panoptic/v2/simulations/simulateClosePremiumProtection.ts
6594
+ /** Estimate unfunded short premium after buyer settlement and collection of chunk fees. */
6595
+ async function simulateClosePremiumProtection(params) {
6596
+ const { client, poolAddress, account, positionIdList, closingTokenIds, targets } = params;
6597
+ let metaPromise;
6598
+ try {
6599
+ const blockNumber = params.blockNumber ?? await client.getBlockNumber();
6600
+ metaPromise = getBlockMeta({
6601
+ client,
6602
+ blockNumber
6603
+ });
6604
+ metaPromise.catch(() => {});
6605
+ if (closingTokenIds.length === 0 || closingTokenIds.some((id) => !positionIdList.includes(id))) throw new PanopticError("Premium protection requires currently held positions to close");
6606
+ const positionSizes = await getCurrentPositionSizes({
6607
+ client,
6608
+ poolAddress,
6609
+ account,
6610
+ positionIdList: closingTokenIds,
6611
+ blockNumber
6612
+ });
6613
+ const { collectionDispatch } = buildProtectedSettlePlan({
6614
+ positionIdList: closingTokenIds,
6615
+ finalPositionIdList: positionIdList,
6616
+ positionSizes,
6617
+ builderCode: params.builderCode
6618
+ });
6619
+ const calls = (targets.length ? buildSettleSequenceCalls({
6620
+ positionIdListFrom: params.signerPositionIdList,
6621
+ targets
6622
+ }) : []).map((data) => ({
6623
+ to: poolAddress,
6624
+ data,
6625
+ account: params.signerAddress
6626
+ }));
6627
+ if (collectionDispatch !== void 0) {
6628
+ const [dispatchData] = buildSettleSequenceCalls({
6629
+ positionIdListFrom: positionIdList,
6630
+ targets: [],
6631
+ dispatch: collectionDispatch
6632
+ });
6633
+ if (dispatchData === void 0) throw new PanopticError("Premium protection collection dispatch is missing");
6634
+ calls.push({
6635
+ ...params.buildCollectionCall(dispatchData),
6636
+ account: params.signerAddress
6637
+ });
6638
+ }
6639
+ for (const includePendingPremium of [true, false]) calls.push({
6640
+ to: poolAddress,
6641
+ account: params.signerAddress,
6642
+ data: encodeFunctionData({
6643
+ abi: panopticPoolV2Abi,
6644
+ functionName: "getFullPositionsData",
6645
+ args: [
6646
+ account,
6647
+ includePendingPremium,
6648
+ closingTokenIds
6649
+ ]
6650
+ })
6651
+ });
6652
+ const blocks = await client.simulateBlocks({
6653
+ blocks: [{ calls }],
6654
+ blockNumber
6655
+ });
6656
+ const results = blocks[0]?.calls;
6657
+ if (results === void 0 || results.length !== calls.length) throw new PanopticError("Premium protection simulation returned incomplete results");
6658
+ for (const result$1 of results) if (result$1.status === "failure") throw result$1.error;
6659
+ const result = results.map((call$1) => call$1.data);
6660
+ const owedResult = result[result.length - 2];
6661
+ const availableResult = result[result.length - 1];
6662
+ if (owedResult === void 0 || availableResult === void 0) throw new PanopticError("Premium protection simulation returned incomplete results");
6663
+ const decodeShortPremium = (data) => decodeLeftRightUnsigned(decodeFunctionResult({
6664
+ abi: panopticPoolV2Abi,
6665
+ functionName: "getFullPositionsData",
6666
+ data
6667
+ })[0]);
6668
+ const owed = decodeShortPremium(owedResult);
6669
+ const available = decodeShortPremium(availableResult);
6670
+ return {
6671
+ success: true,
6672
+ gasEstimate: 0n,
6673
+ data: {
6674
+ owed0: owed.right,
6675
+ owed1: owed.left,
6676
+ available0: available.right,
6677
+ available1: available.left,
6678
+ forfeit0: owed.right > available.right ? owed.right - available.right : 0n,
6679
+ forfeit1: owed.left > available.left ? owed.left - available.left : 0n
6680
+ },
6681
+ _meta: await metaPromise
6682
+ };
6683
+ } catch (error) {
6684
+ const simulationError = error instanceof PanopticError ? error : new PanopticError(String(error));
6685
+ if (metaPromise === void 0) throw simulationError;
6686
+ return {
6687
+ success: false,
6688
+ error: simulationError,
6689
+ _meta: await metaPromise
6690
+ };
6691
+ }
6692
+ }
6693
+
6506
6694
  //#endregion
6507
6695
  //#region src/panoptic/v2/simulations/simulateForceExercise.ts
6508
6696
  /**
@@ -7403,6 +7591,42 @@ async function quoteOneTokenFlow(params) {
7403
7591
  };
7404
7592
  }
7405
7593
 
7594
+ //#endregion
7595
+ //#region src/panoptic/v2/simulations/quoteTokenShortfallSwap.ts
7596
+ /** Quote the opposite pool token needed to buy the deficit in a NotEnoughTokens revert. */
7597
+ async function quoteTokenShortfallSwap({ client, chainId, poolAddress, error, slippageBps = 50n }) {
7598
+ const shortfall = getNotEnoughTokensError(error);
7599
+ if (shortfall === null || shortfall.deficit === 0n) return null;
7600
+ const metadata = await getPoolMetadata({
7601
+ client,
7602
+ poolAddress
7603
+ });
7604
+ const matches0 = [metadata.token0Asset, metadata.collateralToken0Address].some((address) => isAddressEqual(address, shortfall.tokenAddress));
7605
+ const matches1 = [metadata.token1Asset, metadata.collateralToken1Address].some((address) => isAddressEqual(address, shortfall.tokenAddress));
7606
+ if (matches0 === matches1) return null;
7607
+ const tokenIn = matches0 ? metadata.token1Asset : metadata.token0Asset;
7608
+ const quote = await (metadata.isV4 ? quoteSwapExactOutViaRouter : quoteSwapExactOutViaV3Router)({
7609
+ client,
7610
+ chainId,
7611
+ poolAddress,
7612
+ tokenIn,
7613
+ amountOut: shortfall.deficit,
7614
+ slippageBps
7615
+ });
7616
+ return {
7617
+ deficit: shortfall.deficit,
7618
+ outputSymbol: matches0 ? metadata.token0Symbol : metadata.token1Symbol,
7619
+ outputDecimals: matches0 ? metadata.token0Decimals : metadata.token1Decimals,
7620
+ inputSymbol: matches0 ? metadata.token1Symbol : metadata.token0Symbol,
7621
+ inputDecimals: matches0 ? metadata.token1Decimals : metadata.token0Decimals,
7622
+ tokenIn,
7623
+ slippageBps,
7624
+ poolFeePips: metadata.fee <= 1000000n ? metadata.fee : null,
7625
+ estimatedPoolFee: quote.success && metadata.fee <= 1000000n ? (quote.data.amountIn * metadata.fee + 999999n) / 1000000n : null,
7626
+ quote: quote.success ? quote.data : null
7627
+ };
7628
+ }
7629
+
7406
7630
  //#endregion
7407
7631
  //#region src/panoptic/v2/simulations/temporaryLoanRecovery.ts
7408
7632
  const BPS_DENOMINATOR = 10000n;
@@ -8505,7 +8729,7 @@ const DEFAULT_INTERVAL_MS = 12000n;
8505
8729
  /**
8506
8730
  * Default maximum block range per poll.
8507
8731
  */
8508
- const DEFAULT_MAX_BLOCK_RANGE = 1000n;
8732
+ const DEFAULT_MAX_BLOCK_RANGE$1 = 1000n;
8509
8733
  /**
8510
8734
  * Create an HTTP polling-based event fetcher.
8511
8735
  *
@@ -8542,7 +8766,7 @@ const DEFAULT_MAX_BLOCK_RANGE = 1000n;
8542
8766
  * ```
8543
8767
  */
8544
8768
  function createEventPoller(params) {
8545
- const { client, poolAddress, collateralTracker0, collateralTracker1, riskEngineAddress, sfpmAddress, poolManagerAddress, eventTypes, onLogs, onError, intervalMs = DEFAULT_INTERVAL_MS, maxBlockRange = DEFAULT_MAX_BLOCK_RANGE, fromBlock } = params;
8769
+ const { client, poolAddress, collateralTracker0, collateralTracker1, riskEngineAddress, sfpmAddress, poolManagerAddress, eventTypes, onLogs, onError, intervalMs = DEFAULT_INTERVAL_MS, maxBlockRange = DEFAULT_MAX_BLOCK_RANGE$1, fromBlock } = params;
8546
8770
  let isRunning = false;
8547
8771
  let lastPolledBlock = fromBlock ?? 0n;
8548
8772
  let pollTimeout = null;
@@ -8730,6 +8954,431 @@ function createEventPoller(params) {
8730
8954
  };
8731
8955
  }
8732
8956
 
8957
+ //#endregion
8958
+ //#region src/panoptic/v2/events/poolSwapStream.ts
8959
+ const DEFAULT_POLL_INTERVAL_MS$1 = 3e3;
8960
+ const FAILURE_WINDOW_MS = 3e4;
8961
+ const FAILURES_BEFORE_POLL = 3;
8962
+ const STREAM_RETRY_MIN_MS = 15e3;
8963
+ const STREAM_RETRY_MAX_MS = 3e5;
8964
+ const RESYNC_JITTER_MS = 2e3;
8965
+ const EVENT_SOURCE_CLOSED = 2;
8966
+ function parseRecord(data) {
8967
+ const value = JSON.parse(data);
8968
+ if (typeof value !== "object" || value === null) throw new Error("Malformed stream frame");
8969
+ return value;
8970
+ }
8971
+ function bigintField(record, field) {
8972
+ const value = record[field];
8973
+ if (typeof value !== "string") throw new Error(`Stream frame is missing ${field}`);
8974
+ return BigInt(value);
8975
+ }
8976
+ function numberField(record, field) {
8977
+ const value = record[field];
8978
+ if (typeof value !== "number") throw new Error(`Stream frame is missing ${field}`);
8979
+ return value;
8980
+ }
8981
+ function parseStreamedKey(data) {
8982
+ const record = parseRecord(data);
8983
+ return {
8984
+ blockNumber: bigintField(record, "blockNumber"),
8985
+ logIndex: numberField(record, "logIndex")
8986
+ };
8987
+ }
8988
+ function parseStreamedSwap(data) {
8989
+ const record = parseRecord(data);
8990
+ const transactionHash = record.transactionHash;
8991
+ if (typeof transactionHash !== "string") throw new Error("Stream frame is missing transactionHash");
8992
+ return {
8993
+ ...parseStreamedKey(data),
8994
+ timestamp: bigintField(record, "timestamp"),
8995
+ transactionHash,
8996
+ tick: numberField(record, "tick"),
8997
+ amount0: bigintField(record, "amount0"),
8998
+ amount1: bigintField(record, "amount1"),
8999
+ sqrtPriceX96: bigintField(record, "sqrtPriceX96"),
9000
+ liquidity: bigintField(record, "liquidity")
9001
+ };
9002
+ }
9003
+ function parseHead(data) {
9004
+ return bigintField(parseRecord(data ?? ""), "head");
9005
+ }
9006
+ function defaultEventSource() {
9007
+ const EventSourceCtor = globalThis.EventSource;
9008
+ return EventSourceCtor && ((url) => new EventSourceCtor(url));
9009
+ }
9010
+ function toError$1(error) {
9011
+ return error instanceof Error ? error : new Error(String(error));
9012
+ }
9013
+ /**
9014
+ * Stream a pool's swaps from the relay at `streamUrl`.
9015
+ *
9016
+ * Every `ready`/`resync` frame carries the relay's head; swaps from the cursor up
9017
+ * to that head are fetched over HTTP so nothing is lost across reconnects. A
9018
+ * stream that closes, or errors three times in 30 s, is replaced by HTTP polling
9019
+ * and retried with backoff; one that closes before ever opening is not retried.
9020
+ */
9021
+ function streamPoolSwaps(params) {
9022
+ const { client, pool, fromBlock, pollIntervalMs = DEFAULT_POLL_INTERVAL_MS$1, maxBlockRange = DEFAULT_MAX_BLOCK_RANGE, streamUrl, onSwaps, onError, onRemoved, onTransport, eventSource = defaultEventSource() } = params;
9023
+ let running = true;
9024
+ let cursor = fromBlock === void 0 ? void 0 : fromBlock - 1n;
9025
+ let transport = "off";
9026
+ let failures = [];
9027
+ let source;
9028
+ let poller;
9029
+ let retryTimer;
9030
+ let retryDelayMs = STREAM_RETRY_MIN_MS;
9031
+ let catchUpTimer;
9032
+ let catchUpTarget;
9033
+ let catchingUp = false;
9034
+ let everOpened = false;
9035
+ const setTransport = (next) => {
9036
+ if (next === transport) return;
9037
+ transport = next;
9038
+ onTransport?.(next);
9039
+ };
9040
+ const advance = (block) => {
9041
+ if (block !== void 0 && (cursor === void 0 || block > cursor)) cursor = block;
9042
+ };
9043
+ const report = (error) => {
9044
+ if (running) onError?.(toError$1(error));
9045
+ };
9046
+ const catchUpPending = () => catchingUp || catchUpTarget !== void 0;
9047
+ const runCatchUp = async () => {
9048
+ catchingUp = true;
9049
+ while (running && catchUpTarget !== void 0) {
9050
+ const head = catchUpTarget;
9051
+ catchUpTarget = void 0;
9052
+ if (cursor === void 0) {
9053
+ cursor = head;
9054
+ continue;
9055
+ }
9056
+ try {
9057
+ const { swaps } = await fetchPoolSwaps(client, pool, cursor + 1n, head, maxBlockRange);
9058
+ if (!running) break;
9059
+ const first = swaps[0];
9060
+ const last = swaps[swaps.length - 1];
9061
+ if (first && last) onSwaps(swaps, {
9062
+ fromBlock: first.blockNumber,
9063
+ toBlock: last.blockNumber
9064
+ });
9065
+ advance(head);
9066
+ } catch (error) {
9067
+ report(error);
9068
+ if (catchUpTarget === void 0 || catchUpTarget < head) catchUpTarget = head;
9069
+ catchingUp = false;
9070
+ catchUpTimer = setTimeout(() => void runCatchUp(), pollIntervalMs);
9071
+ return;
9072
+ }
9073
+ }
9074
+ catchingUp = false;
9075
+ };
9076
+ const catchUp = (head, delayMs = 0) => {
9077
+ if (catchUpTarget === void 0 || head > catchUpTarget) catchUpTarget = head;
9078
+ if (catchingUp) return;
9079
+ if (catchUpTimer !== void 0) clearTimeout(catchUpTimer);
9080
+ catchUpTimer = setTimeout(() => {
9081
+ catchUpTimer = void 0;
9082
+ runCatchUp();
9083
+ }, delayMs);
9084
+ };
9085
+ const startPolling = () => {
9086
+ if (poller) return;
9087
+ poller = watchPoolSwaps({
9088
+ client,
9089
+ pool,
9090
+ fromBlock: cursor === void 0 ? void 0 : cursor + 1n,
9091
+ pollIntervalMs,
9092
+ maxBlockRange,
9093
+ onSwaps,
9094
+ onError
9095
+ });
9096
+ setTransport("poll");
9097
+ };
9098
+ const stopPolling = () => {
9099
+ if (!poller) return;
9100
+ advance(poller.lastScannedBlock());
9101
+ poller.stop();
9102
+ poller = void 0;
9103
+ };
9104
+ const closeStream = () => {
9105
+ source?.close();
9106
+ source = void 0;
9107
+ };
9108
+ const scheduleStreamRetry = () => {
9109
+ retryTimer = setTimeout(() => {
9110
+ retryTimer = void 0;
9111
+ if (running) openStream();
9112
+ }, retryDelayMs);
9113
+ retryDelayMs = Math.min(retryDelayMs * 2, STREAM_RETRY_MAX_MS);
9114
+ };
9115
+ const openStream = () => {
9116
+ if (!eventSource) {
9117
+ startPolling();
9118
+ return;
9119
+ }
9120
+ const es = eventSource(streamUrl);
9121
+ source = es;
9122
+ failures = [];
9123
+ let opened = false;
9124
+ const listen = (type, handle) => es.addEventListener(type, (event) => {
9125
+ if (!running || source !== es) return;
9126
+ try {
9127
+ handle(event.data);
9128
+ } catch (error) {
9129
+ report(error);
9130
+ }
9131
+ });
9132
+ listen("open", () => {
9133
+ opened = true;
9134
+ everOpened = true;
9135
+ failures = [];
9136
+ retryDelayMs = STREAM_RETRY_MIN_MS;
9137
+ });
9138
+ listen("ready", (data) => {
9139
+ const head = parseHead(data);
9140
+ stopPolling();
9141
+ setTransport("stream");
9142
+ catchUp(head);
9143
+ });
9144
+ listen("resync", (data) => {
9145
+ catchUp(parseHead(data), Math.random() * RESYNC_JITTER_MS);
9146
+ });
9147
+ listen("swap", (data) => {
9148
+ const swap = parseStreamedSwap(data ?? "");
9149
+ onSwaps([swap], {
9150
+ fromBlock: swap.blockNumber,
9151
+ toBlock: swap.blockNumber
9152
+ });
9153
+ if (!catchUpPending()) advance(swap.blockNumber);
9154
+ });
9155
+ listen("removed", (data) => {
9156
+ onRemoved?.(parseStreamedKey(data ?? ""));
9157
+ });
9158
+ listen("error", () => {
9159
+ const now = Date.now();
9160
+ failures = failures.filter((at) => now - at < FAILURE_WINDOW_MS);
9161
+ failures.push(now);
9162
+ const closed = es.readyState === EVENT_SOURCE_CLOSED;
9163
+ if (!closed && failures.length < FAILURES_BEFORE_POLL) return;
9164
+ closeStream();
9165
+ startPolling();
9166
+ if (closed && !opened && !everOpened) return;
9167
+ scheduleStreamRetry();
9168
+ });
9169
+ };
9170
+ openStream();
9171
+ return {
9172
+ stop() {
9173
+ running = false;
9174
+ closeStream();
9175
+ stopPolling();
9176
+ if (retryTimer !== void 0) clearTimeout(retryTimer);
9177
+ if (catchUpTimer !== void 0) clearTimeout(catchUpTimer);
9178
+ setTransport("off");
9179
+ },
9180
+ lastScannedBlock: () => cursor,
9181
+ transport: () => transport
9182
+ };
9183
+ }
9184
+
9185
+ //#endregion
9186
+ //#region src/panoptic/v2/events/poolSwaps.ts
9187
+ const DEFAULT_POLL_INTERVAL_MS = 3e3;
9188
+ const DEFAULT_MAX_BLOCK_RANGE = 2000n;
9189
+ const SHARED_BUFFER_LIMIT = 4096;
9190
+ /**
9191
+ * Parse a decoded v3 `UniswapV3Pool.Swap` or v4 `PoolManager.Swap` log.
9192
+ */
9193
+ function parseSwapLog(log, timestamp) {
9194
+ if (log.blockNumber === null || log.logIndex === null || log.transactionHash === null) throw new Error("Cannot parse a pending Swap log");
9195
+ const args = log.args;
9196
+ return {
9197
+ blockNumber: log.blockNumber,
9198
+ logIndex: log.logIndex,
9199
+ timestamp,
9200
+ transactionHash: log.transactionHash,
9201
+ tick: Number(args.tick),
9202
+ amount0: args.amount0,
9203
+ amount1: args.amount1,
9204
+ sqrtPriceX96: args.sqrtPriceX96,
9205
+ liquidity: args.liquidity
9206
+ };
9207
+ }
9208
+ function toError(error) {
9209
+ return error instanceof Error ? error : new Error(String(error));
9210
+ }
9211
+ async function fetchSwapLogs(client, pool, fromBlock, toBlock) {
9212
+ if (pool.version === "v3") return client.getContractEvents({
9213
+ address: pool.poolAddress,
9214
+ abi: UniswapV3PoolAbi,
9215
+ eventName: "Swap",
9216
+ fromBlock,
9217
+ toBlock
9218
+ });
9219
+ return client.getContractEvents({
9220
+ address: pool.poolManager,
9221
+ abi: poolManagerAbi,
9222
+ eventName: "Swap",
9223
+ args: { id: pool.poolId },
9224
+ fromBlock,
9225
+ toBlock
9226
+ });
9227
+ }
9228
+ async function fetchSwaps(client, pool, fromBlock, toBlock) {
9229
+ const logs = await fetchSwapLogs(client, pool, fromBlock, toBlock);
9230
+ if (logs.length === 0) return [];
9231
+ const blockNumbers = [...new Set(logs.flatMap((log) => log.blockNumber === null ? [] : [log.blockNumber]))];
9232
+ const blocks = await Promise.all(blockNumbers.map((blockNumber) => client.getBlock({ blockNumber })));
9233
+ const timestamps = new Map(blocks.map((block, i) => [blockNumbers[i], block.timestamp]));
9234
+ return logs.map((log) => parseSwapLog(log, timestamps.get(log.blockNumber ?? -1n) ?? 0n)).sort((a, b) => a.blockNumber === b.blockNumber ? a.logIndex - b.logIndex : a.blockNumber < b.blockNumber ? -1 : 1);
9235
+ }
9236
+ /**
9237
+ * Fetch a pool's swaps in `[fromBlock, toBlock]` (default: the current head), in
9238
+ * chain order. Returns no swaps when `fromBlock > toBlock`.
9239
+ */
9240
+ async function fetchPoolSwaps(client, pool, fromBlock, toBlock, maxBlockRange = DEFAULT_MAX_BLOCK_RANGE) {
9241
+ const end = toBlock ?? await client.getBlockNumber();
9242
+ const swaps = [];
9243
+ for (let from = fromBlock; from <= end; from += maxBlockRange) {
9244
+ const to = from + maxBlockRange - 1n < end ? from + maxBlockRange - 1n : end;
9245
+ swaps.push(...await fetchSwaps(client, pool, from, to));
9246
+ }
9247
+ return {
9248
+ swaps,
9249
+ toBlock: end
9250
+ };
9251
+ }
9252
+ /**
9253
+ * Poll a single pool's `Swap` logs with `eth_getLogs`, timestamped by block.
9254
+ *
9255
+ * Works over plain HTTP (no filters or websockets). A chunk that fails is retried on
9256
+ * the next tick; errors never stop the loop.
9257
+ */
9258
+ function watchPoolSwaps(params) {
9259
+ const { client, pool, fromBlock, pollIntervalMs = DEFAULT_POLL_INTERVAL_MS, maxBlockRange = DEFAULT_MAX_BLOCK_RANGE, onSwaps, onError } = params;
9260
+ let running = true;
9261
+ let lastScanned;
9262
+ let timer;
9263
+ const poll = async () => {
9264
+ try {
9265
+ const head = await client.getBlockNumber();
9266
+ let from = lastScanned !== void 0 ? lastScanned + 1n : fromBlock ?? head;
9267
+ while (running && from <= head) {
9268
+ const to = from + maxBlockRange - 1n < head ? from + maxBlockRange - 1n : head;
9269
+ const swaps = await fetchSwaps(client, pool, from, to);
9270
+ if (!running) return;
9271
+ if (swaps.length > 0) onSwaps(swaps, {
9272
+ fromBlock: from,
9273
+ toBlock: to
9274
+ });
9275
+ lastScanned = to;
9276
+ from = to + 1n;
9277
+ }
9278
+ } catch (error) {
9279
+ if (running) onError?.(toError(error));
9280
+ }
9281
+ if (running) timer = setTimeout(() => void poll(), pollIntervalMs);
9282
+ };
9283
+ poll();
9284
+ return {
9285
+ stop() {
9286
+ running = false;
9287
+ if (timer !== void 0) clearTimeout(timer);
9288
+ },
9289
+ lastScannedBlock: () => lastScanned
9290
+ };
9291
+ }
9292
+ const sharedWatchers = new Map();
9293
+ function swapKey(key) {
9294
+ return `${key.blockNumber}:${key.logIndex}`;
9295
+ }
9296
+ /**
9297
+ * Ref-counted pool swap feed: subscribers with the same `key` share one watcher
9298
+ * (relay stream when `streamUrl` is set, else HTTP polling), duplicates are
9299
+ * dropped, late subscribers first receive the swaps already seen, and the watcher
9300
+ * stops when the last subscriber unsubscribes.
9301
+ */
9302
+ function subscribePoolSwaps(params) {
9303
+ const { key, streamUrl, onSwaps, onError, onRemoved, onTransport,...watchParams } = params;
9304
+ const listener = {
9305
+ onSwaps,
9306
+ onError,
9307
+ onRemoved,
9308
+ onTransport
9309
+ };
9310
+ const existing = sharedWatchers.get(key);
9311
+ if (existing) {
9312
+ existing.listeners.add(listener);
9313
+ const buffered = [...existing.buffer.values()];
9314
+ const first = buffered[0];
9315
+ const last = buffered[buffered.length - 1];
9316
+ if (first && last) onSwaps(buffered, {
9317
+ fromBlock: first.blockNumber,
9318
+ toBlock: last.blockNumber
9319
+ });
9320
+ onTransport?.(existing.transport);
9321
+ } else {
9322
+ const listeners = new Set([listener]);
9323
+ const buffer = new Map();
9324
+ const entry = {
9325
+ watcher: {
9326
+ stop: () => void 0,
9327
+ lastScannedBlock: () => void 0
9328
+ },
9329
+ listeners,
9330
+ buffer,
9331
+ transport: "off"
9332
+ };
9333
+ const setTransport = (transport) => {
9334
+ entry.transport = transport;
9335
+ for (const l of listeners) l.onTransport?.(transport);
9336
+ };
9337
+ const callbacks = {
9338
+ onSwaps: (swaps, range) => {
9339
+ const fresh = swaps.filter((swap) => !buffer.has(swapKey(swap)));
9340
+ if (fresh.length === 0) return;
9341
+ for (const swap of fresh) buffer.set(swapKey(swap), swap);
9342
+ for (const k of buffer.keys()) {
9343
+ if (buffer.size <= SHARED_BUFFER_LIMIT) break;
9344
+ buffer.delete(k);
9345
+ }
9346
+ for (const l of listeners) l.onSwaps(fresh, range);
9347
+ },
9348
+ onError: (error) => {
9349
+ for (const l of listeners) l.onError?.(error);
9350
+ }
9351
+ };
9352
+ sharedWatchers.set(key, entry);
9353
+ if (streamUrl !== void 0) entry.watcher = streamPoolSwaps({
9354
+ ...watchParams,
9355
+ ...callbacks,
9356
+ streamUrl,
9357
+ onRemoved: (removed) => {
9358
+ buffer.delete(swapKey(removed));
9359
+ for (const l of listeners) l.onRemoved?.(removed);
9360
+ },
9361
+ onTransport: setTransport
9362
+ });
9363
+ else {
9364
+ entry.watcher = watchPoolSwaps({
9365
+ ...watchParams,
9366
+ ...callbacks
9367
+ });
9368
+ setTransport("poll");
9369
+ }
9370
+ }
9371
+ return () => {
9372
+ const entry = sharedWatchers.get(key);
9373
+ if (!entry) return;
9374
+ entry.listeners.delete(listener);
9375
+ if (entry.listeners.size === 0) {
9376
+ entry.watcher.stop();
9377
+ sharedWatchers.delete(key);
9378
+ }
9379
+ };
9380
+ }
9381
+
8733
9382
  //#endregion
8734
9383
  //#region src/panoptic/v2/strike/ladder.ts
8735
9384
  /** Target number of rungs across a leg's full range (lower tick → upper tick). */
@@ -10093,4 +10742,4 @@ async function getPositionVolatilityHistory({ client, poolAddress, account, toke
10093
10742
  }
10094
10743
 
10095
10744
  //#endregion
10096
- export { DEFAULT_MIN_SWAP_RATIO_BPS, DEFAULT_RECONNECT_CONFIG, MARKET_INDICATOR_PERIODS, REQUIRED_BASE_ERROR_SENTINEL, STRIKE_LADDER_TARGET_STRIKES, VARIANCE_RATIO_LAG, addPendingPosition, addTrackedChunks, apportion, assertCanBurn, assertCanForceExercise, assertCanLiquidate, assertCanMint, assertFresh, assertHealthy, assertLpPositionFunded, assertTradeable, buildCreditWrappedDispatch, buildSfpmSwapCalldata, buildSfpmSwapPlan, buildTemporaryLoanRecoveryDispatch, buildTokenShortfallRecoveryDispatch, calculateAccountGreeksPure, calculateMarketIndicator, calculateSpreadWad, calculateVarianceProfile, checkCollateralAcrossTicks, classifyStrategyGroups, classifyStrike, cleanupStalePendingPositions, clearPendingPositions, clearTrackedChunks, clearTradeHistory, collateralCurveTicks, collateralRuleKindFor, computeV4PoolId, confirmPendingPosition, convertToAssets, convertToShares, createEventPoller, createEventSubscription, createFlowNeutralTokenId, decodePanopticTokenURI, emptyLiquidateParams, encodePoolKeyBytes, encodeV3PoolKeyBytes, ensureSfpmV3PoolInitialized, estimateBlockNumbers, estimateCloseZapSwap, estimateCollateralBreakdown, estimateCollateralRequired, failPendingPosition, feeGrowthInsideX128, fetchSfpmV3PoolId, fetchUniswapFeeData, getAccountBuyingPower, getAccountGreeks, getAccountHistory, getChunkLiquidities, getChunkSpreads, getClosePositionTransferEstimate, getClosedPositions, getCollateralCurve, getCollateralCurveInputs, getCollateralRequiredBase, getCollateralSharePrices, getCollateralTotalAssetsBatch, getDeltaHedgeParams, getEnforcedTickLimits, getExecutableLpMaxSize, getFactoryConstructMetadata, getFactoryOwnerOf, getFactoryTokenURI, getGuardianUnlockState, getHedgeLimits, getItmAmounts, getMarginBuffer, getMaxPositionSize, getMaxRedeem, getMaxWithdrawable, getNativeTokenPrice, getNotEnoughTokensError, getOpenPositionPreview, getPanopticPoolAddress, getPanopticPoolFromPoolId, getPendingPositions, getPoolCollateralAddresses, getPoolLiquidities, getPortfolioValue, getPositionChunkData, getPositionEnrichmentData, getPositionValueCurve, getPositionVolatilityHistory, getRealizedPnL, getRequiredCreditForITM, getSafeMode, getStreamiaHistory, getSyncStatus, getTrackedChunks, getTradeHistory, getUniswapFeeHistory, getUniswapV3PoolFromId, getUniswapV3PoolInfo, getUniswapV3PoolLiquidities, getUniswapV4PoolBasicState, getUniswapV4PoolInfo, getUniswapV4PoolKeyFromId, getUniswapV4PoolLiquidities, interpolateBlocks, isGasError, isNonceError, isRetryableRpcError, isolateGroupTokenId, ladderStrikeSequence, logReturns, marketPnlInQuote, marketRiskFromValues, marketScenario, minePoolAddress, minePoolAddressLocalAsync, multicallRead, netLiquidationValueInQuote, optimizeTokenIdRiskPartners, parseCollateralLog, parsePoolLog, positionValueKey, positionValueTicks, prepareIndicatorCandles, preparePositionValueCurve, previewDeposit, previewMint, previewRedeem, previewWithdraw, quoteLiquidation, quoteOneTokenFlow, quoteSfpmSwap, quoteTemporaryLoanRecovery, quoteTokenShortfallRecovery, realizedBeta, removeTrackedChunks, resolveBlockNumbers, resolveLadderStrike, resolvePanopticPoolFromPoolId, resolveUniswapV4PoolKey, saveClosedPosition, scaleCollateralRequired, scanChunks, screenAccountExact, simulateBatchDispatch, simulateClosePosition, simulateDeployNewPool, simulateDeposit, simulateForceExercise, simulateLiquidate, simulateSFPMBurn, simulateSFPMMint, simulateSwapExactIn, simulateSwapExactOut, simulateWithdraw, slippageBpsToTickDistance, watchEvents };
10745
+ export { DEFAULT_MIN_SWAP_RATIO_BPS, DEFAULT_RECONNECT_CONFIG, MARKET_INDICATOR_PERIODS, REQUIRED_BASE_ERROR_SENTINEL, STRIKE_LADDER_TARGET_STRIKES, VARIANCE_RATIO_LAG, addPendingPosition, addTrackedChunks, apportion, assertCanBurn, assertCanForceExercise, assertCanLiquidate, assertCanMint, assertFresh, assertHealthy, assertLpPositionFunded, assertTradeable, buildCreditWrappedDispatch, buildSfpmSwapCalldata, buildSfpmSwapPlan, buildTemporaryLoanRecoveryDispatch, buildTokenShortfallRecoveryDispatch, calculateAccountGreeksPure, calculateMarketIndicator, calculateSpreadWad, calculateVarianceProfile, checkCollateralAcrossTicks, classifyStrategyGroups, classifyStrike, cleanupStalePendingPositions, clearPendingPositions, clearTrackedChunks, clearTradeHistory, collateralCurveTicks, collateralRuleKindFor, computeV4PoolId, confirmPendingPosition, convertToAssets, convertToShares, createEventPoller, createEventSubscription, createFlowNeutralTokenId, decodePanopticTokenURI, emptyLiquidateParams, encodePoolKeyBytes, encodeV3PoolKeyBytes, ensureSfpmV3PoolInitialized, estimateBlockNumbers, estimateCloseZapSwap, estimateCollateralBreakdown, estimateCollateralRequired, failPendingPosition, feeGrowthInsideX128, fetchPoolSwaps, fetchSfpmV3PoolId, fetchUniswapFeeData, getAccountBuyingPower, getAccountGreeks, getAccountHistory, getChunkLiquidities, getChunkSpreads, getClosePositionTransferEstimate, getClosedPositions, getCollateralCurve, getCollateralCurveInputs, getCollateralRequiredBase, getCollateralSharePrices, getCollateralTotalAssetsBatch, getDeltaHedgeParams, getEnforcedTickLimits, getExecutableLpMaxSize, getFactoryConstructMetadata, getFactoryOwnerOf, getFactoryTokenURI, getGuardianUnlockState, getHedgeLimits, getItmAmounts, getMarginBuffer, getMaxPositionSize, getMaxRedeem, getMaxWithdrawable, getNativeTokenPrice, getNotEnoughTokensError, getOpenPositionPreview, getPanopticPoolAddress, getPanopticPoolFromPoolId, getPendingPositions, getPoolCollateralAddresses, getPoolLiquidities, getPortfolioValue, getPositionChunkData, getPositionEnrichmentData, getPositionMintFunding, getPositionValueCurve, getPositionVolatilityHistory, getRealizedPnL, getRequiredCreditForITM, getSafeMode, getStreamiaHistory, getSyncStatus, getTrackedChunks, getTradeHistory, getUniswapFeeHistory, getUniswapV3PoolFromId, getUniswapV3PoolInfo, getUniswapV3PoolLiquidities, getUniswapV4PoolBasicState, getUniswapV4PoolInfo, getUniswapV4PoolKeyFromId, getUniswapV4PoolLiquidities, interpolateBlocks, isGasError, isNonceError, isRetryableRpcError, isolateGroupTokenId, ladderStrikeSequence, logReturns, marketPnlInQuote, marketRiskFromValues, marketScenario, minePoolAddress, minePoolAddressLocalAsync, multicallRead, netLiquidationValueInQuote, optimizeTokenIdRiskPartners, parseCollateralLog, parsePoolLog, parseStreamedKey, parseStreamedSwap, parseSwapLog, positionValueKey, positionValueTicks, prepareIndicatorCandles, preparePositionValueCurve, previewDeposit, previewMint, previewRedeem, previewWithdraw, quoteLiquidation, quoteOneTokenFlow, quoteSfpmSwap, quoteTemporaryLoanRecovery, quoteTokenShortfallRecovery, quoteTokenShortfallSwap, realizedBeta, removeTrackedChunks, resolveBlockNumbers, resolveLadderStrike, resolvePanopticPoolFromPoolId, resolveUniswapV4PoolKey, saveClosedPosition, scaleCollateralRequired, scanChunks, screenAccountExact, simulateBatchDispatch, simulateClosePosition, simulateClosePremiumProtection, simulateDeployNewPool, simulateDeposit, simulateForceExercise, simulateLiquidate, simulateSFPMBurn, simulateSFPMMint, simulateSwapExactIn, simulateSwapExactOut, simulateWithdraw, slippageBpsToTickDistance, streamPoolSwaps, subscribePoolSwaps, watchEvents, watchPoolSwaps };