@panoptic-eng/sdk 1.0.62 → 1.0.64

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +1 -1
  2. package/dist/cow/index.d.ts +1 -1
  3. package/dist/cow/index.d.ts.map +1 -1
  4. package/dist/cow/index.js +1 -1
  5. package/dist/cow/index.js.map +1 -1
  6. package/dist/{cow-DTi3GjQz.js → cow-BIzmQHon.js} +1 -1
  7. package/dist/index.d.ts +4 -3
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +1 -1
  10. package/dist/index.js.map +1 -1
  11. package/dist/{irm-BDlcUNgJ.js → irm-BGog_tSR.js} +3 -3
  12. package/dist/panoptic/v2/index.d.ts +137 -135
  13. package/dist/panoptic/v2/index.d.ts.map +1 -1
  14. package/dist/panoptic/v2/index.js +52 -51
  15. package/dist/panoptic/v2/index.js.map +1 -1
  16. package/dist/panoptic/v2/react-public.d.ts +144 -142
  17. package/dist/panoptic/v2/react-public.d.ts.map +1 -1
  18. package/dist/panoptic/v2/react-public.js +55 -54
  19. package/dist/panoptic/v2/react-public.js.map +1 -1
  20. package/dist/providers-1WdiaRbY.js +54 -0
  21. package/dist/{rates-BwZnK0tG.js → rates-O6Sn-xqL.js} +3 -3
  22. package/dist/{router-Y1OxL9Nt.js → router-BuAvFpbu.js} +137 -10
  23. package/dist/{router-jMP7fVno.js → router-DHn0rNd7.js} +2 -2
  24. package/dist/rpc/index.d.ts +112 -0
  25. package/dist/rpc/index.d.ts.map +1 -0
  26. package/dist/rpc/index.js +242 -0
  27. package/dist/rpc/index.js.map +1 -0
  28. package/dist/test/index.d.ts +1 -1
  29. package/dist/test/index.d.ts.map +1 -1
  30. package/dist/test/index.js +1 -14
  31. package/dist/test/index.js.map +1 -1
  32. package/dist/uniswap/index.d.ts +311 -216
  33. package/dist/uniswap/index.d.ts.map +1 -1
  34. package/dist/uniswap/index.js +254 -21
  35. package/dist/uniswap/index.js.map +1 -1
  36. package/dist/{v2-B27ILZl4.js → v2-D2Bg1SeN.js} +19 -144
  37. package/dist/{writes-B8NGXzUG.js → writes-jz-N6C_4.js} +37 -37
  38. package/dist/zodiac/index.d.ts +3 -3
  39. package/dist/zodiac/index.d.ts.map +1 -1
  40. package/dist/zodiac/index.js +3 -3
  41. package/dist/zodiac/index.js.map +1 -1
  42. package/package.json +5 -1
