@panoptic-eng/sdk 1.0.40 → 1.0.41

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.
@@ -10024,1502 +10024,1661 @@ function hasLoanOrCredit(tokenId) {
10024
10024
  }
10025
10025
 
10026
10026
  //#endregion
10027
- //#region src/panoptic/v2/sfpmSwap/calldata.ts
10028
- /**
10029
- * Encode the `multicall([mint, burn])` for a swap plan.
10030
- *
10031
- * The order is always `[mint, burn]` — the ERC1155 must be minted before it is
10032
- * burned. Which call carries the inverted (swap) limits is decided in the plan.
10033
- */
10034
- function buildSfpmSwapCalldata(plan) {
10035
- const mintData = encodeFunctionData({
10036
- abi: semiFungiblePositionManagerV3Abi,
10037
- functionName: "mintTokenizedPosition",
10038
- args: [
10039
- plan.poolKey,
10040
- plan.tokenId,
10041
- plan.positionSize,
10042
- plan.mintTickLimits[0],
10043
- plan.mintTickLimits[1]
10044
- ]
10045
- });
10046
- const burnData = encodeFunctionData({
10047
- abi: semiFungiblePositionManagerV3Abi,
10048
- functionName: "burnTokenizedPosition",
10049
- args: [
10050
- plan.poolKey,
10051
- plan.tokenId,
10052
- plan.positionSize,
10053
- plan.burnTickLimits[0],
10054
- plan.burnTickLimits[1]
10055
- ]
10056
- });
10057
- const multicallData = encodeFunctionData({
10058
- abi: semiFungiblePositionManagerV3Abi,
10059
- functionName: "multicall",
10060
- args: [[mintData, burnData]]
10061
- });
10027
+ //#region src/panoptic/v2/formatters/tick.ts
10028
+ const Q192$4 = 1n << 192n;
10029
+ const RAW_PRICE_PRECISION = 40n;
10030
+ function pow10(exponent) {
10031
+ if (exponent < 0n) throw new RangeError("Exponent must be non-negative");
10032
+ return 10n ** exponent;
10033
+ }
10034
+ function absBigint(value) {
10035
+ return value < 0n ? -value : value;
10036
+ }
10037
+ function trimTrailingZeros(value) {
10038
+ const dotIndex = value.indexOf(".");
10039
+ if (dotIndex === -1) return value;
10040
+ let end = value.length;
10041
+ while (end > dotIndex && value[end - 1] === "0") end -= 1;
10042
+ if (end === dotIndex + 1) end = dotIndex;
10043
+ return value.slice(0, end);
10044
+ }
10045
+ function formatRatio$3(numerator, denominator, precision) {
10046
+ if (precision < 0n) throw new RangeError("Precision must be non-negative");
10047
+ const sign = numerator < 0n ? "-" : "";
10048
+ const absNumerator = numerator < 0n ? -numerator : numerator;
10049
+ const scale = pow10(precision);
10050
+ const scaled = (absNumerator * scale + denominator / 2n) / denominator;
10051
+ const integerPart = scaled / scale;
10052
+ const fractionalPart = scaled % scale;
10053
+ if (precision === 0n) return `${sign}${integerPart}`;
10054
+ return `${sign}${integerPart}.${fractionalPart.toString().padStart(Number(precision), "0")}`;
10055
+ }
10056
+ function parseDecimalToFraction(value) {
10057
+ const trimmed = value.trim();
10058
+ if (trimmed.length === 0) throw new Error("Price must be a number");
10059
+ const isNegative = trimmed.startsWith("-");
10060
+ const unsigned = isNegative || trimmed.startsWith("+") ? trimmed.slice(1) : trimmed;
10061
+ const [basePart, exponentPart] = unsigned.toLowerCase().split("e");
10062
+ const [integerStr, fractionalStr = ""] = basePart.split(".");
10063
+ if (integerStr === "" && fractionalStr === "") throw new Error("Price must be a number");
10064
+ const integerDigits = integerStr === "" ? "0" : integerStr;
10065
+ const digits = `${integerDigits}${fractionalStr}`;
10066
+ let numerator = BigInt(digits === "" ? "0" : digits);
10067
+ let denominator = pow10(BigInt(fractionalStr.length));
10068
+ if (exponentPart !== void 0 && exponentPart !== "") {
10069
+ const exponent = BigInt(exponentPart);
10070
+ if (exponent > 0n) numerator *= pow10(exponent);
10071
+ else if (exponent < 0n) denominator *= pow10(-exponent);
10072
+ }
10073
+ if (isNegative) numerator = -numerator;
10062
10074
  return {
10063
- multicallData,
10064
- mintData,
10065
- burnData
10075
+ numerator,
10076
+ denominator
10066
10077
  };
10067
10078
  }
10068
-
10069
- //#endregion
10070
- //#region src/panoptic/v2/sfpmSwap/init.ts
10071
- /**
10072
- * Resolve the `uint64` SFPM poolId for a Uniswap v3 pool, initializing it if needed.
10073
- *
10074
- * `initializeAMMPool` is permissionless and idempotent and **returns the poolId**
10075
- * whether or not the pool was already registered, so a `simulateContract` call is
10076
- * enough to learn the id without sending a transaction. Always resolve the id this
10077
- * way rather than encoding it offline — the SFPM can collision-increment ids.
10078
- */
10079
- async function fetchSfpmV3PoolId(params) {
10080
- const { client, sfpmAddress, token0, token1, fee } = params;
10081
- const vegoid$1 = params.vegoid ?? Number(DEFAULT_VEGOID);
10082
- const { result } = await client.simulateContract({
10083
- address: sfpmAddress,
10084
- abi: semiFungiblePositionManagerV3Abi,
10085
- functionName: "initializeAMMPool",
10086
- args: [
10087
- token0,
10088
- token1,
10089
- fee,
10090
- vegoid$1
10091
- ]
10092
- });
10093
- return BigInt(result);
10079
+ function compareRatios(leftNumerator, leftDenominator, rightNumerator, rightDenominator) {
10080
+ const left = leftNumerator * rightDenominator;
10081
+ const right = rightNumerator * leftDenominator;
10082
+ if (left === right) return 0;
10083
+ return left < right ? -1 : 1;
10094
10084
  }
10095
- /**
10096
- * Ensure a Uniswap v3 pool is registered on the SFPM, sending `initializeAMMPool`
10097
- * only if it is not already registered. Returns the resolved poolId.
10098
- *
10099
- * When `expectedPool` is given, the resolved id is checked to map back to it via
10100
- * `getUniswapV3PoolFromId` — guards against a wrong token/fee triple.
10101
- */
10102
- async function ensureSfpmV3PoolInitialized(params) {
10103
- const { client, wallet, sfpmAddress, token0, token1, fee, expectedPool } = params;
10104
- const vegoid$1 = params.vegoid ?? Number(DEFAULT_VEGOID);
10105
- const poolId = await fetchSfpmV3PoolId({
10106
- client,
10107
- sfpmAddress,
10108
- token0,
10109
- token1,
10110
- fee,
10111
- vegoid: vegoid$1
10112
- });
10113
- const registered = await client.readContract({
10114
- address: sfpmAddress,
10115
- abi: semiFungiblePositionManagerV3Abi,
10116
- functionName: "getUniswapV3PoolFromId",
10117
- args: [poolId]
10118
- });
10119
- let initialized = false;
10120
- let resolved = registered;
10121
- if (getAddress(registered) === zeroAddress) {
10122
- const account = wallet.account;
10123
- if (account === void 0) throw new PanopticError("wallet client has no account");
10124
- const hash = await wallet.writeContract({
10125
- account,
10126
- chain: wallet.chain ?? null,
10127
- address: sfpmAddress,
10128
- abi: semiFungiblePositionManagerV3Abi,
10129
- functionName: "initializeAMMPool",
10130
- args: [
10131
- token0,
10132
- token1,
10133
- fee,
10134
- vegoid$1
10135
- ]
10136
- });
10137
- await client.waitForTransactionReceipt({ hash });
10138
- initialized = true;
10139
- resolved = await client.readContract({
10140
- address: sfpmAddress,
10141
- abi: semiFungiblePositionManagerV3Abi,
10142
- functionName: "getUniswapV3PoolFromId",
10143
- args: [poolId]
10144
- });
10145
- }
10146
- if (expectedPool !== void 0 && getAddress(resolved) !== getAddress(expectedPool)) throw new WrongUniswapPoolError(new PanopticError(`SFPM poolId ${poolId} resolves to ${resolved}, expected ${expectedPool}`));
10085
+ function tickToSqrtPriceX96(tick) {
10086
+ if (tick < MIN_TICK || tick > MAX_TICK) throw new RangeError("Tick out of bounds");
10087
+ const absTick = tick < 0n ? -tick : tick;
10088
+ let ratio = (absTick & 0x1n) !== 0n ? 0xfffcb933bd6fad37aa2d162d1a594001n : 0x100000000000000000000000000000000n;
10089
+ if ((absTick & 0x2n) !== 0n) ratio = ratio * 0xfff97272373d413259a46990580e213an >> 128n;
10090
+ if ((absTick & 0x4n) !== 0n) ratio = ratio * 0xfff2e50f5f656932ef12357cf3c7fdccn >> 128n;
10091
+ if ((absTick & 0x8n) !== 0n) ratio = ratio * 0xffe5caca7e10e4e61c3624eaa0941cd0n >> 128n;
10092
+ if ((absTick & 0x10n) !== 0n) ratio = ratio * 0xffcb9843d60f6159c9db58835c926644n >> 128n;
10093
+ if ((absTick & 0x20n) !== 0n) ratio = ratio * 0xff973b41fa98c081472e6896dfb254c0n >> 128n;
10094
+ if ((absTick & 0x40n) !== 0n) ratio = ratio * 0xff2ea16466c96a3843ec78b326b52861n >> 128n;
10095
+ if ((absTick & 0x80n) !== 0n) ratio = ratio * 0xfe5dee046a99a2a811c461f1969c3053n >> 128n;
10096
+ if ((absTick & 0x100n) !== 0n) ratio = ratio * 0xfcbe86c7900a88aedcffc83b479aa3a4n >> 128n;
10097
+ if ((absTick & 0x200n) !== 0n) ratio = ratio * 0xf987a7253ac413176f2b074cf7815e54n >> 128n;
10098
+ if ((absTick & 0x400n) !== 0n) ratio = ratio * 0xf3392b0822b70005940c7a398e4b70f3n >> 128n;
10099
+ if ((absTick & 0x800n) !== 0n) ratio = ratio * 0xe7159475a2c29b7443b29c7fa6e889d9n >> 128n;
10100
+ if ((absTick & 0x1000n) !== 0n) ratio = ratio * 0xd097f3bdfd2022b8845ad8f792aa5825n >> 128n;
10101
+ if ((absTick & 0x2000n) !== 0n) ratio = ratio * 0xa9f746462d870fdf8a65dc1f90e061e5n >> 128n;
10102
+ if ((absTick & 0x4000n) !== 0n) ratio = ratio * 0x70d869a156d2a1b890bb3df62baf32f7n >> 128n;
10103
+ if ((absTick & 0x8000n) !== 0n) ratio = ratio * 0x31be135f97d08fd981231505542fcfa6n >> 128n;
10104
+ if ((absTick & 0x10000n) !== 0n) ratio = ratio * 0x9aa508b5b7a84e1c677de54f3e99bc9n >> 128n;
10105
+ if ((absTick & 0x20000n) !== 0n) ratio = ratio * 0x5d6af8dedb81196699c329225ee604n >> 128n;
10106
+ if ((absTick & 0x40000n) !== 0n) ratio = ratio * 0x2216e584f5fa1ea926041bedfe98n >> 128n;
10107
+ if ((absTick & 0x80000n) !== 0n) ratio = ratio * 0x48a170391f7dc42444e8fa2n >> 128n;
10108
+ if (tick > 0n) ratio = ((1n << 256n) - 1n) / ratio;
10109
+ const remainderMask = (1n << 32n) - 1n;
10110
+ const sqrtPriceX96 = (ratio >> 32n) + ((ratio & remainderMask) === 0n ? 0n : 1n);
10111
+ return sqrtPriceX96;
10112
+ }
10113
+ const MIN_SQRT_PRICE_X96 = tickToSqrtPriceX96(MIN_TICK);
10114
+ const MAX_SQRT_PRICE_X96 = tickToSqrtPriceX96(MAX_TICK);
10115
+ function getPriceRatioFromSqrtPriceX96(sqrtPriceX96) {
10147
10116
  return {
10148
- poolId,
10149
- initialized
10117
+ numerator: sqrtPriceX96 * sqrtPriceX96,
10118
+ denominator: Q192$4
10150
10119
  };
10151
10120
  }
10152
-
10153
- //#endregion
10154
- //#region src/panoptic/v2/sfpmSwap/plan.ts
10155
- /** Wide, non-inverted band for the paired (non-swapping) call — never triggers a swap. */
10156
- const WIDE_LIMITS = [Number(MIN_TICK) + 1, Number(MAX_TICK) - 1];
10157
- /**
10158
- * Convert a slippage tolerance in bps to a conservative Uniswap tick distance.
10159
- *
10160
- * Ticks are 1.0001^tick, so each tick ≈ 1 bps. This walks up powers of 1.0001
10161
- * until the cumulative price move covers `slippageBps`, matching the hedger-bot's
10162
- * dispatch-path helper so both swap paths agree on band width.
10163
- */
10164
- function slippageBpsToTickDistance(slippageBps) {
10165
- if (slippageBps < 0n || slippageBps > 1000n) throw new PanopticError(`slippage bps ${slippageBps} out of bounds (0..1000)`);
10166
- if (slippageBps === 0n) return 0n;
10167
- let numerator = 1n;
10168
- let denominator = 1n;
10169
- let ticks = 0n;
10170
- while (numerator * 10000n < denominator * (10000n + slippageBps)) {
10171
- numerator *= 10001n;
10172
- denominator *= 10000n;
10173
- ticks += 1n;
10174
- }
10175
- return ticks;
10121
+ function getRawPriceRatio(tick) {
10122
+ return getPriceRatioFromSqrtPriceX96(tickToSqrtPriceX96(tick));
10123
+ }
10124
+ function adjustRatioForDecimals(numerator, denominator, decimals0, decimals1) {
10125
+ const diff = decimals0 - decimals1;
10126
+ if (diff === 0n) return {
10127
+ numerator,
10128
+ denominator
10129
+ };
10130
+ if (diff > 0n) return {
10131
+ numerator: numerator * pow10(diff),
10132
+ denominator
10133
+ };
10134
+ return {
10135
+ numerator,
10136
+ denominator: denominator * pow10(-diff)
10137
+ };
10176
10138
  }
10177
10139
  /**
10178
- * Build the swap plan.
10140
+ * Convert a tick to a raw price string (no decimal adjustment).
10141
+ * Uses the formula: price = 1.0001^tick
10179
10142
  *
10180
- * Mechanism (verified in the Phase 0 fork test):
10181
- * - A single-leg **loan** tokenId (width=0, isLong=false, `asset == tokenType`) moves
10182
- * exactly `positionSize` of the `tokenType` token when its call carries inverted
10183
- * tick limits (`low > high`); the paired call uses a wide band and moves nothing.
10184
- * - `exactIn`: swap on the **mint**; `tokenType` = the **input** token index.
10185
- * - `exactOut`: swap on the **burn** (isLong flips → exact-output); `tokenType` = the
10186
- * **output** token index.
10143
+ * This returns the raw price ratio, not adjusted for token decimals.
10144
+ * A fixed internal precision is used and trailing zeros are trimmed.
10187
10145
  *
10188
- * The inverted band is centered on `currentTick` at ±`slippageBpsToTickDistance`,
10189
- * which the SFPM re-sorts and enforces as an open interval on the post-swap tick.
10146
+ * @param tick - The tick value
10147
+ * @returns Price string
10148
+ *
10149
+ * @example
10150
+ * ```typescript
10151
+ * tickToPrice(0n) // "1"
10152
+ * tickToPrice(1000n) // "1.105..." (approximately)
10153
+ * tickToPrice(-1000n) // "0.904..." (approximately)
10154
+ * tickToPrice(200000n) // Very large number
10155
+ * ```
10190
10156
  */
10191
- function buildSfpmSwapPlan(params) {
10192
- const { sfpmAddress, poolAddress, poolId, kind, zeroForOne, amount, currentTick, slippageBps } = params;
10193
- if (amount <= 0n) throw new PanopticError(`swap amount must be positive (got ${amount})`);
10194
- const distance = slippageBpsToTickDistance(slippageBps);
10195
- if (distance < 1n) throw new PanopticError(`slippageBps ${slippageBps} yields a zero-width tick band; use a larger tolerance`);
10196
- const tokenType = kind === "exactIn" ? zeroForOne ? 0n : 1n : zeroForOne ? 1n : 0n;
10197
- const tokenId = createTokenIdBuilder(poolId).addLoan({
10198
- asset: tokenType,
10199
- tokenType,
10200
- strike: 0n
10201
- }).build();
10202
- const d = Number(distance);
10203
- const low = Math.min(Number(MAX_TICK), currentTick + d);
10204
- const high = Math.max(Number(MIN_TICK), currentTick - d);
10205
- if (low <= high) throw new PanopticError(`slippageBps ${slippageBps} at tick ${currentTick} yields a non-inverted band after clamping`);
10206
- const invertedLimits = [low, high];
10207
- const swapOn = kind === "exactIn" ? "mint" : "burn";
10208
- return {
10209
- sfpmAddress,
10210
- poolAddress,
10211
- poolKey: encodeAbiParameters([{ type: "address" }], [poolAddress]),
10212
- tokenId,
10213
- positionSize: amount,
10214
- mintTickLimits: swapOn === "mint" ? invertedLimits : WIDE_LIMITS,
10215
- burnTickLimits: swapOn === "burn" ? invertedLimits : WIDE_LIMITS,
10216
- swapOn,
10217
- kind
10218
- };
10157
+ function tickToPrice(tick) {
10158
+ const { numerator, denominator } = getRawPriceRatio(tick);
10159
+ const price = formatRatio$3(numerator, denominator, RAW_PRICE_PRECISION);
10160
+ return trimTrailingZeros(price);
10219
10161
  }
10220
-
10221
- //#endregion
10222
- //#region src/panoptic/v2/sfpmSwap/quote.ts
10223
- const UINT128 = 1n << 128n;
10224
- const INT128_MAX = (1n << 127n) - 1n;
10225
- /** Extract the two signed 128-bit slots from a packed `LeftRightSigned` int256. */
10226
- function unpackLeftRightSigned(packed) {
10227
- const u = packed < 0n ? packed + (1n << 256n) : packed;
10228
- const toInt128 = (half) => half > INT128_MAX ? half - UINT128 : half;
10229
- return {
10230
- right: toInt128(u & UINT128 - 1n),
10231
- left: toInt128(u >> 128n & UINT128 - 1n)
10232
- };
10162
+ /**
10163
+ * Convert a tick to a human-readable price with decimal scaling.
10164
+ * Uses the formula: price = 1.0001^tick * 10^(decimals0-decimals1)
10165
+ *
10166
+ * This adjusts for the different decimals of the two tokens in the pair.
10167
+ *
10168
+ * @param tick - The tick value
10169
+ * @param decimals0 - Decimals of token0
10170
+ * @param decimals1 - Decimals of token1
10171
+ * @param precision - Number of decimal places to display
10172
+ * @returns Formatted price string
10173
+ *
10174
+ * @example
10175
+ * ```typescript
10176
+ * // WETH/USDC pool (18 decimals / 6 decimals)
10177
+ * // At tick ~200000, price is roughly $2000 per ETH
10178
+ * tickToPriceDecimalScaled(200000n, 18n, 6n, 2n) // "2000.00" (approximately)
10179
+ *
10180
+ * // For token1/token0 price, swap the decimals
10181
+ * tickToPriceDecimalScaled(200000n, 6n, 18n, 6n) // "0.000500" (approximately)
10182
+ * ```
10183
+ */
10184
+ function tickToPriceDecimalScaled(tick, decimals0, decimals1, precision) {
10185
+ const rawRatio = getRawPriceRatio(tick);
10186
+ const { numerator, denominator } = adjustRatioForDecimals(rawRatio.numerator, rawRatio.denominator, decimals0, decimals1);
10187
+ return formatRatio$3(numerator, denominator, precision);
10233
10188
  }
10234
- /** Wrap an unknown thrown value as a PanopticError, decoding Panoptic reverts when possible. */
10235
- function toPanopticError(err) {
10236
- if (err instanceof PanopticError) return err;
10237
- const parsed = parsePanopticError(err);
10238
- if (parsed) return parsed.error;
10239
- return new PanopticError(err instanceof Error ? err.message : "SFPM swap simulation failed", err instanceof Error ? err : void 0);
10189
+ /**
10190
+ * Convert a sqrtPriceX96 to a human-readable price with decimal scaling.
10191
+ *
10192
+ * Uses the formula: price = (sqrtPriceX96^2 / 2^192) * 10^(decimals0-decimals1)
10193
+ *
10194
+ * @param sqrtPriceX96 - The sqrt price in Q64.96 format
10195
+ * @param decimals0 - Decimals of token0
10196
+ * @param decimals1 - Decimals of token1
10197
+ * @param precision - Number of decimal places to display
10198
+ * @returns Formatted price string
10199
+ *
10200
+ * @example
10201
+ * ```typescript
10202
+ * sqrtPriceX96ToPriceDecimalScaled(2n ** 96n, 18n, 18n, 2n) // "1.00"
10203
+ * ```
10204
+ */
10205
+ function sqrtPriceX96ToPriceDecimalScaled(sqrtPriceX96, decimals0, decimals1, precision) {
10206
+ const rawRatio = getPriceRatioFromSqrtPriceX96(sqrtPriceX96);
10207
+ const { numerator, denominator } = adjustRatioForDecimals(rawRatio.numerator, rawRatio.denominator, decimals0, decimals1);
10208
+ return formatRatio$3(numerator, denominator, precision);
10240
10209
  }
10241
10210
  /**
10242
- * Quote a swap by simulating `SFPM.multicall([mint, burn])` and decoding the swap
10243
- * call's `totalMoved` return. Authoritative — captures the width-0 loan-leg wei
10244
- * rounding a raw QuoterV2 quote would miss.
10211
+ * Convert a price to a tick value.
10212
+ *
10213
+ * @param price - The price string
10214
+ * @param decimals0 - Decimals of token0
10215
+ * @param decimals1 - Decimals of token1
10216
+ * @returns The tick value (rounded to nearest integer)
10217
+ *
10218
+ * @example
10219
+ * ```typescript
10220
+ * // WETH/USDC: What tick for $2000 per ETH?
10221
+ * priceToTick("2000", 18n, 6n) // ~200000n
10222
+ *
10223
+ * // Inverse: What tick for 0.0005 ETH per USDC?
10224
+ * priceToTick("0.0005", 6n, 18n) // ~200000n
10225
+ * ```
10245
10226
  */
10246
- async function quoteSfpmSwap(params) {
10247
- const { client, plan, account, stateOverride, blockNumber } = params;
10248
- const { mintData, burnData } = buildSfpmSwapCalldata(plan);
10249
- const _meta = await getBlockMeta({
10250
- client,
10251
- blockNumber
10252
- });
10253
- try {
10254
- const { result } = await client.simulateContract({
10255
- account,
10256
- address: plan.sfpmAddress,
10257
- abi: semiFungiblePositionManagerV3Abi,
10258
- functionName: "multicall",
10259
- args: [[mintData, burnData]],
10260
- blockNumber,
10261
- stateOverride
10262
- });
10263
- const swapIndex = plan.swapOn === "mint" ? 0 : 1;
10264
- const decoded = decodeFunctionResult({
10265
- abi: semiFungiblePositionManagerV3Abi,
10266
- functionName: plan.swapOn === "mint" ? "mintTokenizedPosition" : "burnTokenizedPosition",
10267
- data: result[swapIndex]
10268
- });
10269
- const finalTick = Number(decoded[2]);
10270
- const { right, left } = unpackLeftRightSigned(decoded[1]);
10271
- const inSlot = [right, left].find((s) => s > 0n);
10272
- const outSlot = [right, left].find((s) => s < 0n);
10273
- if (inSlot === void 0 || outSlot === void 0) return {
10274
- success: false,
10275
- error: new PanopticError(`SFPM swap simulation moved no tokens (totalMoved=${decoded[1]})`),
10276
- _meta
10277
- };
10278
- return {
10279
- success: true,
10280
- data: {
10281
- amountIn: inSlot,
10282
- amountOut: -outSlot,
10283
- finalTick
10284
- },
10285
- gasEstimate: 0n,
10286
- _meta
10287
- };
10288
- } catch (err) {
10289
- return {
10290
- success: false,
10291
- error: toPanopticError(err),
10292
- _meta
10293
- };
10227
+ function priceToTick(price, decimals0, decimals1) {
10228
+ const parsed = parseDecimalToFraction(price);
10229
+ if (parsed.numerator <= 0n) throw new Error("Price must be positive");
10230
+ let targetNumerator = parsed.numerator;
10231
+ let targetDenominator = parsed.denominator;
10232
+ const diff = decimals0 - decimals1;
10233
+ if (diff > 0n) targetDenominator *= pow10(diff);
10234
+ else if (diff < 0n) targetNumerator *= pow10(-diff);
10235
+ let low = MIN_TICK;
10236
+ let high = MAX_TICK;
10237
+ while (low <= high) {
10238
+ const mid = (low + high) / 2n;
10239
+ const { numerator, denominator } = getRawPriceRatio(mid);
10240
+ const cmp = compareRatios(numerator, denominator, targetNumerator, targetDenominator);
10241
+ if (cmp === 0) return mid;
10242
+ if (cmp < 0) low = mid + 1n;
10243
+ else high = mid - 1n;
10294
10244
  }
10245
+ const floorTick = high;
10246
+ const ceilTick = low;
10247
+ if (floorTick < MIN_TICK) return MIN_TICK;
10248
+ if (ceilTick > MAX_TICK) return MAX_TICK;
10249
+ const floorRatio = getRawPriceRatio(floorTick);
10250
+ const ceilRatio = getRawPriceRatio(ceilTick);
10251
+ const floorDiffNumerator = absBigint(targetNumerator * floorRatio.denominator - floorRatio.numerator * targetDenominator);
10252
+ const ceilDiffNumerator = absBigint(targetNumerator * ceilRatio.denominator - ceilRatio.numerator * targetDenominator);
10253
+ const floorDiffDenominator = targetDenominator * floorRatio.denominator;
10254
+ const ceilDiffDenominator = targetDenominator * ceilRatio.denominator;
10255
+ return floorDiffNumerator * ceilDiffDenominator <= ceilDiffNumerator * floorDiffDenominator ? floorTick : ceilTick;
10295
10256
  }
10296
-
10297
- //#endregion
10298
- //#region src/uniswap/v4/router/errors.ts
10299
10257
  /**
10300
- * The chain has no configured Uniswap v4 addresses and none were supplied via
10301
- * overrides.
10258
+ * Convert a sqrtPriceX96 value to the nearest tick.
10259
+ *
10260
+ * @param sqrtPriceX96 - The sqrt price in Q64.96 format
10261
+ * @returns The tick value (rounded to nearest integer)
10262
+ *
10263
+ * @example
10264
+ * ```typescript
10265
+ * const tick = sqrtPriceX96ToTick(2n ** 96n) // 0n
10266
+ * ```
10302
10267
  */
10303
- var UnsupportedChainError = class extends PanopticError {
10304
- name = "UnsupportedChainError";
10305
- constructor(chainId, cause) {
10306
- super(`Uniswap v4 router not configured for chain ${chainId}`, cause);
10307
- this.chainId = chainId;
10268
+ function sqrtPriceX96ToTick(sqrtPriceX96) {
10269
+ if (sqrtPriceX96 <= 0n) throw new Error("Sqrt price must be positive");
10270
+ if (sqrtPriceX96 < MIN_SQRT_PRICE_X96 || sqrtPriceX96 > MAX_SQRT_PRICE_X96) throw new RangeError("Sqrt price out of bounds");
10271
+ let low = MIN_TICK;
10272
+ let high = MAX_TICK;
10273
+ while (low <= high) {
10274
+ const mid = (low + high) / 2n;
10275
+ const midSqrt = tickToSqrtPriceX96(mid);
10276
+ if (midSqrt === sqrtPriceX96) return mid;
10277
+ if (midSqrt < sqrtPriceX96) low = mid + 1n;
10278
+ else high = mid - 1n;
10308
10279
  }
10309
- };
10310
-
10311
- //#endregion
10312
- //#region src/uniswap/v3/router/encodeSwap.ts
10313
- const UINT128_MAX$1 = (1n << 128n) - 1n;
10314
-
10315
- //#endregion
10316
- //#region src/uniswap/v3/abis/quoterV2.ts
10280
+ const floorTick = high;
10281
+ const ceilTick = low;
10282
+ if (floorTick < MIN_TICK) return MIN_TICK;
10283
+ if (ceilTick > MAX_TICK) return MAX_TICK;
10284
+ const floorSqrt = tickToSqrtPriceX96(floorTick);
10285
+ const ceilSqrt = tickToSqrtPriceX96(ceilTick);
10286
+ const floorDiff = absBigint(sqrtPriceX96 - floorSqrt);
10287
+ const ceilDiff = absBigint(ceilSqrt - sqrtPriceX96);
10288
+ return floorDiff <= ceilDiff ? floorTick : ceilTick;
10289
+ }
10317
10290
  /**
10318
- * Minimal Uniswap v3 QuoterV2 ABI (`quoteExactInputSingle`).
10291
+ * Format a tick value for display.
10319
10292
  *
10320
- * QuoterV2 is revert/staticcall-based (nonpayable but intended for `eth_call` /
10321
- * `simulateContract`), so it must be simulated, not read.
10322
- * @module uniswap/v3/abis/quoterV2
10323
- */
10324
- const quoterV2Abi = [{
10325
- type: "function",
10326
- name: "quoteExactInputSingle",
10327
- stateMutability: "nonpayable",
10328
- inputs: [{
10329
- name: "params",
10330
- type: "tuple",
10331
- components: [
10332
- {
10333
- name: "tokenIn",
10334
- type: "address"
10335
- },
10336
- {
10337
- name: "tokenOut",
10338
- type: "address"
10339
- },
10340
- {
10341
- name: "amountIn",
10342
- type: "uint256"
10343
- },
10344
- {
10345
- name: "fee",
10346
- type: "uint24"
10347
- },
10348
- {
10349
- name: "sqrtPriceLimitX96",
10350
- type: "uint160"
10351
- }
10352
- ]
10353
- }],
10354
- outputs: [
10355
- {
10356
- name: "amountOut",
10357
- type: "uint256"
10358
- },
10359
- {
10360
- name: "sqrtPriceX96After",
10361
- type: "uint160"
10362
- },
10363
- {
10364
- name: "initializedTicksCrossed",
10365
- type: "uint32"
10366
- },
10367
- {
10368
- name: "gasEstimate",
10369
- type: "uint256"
10370
- }
10371
- ]
10372
- }];
10373
-
10374
- //#endregion
10375
- //#region src/uniswap/v3/addresses.ts
10376
- /**
10377
- * Verified Uniswap v3 QuoterV2 addresses keyed by chainId. Add a chain only
10378
- * after verifying against the canonical Uniswap v3 deployment listing.
10293
+ * @param tick - The tick value
10294
+ * @returns Formatted tick string
10295
+ *
10296
+ * @example
10297
+ * ```typescript
10298
+ * formatTick(200000n) // "200000"
10299
+ * formatTick(-50000n) // "-50000"
10300
+ * ```
10379
10301
  */
10380
- const UNISWAP_V3_ADDRESSES = { 1: {
10381
- quoterV2: "0x61fFE014bA17989E743c5F6cB21bF9697530B21e",
10382
- nonfungiblePositionManager: "0xC36442b4a4522E871399CD717aBDD847Ab11FE88"
10383
- } };
10302
+ function formatTick(tick) {
10303
+ return tick.toString();
10304
+ }
10384
10305
  /**
10385
- * Resolve Uniswap v3 addresses for a chain, applying optional overrides.
10306
+ * Get the price at a specific tick, returning both token0/token1 and token1/token0 prices.
10386
10307
  *
10387
- * @throws {UnsupportedChainError} when the chain is not listed and overrides do
10388
- * not supply every required address.
10308
+ * @param tick - The tick value
10309
+ * @param decimals0 - Decimals of token0
10310
+ * @param decimals1 - Decimals of token1
10311
+ * @param precision - Number of decimal places to display
10312
+ * @returns Object with both price directions
10313
+ *
10314
+ * @example
10315
+ * ```typescript
10316
+ * const prices = getPricesAtTick(200000n, 18n, 6n, 2n)
10317
+ * // prices.token0PerToken1 = "0.00" (very small)
10318
+ * // prices.token1PerToken0 = "2000.00" (USDC per ETH)
10319
+ * ```
10389
10320
  */
10390
- function getUniswapV3Addresses(chainId, overrides) {
10391
- const id = Number(chainId);
10392
- const base = UNISWAP_V3_ADDRESSES[id];
10393
- const merged = {
10394
- ...base,
10395
- ...overrides
10321
+ function getPricesAtTick(tick, decimals0, decimals1, precision) {
10322
+ const rawRatio = getRawPriceRatio(tick);
10323
+ const adjustedRatio = adjustRatioForDecimals(rawRatio.numerator, rawRatio.denominator, decimals0, decimals1);
10324
+ return {
10325
+ token0PerToken1: formatRatio$3(adjustedRatio.denominator, adjustedRatio.numerator, precision),
10326
+ token1PerToken0: formatRatio$3(adjustedRatio.numerator, adjustedRatio.denominator, precision)
10396
10327
  };
10397
- if (!merged.quoterV2 || !merged.nonfungiblePositionManager) throw new UnsupportedChainError(BigInt(id));
10398
- return merged;
10399
10328
  }
10400
-
10401
- //#endregion
10402
- //#region src/uniswap/v3/router/quote.ts
10403
- const BPS_DENOMINATOR$3 = 10000n;
10404
- const UINT128_MAX = (1n << 128n) - 1n;
10405
10329
  /**
10406
- * Quote an exact-in v3 swap. Returns `null` when the quote reverts (e.g. the
10407
- * pool does not exist / has no liquidity) so callers can skip that pool when
10408
- * ranking venues, rather than aborting the whole cycle.
10330
+ * Format a tick range for display.
10331
+ *
10332
+ * @param tickLower - Lower tick
10333
+ * @param tickUpper - Upper tick
10334
+ * @returns Formatted tick range string
10335
+ *
10336
+ * @example
10337
+ * ```typescript
10338
+ * formatTickRange(-50000n, 200000n) // "-50000 - 200000"
10339
+ * ```
10409
10340
  */
10410
- async function quoteV3ExactIn(params) {
10411
- const { client, chainId, tokenIn, tokenOut, fee, amountIn, slippageBps, blockNumber } = params;
10412
- if (amountIn < 0n || amountIn > UINT128_MAX) throw new PanopticError(`amountIn ${amountIn} exceeds uint128 maximum`);
10413
- if (slippageBps < 0n || slippageBps > BPS_DENOMINATOR$3) throw new PanopticError(`invalid slippageBps ${slippageBps}, must be 0..10000`);
10414
- const { quoterV2 } = getUniswapV3Addresses(chainId, params.addresses);
10415
- try {
10416
- const { result } = await client.simulateContract({
10417
- address: quoterV2,
10418
- abi: quoterV2Abi,
10419
- functionName: "quoteExactInputSingle",
10420
- blockNumber,
10421
- args: [{
10422
- tokenIn,
10423
- tokenOut,
10424
- amountIn,
10425
- fee: Number(fee),
10426
- sqrtPriceLimitX96: 0n
10427
- }]
10428
- });
10429
- const [amountOut, , , gasEstimate] = result;
10430
- const amountOutMinimum = amountOut * (BPS_DENOMINATOR$3 - slippageBps) / BPS_DENOMINATOR$3;
10431
- return {
10432
- amountOut,
10433
- amountOutMinimum,
10434
- gasEstimate
10435
- };
10436
- } catch (err) {
10437
- if (isRevert(err)) return null;
10438
- throw err;
10439
- }
10440
- }
10441
- /** True only for genuine contract reverts (missing pool / no liquidity). */
10442
- function isRevert(err) {
10443
- return err instanceof BaseError && err.walk((e) => e instanceof ContractFunctionRevertedError) instanceof ContractFunctionRevertedError;
10341
+ function formatTickRange(tickLower, tickUpper) {
10342
+ return `${tickLower} - ${tickUpper}`;
10444
10343
  }
10445
-
10446
- //#endregion
10447
- //#region src/panoptic/v2/reads/sfpm.ts
10448
10344
  /**
10449
- * Resolve an SFPM poolId to its corresponding Uniswap V3 pool address.
10345
+ * Format a price range for display.
10450
10346
  *
10451
- * Calls `SemiFungiblePositionManagerV3.getUniswapV3PoolFromId(poolId)`.
10347
+ * @param tickLower - Lower tick
10348
+ * @param tickUpper - Upper tick
10349
+ * @param decimals0 - Decimals of token0
10350
+ * @param decimals1 - Decimals of token1
10351
+ * @param precision - Number of decimal places to display
10352
+ * @returns Formatted price range string
10353
+ *
10354
+ * @example
10355
+ * ```typescript
10356
+ * formatPriceRange(0n, 0n, 18n, 18n, 2n) // "1.00 - 1.00"
10357
+ * ```
10452
10358
  */
10453
- async function getUniswapV3PoolFromId(params) {
10454
- const { client, sfpmAddress, poolId } = params;
10455
- return client.readContract({
10456
- address: sfpmAddress,
10457
- abi: semiFungiblePositionManagerV3Abi,
10458
- functionName: "getUniswapV3PoolFromId",
10459
- args: [poolId]
10460
- });
10359
+ function formatPriceRange(tickLower, tickUpper, decimals0, decimals1, precision) {
10360
+ const lower = tickToPriceDecimalScaled(tickLower, decimals0, decimals1, precision);
10361
+ const upper = tickToPriceDecimalScaled(tickUpper, decimals0, decimals1, precision);
10362
+ return `${lower} - ${upper}`;
10461
10363
  }
10462
10364
  /**
10463
- * Resolve an SFPM poolId to its corresponding Uniswap V4 pool key.
10365
+ * Calculate the tick spacing for a given fee tier.
10464
10366
  *
10465
- * Calls `SemiFungiblePositionManagerV4.getUniswapV4PoolKeyFromId(poolId)`.
10367
+ * @param feeBps - Fee in basis points (e.g., 500n for 0.05%)
10368
+ * @returns Tick spacing
10369
+ *
10370
+ * @example
10371
+ * ```typescript
10372
+ * getTickSpacing(100n) // 1n (0.01% fee tier)
10373
+ * getTickSpacing(500n) // 10n (0.05% fee tier)
10374
+ * getTickSpacing(3000n) // 60n (0.30% fee tier)
10375
+ * getTickSpacing(10000n) // 200n (1.00% fee tier)
10376
+ * ```
10466
10377
  */
10467
- async function getUniswapV4PoolKeyFromId(params) {
10468
- const { client, sfpmAddress, poolId } = params;
10469
- const raw = await client.readContract({
10470
- address: sfpmAddress,
10471
- abi: semiFungiblePositionManagerV4Abi,
10472
- functionName: "getUniswapV4PoolKeyFromId",
10473
- args: [poolId]
10474
- });
10475
- return {
10476
- currency0: raw.currency0,
10477
- currency1: raw.currency1,
10478
- fee: BigInt(raw.fee),
10479
- tickSpacing: BigInt(raw.tickSpacing),
10480
- hooks: raw.hooks
10481
- };
10378
+ function getTickSpacing(feeBps) {
10379
+ switch (feeBps) {
10380
+ case 100n: return 1n;
10381
+ case 500n: return 10n;
10382
+ case 3000n: return 60n;
10383
+ case 10000n: return 200n;
10384
+ default: {
10385
+ const spacing = feeBps / 50n;
10386
+ return spacing > 1n ? spacing : 1n;
10387
+ }
10388
+ }
10482
10389
  }
10483
10390
  /**
10484
- * Get the enforced tick limits for a pool from the SFPM.
10391
+ * Round a tick to the nearest valid tick for a given tick spacing.
10485
10392
  *
10486
- * @param params - The parameters
10487
- * @returns The min and max enforced ticks
10393
+ * @param tick - The tick to round
10394
+ * @param tickSpacing - The tick spacing
10395
+ * @returns Rounded tick
10396
+ *
10397
+ * @example
10398
+ * ```typescript
10399
+ * roundToTickSpacing(12345n, 10n) // 12340n
10400
+ * roundToTickSpacing(12345n, 60n) // 12360n
10401
+ * roundToTickSpacing(-12345n, 10n) // -12350n
10402
+ * ```
10488
10403
  */
10489
- async function getEnforcedTickLimits(params) {
10490
- const { client, sfpmAddress, poolId } = params;
10491
- const [minTick, maxTick] = await client.readContract({
10492
- address: sfpmAddress,
10493
- abi: semiFungiblePositionManagerV4Abi,
10494
- functionName: "getEnforcedTickLimits",
10495
- args: [poolId]
10496
- });
10497
- return {
10498
- minEnforcedTick: minTick,
10499
- maxEnforcedTick: maxTick
10500
- };
10404
+ function roundToTickSpacing(tick, tickSpacing) {
10405
+ const remainder = tick % tickSpacing;
10406
+ if (remainder === 0n) return tick;
10407
+ if (tick >= 0n) return remainder >= tickSpacing / 2n ? tick + (tickSpacing - remainder) : tick - remainder;
10408
+ const absRemainder = -remainder;
10409
+ return absRemainder >= tickSpacing / 2n ? tick - (tickSpacing - absRemainder) : tick + absRemainder;
10501
10410
  }
10502
10411
  /**
10503
- * Fetch liquidity breakdown for a batch of chunks via SFPM.getAccountLiquidity().
10412
+ * Compute slippage-bounded tick limits around the current tick.
10504
10413
  *
10505
- * Uses multicall for efficiency. Returns one result per input chunk,
10506
- * along with block metadata for freshness tracking.
10414
+ * 1 tick ≈ 1 basis point (0.01 %) of price change, so a `toleranceBps`
10415
+ * of 500 allows roughly 5 % price movement. The result is clamped to
10416
+ * the protocol's `[MIN_TICK, MAX_TICK]` range.
10417
+ *
10418
+ * Useful for setting `tickLimitLow` / `tickLimitHigh` on `openPosition`
10419
+ * and `closePosition` to protect against MEV sandwiches and volatile
10420
+ * tick moves.
10421
+ *
10422
+ * @param currentTick - The current pool tick (must be within [MIN_TICK, MAX_TICK]).
10423
+ * @param toleranceBps - Slippage tolerance in basis points (≈ ticks). Must be non-negative.
10424
+ * @returns Clamped `{ low, high }` tick limits.
10425
+ * @throws {RangeError} If `toleranceBps` is negative or `currentTick` is out of bounds.
10426
+ *
10427
+ * @example
10428
+ * ```typescript
10429
+ * const { low, high } = tickLimits(200_000n, 500n)
10430
+ * // low = 199_500n
10431
+ * // high = 200_500n
10432
+ *
10433
+ * await openPosition({ ..., tickLimitLow: low, tickLimitHigh: high })
10434
+ * ```
10507
10435
  */
10508
- async function getChunkLiquidities(params) {
10509
- const { client, sfpmAddress, poolKeyBytes, chunks } = params;
10510
- if (chunks.length > MAX_TRACKED_CHUNKS) throw new ChunkLimitError(BigInt(chunks.length), 0n);
10511
- const _meta = params._meta ?? await getBlockMeta({ client });
10512
- if (chunks.length === 0) return {
10513
- results: [],
10514
- _meta
10515
- };
10516
- const multicallResults = await client.multicall({
10517
- contracts: chunks.map((chunk) => ({
10518
- address: sfpmAddress,
10519
- abi: semiFungiblePositionManagerV4Abi,
10520
- functionName: "getAccountLiquidity",
10521
- args: [
10522
- poolKeyBytes,
10523
- chunk.owner,
10524
- chunk.tokenType,
10525
- Number(chunk.tickLower),
10526
- Number(chunk.tickUpper)
10527
- ]
10528
- })),
10529
- allowFailure: true
10530
- });
10531
- const results = multicallResults.map((result) => {
10532
- if (result.status === "failure") return {
10533
- netLiquidity: 0n,
10534
- removedLiquidity: 0n,
10535
- totalLiquidity: 0n,
10536
- shortLiquidity: 0n,
10537
- longLiquidity: 0n
10538
- };
10539
- const packed = result.result;
10540
- const netLiquidity = packed & (1n << 128n) - 1n;
10541
- const removedLiquidity = packed >> 128n;
10542
- const totalLiquidity = netLiquidity + removedLiquidity;
10543
- return {
10544
- netLiquidity,
10545
- removedLiquidity,
10546
- totalLiquidity,
10547
- shortLiquidity: totalLiquidity,
10548
- longLiquidity: removedLiquidity
10549
- };
10550
- });
10436
+ function tickLimits(currentTick, toleranceBps) {
10437
+ if (toleranceBps < 0n) throw new RangeError(`toleranceBps must be non-negative, got ${toleranceBps}`);
10438
+ if (currentTick < MIN_TICK || currentTick > MAX_TICK) throw new RangeError(`currentTick ${currentTick} is out of bounds [${MIN_TICK}, ${MAX_TICK}]`);
10439
+ const rawLow = currentTick - toleranceBps;
10440
+ const rawHigh = currentTick + toleranceBps;
10441
+ const low = rawLow < MIN_TICK ? MIN_TICK : rawLow;
10442
+ const high = rawHigh > MAX_TICK ? MAX_TICK : rawHigh;
10443
+ if (low > high) throw new RangeError(`Computed tick limits are inverted: low ${low} > high ${high} (currentTick=${currentTick}, toleranceBps=${toleranceBps})`);
10551
10444
  return {
10552
- results,
10553
- _meta
10445
+ low,
10446
+ high
10554
10447
  };
10555
10448
  }
10556
10449
 
10557
10450
  //#endregion
10558
- //#region src/panoptic/v2/reads/factory.ts
10451
+ //#region src/panoptic/v2/tokenId/deriveUniqueTokenId.ts
10452
+ const POOL_ID_MASK$2 = (1n << 64n) - 1n;
10453
+ const MAX_LEGS = TOKEN_ID_BITS.MAX_LEGS;
10454
+ const MAX_OPTION_RATIO = LEG_LIMITS.MAX_RATIO;
10455
+ const Q192$3 = 1n << 192n;
10559
10456
  /**
10560
- * Get the token URI from a PanopticFactory NFT.
10457
+ * Floor integer square root for bigints (Newton's method). Mirrors the isqrt
10458
+ * used in reads/collateralEstimate.ts (buildNeutralLeg).
10561
10459
  */
10562
- async function getFactoryTokenURI(params) {
10563
- const { client, factoryAddress, version, tokenId } = params;
10564
- const abi = version === "v3" ? panopticFactoryV3Abi : panopticFactoryV4Abi;
10565
- return client.readContract({
10566
- address: factoryAddress,
10567
- abi,
10568
- functionName: "tokenURI",
10569
- args: [tokenId]
10570
- });
10460
+ function isqrt$1(value) {
10461
+ if (value < 0n) throw new PanopticError("isqrt of negative number");
10462
+ if (value < 2n) return value;
10463
+ let x = value;
10464
+ let y = x + 1n >> 1n;
10465
+ while (y < x) {
10466
+ x = y;
10467
+ y = x + value / x >> 1n;
10468
+ }
10469
+ return x;
10571
10470
  }
10572
- /**
10573
- * Get the owner of a PanopticFactory NFT.
10574
- */
10575
- async function getFactoryOwnerOf(params) {
10576
- const { client, factoryAddress, version, tokenId } = params;
10577
- const abi = version === "v3" ? panopticFactoryV3Abi : panopticFactoryV4Abi;
10578
- return client.readContract({
10579
- address: factoryAddress,
10580
- abi,
10581
- functionName: "ownerOf",
10582
- args: [tokenId]
10471
+ const DEFAULT_TICK_SPACING = 200n;
10472
+ /**
10473
+ * Target notional (in wei of the tokenType-side asset) for the appended
10474
+ * tiny credit leg. Chosen at 10 wei — small enough to be economically
10475
+ * meaningless against any real position size, large enough to safely
10476
+ * clear rounding on the SFPM's width-2 internal chunk math without any
10477
+ * risk of underflowing to zero (which would revert ChunkHasZeroLiquidity).
10478
+ */
10479
+ const TINY_CREDIT_TARGET_NOTIONAL_WEI = 10n;
10480
+ /**
10481
+ * Pick a signedStrike for the appended width=0 credit leg such that:
10482
+ * 1. positionSize · 1.0001^signedStrike ≈ TINY_CREDIT_TARGET_NOTIONAL_WEI
10483
+ * (~10 wei — economically meaningless but comfortably above any
10484
+ * SFPM width-2 chunk rounding);
10485
+ * 2. the leg's tick range (SFPM internally treats width=0 as width=2, so
10486
+ * the range is `strike ± tickSpacing`) stays strictly inside
10487
+ * `[MIN_POOL_TICK, MAX_POOL_TICK]` — otherwise the getSqrtRatioAtTick
10488
+ * call reverts with `InvalidTick`.
10489
+ *
10490
+ * Closed form: `1.0001^signedStrike = target/positionSize`, so
10491
+ * `sqrtKrawX96 = isqrt(target · 2^192 / positionSize)` and
10492
+ * `signedStrike = sqrtPriceX96ToTick(sqrtKrawX96)`. If the notional-optimal
10493
+ * strike falls outside the safe range, we clamp inward. Clamping raises the
10494
+ * notional but keeps it << position size for any reasonable strike.
10495
+ */
10496
+ function computeTinyCreditSignedStrike(positionSize, tickSpacing) {
10497
+ if (positionSize <= 0n) throw new PanopticError("computeTinyCreditSignedStrike: positionSize must be > 0");
10498
+ const minSafe = MIN_TICK + tickSpacing + 1n;
10499
+ const maxSafe = MAX_TICK - tickSpacing - 1n;
10500
+ let signedStrike;
10501
+ try {
10502
+ const sqrtKrawX96 = isqrt$1(TINY_CREDIT_TARGET_NOTIONAL_WEI * Q192$3 / positionSize);
10503
+ signedStrike = sqrtPriceX96ToTick(sqrtKrawX96);
10504
+ } catch {
10505
+ signedStrike = minSafe;
10506
+ }
10507
+ if (signedStrike < minSafe) return minSafe;
10508
+ if (signedStrike > maxSafe) return maxSafe;
10509
+ return signedStrike;
10510
+ }
10511
+ /**
10512
+ * Assemble the tiny credit leg struct, picking a strike that avoids
10513
+ * colliding with any existing width=0 credit leg on the same (asset,
10514
+ * tokenType) pair.
10515
+ *
10516
+ * The encoded strike stored in the tokenId is
10517
+ * `asset === 0 ? signedStrike : -signedStrike` (mirrors
10518
+ * {@link buildNeutralLeg} in reads/collateralEstimate.ts).
10519
+ */
10520
+ function pickUniqueTinyCreditLeg(baseTokenId, legIndex, positionSize, tickSpacing) {
10521
+ const asset = 1n;
10522
+ const tokenType = 0n;
10523
+ const existingLegs = decodeAllLegs(baseTokenId);
10524
+ const maxSafe = MAX_TICK - tickSpacing - 1n;
10525
+ let signedStrike = computeTinyCreditSignedStrike(positionSize, tickSpacing);
10526
+ while (signedStrike <= maxSafe) {
10527
+ const candidate = asset === 0n ? signedStrike : -signedStrike;
10528
+ let collides = false;
10529
+ for (const leg of existingLegs) if (leg.width === 0n && leg.isLong && leg.asset === asset && leg.tokenType === tokenType && leg.strike === candidate) {
10530
+ collides = true;
10531
+ break;
10532
+ }
10533
+ if (!collides) break;
10534
+ signedStrike += 1n;
10535
+ }
10536
+ if (signedStrike > maxSafe) throw new PanopticError("deriveUniqueTokenId: exhausted strike space picking a unique tiny credit leg");
10537
+ const encodedStrike = asset === 0n ? signedStrike : -signedStrike;
10538
+ return {
10539
+ index: legIndex,
10540
+ asset,
10541
+ tokenType,
10542
+ optionRatio: 1n,
10543
+ isLong: 1n,
10544
+ riskPartner: legIndex,
10545
+ strike: encodedStrike,
10546
+ width: 0n
10547
+ };
10548
+ }
10549
+ function appendTinyCreditLeg(baseTokenId, baseLegCount, positionSize, tickSpacing) {
10550
+ const newLeg = pickUniqueTinyCreditLeg(baseTokenId, baseLegCount, positionSize, tickSpacing);
10551
+ return addLegToTokenId(baseTokenId, newLeg);
10552
+ }
10553
+ function scaleRatios(baseTokenId, targetPositionSize) {
10554
+ const legs = decodeAllLegs(baseTokenId);
10555
+ const maxRatio = legs.reduce((m, leg) => leg.optionRatio > m ? leg.optionRatio : m, 0n);
10556
+ const N = MAX_OPTION_RATIO / maxRatio;
10557
+ if (N < 2n) throw new PanopticError("deriveUniqueTokenId: cannot derive a unique tokenId — all 4 leg slots used and optionRatios already near the 127 ceiling");
10558
+ const newPositionSize = (targetPositionSize + N - 1n) / N;
10559
+ const poolId = baseTokenId & POOL_ID_MASK$2;
10560
+ let out = poolId;
10561
+ for (const leg of legs) out = addLegToTokenId(out, {
10562
+ index: leg.index,
10563
+ asset: leg.asset,
10564
+ tokenType: leg.tokenType,
10565
+ optionRatio: leg.optionRatio * N,
10566
+ isLong: leg.isLong ? 1n : 0n,
10567
+ riskPartner: leg.riskPartner,
10568
+ strike: leg.strike,
10569
+ width: leg.width
10583
10570
  });
10571
+ return {
10572
+ newTokenId: out,
10573
+ newPositionSize,
10574
+ effectivePositionSize: newPositionSize * N,
10575
+ strategy: "ratio-scale"
10576
+ };
10584
10577
  }
10585
10578
  /**
10586
- * Construct NFT metadata for a pool via the factory contract.
10579
+ * Derive a tokenId unique from `baseTokenId` for a partial reduction.
10580
+ *
10581
+ * Prefers a tiny-credit-leg extension (arbitrary new size). Falls back to
10582
+ * optionRatio scaling only when the base tokenId already occupies all 4 leg
10583
+ * slots.
10587
10584
  */
10588
- async function getFactoryConstructMetadata(params) {
10589
- const { client, factoryAddress, version, panopticPoolAddress, symbol0, symbol1, fee } = params;
10590
- const abi = version === "v3" ? panopticFactoryV3Abi : panopticFactoryV4Abi;
10591
- return client.readContract({
10592
- address: factoryAddress,
10593
- abi,
10594
- functionName: "constructMetadata",
10595
- args: [
10596
- panopticPoolAddress,
10597
- symbol0,
10598
- symbol1,
10599
- fee
10600
- ]
10601
- });
10585
+ function deriveUniqueTokenId(params) {
10586
+ const { baseTokenId, targetPositionSize, tickSpacing = DEFAULT_TICK_SPACING } = params;
10587
+ if (targetPositionSize <= 0n) throw new PanopticError("deriveUniqueTokenId: targetPositionSize must be > 0");
10588
+ const legCount = countLegs(baseTokenId);
10589
+ if (legCount === 0n) throw new PanopticError("deriveUniqueTokenId: baseTokenId has no legs");
10590
+ if (legCount < MAX_LEGS) {
10591
+ const newTokenId = appendTinyCreditLeg(baseTokenId, legCount, targetPositionSize, tickSpacing);
10592
+ return {
10593
+ newTokenId,
10594
+ newPositionSize: targetPositionSize,
10595
+ effectivePositionSize: targetPositionSize,
10596
+ strategy: "tiny-credit"
10597
+ };
10598
+ }
10599
+ return scaleRatios(baseTokenId, targetPositionSize);
10602
10600
  }
10603
10601
  /**
10604
- * Get the PanopticPool address for a given pool and risk engine.
10602
+ * Re-export decoded leg count so callers can gate UI on the strategy that
10603
+ * would be chosen (e.g. show a divisibility hint on ratio-scale positions).
10605
10604
  */
10606
- async function getPanopticPoolAddress(params) {
10607
- const { client, factoryAddress, riskEngine } = params;
10608
- if (params.version === "v3") return client.readContract({
10609
- address: factoryAddress,
10610
- abi: panopticFactoryV3Abi,
10611
- functionName: "getPanopticPool",
10612
- args: [params.univ3pool, riskEngine]
10613
- });
10614
- return client.readContract({
10615
- address: factoryAddress,
10616
- abi: panopticFactoryV4Abi,
10617
- functionName: "getPanopticPool",
10618
- args: [{
10619
- currency0: params.poolKey.currency0,
10620
- currency1: params.poolKey.currency1,
10621
- fee: Number(params.poolKey.fee),
10622
- tickSpacing: Number(params.poolKey.tickSpacing),
10623
- hooks: params.poolKey.hooks
10624
- }, riskEngine]
10625
- });
10605
+ function planDeriveStrategy(baseTokenId) {
10606
+ return countLegs(baseTokenId) < MAX_LEGS ? "tiny-credit" : "ratio-scale";
10626
10607
  }
10608
+
10609
+ //#endregion
10610
+ //#region src/panoptic/v2/sfpmSwap/calldata.ts
10627
10611
  /**
10628
- * Mine for an optimal pool address salt with high rarity.
10612
+ * Encode the `multicall([mint, burn])` for a swap plan.
10613
+ *
10614
+ * The order is always `[mint, burn]` — the ERC1155 must be minted before it is
10615
+ * burned. Which call carries the inverted (swap) limits is decided in the plan.
10629
10616
  */
10630
- async function minePoolAddress(params) {
10631
- const { client, factoryAddress, deployerAddress, riskEngine, salt, loops, minTargetRarity } = params;
10632
- let result;
10633
- if (params.version === "v3") result = await client.readContract({
10634
- address: factoryAddress,
10635
- abi: panopticFactoryV3Abi,
10636
- functionName: "minePoolAddress",
10617
+ function buildSfpmSwapCalldata(plan) {
10618
+ const mintData = encodeFunctionData({
10619
+ abi: semiFungiblePositionManagerV3Abi,
10620
+ functionName: "mintTokenizedPosition",
10637
10621
  args: [
10638
- deployerAddress,
10639
- params.v3Pool,
10640
- riskEngine,
10641
- salt,
10642
- loops,
10643
- minTargetRarity
10622
+ plan.poolKey,
10623
+ plan.tokenId,
10624
+ plan.positionSize,
10625
+ plan.mintTickLimits[0],
10626
+ plan.mintTickLimits[1]
10644
10627
  ]
10645
10628
  });
10646
- else result = await client.readContract({
10647
- address: factoryAddress,
10648
- abi: panopticFactoryV4Abi,
10649
- functionName: "minePoolAddress",
10629
+ const burnData = encodeFunctionData({
10630
+ abi: semiFungiblePositionManagerV3Abi,
10631
+ functionName: "burnTokenizedPosition",
10650
10632
  args: [
10651
- deployerAddress,
10652
- {
10653
- currency0: params.poolKey.currency0,
10654
- currency1: params.poolKey.currency1,
10655
- fee: Number(params.poolKey.fee),
10656
- tickSpacing: Number(params.poolKey.tickSpacing),
10657
- hooks: params.poolKey.hooks
10658
- },
10659
- riskEngine,
10660
- salt,
10661
- loops,
10662
- minTargetRarity
10633
+ plan.poolKey,
10634
+ plan.tokenId,
10635
+ plan.positionSize,
10636
+ plan.burnTickLimits[0],
10637
+ plan.burnTickLimits[1]
10663
10638
  ]
10664
10639
  });
10640
+ const multicallData = encodeFunctionData({
10641
+ abi: semiFungiblePositionManagerV3Abi,
10642
+ functionName: "multicall",
10643
+ args: [[mintData, burnData]]
10644
+ });
10665
10645
  return {
10666
- bestSalt: BigInt(result[0]),
10667
- highestRarity: result[1]
10646
+ multicallData,
10647
+ mintData,
10648
+ burnData
10668
10649
  };
10669
10650
  }
10651
+
10652
+ //#endregion
10653
+ //#region src/panoptic/v2/sfpmSwap/init.ts
10670
10654
  /**
10671
- * Simulate a pool deployment to get the predicted pool address.
10655
+ * Resolve the `uint64` SFPM poolId for a Uniswap v3 pool, initializing it if needed.
10672
10656
  *
10673
- * Uses `simulateContract` on `deployNewPool` — the return value is the new pool address
10674
- * without actually executing the transaction.
10657
+ * `initializeAMMPool` is permissionless and idempotent and **returns the poolId**
10658
+ * whether or not the pool was already registered, so a `simulateContract` call is
10659
+ * enough to learn the id without sending a transaction. Always resolve the id this
10660
+ * way rather than encoding it offline — the SFPM can collision-increment ids.
10675
10661
  */
10676
- async function simulateDeployNewPool(params) {
10677
- const { client, factoryAddress, account, riskEngine, salt } = params;
10678
- if (params.version === "v3") {
10679
- const { result: result$1 } = await client.simulateContract({
10680
- address: factoryAddress,
10681
- abi: panopticFactoryV3Abi,
10682
- functionName: "deployNewPool",
10683
- args: [
10684
- params.token0,
10685
- params.token1,
10686
- Number(params.fee),
10687
- riskEngine,
10688
- salt
10689
- ],
10690
- account
10691
- });
10692
- return result$1;
10693
- }
10662
+ async function fetchSfpmV3PoolId(params) {
10663
+ const { client, sfpmAddress, token0, token1, fee } = params;
10664
+ const vegoid$1 = params.vegoid ?? Number(DEFAULT_VEGOID);
10694
10665
  const { result } = await client.simulateContract({
10695
- address: factoryAddress,
10696
- abi: panopticFactoryV4Abi,
10697
- functionName: "deployNewPool",
10666
+ address: sfpmAddress,
10667
+ abi: semiFungiblePositionManagerV3Abi,
10668
+ functionName: "initializeAMMPool",
10698
10669
  args: [
10699
- {
10700
- currency0: params.poolKey.currency0,
10701
- currency1: params.poolKey.currency1,
10702
- fee: Number(params.poolKey.fee),
10703
- tickSpacing: Number(params.poolKey.tickSpacing),
10704
- hooks: params.poolKey.hooks
10705
- },
10706
- riskEngine,
10707
- salt
10708
- ],
10709
- account
10670
+ token0,
10671
+ token1,
10672
+ fee,
10673
+ vegoid$1
10674
+ ]
10710
10675
  });
10711
- return result;
10676
+ return BigInt(result);
10712
10677
  }
10713
10678
  /**
10714
- * Resolve an SFPM poolId to its PanopticPool address.
10679
+ * Ensure a Uniswap v3 pool is registered on the SFPM, sending `initializeAMMPool`
10680
+ * only if it is not already registered. Returns the resolved poolId.
10715
10681
  *
10716
- * Chains two on-chain lookups:
10717
- * 1. SFPM: poolId → Uniswap pool address (V3) or pool key (V4)
10718
- * 2. Factory: Uniswap pool + riskEngine → PanopticPool address
10682
+ * When `expectedPool` is given, the resolved id is checked to map back to it via
10683
+ * `getUniswapV3PoolFromId` — guards against a wrong token/fee triple.
10719
10684
  */
10720
- async function getPanopticPoolFromPoolId(params) {
10721
- const { client, sfpmAddress, factoryAddress, riskEngine, poolId, version } = params;
10722
- if (version === "v3") {
10723
- const univ3pool = await getUniswapV3PoolFromId({
10724
- client,
10725
- sfpmAddress,
10726
- poolId
10727
- });
10728
- return getPanopticPoolAddress({
10729
- version: "v3",
10730
- client,
10731
- factoryAddress,
10732
- univ3pool,
10733
- riskEngine
10734
- });
10735
- }
10736
- const poolKey = await getUniswapV4PoolKeyFromId({
10685
+ async function ensureSfpmV3PoolInitialized(params) {
10686
+ const { client, wallet, sfpmAddress, token0, token1, fee, expectedPool } = params;
10687
+ const vegoid$1 = params.vegoid ?? Number(DEFAULT_VEGOID);
10688
+ const poolId = await fetchSfpmV3PoolId({
10737
10689
  client,
10738
10690
  sfpmAddress,
10739
- poolId
10691
+ token0,
10692
+ token1,
10693
+ fee,
10694
+ vegoid: vegoid$1
10740
10695
  });
10741
- return getPanopticPoolAddress({
10742
- version: "v4",
10743
- client,
10744
- factoryAddress,
10745
- poolKey,
10746
- riskEngine
10696
+ const registered = await client.readContract({
10697
+ address: sfpmAddress,
10698
+ abi: semiFungiblePositionManagerV3Abi,
10699
+ functionName: "getUniswapV3PoolFromId",
10700
+ args: [poolId]
10747
10701
  });
10702
+ let initialized = false;
10703
+ let resolved = registered;
10704
+ if (getAddress(registered) === zeroAddress) {
10705
+ const account = wallet.account;
10706
+ if (account === void 0) throw new PanopticError("wallet client has no account");
10707
+ const hash = await wallet.writeContract({
10708
+ account,
10709
+ chain: wallet.chain ?? null,
10710
+ address: sfpmAddress,
10711
+ abi: semiFungiblePositionManagerV3Abi,
10712
+ functionName: "initializeAMMPool",
10713
+ args: [
10714
+ token0,
10715
+ token1,
10716
+ fee,
10717
+ vegoid$1
10718
+ ]
10719
+ });
10720
+ await client.waitForTransactionReceipt({ hash });
10721
+ initialized = true;
10722
+ resolved = await client.readContract({
10723
+ address: sfpmAddress,
10724
+ abi: semiFungiblePositionManagerV3Abi,
10725
+ functionName: "getUniswapV3PoolFromId",
10726
+ args: [poolId]
10727
+ });
10728
+ }
10729
+ if (expectedPool !== void 0 && getAddress(resolved) !== getAddress(expectedPool)) throw new WrongUniswapPoolError(new PanopticError(`SFPM poolId ${poolId} resolves to ${resolved}, expected ${expectedPool}`));
10730
+ return {
10731
+ poolId,
10732
+ initialized
10733
+ };
10748
10734
  }
10735
+
10736
+ //#endregion
10737
+ //#region src/panoptic/v2/sfpmSwap/plan.ts
10738
+ /** Wide, non-inverted band for the paired (non-swapping) call — never triggers a swap. */
10739
+ const WIDE_LIMITS = [Number(MIN_TICK) + 1, Number(MAX_TICK) - 1];
10749
10740
  /**
10750
- * Resolve an SFPM poolId to its PanopticPool address without knowing the version.
10741
+ * Convert a slippage tolerance in bps to a conservative Uniswap tick distance.
10751
10742
  *
10752
- * Tries both V3 and V4 lookups in parallel. The factory returns `address(0)` for
10753
- * non-existent pools, so the non-zero result identifies the correct version.
10743
+ * Ticks are 1.0001^tick, so each tick ≈ 1 bps. This walks up powers of 1.0001
10744
+ * until the cumulative price move covers `slippageBps`, matching the hedger-bot's
10745
+ * dispatch-path helper so both swap paths agree on band width.
10746
+ */
10747
+ function slippageBpsToTickDistance(slippageBps) {
10748
+ if (slippageBps < 0n || slippageBps > 1000n) throw new PanopticError(`slippage bps ${slippageBps} out of bounds (0..1000)`);
10749
+ if (slippageBps === 0n) return 0n;
10750
+ let numerator = 1n;
10751
+ let denominator = 1n;
10752
+ let ticks = 0n;
10753
+ while (numerator * 10000n < denominator * (10000n + slippageBps)) {
10754
+ numerator *= 10001n;
10755
+ denominator *= 10000n;
10756
+ ticks += 1n;
10757
+ }
10758
+ return ticks;
10759
+ }
10760
+ /**
10761
+ * Build the swap plan.
10754
10762
  *
10755
- * At least one of `v3` or `v4` must be provided.
10763
+ * Mechanism (verified in the Phase 0 fork test):
10764
+ * - A single-leg **loan** tokenId (width=0, isLong=false, `asset == tokenType`) moves
10765
+ * exactly `positionSize` of the `tokenType` token when its call carries inverted
10766
+ * tick limits (`low > high`); the paired call uses a wide band and moves nothing.
10767
+ * - `exactIn`: swap on the **mint**; `tokenType` = the **input** token index.
10768
+ * - `exactOut`: swap on the **burn** (isLong flips → exact-output); `tokenType` = the
10769
+ * **output** token index.
10756
10770
  *
10757
- * @throws {PanopticValidationError} If no version config is provided or neither resolves.
10771
+ * The inverted band is centered on `currentTick` at ±`slippageBpsToTickDistance`,
10772
+ * which the SFPM re-sorts and enforces as an open interval on the post-swap tick.
10758
10773
  */
10759
- async function resolvePanopticPoolFromPoolId(params) {
10760
- const { client, poolId, riskEngine, v3, v4 } = params;
10761
- if (!v3 && !v4) throw new Error("At least one of v3 or v4 must be provided");
10762
- const isNotFoundError = (err) => {
10763
- if (typeof err === "object" && err !== null && "name" in err) {
10764
- const name = err.name;
10765
- return name === "ContractFunctionExecutionError" || name === "ContractFunctionRevertedError";
10766
- }
10767
- return false;
10768
- };
10769
- const results = await Promise.all([v3 ? getPanopticPoolFromPoolId({
10770
- version: "v3",
10771
- client,
10772
- sfpmAddress: v3.sfpmAddress,
10773
- factoryAddress: v3.factoryAddress,
10774
- riskEngine,
10775
- poolId
10776
- }).catch((err) => {
10777
- if (isNotFoundError(err)) return zeroAddress;
10778
- throw err;
10779
- }) : Promise.resolve(zeroAddress), v4 ? getPanopticPoolFromPoolId({
10780
- version: "v4",
10781
- client,
10782
- sfpmAddress: v4.sfpmAddress,
10783
- factoryAddress: v4.factoryAddress,
10784
- riskEngine,
10785
- poolId
10786
- }).catch((err) => {
10787
- if (isNotFoundError(err)) return zeroAddress;
10788
- throw err;
10789
- }) : Promise.resolve(zeroAddress)]);
10790
- const [v3Result, v4Result] = results;
10791
- if (v3Result !== zeroAddress) return {
10792
- panopticPoolAddress: v3Result,
10793
- version: "v3"
10794
- };
10795
- if (v4Result !== zeroAddress) return {
10796
- panopticPoolAddress: v4Result,
10797
- version: "v4"
10774
+ function buildSfpmSwapPlan(params) {
10775
+ const { sfpmAddress, poolAddress, poolId, kind, zeroForOne, amount, currentTick, slippageBps } = params;
10776
+ if (amount <= 0n) throw new PanopticError(`swap amount must be positive (got ${amount})`);
10777
+ const distance = slippageBpsToTickDistance(slippageBps);
10778
+ if (distance < 1n) throw new PanopticError(`slippageBps ${slippageBps} yields a zero-width tick band; use a larger tolerance`);
10779
+ const tokenType = kind === "exactIn" ? zeroForOne ? 0n : 1n : zeroForOne ? 1n : 0n;
10780
+ const tokenId = createTokenIdBuilder(poolId).addLoan({
10781
+ asset: tokenType,
10782
+ tokenType,
10783
+ strike: 0n
10784
+ }).build();
10785
+ const d = Number(distance);
10786
+ const low = Math.min(Number(MAX_TICK), currentTick + d);
10787
+ const high = Math.max(Number(MIN_TICK), currentTick - d);
10788
+ if (low <= high) throw new PanopticError(`slippageBps ${slippageBps} at tick ${currentTick} yields a non-inverted band after clamping`);
10789
+ const invertedLimits = [low, high];
10790
+ const swapOn = kind === "exactIn" ? "mint" : "burn";
10791
+ return {
10792
+ sfpmAddress,
10793
+ poolAddress,
10794
+ poolKey: encodeAbiParameters([{ type: "address" }], [poolAddress]),
10795
+ tokenId,
10796
+ positionSize: amount,
10797
+ mintTickLimits: swapOn === "mint" ? invertedLimits : WIDE_LIMITS,
10798
+ burnTickLimits: swapOn === "burn" ? invertedLimits : WIDE_LIMITS,
10799
+ swapOn,
10800
+ kind
10798
10801
  };
10799
- throw new Error(`No PanopticPool found for poolId ${poolId}`);
10800
10802
  }
10801
10803
 
10802
10804
  //#endregion
10803
- //#region src/panoptic/v2/reads/minePoolAddressLocal.ts
10804
- const MASK_80 = (1n << 80n) - 1n;
10805
- const MASK_40 = (1n << 40n) - 1n;
10806
- const MASK_96 = (1n << 96n) - 1n;
10807
-
10808
- //#endregion
10809
- //#region src/panoptic/v2/abis/stateView.ts
10810
- /**
10811
- * Minimal Uniswap V4 StateView ABI for fee growth reads.
10812
- * Only includes functions needed by streamia history.
10813
- */
10814
- const stateViewAbi = [
10815
- {
10816
- inputs: [{
10817
- internalType: "PoolId",
10818
- name: "poolId",
10819
- type: "bytes32"
10820
- }],
10821
- name: "getSlot0",
10822
- outputs: [
10823
- {
10824
- internalType: "uint160",
10825
- name: "sqrtPriceX96",
10826
- type: "uint160"
10827
- },
10828
- {
10829
- internalType: "int24",
10830
- name: "tick",
10831
- type: "int24"
10832
- },
10833
- {
10834
- internalType: "uint24",
10835
- name: "protocolFee",
10836
- type: "uint24"
10837
- },
10838
- {
10839
- internalType: "uint24",
10840
- name: "lpFee",
10841
- type: "uint24"
10842
- }
10843
- ],
10844
- stateMutability: "view",
10845
- type: "function"
10846
- },
10847
- {
10848
- inputs: [{
10849
- internalType: "PoolId",
10850
- name: "poolId",
10851
- type: "bytes32"
10852
- }],
10853
- name: "getFeeGrowthGlobals",
10854
- outputs: [{
10855
- internalType: "uint256",
10856
- name: "feeGrowthGlobal0",
10857
- type: "uint256"
10858
- }, {
10859
- internalType: "uint256",
10860
- name: "feeGrowthGlobal1",
10861
- type: "uint256"
10862
- }],
10863
- stateMutability: "view",
10864
- type: "function"
10865
- },
10866
- {
10867
- inputs: [{
10868
- internalType: "PoolId",
10869
- name: "poolId",
10870
- type: "bytes32"
10871
- }, {
10872
- internalType: "int24",
10873
- name: "tick",
10874
- type: "int24"
10875
- }],
10876
- name: "getTickInfo",
10877
- outputs: [
10878
- {
10879
- internalType: "uint128",
10880
- name: "liquidityGross",
10881
- type: "uint128"
10882
- },
10883
- {
10884
- internalType: "int128",
10885
- name: "liquidityNet",
10886
- type: "int128"
10887
- },
10888
- {
10889
- internalType: "uint256",
10890
- name: "feeGrowthOutside0X128",
10891
- type: "uint256"
10892
- },
10893
- {
10894
- internalType: "uint256",
10895
- name: "feeGrowthOutside1X128",
10896
- type: "uint256"
10897
- }
10898
- ],
10899
- stateMutability: "view",
10900
- type: "function"
10901
- },
10902
- {
10903
- inputs: [{
10904
- internalType: "PoolId",
10905
- name: "poolId",
10906
- type: "bytes32"
10907
- }],
10908
- name: "getLiquidity",
10909
- outputs: [{
10910
- internalType: "uint128",
10911
- name: "liquidity",
10912
- type: "uint128"
10913
- }],
10914
- stateMutability: "view",
10915
- type: "function"
10916
- }
10917
- ];
10918
-
10919
- //#endregion
10920
- //#region src/panoptic/v2/abis/uniswapV3Pool.ts
10805
+ //#region src/panoptic/v2/sfpmSwap/quote.ts
10806
+ const UINT128 = 1n << 128n;
10807
+ const INT128_MAX = (1n << 127n) - 1n;
10808
+ /** Extract the two signed 128-bit slots from a packed `LeftRightSigned` int256. */
10809
+ function unpackLeftRightSigned(packed) {
10810
+ const u = packed < 0n ? packed + (1n << 256n) : packed;
10811
+ const toInt128 = (half) => half > INT128_MAX ? half - UINT128 : half;
10812
+ return {
10813
+ right: toInt128(u & UINT128 - 1n),
10814
+ left: toInt128(u >> 128n & UINT128 - 1n)
10815
+ };
10816
+ }
10817
+ /** Wrap an unknown thrown value as a PanopticError, decoding Panoptic reverts when possible. */
10818
+ function toPanopticError(err) {
10819
+ if (err instanceof PanopticError) return err;
10820
+ const parsed = parsePanopticError(err);
10821
+ if (parsed) return parsed.error;
10822
+ return new PanopticError(err instanceof Error ? err.message : "SFPM swap simulation failed", err instanceof Error ? err : void 0);
10823
+ }
10921
10824
  /**
10922
- * Minimal Uniswap V3 Pool ABI for fee growth reads.
10923
- * Only includes functions needed by streamia history.
10825
+ * Quote a swap by simulating `SFPM.multicall([mint, burn])` and decoding the swap
10826
+ * call's `totalMoved` return. Authoritative — captures the width-0 loan-leg wei
10827
+ * rounding a raw QuoterV2 quote would miss.
10924
10828
  */
10925
- const uniswapV3PoolAbi = [
10926
- {
10927
- inputs: [],
10928
- name: "slot0",
10929
- outputs: [
10930
- {
10931
- internalType: "uint160",
10932
- name: "sqrtPriceX96",
10933
- type: "uint160"
10934
- },
10935
- {
10936
- internalType: "int24",
10937
- name: "tick",
10938
- type: "int24"
10939
- },
10940
- {
10941
- internalType: "uint16",
10942
- name: "observationIndex",
10943
- type: "uint16"
10944
- },
10945
- {
10946
- internalType: "uint16",
10947
- name: "observationCardinality",
10948
- type: "uint16"
10949
- },
10950
- {
10951
- internalType: "uint16",
10952
- name: "observationCardinalityNext",
10953
- type: "uint16"
10954
- },
10955
- {
10956
- internalType: "uint8",
10957
- name: "feeProtocol",
10958
- type: "uint8"
10959
- },
10960
- {
10961
- internalType: "bool",
10962
- name: "unlocked",
10963
- type: "bool"
10964
- }
10965
- ],
10966
- stateMutability: "view",
10967
- type: "function"
10968
- },
10969
- {
10970
- inputs: [],
10971
- name: "feeGrowthGlobal0X128",
10972
- outputs: [{
10973
- internalType: "uint256",
10974
- name: "",
10975
- type: "uint256"
10976
- }],
10977
- stateMutability: "view",
10978
- type: "function"
10979
- },
10980
- {
10981
- inputs: [],
10982
- name: "feeGrowthGlobal1X128",
10983
- outputs: [{
10984
- internalType: "uint256",
10985
- name: "",
10986
- type: "uint256"
10987
- }],
10988
- stateMutability: "view",
10989
- type: "function"
10990
- },
10991
- {
10992
- inputs: [{
10993
- internalType: "int24",
10994
- name: "",
10995
- type: "int24"
10996
- }],
10997
- name: "ticks",
10998
- outputs: [
10999
- {
11000
- internalType: "uint128",
11001
- name: "liquidityGross",
11002
- type: "uint128"
10829
+ async function quoteSfpmSwap(params) {
10830
+ const { client, plan, account, stateOverride, blockNumber } = params;
10831
+ const { mintData, burnData } = buildSfpmSwapCalldata(plan);
10832
+ const _meta = await getBlockMeta({
10833
+ client,
10834
+ blockNumber
10835
+ });
10836
+ try {
10837
+ const { result } = await client.simulateContract({
10838
+ account,
10839
+ address: plan.sfpmAddress,
10840
+ abi: semiFungiblePositionManagerV3Abi,
10841
+ functionName: "multicall",
10842
+ args: [[mintData, burnData]],
10843
+ blockNumber,
10844
+ stateOverride
10845
+ });
10846
+ const swapIndex = plan.swapOn === "mint" ? 0 : 1;
10847
+ const decoded = decodeFunctionResult({
10848
+ abi: semiFungiblePositionManagerV3Abi,
10849
+ functionName: plan.swapOn === "mint" ? "mintTokenizedPosition" : "burnTokenizedPosition",
10850
+ data: result[swapIndex]
10851
+ });
10852
+ const finalTick = Number(decoded[2]);
10853
+ const { right, left } = unpackLeftRightSigned(decoded[1]);
10854
+ const inSlot = [right, left].find((s) => s > 0n);
10855
+ const outSlot = [right, left].find((s) => s < 0n);
10856
+ if (inSlot === void 0 || outSlot === void 0) return {
10857
+ success: false,
10858
+ error: new PanopticError(`SFPM swap simulation moved no tokens (totalMoved=${decoded[1]})`),
10859
+ _meta
10860
+ };
10861
+ return {
10862
+ success: true,
10863
+ data: {
10864
+ amountIn: inSlot,
10865
+ amountOut: -outSlot,
10866
+ finalTick
11003
10867
  },
10868
+ gasEstimate: 0n,
10869
+ _meta
10870
+ };
10871
+ } catch (err) {
10872
+ return {
10873
+ success: false,
10874
+ error: toPanopticError(err),
10875
+ _meta
10876
+ };
10877
+ }
10878
+ }
10879
+
10880
+ //#endregion
10881
+ //#region src/uniswap/v4/router/errors.ts
10882
+ /**
10883
+ * The chain has no configured Uniswap v4 addresses and none were supplied via
10884
+ * overrides.
10885
+ */
10886
+ var UnsupportedChainError = class extends PanopticError {
10887
+ name = "UnsupportedChainError";
10888
+ constructor(chainId, cause) {
10889
+ super(`Uniswap v4 router not configured for chain ${chainId}`, cause);
10890
+ this.chainId = chainId;
10891
+ }
10892
+ };
10893
+
10894
+ //#endregion
10895
+ //#region src/uniswap/v3/router/encodeSwap.ts
10896
+ const UINT128_MAX$1 = (1n << 128n) - 1n;
10897
+
10898
+ //#endregion
10899
+ //#region src/uniswap/v3/abis/quoterV2.ts
10900
+ /**
10901
+ * Minimal Uniswap v3 QuoterV2 ABI (`quoteExactInputSingle`).
10902
+ *
10903
+ * QuoterV2 is revert/staticcall-based (nonpayable but intended for `eth_call` /
10904
+ * `simulateContract`), so it must be simulated, not read.
10905
+ * @module uniswap/v3/abis/quoterV2
10906
+ */
10907
+ const quoterV2Abi = [{
10908
+ type: "function",
10909
+ name: "quoteExactInputSingle",
10910
+ stateMutability: "nonpayable",
10911
+ inputs: [{
10912
+ name: "params",
10913
+ type: "tuple",
10914
+ components: [
11004
10915
  {
11005
- internalType: "int128",
11006
- name: "liquidityNet",
11007
- type: "int128"
10916
+ name: "tokenIn",
10917
+ type: "address"
11008
10918
  },
11009
10919
  {
11010
- internalType: "uint256",
11011
- name: "feeGrowthOutside0X128",
11012
- type: "uint256"
10920
+ name: "tokenOut",
10921
+ type: "address"
11013
10922
  },
11014
10923
  {
11015
- internalType: "uint256",
11016
- name: "feeGrowthOutside1X128",
10924
+ name: "amountIn",
11017
10925
  type: "uint256"
11018
10926
  },
11019
10927
  {
11020
- internalType: "int56",
11021
- name: "tickCumulativeOutside",
11022
- type: "int56"
10928
+ name: "fee",
10929
+ type: "uint24"
11023
10930
  },
11024
10931
  {
11025
- internalType: "uint160",
11026
- name: "secondsPerLiquidityOutsideX128",
10932
+ name: "sqrtPriceLimitX96",
11027
10933
  type: "uint160"
11028
- },
11029
- {
11030
- internalType: "uint32",
11031
- name: "secondsOutside",
11032
- type: "uint32"
11033
- },
11034
- {
11035
- internalType: "bool",
11036
- name: "initialized",
11037
- type: "bool"
11038
10934
  }
11039
- ],
11040
- stateMutability: "view",
11041
- type: "function"
11042
- },
11043
- {
11044
- inputs: [],
11045
- name: "liquidity",
11046
- outputs: [{
11047
- internalType: "uint128",
11048
- name: "",
11049
- type: "uint128"
11050
- }],
11051
- stateMutability: "view",
11052
- type: "function"
11053
- },
11054
- {
11055
- inputs: [],
11056
- name: "fee",
11057
- outputs: [{
11058
- internalType: "uint24",
11059
- name: "",
11060
- type: "uint24"
11061
- }],
11062
- stateMutability: "view",
11063
- type: "function"
11064
- },
11065
- {
11066
- inputs: [],
11067
- name: "token0",
11068
- outputs: [{
11069
- internalType: "address",
11070
- name: "",
11071
- type: "address"
11072
- }],
11073
- stateMutability: "view",
11074
- type: "function"
11075
- },
11076
- {
11077
- inputs: [],
11078
- name: "token1",
11079
- outputs: [{
11080
- internalType: "address",
11081
- name: "",
11082
- type: "address"
11083
- }],
11084
- stateMutability: "view",
11085
- type: "function"
11086
- },
11087
- {
11088
- inputs: [],
11089
- name: "tickSpacing",
11090
- outputs: [{
11091
- internalType: "int24",
11092
- name: "",
11093
- type: "int24"
11094
- }],
11095
- stateMutability: "view",
11096
- type: "function"
11097
- }
11098
- ];
10935
+ ]
10936
+ }],
10937
+ outputs: [
10938
+ {
10939
+ name: "amountOut",
10940
+ type: "uint256"
10941
+ },
10942
+ {
10943
+ name: "sqrtPriceX96After",
10944
+ type: "uint160"
10945
+ },
10946
+ {
10947
+ name: "initializedTicksCrossed",
10948
+ type: "uint32"
10949
+ },
10950
+ {
10951
+ name: "gasEstimate",
10952
+ type: "uint256"
10953
+ }
10954
+ ]
10955
+ }];
11099
10956
 
11100
10957
  //#endregion
11101
- //#region src/panoptic/v2/formatters/tick.ts
11102
- const Q192$3 = 1n << 192n;
11103
- const RAW_PRICE_PRECISION = 40n;
11104
- function pow10(exponent) {
11105
- if (exponent < 0n) throw new RangeError("Exponent must be non-negative");
11106
- return 10n ** exponent;
11107
- }
11108
- function absBigint(value) {
11109
- return value < 0n ? -value : value;
11110
- }
11111
- function trimTrailingZeros(value) {
11112
- const dotIndex = value.indexOf(".");
11113
- if (dotIndex === -1) return value;
11114
- let end = value.length;
11115
- while (end > dotIndex && value[end - 1] === "0") end -= 1;
11116
- if (end === dotIndex + 1) end = dotIndex;
11117
- return value.slice(0, end);
11118
- }
11119
- function formatRatio$3(numerator, denominator, precision) {
11120
- if (precision < 0n) throw new RangeError("Precision must be non-negative");
11121
- const sign = numerator < 0n ? "-" : "";
11122
- const absNumerator = numerator < 0n ? -numerator : numerator;
11123
- const scale = pow10(precision);
11124
- const scaled = (absNumerator * scale + denominator / 2n) / denominator;
11125
- const integerPart = scaled / scale;
11126
- const fractionalPart = scaled % scale;
11127
- if (precision === 0n) return `${sign}${integerPart}`;
11128
- return `${sign}${integerPart}.${fractionalPart.toString().padStart(Number(precision), "0")}`;
11129
- }
11130
- function parseDecimalToFraction(value) {
11131
- const trimmed = value.trim();
11132
- if (trimmed.length === 0) throw new Error("Price must be a number");
11133
- const isNegative = trimmed.startsWith("-");
11134
- const unsigned = isNegative || trimmed.startsWith("+") ? trimmed.slice(1) : trimmed;
11135
- const [basePart, exponentPart] = unsigned.toLowerCase().split("e");
11136
- const [integerStr, fractionalStr = ""] = basePart.split(".");
11137
- if (integerStr === "" && fractionalStr === "") throw new Error("Price must be a number");
11138
- const integerDigits = integerStr === "" ? "0" : integerStr;
11139
- const digits = `${integerDigits}${fractionalStr}`;
11140
- let numerator = BigInt(digits === "" ? "0" : digits);
11141
- let denominator = pow10(BigInt(fractionalStr.length));
11142
- if (exponentPart !== void 0 && exponentPart !== "") {
11143
- const exponent = BigInt(exponentPart);
11144
- if (exponent > 0n) numerator *= pow10(exponent);
11145
- else if (exponent < 0n) denominator *= pow10(-exponent);
11146
- }
11147
- if (isNegative) numerator = -numerator;
11148
- return {
11149
- numerator,
11150
- denominator
10958
+ //#region src/uniswap/v3/addresses.ts
10959
+ /**
10960
+ * Verified Uniswap v3 QuoterV2 addresses keyed by chainId. Add a chain only
10961
+ * after verifying against the canonical Uniswap v3 deployment listing.
10962
+ */
10963
+ const UNISWAP_V3_ADDRESSES = { 1: {
10964
+ quoterV2: "0x61fFE014bA17989E743c5F6cB21bF9697530B21e",
10965
+ nonfungiblePositionManager: "0xC36442b4a4522E871399CD717aBDD847Ab11FE88"
10966
+ } };
10967
+ /**
10968
+ * Resolve Uniswap v3 addresses for a chain, applying optional overrides.
10969
+ *
10970
+ * @throws {UnsupportedChainError} when the chain is not listed and overrides do
10971
+ * not supply every required address.
10972
+ */
10973
+ function getUniswapV3Addresses(chainId, overrides) {
10974
+ const id = Number(chainId);
10975
+ const base = UNISWAP_V3_ADDRESSES[id];
10976
+ const merged = {
10977
+ ...base,
10978
+ ...overrides
11151
10979
  };
10980
+ if (!merged.quoterV2 || !merged.nonfungiblePositionManager) throw new UnsupportedChainError(BigInt(id));
10981
+ return merged;
11152
10982
  }
11153
- function compareRatios(leftNumerator, leftDenominator, rightNumerator, rightDenominator) {
11154
- const left = leftNumerator * rightDenominator;
11155
- const right = rightNumerator * leftDenominator;
11156
- if (left === right) return 0;
11157
- return left < right ? -1 : 1;
11158
- }
11159
- function tickToSqrtPriceX96(tick) {
11160
- if (tick < MIN_TICK || tick > MAX_TICK) throw new RangeError("Tick out of bounds");
11161
- const absTick = tick < 0n ? -tick : tick;
11162
- let ratio = (absTick & 0x1n) !== 0n ? 0xfffcb933bd6fad37aa2d162d1a594001n : 0x100000000000000000000000000000000n;
11163
- if ((absTick & 0x2n) !== 0n) ratio = ratio * 0xfff97272373d413259a46990580e213an >> 128n;
11164
- if ((absTick & 0x4n) !== 0n) ratio = ratio * 0xfff2e50f5f656932ef12357cf3c7fdccn >> 128n;
11165
- if ((absTick & 0x8n) !== 0n) ratio = ratio * 0xffe5caca7e10e4e61c3624eaa0941cd0n >> 128n;
11166
- if ((absTick & 0x10n) !== 0n) ratio = ratio * 0xffcb9843d60f6159c9db58835c926644n >> 128n;
11167
- if ((absTick & 0x20n) !== 0n) ratio = ratio * 0xff973b41fa98c081472e6896dfb254c0n >> 128n;
11168
- if ((absTick & 0x40n) !== 0n) ratio = ratio * 0xff2ea16466c96a3843ec78b326b52861n >> 128n;
11169
- if ((absTick & 0x80n) !== 0n) ratio = ratio * 0xfe5dee046a99a2a811c461f1969c3053n >> 128n;
11170
- if ((absTick & 0x100n) !== 0n) ratio = ratio * 0xfcbe86c7900a88aedcffc83b479aa3a4n >> 128n;
11171
- if ((absTick & 0x200n) !== 0n) ratio = ratio * 0xf987a7253ac413176f2b074cf7815e54n >> 128n;
11172
- if ((absTick & 0x400n) !== 0n) ratio = ratio * 0xf3392b0822b70005940c7a398e4b70f3n >> 128n;
11173
- if ((absTick & 0x800n) !== 0n) ratio = ratio * 0xe7159475a2c29b7443b29c7fa6e889d9n >> 128n;
11174
- if ((absTick & 0x1000n) !== 0n) ratio = ratio * 0xd097f3bdfd2022b8845ad8f792aa5825n >> 128n;
11175
- if ((absTick & 0x2000n) !== 0n) ratio = ratio * 0xa9f746462d870fdf8a65dc1f90e061e5n >> 128n;
11176
- if ((absTick & 0x4000n) !== 0n) ratio = ratio * 0x70d869a156d2a1b890bb3df62baf32f7n >> 128n;
11177
- if ((absTick & 0x8000n) !== 0n) ratio = ratio * 0x31be135f97d08fd981231505542fcfa6n >> 128n;
11178
- if ((absTick & 0x10000n) !== 0n) ratio = ratio * 0x9aa508b5b7a84e1c677de54f3e99bc9n >> 128n;
11179
- if ((absTick & 0x20000n) !== 0n) ratio = ratio * 0x5d6af8dedb81196699c329225ee604n >> 128n;
11180
- if ((absTick & 0x40000n) !== 0n) ratio = ratio * 0x2216e584f5fa1ea926041bedfe98n >> 128n;
11181
- if ((absTick & 0x80000n) !== 0n) ratio = ratio * 0x48a170391f7dc42444e8fa2n >> 128n;
11182
- if (tick > 0n) ratio = ((1n << 256n) - 1n) / ratio;
11183
- const remainderMask = (1n << 32n) - 1n;
11184
- const sqrtPriceX96 = (ratio >> 32n) + ((ratio & remainderMask) === 0n ? 0n : 1n);
11185
- return sqrtPriceX96;
10983
+
10984
+ //#endregion
10985
+ //#region src/uniswap/v3/router/quote.ts
10986
+ const BPS_DENOMINATOR$3 = 10000n;
10987
+ const UINT128_MAX = (1n << 128n) - 1n;
10988
+ /**
10989
+ * Quote an exact-in v3 swap. Returns `null` when the quote reverts (e.g. the
10990
+ * pool does not exist / has no liquidity) so callers can skip that pool when
10991
+ * ranking venues, rather than aborting the whole cycle.
10992
+ */
10993
+ async function quoteV3ExactIn(params) {
10994
+ const { client, chainId, tokenIn, tokenOut, fee, amountIn, slippageBps, blockNumber } = params;
10995
+ if (amountIn < 0n || amountIn > UINT128_MAX) throw new PanopticError(`amountIn ${amountIn} exceeds uint128 maximum`);
10996
+ if (slippageBps < 0n || slippageBps > BPS_DENOMINATOR$3) throw new PanopticError(`invalid slippageBps ${slippageBps}, must be 0..10000`);
10997
+ const { quoterV2 } = getUniswapV3Addresses(chainId, params.addresses);
10998
+ try {
10999
+ const { result } = await client.simulateContract({
11000
+ address: quoterV2,
11001
+ abi: quoterV2Abi,
11002
+ functionName: "quoteExactInputSingle",
11003
+ blockNumber,
11004
+ args: [{
11005
+ tokenIn,
11006
+ tokenOut,
11007
+ amountIn,
11008
+ fee: Number(fee),
11009
+ sqrtPriceLimitX96: 0n
11010
+ }]
11011
+ });
11012
+ const [amountOut, , , gasEstimate] = result;
11013
+ const amountOutMinimum = amountOut * (BPS_DENOMINATOR$3 - slippageBps) / BPS_DENOMINATOR$3;
11014
+ return {
11015
+ amountOut,
11016
+ amountOutMinimum,
11017
+ gasEstimate
11018
+ };
11019
+ } catch (err) {
11020
+ if (isRevert(err)) return null;
11021
+ throw err;
11022
+ }
11186
11023
  }
11187
- const MIN_SQRT_PRICE_X96 = tickToSqrtPriceX96(MIN_TICK);
11188
- const MAX_SQRT_PRICE_X96 = tickToSqrtPriceX96(MAX_TICK);
11189
- function getPriceRatioFromSqrtPriceX96(sqrtPriceX96) {
11190
- return {
11191
- numerator: sqrtPriceX96 * sqrtPriceX96,
11192
- denominator: Q192$3
11193
- };
11024
+ /** True only for genuine contract reverts (missing pool / no liquidity). */
11025
+ function isRevert(err) {
11026
+ return err instanceof BaseError && err.walk((e) => e instanceof ContractFunctionRevertedError) instanceof ContractFunctionRevertedError;
11194
11027
  }
11195
- function getRawPriceRatio(tick) {
11196
- return getPriceRatioFromSqrtPriceX96(tickToSqrtPriceX96(tick));
11028
+
11029
+ //#endregion
11030
+ //#region src/panoptic/v2/reads/sfpm.ts
11031
+ /**
11032
+ * Resolve an SFPM poolId to its corresponding Uniswap V3 pool address.
11033
+ *
11034
+ * Calls `SemiFungiblePositionManagerV3.getUniswapV3PoolFromId(poolId)`.
11035
+ */
11036
+ async function getUniswapV3PoolFromId(params) {
11037
+ const { client, sfpmAddress, poolId } = params;
11038
+ return client.readContract({
11039
+ address: sfpmAddress,
11040
+ abi: semiFungiblePositionManagerV3Abi,
11041
+ functionName: "getUniswapV3PoolFromId",
11042
+ args: [poolId]
11043
+ });
11197
11044
  }
11198
- function adjustRatioForDecimals(numerator, denominator, decimals0, decimals1) {
11199
- const diff = decimals0 - decimals1;
11200
- if (diff === 0n) return {
11201
- numerator,
11202
- denominator
11203
- };
11204
- if (diff > 0n) return {
11205
- numerator: numerator * pow10(diff),
11206
- denominator
11207
- };
11045
+ /**
11046
+ * Resolve an SFPM poolId to its corresponding Uniswap V4 pool key.
11047
+ *
11048
+ * Calls `SemiFungiblePositionManagerV4.getUniswapV4PoolKeyFromId(poolId)`.
11049
+ */
11050
+ async function getUniswapV4PoolKeyFromId(params) {
11051
+ const { client, sfpmAddress, poolId } = params;
11052
+ const raw = await client.readContract({
11053
+ address: sfpmAddress,
11054
+ abi: semiFungiblePositionManagerV4Abi,
11055
+ functionName: "getUniswapV4PoolKeyFromId",
11056
+ args: [poolId]
11057
+ });
11208
11058
  return {
11209
- numerator,
11210
- denominator: denominator * pow10(-diff)
11059
+ currency0: raw.currency0,
11060
+ currency1: raw.currency1,
11061
+ fee: BigInt(raw.fee),
11062
+ tickSpacing: BigInt(raw.tickSpacing),
11063
+ hooks: raw.hooks
11211
11064
  };
11212
11065
  }
11213
11066
  /**
11214
- * Convert a tick to a raw price string (no decimal adjustment).
11215
- * Uses the formula: price = 1.0001^tick
11216
- *
11217
- * This returns the raw price ratio, not adjusted for token decimals.
11218
- * A fixed internal precision is used and trailing zeros are trimmed.
11219
- *
11220
- * @param tick - The tick value
11221
- * @returns Price string
11067
+ * Get the enforced tick limits for a pool from the SFPM.
11222
11068
  *
11223
- * @example
11224
- * ```typescript
11225
- * tickToPrice(0n) // "1"
11226
- * tickToPrice(1000n) // "1.105..." (approximately)
11227
- * tickToPrice(-1000n) // "0.904..." (approximately)
11228
- * tickToPrice(200000n) // Very large number
11229
- * ```
11069
+ * @param params - The parameters
11070
+ * @returns The min and max enforced ticks
11230
11071
  */
11231
- function tickToPrice(tick) {
11232
- const { numerator, denominator } = getRawPriceRatio(tick);
11233
- const price = formatRatio$3(numerator, denominator, RAW_PRICE_PRECISION);
11234
- return trimTrailingZeros(price);
11072
+ async function getEnforcedTickLimits(params) {
11073
+ const { client, sfpmAddress, poolId } = params;
11074
+ const [minTick, maxTick] = await client.readContract({
11075
+ address: sfpmAddress,
11076
+ abi: semiFungiblePositionManagerV4Abi,
11077
+ functionName: "getEnforcedTickLimits",
11078
+ args: [poolId]
11079
+ });
11080
+ return {
11081
+ minEnforcedTick: minTick,
11082
+ maxEnforcedTick: maxTick
11083
+ };
11235
11084
  }
11236
11085
  /**
11237
- * Convert a tick to a human-readable price with decimal scaling.
11238
- * Uses the formula: price = 1.0001^tick * 10^(decimals0-decimals1)
11239
- *
11240
- * This adjusts for the different decimals of the two tokens in the pair.
11241
- *
11242
- * @param tick - The tick value
11243
- * @param decimals0 - Decimals of token0
11244
- * @param decimals1 - Decimals of token1
11245
- * @param precision - Number of decimal places to display
11246
- * @returns Formatted price string
11247
- *
11248
- * @example
11249
- * ```typescript
11250
- * // WETH/USDC pool (18 decimals / 6 decimals)
11251
- * // At tick ~200000, price is roughly $2000 per ETH
11252
- * tickToPriceDecimalScaled(200000n, 18n, 6n, 2n) // "2000.00" (approximately)
11086
+ * Fetch liquidity breakdown for a batch of chunks via SFPM.getAccountLiquidity().
11253
11087
  *
11254
- * // For token1/token0 price, swap the decimals
11255
- * tickToPriceDecimalScaled(200000n, 6n, 18n, 6n) // "0.000500" (approximately)
11256
- * ```
11088
+ * Uses multicall for efficiency. Returns one result per input chunk,
11089
+ * along with block metadata for freshness tracking.
11257
11090
  */
11258
- function tickToPriceDecimalScaled(tick, decimals0, decimals1, precision) {
11259
- const rawRatio = getRawPriceRatio(tick);
11260
- const { numerator, denominator } = adjustRatioForDecimals(rawRatio.numerator, rawRatio.denominator, decimals0, decimals1);
11261
- return formatRatio$3(numerator, denominator, precision);
11091
+ async function getChunkLiquidities(params) {
11092
+ const { client, sfpmAddress, poolKeyBytes, chunks } = params;
11093
+ if (chunks.length > MAX_TRACKED_CHUNKS) throw new ChunkLimitError(BigInt(chunks.length), 0n);
11094
+ const _meta = params._meta ?? await getBlockMeta({ client });
11095
+ if (chunks.length === 0) return {
11096
+ results: [],
11097
+ _meta
11098
+ };
11099
+ const multicallResults = await client.multicall({
11100
+ contracts: chunks.map((chunk) => ({
11101
+ address: sfpmAddress,
11102
+ abi: semiFungiblePositionManagerV4Abi,
11103
+ functionName: "getAccountLiquidity",
11104
+ args: [
11105
+ poolKeyBytes,
11106
+ chunk.owner,
11107
+ chunk.tokenType,
11108
+ Number(chunk.tickLower),
11109
+ Number(chunk.tickUpper)
11110
+ ]
11111
+ })),
11112
+ allowFailure: true
11113
+ });
11114
+ const results = multicallResults.map((result) => {
11115
+ if (result.status === "failure") return {
11116
+ netLiquidity: 0n,
11117
+ removedLiquidity: 0n,
11118
+ totalLiquidity: 0n,
11119
+ shortLiquidity: 0n,
11120
+ longLiquidity: 0n
11121
+ };
11122
+ const packed = result.result;
11123
+ const netLiquidity = packed & (1n << 128n) - 1n;
11124
+ const removedLiquidity = packed >> 128n;
11125
+ const totalLiquidity = netLiquidity + removedLiquidity;
11126
+ return {
11127
+ netLiquidity,
11128
+ removedLiquidity,
11129
+ totalLiquidity,
11130
+ shortLiquidity: totalLiquidity,
11131
+ longLiquidity: removedLiquidity
11132
+ };
11133
+ });
11134
+ return {
11135
+ results,
11136
+ _meta
11137
+ };
11262
11138
  }
11263
- /**
11264
- * Convert a sqrtPriceX96 to a human-readable price with decimal scaling.
11265
- *
11266
- * Uses the formula: price = (sqrtPriceX96^2 / 2^192) * 10^(decimals0-decimals1)
11267
- *
11268
- * @param sqrtPriceX96 - The sqrt price in Q64.96 format
11269
- * @param decimals0 - Decimals of token0
11270
- * @param decimals1 - Decimals of token1
11271
- * @param precision - Number of decimal places to display
11272
- * @returns Formatted price string
11273
- *
11274
- * @example
11275
- * ```typescript
11276
- * sqrtPriceX96ToPriceDecimalScaled(2n ** 96n, 18n, 18n, 2n) // "1.00"
11277
- * ```
11139
+
11140
+ //#endregion
11141
+ //#region src/panoptic/v2/reads/factory.ts
11142
+ /**
11143
+ * Get the token URI from a PanopticFactory NFT.
11278
11144
  */
11279
- function sqrtPriceX96ToPriceDecimalScaled(sqrtPriceX96, decimals0, decimals1, precision) {
11280
- const rawRatio = getPriceRatioFromSqrtPriceX96(sqrtPriceX96);
11281
- const { numerator, denominator } = adjustRatioForDecimals(rawRatio.numerator, rawRatio.denominator, decimals0, decimals1);
11282
- return formatRatio$3(numerator, denominator, precision);
11145
+ async function getFactoryTokenURI(params) {
11146
+ const { client, factoryAddress, version, tokenId } = params;
11147
+ const abi = version === "v3" ? panopticFactoryV3Abi : panopticFactoryV4Abi;
11148
+ return client.readContract({
11149
+ address: factoryAddress,
11150
+ abi,
11151
+ functionName: "tokenURI",
11152
+ args: [tokenId]
11153
+ });
11283
11154
  }
11284
11155
  /**
11285
- * Convert a price to a tick value.
11286
- *
11287
- * @param price - The price string
11288
- * @param decimals0 - Decimals of token0
11289
- * @param decimals1 - Decimals of token1
11290
- * @returns The tick value (rounded to nearest integer)
11291
- *
11292
- * @example
11293
- * ```typescript
11294
- * // WETH/USDC: What tick for $2000 per ETH?
11295
- * priceToTick("2000", 18n, 6n) // ~200000n
11296
- *
11297
- * // Inverse: What tick for 0.0005 ETH per USDC?
11298
- * priceToTick("0.0005", 6n, 18n) // ~200000n
11299
- * ```
11156
+ * Get the owner of a PanopticFactory NFT.
11300
11157
  */
11301
- function priceToTick(price, decimals0, decimals1) {
11302
- const parsed = parseDecimalToFraction(price);
11303
- if (parsed.numerator <= 0n) throw new Error("Price must be positive");
11304
- let targetNumerator = parsed.numerator;
11305
- let targetDenominator = parsed.denominator;
11306
- const diff = decimals0 - decimals1;
11307
- if (diff > 0n) targetDenominator *= pow10(diff);
11308
- else if (diff < 0n) targetNumerator *= pow10(-diff);
11309
- let low = MIN_TICK;
11310
- let high = MAX_TICK;
11311
- while (low <= high) {
11312
- const mid = (low + high) / 2n;
11313
- const { numerator, denominator } = getRawPriceRatio(mid);
11314
- const cmp = compareRatios(numerator, denominator, targetNumerator, targetDenominator);
11315
- if (cmp === 0) return mid;
11316
- if (cmp < 0) low = mid + 1n;
11317
- else high = mid - 1n;
11318
- }
11319
- const floorTick = high;
11320
- const ceilTick = low;
11321
- if (floorTick < MIN_TICK) return MIN_TICK;
11322
- if (ceilTick > MAX_TICK) return MAX_TICK;
11323
- const floorRatio = getRawPriceRatio(floorTick);
11324
- const ceilRatio = getRawPriceRatio(ceilTick);
11325
- const floorDiffNumerator = absBigint(targetNumerator * floorRatio.denominator - floorRatio.numerator * targetDenominator);
11326
- const ceilDiffNumerator = absBigint(targetNumerator * ceilRatio.denominator - ceilRatio.numerator * targetDenominator);
11327
- const floorDiffDenominator = targetDenominator * floorRatio.denominator;
11328
- const ceilDiffDenominator = targetDenominator * ceilRatio.denominator;
11329
- return floorDiffNumerator * ceilDiffDenominator <= ceilDiffNumerator * floorDiffDenominator ? floorTick : ceilTick;
11158
+ async function getFactoryOwnerOf(params) {
11159
+ const { client, factoryAddress, version, tokenId } = params;
11160
+ const abi = version === "v3" ? panopticFactoryV3Abi : panopticFactoryV4Abi;
11161
+ return client.readContract({
11162
+ address: factoryAddress,
11163
+ abi,
11164
+ functionName: "ownerOf",
11165
+ args: [tokenId]
11166
+ });
11330
11167
  }
11331
11168
  /**
11332
- * Convert a sqrtPriceX96 value to the nearest tick.
11333
- *
11334
- * @param sqrtPriceX96 - The sqrt price in Q64.96 format
11335
- * @returns The tick value (rounded to nearest integer)
11336
- *
11337
- * @example
11338
- * ```typescript
11339
- * const tick = sqrtPriceX96ToTick(2n ** 96n) // 0n
11340
- * ```
11169
+ * Construct NFT metadata for a pool via the factory contract.
11341
11170
  */
11342
- function sqrtPriceX96ToTick(sqrtPriceX96) {
11343
- if (sqrtPriceX96 <= 0n) throw new Error("Sqrt price must be positive");
11344
- if (sqrtPriceX96 < MIN_SQRT_PRICE_X96 || sqrtPriceX96 > MAX_SQRT_PRICE_X96) throw new RangeError("Sqrt price out of bounds");
11345
- let low = MIN_TICK;
11346
- let high = MAX_TICK;
11347
- while (low <= high) {
11348
- const mid = (low + high) / 2n;
11349
- const midSqrt = tickToSqrtPriceX96(mid);
11350
- if (midSqrt === sqrtPriceX96) return mid;
11351
- if (midSqrt < sqrtPriceX96) low = mid + 1n;
11352
- else high = mid - 1n;
11353
- }
11354
- const floorTick = high;
11355
- const ceilTick = low;
11356
- if (floorTick < MIN_TICK) return MIN_TICK;
11357
- if (ceilTick > MAX_TICK) return MAX_TICK;
11358
- const floorSqrt = tickToSqrtPriceX96(floorTick);
11359
- const ceilSqrt = tickToSqrtPriceX96(ceilTick);
11360
- const floorDiff = absBigint(sqrtPriceX96 - floorSqrt);
11361
- const ceilDiff = absBigint(ceilSqrt - sqrtPriceX96);
11362
- return floorDiff <= ceilDiff ? floorTick : ceilTick;
11171
+ async function getFactoryConstructMetadata(params) {
11172
+ const { client, factoryAddress, version, panopticPoolAddress, symbol0, symbol1, fee } = params;
11173
+ const abi = version === "v3" ? panopticFactoryV3Abi : panopticFactoryV4Abi;
11174
+ return client.readContract({
11175
+ address: factoryAddress,
11176
+ abi,
11177
+ functionName: "constructMetadata",
11178
+ args: [
11179
+ panopticPoolAddress,
11180
+ symbol0,
11181
+ symbol1,
11182
+ fee
11183
+ ]
11184
+ });
11363
11185
  }
11364
11186
  /**
11365
- * Format a tick value for display.
11366
- *
11367
- * @param tick - The tick value
11368
- * @returns Formatted tick string
11369
- *
11370
- * @example
11371
- * ```typescript
11372
- * formatTick(200000n) // "200000"
11373
- * formatTick(-50000n) // "-50000"
11374
- * ```
11187
+ * Get the PanopticPool address for a given pool and risk engine.
11375
11188
  */
11376
- function formatTick(tick) {
11377
- return tick.toString();
11189
+ async function getPanopticPoolAddress(params) {
11190
+ const { client, factoryAddress, riskEngine } = params;
11191
+ if (params.version === "v3") return client.readContract({
11192
+ address: factoryAddress,
11193
+ abi: panopticFactoryV3Abi,
11194
+ functionName: "getPanopticPool",
11195
+ args: [params.univ3pool, riskEngine]
11196
+ });
11197
+ return client.readContract({
11198
+ address: factoryAddress,
11199
+ abi: panopticFactoryV4Abi,
11200
+ functionName: "getPanopticPool",
11201
+ args: [{
11202
+ currency0: params.poolKey.currency0,
11203
+ currency1: params.poolKey.currency1,
11204
+ fee: Number(params.poolKey.fee),
11205
+ tickSpacing: Number(params.poolKey.tickSpacing),
11206
+ hooks: params.poolKey.hooks
11207
+ }, riskEngine]
11208
+ });
11378
11209
  }
11379
11210
  /**
11380
- * Get the price at a specific tick, returning both token0/token1 and token1/token0 prices.
11381
- *
11382
- * @param tick - The tick value
11383
- * @param decimals0 - Decimals of token0
11384
- * @param decimals1 - Decimals of token1
11385
- * @param precision - Number of decimal places to display
11386
- * @returns Object with both price directions
11387
- *
11388
- * @example
11389
- * ```typescript
11390
- * const prices = getPricesAtTick(200000n, 18n, 6n, 2n)
11391
- * // prices.token0PerToken1 = "0.00" (very small)
11392
- * // prices.token1PerToken0 = "2000.00" (USDC per ETH)
11393
- * ```
11211
+ * Mine for an optimal pool address salt with high rarity.
11394
11212
  */
11395
- function getPricesAtTick(tick, decimals0, decimals1, precision) {
11396
- const rawRatio = getRawPriceRatio(tick);
11397
- const adjustedRatio = adjustRatioForDecimals(rawRatio.numerator, rawRatio.denominator, decimals0, decimals1);
11213
+ async function minePoolAddress(params) {
11214
+ const { client, factoryAddress, deployerAddress, riskEngine, salt, loops, minTargetRarity } = params;
11215
+ let result;
11216
+ if (params.version === "v3") result = await client.readContract({
11217
+ address: factoryAddress,
11218
+ abi: panopticFactoryV3Abi,
11219
+ functionName: "minePoolAddress",
11220
+ args: [
11221
+ deployerAddress,
11222
+ params.v3Pool,
11223
+ riskEngine,
11224
+ salt,
11225
+ loops,
11226
+ minTargetRarity
11227
+ ]
11228
+ });
11229
+ else result = await client.readContract({
11230
+ address: factoryAddress,
11231
+ abi: panopticFactoryV4Abi,
11232
+ functionName: "minePoolAddress",
11233
+ args: [
11234
+ deployerAddress,
11235
+ {
11236
+ currency0: params.poolKey.currency0,
11237
+ currency1: params.poolKey.currency1,
11238
+ fee: Number(params.poolKey.fee),
11239
+ tickSpacing: Number(params.poolKey.tickSpacing),
11240
+ hooks: params.poolKey.hooks
11241
+ },
11242
+ riskEngine,
11243
+ salt,
11244
+ loops,
11245
+ minTargetRarity
11246
+ ]
11247
+ });
11398
11248
  return {
11399
- token0PerToken1: formatRatio$3(adjustedRatio.denominator, adjustedRatio.numerator, precision),
11400
- token1PerToken0: formatRatio$3(adjustedRatio.numerator, adjustedRatio.denominator, precision)
11249
+ bestSalt: BigInt(result[0]),
11250
+ highestRarity: result[1]
11401
11251
  };
11402
11252
  }
11403
11253
  /**
11404
- * Format a tick range for display.
11405
- *
11406
- * @param tickLower - Lower tick
11407
- * @param tickUpper - Upper tick
11408
- * @returns Formatted tick range string
11254
+ * Simulate a pool deployment to get the predicted pool address.
11409
11255
  *
11410
- * @example
11411
- * ```typescript
11412
- * formatTickRange(-50000n, 200000n) // "-50000 - 200000"
11413
- * ```
11256
+ * Uses `simulateContract` on `deployNewPool` — the return value is the new pool address
11257
+ * without actually executing the transaction.
11414
11258
  */
11415
- function formatTickRange(tickLower, tickUpper) {
11416
- return `${tickLower} - ${tickUpper}`;
11259
+ async function simulateDeployNewPool(params) {
11260
+ const { client, factoryAddress, account, riskEngine, salt } = params;
11261
+ if (params.version === "v3") {
11262
+ const { result: result$1 } = await client.simulateContract({
11263
+ address: factoryAddress,
11264
+ abi: panopticFactoryV3Abi,
11265
+ functionName: "deployNewPool",
11266
+ args: [
11267
+ params.token0,
11268
+ params.token1,
11269
+ Number(params.fee),
11270
+ riskEngine,
11271
+ salt
11272
+ ],
11273
+ account
11274
+ });
11275
+ return result$1;
11276
+ }
11277
+ const { result } = await client.simulateContract({
11278
+ address: factoryAddress,
11279
+ abi: panopticFactoryV4Abi,
11280
+ functionName: "deployNewPool",
11281
+ args: [
11282
+ {
11283
+ currency0: params.poolKey.currency0,
11284
+ currency1: params.poolKey.currency1,
11285
+ fee: Number(params.poolKey.fee),
11286
+ tickSpacing: Number(params.poolKey.tickSpacing),
11287
+ hooks: params.poolKey.hooks
11288
+ },
11289
+ riskEngine,
11290
+ salt
11291
+ ],
11292
+ account
11293
+ });
11294
+ return result;
11417
11295
  }
11418
11296
  /**
11419
- * Format a price range for display.
11420
- *
11421
- * @param tickLower - Lower tick
11422
- * @param tickUpper - Upper tick
11423
- * @param decimals0 - Decimals of token0
11424
- * @param decimals1 - Decimals of token1
11425
- * @param precision - Number of decimal places to display
11426
- * @returns Formatted price range string
11297
+ * Resolve an SFPM poolId to its PanopticPool address.
11427
11298
  *
11428
- * @example
11429
- * ```typescript
11430
- * formatPriceRange(0n, 0n, 18n, 18n, 2n) // "1.00 - 1.00"
11431
- * ```
11299
+ * Chains two on-chain lookups:
11300
+ * 1. SFPM: poolId → Uniswap pool address (V3) or pool key (V4)
11301
+ * 2. Factory: Uniswap pool + riskEngine → PanopticPool address
11432
11302
  */
11433
- function formatPriceRange(tickLower, tickUpper, decimals0, decimals1, precision) {
11434
- const lower = tickToPriceDecimalScaled(tickLower, decimals0, decimals1, precision);
11435
- const upper = tickToPriceDecimalScaled(tickUpper, decimals0, decimals1, precision);
11436
- return `${lower} - ${upper}`;
11303
+ async function getPanopticPoolFromPoolId(params) {
11304
+ const { client, sfpmAddress, factoryAddress, riskEngine, poolId, version } = params;
11305
+ if (version === "v3") {
11306
+ const univ3pool = await getUniswapV3PoolFromId({
11307
+ client,
11308
+ sfpmAddress,
11309
+ poolId
11310
+ });
11311
+ return getPanopticPoolAddress({
11312
+ version: "v3",
11313
+ client,
11314
+ factoryAddress,
11315
+ univ3pool,
11316
+ riskEngine
11317
+ });
11318
+ }
11319
+ const poolKey = await getUniswapV4PoolKeyFromId({
11320
+ client,
11321
+ sfpmAddress,
11322
+ poolId
11323
+ });
11324
+ return getPanopticPoolAddress({
11325
+ version: "v4",
11326
+ client,
11327
+ factoryAddress,
11328
+ poolKey,
11329
+ riskEngine
11330
+ });
11437
11331
  }
11438
11332
  /**
11439
- * Calculate the tick spacing for a given fee tier.
11333
+ * Resolve an SFPM poolId to its PanopticPool address without knowing the version.
11440
11334
  *
11441
- * @param feeBps - Fee in basis points (e.g., 500n for 0.05%)
11442
- * @returns Tick spacing
11335
+ * Tries both V3 and V4 lookups in parallel. The factory returns `address(0)` for
11336
+ * non-existent pools, so the non-zero result identifies the correct version.
11443
11337
  *
11444
- * @example
11445
- * ```typescript
11446
- * getTickSpacing(100n) // 1n (0.01% fee tier)
11447
- * getTickSpacing(500n) // 10n (0.05% fee tier)
11448
- * getTickSpacing(3000n) // 60n (0.30% fee tier)
11449
- * getTickSpacing(10000n) // 200n (1.00% fee tier)
11450
- * ```
11338
+ * At least one of `v3` or `v4` must be provided.
11339
+ *
11340
+ * @throws {PanopticValidationError} If no version config is provided or neither resolves.
11451
11341
  */
11452
- function getTickSpacing(feeBps) {
11453
- switch (feeBps) {
11454
- case 100n: return 1n;
11455
- case 500n: return 10n;
11456
- case 3000n: return 60n;
11457
- case 10000n: return 200n;
11458
- default: {
11459
- const spacing = feeBps / 50n;
11460
- return spacing > 1n ? spacing : 1n;
11342
+ async function resolvePanopticPoolFromPoolId(params) {
11343
+ const { client, poolId, riskEngine, v3, v4 } = params;
11344
+ if (!v3 && !v4) throw new Error("At least one of v3 or v4 must be provided");
11345
+ const isNotFoundError = (err) => {
11346
+ if (typeof err === "object" && err !== null && "name" in err) {
11347
+ const name = err.name;
11348
+ return name === "ContractFunctionExecutionError" || name === "ContractFunctionRevertedError";
11461
11349
  }
11462
- }
11350
+ return false;
11351
+ };
11352
+ const results = await Promise.all([v3 ? getPanopticPoolFromPoolId({
11353
+ version: "v3",
11354
+ client,
11355
+ sfpmAddress: v3.sfpmAddress,
11356
+ factoryAddress: v3.factoryAddress,
11357
+ riskEngine,
11358
+ poolId
11359
+ }).catch((err) => {
11360
+ if (isNotFoundError(err)) return zeroAddress;
11361
+ throw err;
11362
+ }) : Promise.resolve(zeroAddress), v4 ? getPanopticPoolFromPoolId({
11363
+ version: "v4",
11364
+ client,
11365
+ sfpmAddress: v4.sfpmAddress,
11366
+ factoryAddress: v4.factoryAddress,
11367
+ riskEngine,
11368
+ poolId
11369
+ }).catch((err) => {
11370
+ if (isNotFoundError(err)) return zeroAddress;
11371
+ throw err;
11372
+ }) : Promise.resolve(zeroAddress)]);
11373
+ const [v3Result, v4Result] = results;
11374
+ if (v3Result !== zeroAddress) return {
11375
+ panopticPoolAddress: v3Result,
11376
+ version: "v3"
11377
+ };
11378
+ if (v4Result !== zeroAddress) return {
11379
+ panopticPoolAddress: v4Result,
11380
+ version: "v4"
11381
+ };
11382
+ throw new Error(`No PanopticPool found for poolId ${poolId}`);
11463
11383
  }
11384
+
11385
+ //#endregion
11386
+ //#region src/panoptic/v2/reads/minePoolAddressLocal.ts
11387
+ const MASK_80 = (1n << 80n) - 1n;
11388
+ const MASK_40 = (1n << 40n) - 1n;
11389
+ const MASK_96 = (1n << 96n) - 1n;
11390
+
11391
+ //#endregion
11392
+ //#region src/panoptic/v2/abis/stateView.ts
11464
11393
  /**
11465
- * Round a tick to the nearest valid tick for a given tick spacing.
11466
- *
11467
- * @param tick - The tick to round
11468
- * @param tickSpacing - The tick spacing
11469
- * @returns Rounded tick
11470
- *
11471
- * @example
11472
- * ```typescript
11473
- * roundToTickSpacing(12345n, 10n) // 12340n
11474
- * roundToTickSpacing(12345n, 60n) // 12360n
11475
- * roundToTickSpacing(-12345n, 10n) // -12350n
11476
- * ```
11394
+ * Minimal Uniswap V4 StateView ABI for fee growth reads.
11395
+ * Only includes functions needed by streamia history.
11477
11396
  */
11478
- function roundToTickSpacing(tick, tickSpacing) {
11479
- const remainder = tick % tickSpacing;
11480
- if (remainder === 0n) return tick;
11481
- if (tick >= 0n) return remainder >= tickSpacing / 2n ? tick + (tickSpacing - remainder) : tick - remainder;
11482
- const absRemainder = -remainder;
11483
- return absRemainder >= tickSpacing / 2n ? tick - (tickSpacing - absRemainder) : tick + absRemainder;
11484
- }
11485
- /**
11486
- * Compute slippage-bounded tick limits around the current tick.
11487
- *
11488
- * 1 tick ≈ 1 basis point (0.01 %) of price change, so a `toleranceBps`
11489
- * of 500 allows roughly 5 % price movement. The result is clamped to
11490
- * the protocol's `[MIN_TICK, MAX_TICK]` range.
11491
- *
11492
- * Useful for setting `tickLimitLow` / `tickLimitHigh` on `openPosition`
11493
- * and `closePosition` to protect against MEV sandwiches and volatile
11494
- * tick moves.
11495
- *
11496
- * @param currentTick - The current pool tick (must be within [MIN_TICK, MAX_TICK]).
11497
- * @param toleranceBps - Slippage tolerance in basis points (≈ ticks). Must be non-negative.
11498
- * @returns Clamped `{ low, high }` tick limits.
11499
- * @throws {RangeError} If `toleranceBps` is negative or `currentTick` is out of bounds.
11500
- *
11501
- * @example
11502
- * ```typescript
11503
- * const { low, high } = tickLimits(200_000n, 500n)
11504
- * // low = 199_500n
11505
- * // high = 200_500n
11506
- *
11507
- * await openPosition({ ..., tickLimitLow: low, tickLimitHigh: high })
11508
- * ```
11397
+ const stateViewAbi = [
11398
+ {
11399
+ inputs: [{
11400
+ internalType: "PoolId",
11401
+ name: "poolId",
11402
+ type: "bytes32"
11403
+ }],
11404
+ name: "getSlot0",
11405
+ outputs: [
11406
+ {
11407
+ internalType: "uint160",
11408
+ name: "sqrtPriceX96",
11409
+ type: "uint160"
11410
+ },
11411
+ {
11412
+ internalType: "int24",
11413
+ name: "tick",
11414
+ type: "int24"
11415
+ },
11416
+ {
11417
+ internalType: "uint24",
11418
+ name: "protocolFee",
11419
+ type: "uint24"
11420
+ },
11421
+ {
11422
+ internalType: "uint24",
11423
+ name: "lpFee",
11424
+ type: "uint24"
11425
+ }
11426
+ ],
11427
+ stateMutability: "view",
11428
+ type: "function"
11429
+ },
11430
+ {
11431
+ inputs: [{
11432
+ internalType: "PoolId",
11433
+ name: "poolId",
11434
+ type: "bytes32"
11435
+ }],
11436
+ name: "getFeeGrowthGlobals",
11437
+ outputs: [{
11438
+ internalType: "uint256",
11439
+ name: "feeGrowthGlobal0",
11440
+ type: "uint256"
11441
+ }, {
11442
+ internalType: "uint256",
11443
+ name: "feeGrowthGlobal1",
11444
+ type: "uint256"
11445
+ }],
11446
+ stateMutability: "view",
11447
+ type: "function"
11448
+ },
11449
+ {
11450
+ inputs: [{
11451
+ internalType: "PoolId",
11452
+ name: "poolId",
11453
+ type: "bytes32"
11454
+ }, {
11455
+ internalType: "int24",
11456
+ name: "tick",
11457
+ type: "int24"
11458
+ }],
11459
+ name: "getTickInfo",
11460
+ outputs: [
11461
+ {
11462
+ internalType: "uint128",
11463
+ name: "liquidityGross",
11464
+ type: "uint128"
11465
+ },
11466
+ {
11467
+ internalType: "int128",
11468
+ name: "liquidityNet",
11469
+ type: "int128"
11470
+ },
11471
+ {
11472
+ internalType: "uint256",
11473
+ name: "feeGrowthOutside0X128",
11474
+ type: "uint256"
11475
+ },
11476
+ {
11477
+ internalType: "uint256",
11478
+ name: "feeGrowthOutside1X128",
11479
+ type: "uint256"
11480
+ }
11481
+ ],
11482
+ stateMutability: "view",
11483
+ type: "function"
11484
+ },
11485
+ {
11486
+ inputs: [{
11487
+ internalType: "PoolId",
11488
+ name: "poolId",
11489
+ type: "bytes32"
11490
+ }],
11491
+ name: "getLiquidity",
11492
+ outputs: [{
11493
+ internalType: "uint128",
11494
+ name: "liquidity",
11495
+ type: "uint128"
11496
+ }],
11497
+ stateMutability: "view",
11498
+ type: "function"
11499
+ }
11500
+ ];
11501
+
11502
+ //#endregion
11503
+ //#region src/panoptic/v2/abis/uniswapV3Pool.ts
11504
+ /**
11505
+ * Minimal Uniswap V3 Pool ABI for fee growth reads.
11506
+ * Only includes functions needed by streamia history.
11509
11507
  */
11510
- function tickLimits(currentTick, toleranceBps) {
11511
- if (toleranceBps < 0n) throw new RangeError(`toleranceBps must be non-negative, got ${toleranceBps}`);
11512
- if (currentTick < MIN_TICK || currentTick > MAX_TICK) throw new RangeError(`currentTick ${currentTick} is out of bounds [${MIN_TICK}, ${MAX_TICK}]`);
11513
- const rawLow = currentTick - toleranceBps;
11514
- const rawHigh = currentTick + toleranceBps;
11515
- const low = rawLow < MIN_TICK ? MIN_TICK : rawLow;
11516
- const high = rawHigh > MAX_TICK ? MAX_TICK : rawHigh;
11517
- if (low > high) throw new RangeError(`Computed tick limits are inverted: low ${low} > high ${high} (currentTick=${currentTick}, toleranceBps=${toleranceBps})`);
11518
- return {
11519
- low,
11520
- high
11521
- };
11522
- }
11508
+ const uniswapV3PoolAbi = [
11509
+ {
11510
+ inputs: [],
11511
+ name: "slot0",
11512
+ outputs: [
11513
+ {
11514
+ internalType: "uint160",
11515
+ name: "sqrtPriceX96",
11516
+ type: "uint160"
11517
+ },
11518
+ {
11519
+ internalType: "int24",
11520
+ name: "tick",
11521
+ type: "int24"
11522
+ },
11523
+ {
11524
+ internalType: "uint16",
11525
+ name: "observationIndex",
11526
+ type: "uint16"
11527
+ },
11528
+ {
11529
+ internalType: "uint16",
11530
+ name: "observationCardinality",
11531
+ type: "uint16"
11532
+ },
11533
+ {
11534
+ internalType: "uint16",
11535
+ name: "observationCardinalityNext",
11536
+ type: "uint16"
11537
+ },
11538
+ {
11539
+ internalType: "uint8",
11540
+ name: "feeProtocol",
11541
+ type: "uint8"
11542
+ },
11543
+ {
11544
+ internalType: "bool",
11545
+ name: "unlocked",
11546
+ type: "bool"
11547
+ }
11548
+ ],
11549
+ stateMutability: "view",
11550
+ type: "function"
11551
+ },
11552
+ {
11553
+ inputs: [],
11554
+ name: "feeGrowthGlobal0X128",
11555
+ outputs: [{
11556
+ internalType: "uint256",
11557
+ name: "",
11558
+ type: "uint256"
11559
+ }],
11560
+ stateMutability: "view",
11561
+ type: "function"
11562
+ },
11563
+ {
11564
+ inputs: [],
11565
+ name: "feeGrowthGlobal1X128",
11566
+ outputs: [{
11567
+ internalType: "uint256",
11568
+ name: "",
11569
+ type: "uint256"
11570
+ }],
11571
+ stateMutability: "view",
11572
+ type: "function"
11573
+ },
11574
+ {
11575
+ inputs: [{
11576
+ internalType: "int24",
11577
+ name: "",
11578
+ type: "int24"
11579
+ }],
11580
+ name: "ticks",
11581
+ outputs: [
11582
+ {
11583
+ internalType: "uint128",
11584
+ name: "liquidityGross",
11585
+ type: "uint128"
11586
+ },
11587
+ {
11588
+ internalType: "int128",
11589
+ name: "liquidityNet",
11590
+ type: "int128"
11591
+ },
11592
+ {
11593
+ internalType: "uint256",
11594
+ name: "feeGrowthOutside0X128",
11595
+ type: "uint256"
11596
+ },
11597
+ {
11598
+ internalType: "uint256",
11599
+ name: "feeGrowthOutside1X128",
11600
+ type: "uint256"
11601
+ },
11602
+ {
11603
+ internalType: "int56",
11604
+ name: "tickCumulativeOutside",
11605
+ type: "int56"
11606
+ },
11607
+ {
11608
+ internalType: "uint160",
11609
+ name: "secondsPerLiquidityOutsideX128",
11610
+ type: "uint160"
11611
+ },
11612
+ {
11613
+ internalType: "uint32",
11614
+ name: "secondsOutside",
11615
+ type: "uint32"
11616
+ },
11617
+ {
11618
+ internalType: "bool",
11619
+ name: "initialized",
11620
+ type: "bool"
11621
+ }
11622
+ ],
11623
+ stateMutability: "view",
11624
+ type: "function"
11625
+ },
11626
+ {
11627
+ inputs: [],
11628
+ name: "liquidity",
11629
+ outputs: [{
11630
+ internalType: "uint128",
11631
+ name: "",
11632
+ type: "uint128"
11633
+ }],
11634
+ stateMutability: "view",
11635
+ type: "function"
11636
+ },
11637
+ {
11638
+ inputs: [],
11639
+ name: "fee",
11640
+ outputs: [{
11641
+ internalType: "uint24",
11642
+ name: "",
11643
+ type: "uint24"
11644
+ }],
11645
+ stateMutability: "view",
11646
+ type: "function"
11647
+ },
11648
+ {
11649
+ inputs: [],
11650
+ name: "token0",
11651
+ outputs: [{
11652
+ internalType: "address",
11653
+ name: "",
11654
+ type: "address"
11655
+ }],
11656
+ stateMutability: "view",
11657
+ type: "function"
11658
+ },
11659
+ {
11660
+ inputs: [],
11661
+ name: "token1",
11662
+ outputs: [{
11663
+ internalType: "address",
11664
+ name: "",
11665
+ type: "address"
11666
+ }],
11667
+ stateMutability: "view",
11668
+ type: "function"
11669
+ },
11670
+ {
11671
+ inputs: [],
11672
+ name: "tickSpacing",
11673
+ outputs: [{
11674
+ internalType: "int24",
11675
+ name: "",
11676
+ type: "int24"
11677
+ }],
11678
+ stateMutability: "view",
11679
+ type: "function"
11680
+ }
11681
+ ];
11523
11682
 
11524
11683
  //#endregion
11525
11684
  //#region src/panoptic/v2/reads/mintBuffer.ts
@@ -14525,7 +14684,7 @@ async function readBlockAndAggregate({ client, calls, blockNumber }) {
14525
14684
 
14526
14685
  //#endregion
14527
14686
  //#region src/panoptic/v2/reads/checks.ts
14528
- const FP96$1 = 1n << 96n;
14687
+ const FP96$2 = 1n << 96n;
14529
14688
  const Q128$4 = 1n << 128n;
14530
14689
  function convert0to1$3(amount, sqrtPriceX96) {
14531
14690
  if (sqrtPriceX96 < Q128$4) return amount * sqrtPriceX96 * sqrtPriceX96 >> 192n;
@@ -14676,7 +14835,7 @@ async function isLiquidatable(params) {
14676
14835
  const requiredMargin1 = required1Native + convert0to1$3(required0Native, sqrtPriceX96);
14677
14836
  const marginShortfall0 = requiredMargin0 - currentMargin0;
14678
14837
  const marginShortfall1 = requiredMargin1 - currentMargin1;
14679
- const denominatedInToken = sqrtPriceX96 < FP96$1 ? 0n : 1n;
14838
+ const denominatedInToken = sqrtPriceX96 < FP96$2 ? 0n : 1n;
14680
14839
  const isLiquidatableResult = denominatedInToken === 0n ? marginShortfall0 > 0n : marginShortfall1 > 0n;
14681
14840
  return {
14682
14841
  isLiquidatable: isLiquidatableResult,
@@ -16919,8 +17078,9 @@ async function quoteTokenShortfallRecovery(params) {
16919
17078
  error: new PanopticError("Could not size the prefixed recovery swap")
16920
17079
  };
16921
17080
  };
17081
+ let creditOutSize = amountOut;
16922
17082
  for (let attempt = 0; attempt < MAX_RECOVERY_QUOTE_ATTEMPTS; attempt++) {
16923
- const credit = buildUniqueCredit(pool.poolId, tokenOutIndex, tokenOutIndex, pool.currentTick, pool.tickSpacing, collisionIds, amountOut);
17083
+ const credit = buildUniqueCredit(pool.poolId, tokenOutIndex, tokenOutIndex, pool.currentTick, pool.tickSpacing, collisionIds, creditOutSize);
16924
17084
  const recoveredDispatch = buildTokenShortfallRecoveryDispatch({
16925
17085
  dispatch: params.dispatch,
16926
17086
  creditTokenId: credit.tokenId,
@@ -16964,10 +17124,14 @@ async function quoteTokenShortfallRecovery(params) {
16964
17124
  const maxAmountIn = maximumAmountIn(estimatedAmountIn, params.slippageBps);
16965
17125
  const swapOutput = getOutputAmount(swapSimulation.tokenFlow, tokenOutIndex);
16966
17126
  const sourceBalance = getBalanceBefore(swapSimulation.tokenFlow, tokenInIndex);
16967
- if (swapOutput < amountOut || sourceBalance < maxAmountIn) return {
17127
+ if (swapOutput < amountOut) {
17128
+ creditOutSize = swapOutput > 0n ? ceilDiv(creditOutSize * amountOut, swapOutput) + 1n : creditOutSize * 2n;
17129
+ continue;
17130
+ }
17131
+ if (sourceBalance < maxAmountIn) return {
16968
17132
  available: false,
16969
17133
  reason: "swap-unavailable",
16970
- detail: swapOutput < amountOut ? `swap output ${swapOutput} < required ${amountOut}` : `source balance ${sourceBalance} < maximumAmountIn ${maxAmountIn} (estimated ${estimatedAmountIn}, slippageBps ${params.slippageBps})`,
17134
+ detail: `source balance ${sourceBalance} < maximumAmountIn ${maxAmountIn} (estimated ${estimatedAmountIn}, slippageBps ${params.slippageBps})`,
16971
17135
  error: new PanopticError("Insufficient source collateral for the recovery swap")
16972
17136
  };
16973
17137
  const recoverySimulation = await simulateDispatch({
@@ -17019,6 +17183,7 @@ async function quoteTokenShortfallRecovery(params) {
17019
17183
  const decodedShortfall = remainingShortfall.assetsRequested - remainingShortfall.assetBalance;
17020
17184
  const additionalAmountOut = decodedShortfall > 0n ? decodedShortfall : amountOut;
17021
17185
  amountOut += additionalAmountOut;
17186
+ creditOutSize = swapOutput > 0n ? ceilDiv(creditOutSize * amountOut, swapOutput) + 1n : creditOutSize * 2n;
17022
17187
  }
17023
17188
  return {
17024
17189
  available: false,
@@ -17045,6 +17210,7 @@ const MAX_EFFECTIVE_LIQUIDITY_LIMIT = 8388607;
17045
17210
  * returned value must be scaled down to the caller's actual `positionSize`.
17046
17211
  */
17047
17212
  const MAX_UINT64$1 = 2n ** 64n - 1n;
17213
+ const FP96$1 = 1n << 96n;
17048
17214
  /**
17049
17215
  * `getRequiredBase` returns `type(uint128).max` as an error sentinel (invalid
17050
17216
  * tokenId or reverting `getMargin`). Detect it so we don't scale a garbage value.
@@ -17057,7 +17223,9 @@ const REQUIRED_BASE_ERROR_SENTINEL = 2n ** 128n - 1n;
17057
17223
  * computes the requirement at `type(uint64).max` size and 0% utilization. Since
17058
17224
  * the requirement is linear in size, the raw result is scaled by
17059
17225
  * `positionSize / type(uint64).max` to yield the requirement for the requested
17060
- * size. Returns collateral requirement in terms of token0.
17226
+ * size. `PanopticQuery.getRequiredBase` returns the cross-margin requirement in
17227
+ * the higher-precision raw token at `atTick`; this function places that amount
17228
+ * in the matching `required0` or `required1` field.
17061
17229
  *
17062
17230
  * @param params - The parameters
17063
17231
  * @returns Estimated collateral requirements with block metadata
@@ -17076,7 +17244,7 @@ async function estimateCollateralRequired(params) {
17076
17244
  });
17077
17245
  effectiveTick = BigInt(currentTickResult);
17078
17246
  }
17079
- const [required0, _meta] = await Promise.all([client.readContract({
17247
+ const [requiredBase, _meta] = await Promise.all([client.readContract({
17080
17248
  address: queryAddress,
17081
17249
  abi: panopticQueryAbi,
17082
17250
  functionName: "getRequiredBase",
@@ -17090,10 +17258,11 @@ async function estimateCollateralRequired(params) {
17090
17258
  client,
17091
17259
  blockNumber: targetBlockNumber
17092
17260
  })]);
17093
- const scaled0 = required0 >= REQUIRED_BASE_ERROR_SENTINEL ? required0 : required0 * positionSize / MAX_UINT64$1;
17261
+ const scaledRequirement = requiredBase >= REQUIRED_BASE_ERROR_SENTINEL ? requiredBase : requiredBase * positionSize / MAX_UINT64$1;
17262
+ const denominatedInToken0 = tickToSqrtPriceX96(effectiveTick) < FP96$1;
17094
17263
  return {
17095
- required0: scaled0,
17096
- required1: 0n,
17264
+ required0: denominatedInToken0 ? scaledRequirement : 0n,
17265
+ required1: denominatedInToken0 ? 0n : scaledRequirement,
17097
17266
  _meta
17098
17267
  };
17099
17268
  }
@@ -20730,7 +20899,9 @@ async function getStreamiaHistory(params) {
20730
20899
  _meta: _meta$1
20731
20900
  };
20732
20901
  }
20733
- const premiaRequests = blockNumbers.map((bn) => client.readContract({
20902
+ const _meta = params._meta ?? await getBlockMeta({ client });
20903
+ const resolvedBlockNumbers = blockNumbers.map((blockNumber) => blockNumber ?? _meta.blockNumber);
20904
+ const premiaRequests = resolvedBlockNumbers.map((blockNumber) => client.readContract({
20734
20905
  address: panopticPoolAddress,
20735
20906
  abi: panopticPoolV2Abi,
20736
20907
  functionName: "getFullPositionsData",
@@ -20739,18 +20910,36 @@ async function getStreamiaHistory(params) {
20739
20910
  true,
20740
20911
  [tokenId]
20741
20912
  ],
20742
- blockNumber: bn
20913
+ blockNumber
20743
20914
  }));
20744
- const uniswapDataPromise = includeUniswapFees && legs.length > 0 ? fetchUniswapFeeData(client, blockNumbers, legs, poolConfig) : void 0;
20745
- const [premiaResults, uniswapData, _meta] = await Promise.all([
20746
- Promise.all(premiaRequests),
20747
- uniswapDataPromise ?? Promise.resolve(void 0),
20748
- params._meta ? Promise.resolve(params._meta) : getBlockMeta({ client })
20749
- ]);
20750
- const sortedSettled = settledEvents ? [...settledEvents].sort((a, b) => a.blockNumber < b.blockNumber ? -1 : 1) : [];
20915
+ const uniswapDataPromise = includeUniswapFees && legs.length > 0 ? fetchUniswapFeeData(client, resolvedBlockNumbers, legs, poolConfig) : void 0;
20916
+ const [premiaResults, uniswapData] = await Promise.all([Promise.all(premiaRequests), uniswapDataPromise ?? Promise.resolve(void 0)]);
20917
+ const sortedSettled = settledEvents ? [...settledEvents].sort((a, b) => a.blockNumber === b.blockNumber ? 0 : a.blockNumber < b.blockNumber ? -1 : 1) : [];
20751
20918
  let settledIdx = 0;
20752
20919
  let accSettled0 = 0n;
20753
20920
  let accSettled1 = 0n;
20921
+ const cumulativePremiaByInputIndex = Array.from({ length: blockNumbers.length }, () => ({
20922
+ token0: 0n,
20923
+ token1: 0n
20924
+ }));
20925
+ const chronologicalInputs = resolvedBlockNumbers.map((blockNumber, inputIndex) => ({
20926
+ blockNumber,
20927
+ inputIndex
20928
+ })).sort((a, b) => a.blockNumber === b.blockNumber ? a.inputIndex - b.inputIndex : a.blockNumber < b.blockNumber ? -1 : 1);
20929
+ for (const { blockNumber, inputIndex } of chronologicalInputs) {
20930
+ while (settledIdx < sortedSettled.length && sortedSettled[settledIdx].blockNumber <= blockNumber) {
20931
+ accSettled0 += sortedSettled[settledIdx].settled0;
20932
+ accSettled1 += sortedSettled[settledIdx].settled1;
20933
+ settledIdx++;
20934
+ }
20935
+ const result = premiaResults[inputIndex];
20936
+ const premia0 = (result[0] & MASK_128) - (result[1] & MASK_128);
20937
+ const premia1 = (result[0] >> 128n) - (result[1] >> 128n);
20938
+ cumulativePremiaByInputIndex[inputIndex] = {
20939
+ token0: premia0 + accSettled0,
20940
+ token1: premia1 + accSettled1
20941
+ };
20942
+ }
20754
20943
  let initialUniswapFees0 = null;
20755
20944
  let initialUniswapFees1 = null;
20756
20945
  const snapshots = premiaResults.map((result, i) => {
@@ -20761,14 +20950,8 @@ async function getStreamiaHistory(params) {
20761
20950
  const short1 = shortPacked >> 128n;
20762
20951
  const long0 = longPacked & MASK_128;
20763
20952
  const long1 = longPacked >> 128n;
20764
- const effectiveBn = bn ?? BigInt(Number.MAX_SAFE_INTEGER);
20765
- while (settledIdx < sortedSettled.length && sortedSettled[settledIdx].blockNumber <= effectiveBn) {
20766
- accSettled0 += sortedSettled[settledIdx].settled0;
20767
- accSettled1 += sortedSettled[settledIdx].settled1;
20768
- settledIdx++;
20769
- }
20770
- const premia0 = short0 - long0 - accSettled0;
20771
- const premia1 = short1 - long1 - accSettled1;
20953
+ const premia0 = short0 - long0;
20954
+ const premia1 = short1 - long1;
20772
20955
  let uniswapFees0 = 0n;
20773
20956
  let uniswapFees1 = 0n;
20774
20957
  if (uniswapData) {
@@ -20787,6 +20970,7 @@ async function getStreamiaHistory(params) {
20787
20970
  token0: premia0,
20788
20971
  token1: premia1
20789
20972
  },
20973
+ cumulativePanopticPremia: cumulativePremiaByInputIndex[i],
20790
20974
  uniswapFees: {
20791
20975
  token0: uniswapFees0,
20792
20976
  token1: uniswapFees1
@@ -21768,6 +21952,31 @@ async function fetchSlot0(client, blockNumber, poolConfig) {
21768
21952
  }
21769
21953
  }
21770
21954
 
21955
+ //#endregion
21956
+ //#region src/panoptic/v2/reads/positionSizes.ts
21957
+ const BIT_MASK_128$1 = (1n << 128n) - 1n;
21958
+ /**
21959
+ * Returns the current stored positionSize for each tokenId, in the same order
21960
+ * as the input `positionIdList`. Reverts (via the contract) if any tokenId is
21961
+ * not held by `account`.
21962
+ */
21963
+ async function getCurrentPositionSizes(params) {
21964
+ const { client, poolAddress, account, positionIdList, blockNumber } = params;
21965
+ if (positionIdList.length === 0) return [];
21966
+ const [, , positionBalances] = await client.readContract({
21967
+ address: poolAddress,
21968
+ abi: panopticPoolV2Abi,
21969
+ functionName: "getFullPositionsData",
21970
+ args: [
21971
+ account,
21972
+ false,
21973
+ positionIdList
21974
+ ],
21975
+ blockNumber
21976
+ });
21977
+ return positionBalances.map((packed) => packed & BIT_MASK_128$1);
21978
+ }
21979
+
21771
21980
  //#endregion
21772
21981
  //#region src/panoptic/v2/reads/enrichment.ts
21773
21982
  /**
@@ -23801,8 +24010,14 @@ async function forceExerciseAndWait(params) {
23801
24010
  * ```
23802
24011
  */
23803
24012
  async function settleAccumulatedPremia(params) {
23804
- const { client, walletClient, account, poolAddress, positionIdList, usePremiaAsCollateral = false, builderCode = 0n, txOverrides } = params;
23805
- const positionSizes = positionIdList.map(() => 0n);
24013
+ const { client, walletClient, account, poolAddress, positionIdList, finalPositionIdList, positionSizes: providedSizes, usePremiaAsCollateral = false, builderCode = 0n, txOverrides } = params;
24014
+ if (providedSizes && providedSizes.length !== positionIdList.length) throw new PanopticError("settleAccumulatedPremia: positionSizes length must match positionIdList");
24015
+ const positionSizes = providedSizes ?? await getCurrentPositionSizes({
24016
+ client,
24017
+ poolAddress,
24018
+ account,
24019
+ positionIdList
24020
+ });
23806
24021
  const tickAndSpreadLimits = positionIdList.map(() => [
23807
24022
  -887272n,
23808
24023
  887272n,
@@ -23817,7 +24032,7 @@ async function settleAccumulatedPremia(params) {
23817
24032
  functionName: "dispatch",
23818
24033
  args: [
23819
24034
  positionIdList,
23820
- positionIdList,
24035
+ finalPositionIdList ?? positionIdList,
23821
24036
  positionSizes.map((s) => BigInt(s)),
23822
24037
  tickAndSpreadLimits.map((t) => [
23823
24038
  Number(t[0]),
@@ -25398,14 +25613,21 @@ const multicallAbi = [{
25398
25613
  * @returns Simulation result with settlement data or error
25399
25614
  */
25400
25615
  async function simulateSettle(params) {
25401
- const { client, poolAddress, account, positionIdList, tokenId, blockNumber } = params;
25616
+ const { client, poolAddress, account, positionIdList, finalPositionIdList, positionSizes: providedSizes, tokenId, blockNumber } = params;
25402
25617
  const targetBlockNumber = blockNumber ?? await client.getBlockNumber();
25403
25618
  const metaPromise = getBlockMeta({
25404
25619
  client,
25405
25620
  blockNumber: targetBlockNumber
25406
25621
  });
25407
25622
  try {
25408
- const positionSizes = positionIdList.map(() => 0n);
25623
+ if (providedSizes && providedSizes.length !== positionIdList.length) throw new PanopticError("simulateSettle: positionSizes length must match positionIdList");
25624
+ const positionSizes = providedSizes ?? await getCurrentPositionSizes({
25625
+ client,
25626
+ poolAddress,
25627
+ account,
25628
+ positionIdList,
25629
+ blockNumber: targetBlockNumber
25630
+ });
25409
25631
  const tickAndSpreadLimits = positionIdList.map(() => [
25410
25632
  -887272n,
25411
25633
  887272n,
@@ -25416,7 +25638,7 @@ async function simulateSettle(params) {
25416
25638
  functionName: "dispatch",
25417
25639
  args: [
25418
25640
  positionIdList,
25419
- positionIdList,
25641
+ finalPositionIdList ?? positionIdList,
25420
25642
  positionSizes.map((s) => BigInt(s)),
25421
25643
  tickAndSpreadLimits.map((t) => [
25422
25644
  Number(t[0]),
@@ -25448,8 +25670,8 @@ async function simulateSettle(params) {
25448
25670
  });
25449
25671
  const _meta = await metaPromise;
25450
25672
  const data = {
25451
- premiaReceived0: tokenFlow.delta0 > 0n ? tokenFlow.delta0 : 0n,
25452
- premiaReceived1: tokenFlow.delta1 > 0n ? tokenFlow.delta1 : 0n,
25673
+ premiaReceived0: tokenFlow.delta0,
25674
+ premiaReceived1: tokenFlow.delta1,
25453
25675
  postCollateral0: tokenFlow.balanceAfter0,
25454
25676
  postCollateral1: tokenFlow.balanceAfter1,
25455
25677
  forfeitAmounts
@@ -27871,5 +28093,5 @@ const VAULT_DISPLAY_NAME_RESOLVERS_PER_CHAIN = {
27871
28093
  };
27872
28094
 
27873
28095
  //#endregion
27874
- export { AccountInsolventError, AlreadyInitializedError, BORROW_INDEX_BITS, BPS_DENOMINATOR, BPS_SCALE, BatchValidationError, BelowMinimumRedemptionError, CastingError, ChunkHasZeroLiquidityError, ChunkLimitError, CrossPoolError, DEFAULT_MAX_SPREAD, DEFAULT_MINT_BUFFER, DEFAULT_MIN_SWAP_RATIO_BPS, DEFAULT_RECONNECT_CONFIG, DEFAULT_VEGOID, DepositTooLargeError, DuplicateTokenIdError, EffectiveLiquidityAboveThresholdError, ExceedsMaximumRedemptionError, InputListFailError, InsufficientCreditLiquidityError, InvalidBuilderCodeError, InvalidHistoryRangeError, InvalidTickBoundError, InvalidTickError, InvalidTokenIdParameterError, InvalidUniswapCallbackError, LEG_BITS, LEG_LIMITS, LEG_MASKS, LengthMismatchError, LiquidityTooHighError, LoanSlotExhaustedError, MARKET_EPOCH_BITS, MARKET_EPOCH_SHIFT, MAX_TICK, MAX_TRACKED_CHUNKS, MINT_BUFFER, MINT_BUFFER_DENOMINATOR, MIN_TICK, MaxRetriesExceededError, MissingPositionIdsError, NetLiquidityZeroError, NetworkMismatchError, NoLegsExercisableError, NotALongLegError, NotBuilderError, NotEnoughLiquidityInChunkError, NotEnoughTokensError, NotGuardianError, NotMarginCalledError, NotPanopticPoolError, ORACLE_EPOCH_SECONDS, OracleRateLimitedError, PanopticError, PanopticHelperNotDeployedError, PanopticValidationError, PoolNotInitializedError, PositionCountNotZeroError, PositionNotOwnedError, PositionSnapshotNotFoundError, PositionTooLargeError, PriceBoundFailError, PriceImpactTooLargeError, ProviderLagError, RATE_AT_TARGET_BITS, REORG_DEPTH, REQUIRED_BASE_ERROR_SENTINEL, ReentrancyError, RpcError, RpcResponseError, SCHEMA_VERSION, SECONDS_PER_YEAR, STANDARD_TICK_WIDTHS, STORAGE_PREFIX, SafeModeError, StaleDataError, StaleOracleError, SwapTokenMismatchError, SyncTimeoutError, TOKEN_ID_BITS, TokenIdHasZeroLegsError, TooManyLegsOpenError, TransferFailedError, UNREALIZED_INTEREST_BITS, UTILIZATION_DENOMINATOR, UnauthorizedUniswapCallbackError, UnderOverFlowError, UnhealthyPoolError, WAD, WrongPoolIdError, WrongUniswapPoolError, ZERO_COLLATERAL, ZERO_VALUATION, ZeroAddressError, ZeroCollateralRequirementError, addLegToTokenId, addPendingPosition, addTrackedChunks, annualizePerSecondRateWad, applyMintBuffer, applyMintBufferPerToken, apportion, approve, approveAndWait, approvePool, assertCanBurn, assertCanForceExercise, assertCanLiquidate, assertCanMint, assertFresh, assertHealthy, assertTradeable, borrow, borrowAndWait, buildBatchDispatchArgs, buildCreditSwapCall, buildCreditWrappedDispatch, buildOpenPositionCalldata, buildSfpmSwapCalldata, buildSfpmSwapPlan, buildTokenShortfallRecoveryDispatch, buildUniqueCredit, buildUniqueLoan, buildUniqueWidthZeroLeg, calculateAccountGreeksPure, calculatePortfolioDelta, calculatePortfolioGamma, calculatePortfolioGreeks, calculatePortfolioValue, calculatePositionDelta, calculatePositionDeltaDebtOnly, calculatePositionDeltaWithSwap, calculatePositionGamma, calculatePositionGreeks, calculatePositionValue, calculateResyncBlock, calculateSpreadWad, cancelTransaction, checkApproval, checkCollateralAcrossTicks, classifyStrategyGroups, cleanupStalePendingPositions, clearCheckpoint, clearPendingPositions, clearTrackedChunks, clearTrackedPositions, clearTradeHistory, closePosition, closePositionAndWait, collateralRuleKindFor, collateralTrackerV2Abi, computeV4PoolId, confirmPendingPosition, convertToAssets, convertToShares, countLegs, createEventPoller, createEventSubscription, createFileStorage, createFlowNeutralTokenId, createMemoryStorage, createNonceManager, createPoolFormatters, createTokenIdBuilder, decodeAllDispatchCalldata, decodeAllLegs, decodeDispatchCalldata, decodeLeftRightSigned, decodeLeftRightUnsigned, decodeLeg, decodeOraclePack, decodeOracleRiskParameters, decodeOracleTiming, decodePanopticTokenURI, decodePoolId, decodeTickSpacing, decodeTokenId, decodeVegoid, deployNewPool, deployNewPoolAndWait, deposit, depositAndWait, deriveSupplyRatePerSecWad, detectReorg, diagnoseOracleSafeMode, dispatch, dispatchAndWait, encodeLeg, encodePoolId, encodePoolKeyBytes, encodeV3PoolKeyBytes, encodeV4PoolId, ensureSfpmV3PoolInitialized, estimateBlockNumbers, estimateCollateralBreakdown, estimateCollateralRequired, executeBatchDispatch, executeBatchDispatchAndWait, failPendingPosition, feesFromFeeGrowthDelta, fetchPoolId, fetchSfpmV3PoolId, forceExercise, forceExerciseAndWait, formatBlockNumber, formatBps, formatCompact, formatDatetime, formatDuration, formatDurationSeconds, formatFeeTier, formatGas, formatGwei, formatPerSecondRateWadAsAprPct, formatPerSecondRateWadAsApyPct, formatPoolIdHex, formatPriceRange, formatRateWad, formatRatioPercent, formatTick, formatTickRange, formatTimestamp, formatTimestampLocale, formatTokenAmount, formatTokenAmountSigned, formatTokenDelta, formatTokenFlow, formatTokenIdHex, formatTokenIdShort, formatTxHash, formatUtilization, formatWad, formatWadPercent, formatWadSigned, formatWei, getAccountBuyingPower, getAccountCollateral, getAccountGreeks, getAccountHistory, getAccountPremia, getAccountSummaryBasic, getAccountSummaryRisk, getAssetIndex, getBlockMeta, getChainDeployment, getChunkLiquidities, getChunkSpreads, getClosedPositions, getClosedPositionsKey, getCollateralAddresses, getCollateralData, getCollateralSharePrices, getCollateralTotalAssetsBatch, getCurrentRates, getDeltaHedgeParams, getEnforcedTickLimits, getFactoryConstructMetadata, getFactoryOwnerOf, getFactoryTokenURI, getGuardianUnlockState, getInterestState, getIrmCurrent, getIrmCurve, getItmAmounts, getLegDelta, getLegDeltaInVaultFrame, getLegGamma, getLegNetValueWidth0, getLegValue, getLiquidationPrices, getMarginBuffer, getMaxPositionSize, getMaxRedeem, getMaxWithdrawable, getNativeTokenPrice, getNetLiquidationValue, getNetLiquidationValues, getNotEnoughTokensError, getOpenPositionIds, getOpenPositionPreview, getOracleRiskParameters, getOracleState, getPanopticPoolAddress, getPanopticPoolFromPoolId, getPendingPositions, getPendingPositionsKey, getPool, getPoolDeploymentBlock, getPoolDisplayId, getPoolLiquidities, getPoolMetaKey, getPoolMetadata, getPoolPrefix, getPortfolioValue, getPosition, getPositionChunkData, getPositionEnrichmentData, getPositionGreeks, getPositionMetaKey, getPositions, getPositionsKey, getPositionsWithPremia, getPriceHistory, getPricesAtTick, getRealizedPnL, getRequiredCreditForITM, getRiskParameters, getSafeMode, getSchemaVersionKey, getStreamiaHistory, getSyncCheckpointKey, getSyncStatus, getTickSpacing, getTokenListId, getTrackedChunks, getTrackedChunksKey, getTrackedPositionIds, getTradeHistory, getUniswapFeeHistory, getUniswapV3LpPositionState, getUniswapV3PoolFromId, getUniswapV3PoolInfo, getUniswapV3PoolLiquidities, getUniswapV4LpPositionState, getUniswapV4PoolBasicState, getUniswapV4PoolInfo, getUniswapV4PoolKeyFromId, getUniswapV4PoolLiquidities, getUtilization, hasLoanOrCredit, hasLongLeg, interpolateBlocks, isCall, isCredit, isCreditLeg, isDefinedRisk, isGasError, isInputListFailError, isLiquidatable, isLoan, isLoanLeg, isNonceError, isPanopticErrorType, isPositionTracked, isRetryableRpcError, isShortOnly, isSpread, isSupportedChain, isolateGroupTokenId, jsonSerializer, liquidate, liquidateAndWait, loadCheckpoint, minePoolAddress, mint, mintAndWait, mintableAfterBuffer, multicallRead, openPosition, openPositionAndWait, optimizeTokenIdRiskPartners, oracleEpochAt, packMarketState, panopticPoolV2Abi, parseBps, parseCollateralLog, parsePanopticError, parsePoolLog, parseTokenAmount, parseTokenListId, parseWad, pokeOracle, pokeOracleAndWait, previewBorrow, previewDeposit, previewMint, previewRedeem, previewUnwrap, previewWithdraw, previewWrap, priceToTick, publicBroadcaster, quoteOneTokenFlow, quoteSfpmSwap, quoteTokenShortfallRecovery, quoteV3ExactIn, ratePerSecWadToAprPct, reconstructFromEvents, recoverSnapshot, recoverSnapshotFromTx, redeem, redeemAndWait, removeTrackedChunks, repay, repayAndWait, requireChainDeployment, resolveBlockNumbers, resolvePanopticPoolFromPoolId, resolveTokenIndex, resolveUniswapV4PoolKey, riskEngineAbi, rollPosition, rollPositionAndWait, roundToTickSpacing, saveCheckpoint, saveClosedPosition, scanChunks, selectDispatchForAccount, settleAccumulatedPremia, settleAccumulatedPremiaAndWait, simulateBatchDispatch, simulateClosePosition, simulateDeployNewPool, simulateDeposit, simulateDispatch, simulateForceExercise, simulateLiquidate, simulateOpenPosition, simulateSFPMBurn, simulateSFPMMint, simulateSettle, simulateSwapExactIn, simulateSwapExactOut, simulateWithTokenFlow, simulateWithdraw, slippageBpsToTickDistance, smartRepay, smartRepayAndWait, speedUpTransaction, sqrtPriceX96ToPriceDecimalScaled, sqrtPriceX96ToTick, supply, supplyAndWait, swapExactIn, swapExactInAndWait, swapExactOut, swapExactOutAndWait, syncPositions, tickLimits, tickToPrice, tickToPriceDecimalScaled, tickToSqrtPriceX96, toVaultFrameAtTick, truncateAddress, unsupply, unsupplyAndWait, unwrapWeth, unwrapWethAndWait, unwrapXstock, unwrapXstockAndWait, utilizationBpsToWad, utilizationPctToWad, validateBatch, validateBuilderCode, validatePoolId, verifyBlockContinuity, watchEvents, wethWrapAbi, withdraw, withdrawAndWait, withdrawWithPositions, withdrawWithPositionsAndWait, wrapEth, wrapEthAndWait, wrapXstock, wrapXstockAndWait, xstockWrapperAbi };
28096
+ export { AccountInsolventError, AlreadyInitializedError, BORROW_INDEX_BITS, BPS_DENOMINATOR, BPS_SCALE, BatchValidationError, BelowMinimumRedemptionError, CastingError, ChunkHasZeroLiquidityError, ChunkLimitError, CrossPoolError, DEFAULT_MAX_SPREAD, DEFAULT_MINT_BUFFER, DEFAULT_MIN_SWAP_RATIO_BPS, DEFAULT_RECONNECT_CONFIG, DEFAULT_VEGOID, DepositTooLargeError, DuplicateTokenIdError, EffectiveLiquidityAboveThresholdError, ExceedsMaximumRedemptionError, InputListFailError, InsufficientCreditLiquidityError, InvalidBuilderCodeError, InvalidHistoryRangeError, InvalidTickBoundError, InvalidTickError, InvalidTokenIdParameterError, InvalidUniswapCallbackError, LEG_BITS, LEG_LIMITS, LEG_MASKS, LengthMismatchError, LiquidityTooHighError, LoanSlotExhaustedError, MARKET_EPOCH_BITS, MARKET_EPOCH_SHIFT, MAX_TICK, MAX_TRACKED_CHUNKS, MINT_BUFFER, MINT_BUFFER_DENOMINATOR, MIN_TICK, MaxRetriesExceededError, MissingPositionIdsError, NetLiquidityZeroError, NetworkMismatchError, NoLegsExercisableError, NotALongLegError, NotBuilderError, NotEnoughLiquidityInChunkError, NotEnoughTokensError, NotGuardianError, NotMarginCalledError, NotPanopticPoolError, ORACLE_EPOCH_SECONDS, OracleRateLimitedError, PanopticError, PanopticHelperNotDeployedError, PanopticValidationError, PoolNotInitializedError, PositionCountNotZeroError, PositionNotOwnedError, PositionSnapshotNotFoundError, PositionTooLargeError, PriceBoundFailError, PriceImpactTooLargeError, ProviderLagError, RATE_AT_TARGET_BITS, REORG_DEPTH, REQUIRED_BASE_ERROR_SENTINEL, ReentrancyError, RpcError, RpcResponseError, SCHEMA_VERSION, SECONDS_PER_YEAR, STANDARD_TICK_WIDTHS, STORAGE_PREFIX, SafeModeError, StaleDataError, StaleOracleError, SwapTokenMismatchError, SyncTimeoutError, TOKEN_ID_BITS, TokenIdHasZeroLegsError, TooManyLegsOpenError, TransferFailedError, UNREALIZED_INTEREST_BITS, UTILIZATION_DENOMINATOR, UnauthorizedUniswapCallbackError, UnderOverFlowError, UnhealthyPoolError, WAD, WrongPoolIdError, WrongUniswapPoolError, ZERO_COLLATERAL, ZERO_VALUATION, ZeroAddressError, ZeroCollateralRequirementError, addLegToTokenId, addPendingPosition, addTrackedChunks, annualizePerSecondRateWad, applyMintBuffer, applyMintBufferPerToken, apportion, approve, approveAndWait, approvePool, assertCanBurn, assertCanForceExercise, assertCanLiquidate, assertCanMint, assertFresh, assertHealthy, assertTradeable, borrow, borrowAndWait, buildBatchDispatchArgs, buildCreditSwapCall, buildCreditWrappedDispatch, buildOpenPositionCalldata, buildSfpmSwapCalldata, buildSfpmSwapPlan, buildTokenShortfallRecoveryDispatch, buildUniqueCredit, buildUniqueLoan, buildUniqueWidthZeroLeg, calculateAccountGreeksPure, calculatePortfolioDelta, calculatePortfolioGamma, calculatePortfolioGreeks, calculatePortfolioValue, calculatePositionDelta, calculatePositionDeltaDebtOnly, calculatePositionDeltaWithSwap, calculatePositionGamma, calculatePositionGreeks, calculatePositionValue, calculateResyncBlock, calculateSpreadWad, cancelTransaction, checkApproval, checkCollateralAcrossTicks, classifyStrategyGroups, cleanupStalePendingPositions, clearCheckpoint, clearPendingPositions, clearTrackedChunks, clearTrackedPositions, clearTradeHistory, closePosition, closePositionAndWait, collateralRuleKindFor, collateralTrackerV2Abi, computeV4PoolId, confirmPendingPosition, convertToAssets, convertToShares, countLegs, createEventPoller, createEventSubscription, createFileStorage, createFlowNeutralTokenId, createMemoryStorage, createNonceManager, createPoolFormatters, createTokenIdBuilder, decodeAllDispatchCalldata, decodeAllLegs, decodeDispatchCalldata, decodeLeftRightSigned, decodeLeftRightUnsigned, decodeLeg, decodeOraclePack, decodeOracleRiskParameters, decodeOracleTiming, decodePanopticTokenURI, decodePoolId, decodeTickSpacing, decodeTokenId, decodeVegoid, deployNewPool, deployNewPoolAndWait, deposit, depositAndWait, deriveSupplyRatePerSecWad, deriveUniqueTokenId, detectReorg, diagnoseOracleSafeMode, dispatch, dispatchAndWait, encodeLeg, encodePoolId, encodePoolKeyBytes, encodeV3PoolKeyBytes, encodeV4PoolId, ensureSfpmV3PoolInitialized, estimateBlockNumbers, estimateCollateralBreakdown, estimateCollateralRequired, executeBatchDispatch, executeBatchDispatchAndWait, failPendingPosition, feesFromFeeGrowthDelta, fetchPoolId, fetchSfpmV3PoolId, forceExercise, forceExerciseAndWait, formatBlockNumber, formatBps, formatCompact, formatDatetime, formatDuration, formatDurationSeconds, formatFeeTier, formatGas, formatGwei, formatPerSecondRateWadAsAprPct, formatPerSecondRateWadAsApyPct, formatPoolIdHex, formatPriceRange, formatRateWad, formatRatioPercent, formatTick, formatTickRange, formatTimestamp, formatTimestampLocale, formatTokenAmount, formatTokenAmountSigned, formatTokenDelta, formatTokenFlow, formatTokenIdHex, formatTokenIdShort, formatTxHash, formatUtilization, formatWad, formatWadPercent, formatWadSigned, formatWei, getAccountBuyingPower, getAccountCollateral, getAccountGreeks, getAccountHistory, getAccountPremia, getAccountSummaryBasic, getAccountSummaryRisk, getAssetIndex, getBlockMeta, getChainDeployment, getChunkLiquidities, getChunkSpreads, getClosedPositions, getClosedPositionsKey, getCollateralAddresses, getCollateralData, getCollateralSharePrices, getCollateralTotalAssetsBatch, getCurrentRates, getDeltaHedgeParams, getEnforcedTickLimits, getFactoryConstructMetadata, getFactoryOwnerOf, getFactoryTokenURI, getGuardianUnlockState, getInterestState, getIrmCurrent, getIrmCurve, getItmAmounts, getLegDelta, getLegDeltaInVaultFrame, getLegGamma, getLegNetValueWidth0, getLegValue, getLiquidationPrices, getMarginBuffer, getMaxPositionSize, getMaxRedeem, getMaxWithdrawable, getNativeTokenPrice, getNetLiquidationValue, getNetLiquidationValues, getNotEnoughTokensError, getOpenPositionIds, getOpenPositionPreview, getOracleRiskParameters, getOracleState, getPanopticPoolAddress, getPanopticPoolFromPoolId, getPendingPositions, getPendingPositionsKey, getPool, getPoolDeploymentBlock, getPoolDisplayId, getPoolLiquidities, getPoolMetaKey, getPoolMetadata, getPoolPrefix, getPortfolioValue, getPosition, getPositionChunkData, getPositionEnrichmentData, getPositionGreeks, getPositionMetaKey, getPositions, getPositionsKey, getPositionsWithPremia, getPriceHistory, getPricesAtTick, getRealizedPnL, getRequiredCreditForITM, getRiskParameters, getSafeMode, getSchemaVersionKey, getStreamiaHistory, getSyncCheckpointKey, getSyncStatus, getTickSpacing, getTokenListId, getTrackedChunks, getTrackedChunksKey, getTrackedPositionIds, getTradeHistory, getUniswapFeeHistory, getUniswapV3LpPositionState, getUniswapV3PoolFromId, getUniswapV3PoolInfo, getUniswapV3PoolLiquidities, getUniswapV4LpPositionState, getUniswapV4PoolBasicState, getUniswapV4PoolInfo, getUniswapV4PoolKeyFromId, getUniswapV4PoolLiquidities, getUtilization, hasLoanOrCredit, hasLongLeg, interpolateBlocks, isCall, isCredit, isCreditLeg, isDefinedRisk, isGasError, isInputListFailError, isLiquidatable, isLoan, isLoanLeg, isNonceError, isPanopticErrorType, isPositionTracked, isRetryableRpcError, isShortOnly, isSpread, isSupportedChain, isolateGroupTokenId, jsonSerializer, liquidate, liquidateAndWait, loadCheckpoint, minePoolAddress, mint, mintAndWait, mintableAfterBuffer, multicallRead, openPosition, openPositionAndWait, optimizeTokenIdRiskPartners, oracleEpochAt, packMarketState, panopticPoolV2Abi, parseBps, parseCollateralLog, parsePanopticError, parsePoolLog, parseTokenAmount, parseTokenListId, parseWad, planDeriveStrategy, pokeOracle, pokeOracleAndWait, previewBorrow, previewDeposit, previewMint, previewRedeem, previewUnwrap, previewWithdraw, previewWrap, priceToTick, publicBroadcaster, quoteOneTokenFlow, quoteSfpmSwap, quoteTokenShortfallRecovery, quoteV3ExactIn, ratePerSecWadToAprPct, reconstructFromEvents, recoverSnapshot, recoverSnapshotFromTx, redeem, redeemAndWait, removeTrackedChunks, repay, repayAndWait, requireChainDeployment, resolveBlockNumbers, resolvePanopticPoolFromPoolId, resolveTokenIndex, resolveUniswapV4PoolKey, riskEngineAbi, rollPosition, rollPositionAndWait, roundToTickSpacing, saveCheckpoint, saveClosedPosition, scanChunks, selectDispatchForAccount, settleAccumulatedPremia, settleAccumulatedPremiaAndWait, simulateBatchDispatch, simulateClosePosition, simulateDeployNewPool, simulateDeposit, simulateDispatch, simulateForceExercise, simulateLiquidate, simulateOpenPosition, simulateSFPMBurn, simulateSFPMMint, simulateSettle, simulateSwapExactIn, simulateSwapExactOut, simulateWithTokenFlow, simulateWithdraw, slippageBpsToTickDistance, smartRepay, smartRepayAndWait, speedUpTransaction, sqrtPriceX96ToPriceDecimalScaled, sqrtPriceX96ToTick, supply, supplyAndWait, swapExactIn, swapExactInAndWait, swapExactOut, swapExactOutAndWait, syncPositions, tickLimits, tickToPrice, tickToPriceDecimalScaled, tickToSqrtPriceX96, toVaultFrameAtTick, truncateAddress, unsupply, unsupplyAndWait, unwrapWeth, unwrapWethAndWait, unwrapXstock, unwrapXstockAndWait, utilizationBpsToWad, utilizationPctToWad, validateBatch, validateBuilderCode, validatePoolId, verifyBlockContinuity, watchEvents, wethWrapAbi, withdraw, withdrawAndWait, withdrawWithPositions, withdrawWithPositionsAndWait, wrapEth, wrapEthAndWait, wrapXstock, wrapXstockAndWait, xstockWrapperAbi };
27875
28097
  //# sourceMappingURL=index.js.map