@@ -0,0 +1,54 @@
1
+ //#region src/rpc/providers.ts
2
+ const alchemyChains = {
3
+ 1: "eth-mainnet",
4
+ 137: "polygon-mainnet",
5
+ 42161: "arb-mainnet",
6
+ 8453: "base-mainnet",
7
+ 10: "opt-mainnet",
8
+ 130: "unichain-mainnet",
9
+ 4663: "robinhood-mainnet",
10
+ 11155111: "eth-sepolia"
11
+ };
12
+ function isAlchemyChainId(chainId) {
13
+ return chainId in alchemyChains;
14
+ }
15
+ const infuraChains = {
16
+ 1: "mainnet",
17
+ 10: "optimism-mainnet",
18
+ 137: "polygon-mainnet",
19
+ 8453: "base-mainnet",
20
+ 42161: "arbitrum-mainnet",
21
+ 43114: "avalanche-mainnet",
22
+ 11155111: "sepolia"
23
+ };
24
+ const RPC_PROVIDER_PRESETS = {
25
+ alchemy: {
26
+ apiKeyEnv: () => ["ALCHEMY_API_KEY"],
27
+ endpoint: (chainId, apiKey) => isAlchemyChainId(chainId) ? { url: `https://${alchemyChains[chainId]}.g.alchemy.com/v2/${apiKey}` } : void 0
28
+ },
29
+ infura: {
30
+ apiKeyEnv: () => ["INFURA_API_KEY"],
31
+ endpoint: (chainId, apiKey) => infuraChains[chainId] ? { url: `https://${infuraChains[chainId]}.infura.io/v3/${apiKey}` } : void 0
32
+ },
33
+ uniblock: {
34
+ apiKeyEnv: () => ["UNIBLOCK_API_KEY"],
35
+ endpoint: (chainId, apiKey) => ({
36
+ url: `https://api.uniblock.dev/uni/v1/json-rpc?chainId=${chainId}`,
37
+ headers: { "x-api-key": apiKey }
38
+ })
39
+ }
40
+ };
41
+ function isRpcProviderName(name) {
42
+ return Object.prototype.hasOwnProperty.call(RPC_PROVIDER_PRESETS, name);
43
+ }
44
+ function getAlchemyRpcUrl(chainId, alchemyApiKey) {
45
+ if (alchemyChains[chainId]) return `https://${alchemyChains[chainId]}.g.alchemy.com/v2/${alchemyApiKey}`;
46
+ else throw new Error(`Unsupported chainId: ${chainId}`);
47
+ }
48
+ function getAlchemyWsRpcUrl(chainId, alchemyApiKey) {
49
+ if (alchemyChains[chainId]) return `wss://${alchemyChains[chainId]}.g.alchemy.com/v2/${alchemyApiKey}`;
50
+ else throw new Error(`Unsupported chainId: ${chainId}`);
51
+ }
52
+
53
+ //#endregion
54
+ export { RPC_PROVIDER_PRESETS, getAlchemyRpcUrl, getAlchemyWsRpcUrl, isRpcProviderName };
@@ -7945,7 +7945,7 @@ var NoLegsExercisableError = class extends PanopticError {
7945
7945
  var NotALongLegError = class extends PanopticError {
7946
7946
  name = "NotALongLegError";
7947
7947
  constructor(cause) {
7948
- super("Cannot settle premium for non-long leg", cause);
7948
+ super("Cannot settle streamia for non-long leg", cause);
7949
7949
  }
7950
7950
  };
7951
7951
  /**
@@ -8414,7 +8414,7 @@ var UnsafePremiumSettlementError = class extends PanopticError {
8414
8414
  name = "UnsafePremiumSettlementError";
8415
8415
  constructor(remainingForfeit, failedBuyerCount, cause) {
8416
8416
  const [token0, token1] = remainingForfeit;
8417
- super(failedBuyerCount > 0 ? `Premium settlement blocked: ${failedBuyerCount} buyer settlement${failedBuyerCount === 1 ? "" : "s"} would fail` : `Premium settlement blocked: uncollectable premium remains (${token0}, ${token1})`, cause);
8417
+ super(failedBuyerCount > 0 ? `Streamia settlement blocked: ${failedBuyerCount} buyer settlement${failedBuyerCount === 1 ? "" : "s"} would fail` : `Streamia settlement blocked: uncollectable streamia remains (${token0}, ${token1})`, cause);
8418
8418
  this.remainingForfeit = remainingForfeit;
8419
8419
  this.failedBuyerCount = failedBuyerCount;
8420
8420
  }
@@ -11964,4 +11964,4 @@ function formatPerSecondRateWadAsAprPct(ratePerSecondWad, precision) {
11964
11964
  }
11965
11965
 
11966
11966
  //#endregion
11967
- export { AccountInsolventError, AlreadyInitializedError, BatchValidationError, BelowMinimumRedemptionError, CastingError, ChunkHasZeroLiquidityError, ChunkLimitError, CrossPoolError, DEFAULT_MAX_SPREAD, DEFAULT_MINT_BUFFER, DEFAULT_VEGOID, DepositTooLargeError, DuplicateTokenIdError, EffectiveLiquidityAboveThresholdError, ExceedsMaximumRedemptionError, InputListFailError, InsufficientCreditLiquidityError, InvalidBuilderCodeError, InvalidHistoryRangeError, InvalidTickBoundError, InvalidTickError, InvalidTickLimitsError, InvalidTokenIdParameterError, InvalidUniswapCallbackError, LEG_BITS, LEG_LIMITS, LEG_MASKS, LengthMismatchError, LiquidityTooHighError, LoanSlotExhaustedError, MINT_BUFFER, MINT_BUFFER_DENOMINATOR, MaxRetriesExceededError, MissingPositionIdsError, MulticallNoDataError, MulticallResultFailedError, MulticallResultMissingError, NetLiquidityZeroError, NetworkMismatchError, NoLegsExercisableError, NoLoanPositionsError, NotALongLegError, NotBuilderError, NotEnoughLiquidityInChunkError, NotEnoughTokensError, NotGuardianError, NotMarginCalledError, NotPanopticPoolError, OracleRateLimitedError, PanopticError, PanopticHelperNotDeployedError, PanopticValidationError, PoolNotInitializedError, PositionCountNotZeroError, PositionNotOwnedError, PositionSnapshotNotFoundError, PositionTooLargeError, PriceBoundFailError, PriceImpactTooLargeError, ProviderLagError, ReentrancyError, RpcError, RpcResponseError, STANDARD_TICK_WIDTHS, STRIKE_CONVERSION_FACTOR, SafeModeError, StaleDataError, StaleOracleError, StorageDataNotFoundError, SwapTokenMismatchError, SyncTimeoutError, TOKEN_ID_BITS, TokenIdHasZeroLegsError, TooManyLegsOpenError, TransferFailedError, UnauthorizedUniswapCallbackError, UnderOverFlowError, UnhealthyPoolError, UnsafePremiumSettlementError, WrongPoolIdError, WrongUniswapPoolError, ZeroAddressError, ZeroCollateralRequirementError, annualizePerSecondRateWad, applyMintBuffer, applyMintBufferPerToken, availableToBorrow, clearTrackedPositions, collateralTrackerV2Abi, createFileStorage, createMemoryStorage, createTxResult, decodeAllDispatchCalldata, decodeDispatchCalldata, decodeLeftRightSigned, decodeLeftRightUnsigned, decodeOraclePack, decodeOracleRiskParameters, decodeOracleTiming, decodePosition, decodePositionBalance$1 as decodePositionBalance, decodeTickSpacing as decodeTickSpacing$1, diagnoseOracleSafeMode, fetchPoolId, formatPerSecondRateWadAsAprPct, formatPerSecondRateWadAsApyPct, formatRateWad, formatTokenAmount, formatTokenAmountSigned, formatTokenDelta, formatTokenFlow, formatWad, formatWadPercent, formatWadSigned, getBlockMeta, getClosedPositionsKey, getOpenPositionIds, getOracleRiskParameters, getOracleState, getPendingPositionsKey, getPool, getPoolCurrentTick, getPoolMetaKey, getPoolMetadata, getPoolPrefix, getPosition, getPositionGreeks, getPositionMetaKey, getPositions, getPositionsKey, getRiskParameters, getSchemaVersionKey, getSyncCheckpointKey, getTrackedChunksKey, getTrackedPositionIds, getUtilization, isPanopticErrorType, isPositionTracked, jsonSerializer, mintableAfterBuffer, oracleEpochAt, panopticErrorsAbi, panopticFactoryV3Abi, panopticFactoryV4Abi, panopticGuardianAbi, panopticLiquidatorAbi, panopticPoolV2Abi, panopticQueryAbi, parsePanopticError, parseTokenAmount, parseWad, recoverSnapshot, recoverSnapshotFromTx, riskEngineAbi, selectDispatchForAccount, semiFungiblePositionManagerV3Abi, semiFungiblePositionManagerV4Abi, stateViewAbi, submitWrite, uniswapV3PoolAbi, validateBuilderCode };
11967
+ export { AccountInsolventError, AlreadyInitializedError, BatchValidationError, BelowMinimumRedemptionError, CastingError, ChunkHasZeroLiquidityError, ChunkLimitError, CrossPoolError, DEFAULT_MAX_SPREAD, DEFAULT_MINT_BUFFER, DEFAULT_VEGOID, DepositTooLargeError, DuplicateTokenIdError, EffectiveLiquidityAboveThresholdError, ExceedsMaximumRedemptionError, InputListFailError, InsufficientCreditLiquidityError, InvalidBuilderCodeError, InvalidHistoryRangeError, InvalidTickBoundError, InvalidTickError, InvalidTickLimitsError, InvalidTokenIdParameterError, InvalidUniswapCallbackError, LEG_BITS, LEG_LIMITS, LEG_MASKS, LengthMismatchError, LiquidityTooHighError, LoanSlotExhaustedError, MINT_BUFFER, MINT_BUFFER_DENOMINATOR, MaxRetriesExceededError, MissingPositionIdsError, MulticallNoDataError, MulticallResultFailedError, MulticallResultMissingError, NetLiquidityZeroError, NetworkMismatchError, NoLegsExercisableError, NoLoanPositionsError, NotALongLegError, NotBuilderError, NotEnoughLiquidityInChunkError, NotEnoughTokensError, NotGuardianError, NotMarginCalledError, NotPanopticPoolError, OracleRateLimitedError, PanopticError, PanopticHelperNotDeployedError, PanopticValidationError, PoolNotInitializedError, PositionCountNotZeroError, PositionNotOwnedError, PositionSnapshotNotFoundError, PositionTooLargeError, PriceBoundFailError, PriceImpactTooLargeError, ProviderLagError, ReentrancyError, RpcError, RpcResponseError, STANDARD_TICK_WIDTHS, STRIKE_CONVERSION_FACTOR, SafeModeError, StaleDataError, StaleOracleError, StorageDataNotFoundError, SwapTokenMismatchError, SyncTimeoutError, TOKEN_ID_BITS, TokenIdHasZeroLegsError, TooManyLegsOpenError, TransferFailedError, UnauthorizedUniswapCallbackError, UnderOverFlowError, UnhealthyPoolError, UnsafePremiumSettlementError, WrongPoolIdError, WrongUniswapPoolError, ZeroAddressError, ZeroCollateralRequirementError, annualizePerSecondRateWad, applyMintBuffer, applyMintBufferPerToken, availableToBorrow, clearTrackedPositions, collateralTrackerV2Abi, createFileStorage, createMemoryStorage, createTxResult, decodeAllDispatchCalldata, decodeDispatchCalldata, decodeLeftRightSigned, decodeLeftRightUnsigned, decodeOraclePack, decodeOracleRiskParameters, decodeOracleTiming, decodePosition, decodePositionBalance$1 as decodePositionBalance, decodeTickSpacing, diagnoseOracleSafeMode, fetchPoolId, formatPerSecondRateWadAsAprPct, formatPerSecondRateWadAsApyPct, formatRateWad, formatTokenAmount, formatTokenAmountSigned, formatTokenDelta, formatTokenFlow, formatWad, formatWadPercent, formatWadSigned, getBlockMeta, getClosedPositionsKey, getOpenPositionIds, getOracleRiskParameters, getOracleState, getPendingPositionsKey, getPool, getPoolCurrentTick, getPoolMetaKey, getPoolMetadata, getPoolPrefix, getPosition, getPositionGreeks, getPositionMetaKey, getPositions, getPositionsKey, getRiskParameters, getSchemaVersionKey, getSyncCheckpointKey, getTrackedChunksKey, getTrackedPositionIds, getUtilization, isPanopticErrorType, isPositionTracked, jsonSerializer, mintableAfterBuffer, oracleEpochAt, panopticErrorsAbi, panopticFactoryV3Abi, panopticFactoryV4Abi, panopticGuardianAbi, panopticLiquidatorAbi, panopticPoolV2Abi, panopticQueryAbi as panopticQueryAbi$1, parsePanopticError, parseTokenAmount, parseWad, recoverSnapshot, recoverSnapshotFromTx, riskEngineAbi, selectDispatchForAccount, semiFungiblePositionManagerV3Abi, semiFungiblePositionManagerV4Abi, stateViewAbi, submitWrite, uniswapV3PoolAbi, validateBuilderCode };
@@ -1,10 +1,11 @@
1
- import { PanopticError, getBlockMeta, getPoolMetadata, submitWrite } from "./rates-BwZnK0tG.js";
1
+ import { PanopticError, getBlockMeta, getPoolMetadata, submitWrite } from "./rates-O6Sn-xqL.js";
2
2
  import { tickToSqrtPriceX96 } from "./greeks-CNi1-cnp.js";
3
- import { decodeAllLegs } from "./writes-B8NGXzUG.js";
3
+ import { decodeAllLegs, decodeTokenId } from "./writes-jz-N6C_4.js";
4
4
  import { BaseError, ContractFunctionRevertedError, encodeAbiParameters, encodeFunctionData, encodePacked, isAddressEqual, zeroAddress } from "viem";
5
+ import Decimal from "decimal.js";
5
6
 
6
7
  //#region src/uniswap/lpDeposit.ts
7
- const Q96 = 1n << 96n;
8
+ const Q96$1 = 1n << 96n;
8
9
  const ceilDiv = (n, d) => (n + d - 1n) / d;
9
10
  /** Uniswap mint principal in raw token units, rounded up per leg, plus 5% headroom. */
10
11
  function getLpDepositAmounts(ranges, sqrtPriceX96) {
@@ -20,8 +21,8 @@ function getLpDepositBreakdown(ranges, sqrtPriceX96) {
20
21
  const lower = tickToSqrtPriceX96(BigInt(tickLower));
21
22
  const upper = tickToSqrtPriceX96(BigInt(tickUpper));
22
23
  const price = sqrtPriceX96 < lower ? lower : sqrtPriceX96 > upper ? upper : sqrtPriceX96;
23
- amount0 += ceilDiv(liquidity * Q96 * (upper - price), upper * price);
24
- amount1 += ceilDiv(liquidity * (price - lower), Q96);
24
+ amount0 += ceilDiv(liquidity * Q96$1 * (upper - price), upper * price);
25
+ amount1 += ceilDiv(liquidity * (price - lower), Q96$1);
25
26
  }
26
27
  const buffer = {
27
28
  amount0: ceilDiv(amount0 * 5n, 100n),
@@ -49,7 +50,7 @@ function getUnhedgedLpRanges({ tokenId, positionSize, tickSpacing }) {
49
50
  const lower = tickToSqrtPriceX96(tickLower);
50
51
  const upper = tickToSqrtPriceX96(tickUpper);
51
52
  const amount = positionSize * leg.optionRatio;
52
- const liquidity = leg.asset === 0n ? amount * (lower * upper / Q96) / (upper - lower) : amount * Q96 / (upper - lower);
53
+ const liquidity = leg.asset === 0n ? amount * (lower * upper / Q96$1) / (upper - lower) : amount * Q96$1 / (upper - lower);
53
54
  return liquidity <= 0n ? [] : [{
54
55
  tickLower: Number(tickLower),
55
56
  tickUpper: Number(tickUpper),
@@ -70,7 +71,7 @@ function getLpPositionFunding(params) {
70
71
  });
71
72
  const breakdown = getLpDepositBreakdown(ranges, sqrtPriceX96);
72
73
  const priceSquared = valuationSqrtPriceX96 * valuationSqrtPriceX96;
73
- const value = ({ amount0, amount1 }) => quoteTokenIndex === 0 ? amount0 + ceilDiv(amount1 * Q96 * Q96, priceSquared) : amount1 + ceilDiv(amount0 * priceSquared, Q96 * Q96);
74
+ const value = ({ amount0, amount1 }) => quoteTokenIndex === 0 ? amount0 + ceilDiv(amount1 * Q96$1 * Q96$1, priceSquared) : amount1 + ceilDiv(amount0 * priceSquared, Q96$1 * Q96$1);
74
75
  const principalInQuote = value(breakdown.principal);
75
76
  const totalInQuote = value(breakdown.total);
76
77
  return {
@@ -100,8 +101,8 @@ function getLpFundingDeposit({ funding, availableInQuote, minimumInQuote = 0n, q
100
101
  requiredInQuote,
101
102
  shortfallInQuote,
102
103
  defaultQuotePercent,
103
- amount0: quoteTokenIndex === 0 ? quoteAmount : ceilDiv(assetValue * Q96 * Q96, priceSquared),
104
- amount1: quoteTokenIndex === 1 ? quoteAmount : ceilDiv(assetValue * priceSquared, Q96 * Q96)
104
+ amount0: quoteTokenIndex === 0 ? quoteAmount : ceilDiv(assetValue * Q96$1 * Q96$1, priceSquared),
105
+ amount1: quoteTokenIndex === 1 ? quoteAmount : ceilDiv(assetValue * priceSquared, Q96$1 * Q96$1)
105
106
  };
106
107
  }
107
108
  /** Largest raw size funded by available collateral, including native-token rounding. */
@@ -123,6 +124,132 @@ function getMaxLpPositionSize(params) {
123
124
  return low;
124
125
  }
125
126
 
127
+ //#endregion
128
+ //#region src/panoptic/v2/greeks/positionVolatility.ts
129
+ const Precision = Decimal.clone({ precision: 80 });
130
+ const Q96 = 1n << 96n;
131
+ const YEAR_SECONDS = new Precision(31536e3);
132
+ /** The streamia-free position curve, expressed in one quote token's human units. */
133
+ function preparePositionGamma({ tokenId, positionSize, quoteIsToken0, quoteDecimals }) {
134
+ if (positionSize <= 0n || positionSize >= 1n << 128n) throw new RangeError("Invalid position size");
135
+ if (!Number.isInteger(quoteDecimals) || quoteDecimals < 0 || quoteDecimals > 255) throw new RangeError("Invalid quote decimals");
136
+ const decoded = decodeTokenId(tokenId);
137
+ const chunks = decoded.legs.flatMap((leg) => {
138
+ if (leg.width === 0n) return [];
139
+ const width = leg.width * decoded.tickSpacing;
140
+ const lowerTick = leg.strike - width / 2n;
141
+ const upperTick = leg.strike + (width + 1n) / 2n;
142
+ if (lowerTick < -887272n || upperTick > 887272n || lowerTick >= upperTick) throw new RangeError("Invalid position range");
143
+ const lower = tickToSqrtPriceX96(lowerTick);
144
+ const upper = tickToSqrtPriceX96(upperTick);
145
+ const amount = positionSize * leg.optionRatio;
146
+ const liquidity = leg.asset === 0n ? amount * (lower * upper / Q96) / (upper - lower) : amount * Q96 / (upper - lower);
147
+ if (liquidity >= 1n << 128n) throw new RangeError("Liquidity exceeds uint128");
148
+ return [{
149
+ lowerTick: Number(lowerTick),
150
+ upperTick: Number(upperTick),
151
+ liquidity,
152
+ isLong: leg.isLong
153
+ }];
154
+ });
155
+ const scale = new Precision(10).pow(quoteDecimals);
156
+ const atTick = (tick) => {
157
+ const netLiquidity = chunks.reduce((sum, chunk) => tick >= BigInt(chunk.lowerTick) && tick < BigInt(chunk.upperTick) ? sum + (chunk.isLong ? -chunk.liquidity : chunk.liquidity) : sum, 0n);
158
+ const sqrt = new Precision(tickToSqrtPriceX96(tick).toString()).div(Q96.toString());
159
+ const gamma = new Precision(netLiquidity.toString()).neg().div(2).div(scale);
160
+ return quoteIsToken0 ? gamma.div(sqrt) : gamma.mul(sqrt);
161
+ };
162
+ const inRange = (low, high) => {
163
+ if (low > high) throw new RangeError("Invalid candle range");
164
+ const ticks = new Set([low, high]);
165
+ for (const chunk of chunks) for (const boundary of [BigInt(chunk.lowerTick), BigInt(chunk.upperTick)]) {
166
+ if (boundary >= low && boundary <= high) ticks.add(boundary);
167
+ if (boundary - 1n >= low && boundary - 1n <= high) ticks.add(boundary - 1n);
168
+ }
169
+ return [...ticks].map(atTick);
170
+ };
171
+ return {
172
+ chunks,
173
+ atTick,
174
+ inRange
175
+ };
176
+ }
177
+ /** Historical whole-position diagnostics, with signs retained independently of eligibility. */
178
+ function calculatePositionVolatilityMetrics({ observations, netPremium, baseFees, quoteDecimals }) {
179
+ if (observations.length < 2) throw new RangeError("At least two price observations are required");
180
+ if (!Number.isInteger(quoteDecimals) || quoteDecimals < 0 || quoteDecimals > 255) throw new RangeError("Invalid quote decimals");
181
+ const points = observations.map((point) => ({
182
+ ...point,
183
+ price: new Precision(point.price),
184
+ gamma: new Precision(point.gamma),
185
+ rangeGammas: (point.rangeGammas ?? []).map((gamma) => new Precision(gamma))
186
+ }));
187
+ for (const point of points) if (!point.price.isFinite() || point.price.lte(0) || !point.gamma.isFinite() || point.rangeGammas.some((gamma) => !gamma.isFinite())) throw new RangeError("Invalid price or gamma observation");
188
+ const quantum = new Precision(10).pow(-quoteDecimals);
189
+ const gammas = points.flatMap((point) => [point.gamma, ...point.rangeGammas]);
190
+ const peak = gammas.reduce((max, gamma) => Precision.max(max, gamma.abs()), new Precision(0));
191
+ const tolerance = Precision.max(quantum, peak.mul("1e-12"));
192
+ const positive = gammas.some((gamma) => gamma.gt(tolerance));
193
+ const negative = gammas.some((gamma) => gamma.lt(tolerance.neg()));
194
+ const gammaSign = positive && negative ? "changing" : positive ? "positive" : negative ? "negative" : "zero";
195
+ let exposure = new Precision(0);
196
+ let signedConvexity = new Precision(0);
197
+ let absoluteConvexity = new Precision(0);
198
+ let years = new Precision(0);
199
+ for (let i = 1; i < points.length; i++) {
200
+ const previous = points[i - 1];
201
+ const point = points[i];
202
+ const seconds = point.timestamp - previous.timestamp;
203
+ if (seconds <= 0n) throw new RangeError("Observations must have increasing timestamps");
204
+ const elapsed = new Precision(seconds.toString()).div(YEAR_SECONDS);
205
+ const squaredReturn = point.price.div(previous.price).ln().pow(2);
206
+ exposure = exposure.plus(previous.gamma.abs().mul(elapsed));
207
+ signedConvexity = signedConvexity.plus(previous.gamma.mul(squaredReturn).div(2));
208
+ absoluteConvexity = absoluteConvexity.plus(previous.gamma.abs().mul(squaredReturn).div(2));
209
+ years = years.plus(elapsed);
210
+ }
211
+ const sufficientExposure = exposure.gt(tolerance.mul(years).mul(100));
212
+ const premium = netPremium === null ? null : new Precision(netPremium);
213
+ const fees = baseFees == null ? null : new Precision(baseFees);
214
+ if (premium !== null && !premium.isFinite() || fees !== null && !fees.isFinite()) throw new RangeError("Invalid accounting amount");
215
+ const consistent = (amount) => gammaSign === "positive" ? amount.lte(quantum) : gammaSign === "negative" && amount.gte(quantum.neg());
216
+ const reason = !sufficientExposure ? "insufficient-exposure" : gammaSign === "changing" ? "changing-sign" : premium === null ? "premium-unavailable" : !consistent(premium) ? "inconsistent-premium-direction" : null;
217
+ const equivalent = (amount) => amount.abs().mul(2).div(exposure).sqrt().toString();
218
+ const coverage = (amount) => absoluteConvexity.gt(quantum) ? amount.abs().div(absoluteConvexity).toString() : null;
219
+ const eligibleFees = sufficientExposure && fees !== null && consistent(fees);
220
+ return {
221
+ gammaSign,
222
+ exposure: exposure.toString(),
223
+ signedConvexity: signedConvexity.toString(),
224
+ absoluteConvexity: absoluteConvexity.toString(),
225
+ netPremium: premium?.toString() ?? null,
226
+ estimatedHedgedResult: premium?.plus(signedConvexity).toString() ?? null,
227
+ weightedRealizedVolatility: sufficientExposure ? equivalent(absoluteConvexity) : null,
228
+ premiumEquivalentVolatility: reason === null && premium !== null ? equivalent(premium) : null,
229
+ premiumToConvexity: reason === null && premium !== null ? coverage(premium) : null,
230
+ baseFees: fees?.toString() ?? null,
231
+ feeEquivalentVolatility: eligibleFees ? equivalent(fees) : null,
232
+ feeToConvexity: eligibleFees ? coverage(fees) : null,
233
+ comparisonReason: reason
234
+ };
235
+ }
236
+ /** Quote-value signed cumulative token increments, excluding revaluation of previous accrual. */
237
+ function valuePositionAccrual({ snapshots, quoteIsToken0, quoteDecimals }) {
238
+ let previous0 = 0n;
239
+ let previous1 = 0n;
240
+ let total = new Precision(0);
241
+ for (const snapshot of snapshots) {
242
+ const amount0 = new Precision((snapshot.token0 - previous0).toString());
243
+ const amount1 = new Precision((snapshot.token1 - previous1).toString());
244
+ if (snapshot.sqrtPriceX96 !== void 0 && snapshot.sqrtPriceX96 <= 0n) throw new RangeError("Invalid sqrt price");
245
+ const price = snapshot.sqrtPriceX96 === void 0 ? new Precision("1.0001").pow(snapshot.tick.toString()) : new Precision(snapshot.sqrtPriceX96.toString()).div(Q96.toString()).pow(2);
246
+ total = total.plus(quoteIsToken0 ? amount0.plus(amount1.div(price)) : amount1.plus(amount0.mul(price)));
247
+ previous0 = snapshot.token0;
248
+ previous1 = snapshot.token1;
249
+ }
250
+ return total.div(new Precision(10).pow(quoteDecimals));
251
+ }
252
+
126
253
  //#endregion
127
254
  //#region src/uniswap/v4/router/errors.ts
128
255
  /**
@@ -843,4 +970,4 @@ async function swapExactOutViaV3Router(params) {
843
970
  }
844
971
 
845
972
  //#endregion
846
- export { AmountExceedsUint128Error, InvalidSwapTokenError, MSG_SENDER, MissingSweepRecipientError, PERMIT2_ADDRESS, QuoterUnavailableError, UNISWAP_V3_ADDRESSES, UNISWAP_V4_ADDRESSES, UnsupportedChainError, V3_SWAP_EXACT_IN, V3_SWAP_EXACT_OUT, buildV3ExactOutSwapExecuteArgs, buildV3ExactOutSwapExecuteCalldata, buildV3SwapExecuteArgs, buildV3SwapExecuteCalldata, encodeV3Path, getLpDepositAmounts, getLpDepositBreakdown, getLpFundingDeposit, getLpPositionFunding, getMaxLpPositionSize, getUnhedgedLpRanges, getUniswapV3Addresses, getUniswapV4Addresses, quoteSwapExactInViaV3Router, quoteSwapExactOutViaV3Router, quoteV3ExactIn, quoteV3ExactOut, resolveV3SwapRoute, swapExactInViaV3Router, swapExactOutViaV3Router, universalRouterAbi };
973
+ export { AmountExceedsUint128Error, InvalidSwapTokenError, MSG_SENDER, MissingSweepRecipientError, PERMIT2_ADDRESS, QuoterUnavailableError, UNISWAP_V3_ADDRESSES, UNISWAP_V4_ADDRESSES, UnsupportedChainError, V3_SWAP_EXACT_IN, V3_SWAP_EXACT_OUT, buildV3ExactOutSwapExecuteArgs, buildV3ExactOutSwapExecuteCalldata, buildV3SwapExecuteArgs, buildV3SwapExecuteCalldata, calculatePositionVolatilityMetrics, encodeV3Path, getLpDepositAmounts, getLpDepositBreakdown, getLpFundingDeposit, getLpPositionFunding, getMaxLpPositionSize, getUnhedgedLpRanges, getUniswapV3Addresses, getUniswapV4Addresses, preparePositionGamma, quoteSwapExactInViaV3Router, quoteSwapExactOutViaV3Router, quoteV3ExactIn, quoteV3ExactOut, resolveV3SwapRoute, swapExactInViaV3Router, swapExactOutViaV3Router, universalRouterAbi, valuePositionAccrual };
@@ -1,5 +1,5 @@
1
- import { PanopticError, getBlockMeta, getPool, submitWrite } from "./rates-BwZnK0tG.js";
2
- import { AmountExceedsUint128Error, InvalidSwapTokenError, MissingSweepRecipientError, QuoterUnavailableError, getUniswapV4Addresses, universalRouterAbi } from "./router-Y1OxL9Nt.js";
1
+ import { PanopticError, getBlockMeta, getPool, submitWrite } from "./rates-O6Sn-xqL.js";
2
+ import { AmountExceedsUint128Error, InvalidSwapTokenError, MissingSweepRecipientError, QuoterUnavailableError, getUniswapV4Addresses, universalRouterAbi } from "./router-BuAvFpbu.js";
3
3
  import { BaseError, ContractFunctionRevertedError, encodeAbiParameters, encodeFunctionData, encodePacked, erc20Abi, isAddressEqual, maxUint256, zeroAddress } from "viem";
4
4
 
5
5
  //#region src/uniswap/v4/router/encodeSwap.ts
@@ -0,0 +1,112 @@
1
+ import * as viem0 from "viem";
2
+ import * as viem1 from "viem";
3
+ import { HttpTransportConfig, Transport } from "viem";
4
+
5
+ //#region src/rpc/providers.d.ts
6
+ /**
7
+ * The only place that knows RPC vendor URL formats and credentials. Supporting
8
+ * a new vendor means adding one preset here; services select it through env
9
+ * (see `resolveRpcEndpoints`).
10
+ */
11
+ /**
12
+ * The only place that knows RPC vendor URL formats and credentials. Supporting
13
+ * a new vendor means adding one preset here; services select it through env
14
+ * (see `resolveRpcEndpoints`).
15
+ */
16
+ type RpcProviderPreset = {
17
+ /** Env vars holding the API key for `chainId`, highest precedence first. */
18
+ apiKeyEnv: (chainId: number) => readonly string[];
19
+ /** Endpoint for `chainId`, or undefined when the vendor does not serve it. */
20
+ endpoint: (chainId: number, apiKey: string) => {
21
+ url: string;
22
+ headers?: Record<string, string>;
23
+ } | undefined;
24
+ };
25
+ declare const alchemyChains: {
26
+ readonly 1: "eth-mainnet";
27
+ readonly 137: "polygon-mainnet";
28
+ readonly 42161: "arb-mainnet";
29
+ readonly 8453: "base-mainnet";
30
+ readonly 10: "opt-mainnet";
31
+ readonly 130: "unichain-mainnet";
32
+ readonly 4663: "robinhood-mainnet";
33
+ readonly 11155111: "eth-sepolia";
34
+ };
35
+ type AlchemyChainId = keyof typeof alchemyChains;
36
+ type RpcProviderName = 'alchemy' | 'infura' | 'uniblock';
37
+ declare const RPC_PROVIDER_PRESETS: Readonly<Record<RpcProviderName, RpcProviderPreset>>;
38
+ declare function getAlchemyRpcUrl(chainId: AlchemyChainId, alchemyApiKey: string): string;
39
+ declare function getAlchemyWsRpcUrl(chainId: AlchemyChainId, alchemyApiKey: string): string; //#endregion
40
+ //#region src/rpc/redact.d.ts
41
+ /**
42
+ * Strip RPC credentials from text before it is logged or shown. Providers
43
+ * embed keys in the URL path or query, so every URL keeps only its origin;
44
+ * credential header values are masked too.
45
+ */
46
+ declare function redactRpcSecrets(text: string | undefined): string;
47
+
48
+ //#endregion
49
+ //#region src/rpc/resolve.d.ts
50
+ type RpcEnv = Readonly<Record<string, string | undefined>>;
51
+ type RpcEndpoint = {
52
+ /** Preset name, or `'url'` for an explicit URL entry. */
53
+ source: RpcProviderName | 'url';
54
+ url: string;
55
+ /** Credentials that must accompany every request to `url`, and only to `url`. */
56
+ headers: Record<string, string>;
57
+ };
58
+ type ResolveRpcEndpointsOptions = {
59
+ /** Service-specific URL variables honoured when `RPC_URL_<chainId>` is unset, in order. */
60
+ legacyUrlEnv?: readonly string[];
61
+ /** Presets tried, in order, when no URL is configured. Overridden by `RPC_PROVIDERS`. */
62
+ defaultProviders?: readonly RpcProviderName[];
63
+ };
64
+ declare const DEFAULT_RPC_PROVIDERS: readonly RpcProviderName[];
65
+ /**
66
+ * Ordered RPC endpoints for `chainId` (primary first, the rest failover):
67
+ *
68
+ * 1. `RPC_URL_<chainId>` — comma-separated URLs and/or preset names; entry `n`
69
+ * gets headers from `RPC_HEADERS_<chainId>_<n>` ("Name: value;Name: value").
70
+ * 2. Otherwise the service's legacy URL variables (`legacyUrlEnv`).
71
+ * 3. Otherwise the first preset in `RPC_PROVIDERS` (or `defaultProviders`)
72
+ * that has an API key and serves the chain.
73
+ *
74
+ * Blank values count as unset. Throws when nothing resolves.
75
+ */
76
+ declare function resolveRpcEndpoints(chainId: number, env: RpcEnv, options?: ResolveRpcEndpointsOptions): RpcEndpoint[];
77
+
78
+ //#endregion
79
+ //#region src/rpc/transport.d.ts
80
+ type RpcHttpConfig = Omit<HttpTransportConfig, 'fetchOptions'>;
81
+ /** viem HTTP transport for one endpoint, carrying its credential headers. */
82
+ declare function rpcHttp(endpoint: RpcEndpoint, config?: RpcHttpConfig): viem1.HttpTransport<viem0.RpcSchema | undefined, boolean>;
83
+ type RpcTransportOptions = RpcHttpConfig & {
84
+ /**
85
+ * Fail over to later endpoints. Disable for clients that submit
86
+ * transactions: a tx broadcast through one provider and retried through
87
+ * another surfaces as "already known" / "nonce too low" although it was sent.
88
+ */
89
+ failover?: boolean;
90
+ };
91
+ /**
92
+ * Transport over resolved endpoints: plain HTTP for one endpoint, otherwise an
93
+ * unranked viem `fallback` in configured order.
94
+ */
95
+ declare function rpcTransport(endpoints: readonly RpcEndpoint[], {
96
+ failover,
97
+ ...config
98
+ }?: RpcTransportOptions): Transport;
99
+ /** anvil fork arguments for the primary endpoint (`--fork-header` only when it needs one). */
100
+ declare function rpcForkArgs(endpoints: readonly RpcEndpoint[]): {
101
+ forkUrl: string;
102
+ forkHeader?: string;
103
+ };
104
+ /**
105
+ * Value for a Ponder chain's `rpc`. Header-free endpoints stay plain URLs so
106
+ * Ponder keeps its own load balancing; credential headers need a transport.
107
+ */
108
+ declare function ponderRpc(endpoints: readonly RpcEndpoint[]): string | string[] | Transport;
109
+
110
+ //#endregion
111
+ export { AlchemyChainId, DEFAULT_RPC_PROVIDERS, RPC_PROVIDER_PRESETS, ResolveRpcEndpointsOptions, RpcEndpoint, RpcEnv, RpcHttpConfig, RpcProviderName, RpcProviderPreset, RpcTransportOptions, getAlchemyRpcUrl, getAlchemyWsRpcUrl, ponderRpc, redactRpcSecrets, resolveRpcEndpoints, rpcForkArgs, rpcHttp, rpcTransport };
112
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","names":["RpcProviderPreset","Record","alchemyChains","AlchemyChainId","RpcProviderName","RPC_PROVIDER_PRESETS","Readonly","isRpcProviderName","getAlchemyRpcUrl","getAlchemyWsRpcUrl","redactRpcSecrets","RpcProviderName","RpcEnv","Record","Readonly","RpcEndpoint","ResolveRpcEndpointsOptions","DEFAULT_RPC_PROVIDERS","resolveRpcEndpoints","viem1","HttpTransportConfig","Transport","RpcEndpoint","RpcHttpConfig","Omit","rpcHttp","viem0","RpcSchema","HttpTransport","RpcTransportOptions","rpcTransport","failover","rpcForkArgs","ponderRpc"],"sources":["../../src/rpc/providers.d.ts","../../src/rpc/redact.d.ts","../../src/rpc/resolve.d.ts","../../src/rpc/transport.d.ts"],"sourcesContent":null,"mappings":";;;;;;;;;;;;;;;KAKYA,iBAAAA;;;;;IAAAA,GAAAA,EAAAA,MAAAA;IASEE,OAAAA,CAAAA,EAHIW,MAYjB,CAAA,MAAA,EAAA,MAAA,CAAA;EACWV,CAAAA,GAAAA,SAAAA;AACZ,CAAA;AACA,cAZcD,aAYiF,EAAA;EAAA,SAAA,CAAA,EAAA,aAAA;EAAA,SAApCS,GAAAA,EAAAA,iBAAAA;EAAe,SAAEX,KAAAA,EAAAA,aAAAA;EAAiB,SAAzCa,IAAAA,EAAAA,cAAAA;EAAM,SAAfC,EAAAA,EAAAA,aAAAA;EAAQ,SAAA,GAAA,EAAA,kBAAA;EAE3BN,SAAAA,IAAAA,EAAAA,mBAA0BL;EAC1BM,SAAAA,QAAAA,EAAAA,aAA4BN;;KALxCA,cAAAA,gBAA8BD;KAC9BS,eAAAA;cACSN,sBAAsBS,SAASD,OAAOF,iBAAiBX;iBAEpDQ,gBAAAA,UAA0BL;iBAC1BM,kBAAAA,UAA4BN;;;;;;;iBCxB5BO,gBAAAA;;;;KCJZE,MAAAA,GAASE,SAASD;KAClBS,WAAAA;;UAEAX;;;WAGCE;;KAEDG,0BAAAA;EFJAhB;EASEE,YAAAA,CAAAA,EAAAA,SASb,MAAA,EAAA;EACWC;EACAQ,gBAAAA,CAAAA,EAAe,SEZKA,eFYL,EAAA;AAC3B,CAAA;AAA+F,cEX1EM,qBFW0E,EAAA,SEX1CN,eFW0C,EAAA;;;;;AAA5C;AAEnD;AACA;;;;;iBEFwBO,mBAAAA,uBAA0CN,kBAAkBI,6BAA6BM;;;;KCzBrGC,aAAAA,GAAgBC,KAAKJ;;iBAETK,OAAAA,WAAkBH,sBAAsBC,gBAAqEJ,KAAAA,CAAtCS,cAAlBF,KAAAA,CAA+CC,SAAAA;KAChHE,mBAAAA,GAAsBN;;;;;AHAlC;EAScrB,QAAAA,CAAAA,EAAAA,OASb;AACD,CAAA;AACA;AACA;;;AAA4EF,iBGTpD8B,YAAAA,CHSoD9B,SAAAA,EAAAA,SGTnBsB,WHSmBtB,EAAAA,EAAAA;EAAAA,QAAAA;EAAAA,GAAAA;AAAAA,CAAAA,CAAAA,EGTsB6B,mBHStB7B,CAAAA,EGT4CqB,SHS5CrB;;AAAjCc,iBGPnBkB,WAAAA,CHOmBlB,SAAAA,EAAAA,SGPaQ,WHObR,EAAAA,CAAAA,EAAAA;EAAQ,OAAA,EAAA,MAAA;EAE3BN,UAAAA,CAAAA,EAAAA,MAAgB;AACxC,CAAA;;;;;iBGFwByB,SAAAA,qBAA8BX,oCAAoCD"}
@@ -0,0 +1,242 @@
1
+ import { fallback, http } from "viem";
2
+
3
+ //#region src/rpc/providers.ts
4
+ const alchemyChains = {
5
+ 1: "eth-mainnet",
6
+ 137: "polygon-mainnet",
7
+ 42161: "arb-mainnet",
8
+ 8453: "base-mainnet",
9
+ 10: "opt-mainnet",
10
+ 130: "unichain-mainnet",
11
+ 4663: "robinhood-mainnet",
12
+ 11155111: "eth-sepolia"
13
+ };
14
+ function isAlchemyChainId(chainId) {
15
+ return chainId in alchemyChains;
16
+ }
17
+ const infuraChains = {
18
+ 1: "mainnet",
19
+ 10: "optimism-mainnet",
20
+ 137: "polygon-mainnet",
21
+ 8453: "base-mainnet",
22
+ 42161: "arbitrum-mainnet",
23
+ 43114: "avalanche-mainnet",
24
+ 11155111: "sepolia"
25
+ };
26
+ const RPC_PROVIDER_PRESETS = {
27
+ alchemy: {
28
+ apiKeyEnv: () => ["ALCHEMY_API_KEY"],
29
+ endpoint: (chainId, apiKey) => isAlchemyChainId(chainId) ? { url: `https://${alchemyChains[chainId]}.g.alchemy.com/v2/${apiKey}` } : void 0
30
+ },
31
+ infura: {
32
+ apiKeyEnv: () => ["INFURA_API_KEY"],
33
+ endpoint: (chainId, apiKey) => infuraChains[chainId] ? { url: `https://${infuraChains[chainId]}.infura.io/v3/${apiKey}` } : void 0
34
+ },
35
+ uniblock: {
36
+ apiKeyEnv: () => ["UNIBLOCK_API_KEY"],
37
+ endpoint: (chainId, apiKey) => ({
38
+ url: `https://api.uniblock.dev/uni/v1/json-rpc?chainId=${chainId}`,
39
+ headers: { "x-api-key": apiKey }
40
+ })
41
+ }
42
+ };
43
+ function isRpcProviderName(name) {
44
+ return Object.prototype.hasOwnProperty.call(RPC_PROVIDER_PRESETS, name);
45
+ }
46
+ function getAlchemyRpcUrl(chainId, alchemyApiKey) {
47
+ if (alchemyChains[chainId]) return `https://${alchemyChains[chainId]}.g.alchemy.com/v2/${alchemyApiKey}`;
48
+ else throw new Error(`Unsupported chainId: ${chainId}`);
49
+ }
50
+ function getAlchemyWsRpcUrl(chainId, alchemyApiKey) {
51
+ if (alchemyChains[chainId]) return `wss://${alchemyChains[chainId]}.g.alchemy.com/v2/${alchemyApiKey}`;
52
+ else throw new Error(`Unsupported chainId: ${chainId}`);
53
+ }
54
+
55
+ //#endregion
56
+ //#region src/rpc/redact.ts
57
+ const URL_PATTERN = /\b(?:https?|wss?):\/\/[^\s"'<>)]+/giu;
58
+ const CREDENTIAL_HEADER_PATTERN = /\b(x-api-key|authorization|proxy-authorization)(["']?\s*[:=]\s*)(?:(["'])(?:\\.|(?!\3)[^\\\r\n])*\3|[^\r\n]+)/giu;
59
+ function redactUrl(raw) {
60
+ try {
61
+ const url = new URL(raw);
62
+ const port = url.port ? `:${url.port}` : "";
63
+ return `${url.protocol}//${url.hostname}${port}/<redacted>`;
64
+ } catch {
65
+ return "<redacted-url>";
66
+ }
67
+ }
68
+ /**
69
+ * Strip RPC credentials from text before it is logged or shown. Providers
70
+ * embed keys in the URL path or query, so every URL keeps only its origin;
71
+ * credential header values are masked too.
72
+ */
73
+ function redactRpcSecrets(text) {
74
+ if (!text) return "";
75
+ return text.replace(CREDENTIAL_HEADER_PATTERN, "$1$2$3<redacted>$3").replace(URL_PATTERN, redactUrl);
76
+ }
77
+
78
+ //#endregion
79
+ //#region src/rpc/resolve.ts
80
+ const DEFAULT_RPC_PROVIDERS = ["alchemy", "uniblock"];
81
+ function present(value) {
82
+ const trimmed = value?.trim();
83
+ return trimmed ? trimmed : void 0;
84
+ }
85
+ function splitList(value) {
86
+ return (value ?? "").split(",").map((entry) => entry.trim()).filter((entry) => entry !== "");
87
+ }
88
+ function parseHeaders(value, envName) {
89
+ const headers = {};
90
+ for (const pair of splitHeaderPairs(value)) {
91
+ const separator = pair.indexOf(":");
92
+ const name = pair.slice(0, separator).trim();
93
+ if (separator <= 0 || name === "") throw new Error(`${envName} must be "Name: value" pairs separated by ";"`);
94
+ headers[name] = pair.slice(separator + 1).trim();
95
+ }
96
+ return headers;
97
+ }
98
+ function splitHeaderPairs(value) {
99
+ return (value ?? "").split(";").map((pair) => pair.trim()).filter((pair) => pair !== "");
100
+ }
101
+ function isHttpUrl(value) {
102
+ try {
103
+ const { protocol } = new URL(value);
104
+ return protocol === "http:" || protocol === "https:";
105
+ } catch {
106
+ return false;
107
+ }
108
+ }
109
+ function presetApiKey(name, chainId, env) {
110
+ for (const envName of RPC_PROVIDER_PRESETS[name].apiKeyEnv(chainId)) {
111
+ const key = present(env[envName]);
112
+ if (key !== void 0) return key;
113
+ }
114
+ return void 0;
115
+ }
116
+ function presetEndpoint(name, chainId, env) {
117
+ const apiKey = presetApiKey(name, chainId, env);
118
+ if (apiKey === void 0) return void 0;
119
+ const endpoint = RPC_PROVIDER_PRESETS[name].endpoint(chainId, apiKey);
120
+ return endpoint && {
121
+ source: name,
122
+ url: endpoint.url,
123
+ headers: endpoint.headers ?? {}
124
+ };
125
+ }
126
+ function explicitEndpoints(chainId, env) {
127
+ const listEnv = `RPC_URL_${chainId}`;
128
+ return splitList(env[listEnv]).map((entry, index) => {
129
+ if (isHttpUrl(entry)) {
130
+ const headersEnv = `RPC_HEADERS_${chainId}_${index}`;
131
+ return {
132
+ source: "url",
133
+ url: entry,
134
+ headers: parseHeaders(env[headersEnv], headersEnv)
135
+ };
136
+ }
137
+ if (!isRpcProviderName(entry)) throw new Error(`${listEnv} entry "${entry}" is neither an http(s) URL nor a known provider`);
138
+ const endpoint = presetEndpoint(entry, chainId, env);
139
+ if (endpoint === void 0) {
140
+ const keyEnv = RPC_PROVIDER_PRESETS[entry].apiKeyEnv(chainId).join(" or ");
141
+ throw new Error(`${listEnv} lists "${entry}", which needs ${keyEnv} and must serve chain ${chainId}`);
142
+ }
143
+ return endpoint;
144
+ });
145
+ }
146
+ function legacyEndpoints(env, legacyUrlEnv) {
147
+ return legacyUrlEnv.flatMap((envName) => splitList(env[envName]).map((url) => ({
148
+ source: "url",
149
+ url,
150
+ headers: {}
151
+ })));
152
+ }
153
+ function firstPresetEndpoint(chainId, env, providers) {
154
+ for (const name of providers) {
155
+ if (!isRpcProviderName(name)) throw new Error(`RPC_PROVIDERS entry "${name}" is not a known provider`);
156
+ const endpoint = presetEndpoint(name, chainId, env);
157
+ if (endpoint !== void 0) return endpoint;
158
+ }
159
+ return void 0;
160
+ }
161
+ function dedupe(endpoints) {
162
+ const seen = new Set();
163
+ return endpoints.filter((endpoint) => {
164
+ const key = `${endpoint.url}\n${JSON.stringify(endpoint.headers)}`;
165
+ if (seen.has(key)) return false;
166
+ seen.add(key);
167
+ return true;
168
+ });
169
+ }
170
+ /**
171
+ * Ordered RPC endpoints for `chainId` (primary first, the rest failover):
172
+ *
173
+ * 1. `RPC_URL_<chainId>` — comma-separated URLs and/or preset names; entry `n`
174
+ * gets headers from `RPC_HEADERS_<chainId>_<n>` ("Name: value;Name: value").
175
+ * 2. Otherwise the service's legacy URL variables (`legacyUrlEnv`).
176
+ * 3. Otherwise the first preset in `RPC_PROVIDERS` (or `defaultProviders`)
177
+ * that has an API key and serves the chain.
178
+ *
179
+ * Blank values count as unset. Throws when nothing resolves.
180
+ */
181
+ function resolveRpcEndpoints(chainId, env, options = {}) {
182
+ const explicit = explicitEndpoints(chainId, env);
183
+ if (explicit.length > 0) return dedupe(explicit);
184
+ const legacyUrlEnv = options.legacyUrlEnv ?? [];
185
+ const legacy = legacyEndpoints(env, legacyUrlEnv);
186
+ if (legacy.length > 0) return dedupe(legacy);
187
+ const providers = present(env.RPC_PROVIDERS) ? splitList(env.RPC_PROVIDERS) : options.defaultProviders ?? DEFAULT_RPC_PROVIDERS;
188
+ const preset = firstPresetEndpoint(chainId, env, providers);
189
+ if (preset !== void 0) return [preset];
190
+ const keyEnv = [...new Set(providers.flatMap((name) => isRpcProviderName(name) ? RPC_PROVIDER_PRESETS[name].apiKeyEnv(chainId) : []))];
191
+ throw new Error(`No RPC configured for chain ${chainId}: set ${[
192
+ `RPC_URL_${chainId}`,
193
+ ...legacyUrlEnv,
194
+ ...keyEnv
195
+ ].join(", ")}`);
196
+ }
197
+
198
+ //#endregion
199
+ //#region src/rpc/transport.ts
200
+ /** viem HTTP transport for one endpoint, carrying its credential headers. */
201
+ function rpcHttp(endpoint, config = {}) {
202
+ return http(endpoint.url, {
203
+ ...config,
204
+ fetchOptions: { headers: endpoint.headers }
205
+ });
206
+ }
207
+ /**
208
+ * Transport over resolved endpoints: plain HTTP for one endpoint, otherwise an
209
+ * unranked viem `fallback` in configured order.
210
+ */
211
+ function rpcTransport(endpoints, { failover = true,...config } = {}) {
212
+ const [primary, ...rest] = endpoints;
213
+ if (primary === void 0) throw new Error("rpcTransport needs at least one endpoint");
214
+ if (!failover || rest.length === 0) return rpcHttp(primary, config);
215
+ return fallback(endpoints.map((endpoint) => rpcHttp(endpoint, config)), { rank: false });
216
+ }
217
+ /** anvil fork arguments for the primary endpoint (`--fork-header` only when it needs one). */
218
+ function rpcForkArgs(endpoints) {
219
+ const [primary] = endpoints;
220
+ if (primary === void 0) throw new Error("rpcForkArgs needs at least one endpoint");
221
+ const headers = Object.entries(primary.headers);
222
+ if (headers.length === 0) return { forkUrl: primary.url };
223
+ if (headers.length > 1) throw new Error("anvil --fork-header supports one header per fork");
224
+ const [[name, value]] = headers;
225
+ return {
226
+ forkUrl: primary.url,
227
+ forkHeader: `${name}: ${value}`
228
+ };
229
+ }
230
+ /**
231
+ * Value for a Ponder chain's `rpc`. Header-free endpoints stay plain URLs so
232
+ * Ponder keeps its own load balancing; credential headers need a transport.
233
+ */
234
+ function ponderRpc(endpoints) {
235
+ if (endpoints.some((endpoint) => Object.keys(endpoint.headers).length > 0)) return rpcTransport(endpoints);
236
+ const urls = endpoints.map((endpoint) => endpoint.url);
237
+ return urls.length === 1 ? urls[0] : urls;
238
+ }
239
+
240
+ //#endregion
241
+ export { DEFAULT_RPC_PROVIDERS, RPC_PROVIDER_PRESETS, getAlchemyRpcUrl, getAlchemyWsRpcUrl, ponderRpc, redactRpcSecrets, resolveRpcEndpoints, rpcForkArgs, rpcHttp, rpcTransport };
242
+ //# sourceMappingURL=index.js.map