@panoptic-eng/sdk 1.0.40 → 1.0.42

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.
@@ -10027,1763 +10027,1922 @@ function hasLoanOrCredit(tokenId) {
10027
10027
  }
10028
10028
 
10029
10029
  //#endregion
10030
- //#region src/panoptic/v2/sfpmSwap/calldata.ts
10031
- /**
10032
- * Encode the `multicall([mint, burn])` for a swap plan.
10033
- *
10034
- * The order is always `[mint, burn]` — the ERC1155 must be minted before it is
10035
- * burned. Which call carries the inverted (swap) limits is decided in the plan.
10036
- */
10037
- function buildSfpmSwapCalldata(plan) {
10038
- const mintData = encodeFunctionData({
10039
- abi: semiFungiblePositionManagerV3Abi,
10040
- functionName: "mintTokenizedPosition",
10041
- args: [
10042
- plan.poolKey,
10043
- plan.tokenId,
10044
- plan.positionSize,
10045
- plan.mintTickLimits[0],
10046
- plan.mintTickLimits[1]
10047
- ]
10048
- });
10049
- const burnData = encodeFunctionData({
10050
- abi: semiFungiblePositionManagerV3Abi,
10051
- functionName: "burnTokenizedPosition",
10052
- args: [
10053
- plan.poolKey,
10054
- plan.tokenId,
10055
- plan.positionSize,
10056
- plan.burnTickLimits[0],
10057
- plan.burnTickLimits[1]
10058
- ]
10059
- });
10060
- const multicallData = encodeFunctionData({
10061
- abi: semiFungiblePositionManagerV3Abi,
10062
- functionName: "multicall",
10063
- args: [[mintData, burnData]]
10064
- });
10030
+ //#region src/panoptic/v2/formatters/tick.ts
10031
+ const Q192$4 = 1n << 192n;
10032
+ const RAW_PRICE_PRECISION = 40n;
10033
+ function pow10(exponent) {
10034
+ if (exponent < 0n) throw new RangeError("Exponent must be non-negative");
10035
+ return 10n ** exponent;
10036
+ }
10037
+ function absBigint(value) {
10038
+ return value < 0n ? -value : value;
10039
+ }
10040
+ function trimTrailingZeros(value) {
10041
+ const dotIndex = value.indexOf(".");
10042
+ if (dotIndex === -1) return value;
10043
+ let end = value.length;
10044
+ while (end > dotIndex && value[end - 1] === "0") end -= 1;
10045
+ if (end === dotIndex + 1) end = dotIndex;
10046
+ return value.slice(0, end);
10047
+ }
10048
+ function formatRatio$3(numerator, denominator, precision) {
10049
+ if (precision < 0n) throw new RangeError("Precision must be non-negative");
10050
+ const sign = numerator < 0n ? "-" : "";
10051
+ const absNumerator = numerator < 0n ? -numerator : numerator;
10052
+ const scale = pow10(precision);
10053
+ const scaled = (absNumerator * scale + denominator / 2n) / denominator;
10054
+ const integerPart = scaled / scale;
10055
+ const fractionalPart = scaled % scale;
10056
+ if (precision === 0n) return `${sign}${integerPart}`;
10057
+ return `${sign}${integerPart}.${fractionalPart.toString().padStart(Number(precision), "0")}`;
10058
+ }
10059
+ function parseDecimalToFraction(value) {
10060
+ const trimmed = value.trim();
10061
+ if (trimmed.length === 0) throw new Error("Price must be a number");
10062
+ const isNegative = trimmed.startsWith("-");
10063
+ const unsigned = isNegative || trimmed.startsWith("+") ? trimmed.slice(1) : trimmed;
10064
+ const [basePart, exponentPart] = unsigned.toLowerCase().split("e");
10065
+ const [integerStr, fractionalStr = ""] = basePart.split(".");
10066
+ if (integerStr === "" && fractionalStr === "") throw new Error("Price must be a number");
10067
+ const integerDigits = integerStr === "" ? "0" : integerStr;
10068
+ const digits = `${integerDigits}${fractionalStr}`;
10069
+ let numerator = BigInt(digits === "" ? "0" : digits);
10070
+ let denominator = pow10(BigInt(fractionalStr.length));
10071
+ if (exponentPart !== void 0 && exponentPart !== "") {
10072
+ const exponent = BigInt(exponentPart);
10073
+ if (exponent > 0n) numerator *= pow10(exponent);
10074
+ else if (exponent < 0n) denominator *= pow10(-exponent);
10075
+ }
10076
+ if (isNegative) numerator = -numerator;
10065
10077
  return {
10066
- multicallData,
10067
- mintData,
10068
- burnData
10078
+ numerator,
10079
+ denominator
10069
10080
  };
10070
10081
  }
10071
-
10072
- //#endregion
10073
- //#region src/panoptic/v2/sfpmSwap/init.ts
10074
- /**
10075
- * Resolve the `uint64` SFPM poolId for a Uniswap v3 pool, initializing it if needed.
10076
- *
10077
- * `initializeAMMPool` is permissionless and idempotent and **returns the poolId**
10078
- * whether or not the pool was already registered, so a `simulateContract` call is
10079
- * enough to learn the id without sending a transaction. Always resolve the id this
10080
- * way rather than encoding it offline — the SFPM can collision-increment ids.
10081
- */
10082
- async function fetchSfpmV3PoolId(params) {
10083
- const { client, sfpmAddress, token0, token1, fee } = params;
10084
- const vegoid$1 = params.vegoid ?? Number(DEFAULT_VEGOID);
10085
- const { result } = await client.simulateContract({
10086
- address: sfpmAddress,
10087
- abi: semiFungiblePositionManagerV3Abi,
10088
- functionName: "initializeAMMPool",
10089
- args: [
10090
- token0,
10091
- token1,
10092
- fee,
10093
- vegoid$1
10094
- ]
10095
- });
10096
- return BigInt(result);
10082
+ function compareRatios(leftNumerator, leftDenominator, rightNumerator, rightDenominator) {
10083
+ const left = leftNumerator * rightDenominator;
10084
+ const right = rightNumerator * leftDenominator;
10085
+ if (left === right) return 0;
10086
+ return left < right ? -1 : 1;
10097
10087
  }
10098
- /**
10099
- * Ensure a Uniswap v3 pool is registered on the SFPM, sending `initializeAMMPool`
10100
- * only if it is not already registered. Returns the resolved poolId.
10101
- *
10102
- * When `expectedPool` is given, the resolved id is checked to map back to it via
10103
- * `getUniswapV3PoolFromId` — guards against a wrong token/fee triple.
10104
- */
10105
- async function ensureSfpmV3PoolInitialized(params) {
10106
- const { client, wallet, sfpmAddress, token0, token1, fee, expectedPool } = params;
10107
- const vegoid$1 = params.vegoid ?? Number(DEFAULT_VEGOID);
10108
- const poolId = await fetchSfpmV3PoolId({
10109
- client,
10110
- sfpmAddress,
10111
- token0,
10112
- token1,
10113
- fee,
10114
- vegoid: vegoid$1
10115
- });
10116
- const registered = await client.readContract({
10117
- address: sfpmAddress,
10118
- abi: semiFungiblePositionManagerV3Abi,
10119
- functionName: "getUniswapV3PoolFromId",
10120
- args: [poolId]
10121
- });
10122
- let initialized = false;
10123
- let resolved = registered;
10124
- if (getAddress(registered) === zeroAddress) {
10125
- const account = wallet.account;
10126
- if (account === void 0) throw new PanopticError("wallet client has no account");
10127
- const hash = await wallet.writeContract({
10128
- account,
10129
- chain: wallet.chain ?? null,
10130
- address: sfpmAddress,
10131
- abi: semiFungiblePositionManagerV3Abi,
10132
- functionName: "initializeAMMPool",
10133
- args: [
10134
- token0,
10135
- token1,
10136
- fee,
10137
- vegoid$1
10138
- ]
10139
- });
10140
- await client.waitForTransactionReceipt({ hash });
10141
- initialized = true;
10142
- resolved = await client.readContract({
10143
- address: sfpmAddress,
10144
- abi: semiFungiblePositionManagerV3Abi,
10145
- functionName: "getUniswapV3PoolFromId",
10146
- args: [poolId]
10147
- });
10148
- }
10149
- if (expectedPool !== void 0 && getAddress(resolved) !== getAddress(expectedPool)) throw new WrongUniswapPoolError(new PanopticError(`SFPM poolId ${poolId} resolves to ${resolved}, expected ${expectedPool}`));
10088
+ function tickToSqrtPriceX96(tick) {
10089
+ if (tick < MIN_TICK || tick > MAX_TICK) throw new RangeError("Tick out of bounds");
10090
+ const absTick = tick < 0n ? -tick : tick;
10091
+ let ratio = (absTick & 0x1n) !== 0n ? 0xfffcb933bd6fad37aa2d162d1a594001n : 0x100000000000000000000000000000000n;
10092
+ if ((absTick & 0x2n) !== 0n) ratio = ratio * 0xfff97272373d413259a46990580e213an >> 128n;
10093
+ if ((absTick & 0x4n) !== 0n) ratio = ratio * 0xfff2e50f5f656932ef12357cf3c7fdccn >> 128n;
10094
+ if ((absTick & 0x8n) !== 0n) ratio = ratio * 0xffe5caca7e10e4e61c3624eaa0941cd0n >> 128n;
10095
+ if ((absTick & 0x10n) !== 0n) ratio = ratio * 0xffcb9843d60f6159c9db58835c926644n >> 128n;
10096
+ if ((absTick & 0x20n) !== 0n) ratio = ratio * 0xff973b41fa98c081472e6896dfb254c0n >> 128n;
10097
+ if ((absTick & 0x40n) !== 0n) ratio = ratio * 0xff2ea16466c96a3843ec78b326b52861n >> 128n;
10098
+ if ((absTick & 0x80n) !== 0n) ratio = ratio * 0xfe5dee046a99a2a811c461f1969c3053n >> 128n;
10099
+ if ((absTick & 0x100n) !== 0n) ratio = ratio * 0xfcbe86c7900a88aedcffc83b479aa3a4n >> 128n;
10100
+ if ((absTick & 0x200n) !== 0n) ratio = ratio * 0xf987a7253ac413176f2b074cf7815e54n >> 128n;
10101
+ if ((absTick & 0x400n) !== 0n) ratio = ratio * 0xf3392b0822b70005940c7a398e4b70f3n >> 128n;
10102
+ if ((absTick & 0x800n) !== 0n) ratio = ratio * 0xe7159475a2c29b7443b29c7fa6e889d9n >> 128n;
10103
+ if ((absTick & 0x1000n) !== 0n) ratio = ratio * 0xd097f3bdfd2022b8845ad8f792aa5825n >> 128n;
10104
+ if ((absTick & 0x2000n) !== 0n) ratio = ratio * 0xa9f746462d870fdf8a65dc1f90e061e5n >> 128n;
10105
+ if ((absTick & 0x4000n) !== 0n) ratio = ratio * 0x70d869a156d2a1b890bb3df62baf32f7n >> 128n;
10106
+ if ((absTick & 0x8000n) !== 0n) ratio = ratio * 0x31be135f97d08fd981231505542fcfa6n >> 128n;
10107
+ if ((absTick & 0x10000n) !== 0n) ratio = ratio * 0x9aa508b5b7a84e1c677de54f3e99bc9n >> 128n;
10108
+ if ((absTick & 0x20000n) !== 0n) ratio = ratio * 0x5d6af8dedb81196699c329225ee604n >> 128n;
10109
+ if ((absTick & 0x40000n) !== 0n) ratio = ratio * 0x2216e584f5fa1ea926041bedfe98n >> 128n;
10110
+ if ((absTick & 0x80000n) !== 0n) ratio = ratio * 0x48a170391f7dc42444e8fa2n >> 128n;
10111
+ if (tick > 0n) ratio = ((1n << 256n) - 1n) / ratio;
10112
+ const remainderMask = (1n << 32n) - 1n;
10113
+ const sqrtPriceX96 = (ratio >> 32n) + ((ratio & remainderMask) === 0n ? 0n : 1n);
10114
+ return sqrtPriceX96;
10115
+ }
10116
+ const MIN_SQRT_PRICE_X96 = tickToSqrtPriceX96(MIN_TICK);
10117
+ const MAX_SQRT_PRICE_X96 = tickToSqrtPriceX96(MAX_TICK);
10118
+ function getPriceRatioFromSqrtPriceX96(sqrtPriceX96) {
10150
10119
  return {
10151
- poolId,
10152
- initialized
10120
+ numerator: sqrtPriceX96 * sqrtPriceX96,
10121
+ denominator: Q192$4
10153
10122
  };
10154
10123
  }
10155
-
10156
- //#endregion
10157
- //#region src/panoptic/v2/sfpmSwap/plan.ts
10158
- /** Wide, non-inverted band for the paired (non-swapping) call — never triggers a swap. */
10159
- const WIDE_LIMITS = [Number(MIN_TICK) + 1, Number(MAX_TICK) - 1];
10160
- /**
10161
- * Convert a slippage tolerance in bps to a conservative Uniswap tick distance.
10162
- *
10163
- * Ticks are 1.0001^tick, so each tick ≈ 1 bps. This walks up powers of 1.0001
10164
- * until the cumulative price move covers `slippageBps`, matching the hedger-bot's
10165
- * dispatch-path helper so both swap paths agree on band width.
10166
- */
10167
- function slippageBpsToTickDistance(slippageBps) {
10168
- if (slippageBps < 0n || slippageBps > 1000n) throw new PanopticError(`slippage bps ${slippageBps} out of bounds (0..1000)`);
10169
- if (slippageBps === 0n) return 0n;
10170
- let numerator = 1n;
10171
- let denominator = 1n;
10172
- let ticks = 0n;
10173
- while (numerator * 10000n < denominator * (10000n + slippageBps)) {
10174
- numerator *= 10001n;
10175
- denominator *= 10000n;
10176
- ticks += 1n;
10177
- }
10178
- return ticks;
10124
+ function getRawPriceRatio(tick) {
10125
+ return getPriceRatioFromSqrtPriceX96(tickToSqrtPriceX96(tick));
10126
+ }
10127
+ function adjustRatioForDecimals(numerator, denominator, decimals0, decimals1) {
10128
+ const diff = decimals0 - decimals1;
10129
+ if (diff === 0n) return {
10130
+ numerator,
10131
+ denominator
10132
+ };
10133
+ if (diff > 0n) return {
10134
+ numerator: numerator * pow10(diff),
10135
+ denominator
10136
+ };
10137
+ return {
10138
+ numerator,
10139
+ denominator: denominator * pow10(-diff)
10140
+ };
10179
10141
  }
10180
10142
  /**
10181
- * Build the swap plan.
10143
+ * Convert a tick to a raw price string (no decimal adjustment).
10144
+ * Uses the formula: price = 1.0001^tick
10182
10145
  *
10183
- * Mechanism (verified in the Phase 0 fork test):
10184
- * - A single-leg **loan** tokenId (width=0, isLong=false, `asset == tokenType`) moves
10185
- * exactly `positionSize` of the `tokenType` token when its call carries inverted
10186
- * tick limits (`low > high`); the paired call uses a wide band and moves nothing.
10187
- * - `exactIn`: swap on the **mint**; `tokenType` = the **input** token index.
10188
- * - `exactOut`: swap on the **burn** (isLong flips → exact-output); `tokenType` = the
10189
- * **output** token index.
10146
+ * This returns the raw price ratio, not adjusted for token decimals.
10147
+ * A fixed internal precision is used and trailing zeros are trimmed.
10190
10148
  *
10191
- * The inverted band is centered on `currentTick` at ±`slippageBpsToTickDistance`,
10192
- * which the SFPM re-sorts and enforces as an open interval on the post-swap tick.
10149
+ * @param tick - The tick value
10150
+ * @returns Price string
10151
+ *
10152
+ * @example
10153
+ * ```typescript
10154
+ * tickToPrice(0n) // "1"
10155
+ * tickToPrice(1000n) // "1.105..." (approximately)
10156
+ * tickToPrice(-1000n) // "0.904..." (approximately)
10157
+ * tickToPrice(200000n) // Very large number
10158
+ * ```
10193
10159
  */
10194
- function buildSfpmSwapPlan(params) {
10195
- const { sfpmAddress, poolAddress, poolId, kind, zeroForOne, amount, currentTick, slippageBps } = params;
10196
- if (amount <= 0n) throw new PanopticError(`swap amount must be positive (got ${amount})`);
10197
- const distance = slippageBpsToTickDistance(slippageBps);
10198
- if (distance < 1n) throw new PanopticError(`slippageBps ${slippageBps} yields a zero-width tick band; use a larger tolerance`);
10199
- const tokenType = kind === "exactIn" ? zeroForOne ? 0n : 1n : zeroForOne ? 1n : 0n;
10200
- const tokenId = createTokenIdBuilder(poolId).addLoan({
10201
- asset: tokenType,
10202
- tokenType,
10203
- strike: 0n
10204
- }).build();
10205
- const d = Number(distance);
10206
- const low = Math.min(Number(MAX_TICK), currentTick + d);
10207
- const high = Math.max(Number(MIN_TICK), currentTick - d);
10208
- if (low <= high) throw new PanopticError(`slippageBps ${slippageBps} at tick ${currentTick} yields a non-inverted band after clamping`);
10209
- const invertedLimits = [low, high];
10210
- const swapOn = kind === "exactIn" ? "mint" : "burn";
10211
- return {
10212
- sfpmAddress,
10213
- poolAddress,
10214
- poolKey: encodeAbiParameters([{ type: "address" }], [poolAddress]),
10215
- tokenId,
10216
- positionSize: amount,
10217
- mintTickLimits: swapOn === "mint" ? invertedLimits : WIDE_LIMITS,
10218
- burnTickLimits: swapOn === "burn" ? invertedLimits : WIDE_LIMITS,
10219
- swapOn,
10220
- kind
10221
- };
10222
- }
10223
-
10224
- //#endregion
10225
- //#region src/panoptic/v2/sfpmSwap/quote.ts
10226
- const UINT128 = 1n << 128n;
10227
- const INT128_MAX = (1n << 127n) - 1n;
10228
- /** Extract the two signed 128-bit slots from a packed `LeftRightSigned` int256. */
10229
- function unpackLeftRightSigned(packed) {
10230
- const u = packed < 0n ? packed + (1n << 256n) : packed;
10231
- const toInt128 = (half) => half > INT128_MAX ? half - UINT128 : half;
10232
- return {
10233
- right: toInt128(u & UINT128 - 1n),
10234
- left: toInt128(u >> 128n & UINT128 - 1n)
10235
- };
10236
- }
10237
- /** Wrap an unknown thrown value as a PanopticError, decoding Panoptic reverts when possible. */
10238
- function toPanopticError(err) {
10239
- if (err instanceof PanopticError) return err;
10240
- const parsed = parsePanopticError(err);
10241
- if (parsed) return parsed.error;
10242
- return new PanopticError(err instanceof Error ? err.message : "SFPM swap simulation failed", err instanceof Error ? err : void 0);
10160
+ function tickToPrice(tick) {
10161
+ const { numerator, denominator } = getRawPriceRatio(tick);
10162
+ const price = formatRatio$3(numerator, denominator, RAW_PRICE_PRECISION);
10163
+ return trimTrailingZeros(price);
10243
10164
  }
10244
10165
  /**
10245
- * Quote a swap by simulating `SFPM.multicall([mint, burn])` and decoding the swap
10246
- * call's `totalMoved` return. Authoritative — captures the width-0 loan-leg wei
10247
- * rounding a raw QuoterV2 quote would miss.
10166
+ * Convert a tick to a human-readable price with decimal scaling.
10167
+ * Uses the formula: price = 1.0001^tick * 10^(decimals0-decimals1)
10168
+ *
10169
+ * This adjusts for the different decimals of the two tokens in the pair.
10170
+ *
10171
+ * @param tick - The tick value
10172
+ * @param decimals0 - Decimals of token0
10173
+ * @param decimals1 - Decimals of token1
10174
+ * @param precision - Number of decimal places to display
10175
+ * @returns Formatted price string
10176
+ *
10177
+ * @example
10178
+ * ```typescript
10179
+ * // WETH/USDC pool (18 decimals / 6 decimals)
10180
+ * // At tick ~200000, price is roughly $2000 per ETH
10181
+ * tickToPriceDecimalScaled(200000n, 18n, 6n, 2n) // "2000.00" (approximately)
10182
+ *
10183
+ * // For token1/token0 price, swap the decimals
10184
+ * tickToPriceDecimalScaled(200000n, 6n, 18n, 6n) // "0.000500" (approximately)
10185
+ * ```
10248
10186
  */
10249
- async function quoteSfpmSwap(params) {
10250
- const { client, plan, account, stateOverride, blockNumber } = params;
10251
- const { mintData, burnData } = buildSfpmSwapCalldata(plan);
10252
- const _meta = await getBlockMeta({
10253
- client,
10254
- blockNumber
10255
- });
10256
- try {
10257
- const { result } = await client.simulateContract({
10258
- account,
10259
- address: plan.sfpmAddress,
10260
- abi: semiFungiblePositionManagerV3Abi,
10261
- functionName: "multicall",
10262
- args: [[mintData, burnData]],
10263
- blockNumber,
10264
- stateOverride
10265
- });
10266
- const swapIndex = plan.swapOn === "mint" ? 0 : 1;
10267
- const decoded = decodeFunctionResult({
10268
- abi: semiFungiblePositionManagerV3Abi,
10269
- functionName: plan.swapOn === "mint" ? "mintTokenizedPosition" : "burnTokenizedPosition",
10270
- data: result[swapIndex]
10271
- });
10272
- const finalTick = Number(decoded[2]);
10273
- const { right, left } = unpackLeftRightSigned(decoded[1]);
10274
- const inSlot = [right, left].find((s) => s > 0n);
10275
- const outSlot = [right, left].find((s) => s < 0n);
10276
- if (inSlot === void 0 || outSlot === void 0) return {
10277
- success: false,
10278
- error: new PanopticError(`SFPM swap simulation moved no tokens (totalMoved=${decoded[1]})`),
10279
- _meta
10280
- };
10281
- return {
10282
- success: true,
10283
- data: {
10284
- amountIn: inSlot,
10285
- amountOut: -outSlot,
10286
- finalTick
10287
- },
10288
- gasEstimate: 0n,
10289
- _meta
10290
- };
10291
- } catch (err) {
10292
- return {
10293
- success: false,
10294
- error: toPanopticError(err),
10295
- _meta
10296
- };
10297
- }
10187
+ function tickToPriceDecimalScaled(tick, decimals0, decimals1, precision) {
10188
+ const rawRatio = getRawPriceRatio(tick);
10189
+ const { numerator, denominator } = adjustRatioForDecimals(rawRatio.numerator, rawRatio.denominator, decimals0, decimals1);
10190
+ return formatRatio$3(numerator, denominator, precision);
10298
10191
  }
10299
-
10300
- //#endregion
10301
- //#region src/uniswap/v4/abis/universalRouter.ts
10302
10192
  /**
10303
- * Minimal Universal Router ABI (only the `execute` overload we use).
10304
- * @module uniswap/v4/abis/universalRouter
10193
+ * Convert a sqrtPriceX96 to a human-readable price with decimal scaling.
10194
+ *
10195
+ * Uses the formula: price = (sqrtPriceX96^2 / 2^192) * 10^(decimals0-decimals1)
10196
+ *
10197
+ * @param sqrtPriceX96 - The sqrt price in Q64.96 format
10198
+ * @param decimals0 - Decimals of token0
10199
+ * @param decimals1 - Decimals of token1
10200
+ * @param precision - Number of decimal places to display
10201
+ * @returns Formatted price string
10202
+ *
10203
+ * @example
10204
+ * ```typescript
10205
+ * sqrtPriceX96ToPriceDecimalScaled(2n ** 96n, 18n, 18n, 2n) // "1.00"
10206
+ * ```
10305
10207
  */
10306
- const universalRouterAbi = [{
10307
- type: "function",
10308
- name: "execute",
10309
- stateMutability: "payable",
10310
- inputs: [
10311
- {
10312
- name: "commands",
10313
- type: "bytes"
10314
- },
10315
- {
10316
- name: "inputs",
10317
- type: "bytes[]"
10318
- },
10319
- {
10320
- name: "deadline",
10321
- type: "uint256"
10322
- }
10323
- ],
10324
- outputs: []
10325
- }];
10326
-
10327
- //#endregion
10328
- //#region src/uniswap/v4/router/errors.ts
10208
+ function sqrtPriceX96ToPriceDecimalScaled(sqrtPriceX96, decimals0, decimals1, precision) {
10209
+ const rawRatio = getPriceRatioFromSqrtPriceX96(sqrtPriceX96);
10210
+ const { numerator, denominator } = adjustRatioForDecimals(rawRatio.numerator, rawRatio.denominator, decimals0, decimals1);
10211
+ return formatRatio$3(numerator, denominator, precision);
10212
+ }
10329
10213
  /**
10330
- * The chain has no configured Uniswap v4 addresses and none were supplied via
10331
- * overrides.
10214
+ * Convert a price to a tick value.
10215
+ *
10216
+ * @param price - The price string
10217
+ * @param decimals0 - Decimals of token0
10218
+ * @param decimals1 - Decimals of token1
10219
+ * @returns The tick value (rounded to nearest integer)
10220
+ *
10221
+ * @example
10222
+ * ```typescript
10223
+ * // WETH/USDC: What tick for $2000 per ETH?
10224
+ * priceToTick("2000", 18n, 6n) // ~200000n
10225
+ *
10226
+ * // Inverse: What tick for 0.0005 ETH per USDC?
10227
+ * priceToTick("0.0005", 6n, 18n) // ~200000n
10228
+ * ```
10332
10229
  */
10333
- var UnsupportedChainError = class extends PanopticError {
10334
- name = "UnsupportedChainError";
10335
- constructor(chainId, cause) {
10336
- super(`Uniswap v4 router not configured for chain ${chainId}`, cause);
10337
- this.chainId = chainId;
10230
+ function priceToTick(price, decimals0, decimals1) {
10231
+ const parsed = parseDecimalToFraction(price);
10232
+ if (parsed.numerator <= 0n) throw new Error("Price must be positive");
10233
+ let targetNumerator = parsed.numerator;
10234
+ let targetDenominator = parsed.denominator;
10235
+ const diff = decimals0 - decimals1;
10236
+ if (diff > 0n) targetDenominator *= pow10(diff);
10237
+ else if (diff < 0n) targetNumerator *= pow10(-diff);
10238
+ let low = MIN_TICK;
10239
+ let high = MAX_TICK;
10240
+ while (low <= high) {
10241
+ const mid = (low + high) / 2n;
10242
+ const { numerator, denominator } = getRawPriceRatio(mid);
10243
+ const cmp = compareRatios(numerator, denominator, targetNumerator, targetDenominator);
10244
+ if (cmp === 0) return mid;
10245
+ if (cmp < 0) low = mid + 1n;
10246
+ else high = mid - 1n;
10338
10247
  }
10339
- };
10248
+ const floorTick = high;
10249
+ const ceilTick = low;
10250
+ if (floorTick < MIN_TICK) return MIN_TICK;
10251
+ if (ceilTick > MAX_TICK) return MAX_TICK;
10252
+ const floorRatio = getRawPriceRatio(floorTick);
10253
+ const ceilRatio = getRawPriceRatio(ceilTick);
10254
+ const floorDiffNumerator = absBigint(targetNumerator * floorRatio.denominator - floorRatio.numerator * targetDenominator);
10255
+ const ceilDiffNumerator = absBigint(targetNumerator * ceilRatio.denominator - ceilRatio.numerator * targetDenominator);
10256
+ const floorDiffDenominator = targetDenominator * floorRatio.denominator;
10257
+ const ceilDiffDenominator = targetDenominator * ceilRatio.denominator;
10258
+ return floorDiffNumerator * ceilDiffDenominator <= ceilDiffNumerator * floorDiffDenominator ? floorTick : ceilTick;
10259
+ }
10340
10260
  /**
10341
- * `tokenIn` is neither `currency0` nor `currency1` of the resolved pool.
10261
+ * Convert a sqrtPriceX96 value to the nearest tick.
10262
+ *
10263
+ * @param sqrtPriceX96 - The sqrt price in Q64.96 format
10264
+ * @returns The tick value (rounded to nearest integer)
10265
+ *
10266
+ * @example
10267
+ * ```typescript
10268
+ * const tick = sqrtPriceX96ToTick(2n ** 96n) // 0n
10269
+ * ```
10342
10270
  */
10343
- var InvalidSwapTokenError = class extends PanopticError {
10344
- name = "InvalidSwapTokenError";
10345
- constructor(token, currency0, currency1, cause) {
10346
- super(`Token ${token} is not part of the pool (currency0=${currency0}, currency1=${currency1})`, cause);
10347
- this.token = token;
10348
- this.currency0 = currency0;
10349
- this.currency1 = currency1;
10350
- }
10351
- };
10352
- /**
10353
- * An amount exceeds the uint128 range required by the v4 swap encoding.
10354
- */
10355
- var AmountExceedsUint128Error = class extends PanopticError {
10356
- name = "AmountExceedsUint128Error";
10357
- constructor(amount, cause) {
10358
- super(`Amount ${amount} exceeds uint128 maximum`, cause);
10359
- this.amount = amount;
10360
- }
10361
- };
10362
- /**
10363
- * A native-ETH swap needs a trailing Universal Router SWEEP to deliver the ETH
10364
- * output (or refund the input overpay), but no `recipient` was supplied.
10365
- */
10366
- var MissingSweepRecipientError = class extends PanopticError {
10367
- name = "MissingSweepRecipientError";
10368
- constructor(cause) {
10369
- super("A recipient is required to sweep native ETH back to the user", cause);
10370
- }
10371
- };
10372
- /**
10373
- * The V4Quoter is not available for the chain (no fallback in v1).
10374
- */
10375
- var QuoterUnavailableError = class extends PanopticError {
10376
- name = "QuoterUnavailableError";
10377
- constructor(chainId, cause) {
10378
- super(`V4Quoter unavailable for chain ${chainId}`, cause);
10379
- this.chainId = chainId;
10271
+ function sqrtPriceX96ToTick(sqrtPriceX96) {
10272
+ if (sqrtPriceX96 <= 0n) throw new Error("Sqrt price must be positive");
10273
+ if (sqrtPriceX96 < MIN_SQRT_PRICE_X96 || sqrtPriceX96 > MAX_SQRT_PRICE_X96) throw new RangeError("Sqrt price out of bounds");
10274
+ let low = MIN_TICK;
10275
+ let high = MAX_TICK;
10276
+ while (low <= high) {
10277
+ const mid = (low + high) / 2n;
10278
+ const midSqrt = tickToSqrtPriceX96(mid);
10279
+ if (midSqrt === sqrtPriceX96) return mid;
10280
+ if (midSqrt < sqrtPriceX96) low = mid + 1n;
10281
+ else high = mid - 1n;
10380
10282
  }
10381
- };
10382
-
10383
- //#endregion
10384
- //#region src/uniswap/v3/router/encodeSwap.ts
10385
- const UINT128_MAX$4 = (1n << 128n) - 1n;
10386
-
10387
- //#endregion
10388
- //#region src/uniswap/v3/abis/quoterV2.ts
10283
+ const floorTick = high;
10284
+ const ceilTick = low;
10285
+ if (floorTick < MIN_TICK) return MIN_TICK;
10286
+ if (ceilTick > MAX_TICK) return MAX_TICK;
10287
+ const floorSqrt = tickToSqrtPriceX96(floorTick);
10288
+ const ceilSqrt = tickToSqrtPriceX96(ceilTick);
10289
+ const floorDiff = absBigint(sqrtPriceX96 - floorSqrt);
10290
+ const ceilDiff = absBigint(ceilSqrt - sqrtPriceX96);
10291
+ return floorDiff <= ceilDiff ? floorTick : ceilTick;
10292
+ }
10389
10293
  /**
10390
- * Minimal Uniswap v3 QuoterV2 ABI (`quoteExactInputSingle`).
10294
+ * Format a tick value for display.
10391
10295
  *
10392
- * QuoterV2 is revert/staticcall-based (nonpayable but intended for `eth_call` /
10393
- * `simulateContract`), so it must be simulated, not read.
10394
- * @module uniswap/v3/abis/quoterV2
10395
- */
10396
- const quoterV2Abi = [{
10397
- type: "function",
10398
- name: "quoteExactInputSingle",
10399
- stateMutability: "nonpayable",
10400
- inputs: [{
10401
- name: "params",
10402
- type: "tuple",
10403
- components: [
10404
- {
10405
- name: "tokenIn",
10406
- type: "address"
10407
- },
10408
- {
10409
- name: "tokenOut",
10410
- type: "address"
10411
- },
10412
- {
10413
- name: "amountIn",
10414
- type: "uint256"
10415
- },
10416
- {
10417
- name: "fee",
10418
- type: "uint24"
10419
- },
10420
- {
10421
- name: "sqrtPriceLimitX96",
10422
- type: "uint160"
10423
- }
10424
- ]
10425
- }],
10426
- outputs: [
10427
- {
10428
- name: "amountOut",
10429
- type: "uint256"
10430
- },
10431
- {
10432
- name: "sqrtPriceX96After",
10433
- type: "uint160"
10434
- },
10435
- {
10436
- name: "initializedTicksCrossed",
10437
- type: "uint32"
10438
- },
10439
- {
10440
- name: "gasEstimate",
10441
- type: "uint256"
10442
- }
10443
- ]
10444
- }];
10445
-
10446
- //#endregion
10447
- //#region src/uniswap/v3/addresses.ts
10448
- /**
10449
- * Verified Uniswap v3 QuoterV2 addresses keyed by chainId. Add a chain only
10450
- * after verifying against the canonical Uniswap v3 deployment listing.
10296
+ * @param tick - The tick value
10297
+ * @returns Formatted tick string
10298
+ *
10299
+ * @example
10300
+ * ```typescript
10301
+ * formatTick(200000n) // "200000"
10302
+ * formatTick(-50000n) // "-50000"
10303
+ * ```
10451
10304
  */
10452
- const UNISWAP_V3_ADDRESSES = { 1: {
10453
- quoterV2: "0x61fFE014bA17989E743c5F6cB21bF9697530B21e",
10454
- nonfungiblePositionManager: "0xC36442b4a4522E871399CD717aBDD847Ab11FE88"
10455
- } };
10305
+ function formatTick(tick) {
10306
+ return tick.toString();
10307
+ }
10456
10308
  /**
10457
- * Resolve Uniswap v3 addresses for a chain, applying optional overrides.
10309
+ * Get the price at a specific tick, returning both token0/token1 and token1/token0 prices.
10458
10310
  *
10459
- * @throws {UnsupportedChainError} when the chain is not listed and overrides do
10460
- * not supply every required address.
10311
+ * @param tick - The tick value
10312
+ * @param decimals0 - Decimals of token0
10313
+ * @param decimals1 - Decimals of token1
10314
+ * @param precision - Number of decimal places to display
10315
+ * @returns Object with both price directions
10316
+ *
10317
+ * @example
10318
+ * ```typescript
10319
+ * const prices = getPricesAtTick(200000n, 18n, 6n, 2n)
10320
+ * // prices.token0PerToken1 = "0.00" (very small)
10321
+ * // prices.token1PerToken0 = "2000.00" (USDC per ETH)
10322
+ * ```
10461
10323
  */
10462
- function getUniswapV3Addresses(chainId, overrides) {
10463
- const id = Number(chainId);
10464
- const base = UNISWAP_V3_ADDRESSES[id];
10465
- const merged = {
10466
- ...base,
10467
- ...overrides
10324
+ function getPricesAtTick(tick, decimals0, decimals1, precision) {
10325
+ const rawRatio = getRawPriceRatio(tick);
10326
+ const adjustedRatio = adjustRatioForDecimals(rawRatio.numerator, rawRatio.denominator, decimals0, decimals1);
10327
+ return {
10328
+ token0PerToken1: formatRatio$3(adjustedRatio.denominator, adjustedRatio.numerator, precision),
10329
+ token1PerToken0: formatRatio$3(adjustedRatio.numerator, adjustedRatio.denominator, precision)
10468
10330
  };
10469
- if (!merged.quoterV2 || !merged.nonfungiblePositionManager) throw new UnsupportedChainError(BigInt(id));
10470
- return merged;
10471
10331
  }
10472
-
10473
- //#endregion
10474
- //#region src/uniswap/v3/router/quote.ts
10475
- const BPS_DENOMINATOR$5 = 10000n;
10476
- const UINT128_MAX$3 = (1n << 128n) - 1n;
10477
10332
  /**
10478
- * Quote an exact-in v3 swap. Returns `null` when the quote reverts (e.g. the
10479
- * pool does not exist / has no liquidity) so callers can skip that pool when
10480
- * ranking venues, rather than aborting the whole cycle.
10333
+ * Format a tick range for display.
10334
+ *
10335
+ * @param tickLower - Lower tick
10336
+ * @param tickUpper - Upper tick
10337
+ * @returns Formatted tick range string
10338
+ *
10339
+ * @example
10340
+ * ```typescript
10341
+ * formatTickRange(-50000n, 200000n) // "-50000 - 200000"
10342
+ * ```
10481
10343
  */
10482
- async function quoteV3ExactIn(params) {
10483
- const { client, chainId, tokenIn, tokenOut, fee, amountIn, slippageBps, blockNumber } = params;
10484
- if (amountIn < 0n || amountIn > UINT128_MAX$3) throw new PanopticError(`amountIn ${amountIn} exceeds uint128 maximum`);
10485
- if (slippageBps < 0n || slippageBps > BPS_DENOMINATOR$5) throw new PanopticError(`invalid slippageBps ${slippageBps}, must be 0..10000`);
10486
- const { quoterV2 } = getUniswapV3Addresses(chainId, params.addresses);
10487
- try {
10488
- const { result } = await client.simulateContract({
10489
- address: quoterV2,
10490
- abi: quoterV2Abi,
10491
- functionName: "quoteExactInputSingle",
10492
- blockNumber,
10493
- args: [{
10494
- tokenIn,
10495
- tokenOut,
10496
- amountIn,
10497
- fee: Number(fee),
10498
- sqrtPriceLimitX96: 0n
10499
- }]
10500
- });
10501
- const [amountOut, , , gasEstimate] = result;
10502
- const amountOutMinimum = amountOut * (BPS_DENOMINATOR$5 - slippageBps) / BPS_DENOMINATOR$5;
10503
- return {
10504
- amountOut,
10505
- amountOutMinimum,
10506
- gasEstimate
10507
- };
10508
- } catch (err) {
10509
- if (isRevert(err)) return null;
10510
- throw err;
10511
- }
10512
- }
10513
- /** True only for genuine contract reverts (missing pool / no liquidity). */
10514
- function isRevert(err) {
10515
- return err instanceof BaseError && err.walk((e) => e instanceof ContractFunctionRevertedError) instanceof ContractFunctionRevertedError;
10344
+ function formatTickRange(tickLower, tickUpper) {
10345
+ return `${tickLower} - ${tickUpper}`;
10516
10346
  }
10517
-
10518
- //#endregion
10519
- //#region src/panoptic/v2/reads/sfpm.ts
10520
10347
  /**
10521
- * Resolve an SFPM poolId to its corresponding Uniswap V3 pool address.
10348
+ * Format a price range for display.
10522
10349
  *
10523
- * Calls `SemiFungiblePositionManagerV3.getUniswapV3PoolFromId(poolId)`.
10350
+ * @param tickLower - Lower tick
10351
+ * @param tickUpper - Upper tick
10352
+ * @param decimals0 - Decimals of token0
10353
+ * @param decimals1 - Decimals of token1
10354
+ * @param precision - Number of decimal places to display
10355
+ * @returns Formatted price range string
10356
+ *
10357
+ * @example
10358
+ * ```typescript
10359
+ * formatPriceRange(0n, 0n, 18n, 18n, 2n) // "1.00 - 1.00"
10360
+ * ```
10524
10361
  */
10525
- async function getUniswapV3PoolFromId(params) {
10526
- const { client, sfpmAddress, poolId } = params;
10527
- return client.readContract({
10528
- address: sfpmAddress,
10529
- abi: semiFungiblePositionManagerV3Abi,
10530
- functionName: "getUniswapV3PoolFromId",
10531
- args: [poolId]
10532
- });
10362
+ function formatPriceRange(tickLower, tickUpper, decimals0, decimals1, precision) {
10363
+ const lower = tickToPriceDecimalScaled(tickLower, decimals0, decimals1, precision);
10364
+ const upper = tickToPriceDecimalScaled(tickUpper, decimals0, decimals1, precision);
10365
+ return `${lower} - ${upper}`;
10533
10366
  }
10534
10367
  /**
10535
- * Resolve an SFPM poolId to its corresponding Uniswap V4 pool key.
10368
+ * Calculate the tick spacing for a given fee tier.
10536
10369
  *
10537
- * Calls `SemiFungiblePositionManagerV4.getUniswapV4PoolKeyFromId(poolId)`.
10538
- */
10539
- async function getUniswapV4PoolKeyFromId(params) {
10540
- const { client, sfpmAddress, poolId } = params;
10541
- const raw = await client.readContract({
10542
- address: sfpmAddress,
10543
- abi: semiFungiblePositionManagerV4Abi,
10544
- functionName: "getUniswapV4PoolKeyFromId",
10545
- args: [poolId]
10546
- });
10547
- return {
10548
- currency0: raw.currency0,
10549
- currency1: raw.currency1,
10550
- fee: BigInt(raw.fee),
10551
- tickSpacing: BigInt(raw.tickSpacing),
10552
- hooks: raw.hooks
10553
- };
10370
+ * @param feeBps - Fee in basis points (e.g., 500n for 0.05%)
10371
+ * @returns Tick spacing
10372
+ *
10373
+ * @example
10374
+ * ```typescript
10375
+ * getTickSpacing(100n) // 1n (0.01% fee tier)
10376
+ * getTickSpacing(500n) // 10n (0.05% fee tier)
10377
+ * getTickSpacing(3000n) // 60n (0.30% fee tier)
10378
+ * getTickSpacing(10000n) // 200n (1.00% fee tier)
10379
+ * ```
10380
+ */
10381
+ function getTickSpacing(feeBps) {
10382
+ switch (feeBps) {
10383
+ case 100n: return 1n;
10384
+ case 500n: return 10n;
10385
+ case 3000n: return 60n;
10386
+ case 10000n: return 200n;
10387
+ default: {
10388
+ const spacing = feeBps / 50n;
10389
+ return spacing > 1n ? spacing : 1n;
10390
+ }
10391
+ }
10554
10392
  }
10555
10393
  /**
10556
- * Get the enforced tick limits for a pool from the SFPM.
10394
+ * Round a tick to the nearest valid tick for a given tick spacing.
10557
10395
  *
10558
- * @param params - The parameters
10559
- * @returns The min and max enforced ticks
10396
+ * @param tick - The tick to round
10397
+ * @param tickSpacing - The tick spacing
10398
+ * @returns Rounded tick
10399
+ *
10400
+ * @example
10401
+ * ```typescript
10402
+ * roundToTickSpacing(12345n, 10n) // 12340n
10403
+ * roundToTickSpacing(12345n, 60n) // 12360n
10404
+ * roundToTickSpacing(-12345n, 10n) // -12350n
10405
+ * ```
10560
10406
  */
10561
- async function getEnforcedTickLimits(params) {
10562
- const { client, sfpmAddress, poolId } = params;
10563
- const [minTick, maxTick] = await client.readContract({
10564
- address: sfpmAddress,
10565
- abi: semiFungiblePositionManagerV4Abi,
10566
- functionName: "getEnforcedTickLimits",
10567
- args: [poolId]
10568
- });
10569
- return {
10570
- minEnforcedTick: minTick,
10571
- maxEnforcedTick: maxTick
10572
- };
10407
+ function roundToTickSpacing(tick, tickSpacing) {
10408
+ const remainder = tick % tickSpacing;
10409
+ if (remainder === 0n) return tick;
10410
+ if (tick >= 0n) return remainder >= tickSpacing / 2n ? tick + (tickSpacing - remainder) : tick - remainder;
10411
+ const absRemainder = -remainder;
10412
+ return absRemainder >= tickSpacing / 2n ? tick - (tickSpacing - absRemainder) : tick + absRemainder;
10573
10413
  }
10574
10414
  /**
10575
- * Fetch liquidity breakdown for a batch of chunks via SFPM.getAccountLiquidity().
10415
+ * Compute slippage-bounded tick limits around the current tick.
10576
10416
  *
10577
- * Uses multicall for efficiency. Returns one result per input chunk,
10578
- * along with block metadata for freshness tracking.
10417
+ * 1 tick ≈ 1 basis point (0.01 %) of price change, so a `toleranceBps`
10418
+ * of 500 allows roughly 5 % price movement. The result is clamped to
10419
+ * the protocol's `[MIN_TICK, MAX_TICK]` range.
10420
+ *
10421
+ * Useful for setting `tickLimitLow` / `tickLimitHigh` on `openPosition`
10422
+ * and `closePosition` to protect against MEV sandwiches and volatile
10423
+ * tick moves.
10424
+ *
10425
+ * @param currentTick - The current pool tick (must be within [MIN_TICK, MAX_TICK]).
10426
+ * @param toleranceBps - Slippage tolerance in basis points (≈ ticks). Must be non-negative.
10427
+ * @returns Clamped `{ low, high }` tick limits.
10428
+ * @throws {RangeError} If `toleranceBps` is negative or `currentTick` is out of bounds.
10429
+ *
10430
+ * @example
10431
+ * ```typescript
10432
+ * const { low, high } = tickLimits(200_000n, 500n)
10433
+ * // low = 199_500n
10434
+ * // high = 200_500n
10435
+ *
10436
+ * await openPosition({ ..., tickLimitLow: low, tickLimitHigh: high })
10437
+ * ```
10579
10438
  */
10580
- async function getChunkLiquidities(params) {
10581
- const { client, sfpmAddress, poolKeyBytes, chunks } = params;
10582
- if (chunks.length > MAX_TRACKED_CHUNKS) throw new ChunkLimitError(BigInt(chunks.length), 0n);
10583
- const _meta = params._meta ?? await getBlockMeta({ client });
10584
- if (chunks.length === 0) return {
10585
- results: [],
10586
- _meta
10587
- };
10588
- const multicallResults = await client.multicall({
10589
- contracts: chunks.map((chunk) => ({
10590
- address: sfpmAddress,
10591
- abi: semiFungiblePositionManagerV4Abi,
10592
- functionName: "getAccountLiquidity",
10593
- args: [
10594
- poolKeyBytes,
10595
- chunk.owner,
10596
- chunk.tokenType,
10597
- Number(chunk.tickLower),
10598
- Number(chunk.tickUpper)
10599
- ]
10600
- })),
10601
- allowFailure: true
10602
- });
10603
- const results = multicallResults.map((result) => {
10604
- if (result.status === "failure") return {
10605
- netLiquidity: 0n,
10606
- removedLiquidity: 0n,
10607
- totalLiquidity: 0n,
10608
- shortLiquidity: 0n,
10609
- longLiquidity: 0n
10610
- };
10611
- const packed = result.result;
10612
- const netLiquidity = packed & (1n << 128n) - 1n;
10613
- const removedLiquidity = packed >> 128n;
10614
- const totalLiquidity = netLiquidity + removedLiquidity;
10615
- return {
10616
- netLiquidity,
10617
- removedLiquidity,
10618
- totalLiquidity,
10619
- shortLiquidity: totalLiquidity,
10620
- longLiquidity: removedLiquidity
10621
- };
10622
- });
10439
+ function tickLimits(currentTick, toleranceBps) {
10440
+ if (toleranceBps < 0n) throw new RangeError(`toleranceBps must be non-negative, got ${toleranceBps}`);
10441
+ if (currentTick < MIN_TICK || currentTick > MAX_TICK) throw new RangeError(`currentTick ${currentTick} is out of bounds [${MIN_TICK}, ${MAX_TICK}]`);
10442
+ const rawLow = currentTick - toleranceBps;
10443
+ const rawHigh = currentTick + toleranceBps;
10444
+ const low = rawLow < MIN_TICK ? MIN_TICK : rawLow;
10445
+ const high = rawHigh > MAX_TICK ? MAX_TICK : rawHigh;
10446
+ if (low > high) throw new RangeError(`Computed tick limits are inverted: low ${low} > high ${high} (currentTick=${currentTick}, toleranceBps=${toleranceBps})`);
10623
10447
  return {
10624
- results,
10625
- _meta
10448
+ low,
10449
+ high
10626
10450
  };
10627
10451
  }
10628
10452
 
10629
10453
  //#endregion
10630
- //#region src/panoptic/v2/reads/factory.ts
10454
+ //#region src/panoptic/v2/tokenId/deriveUniqueTokenId.ts
10455
+ const POOL_ID_MASK$2 = (1n << 64n) - 1n;
10456
+ const MAX_LEGS = TOKEN_ID_BITS.MAX_LEGS;
10457
+ const MAX_OPTION_RATIO = LEG_LIMITS.MAX_RATIO;
10458
+ const Q192$3 = 1n << 192n;
10631
10459
  /**
10632
- * Get the token URI from a PanopticFactory NFT.
10460
+ * Floor integer square root for bigints (Newton's method). Mirrors the isqrt
10461
+ * used in reads/collateralEstimate.ts (buildNeutralLeg).
10633
10462
  */
10634
- async function getFactoryTokenURI(params) {
10635
- const { client, factoryAddress, version, tokenId } = params;
10636
- const abi = version === "v3" ? panopticFactoryV3Abi : panopticFactoryV4Abi;
10637
- return client.readContract({
10638
- address: factoryAddress,
10639
- abi,
10640
- functionName: "tokenURI",
10641
- args: [tokenId]
10642
- });
10463
+ function isqrt$1(value) {
10464
+ if (value < 0n) throw new PanopticError("isqrt of negative number");
10465
+ if (value < 2n) return value;
10466
+ let x = value;
10467
+ let y = x + 1n >> 1n;
10468
+ while (y < x) {
10469
+ x = y;
10470
+ y = x + value / x >> 1n;
10471
+ }
10472
+ return x;
10643
10473
  }
10644
- /**
10645
- * Get the owner of a PanopticFactory NFT.
10646
- */
10647
- async function getFactoryOwnerOf(params) {
10648
- const { client, factoryAddress, version, tokenId } = params;
10649
- const abi = version === "v3" ? panopticFactoryV3Abi : panopticFactoryV4Abi;
10650
- return client.readContract({
10651
- address: factoryAddress,
10652
- abi,
10653
- functionName: "ownerOf",
10654
- args: [tokenId]
10474
+ const DEFAULT_TICK_SPACING = 200n;
10475
+ /**
10476
+ * Target notional (in wei of the tokenType-side asset) for the appended
10477
+ * tiny credit leg. Chosen at 10 wei — small enough to be economically
10478
+ * meaningless against any real position size, large enough to safely
10479
+ * clear rounding on the SFPM's width-2 internal chunk math without any
10480
+ * risk of underflowing to zero (which would revert ChunkHasZeroLiquidity).
10481
+ */
10482
+ const TINY_CREDIT_TARGET_NOTIONAL_WEI = 10n;
10483
+ /**
10484
+ * Pick a signedStrike for the appended width=0 credit leg such that:
10485
+ * 1. positionSize · 1.0001^signedStrike ≈ TINY_CREDIT_TARGET_NOTIONAL_WEI
10486
+ * (~10 wei — economically meaningless but comfortably above any
10487
+ * SFPM width-2 chunk rounding);
10488
+ * 2. the leg's tick range (SFPM internally treats width=0 as width=2, so
10489
+ * the range is `strike ± tickSpacing`) stays strictly inside
10490
+ * `[MIN_POOL_TICK, MAX_POOL_TICK]` — otherwise the getSqrtRatioAtTick
10491
+ * call reverts with `InvalidTick`.
10492
+ *
10493
+ * Closed form: `1.0001^signedStrike = target/positionSize`, so
10494
+ * `sqrtKrawX96 = isqrt(target · 2^192 / positionSize)` and
10495
+ * `signedStrike = sqrtPriceX96ToTick(sqrtKrawX96)`. If the notional-optimal
10496
+ * strike falls outside the safe range, we clamp inward. Clamping raises the
10497
+ * notional but keeps it << position size for any reasonable strike.
10498
+ */
10499
+ function computeTinyCreditSignedStrike(positionSize, tickSpacing) {
10500
+ if (positionSize <= 0n) throw new PanopticError("computeTinyCreditSignedStrike: positionSize must be > 0");
10501
+ const minSafe = MIN_TICK + tickSpacing + 1n;
10502
+ const maxSafe = MAX_TICK - tickSpacing - 1n;
10503
+ let signedStrike;
10504
+ try {
10505
+ const sqrtKrawX96 = isqrt$1(TINY_CREDIT_TARGET_NOTIONAL_WEI * Q192$3 / positionSize);
10506
+ signedStrike = sqrtPriceX96ToTick(sqrtKrawX96);
10507
+ } catch {
10508
+ signedStrike = minSafe;
10509
+ }
10510
+ if (signedStrike < minSafe) return minSafe;
10511
+ if (signedStrike > maxSafe) return maxSafe;
10512
+ return signedStrike;
10513
+ }
10514
+ /**
10515
+ * Assemble the tiny credit leg struct, picking a strike that avoids
10516
+ * colliding with any existing width=0 credit leg on the same (asset,
10517
+ * tokenType) pair.
10518
+ *
10519
+ * The encoded strike stored in the tokenId is
10520
+ * `asset === 0 ? signedStrike : -signedStrike` (mirrors
10521
+ * {@link buildNeutralLeg} in reads/collateralEstimate.ts).
10522
+ */
10523
+ function pickUniqueTinyCreditLeg(baseTokenId, legIndex, positionSize, tickSpacing) {
10524
+ const asset = 1n;
10525
+ const tokenType = 0n;
10526
+ const existingLegs = decodeAllLegs(baseTokenId);
10527
+ const maxSafe = MAX_TICK - tickSpacing - 1n;
10528
+ let signedStrike = computeTinyCreditSignedStrike(positionSize, tickSpacing);
10529
+ while (signedStrike <= maxSafe) {
10530
+ const candidate = asset === 0n ? signedStrike : -signedStrike;
10531
+ let collides = false;
10532
+ for (const leg of existingLegs) if (leg.width === 0n && leg.isLong && leg.asset === asset && leg.tokenType === tokenType && leg.strike === candidate) {
10533
+ collides = true;
10534
+ break;
10535
+ }
10536
+ if (!collides) break;
10537
+ signedStrike += 1n;
10538
+ }
10539
+ if (signedStrike > maxSafe) throw new PanopticError("deriveUniqueTokenId: exhausted strike space picking a unique tiny credit leg");
10540
+ const encodedStrike = asset === 0n ? signedStrike : -signedStrike;
10541
+ return {
10542
+ index: legIndex,
10543
+ asset,
10544
+ tokenType,
10545
+ optionRatio: 1n,
10546
+ isLong: 1n,
10547
+ riskPartner: legIndex,
10548
+ strike: encodedStrike,
10549
+ width: 0n
10550
+ };
10551
+ }
10552
+ function appendTinyCreditLeg(baseTokenId, baseLegCount, positionSize, tickSpacing) {
10553
+ const newLeg = pickUniqueTinyCreditLeg(baseTokenId, baseLegCount, positionSize, tickSpacing);
10554
+ return addLegToTokenId(baseTokenId, newLeg);
10555
+ }
10556
+ function scaleRatios(baseTokenId, targetPositionSize) {
10557
+ const legs = decodeAllLegs(baseTokenId);
10558
+ const maxRatio = legs.reduce((m, leg) => leg.optionRatio > m ? leg.optionRatio : m, 0n);
10559
+ const N = MAX_OPTION_RATIO / maxRatio;
10560
+ if (N < 2n) throw new PanopticError("deriveUniqueTokenId: cannot derive a unique tokenId — all 4 leg slots used and optionRatios already near the 127 ceiling");
10561
+ const newPositionSize = (targetPositionSize + N - 1n) / N;
10562
+ const poolId = baseTokenId & POOL_ID_MASK$2;
10563
+ let out = poolId;
10564
+ for (const leg of legs) out = addLegToTokenId(out, {
10565
+ index: leg.index,
10566
+ asset: leg.asset,
10567
+ tokenType: leg.tokenType,
10568
+ optionRatio: leg.optionRatio * N,
10569
+ isLong: leg.isLong ? 1n : 0n,
10570
+ riskPartner: leg.riskPartner,
10571
+ strike: leg.strike,
10572
+ width: leg.width
10655
10573
  });
10574
+ return {
10575
+ newTokenId: out,
10576
+ newPositionSize,
10577
+ effectivePositionSize: newPositionSize * N,
10578
+ strategy: "ratio-scale"
10579
+ };
10656
10580
  }
10657
10581
  /**
10658
- * Construct NFT metadata for a pool via the factory contract.
10582
+ * Derive a tokenId unique from `baseTokenId` for a partial reduction.
10583
+ *
10584
+ * Prefers a tiny-credit-leg extension (arbitrary new size). Falls back to
10585
+ * optionRatio scaling only when the base tokenId already occupies all 4 leg
10586
+ * slots.
10659
10587
  */
10660
- async function getFactoryConstructMetadata(params) {
10661
- const { client, factoryAddress, version, panopticPoolAddress, symbol0, symbol1, fee } = params;
10662
- const abi = version === "v3" ? panopticFactoryV3Abi : panopticFactoryV4Abi;
10663
- return client.readContract({
10664
- address: factoryAddress,
10665
- abi,
10666
- functionName: "constructMetadata",
10667
- args: [
10668
- panopticPoolAddress,
10669
- symbol0,
10670
- symbol1,
10671
- fee
10672
- ]
10673
- });
10588
+ function deriveUniqueTokenId(params) {
10589
+ const { baseTokenId, targetPositionSize, tickSpacing = DEFAULT_TICK_SPACING } = params;
10590
+ if (targetPositionSize <= 0n) throw new PanopticError("deriveUniqueTokenId: targetPositionSize must be > 0");
10591
+ const legCount = countLegs(baseTokenId);
10592
+ if (legCount === 0n) throw new PanopticError("deriveUniqueTokenId: baseTokenId has no legs");
10593
+ if (legCount < MAX_LEGS) {
10594
+ const newTokenId = appendTinyCreditLeg(baseTokenId, legCount, targetPositionSize, tickSpacing);
10595
+ return {
10596
+ newTokenId,
10597
+ newPositionSize: targetPositionSize,
10598
+ effectivePositionSize: targetPositionSize,
10599
+ strategy: "tiny-credit"
10600
+ };
10601
+ }
10602
+ return scaleRatios(baseTokenId, targetPositionSize);
10674
10603
  }
10675
10604
  /**
10676
- * Get the PanopticPool address for a given pool and risk engine.
10605
+ * Re-export decoded leg count so callers can gate UI on the strategy that
10606
+ * would be chosen (e.g. show a divisibility hint on ratio-scale positions).
10677
10607
  */
10678
- async function getPanopticPoolAddress(params) {
10679
- const { client, factoryAddress, riskEngine } = params;
10680
- if (params.version === "v3") return client.readContract({
10681
- address: factoryAddress,
10682
- abi: panopticFactoryV3Abi,
10683
- functionName: "getPanopticPool",
10684
- args: [params.univ3pool, riskEngine]
10685
- });
10686
- return client.readContract({
10687
- address: factoryAddress,
10688
- abi: panopticFactoryV4Abi,
10689
- functionName: "getPanopticPool",
10690
- args: [{
10691
- currency0: params.poolKey.currency0,
10692
- currency1: params.poolKey.currency1,
10693
- fee: Number(params.poolKey.fee),
10694
- tickSpacing: Number(params.poolKey.tickSpacing),
10695
- hooks: params.poolKey.hooks
10696
- }, riskEngine]
10697
- });
10608
+ function planDeriveStrategy(baseTokenId) {
10609
+ return countLegs(baseTokenId) < MAX_LEGS ? "tiny-credit" : "ratio-scale";
10698
10610
  }
10611
+
10612
+ //#endregion
10613
+ //#region src/panoptic/v2/sfpmSwap/calldata.ts
10699
10614
  /**
10700
- * Mine for an optimal pool address salt with high rarity.
10615
+ * Encode the `multicall([mint, burn])` for a swap plan.
10616
+ *
10617
+ * The order is always `[mint, burn]` — the ERC1155 must be minted before it is
10618
+ * burned. Which call carries the inverted (swap) limits is decided in the plan.
10701
10619
  */
10702
- async function minePoolAddress(params) {
10703
- const { client, factoryAddress, deployerAddress, riskEngine, salt, loops, minTargetRarity } = params;
10704
- let result;
10705
- if (params.version === "v3") result = await client.readContract({
10706
- address: factoryAddress,
10707
- abi: panopticFactoryV3Abi,
10708
- functionName: "minePoolAddress",
10620
+ function buildSfpmSwapCalldata(plan) {
10621
+ const mintData = encodeFunctionData({
10622
+ abi: semiFungiblePositionManagerV3Abi,
10623
+ functionName: "mintTokenizedPosition",
10709
10624
  args: [
10710
- deployerAddress,
10711
- params.v3Pool,
10712
- riskEngine,
10713
- salt,
10714
- loops,
10715
- minTargetRarity
10625
+ plan.poolKey,
10626
+ plan.tokenId,
10627
+ plan.positionSize,
10628
+ plan.mintTickLimits[0],
10629
+ plan.mintTickLimits[1]
10716
10630
  ]
10717
10631
  });
10718
- else result = await client.readContract({
10719
- address: factoryAddress,
10720
- abi: panopticFactoryV4Abi,
10721
- functionName: "minePoolAddress",
10632
+ const burnData = encodeFunctionData({
10633
+ abi: semiFungiblePositionManagerV3Abi,
10634
+ functionName: "burnTokenizedPosition",
10722
10635
  args: [
10723
- deployerAddress,
10724
- {
10725
- currency0: params.poolKey.currency0,
10726
- currency1: params.poolKey.currency1,
10727
- fee: Number(params.poolKey.fee),
10728
- tickSpacing: Number(params.poolKey.tickSpacing),
10729
- hooks: params.poolKey.hooks
10730
- },
10731
- riskEngine,
10732
- salt,
10733
- loops,
10734
- minTargetRarity
10636
+ plan.poolKey,
10637
+ plan.tokenId,
10638
+ plan.positionSize,
10639
+ plan.burnTickLimits[0],
10640
+ plan.burnTickLimits[1]
10735
10641
  ]
10736
10642
  });
10643
+ const multicallData = encodeFunctionData({
10644
+ abi: semiFungiblePositionManagerV3Abi,
10645
+ functionName: "multicall",
10646
+ args: [[mintData, burnData]]
10647
+ });
10737
10648
  return {
10738
- bestSalt: BigInt(result[0]),
10739
- highestRarity: result[1]
10649
+ multicallData,
10650
+ mintData,
10651
+ burnData
10740
10652
  };
10741
10653
  }
10654
+
10655
+ //#endregion
10656
+ //#region src/panoptic/v2/sfpmSwap/init.ts
10742
10657
  /**
10743
- * Simulate a pool deployment to get the predicted pool address.
10658
+ * Resolve the `uint64` SFPM poolId for a Uniswap v3 pool, initializing it if needed.
10744
10659
  *
10745
- * Uses `simulateContract` on `deployNewPool` — the return value is the new pool address
10746
- * without actually executing the transaction.
10660
+ * `initializeAMMPool` is permissionless and idempotent and **returns the poolId**
10661
+ * whether or not the pool was already registered, so a `simulateContract` call is
10662
+ * enough to learn the id without sending a transaction. Always resolve the id this
10663
+ * way rather than encoding it offline — the SFPM can collision-increment ids.
10747
10664
  */
10748
- async function simulateDeployNewPool(params) {
10749
- const { client, factoryAddress, account, riskEngine, salt } = params;
10750
- if (params.version === "v3") {
10751
- const { result: result$1 } = await client.simulateContract({
10752
- address: factoryAddress,
10753
- abi: panopticFactoryV3Abi,
10754
- functionName: "deployNewPool",
10755
- args: [
10756
- params.token0,
10757
- params.token1,
10758
- Number(params.fee),
10759
- riskEngine,
10760
- salt
10761
- ],
10762
- account
10763
- });
10764
- return result$1;
10765
- }
10665
+ async function fetchSfpmV3PoolId(params) {
10666
+ const { client, sfpmAddress, token0, token1, fee } = params;
10667
+ const vegoid$1 = params.vegoid ?? Number(DEFAULT_VEGOID);
10766
10668
  const { result } = await client.simulateContract({
10767
- address: factoryAddress,
10768
- abi: panopticFactoryV4Abi,
10769
- functionName: "deployNewPool",
10669
+ address: sfpmAddress,
10670
+ abi: semiFungiblePositionManagerV3Abi,
10671
+ functionName: "initializeAMMPool",
10770
10672
  args: [
10771
- {
10772
- currency0: params.poolKey.currency0,
10773
- currency1: params.poolKey.currency1,
10774
- fee: Number(params.poolKey.fee),
10775
- tickSpacing: Number(params.poolKey.tickSpacing),
10776
- hooks: params.poolKey.hooks
10777
- },
10778
- riskEngine,
10779
- salt
10780
- ],
10781
- account
10673
+ token0,
10674
+ token1,
10675
+ fee,
10676
+ vegoid$1
10677
+ ]
10782
10678
  });
10783
- return result;
10679
+ return BigInt(result);
10784
10680
  }
10785
10681
  /**
10786
- * Resolve an SFPM poolId to its PanopticPool address.
10682
+ * Ensure a Uniswap v3 pool is registered on the SFPM, sending `initializeAMMPool`
10683
+ * only if it is not already registered. Returns the resolved poolId.
10787
10684
  *
10788
- * Chains two on-chain lookups:
10789
- * 1. SFPM: poolId → Uniswap pool address (V3) or pool key (V4)
10790
- * 2. Factory: Uniswap pool + riskEngine → PanopticPool address
10685
+ * When `expectedPool` is given, the resolved id is checked to map back to it via
10686
+ * `getUniswapV3PoolFromId` — guards against a wrong token/fee triple.
10791
10687
  */
10792
- async function getPanopticPoolFromPoolId(params) {
10793
- const { client, sfpmAddress, factoryAddress, riskEngine, poolId, version } = params;
10794
- if (version === "v3") {
10795
- const univ3pool = await getUniswapV3PoolFromId({
10796
- client,
10797
- sfpmAddress,
10798
- poolId
10799
- });
10800
- return getPanopticPoolAddress({
10801
- version: "v3",
10802
- client,
10803
- factoryAddress,
10804
- univ3pool,
10805
- riskEngine
10806
- });
10807
- }
10808
- const poolKey = await getUniswapV4PoolKeyFromId({
10688
+ async function ensureSfpmV3PoolInitialized(params) {
10689
+ const { client, wallet, sfpmAddress, token0, token1, fee, expectedPool } = params;
10690
+ const vegoid$1 = params.vegoid ?? Number(DEFAULT_VEGOID);
10691
+ const poolId = await fetchSfpmV3PoolId({
10809
10692
  client,
10810
10693
  sfpmAddress,
10811
- poolId
10694
+ token0,
10695
+ token1,
10696
+ fee,
10697
+ vegoid: vegoid$1
10812
10698
  });
10813
- return getPanopticPoolAddress({
10814
- version: "v4",
10815
- client,
10816
- factoryAddress,
10817
- poolKey,
10818
- riskEngine
10699
+ const registered = await client.readContract({
10700
+ address: sfpmAddress,
10701
+ abi: semiFungiblePositionManagerV3Abi,
10702
+ functionName: "getUniswapV3PoolFromId",
10703
+ args: [poolId]
10819
10704
  });
10705
+ let initialized = false;
10706
+ let resolved = registered;
10707
+ if (getAddress(registered) === zeroAddress) {
10708
+ const account = wallet.account;
10709
+ if (account === void 0) throw new PanopticError("wallet client has no account");
10710
+ const hash = await wallet.writeContract({
10711
+ account,
10712
+ chain: wallet.chain ?? null,
10713
+ address: sfpmAddress,
10714
+ abi: semiFungiblePositionManagerV3Abi,
10715
+ functionName: "initializeAMMPool",
10716
+ args: [
10717
+ token0,
10718
+ token1,
10719
+ fee,
10720
+ vegoid$1
10721
+ ]
10722
+ });
10723
+ await client.waitForTransactionReceipt({ hash });
10724
+ initialized = true;
10725
+ resolved = await client.readContract({
10726
+ address: sfpmAddress,
10727
+ abi: semiFungiblePositionManagerV3Abi,
10728
+ functionName: "getUniswapV3PoolFromId",
10729
+ args: [poolId]
10730
+ });
10731
+ }
10732
+ if (expectedPool !== void 0 && getAddress(resolved) !== getAddress(expectedPool)) throw new WrongUniswapPoolError(new PanopticError(`SFPM poolId ${poolId} resolves to ${resolved}, expected ${expectedPool}`));
10733
+ return {
10734
+ poolId,
10735
+ initialized
10736
+ };
10820
10737
  }
10738
+
10739
+ //#endregion
10740
+ //#region src/panoptic/v2/sfpmSwap/plan.ts
10741
+ /** Wide, non-inverted band for the paired (non-swapping) call — never triggers a swap. */
10742
+ const WIDE_LIMITS = [Number(MIN_TICK) + 1, Number(MAX_TICK) - 1];
10821
10743
  /**
10822
- * Resolve an SFPM poolId to its PanopticPool address without knowing the version.
10744
+ * Convert a slippage tolerance in bps to a conservative Uniswap tick distance.
10823
10745
  *
10824
- * Tries both V3 and V4 lookups in parallel. The factory returns `address(0)` for
10825
- * non-existent pools, so the non-zero result identifies the correct version.
10746
+ * Ticks are 1.0001^tick, so each tick ≈ 1 bps. This walks up powers of 1.0001
10747
+ * until the cumulative price move covers `slippageBps`, matching the hedger-bot's
10748
+ * dispatch-path helper so both swap paths agree on band width.
10749
+ */
10750
+ function slippageBpsToTickDistance(slippageBps) {
10751
+ if (slippageBps < 0n || slippageBps > 1000n) throw new PanopticError(`slippage bps ${slippageBps} out of bounds (0..1000)`);
10752
+ if (slippageBps === 0n) return 0n;
10753
+ let numerator = 1n;
10754
+ let denominator = 1n;
10755
+ let ticks = 0n;
10756
+ while (numerator * 10000n < denominator * (10000n + slippageBps)) {
10757
+ numerator *= 10001n;
10758
+ denominator *= 10000n;
10759
+ ticks += 1n;
10760
+ }
10761
+ return ticks;
10762
+ }
10763
+ /**
10764
+ * Build the swap plan.
10826
10765
  *
10827
- * At least one of `v3` or `v4` must be provided.
10766
+ * Mechanism (verified in the Phase 0 fork test):
10767
+ * - A single-leg **loan** tokenId (width=0, isLong=false, `asset == tokenType`) moves
10768
+ * exactly `positionSize` of the `tokenType` token when its call carries inverted
10769
+ * tick limits (`low > high`); the paired call uses a wide band and moves nothing.
10770
+ * - `exactIn`: swap on the **mint**; `tokenType` = the **input** token index.
10771
+ * - `exactOut`: swap on the **burn** (isLong flips → exact-output); `tokenType` = the
10772
+ * **output** token index.
10828
10773
  *
10829
- * @throws {PanopticValidationError} If no version config is provided or neither resolves.
10774
+ * The inverted band is centered on `currentTick` at ±`slippageBpsToTickDistance`,
10775
+ * which the SFPM re-sorts and enforces as an open interval on the post-swap tick.
10830
10776
  */
10831
- async function resolvePanopticPoolFromPoolId(params) {
10832
- const { client, poolId, riskEngine, v3, v4 } = params;
10833
- if (!v3 && !v4) throw new Error("At least one of v3 or v4 must be provided");
10834
- const isNotFoundError = (err) => {
10835
- if (typeof err === "object" && err !== null && "name" in err) {
10836
- const name = err.name;
10837
- return name === "ContractFunctionExecutionError" || name === "ContractFunctionRevertedError";
10838
- }
10839
- return false;
10777
+ function buildSfpmSwapPlan(params) {
10778
+ const { sfpmAddress, poolAddress, poolId, kind, zeroForOne, amount, currentTick, slippageBps } = params;
10779
+ if (amount <= 0n) throw new PanopticError(`swap amount must be positive (got ${amount})`);
10780
+ const distance = slippageBpsToTickDistance(slippageBps);
10781
+ if (distance < 1n) throw new PanopticError(`slippageBps ${slippageBps} yields a zero-width tick band; use a larger tolerance`);
10782
+ const tokenType = kind === "exactIn" ? zeroForOne ? 0n : 1n : zeroForOne ? 1n : 0n;
10783
+ const tokenId = createTokenIdBuilder(poolId).addLoan({
10784
+ asset: tokenType,
10785
+ tokenType,
10786
+ strike: 0n
10787
+ }).build();
10788
+ const d = Number(distance);
10789
+ const low = Math.min(Number(MAX_TICK), currentTick + d);
10790
+ const high = Math.max(Number(MIN_TICK), currentTick - d);
10791
+ if (low <= high) throw new PanopticError(`slippageBps ${slippageBps} at tick ${currentTick} yields a non-inverted band after clamping`);
10792
+ const invertedLimits = [low, high];
10793
+ const swapOn = kind === "exactIn" ? "mint" : "burn";
10794
+ return {
10795
+ sfpmAddress,
10796
+ poolAddress,
10797
+ poolKey: encodeAbiParameters([{ type: "address" }], [poolAddress]),
10798
+ tokenId,
10799
+ positionSize: amount,
10800
+ mintTickLimits: swapOn === "mint" ? invertedLimits : WIDE_LIMITS,
10801
+ burnTickLimits: swapOn === "burn" ? invertedLimits : WIDE_LIMITS,
10802
+ swapOn,
10803
+ kind
10840
10804
  };
10841
- const results = await Promise.all([v3 ? getPanopticPoolFromPoolId({
10842
- version: "v3",
10843
- client,
10844
- sfpmAddress: v3.sfpmAddress,
10845
- factoryAddress: v3.factoryAddress,
10846
- riskEngine,
10847
- poolId
10848
- }).catch((err) => {
10849
- if (isNotFoundError(err)) return zeroAddress;
10850
- throw err;
10851
- }) : Promise.resolve(zeroAddress), v4 ? getPanopticPoolFromPoolId({
10852
- version: "v4",
10853
- client,
10854
- sfpmAddress: v4.sfpmAddress,
10855
- factoryAddress: v4.factoryAddress,
10856
- riskEngine,
10857
- poolId
10858
- }).catch((err) => {
10859
- if (isNotFoundError(err)) return zeroAddress;
10860
- throw err;
10861
- }) : Promise.resolve(zeroAddress)]);
10862
- const [v3Result, v4Result] = results;
10863
- if (v3Result !== zeroAddress) return {
10864
- panopticPoolAddress: v3Result,
10865
- version: "v3"
10866
- };
10867
- if (v4Result !== zeroAddress) return {
10868
- panopticPoolAddress: v4Result,
10869
- version: "v4"
10870
- };
10871
- throw new Error(`No PanopticPool found for poolId ${poolId}`);
10872
10805
  }
10873
10806
 
10874
10807
  //#endregion
10875
- //#region src/panoptic/v2/reads/minePoolAddressLocal.ts
10808
+ //#region src/panoptic/v2/sfpmSwap/quote.ts
10809
+ const UINT128 = 1n << 128n;
10810
+ const INT128_MAX = (1n << 127n) - 1n;
10811
+ /** Extract the two signed 128-bit slots from a packed `LeftRightSigned` int256. */
10812
+ function unpackLeftRightSigned(packed) {
10813
+ const u = packed < 0n ? packed + (1n << 256n) : packed;
10814
+ const toInt128 = (half) => half > INT128_MAX ? half - UINT128 : half;
10815
+ return {
10816
+ right: toInt128(u & UINT128 - 1n),
10817
+ left: toInt128(u >> 128n & UINT128 - 1n)
10818
+ };
10819
+ }
10820
+ /** Wrap an unknown thrown value as a PanopticError, decoding Panoptic reverts when possible. */
10821
+ function toPanopticError(err) {
10822
+ if (err instanceof PanopticError) return err;
10823
+ const parsed = parsePanopticError(err);
10824
+ if (parsed) return parsed.error;
10825
+ return new PanopticError(err instanceof Error ? err.message : "SFPM swap simulation failed", err instanceof Error ? err : void 0);
10826
+ }
10876
10827
  /**
10877
- * keccak256 of the CREATE3 proxy initcode used by ClonesWithImmutableArgs.
10878
- * Source: packages/panoptic-v2-core/lib/clones-with-immutable-args/src/ClonesWithImmutableArgs.sol
10828
+ * Quote a swap by simulating `SFPM.multicall([mint, burn])` and decoding the swap
10829
+ * call's `totalMoved` return. Authoritative — captures the width-0 loan-leg wei
10830
+ * rounding a raw QuoterV2 quote would miss.
10879
10831
  */
10880
- const CREATE3_PROXY_BYTECODE_HASH = "0x21c35dbe1b344a2488cf3321d6ce542f8e9f305544ff09e4993a62319a497c1f";
10881
- const MASK_80 = (1n << 80n) - 1n;
10882
- const MASK_40 = (1n << 40n) - 1n;
10883
- const MASK_96 = (1n << 96n) - 1n;
10884
- /** Encode a BigInt as a big-endian fixed-length byte array. */
10885
- function bigintToBytes(value, byteLength) {
10886
- const bytes = new Uint8Array(byteLength);
10887
- let v = value;
10888
- for (let i = byteLength - 1; i >= 0; i--) {
10889
- bytes[i] = Number(v & 0xffn);
10890
- v >>= 8n;
10832
+ async function quoteSfpmSwap(params) {
10833
+ const { client, plan, account, stateOverride, blockNumber } = params;
10834
+ const { mintData, burnData } = buildSfpmSwapCalldata(plan);
10835
+ const _meta = await getBlockMeta({
10836
+ client,
10837
+ blockNumber
10838
+ });
10839
+ try {
10840
+ const { result } = await client.simulateContract({
10841
+ account,
10842
+ address: plan.sfpmAddress,
10843
+ abi: semiFungiblePositionManagerV3Abi,
10844
+ functionName: "multicall",
10845
+ args: [[mintData, burnData]],
10846
+ blockNumber,
10847
+ stateOverride
10848
+ });
10849
+ const swapIndex = plan.swapOn === "mint" ? 0 : 1;
10850
+ const decoded = decodeFunctionResult({
10851
+ abi: semiFungiblePositionManagerV3Abi,
10852
+ functionName: plan.swapOn === "mint" ? "mintTokenizedPosition" : "burnTokenizedPosition",
10853
+ data: result[swapIndex]
10854
+ });
10855
+ const finalTick = Number(decoded[2]);
10856
+ const { right, left } = unpackLeftRightSigned(decoded[1]);
10857
+ const inSlot = [right, left].find((s) => s > 0n);
10858
+ const outSlot = [right, left].find((s) => s < 0n);
10859
+ if (inSlot === void 0 || outSlot === void 0) return {
10860
+ success: false,
10861
+ error: new PanopticError(`SFPM swap simulation moved no tokens (totalMoved=${decoded[1]})`),
10862
+ _meta
10863
+ };
10864
+ return {
10865
+ success: true,
10866
+ data: {
10867
+ amountIn: inSlot,
10868
+ amountOut: -outSlot,
10869
+ finalTick
10870
+ },
10871
+ gasEstimate: 0n,
10872
+ _meta
10873
+ };
10874
+ } catch (err) {
10875
+ return {
10876
+ success: false,
10877
+ error: toPanopticError(err),
10878
+ _meta
10879
+ };
10891
10880
  }
10892
- return bytes;
10893
- }
10894
- /** Parse a 0x-prefixed address into 20 bytes. */
10895
- function addressToBytes(addr) {
10896
- const hex = addr.slice(2).padStart(40, "0");
10897
- const bytes = new Uint8Array(20);
10898
- for (let i = 0; i < 20; i++) bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
10899
- return bytes;
10900
- }
10901
- /** Parse a 0x-prefixed 32-byte hex string into bytes. */
10902
- function hex32ToBytes(hex) {
10903
- const h = hex.slice(2).padStart(64, "0");
10904
- const bytes = new Uint8Array(32);
10905
- for (let i = 0; i < 32; i++) bytes[i] = parseInt(h.slice(i * 2, i * 2 + 2), 16);
10906
- return bytes;
10907
10881
  }
10882
+
10883
+ //#endregion
10884
+ //#region src/uniswap/v4/abis/universalRouter.ts
10908
10885
  /**
10909
- * Compute the CREATE3 deployed address for a given factory and packed salt.
10910
- *
10911
- * Mirrors `ClonesWithImmutableArgs.addressOfClone3(salt)` (with `address(this)` = factory):
10912
- * proxy = CREATE2(factory, salt, PROXY_BYTECODE_HASH)
10913
- * deployed = CREATE1(proxy, nonce=1)
10914
- *
10915
- * Returns the deployed address as a uint160 BigInt.
10886
+ * Minimal Universal Router ABI (only the `execute` overload we use).
10887
+ * @module uniswap/v4/abis/universalRouter
10916
10888
  */
10917
- function addressOfClone3(factory, salt) {
10918
- const saltBytes = bigintToBytes(salt, 32);
10919
- const create2Input = new Uint8Array(85);
10920
- create2Input[0] = 255;
10921
- create2Input.set(addressToBytes(factory), 1);
10922
- create2Input.set(saltBytes, 21);
10923
- create2Input.set(hex32ToBytes(CREATE3_PROXY_BYTECODE_HASH), 53);
10924
- const proxyHash = keccak256(create2Input, "bytes");
10925
- const proxyAddress = proxyHash.slice(12);
10926
- const create1Input = new Uint8Array(23);
10927
- create1Input[0] = 214;
10928
- create1Input[1] = 148;
10929
- create1Input.set(proxyAddress, 2);
10930
- create1Input[22] = 1;
10931
- const deployedHash = keccak256(create1Input, "bytes");
10932
- let addr = 0n;
10933
- for (let i = 12; i < 32; i++) addr = addr << 8n | BigInt(deployedHash[i]);
10934
- return addr;
10935
- }
10889
+ const universalRouterAbi = [{
10890
+ type: "function",
10891
+ name: "execute",
10892
+ stateMutability: "payable",
10893
+ inputs: [
10894
+ {
10895
+ name: "commands",
10896
+ type: "bytes"
10897
+ },
10898
+ {
10899
+ name: "inputs",
10900
+ type: "bytes[]"
10901
+ },
10902
+ {
10903
+ name: "deadline",
10904
+ type: "uint256"
10905
+ }
10906
+ ],
10907
+ outputs: []
10908
+ }];
10909
+
10910
+ //#endregion
10911
+ //#region src/uniswap/v4/router/errors.ts
10936
10912
  /**
10937
- * Count leading hex-zero characters in a 160-bit address value.
10938
- *
10939
- * Mirrors `PanopticMath.numberOfLeadingHexZeros(addr)`.
10940
- * Returns 40 for the zero address.
10913
+ * The chain has no configured Uniswap v4 addresses and none were supplied via
10914
+ * overrides.
10941
10915
  */
10942
- function numberOfLeadingHexZeros(addrInt) {
10943
- if (addrInt === 0n) return 40;
10944
- let x = addrInt;
10945
- let r = 0;
10946
- if (x >= 0x100000000000000000000000000000000n) {
10947
- x >>= 128n;
10948
- r += 32;
10949
- }
10950
- if (x >= 0x10000000000000000n) {
10951
- x >>= 64n;
10952
- r += 16;
10916
+ var UnsupportedChainError = class extends PanopticError {
10917
+ name = "UnsupportedChainError";
10918
+ constructor(chainId, cause) {
10919
+ super(`Uniswap v4 router not configured for chain ${chainId}`, cause);
10920
+ this.chainId = chainId;
10953
10921
  }
10954
- if (x >= 0x100000000n) {
10955
- x >>= 32n;
10956
- r += 8;
10922
+ };
10923
+ /**
10924
+ * `tokenIn` is neither `currency0` nor `currency1` of the resolved pool.
10925
+ */
10926
+ var InvalidSwapTokenError = class extends PanopticError {
10927
+ name = "InvalidSwapTokenError";
10928
+ constructor(token, currency0, currency1, cause) {
10929
+ super(`Token ${token} is not part of the pool (currency0=${currency0}, currency1=${currency1})`, cause);
10930
+ this.token = token;
10931
+ this.currency0 = currency0;
10932
+ this.currency1 = currency1;
10957
10933
  }
10958
- if (x >= 0x10000n) {
10959
- x >>= 16n;
10960
- r += 4;
10934
+ };
10935
+ /**
10936
+ * An amount exceeds the uint128 range required by the v4 swap encoding.
10937
+ */
10938
+ var AmountExceedsUint128Error = class extends PanopticError {
10939
+ name = "AmountExceedsUint128Error";
10940
+ constructor(amount, cause) {
10941
+ super(`Amount ${amount} exceeds uint128 maximum`, cause);
10942
+ this.amount = amount;
10961
10943
  }
10962
- if (x >= 0x100n) {
10963
- x >>= 8n;
10964
- r += 2;
10944
+ };
10945
+ /**
10946
+ * A native-ETH swap needs a trailing Universal Router SWEEP to deliver the ETH
10947
+ * output (or refund the input overpay), but no `recipient` was supplied.
10948
+ */
10949
+ var MissingSweepRecipientError = class extends PanopticError {
10950
+ name = "MissingSweepRecipientError";
10951
+ constructor(cause) {
10952
+ super("A recipient is required to sweep native ETH back to the user", cause);
10965
10953
  }
10966
- if (x >= 0x10n) r += 1;
10967
- return 39 - r;
10968
- }
10954
+ };
10969
10955
  /**
10970
- * Construct the bytes32 CREATE3 salt for PanopticFactoryV3.
10971
- *
10972
- * Mirrors:
10973
- * bytes32(abi.encodePacked(
10974
- * uint80(uint160(deployerAddress) >> 80), // bits [159:80] of deployer → 10 bytes
10975
- * uint40(uint160(v3Pool) >> 120), // bits [159:120] of v3Pool → 5 bytes
10976
- * uint40(uint160(riskEngine) >> 120), // bits [159:120] of riskEngine→ 5 bytes
10977
- * salt // uint96 → 12 bytes
10978
- * ))
10956
+ * The V4Quoter is not available for the chain (no fallback in v1).
10979
10957
  */
10980
- function computeSaltPrefixV3(deployerAddress, v3Pool, riskEngine) {
10981
- const deployer80 = BigInt(deployerAddress) >> 80n & MASK_80;
10982
- const pool40 = BigInt(v3Pool) >> 120n & MASK_40;
10983
- const risk40 = BigInt(riskEngine) >> 120n & MASK_40;
10984
- return deployer80 << 176n | pool40 << 136n | risk40 << 96n;
10985
- }
10986
- /**
10987
- * Compute the Uniswap V4 PoolId for a PoolKey.
10988
- *
10989
- * Mirrors `PoolId.toId(key)` = keccak256 of the ABI-encoded PoolKey struct
10990
- * (5 fields × 32 bytes = 160 bytes).
10991
- */
10992
- function computePoolIdV4(poolKey) {
10993
- const fee = Number(poolKey.fee);
10994
- if (!Number.isInteger(fee) || fee < 0 || fee >= 2 ** 24) throw new PanopticValidationError(`fee out of uint24 range: ${fee}`);
10995
- const tickSpacing = Number(poolKey.tickSpacing);
10996
- if (!Number.isInteger(tickSpacing) || tickSpacing < -(2 ** 23) || tickSpacing > 2 ** 23 - 1) throw new PanopticValidationError(`tickSpacing out of int24 range: ${tickSpacing}`);
10997
- const encoded = encodeAbiParameters([
10998
- { type: "address" },
10999
- { type: "address" },
11000
- { type: "uint24" },
11001
- { type: "int24" },
11002
- { type: "address" }
11003
- ], [
11004
- poolKey.currency0,
11005
- poolKey.currency1,
11006
- Number(poolKey.fee),
11007
- Number(poolKey.tickSpacing),
11008
- poolKey.hooks
11009
- ]);
11010
- return BigInt(keccak256(encoded));
11011
- }
11012
- /**
11013
- * Construct the bytes32 CREATE3 salt for PanopticFactoryV4.
11014
- *
11015
- * Mirrors:
11016
- * bytes32(abi.encodePacked(
11017
- * uint80(uint160(deployerAddress) >> 80),
11018
- * uint40(uint256(PoolId.unwrap(key.toId())) >> 120), // bits [159:120] of poolId
11019
- * uint40(uint160(riskEngine) >> 120),
11020
- * salt
11021
- * ))
11022
- */
11023
- function computeSaltPrefixV4(deployerAddress, poolKey, riskEngine) {
11024
- const deployer80 = BigInt(deployerAddress) >> 80n & MASK_80;
11025
- const poolId40 = computePoolIdV4(poolKey) >> 120n & MASK_40;
11026
- const risk40 = BigInt(riskEngine) >> 120n & MASK_40;
11027
- return deployer80 << 176n | poolId40 << 136n | risk40 << 96n;
11028
- }
11029
- /** Number of iterations per chunk before yielding back to the event loop. */
11030
- const CHUNK_SIZE = 5000n;
11031
- /**
11032
- * Async version of {@link minePoolAddressLocal} that yields to the event loop
11033
- * between chunks of iterations, preventing the browser UI from freezing.
11034
- *
11035
- * @param params - Mining parameters (versioned: 'v3' or 'v4'). No `client` required.
11036
- * @returns The best salt found and its rarity (number of leading hex zeros).
11037
- */
11038
- async function minePoolAddressLocalAsync(params) {
11039
- const { factoryAddress, deployerAddress, riskEngine, salt, loops, minTargetRarity } = params;
11040
- let bestSalt = salt;
11041
- let highestRarity = 0n;
11042
- const maxSalt = salt + loops;
11043
- const saltPrefix = params.version === "v3" ? computeSaltPrefixV3(deployerAddress, params.v3Pool, riskEngine) : computeSaltPrefixV4(deployerAddress, params.poolKey, riskEngine);
11044
- let currentSalt = salt;
11045
- while (currentSalt < maxSalt) {
11046
- const chunkEnd = currentSalt + CHUNK_SIZE < maxSalt ? currentSalt + CHUNK_SIZE : maxSalt;
11047
- let done = false;
11048
- for (; currentSalt < chunkEnd; currentSalt++) {
11049
- const newSalt = saltPrefix | currentSalt & MASK_96;
11050
- const addrInt = addressOfClone3(factoryAddress, newSalt);
11051
- const rarity = BigInt(numberOfLeadingHexZeros(addrInt));
11052
- if (rarity > highestRarity) {
11053
- highestRarity = rarity;
11054
- bestSalt = currentSalt;
11055
- }
11056
- if (rarity >= minTargetRarity) {
11057
- highestRarity = rarity;
11058
- bestSalt = currentSalt;
11059
- done = true;
11060
- break;
11061
- }
11062
- }
11063
- if (done) break;
11064
- await new Promise((resolve) => setTimeout(resolve, 0));
10958
+ var QuoterUnavailableError = class extends PanopticError {
10959
+ name = "QuoterUnavailableError";
10960
+ constructor(chainId, cause) {
10961
+ super(`V4Quoter unavailable for chain ${chainId}`, cause);
10962
+ this.chainId = chainId;
11065
10963
  }
11066
- return {
11067
- bestSalt,
11068
- highestRarity
11069
- };
11070
- }
10964
+ };
11071
10965
 
11072
10966
  //#endregion
11073
- //#region src/panoptic/v2/abis/stateView.ts
10967
+ //#region src/uniswap/v3/router/encodeSwap.ts
10968
+ const UINT128_MAX$4 = (1n << 128n) - 1n;
10969
+
10970
+ //#endregion
10971
+ //#region src/uniswap/v3/abis/quoterV2.ts
11074
10972
  /**
11075
- * Minimal Uniswap V4 StateView ABI for fee growth reads.
11076
- * Only includes functions needed by streamia history.
10973
+ * Minimal Uniswap v3 QuoterV2 ABI (`quoteExactInputSingle`).
10974
+ *
10975
+ * QuoterV2 is revert/staticcall-based (nonpayable but intended for `eth_call` /
10976
+ * `simulateContract`), so it must be simulated, not read.
10977
+ * @module uniswap/v3/abis/quoterV2
11077
10978
  */
11078
- const stateViewAbi = [
11079
- {
11080
- inputs: [{
11081
- internalType: "PoolId",
11082
- name: "poolId",
11083
- type: "bytes32"
11084
- }],
11085
- name: "getSlot0",
11086
- outputs: [
10979
+ const quoterV2Abi = [{
10980
+ type: "function",
10981
+ name: "quoteExactInputSingle",
10982
+ stateMutability: "nonpayable",
10983
+ inputs: [{
10984
+ name: "params",
10985
+ type: "tuple",
10986
+ components: [
11087
10987
  {
11088
- internalType: "uint160",
11089
- name: "sqrtPriceX96",
11090
- type: "uint160"
10988
+ name: "tokenIn",
10989
+ type: "address"
11091
10990
  },
11092
10991
  {
11093
- internalType: "int24",
11094
- name: "tick",
11095
- type: "int24"
10992
+ name: "tokenOut",
10993
+ type: "address"
11096
10994
  },
11097
10995
  {
11098
- internalType: "uint24",
11099
- name: "protocolFee",
11100
- type: "uint24"
10996
+ name: "amountIn",
10997
+ type: "uint256"
11101
10998
  },
11102
10999
  {
11103
- internalType: "uint24",
11104
- name: "lpFee",
11000
+ name: "fee",
11105
11001
  type: "uint24"
11106
- }
11107
- ],
11108
- stateMutability: "view",
11109
- type: "function"
11110
- },
11111
- {
11112
- inputs: [{
11113
- internalType: "PoolId",
11114
- name: "poolId",
11115
- type: "bytes32"
11116
- }],
11117
- name: "getFeeGrowthGlobals",
11118
- outputs: [{
11119
- internalType: "uint256",
11120
- name: "feeGrowthGlobal0",
11121
- type: "uint256"
11122
- }, {
11123
- internalType: "uint256",
11124
- name: "feeGrowthGlobal1",
11125
- type: "uint256"
11126
- }],
11127
- stateMutability: "view",
11128
- type: "function"
11129
- },
11130
- {
11131
- inputs: [{
11132
- internalType: "PoolId",
11133
- name: "poolId",
11134
- type: "bytes32"
11135
- }, {
11136
- internalType: "int24",
11137
- name: "tick",
11138
- type: "int24"
11139
- }],
11140
- name: "getTickInfo",
11141
- outputs: [
11142
- {
11143
- internalType: "uint128",
11144
- name: "liquidityGross",
11145
- type: "uint128"
11146
- },
11147
- {
11148
- internalType: "int128",
11149
- name: "liquidityNet",
11150
- type: "int128"
11151
- },
11152
- {
11153
- internalType: "uint256",
11154
- name: "feeGrowthOutside0X128",
11155
- type: "uint256"
11156
11002
  },
11157
11003
  {
11158
- internalType: "uint256",
11159
- name: "feeGrowthOutside1X128",
11160
- type: "uint256"
11004
+ name: "sqrtPriceLimitX96",
11005
+ type: "uint160"
11161
11006
  }
11162
- ],
11163
- stateMutability: "view",
11164
- type: "function"
11165
- },
11166
- {
11167
- inputs: [{
11168
- internalType: "PoolId",
11169
- name: "poolId",
11170
- type: "bytes32"
11171
- }],
11172
- name: "getLiquidity",
11173
- outputs: [{
11174
- internalType: "uint128",
11175
- name: "liquidity",
11176
- type: "uint128"
11177
- }],
11178
- stateMutability: "view",
11179
- type: "function"
11180
- }
11181
- ];
11007
+ ]
11008
+ }],
11009
+ outputs: [
11010
+ {
11011
+ name: "amountOut",
11012
+ type: "uint256"
11013
+ },
11014
+ {
11015
+ name: "sqrtPriceX96After",
11016
+ type: "uint160"
11017
+ },
11018
+ {
11019
+ name: "initializedTicksCrossed",
11020
+ type: "uint32"
11021
+ },
11022
+ {
11023
+ name: "gasEstimate",
11024
+ type: "uint256"
11025
+ }
11026
+ ]
11027
+ }];
11182
11028
 
11183
11029
  //#endregion
11184
- //#region src/panoptic/v2/abis/uniswapV3Pool.ts
11030
+ //#region src/uniswap/v3/addresses.ts
11185
11031
  /**
11186
- * Minimal Uniswap V3 Pool ABI for fee growth reads.
11187
- * Only includes functions needed by streamia history.
11032
+ * Verified Uniswap v3 QuoterV2 addresses keyed by chainId. Add a chain only
11033
+ * after verifying against the canonical Uniswap v3 deployment listing.
11188
11034
  */
11189
- const uniswapV3PoolAbi = [
11190
- {
11191
- inputs: [],
11192
- name: "slot0",
11193
- outputs: [
11194
- {
11195
- internalType: "uint160",
11196
- name: "sqrtPriceX96",
11197
- type: "uint160"
11198
- },
11199
- {
11200
- internalType: "int24",
11201
- name: "tick",
11202
- type: "int24"
11203
- },
11204
- {
11205
- internalType: "uint16",
11206
- name: "observationIndex",
11207
- type: "uint16"
11208
- },
11209
- {
11210
- internalType: "uint16",
11211
- name: "observationCardinality",
11212
- type: "uint16"
11213
- },
11214
- {
11215
- internalType: "uint16",
11216
- name: "observationCardinalityNext",
11217
- type: "uint16"
11218
- },
11219
- {
11220
- internalType: "uint8",
11221
- name: "feeProtocol",
11222
- type: "uint8"
11223
- },
11224
- {
11225
- internalType: "bool",
11226
- name: "unlocked",
11227
- type: "bool"
11228
- }
11229
- ],
11230
- stateMutability: "view",
11231
- type: "function"
11232
- },
11233
- {
11234
- inputs: [],
11235
- name: "feeGrowthGlobal0X128",
11236
- outputs: [{
11237
- internalType: "uint256",
11238
- name: "",
11239
- type: "uint256"
11240
- }],
11241
- stateMutability: "view",
11242
- type: "function"
11243
- },
11244
- {
11245
- inputs: [],
11246
- name: "feeGrowthGlobal1X128",
11247
- outputs: [{
11248
- internalType: "uint256",
11249
- name: "",
11250
- type: "uint256"
11251
- }],
11252
- stateMutability: "view",
11253
- type: "function"
11254
- },
11255
- {
11256
- inputs: [{
11257
- internalType: "int24",
11258
- name: "",
11259
- type: "int24"
11260
- }],
11261
- name: "ticks",
11262
- outputs: [
11263
- {
11264
- internalType: "uint128",
11265
- name: "liquidityGross",
11266
- type: "uint128"
11267
- },
11268
- {
11269
- internalType: "int128",
11270
- name: "liquidityNet",
11271
- type: "int128"
11272
- },
11273
- {
11274
- internalType: "uint256",
11275
- name: "feeGrowthOutside0X128",
11276
- type: "uint256"
11277
- },
11278
- {
11279
- internalType: "uint256",
11280
- name: "feeGrowthOutside1X128",
11281
- type: "uint256"
11282
- },
11283
- {
11284
- internalType: "int56",
11285
- name: "tickCumulativeOutside",
11286
- type: "int56"
11287
- },
11288
- {
11289
- internalType: "uint160",
11290
- name: "secondsPerLiquidityOutsideX128",
11291
- type: "uint160"
11292
- },
11293
- {
11294
- internalType: "uint32",
11295
- name: "secondsOutside",
11296
- type: "uint32"
11297
- },
11298
- {
11299
- internalType: "bool",
11300
- name: "initialized",
11301
- type: "bool"
11302
- }
11303
- ],
11304
- stateMutability: "view",
11305
- type: "function"
11306
- },
11307
- {
11308
- inputs: [],
11309
- name: "liquidity",
11310
- outputs: [{
11311
- internalType: "uint128",
11312
- name: "",
11313
- type: "uint128"
11314
- }],
11315
- stateMutability: "view",
11316
- type: "function"
11317
- },
11318
- {
11319
- inputs: [],
11320
- name: "fee",
11321
- outputs: [{
11322
- internalType: "uint24",
11323
- name: "",
11324
- type: "uint24"
11325
- }],
11326
- stateMutability: "view",
11327
- type: "function"
11328
- },
11329
- {
11330
- inputs: [],
11331
- name: "token0",
11332
- outputs: [{
11333
- internalType: "address",
11334
- name: "",
11335
- type: "address"
11336
- }],
11337
- stateMutability: "view",
11338
- type: "function"
11339
- },
11340
- {
11341
- inputs: [],
11342
- name: "token1",
11343
- outputs: [{
11344
- internalType: "address",
11345
- name: "",
11346
- type: "address"
11347
- }],
11348
- stateMutability: "view",
11349
- type: "function"
11350
- },
11351
- {
11352
- inputs: [],
11353
- name: "tickSpacing",
11354
- outputs: [{
11355
- internalType: "int24",
11356
- name: "",
11357
- type: "int24"
11358
- }],
11359
- stateMutability: "view",
11360
- type: "function"
11361
- }
11362
- ];
11035
+ const UNISWAP_V3_ADDRESSES = { 1: {
11036
+ quoterV2: "0x61fFE014bA17989E743c5F6cB21bF9697530B21e",
11037
+ nonfungiblePositionManager: "0xC36442b4a4522E871399CD717aBDD847Ab11FE88"
11038
+ } };
11039
+ /**
11040
+ * Resolve Uniswap v3 addresses for a chain, applying optional overrides.
11041
+ *
11042
+ * @throws {UnsupportedChainError} when the chain is not listed and overrides do
11043
+ * not supply every required address.
11044
+ */
11045
+ function getUniswapV3Addresses(chainId, overrides) {
11046
+ const id = Number(chainId);
11047
+ const base = UNISWAP_V3_ADDRESSES[id];
11048
+ const merged = {
11049
+ ...base,
11050
+ ...overrides
11051
+ };
11052
+ if (!merged.quoterV2 || !merged.nonfungiblePositionManager) throw new UnsupportedChainError(BigInt(id));
11053
+ return merged;
11054
+ }
11363
11055
 
11364
11056
  //#endregion
11365
- //#region src/panoptic/v2/formatters/tick.ts
11366
- const Q192$3 = 1n << 192n;
11367
- const RAW_PRICE_PRECISION = 40n;
11368
- function pow10(exponent) {
11369
- if (exponent < 0n) throw new RangeError("Exponent must be non-negative");
11370
- return 10n ** exponent;
11057
+ //#region src/uniswap/v3/router/quote.ts
11058
+ const BPS_DENOMINATOR$6 = 10000n;
11059
+ const UINT128_MAX$3 = (1n << 128n) - 1n;
11060
+ /**
11061
+ * Quote an exact-in v3 swap. Returns `null` when the quote reverts (e.g. the
11062
+ * pool does not exist / has no liquidity) so callers can skip that pool when
11063
+ * ranking venues, rather than aborting the whole cycle.
11064
+ */
11065
+ async function quoteV3ExactIn(params) {
11066
+ const { client, chainId, tokenIn, tokenOut, fee, amountIn, slippageBps, blockNumber } = params;
11067
+ if (amountIn < 0n || amountIn > UINT128_MAX$3) throw new PanopticError(`amountIn ${amountIn} exceeds uint128 maximum`);
11068
+ if (slippageBps < 0n || slippageBps > BPS_DENOMINATOR$6) throw new PanopticError(`invalid slippageBps ${slippageBps}, must be 0..10000`);
11069
+ const { quoterV2 } = getUniswapV3Addresses(chainId, params.addresses);
11070
+ try {
11071
+ const { result } = await client.simulateContract({
11072
+ address: quoterV2,
11073
+ abi: quoterV2Abi,
11074
+ functionName: "quoteExactInputSingle",
11075
+ blockNumber,
11076
+ args: [{
11077
+ tokenIn,
11078
+ tokenOut,
11079
+ amountIn,
11080
+ fee: Number(fee),
11081
+ sqrtPriceLimitX96: 0n
11082
+ }]
11083
+ });
11084
+ const [amountOut, , , gasEstimate] = result;
11085
+ const amountOutMinimum = amountOut * (BPS_DENOMINATOR$6 - slippageBps) / BPS_DENOMINATOR$6;
11086
+ return {
11087
+ amountOut,
11088
+ amountOutMinimum,
11089
+ gasEstimate
11090
+ };
11091
+ } catch (err) {
11092
+ if (isRevert(err)) return null;
11093
+ throw err;
11094
+ }
11371
11095
  }
11372
- function absBigint(value) {
11373
- return value < 0n ? -value : value;
11096
+ /** True only for genuine contract reverts (missing pool / no liquidity). */
11097
+ function isRevert(err) {
11098
+ return err instanceof BaseError && err.walk((e) => e instanceof ContractFunctionRevertedError) instanceof ContractFunctionRevertedError;
11374
11099
  }
11375
- function trimTrailingZeros(value) {
11376
- const dotIndex = value.indexOf(".");
11377
- if (dotIndex === -1) return value;
11378
- let end = value.length;
11379
- while (end > dotIndex && value[end - 1] === "0") end -= 1;
11380
- if (end === dotIndex + 1) end = dotIndex;
11381
- return value.slice(0, end);
11100
+
11101
+ //#endregion
11102
+ //#region src/panoptic/v2/reads/sfpm.ts
11103
+ /**
11104
+ * Resolve an SFPM poolId to its corresponding Uniswap V3 pool address.
11105
+ *
11106
+ * Calls `SemiFungiblePositionManagerV3.getUniswapV3PoolFromId(poolId)`.
11107
+ */
11108
+ async function getUniswapV3PoolFromId(params) {
11109
+ const { client, sfpmAddress, poolId } = params;
11110
+ return client.readContract({
11111
+ address: sfpmAddress,
11112
+ abi: semiFungiblePositionManagerV3Abi,
11113
+ functionName: "getUniswapV3PoolFromId",
11114
+ args: [poolId]
11115
+ });
11382
11116
  }
11383
- function formatRatio$3(numerator, denominator, precision) {
11384
- if (precision < 0n) throw new RangeError("Precision must be non-negative");
11385
- const sign = numerator < 0n ? "-" : "";
11386
- const absNumerator = numerator < 0n ? -numerator : numerator;
11387
- const scale = pow10(precision);
11388
- const scaled = (absNumerator * scale + denominator / 2n) / denominator;
11389
- const integerPart = scaled / scale;
11390
- const fractionalPart = scaled % scale;
11391
- if (precision === 0n) return `${sign}${integerPart}`;
11392
- return `${sign}${integerPart}.${fractionalPart.toString().padStart(Number(precision), "0")}`;
11393
- }
11394
- function parseDecimalToFraction(value) {
11395
- const trimmed = value.trim();
11396
- if (trimmed.length === 0) throw new Error("Price must be a number");
11397
- const isNegative = trimmed.startsWith("-");
11398
- const unsigned = isNegative || trimmed.startsWith("+") ? trimmed.slice(1) : trimmed;
11399
- const [basePart, exponentPart] = unsigned.toLowerCase().split("e");
11400
- const [integerStr, fractionalStr = ""] = basePart.split(".");
11401
- if (integerStr === "" && fractionalStr === "") throw new Error("Price must be a number");
11402
- const integerDigits = integerStr === "" ? "0" : integerStr;
11403
- const digits = `${integerDigits}${fractionalStr}`;
11404
- let numerator = BigInt(digits === "" ? "0" : digits);
11405
- let denominator = pow10(BigInt(fractionalStr.length));
11406
- if (exponentPart !== void 0 && exponentPart !== "") {
11407
- const exponent = BigInt(exponentPart);
11408
- if (exponent > 0n) numerator *= pow10(exponent);
11409
- else if (exponent < 0n) denominator *= pow10(-exponent);
11410
- }
11411
- if (isNegative) numerator = -numerator;
11117
+ /**
11118
+ * Resolve an SFPM poolId to its corresponding Uniswap V4 pool key.
11119
+ *
11120
+ * Calls `SemiFungiblePositionManagerV4.getUniswapV4PoolKeyFromId(poolId)`.
11121
+ */
11122
+ async function getUniswapV4PoolKeyFromId(params) {
11123
+ const { client, sfpmAddress, poolId } = params;
11124
+ const raw = await client.readContract({
11125
+ address: sfpmAddress,
11126
+ abi: semiFungiblePositionManagerV4Abi,
11127
+ functionName: "getUniswapV4PoolKeyFromId",
11128
+ args: [poolId]
11129
+ });
11412
11130
  return {
11413
- numerator,
11414
- denominator
11131
+ currency0: raw.currency0,
11132
+ currency1: raw.currency1,
11133
+ fee: BigInt(raw.fee),
11134
+ tickSpacing: BigInt(raw.tickSpacing),
11135
+ hooks: raw.hooks
11415
11136
  };
11416
11137
  }
11417
- function compareRatios(leftNumerator, leftDenominator, rightNumerator, rightDenominator) {
11418
- const left = leftNumerator * rightDenominator;
11419
- const right = rightNumerator * leftDenominator;
11420
- if (left === right) return 0;
11421
- return left < right ? -1 : 1;
11422
- }
11423
- function tickToSqrtPriceX96(tick) {
11424
- if (tick < MIN_TICK || tick > MAX_TICK) throw new RangeError("Tick out of bounds");
11425
- const absTick = tick < 0n ? -tick : tick;
11426
- let ratio = (absTick & 0x1n) !== 0n ? 0xfffcb933bd6fad37aa2d162d1a594001n : 0x100000000000000000000000000000000n;
11427
- if ((absTick & 0x2n) !== 0n) ratio = ratio * 0xfff97272373d413259a46990580e213an >> 128n;
11428
- if ((absTick & 0x4n) !== 0n) ratio = ratio * 0xfff2e50f5f656932ef12357cf3c7fdccn >> 128n;
11429
- if ((absTick & 0x8n) !== 0n) ratio = ratio * 0xffe5caca7e10e4e61c3624eaa0941cd0n >> 128n;
11430
- if ((absTick & 0x10n) !== 0n) ratio = ratio * 0xffcb9843d60f6159c9db58835c926644n >> 128n;
11431
- if ((absTick & 0x20n) !== 0n) ratio = ratio * 0xff973b41fa98c081472e6896dfb254c0n >> 128n;
11432
- if ((absTick & 0x40n) !== 0n) ratio = ratio * 0xff2ea16466c96a3843ec78b326b52861n >> 128n;
11433
- if ((absTick & 0x80n) !== 0n) ratio = ratio * 0xfe5dee046a99a2a811c461f1969c3053n >> 128n;
11434
- if ((absTick & 0x100n) !== 0n) ratio = ratio * 0xfcbe86c7900a88aedcffc83b479aa3a4n >> 128n;
11435
- if ((absTick & 0x200n) !== 0n) ratio = ratio * 0xf987a7253ac413176f2b074cf7815e54n >> 128n;
11436
- if ((absTick & 0x400n) !== 0n) ratio = ratio * 0xf3392b0822b70005940c7a398e4b70f3n >> 128n;
11437
- if ((absTick & 0x800n) !== 0n) ratio = ratio * 0xe7159475a2c29b7443b29c7fa6e889d9n >> 128n;
11438
- if ((absTick & 0x1000n) !== 0n) ratio = ratio * 0xd097f3bdfd2022b8845ad8f792aa5825n >> 128n;
11439
- if ((absTick & 0x2000n) !== 0n) ratio = ratio * 0xa9f746462d870fdf8a65dc1f90e061e5n >> 128n;
11440
- if ((absTick & 0x4000n) !== 0n) ratio = ratio * 0x70d869a156d2a1b890bb3df62baf32f7n >> 128n;
11441
- if ((absTick & 0x8000n) !== 0n) ratio = ratio * 0x31be135f97d08fd981231505542fcfa6n >> 128n;
11442
- if ((absTick & 0x10000n) !== 0n) ratio = ratio * 0x9aa508b5b7a84e1c677de54f3e99bc9n >> 128n;
11443
- if ((absTick & 0x20000n) !== 0n) ratio = ratio * 0x5d6af8dedb81196699c329225ee604n >> 128n;
11444
- if ((absTick & 0x40000n) !== 0n) ratio = ratio * 0x2216e584f5fa1ea926041bedfe98n >> 128n;
11445
- if ((absTick & 0x80000n) !== 0n) ratio = ratio * 0x48a170391f7dc42444e8fa2n >> 128n;
11446
- if (tick > 0n) ratio = ((1n << 256n) - 1n) / ratio;
11447
- const remainderMask = (1n << 32n) - 1n;
11448
- const sqrtPriceX96 = (ratio >> 32n) + ((ratio & remainderMask) === 0n ? 0n : 1n);
11449
- return sqrtPriceX96;
11450
- }
11451
- const MIN_SQRT_PRICE_X96 = tickToSqrtPriceX96(MIN_TICK);
11452
- const MAX_SQRT_PRICE_X96 = tickToSqrtPriceX96(MAX_TICK);
11453
- function getPriceRatioFromSqrtPriceX96(sqrtPriceX96) {
11138
+ /**
11139
+ * Get the enforced tick limits for a pool from the SFPM.
11140
+ *
11141
+ * @param params - The parameters
11142
+ * @returns The min and max enforced ticks
11143
+ */
11144
+ async function getEnforcedTickLimits(params) {
11145
+ const { client, sfpmAddress, poolId } = params;
11146
+ const [minTick, maxTick] = await client.readContract({
11147
+ address: sfpmAddress,
11148
+ abi: semiFungiblePositionManagerV4Abi,
11149
+ functionName: "getEnforcedTickLimits",
11150
+ args: [poolId]
11151
+ });
11454
11152
  return {
11455
- numerator: sqrtPriceX96 * sqrtPriceX96,
11456
- denominator: Q192$3
11153
+ minEnforcedTick: minTick,
11154
+ maxEnforcedTick: maxTick
11457
11155
  };
11458
11156
  }
11459
- function getRawPriceRatio(tick) {
11460
- return getPriceRatioFromSqrtPriceX96(tickToSqrtPriceX96(tick));
11461
- }
11462
- function adjustRatioForDecimals(numerator, denominator, decimals0, decimals1) {
11463
- const diff = decimals0 - decimals1;
11464
- if (diff === 0n) return {
11465
- numerator,
11466
- denominator
11467
- };
11468
- if (diff > 0n) return {
11469
- numerator: numerator * pow10(diff),
11470
- denominator
11157
+ /**
11158
+ * Fetch liquidity breakdown for a batch of chunks via SFPM.getAccountLiquidity().
11159
+ *
11160
+ * Uses multicall for efficiency. Returns one result per input chunk,
11161
+ * along with block metadata for freshness tracking.
11162
+ */
11163
+ async function getChunkLiquidities(params) {
11164
+ const { client, sfpmAddress, poolKeyBytes, chunks } = params;
11165
+ if (chunks.length > MAX_TRACKED_CHUNKS) throw new ChunkLimitError(BigInt(chunks.length), 0n);
11166
+ const _meta = params._meta ?? await getBlockMeta({ client });
11167
+ if (chunks.length === 0) return {
11168
+ results: [],
11169
+ _meta
11471
11170
  };
11171
+ const multicallResults = await client.multicall({
11172
+ contracts: chunks.map((chunk) => ({
11173
+ address: sfpmAddress,
11174
+ abi: semiFungiblePositionManagerV4Abi,
11175
+ functionName: "getAccountLiquidity",
11176
+ args: [
11177
+ poolKeyBytes,
11178
+ chunk.owner,
11179
+ chunk.tokenType,
11180
+ Number(chunk.tickLower),
11181
+ Number(chunk.tickUpper)
11182
+ ]
11183
+ })),
11184
+ allowFailure: true
11185
+ });
11186
+ const results = multicallResults.map((result) => {
11187
+ if (result.status === "failure") return {
11188
+ netLiquidity: 0n,
11189
+ removedLiquidity: 0n,
11190
+ totalLiquidity: 0n,
11191
+ shortLiquidity: 0n,
11192
+ longLiquidity: 0n
11193
+ };
11194
+ const packed = result.result;
11195
+ const netLiquidity = packed & (1n << 128n) - 1n;
11196
+ const removedLiquidity = packed >> 128n;
11197
+ const totalLiquidity = netLiquidity + removedLiquidity;
11198
+ return {
11199
+ netLiquidity,
11200
+ removedLiquidity,
11201
+ totalLiquidity,
11202
+ shortLiquidity: totalLiquidity,
11203
+ longLiquidity: removedLiquidity
11204
+ };
11205
+ });
11472
11206
  return {
11473
- numerator,
11474
- denominator: denominator * pow10(-diff)
11207
+ results,
11208
+ _meta
11475
11209
  };
11476
11210
  }
11211
+
11212
+ //#endregion
11213
+ //#region src/panoptic/v2/reads/factory.ts
11477
11214
  /**
11478
- * Convert a tick to a raw price string (no decimal adjustment).
11479
- * Uses the formula: price = 1.0001^tick
11480
- *
11481
- * This returns the raw price ratio, not adjusted for token decimals.
11482
- * A fixed internal precision is used and trailing zeros are trimmed.
11483
- *
11484
- * @param tick - The tick value
11485
- * @returns Price string
11486
- *
11487
- * @example
11488
- * ```typescript
11489
- * tickToPrice(0n) // "1"
11490
- * tickToPrice(1000n) // "1.105..." (approximately)
11491
- * tickToPrice(-1000n) // "0.904..." (approximately)
11492
- * tickToPrice(200000n) // Very large number
11493
- * ```
11215
+ * Get the token URI from a PanopticFactory NFT.
11494
11216
  */
11495
- function tickToPrice(tick) {
11496
- const { numerator, denominator } = getRawPriceRatio(tick);
11497
- const price = formatRatio$3(numerator, denominator, RAW_PRICE_PRECISION);
11498
- return trimTrailingZeros(price);
11217
+ async function getFactoryTokenURI(params) {
11218
+ const { client, factoryAddress, version, tokenId } = params;
11219
+ const abi = version === "v3" ? panopticFactoryV3Abi : panopticFactoryV4Abi;
11220
+ return client.readContract({
11221
+ address: factoryAddress,
11222
+ abi,
11223
+ functionName: "tokenURI",
11224
+ args: [tokenId]
11225
+ });
11499
11226
  }
11500
11227
  /**
11501
- * Convert a tick to a human-readable price with decimal scaling.
11502
- * Uses the formula: price = 1.0001^tick * 10^(decimals0-decimals1)
11503
- *
11504
- * This adjusts for the different decimals of the two tokens in the pair.
11505
- *
11506
- * @param tick - The tick value
11507
- * @param decimals0 - Decimals of token0
11508
- * @param decimals1 - Decimals of token1
11509
- * @param precision - Number of decimal places to display
11510
- * @returns Formatted price string
11511
- *
11512
- * @example
11513
- * ```typescript
11514
- * // WETH/USDC pool (18 decimals / 6 decimals)
11515
- * // At tick ~200000, price is roughly $2000 per ETH
11516
- * tickToPriceDecimalScaled(200000n, 18n, 6n, 2n) // "2000.00" (approximately)
11517
- *
11518
- * // For token1/token0 price, swap the decimals
11519
- * tickToPriceDecimalScaled(200000n, 6n, 18n, 6n) // "0.000500" (approximately)
11520
- * ```
11228
+ * Get the owner of a PanopticFactory NFT.
11521
11229
  */
11522
- function tickToPriceDecimalScaled(tick, decimals0, decimals1, precision) {
11523
- const rawRatio = getRawPriceRatio(tick);
11524
- const { numerator, denominator } = adjustRatioForDecimals(rawRatio.numerator, rawRatio.denominator, decimals0, decimals1);
11525
- return formatRatio$3(numerator, denominator, precision);
11230
+ async function getFactoryOwnerOf(params) {
11231
+ const { client, factoryAddress, version, tokenId } = params;
11232
+ const abi = version === "v3" ? panopticFactoryV3Abi : panopticFactoryV4Abi;
11233
+ return client.readContract({
11234
+ address: factoryAddress,
11235
+ abi,
11236
+ functionName: "ownerOf",
11237
+ args: [tokenId]
11238
+ });
11526
11239
  }
11527
11240
  /**
11528
- * Convert a sqrtPriceX96 to a human-readable price with decimal scaling.
11529
- *
11530
- * Uses the formula: price = (sqrtPriceX96^2 / 2^192) * 10^(decimals0-decimals1)
11531
- *
11532
- * @param sqrtPriceX96 - The sqrt price in Q64.96 format
11533
- * @param decimals0 - Decimals of token0
11534
- * @param decimals1 - Decimals of token1
11535
- * @param precision - Number of decimal places to display
11536
- * @returns Formatted price string
11537
- *
11538
- * @example
11539
- * ```typescript
11540
- * sqrtPriceX96ToPriceDecimalScaled(2n ** 96n, 18n, 18n, 2n) // "1.00"
11541
- * ```
11241
+ * Construct NFT metadata for a pool via the factory contract.
11542
11242
  */
11543
- function sqrtPriceX96ToPriceDecimalScaled(sqrtPriceX96, decimals0, decimals1, precision) {
11544
- const rawRatio = getPriceRatioFromSqrtPriceX96(sqrtPriceX96);
11545
- const { numerator, denominator } = adjustRatioForDecimals(rawRatio.numerator, rawRatio.denominator, decimals0, decimals1);
11546
- return formatRatio$3(numerator, denominator, precision);
11547
- }
11548
- /**
11549
- * Convert a price to a tick value.
11550
- *
11551
- * @param price - The price string
11552
- * @param decimals0 - Decimals of token0
11553
- * @param decimals1 - Decimals of token1
11554
- * @returns The tick value (rounded to nearest integer)
11555
- *
11556
- * @example
11557
- * ```typescript
11558
- * // WETH/USDC: What tick for $2000 per ETH?
11559
- * priceToTick("2000", 18n, 6n) // ~200000n
11560
- *
11561
- * // Inverse: What tick for 0.0005 ETH per USDC?
11562
- * priceToTick("0.0005", 6n, 18n) // ~200000n
11563
- * ```
11564
- */
11565
- function priceToTick(price, decimals0, decimals1) {
11566
- const parsed = parseDecimalToFraction(price);
11567
- if (parsed.numerator <= 0n) throw new Error("Price must be positive");
11568
- let targetNumerator = parsed.numerator;
11569
- let targetDenominator = parsed.denominator;
11570
- const diff = decimals0 - decimals1;
11571
- if (diff > 0n) targetDenominator *= pow10(diff);
11572
- else if (diff < 0n) targetNumerator *= pow10(-diff);
11573
- let low = MIN_TICK;
11574
- let high = MAX_TICK;
11575
- while (low <= high) {
11576
- const mid = (low + high) / 2n;
11577
- const { numerator, denominator } = getRawPriceRatio(mid);
11578
- const cmp = compareRatios(numerator, denominator, targetNumerator, targetDenominator);
11579
- if (cmp === 0) return mid;
11580
- if (cmp < 0) low = mid + 1n;
11581
- else high = mid - 1n;
11582
- }
11583
- const floorTick = high;
11584
- const ceilTick = low;
11585
- if (floorTick < MIN_TICK) return MIN_TICK;
11586
- if (ceilTick > MAX_TICK) return MAX_TICK;
11587
- const floorRatio = getRawPriceRatio(floorTick);
11588
- const ceilRatio = getRawPriceRatio(ceilTick);
11589
- const floorDiffNumerator = absBigint(targetNumerator * floorRatio.denominator - floorRatio.numerator * targetDenominator);
11590
- const ceilDiffNumerator = absBigint(targetNumerator * ceilRatio.denominator - ceilRatio.numerator * targetDenominator);
11591
- const floorDiffDenominator = targetDenominator * floorRatio.denominator;
11592
- const ceilDiffDenominator = targetDenominator * ceilRatio.denominator;
11593
- return floorDiffNumerator * ceilDiffDenominator <= ceilDiffNumerator * floorDiffDenominator ? floorTick : ceilTick;
11594
- }
11595
- /**
11596
- * Convert a sqrtPriceX96 value to the nearest tick.
11597
- *
11598
- * @param sqrtPriceX96 - The sqrt price in Q64.96 format
11599
- * @returns The tick value (rounded to nearest integer)
11600
- *
11601
- * @example
11602
- * ```typescript
11603
- * const tick = sqrtPriceX96ToTick(2n ** 96n) // 0n
11604
- * ```
11605
- */
11606
- function sqrtPriceX96ToTick(sqrtPriceX96) {
11607
- if (sqrtPriceX96 <= 0n) throw new Error("Sqrt price must be positive");
11608
- if (sqrtPriceX96 < MIN_SQRT_PRICE_X96 || sqrtPriceX96 > MAX_SQRT_PRICE_X96) throw new RangeError("Sqrt price out of bounds");
11609
- let low = MIN_TICK;
11610
- let high = MAX_TICK;
11611
- while (low <= high) {
11612
- const mid = (low + high) / 2n;
11613
- const midSqrt = tickToSqrtPriceX96(mid);
11614
- if (midSqrt === sqrtPriceX96) return mid;
11615
- if (midSqrt < sqrtPriceX96) low = mid + 1n;
11616
- else high = mid - 1n;
11617
- }
11618
- const floorTick = high;
11619
- const ceilTick = low;
11620
- if (floorTick < MIN_TICK) return MIN_TICK;
11621
- if (ceilTick > MAX_TICK) return MAX_TICK;
11622
- const floorSqrt = tickToSqrtPriceX96(floorTick);
11623
- const ceilSqrt = tickToSqrtPriceX96(ceilTick);
11624
- const floorDiff = absBigint(sqrtPriceX96 - floorSqrt);
11625
- const ceilDiff = absBigint(ceilSqrt - sqrtPriceX96);
11626
- return floorDiff <= ceilDiff ? floorTick : ceilTick;
11243
+ async function getFactoryConstructMetadata(params) {
11244
+ const { client, factoryAddress, version, panopticPoolAddress, symbol0, symbol1, fee } = params;
11245
+ const abi = version === "v3" ? panopticFactoryV3Abi : panopticFactoryV4Abi;
11246
+ return client.readContract({
11247
+ address: factoryAddress,
11248
+ abi,
11249
+ functionName: "constructMetadata",
11250
+ args: [
11251
+ panopticPoolAddress,
11252
+ symbol0,
11253
+ symbol1,
11254
+ fee
11255
+ ]
11256
+ });
11627
11257
  }
11628
11258
  /**
11629
- * Format a tick value for display.
11630
- *
11631
- * @param tick - The tick value
11632
- * @returns Formatted tick string
11633
- *
11634
- * @example
11635
- * ```typescript
11636
- * formatTick(200000n) // "200000"
11637
- * formatTick(-50000n) // "-50000"
11638
- * ```
11259
+ * Get the PanopticPool address for a given pool and risk engine.
11639
11260
  */
11640
- function formatTick(tick) {
11641
- return tick.toString();
11261
+ async function getPanopticPoolAddress(params) {
11262
+ const { client, factoryAddress, riskEngine } = params;
11263
+ if (params.version === "v3") return client.readContract({
11264
+ address: factoryAddress,
11265
+ abi: panopticFactoryV3Abi,
11266
+ functionName: "getPanopticPool",
11267
+ args: [params.univ3pool, riskEngine]
11268
+ });
11269
+ return client.readContract({
11270
+ address: factoryAddress,
11271
+ abi: panopticFactoryV4Abi,
11272
+ functionName: "getPanopticPool",
11273
+ args: [{
11274
+ currency0: params.poolKey.currency0,
11275
+ currency1: params.poolKey.currency1,
11276
+ fee: Number(params.poolKey.fee),
11277
+ tickSpacing: Number(params.poolKey.tickSpacing),
11278
+ hooks: params.poolKey.hooks
11279
+ }, riskEngine]
11280
+ });
11642
11281
  }
11643
11282
  /**
11644
- * Get the price at a specific tick, returning both token0/token1 and token1/token0 prices.
11645
- *
11646
- * @param tick - The tick value
11647
- * @param decimals0 - Decimals of token0
11648
- * @param decimals1 - Decimals of token1
11649
- * @param precision - Number of decimal places to display
11650
- * @returns Object with both price directions
11651
- *
11652
- * @example
11653
- * ```typescript
11654
- * const prices = getPricesAtTick(200000n, 18n, 6n, 2n)
11655
- * // prices.token0PerToken1 = "0.00" (very small)
11656
- * // prices.token1PerToken0 = "2000.00" (USDC per ETH)
11657
- * ```
11283
+ * Mine for an optimal pool address salt with high rarity.
11658
11284
  */
11659
- function getPricesAtTick(tick, decimals0, decimals1, precision) {
11660
- const rawRatio = getRawPriceRatio(tick);
11661
- const adjustedRatio = adjustRatioForDecimals(rawRatio.numerator, rawRatio.denominator, decimals0, decimals1);
11285
+ async function minePoolAddress(params) {
11286
+ const { client, factoryAddress, deployerAddress, riskEngine, salt, loops, minTargetRarity } = params;
11287
+ let result;
11288
+ if (params.version === "v3") result = await client.readContract({
11289
+ address: factoryAddress,
11290
+ abi: panopticFactoryV3Abi,
11291
+ functionName: "minePoolAddress",
11292
+ args: [
11293
+ deployerAddress,
11294
+ params.v3Pool,
11295
+ riskEngine,
11296
+ salt,
11297
+ loops,
11298
+ minTargetRarity
11299
+ ]
11300
+ });
11301
+ else result = await client.readContract({
11302
+ address: factoryAddress,
11303
+ abi: panopticFactoryV4Abi,
11304
+ functionName: "minePoolAddress",
11305
+ args: [
11306
+ deployerAddress,
11307
+ {
11308
+ currency0: params.poolKey.currency0,
11309
+ currency1: params.poolKey.currency1,
11310
+ fee: Number(params.poolKey.fee),
11311
+ tickSpacing: Number(params.poolKey.tickSpacing),
11312
+ hooks: params.poolKey.hooks
11313
+ },
11314
+ riskEngine,
11315
+ salt,
11316
+ loops,
11317
+ minTargetRarity
11318
+ ]
11319
+ });
11662
11320
  return {
11663
- token0PerToken1: formatRatio$3(adjustedRatio.denominator, adjustedRatio.numerator, precision),
11664
- token1PerToken0: formatRatio$3(adjustedRatio.numerator, adjustedRatio.denominator, precision)
11321
+ bestSalt: BigInt(result[0]),
11322
+ highestRarity: result[1]
11665
11323
  };
11666
11324
  }
11667
11325
  /**
11668
- * Format a tick range for display.
11669
- *
11670
- * @param tickLower - Lower tick
11671
- * @param tickUpper - Upper tick
11672
- * @returns Formatted tick range string
11326
+ * Simulate a pool deployment to get the predicted pool address.
11673
11327
  *
11674
- * @example
11675
- * ```typescript
11676
- * formatTickRange(-50000n, 200000n) // "-50000 - 200000"
11677
- * ```
11328
+ * Uses `simulateContract` on `deployNewPool` — the return value is the new pool address
11329
+ * without actually executing the transaction.
11678
11330
  */
11679
- function formatTickRange(tickLower, tickUpper) {
11680
- return `${tickLower} - ${tickUpper}`;
11331
+ async function simulateDeployNewPool(params) {
11332
+ const { client, factoryAddress, account, riskEngine, salt } = params;
11333
+ if (params.version === "v3") {
11334
+ const { result: result$1 } = await client.simulateContract({
11335
+ address: factoryAddress,
11336
+ abi: panopticFactoryV3Abi,
11337
+ functionName: "deployNewPool",
11338
+ args: [
11339
+ params.token0,
11340
+ params.token1,
11341
+ Number(params.fee),
11342
+ riskEngine,
11343
+ salt
11344
+ ],
11345
+ account
11346
+ });
11347
+ return result$1;
11348
+ }
11349
+ const { result } = await client.simulateContract({
11350
+ address: factoryAddress,
11351
+ abi: panopticFactoryV4Abi,
11352
+ functionName: "deployNewPool",
11353
+ args: [
11354
+ {
11355
+ currency0: params.poolKey.currency0,
11356
+ currency1: params.poolKey.currency1,
11357
+ fee: Number(params.poolKey.fee),
11358
+ tickSpacing: Number(params.poolKey.tickSpacing),
11359
+ hooks: params.poolKey.hooks
11360
+ },
11361
+ riskEngine,
11362
+ salt
11363
+ ],
11364
+ account
11365
+ });
11366
+ return result;
11681
11367
  }
11682
11368
  /**
11683
- * Format a price range for display.
11684
- *
11685
- * @param tickLower - Lower tick
11686
- * @param tickUpper - Upper tick
11687
- * @param decimals0 - Decimals of token0
11688
- * @param decimals1 - Decimals of token1
11689
- * @param precision - Number of decimal places to display
11690
- * @returns Formatted price range string
11369
+ * Resolve an SFPM poolId to its PanopticPool address.
11691
11370
  *
11692
- * @example
11693
- * ```typescript
11694
- * formatPriceRange(0n, 0n, 18n, 18n, 2n) // "1.00 - 1.00"
11695
- * ```
11371
+ * Chains two on-chain lookups:
11372
+ * 1. SFPM: poolId → Uniswap pool address (V3) or pool key (V4)
11373
+ * 2. Factory: Uniswap pool + riskEngine → PanopticPool address
11696
11374
  */
11697
- function formatPriceRange(tickLower, tickUpper, decimals0, decimals1, precision) {
11698
- const lower = tickToPriceDecimalScaled(tickLower, decimals0, decimals1, precision);
11699
- const upper = tickToPriceDecimalScaled(tickUpper, decimals0, decimals1, precision);
11700
- return `${lower} - ${upper}`;
11701
- }
11702
- /**
11703
- * Calculate the tick spacing for a given fee tier.
11704
- *
11705
- * @param feeBps - Fee in basis points (e.g., 500n for 0.05%)
11706
- * @returns Tick spacing
11707
- *
11708
- * @example
11709
- * ```typescript
11710
- * getTickSpacing(100n) // 1n (0.01% fee tier)
11711
- * getTickSpacing(500n) // 10n (0.05% fee tier)
11712
- * getTickSpacing(3000n) // 60n (0.30% fee tier)
11713
- * getTickSpacing(10000n) // 200n (1.00% fee tier)
11714
- * ```
11375
+ async function getPanopticPoolFromPoolId(params) {
11376
+ const { client, sfpmAddress, factoryAddress, riskEngine, poolId, version } = params;
11377
+ if (version === "v3") {
11378
+ const univ3pool = await getUniswapV3PoolFromId({
11379
+ client,
11380
+ sfpmAddress,
11381
+ poolId
11382
+ });
11383
+ return getPanopticPoolAddress({
11384
+ version: "v3",
11385
+ client,
11386
+ factoryAddress,
11387
+ univ3pool,
11388
+ riskEngine
11389
+ });
11390
+ }
11391
+ const poolKey = await getUniswapV4PoolKeyFromId({
11392
+ client,
11393
+ sfpmAddress,
11394
+ poolId
11395
+ });
11396
+ return getPanopticPoolAddress({
11397
+ version: "v4",
11398
+ client,
11399
+ factoryAddress,
11400
+ poolKey,
11401
+ riskEngine
11402
+ });
11403
+ }
11404
+ /**
11405
+ * Resolve an SFPM poolId to its PanopticPool address without knowing the version.
11406
+ *
11407
+ * Tries both V3 and V4 lookups in parallel. The factory returns `address(0)` for
11408
+ * non-existent pools, so the non-zero result identifies the correct version.
11409
+ *
11410
+ * At least one of `v3` or `v4` must be provided.
11411
+ *
11412
+ * @throws {PanopticValidationError} If no version config is provided or neither resolves.
11715
11413
  */
11716
- function getTickSpacing(feeBps) {
11717
- switch (feeBps) {
11718
- case 100n: return 1n;
11719
- case 500n: return 10n;
11720
- case 3000n: return 60n;
11721
- case 10000n: return 200n;
11722
- default: {
11723
- const spacing = feeBps / 50n;
11724
- return spacing > 1n ? spacing : 1n;
11414
+ async function resolvePanopticPoolFromPoolId(params) {
11415
+ const { client, poolId, riskEngine, v3, v4 } = params;
11416
+ if (!v3 && !v4) throw new Error("At least one of v3 or v4 must be provided");
11417
+ const isNotFoundError = (err) => {
11418
+ if (typeof err === "object" && err !== null && "name" in err) {
11419
+ const name = err.name;
11420
+ return name === "ContractFunctionExecutionError" || name === "ContractFunctionRevertedError";
11725
11421
  }
11422
+ return false;
11423
+ };
11424
+ const results = await Promise.all([v3 ? getPanopticPoolFromPoolId({
11425
+ version: "v3",
11426
+ client,
11427
+ sfpmAddress: v3.sfpmAddress,
11428
+ factoryAddress: v3.factoryAddress,
11429
+ riskEngine,
11430
+ poolId
11431
+ }).catch((err) => {
11432
+ if (isNotFoundError(err)) return zeroAddress;
11433
+ throw err;
11434
+ }) : Promise.resolve(zeroAddress), v4 ? getPanopticPoolFromPoolId({
11435
+ version: "v4",
11436
+ client,
11437
+ sfpmAddress: v4.sfpmAddress,
11438
+ factoryAddress: v4.factoryAddress,
11439
+ riskEngine,
11440
+ poolId
11441
+ }).catch((err) => {
11442
+ if (isNotFoundError(err)) return zeroAddress;
11443
+ throw err;
11444
+ }) : Promise.resolve(zeroAddress)]);
11445
+ const [v3Result, v4Result] = results;
11446
+ if (v3Result !== zeroAddress) return {
11447
+ panopticPoolAddress: v3Result,
11448
+ version: "v3"
11449
+ };
11450
+ if (v4Result !== zeroAddress) return {
11451
+ panopticPoolAddress: v4Result,
11452
+ version: "v4"
11453
+ };
11454
+ throw new Error(`No PanopticPool found for poolId ${poolId}`);
11455
+ }
11456
+
11457
+ //#endregion
11458
+ //#region src/panoptic/v2/reads/minePoolAddressLocal.ts
11459
+ /**
11460
+ * keccak256 of the CREATE3 proxy initcode used by ClonesWithImmutableArgs.
11461
+ * Source: packages/panoptic-v2-core/lib/clones-with-immutable-args/src/ClonesWithImmutableArgs.sol
11462
+ */
11463
+ const CREATE3_PROXY_BYTECODE_HASH = "0x21c35dbe1b344a2488cf3321d6ce542f8e9f305544ff09e4993a62319a497c1f";
11464
+ const MASK_80 = (1n << 80n) - 1n;
11465
+ const MASK_40 = (1n << 40n) - 1n;
11466
+ const MASK_96 = (1n << 96n) - 1n;
11467
+ /** Encode a BigInt as a big-endian fixed-length byte array. */
11468
+ function bigintToBytes(value, byteLength) {
11469
+ const bytes = new Uint8Array(byteLength);
11470
+ let v = value;
11471
+ for (let i = byteLength - 1; i >= 0; i--) {
11472
+ bytes[i] = Number(v & 0xffn);
11473
+ v >>= 8n;
11474
+ }
11475
+ return bytes;
11476
+ }
11477
+ /** Parse a 0x-prefixed address into 20 bytes. */
11478
+ function addressToBytes(addr) {
11479
+ const hex = addr.slice(2).padStart(40, "0");
11480
+ const bytes = new Uint8Array(20);
11481
+ for (let i = 0; i < 20; i++) bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
11482
+ return bytes;
11483
+ }
11484
+ /** Parse a 0x-prefixed 32-byte hex string into bytes. */
11485
+ function hex32ToBytes(hex) {
11486
+ const h = hex.slice(2).padStart(64, "0");
11487
+ const bytes = new Uint8Array(32);
11488
+ for (let i = 0; i < 32; i++) bytes[i] = parseInt(h.slice(i * 2, i * 2 + 2), 16);
11489
+ return bytes;
11490
+ }
11491
+ /**
11492
+ * Compute the CREATE3 deployed address for a given factory and packed salt.
11493
+ *
11494
+ * Mirrors `ClonesWithImmutableArgs.addressOfClone3(salt)` (with `address(this)` = factory):
11495
+ * proxy = CREATE2(factory, salt, PROXY_BYTECODE_HASH)
11496
+ * deployed = CREATE1(proxy, nonce=1)
11497
+ *
11498
+ * Returns the deployed address as a uint160 BigInt.
11499
+ */
11500
+ function addressOfClone3(factory, salt) {
11501
+ const saltBytes = bigintToBytes(salt, 32);
11502
+ const create2Input = new Uint8Array(85);
11503
+ create2Input[0] = 255;
11504
+ create2Input.set(addressToBytes(factory), 1);
11505
+ create2Input.set(saltBytes, 21);
11506
+ create2Input.set(hex32ToBytes(CREATE3_PROXY_BYTECODE_HASH), 53);
11507
+ const proxyHash = keccak256(create2Input, "bytes");
11508
+ const proxyAddress = proxyHash.slice(12);
11509
+ const create1Input = new Uint8Array(23);
11510
+ create1Input[0] = 214;
11511
+ create1Input[1] = 148;
11512
+ create1Input.set(proxyAddress, 2);
11513
+ create1Input[22] = 1;
11514
+ const deployedHash = keccak256(create1Input, "bytes");
11515
+ let addr = 0n;
11516
+ for (let i = 12; i < 32; i++) addr = addr << 8n | BigInt(deployedHash[i]);
11517
+ return addr;
11518
+ }
11519
+ /**
11520
+ * Count leading hex-zero characters in a 160-bit address value.
11521
+ *
11522
+ * Mirrors `PanopticMath.numberOfLeadingHexZeros(addr)`.
11523
+ * Returns 40 for the zero address.
11524
+ */
11525
+ function numberOfLeadingHexZeros(addrInt) {
11526
+ if (addrInt === 0n) return 40;
11527
+ let x = addrInt;
11528
+ let r = 0;
11529
+ if (x >= 0x100000000000000000000000000000000n) {
11530
+ x >>= 128n;
11531
+ r += 32;
11532
+ }
11533
+ if (x >= 0x10000000000000000n) {
11534
+ x >>= 64n;
11535
+ r += 16;
11536
+ }
11537
+ if (x >= 0x100000000n) {
11538
+ x >>= 32n;
11539
+ r += 8;
11540
+ }
11541
+ if (x >= 0x10000n) {
11542
+ x >>= 16n;
11543
+ r += 4;
11544
+ }
11545
+ if (x >= 0x100n) {
11546
+ x >>= 8n;
11547
+ r += 2;
11548
+ }
11549
+ if (x >= 0x10n) r += 1;
11550
+ return 39 - r;
11551
+ }
11552
+ /**
11553
+ * Construct the bytes32 CREATE3 salt for PanopticFactoryV3.
11554
+ *
11555
+ * Mirrors:
11556
+ * bytes32(abi.encodePacked(
11557
+ * uint80(uint160(deployerAddress) >> 80), // bits [159:80] of deployer → 10 bytes
11558
+ * uint40(uint160(v3Pool) >> 120), // bits [159:120] of v3Pool → 5 bytes
11559
+ * uint40(uint160(riskEngine) >> 120), // bits [159:120] of riskEngine→ 5 bytes
11560
+ * salt // uint96 → 12 bytes
11561
+ * ))
11562
+ */
11563
+ function computeSaltPrefixV3(deployerAddress, v3Pool, riskEngine) {
11564
+ const deployer80 = BigInt(deployerAddress) >> 80n & MASK_80;
11565
+ const pool40 = BigInt(v3Pool) >> 120n & MASK_40;
11566
+ const risk40 = BigInt(riskEngine) >> 120n & MASK_40;
11567
+ return deployer80 << 176n | pool40 << 136n | risk40 << 96n;
11568
+ }
11569
+ /**
11570
+ * Compute the Uniswap V4 PoolId for a PoolKey.
11571
+ *
11572
+ * Mirrors `PoolId.toId(key)` = keccak256 of the ABI-encoded PoolKey struct
11573
+ * (5 fields × 32 bytes = 160 bytes).
11574
+ */
11575
+ function computePoolIdV4(poolKey) {
11576
+ const fee = Number(poolKey.fee);
11577
+ if (!Number.isInteger(fee) || fee < 0 || fee >= 2 ** 24) throw new PanopticValidationError(`fee out of uint24 range: ${fee}`);
11578
+ const tickSpacing = Number(poolKey.tickSpacing);
11579
+ if (!Number.isInteger(tickSpacing) || tickSpacing < -(2 ** 23) || tickSpacing > 2 ** 23 - 1) throw new PanopticValidationError(`tickSpacing out of int24 range: ${tickSpacing}`);
11580
+ const encoded = encodeAbiParameters([
11581
+ { type: "address" },
11582
+ { type: "address" },
11583
+ { type: "uint24" },
11584
+ { type: "int24" },
11585
+ { type: "address" }
11586
+ ], [
11587
+ poolKey.currency0,
11588
+ poolKey.currency1,
11589
+ Number(poolKey.fee),
11590
+ Number(poolKey.tickSpacing),
11591
+ poolKey.hooks
11592
+ ]);
11593
+ return BigInt(keccak256(encoded));
11594
+ }
11595
+ /**
11596
+ * Construct the bytes32 CREATE3 salt for PanopticFactoryV4.
11597
+ *
11598
+ * Mirrors:
11599
+ * bytes32(abi.encodePacked(
11600
+ * uint80(uint160(deployerAddress) >> 80),
11601
+ * uint40(uint256(PoolId.unwrap(key.toId())) >> 120), // bits [159:120] of poolId
11602
+ * uint40(uint160(riskEngine) >> 120),
11603
+ * salt
11604
+ * ))
11605
+ */
11606
+ function computeSaltPrefixV4(deployerAddress, poolKey, riskEngine) {
11607
+ const deployer80 = BigInt(deployerAddress) >> 80n & MASK_80;
11608
+ const poolId40 = computePoolIdV4(poolKey) >> 120n & MASK_40;
11609
+ const risk40 = BigInt(riskEngine) >> 120n & MASK_40;
11610
+ return deployer80 << 176n | poolId40 << 136n | risk40 << 96n;
11611
+ }
11612
+ /** Number of iterations per chunk before yielding back to the event loop. */
11613
+ const CHUNK_SIZE = 5000n;
11614
+ /**
11615
+ * Async version of {@link minePoolAddressLocal} that yields to the event loop
11616
+ * between chunks of iterations, preventing the browser UI from freezing.
11617
+ *
11618
+ * @param params - Mining parameters (versioned: 'v3' or 'v4'). No `client` required.
11619
+ * @returns The best salt found and its rarity (number of leading hex zeros).
11620
+ */
11621
+ async function minePoolAddressLocalAsync(params) {
11622
+ const { factoryAddress, deployerAddress, riskEngine, salt, loops, minTargetRarity } = params;
11623
+ let bestSalt = salt;
11624
+ let highestRarity = 0n;
11625
+ const maxSalt = salt + loops;
11626
+ const saltPrefix = params.version === "v3" ? computeSaltPrefixV3(deployerAddress, params.v3Pool, riskEngine) : computeSaltPrefixV4(deployerAddress, params.poolKey, riskEngine);
11627
+ let currentSalt = salt;
11628
+ while (currentSalt < maxSalt) {
11629
+ const chunkEnd = currentSalt + CHUNK_SIZE < maxSalt ? currentSalt + CHUNK_SIZE : maxSalt;
11630
+ let done = false;
11631
+ for (; currentSalt < chunkEnd; currentSalt++) {
11632
+ const newSalt = saltPrefix | currentSalt & MASK_96;
11633
+ const addrInt = addressOfClone3(factoryAddress, newSalt);
11634
+ const rarity = BigInt(numberOfLeadingHexZeros(addrInt));
11635
+ if (rarity > highestRarity) {
11636
+ highestRarity = rarity;
11637
+ bestSalt = currentSalt;
11638
+ }
11639
+ if (rarity >= minTargetRarity) {
11640
+ highestRarity = rarity;
11641
+ bestSalt = currentSalt;
11642
+ done = true;
11643
+ break;
11644
+ }
11645
+ }
11646
+ if (done) break;
11647
+ await new Promise((resolve) => setTimeout(resolve, 0));
11648
+ }
11649
+ return {
11650
+ bestSalt,
11651
+ highestRarity
11652
+ };
11653
+ }
11654
+
11655
+ //#endregion
11656
+ //#region src/panoptic/v2/abis/stateView.ts
11657
+ /**
11658
+ * Minimal Uniswap V4 StateView ABI for fee growth reads.
11659
+ * Only includes functions needed by streamia history.
11660
+ */
11661
+ const stateViewAbi = [
11662
+ {
11663
+ inputs: [{
11664
+ internalType: "PoolId",
11665
+ name: "poolId",
11666
+ type: "bytes32"
11667
+ }],
11668
+ name: "getSlot0",
11669
+ outputs: [
11670
+ {
11671
+ internalType: "uint160",
11672
+ name: "sqrtPriceX96",
11673
+ type: "uint160"
11674
+ },
11675
+ {
11676
+ internalType: "int24",
11677
+ name: "tick",
11678
+ type: "int24"
11679
+ },
11680
+ {
11681
+ internalType: "uint24",
11682
+ name: "protocolFee",
11683
+ type: "uint24"
11684
+ },
11685
+ {
11686
+ internalType: "uint24",
11687
+ name: "lpFee",
11688
+ type: "uint24"
11689
+ }
11690
+ ],
11691
+ stateMutability: "view",
11692
+ type: "function"
11693
+ },
11694
+ {
11695
+ inputs: [{
11696
+ internalType: "PoolId",
11697
+ name: "poolId",
11698
+ type: "bytes32"
11699
+ }],
11700
+ name: "getFeeGrowthGlobals",
11701
+ outputs: [{
11702
+ internalType: "uint256",
11703
+ name: "feeGrowthGlobal0",
11704
+ type: "uint256"
11705
+ }, {
11706
+ internalType: "uint256",
11707
+ name: "feeGrowthGlobal1",
11708
+ type: "uint256"
11709
+ }],
11710
+ stateMutability: "view",
11711
+ type: "function"
11712
+ },
11713
+ {
11714
+ inputs: [{
11715
+ internalType: "PoolId",
11716
+ name: "poolId",
11717
+ type: "bytes32"
11718
+ }, {
11719
+ internalType: "int24",
11720
+ name: "tick",
11721
+ type: "int24"
11722
+ }],
11723
+ name: "getTickInfo",
11724
+ outputs: [
11725
+ {
11726
+ internalType: "uint128",
11727
+ name: "liquidityGross",
11728
+ type: "uint128"
11729
+ },
11730
+ {
11731
+ internalType: "int128",
11732
+ name: "liquidityNet",
11733
+ type: "int128"
11734
+ },
11735
+ {
11736
+ internalType: "uint256",
11737
+ name: "feeGrowthOutside0X128",
11738
+ type: "uint256"
11739
+ },
11740
+ {
11741
+ internalType: "uint256",
11742
+ name: "feeGrowthOutside1X128",
11743
+ type: "uint256"
11744
+ }
11745
+ ],
11746
+ stateMutability: "view",
11747
+ type: "function"
11748
+ },
11749
+ {
11750
+ inputs: [{
11751
+ internalType: "PoolId",
11752
+ name: "poolId",
11753
+ type: "bytes32"
11754
+ }],
11755
+ name: "getLiquidity",
11756
+ outputs: [{
11757
+ internalType: "uint128",
11758
+ name: "liquidity",
11759
+ type: "uint128"
11760
+ }],
11761
+ stateMutability: "view",
11762
+ type: "function"
11763
+ }
11764
+ ];
11765
+
11766
+ //#endregion
11767
+ //#region src/panoptic/v2/abis/uniswapV3Pool.ts
11768
+ /**
11769
+ * Minimal Uniswap V3 Pool ABI for fee growth reads.
11770
+ * Only includes functions needed by streamia history.
11771
+ */
11772
+ const uniswapV3PoolAbi = [
11773
+ {
11774
+ inputs: [],
11775
+ name: "slot0",
11776
+ outputs: [
11777
+ {
11778
+ internalType: "uint160",
11779
+ name: "sqrtPriceX96",
11780
+ type: "uint160"
11781
+ },
11782
+ {
11783
+ internalType: "int24",
11784
+ name: "tick",
11785
+ type: "int24"
11786
+ },
11787
+ {
11788
+ internalType: "uint16",
11789
+ name: "observationIndex",
11790
+ type: "uint16"
11791
+ },
11792
+ {
11793
+ internalType: "uint16",
11794
+ name: "observationCardinality",
11795
+ type: "uint16"
11796
+ },
11797
+ {
11798
+ internalType: "uint16",
11799
+ name: "observationCardinalityNext",
11800
+ type: "uint16"
11801
+ },
11802
+ {
11803
+ internalType: "uint8",
11804
+ name: "feeProtocol",
11805
+ type: "uint8"
11806
+ },
11807
+ {
11808
+ internalType: "bool",
11809
+ name: "unlocked",
11810
+ type: "bool"
11811
+ }
11812
+ ],
11813
+ stateMutability: "view",
11814
+ type: "function"
11815
+ },
11816
+ {
11817
+ inputs: [],
11818
+ name: "feeGrowthGlobal0X128",
11819
+ outputs: [{
11820
+ internalType: "uint256",
11821
+ name: "",
11822
+ type: "uint256"
11823
+ }],
11824
+ stateMutability: "view",
11825
+ type: "function"
11826
+ },
11827
+ {
11828
+ inputs: [],
11829
+ name: "feeGrowthGlobal1X128",
11830
+ outputs: [{
11831
+ internalType: "uint256",
11832
+ name: "",
11833
+ type: "uint256"
11834
+ }],
11835
+ stateMutability: "view",
11836
+ type: "function"
11837
+ },
11838
+ {
11839
+ inputs: [{
11840
+ internalType: "int24",
11841
+ name: "",
11842
+ type: "int24"
11843
+ }],
11844
+ name: "ticks",
11845
+ outputs: [
11846
+ {
11847
+ internalType: "uint128",
11848
+ name: "liquidityGross",
11849
+ type: "uint128"
11850
+ },
11851
+ {
11852
+ internalType: "int128",
11853
+ name: "liquidityNet",
11854
+ type: "int128"
11855
+ },
11856
+ {
11857
+ internalType: "uint256",
11858
+ name: "feeGrowthOutside0X128",
11859
+ type: "uint256"
11860
+ },
11861
+ {
11862
+ internalType: "uint256",
11863
+ name: "feeGrowthOutside1X128",
11864
+ type: "uint256"
11865
+ },
11866
+ {
11867
+ internalType: "int56",
11868
+ name: "tickCumulativeOutside",
11869
+ type: "int56"
11870
+ },
11871
+ {
11872
+ internalType: "uint160",
11873
+ name: "secondsPerLiquidityOutsideX128",
11874
+ type: "uint160"
11875
+ },
11876
+ {
11877
+ internalType: "uint32",
11878
+ name: "secondsOutside",
11879
+ type: "uint32"
11880
+ },
11881
+ {
11882
+ internalType: "bool",
11883
+ name: "initialized",
11884
+ type: "bool"
11885
+ }
11886
+ ],
11887
+ stateMutability: "view",
11888
+ type: "function"
11889
+ },
11890
+ {
11891
+ inputs: [],
11892
+ name: "liquidity",
11893
+ outputs: [{
11894
+ internalType: "uint128",
11895
+ name: "",
11896
+ type: "uint128"
11897
+ }],
11898
+ stateMutability: "view",
11899
+ type: "function"
11900
+ },
11901
+ {
11902
+ inputs: [],
11903
+ name: "fee",
11904
+ outputs: [{
11905
+ internalType: "uint24",
11906
+ name: "",
11907
+ type: "uint24"
11908
+ }],
11909
+ stateMutability: "view",
11910
+ type: "function"
11911
+ },
11912
+ {
11913
+ inputs: [],
11914
+ name: "token0",
11915
+ outputs: [{
11916
+ internalType: "address",
11917
+ name: "",
11918
+ type: "address"
11919
+ }],
11920
+ stateMutability: "view",
11921
+ type: "function"
11922
+ },
11923
+ {
11924
+ inputs: [],
11925
+ name: "token1",
11926
+ outputs: [{
11927
+ internalType: "address",
11928
+ name: "",
11929
+ type: "address"
11930
+ }],
11931
+ stateMutability: "view",
11932
+ type: "function"
11933
+ },
11934
+ {
11935
+ inputs: [],
11936
+ name: "tickSpacing",
11937
+ outputs: [{
11938
+ internalType: "int24",
11939
+ name: "",
11940
+ type: "int24"
11941
+ }],
11942
+ stateMutability: "view",
11943
+ type: "function"
11726
11944
  }
11727
- }
11728
- /**
11729
- * Round a tick to the nearest valid tick for a given tick spacing.
11730
- *
11731
- * @param tick - The tick to round
11732
- * @param tickSpacing - The tick spacing
11733
- * @returns Rounded tick
11734
- *
11735
- * @example
11736
- * ```typescript
11737
- * roundToTickSpacing(12345n, 10n) // 12340n
11738
- * roundToTickSpacing(12345n, 60n) // 12360n
11739
- * roundToTickSpacing(-12345n, 10n) // -12350n
11740
- * ```
11741
- */
11742
- function roundToTickSpacing(tick, tickSpacing) {
11743
- const remainder = tick % tickSpacing;
11744
- if (remainder === 0n) return tick;
11745
- if (tick >= 0n) return remainder >= tickSpacing / 2n ? tick + (tickSpacing - remainder) : tick - remainder;
11746
- const absRemainder = -remainder;
11747
- return absRemainder >= tickSpacing / 2n ? tick - (tickSpacing - absRemainder) : tick + absRemainder;
11748
- }
11749
- /**
11750
- * Compute slippage-bounded tick limits around the current tick.
11751
- *
11752
- * 1 tick ≈ 1 basis point (0.01 %) of price change, so a `toleranceBps`
11753
- * of 500 allows roughly 5 % price movement. The result is clamped to
11754
- * the protocol's `[MIN_TICK, MAX_TICK]` range.
11755
- *
11756
- * Useful for setting `tickLimitLow` / `tickLimitHigh` on `openPosition`
11757
- * and `closePosition` to protect against MEV sandwiches and volatile
11758
- * tick moves.
11759
- *
11760
- * @param currentTick - The current pool tick (must be within [MIN_TICK, MAX_TICK]).
11761
- * @param toleranceBps - Slippage tolerance in basis points (≈ ticks). Must be non-negative.
11762
- * @returns Clamped `{ low, high }` tick limits.
11763
- * @throws {RangeError} If `toleranceBps` is negative or `currentTick` is out of bounds.
11764
- *
11765
- * @example
11766
- * ```typescript
11767
- * const { low, high } = tickLimits(200_000n, 500n)
11768
- * // low = 199_500n
11769
- * // high = 200_500n
11770
- *
11771
- * await openPosition({ ..., tickLimitLow: low, tickLimitHigh: high })
11772
- * ```
11773
- */
11774
- function tickLimits(currentTick, toleranceBps) {
11775
- if (toleranceBps < 0n) throw new RangeError(`toleranceBps must be non-negative, got ${toleranceBps}`);
11776
- if (currentTick < MIN_TICK || currentTick > MAX_TICK) throw new RangeError(`currentTick ${currentTick} is out of bounds [${MIN_TICK}, ${MAX_TICK}]`);
11777
- const rawLow = currentTick - toleranceBps;
11778
- const rawHigh = currentTick + toleranceBps;
11779
- const low = rawLow < MIN_TICK ? MIN_TICK : rawLow;
11780
- const high = rawHigh > MAX_TICK ? MAX_TICK : rawHigh;
11781
- if (low > high) throw new RangeError(`Computed tick limits are inverted: low ${low} > high ${high} (currentTick=${currentTick}, toleranceBps=${toleranceBps})`);
11782
- return {
11783
- low,
11784
- high
11785
- };
11786
- }
11945
+ ];
11787
11946
 
11788
11947
  //#endregion
11789
11948
  //#region src/panoptic/v2/reads/mintBuffer.ts
@@ -14789,7 +14948,7 @@ async function readBlockAndAggregate({ client, calls, blockNumber }) {
14789
14948
 
14790
14949
  //#endregion
14791
14950
  //#region src/panoptic/v2/reads/checks.ts
14792
- const FP96$1 = 1n << 96n;
14951
+ const FP96$2 = 1n << 96n;
14793
14952
  const Q128$4 = 1n << 128n;
14794
14953
  function convert0to1$3(amount, sqrtPriceX96) {
14795
14954
  if (sqrtPriceX96 < Q128$4) return amount * sqrtPriceX96 * sqrtPriceX96 >> 192n;
@@ -14940,7 +15099,7 @@ async function isLiquidatable(params) {
14940
15099
  const requiredMargin1 = required1Native + convert0to1$3(required0Native, sqrtPriceX96);
14941
15100
  const marginShortfall0 = requiredMargin0 - currentMargin0;
14942
15101
  const marginShortfall1 = requiredMargin1 - currentMargin1;
14943
- const denominatedInToken = sqrtPriceX96 < FP96$1 ? 0n : 1n;
15102
+ const denominatedInToken = sqrtPriceX96 < FP96$2 ? 0n : 1n;
14944
15103
  const isLiquidatableResult = denominatedInToken === 0n ? marginShortfall0 > 0n : marginShortfall1 > 0n;
14945
15104
  return {
14946
15105
  isLiquidatable: isLiquidatableResult,
@@ -16954,7 +17113,7 @@ async function simulateDispatch(params) {
16954
17113
 
16955
17114
  //#endregion
16956
17115
  //#region src/panoptic/v2/simulations/tokenShortfallRecovery.ts
16957
- const BPS_DENOMINATOR$4 = 10000n;
17116
+ const BPS_DENOMINATOR$5 = 10000n;
16958
17117
  const MAX_RECOVERY_QUOTE_ATTEMPTS = 8;
16959
17118
  /**
16960
17119
  * Wrap a dispatch with a temporary credit leg that sources the shortfall.
@@ -17004,7 +17163,7 @@ function getNotEnoughTokensError(error) {
17004
17163
  }
17005
17164
  function maximumAmountIn(estimatedAmountIn, slippageBps) {
17006
17165
  if (slippageBps < 0n) throw new PanopticError("slippageBps must be non-negative");
17007
- return (estimatedAmountIn * (BPS_DENOMINATOR$4 + slippageBps) + BPS_DENOMINATOR$4 - 1n) / BPS_DENOMINATOR$4;
17166
+ return (estimatedAmountIn * (BPS_DENOMINATOR$5 + slippageBps) + BPS_DENOMINATOR$5 - 1n) / BPS_DENOMINATOR$5;
17008
17167
  }
17009
17168
  function ceilDiv(numerator, denominator) {
17010
17169
  return (numerator + denominator - 1n) / denominator;
@@ -17032,10 +17191,10 @@ async function quoteTokenShortfallRecovery(params) {
17032
17191
  reason: "invalid-shortfall",
17033
17192
  detail: `requested=${shortfallError.assetsRequested} <= balance=${shortfallError.assetBalance}`
17034
17193
  };
17035
- if (params.slippageBps < 0n || params.slippageBps > BPS_DENOMINATOR$4) return {
17194
+ if (params.slippageBps < 0n || params.slippageBps > BPS_DENOMINATOR$5) return {
17036
17195
  available: false,
17037
17196
  reason: "invalid-slippage",
17038
- detail: `slippageBps=${params.slippageBps} is outside [0, ${BPS_DENOMINATOR$4}]`
17197
+ detail: `slippageBps=${params.slippageBps} is outside [0, ${BPS_DENOMINATOR$5}]`
17039
17198
  };
17040
17199
  const targetBlockNumber = params.blockNumber ?? await params.client.getBlockNumber();
17041
17200
  const pool = await getPool({
@@ -17183,8 +17342,9 @@ async function quoteTokenShortfallRecovery(params) {
17183
17342
  error: new PanopticError("Could not size the prefixed recovery swap")
17184
17343
  };
17185
17344
  };
17345
+ let creditOutSize = amountOut;
17186
17346
  for (let attempt = 0; attempt < MAX_RECOVERY_QUOTE_ATTEMPTS; attempt++) {
17187
- const credit = buildUniqueCredit(pool.poolId, tokenOutIndex, tokenOutIndex, pool.currentTick, pool.tickSpacing, collisionIds, amountOut);
17347
+ const credit = buildUniqueCredit(pool.poolId, tokenOutIndex, tokenOutIndex, pool.currentTick, pool.tickSpacing, collisionIds, creditOutSize);
17188
17348
  const recoveredDispatch = buildTokenShortfallRecoveryDispatch({
17189
17349
  dispatch: params.dispatch,
17190
17350
  creditTokenId: credit.tokenId,
@@ -17228,10 +17388,14 @@ async function quoteTokenShortfallRecovery(params) {
17228
17388
  const maxAmountIn = maximumAmountIn(estimatedAmountIn, params.slippageBps);
17229
17389
  const swapOutput = getOutputAmount(swapSimulation.tokenFlow, tokenOutIndex);
17230
17390
  const sourceBalance = getBalanceBefore(swapSimulation.tokenFlow, tokenInIndex);
17231
- if (swapOutput < amountOut || sourceBalance < maxAmountIn) return {
17391
+ if (swapOutput < amountOut) {
17392
+ creditOutSize = swapOutput > 0n ? ceilDiv(creditOutSize * amountOut, swapOutput) + 1n : creditOutSize * 2n;
17393
+ continue;
17394
+ }
17395
+ if (sourceBalance < maxAmountIn) return {
17232
17396
  available: false,
17233
17397
  reason: "swap-unavailable",
17234
- detail: swapOutput < amountOut ? `swap output ${swapOutput} < required ${amountOut}` : `source balance ${sourceBalance} < maximumAmountIn ${maxAmountIn} (estimated ${estimatedAmountIn}, slippageBps ${params.slippageBps})`,
17398
+ detail: `source balance ${sourceBalance} < maximumAmountIn ${maxAmountIn} (estimated ${estimatedAmountIn}, slippageBps ${params.slippageBps})`,
17235
17399
  error: new PanopticError("Insufficient source collateral for the recovery swap")
17236
17400
  };
17237
17401
  const recoverySimulation = await simulateDispatch({
@@ -17283,6 +17447,7 @@ async function quoteTokenShortfallRecovery(params) {
17283
17447
  const decodedShortfall = remainingShortfall.assetsRequested - remainingShortfall.assetBalance;
17284
17448
  const additionalAmountOut = decodedShortfall > 0n ? decodedShortfall : amountOut;
17285
17449
  amountOut += additionalAmountOut;
17450
+ creditOutSize = swapOutput > 0n ? ceilDiv(creditOutSize * amountOut, swapOutput) + 1n : creditOutSize * 2n;
17286
17451
  }
17287
17452
  return {
17288
17453
  available: false,
@@ -17309,6 +17474,7 @@ const MAX_EFFECTIVE_LIQUIDITY_LIMIT = 8388607;
17309
17474
  * returned value must be scaled down to the caller's actual `positionSize`.
17310
17475
  */
17311
17476
  const MAX_UINT64$1 = 2n ** 64n - 1n;
17477
+ const FP96$1 = 1n << 96n;
17312
17478
  /**
17313
17479
  * `getRequiredBase` returns `type(uint128).max` as an error sentinel (invalid
17314
17480
  * tokenId or reverting `getMargin`). Detect it so we don't scale a garbage value.
@@ -17321,7 +17487,9 @@ const REQUIRED_BASE_ERROR_SENTINEL = 2n ** 128n - 1n;
17321
17487
  * computes the requirement at `type(uint64).max` size and 0% utilization. Since
17322
17488
  * the requirement is linear in size, the raw result is scaled by
17323
17489
  * `positionSize / type(uint64).max` to yield the requirement for the requested
17324
- * size. Returns collateral requirement in terms of token0.
17490
+ * size. `PanopticQuery.getRequiredBase` returns the cross-margin requirement in
17491
+ * the higher-precision raw token at `atTick`; this function places that amount
17492
+ * in the matching `required0` or `required1` field.
17325
17493
  *
17326
17494
  * @param params - The parameters
17327
17495
  * @returns Estimated collateral requirements with block metadata
@@ -17340,7 +17508,7 @@ async function estimateCollateralRequired(params) {
17340
17508
  });
17341
17509
  effectiveTick = BigInt(currentTickResult);
17342
17510
  }
17343
- const [required0, _meta] = await Promise.all([client.readContract({
17511
+ const [requiredBase, _meta] = await Promise.all([client.readContract({
17344
17512
  address: queryAddress,
17345
17513
  abi: panopticQueryAbi,
17346
17514
  functionName: "getRequiredBase",
@@ -17354,10 +17522,11 @@ async function estimateCollateralRequired(params) {
17354
17522
  client,
17355
17523
  blockNumber: targetBlockNumber
17356
17524
  })]);
17357
- const scaled0 = required0 >= REQUIRED_BASE_ERROR_SENTINEL ? required0 : required0 * positionSize / MAX_UINT64$1;
17525
+ const scaledRequirement = requiredBase >= REQUIRED_BASE_ERROR_SENTINEL ? requiredBase : requiredBase * positionSize / MAX_UINT64$1;
17526
+ const denominatedInToken0 = tickToSqrtPriceX96(effectiveTick) < FP96$1;
17358
17527
  return {
17359
- required0: scaled0,
17360
- required1: 0n,
17528
+ required0: denominatedInToken0 ? scaledRequirement : 0n,
17529
+ required1: denominatedInToken0 ? 0n : scaledRequirement,
17361
17530
  _meta
17362
17531
  };
17363
17532
  }
@@ -20994,7 +21163,9 @@ async function getStreamiaHistory(params) {
20994
21163
  _meta: _meta$1
20995
21164
  };
20996
21165
  }
20997
- const premiaRequests = blockNumbers.map((bn) => client.readContract({
21166
+ const _meta = params._meta ?? await getBlockMeta({ client });
21167
+ const resolvedBlockNumbers = blockNumbers.map((blockNumber) => blockNumber ?? _meta.blockNumber);
21168
+ const premiaRequests = resolvedBlockNumbers.map((blockNumber) => client.readContract({
20998
21169
  address: panopticPoolAddress,
20999
21170
  abi: panopticPoolV2Abi,
21000
21171
  functionName: "getFullPositionsData",
@@ -21003,18 +21174,36 @@ async function getStreamiaHistory(params) {
21003
21174
  true,
21004
21175
  [tokenId]
21005
21176
  ],
21006
- blockNumber: bn
21177
+ blockNumber
21007
21178
  }));
21008
- const uniswapDataPromise = includeUniswapFees && legs.length > 0 ? fetchUniswapFeeData(client, blockNumbers, legs, poolConfig) : void 0;
21009
- const [premiaResults, uniswapData, _meta] = await Promise.all([
21010
- Promise.all(premiaRequests),
21011
- uniswapDataPromise ?? Promise.resolve(void 0),
21012
- params._meta ? Promise.resolve(params._meta) : getBlockMeta({ client })
21013
- ]);
21014
- const sortedSettled = settledEvents ? [...settledEvents].sort((a, b) => a.blockNumber < b.blockNumber ? -1 : 1) : [];
21179
+ const uniswapDataPromise = includeUniswapFees && legs.length > 0 ? fetchUniswapFeeData(client, resolvedBlockNumbers, legs, poolConfig) : void 0;
21180
+ const [premiaResults, uniswapData] = await Promise.all([Promise.all(premiaRequests), uniswapDataPromise ?? Promise.resolve(void 0)]);
21181
+ const sortedSettled = settledEvents ? [...settledEvents].sort((a, b) => a.blockNumber === b.blockNumber ? 0 : a.blockNumber < b.blockNumber ? -1 : 1) : [];
21015
21182
  let settledIdx = 0;
21016
21183
  let accSettled0 = 0n;
21017
21184
  let accSettled1 = 0n;
21185
+ const cumulativePremiaByInputIndex = Array.from({ length: blockNumbers.length }, () => ({
21186
+ token0: 0n,
21187
+ token1: 0n
21188
+ }));
21189
+ const chronologicalInputs = resolvedBlockNumbers.map((blockNumber, inputIndex) => ({
21190
+ blockNumber,
21191
+ inputIndex
21192
+ })).sort((a, b) => a.blockNumber === b.blockNumber ? a.inputIndex - b.inputIndex : a.blockNumber < b.blockNumber ? -1 : 1);
21193
+ for (const { blockNumber, inputIndex } of chronologicalInputs) {
21194
+ while (settledIdx < sortedSettled.length && sortedSettled[settledIdx].blockNumber <= blockNumber) {
21195
+ accSettled0 += sortedSettled[settledIdx].settled0;
21196
+ accSettled1 += sortedSettled[settledIdx].settled1;
21197
+ settledIdx++;
21198
+ }
21199
+ const result = premiaResults[inputIndex];
21200
+ const premia0 = (result[0] & MASK_128) - (result[1] & MASK_128);
21201
+ const premia1 = (result[0] >> 128n) - (result[1] >> 128n);
21202
+ cumulativePremiaByInputIndex[inputIndex] = {
21203
+ token0: premia0 + accSettled0,
21204
+ token1: premia1 + accSettled1
21205
+ };
21206
+ }
21018
21207
  let initialUniswapFees0 = null;
21019
21208
  let initialUniswapFees1 = null;
21020
21209
  const snapshots = premiaResults.map((result, i) => {
@@ -21025,14 +21214,8 @@ async function getStreamiaHistory(params) {
21025
21214
  const short1 = shortPacked >> 128n;
21026
21215
  const long0 = longPacked & MASK_128;
21027
21216
  const long1 = longPacked >> 128n;
21028
- const effectiveBn = bn ?? BigInt(Number.MAX_SAFE_INTEGER);
21029
- while (settledIdx < sortedSettled.length && sortedSettled[settledIdx].blockNumber <= effectiveBn) {
21030
- accSettled0 += sortedSettled[settledIdx].settled0;
21031
- accSettled1 += sortedSettled[settledIdx].settled1;
21032
- settledIdx++;
21033
- }
21034
- const premia0 = short0 - long0 - accSettled0;
21035
- const premia1 = short1 - long1 - accSettled1;
21217
+ const premia0 = short0 - long0;
21218
+ const premia1 = short1 - long1;
21036
21219
  let uniswapFees0 = 0n;
21037
21220
  let uniswapFees1 = 0n;
21038
21221
  if (uniswapData) {
@@ -21051,6 +21234,7 @@ async function getStreamiaHistory(params) {
21051
21234
  token0: premia0,
21052
21235
  token1: premia1
21053
21236
  },
21237
+ cumulativePanopticPremia: cumulativePremiaByInputIndex[i],
21054
21238
  uniswapFees: {
21055
21239
  token0: uniswapFees0,
21056
21240
  token1: uniswapFees1
@@ -21839,7 +22023,7 @@ const nfpmAbi = [{
21839
22023
  }],
21840
22024
  stateMutability: "payable"
21841
22025
  }];
21842
- const MAX_UINT128 = 2n ** 128n - 1n;
22026
+ const MAX_UINT128$1 = 2n ** 128n - 1n;
21843
22027
  const MAX_UINT256 = 2n ** 256n - 1n;
21844
22028
  const Q128 = 2n ** 128n;
21845
22029
  /**
@@ -21881,8 +22065,8 @@ async function getUniswapV3LpPositionState(params) {
21881
22065
  args: [{
21882
22066
  tokenId,
21883
22067
  recipient: owner,
21884
- amount0Max: MAX_UINT128,
21885
- amount1Max: MAX_UINT128
22068
+ amount0Max: MAX_UINT128$1,
22069
+ amount1Max: MAX_UINT128$1
21886
22070
  }],
21887
22071
  account: owner,
21888
22072
  blockNumber: _meta.blockNumber
@@ -22032,6 +22216,31 @@ async function fetchSlot0(client, blockNumber, poolConfig) {
22032
22216
  }
22033
22217
  }
22034
22218
 
22219
+ //#endregion
22220
+ //#region src/panoptic/v2/reads/positionSizes.ts
22221
+ const BIT_MASK_128$1 = (1n << 128n) - 1n;
22222
+ /**
22223
+ * Returns the current stored positionSize for each tokenId, in the same order
22224
+ * as the input `positionIdList`. Reverts (via the contract) if any tokenId is
22225
+ * not held by `account`.
22226
+ */
22227
+ async function getCurrentPositionSizes(params) {
22228
+ const { client, poolAddress, account, positionIdList, blockNumber } = params;
22229
+ if (positionIdList.length === 0) return [];
22230
+ const [, , positionBalances] = await client.readContract({
22231
+ address: poolAddress,
22232
+ abi: panopticPoolV2Abi,
22233
+ functionName: "getFullPositionsData",
22234
+ args: [
22235
+ account,
22236
+ false,
22237
+ positionIdList
22238
+ ],
22239
+ blockNumber
22240
+ });
22241
+ return positionBalances.map((packed) => packed & BIT_MASK_128$1);
22242
+ }
22243
+
22035
22244
  //#endregion
22036
22245
  //#region src/panoptic/v2/reads/enrichment.ts
22037
22246
  /**
@@ -24065,8 +24274,14 @@ async function forceExerciseAndWait(params) {
24065
24274
  * ```
24066
24275
  */
24067
24276
  async function settleAccumulatedPremia(params) {
24068
- const { client, walletClient, account, poolAddress, positionIdList, usePremiaAsCollateral = false, builderCode = 0n, txOverrides } = params;
24069
- const positionSizes = positionIdList.map(() => 0n);
24277
+ const { client, walletClient, account, poolAddress, positionIdList, finalPositionIdList, positionSizes: providedSizes, usePremiaAsCollateral = false, builderCode = 0n, txOverrides } = params;
24278
+ if (providedSizes && providedSizes.length !== positionIdList.length) throw new PanopticError("settleAccumulatedPremia: positionSizes length must match positionIdList");
24279
+ const positionSizes = providedSizes ?? await getCurrentPositionSizes({
24280
+ client,
24281
+ poolAddress,
24282
+ account,
24283
+ positionIdList
24284
+ });
24070
24285
  const tickAndSpreadLimits = positionIdList.map(() => [
24071
24286
  -887272n,
24072
24287
  887272n,
@@ -24081,7 +24296,7 @@ async function settleAccumulatedPremia(params) {
24081
24296
  functionName: "dispatch",
24082
24297
  args: [
24083
24298
  positionIdList,
24084
- positionIdList,
24299
+ finalPositionIdList ?? positionIdList,
24085
24300
  positionSizes.map((s) => BigInt(s)),
24086
24301
  tickAndSpreadLimits.map((t) => [
24087
24302
  Number(t[0]),
@@ -25662,14 +25877,21 @@ const multicallAbi = [{
25662
25877
  * @returns Simulation result with settlement data or error
25663
25878
  */
25664
25879
  async function simulateSettle(params) {
25665
- const { client, poolAddress, account, positionIdList, tokenId, blockNumber } = params;
25880
+ const { client, poolAddress, account, positionIdList, finalPositionIdList, positionSizes: providedSizes, tokenId, blockNumber } = params;
25666
25881
  const targetBlockNumber = blockNumber ?? await client.getBlockNumber();
25667
25882
  const metaPromise = getBlockMeta({
25668
25883
  client,
25669
25884
  blockNumber: targetBlockNumber
25670
25885
  });
25671
25886
  try {
25672
- const positionSizes = positionIdList.map(() => 0n);
25887
+ if (providedSizes && providedSizes.length !== positionIdList.length) throw new PanopticError("simulateSettle: positionSizes length must match positionIdList");
25888
+ const positionSizes = providedSizes ?? await getCurrentPositionSizes({
25889
+ client,
25890
+ poolAddress,
25891
+ account,
25892
+ positionIdList,
25893
+ blockNumber: targetBlockNumber
25894
+ });
25673
25895
  const tickAndSpreadLimits = positionIdList.map(() => [
25674
25896
  -887272n,
25675
25897
  887272n,
@@ -25680,7 +25902,7 @@ async function simulateSettle(params) {
25680
25902
  functionName: "dispatch",
25681
25903
  args: [
25682
25904
  positionIdList,
25683
- positionIdList,
25905
+ finalPositionIdList ?? positionIdList,
25684
25906
  positionSizes.map((s) => BigInt(s)),
25685
25907
  tickAndSpreadLimits.map((t) => [
25686
25908
  Number(t[0]),
@@ -25712,8 +25934,8 @@ async function simulateSettle(params) {
25712
25934
  });
25713
25935
  const _meta = await metaPromise;
25714
25936
  const data = {
25715
- premiaReceived0: tokenFlow.delta0 > 0n ? tokenFlow.delta0 : 0n,
25716
- premiaReceived1: tokenFlow.delta1 > 0n ? tokenFlow.delta1 : 0n,
25937
+ premiaReceived0: tokenFlow.delta0,
25938
+ premiaReceived1: tokenFlow.delta1,
25717
25939
  postCollateral0: tokenFlow.balanceAfter0,
25718
25940
  postCollateral1: tokenFlow.balanceAfter1,
25719
25941
  forfeitAmounts
@@ -26225,7 +26447,7 @@ async function simulateSwapExactIn(params) {
26225
26447
 
26226
26448
  //#endregion
26227
26449
  //#region src/panoptic/v2/simulations/oneTokenFlow.ts
26228
- const BPS_DENOMINATOR$3 = 10000n;
26450
+ const BPS_DENOMINATOR$4 = 10000n;
26229
26451
  /**
26230
26452
  * Default {@link OneTokenFlowQuoteParams.minSwapRatioBps}: 0.5%.
26231
26453
  *
@@ -26245,7 +26467,7 @@ function abs(value) {
26245
26467
  return value < 0n ? -value : value;
26246
26468
  }
26247
26469
  function padForSlippage(amount, slippageBps) {
26248
- return (amount * (BPS_DENOMINATOR$3 + slippageBps) + BPS_DENOMINATOR$3 - 1n) / BPS_DENOMINATOR$3;
26470
+ return (amount * (BPS_DENOMINATOR$4 + slippageBps) + BPS_DENOMINATOR$4 - 1n) / BPS_DENOMINATOR$4;
26249
26471
  }
26250
26472
  /**
26251
26473
  * Quote a dispatch wrapped so its net flow lands in `targetTokenIndex` only.
@@ -26265,10 +26487,10 @@ async function quoteOneTokenFlow(params) {
26265
26487
  reason: "invalid-target-token",
26266
26488
  detail: `targetTokenIndex=${targetTokenIndex} is neither 0 nor 1`
26267
26489
  };
26268
- if (params.slippageBps < 0n || params.slippageBps > BPS_DENOMINATOR$3) return {
26490
+ if (params.slippageBps < 0n || params.slippageBps > BPS_DENOMINATOR$4) return {
26269
26491
  available: false,
26270
26492
  reason: "invalid-slippage",
26271
- detail: `slippageBps=${params.slippageBps} is outside [0, ${BPS_DENOMINATOR$3}]`
26493
+ detail: `slippageBps=${params.slippageBps} is outside [0, ${BPS_DENOMINATOR$4}]`
26272
26494
  };
26273
26495
  const tickLimitLow = params.tickLimitLow ?? MIN_TICK;
26274
26496
  const tickLimitHigh = params.tickLimitHigh ?? MAX_TICK;
@@ -26376,7 +26598,7 @@ async function quoteOneTokenFlow(params) {
26376
26598
  }
26377
26599
  if (baseTargetDelta !== null && minSwapRatioBps > 0n) {
26378
26600
  const swapInTargetTerms = convertToTokenIndex(swapAmount, otherTokenIndex, targetTokenIndex, pool.sqrtPriceX96);
26379
- if (swapInTargetTerms * BPS_DENOMINATOR$3 < minSwapRatioBps * abs(baseTargetDelta)) return {
26601
+ if (swapInTargetTerms * BPS_DENOMINATOR$4 < minSwapRatioBps * abs(baseTargetDelta)) return {
26380
26602
  available: false,
26381
26603
  reason: "below-threshold",
26382
26604
  detail: `non-target flow ${swapInTargetTerms} (in target token terms) is below ${minSwapRatioBps}bps of the target flow ${abs(baseTargetDelta)}`
@@ -26468,6 +26690,200 @@ async function quoteOneTokenFlow(params) {
26468
26690
  };
26469
26691
  }
26470
26692
 
26693
+ //#endregion
26694
+ //#region src/panoptic/v2/simulations/temporaryLoanRecovery.ts
26695
+ const BPS_DENOMINATOR$3 = 10000n;
26696
+ const MAX_RECOVERY_ATTEMPTS = 8n;
26697
+ const MAX_UINT128 = (1n << 128n) - 1n;
26698
+ const LOAN_GROWTH_DENOMINATOR = 20n;
26699
+ /**
26700
+ * Wrap a dispatch with `loan mint -> user operations -> swapped loan burn`.
26701
+ * The repeated tokenId is intentional: the first occurrence mints it and the
26702
+ * last occurrence burns it after the user's operations have settled.
26703
+ */
26704
+ function buildTemporaryLoanRecoveryDispatch(params) {
26705
+ const low = params.tickLimitLow <= params.tickLimitHigh ? params.tickLimitLow : params.tickLimitHigh;
26706
+ const high = params.tickLimitLow <= params.tickLimitHigh ? params.tickLimitHigh : params.tickLimitLow;
26707
+ return {
26708
+ positionIdList: [
26709
+ params.loanTokenId,
26710
+ ...params.dispatch.positionIdList,
26711
+ params.loanTokenId
26712
+ ],
26713
+ finalPositionIdList: [...params.dispatch.finalPositionIdList],
26714
+ positionSizes: [
26715
+ params.loanPositionSize,
26716
+ ...params.dispatch.positionSizes,
26717
+ 0n
26718
+ ],
26719
+ tickAndSpreadLimits: [
26720
+ [
26721
+ low,
26722
+ high,
26723
+ 0n
26724
+ ],
26725
+ ...params.dispatch.tickAndSpreadLimits,
26726
+ [
26727
+ high,
26728
+ low,
26729
+ 0n
26730
+ ]
26731
+ ],
26732
+ usePremiaAsCollateral: params.dispatch.usePremiaAsCollateral,
26733
+ builderCode: params.dispatch.builderCode
26734
+ };
26735
+ }
26736
+ /**
26737
+ * Quotes and simulates an atomic temporary loan around a full-close dispatch.
26738
+ *
26739
+ * @param params - Chain client, account, failed dispatch, shortfall error, and quote limits.
26740
+ * @returns An executable quote when recovery succeeds, otherwise a categorized unavailable result.
26741
+ * @throws {PanopticError} When the dispatch is not a full close or required RPC reads fail.
26742
+ */
26743
+ async function quoteTemporaryLoanRecovery(params) {
26744
+ const initialShortfall = getNotEnoughTokensError(params.error);
26745
+ if (initialShortfall === null) return {
26746
+ available: false,
26747
+ reason: "not-token-shortfall"
26748
+ };
26749
+ let loanAmount = initialShortfall.assetsRequested - initialShortfall.assetBalance;
26750
+ if (loanAmount <= 0n) return {
26751
+ available: false,
26752
+ reason: "invalid-shortfall",
26753
+ detail: `requested=${initialShortfall.assetsRequested} <= balance=${initialShortfall.assetBalance}`
26754
+ };
26755
+ if (params.slippageBps <= 0n || params.slippageBps > BPS_DENOMINATOR$3) return {
26756
+ available: false,
26757
+ reason: "invalid-slippage",
26758
+ detail: `slippageBps=${params.slippageBps} is outside (0, ${BPS_DENOMINATOR$3}]`
26759
+ };
26760
+ if (params.dispatch.finalPositionIdList.length !== 0) throw new PanopticError("Temporary-loan recovery requires a full-close dispatch with an empty finalPositionIdList");
26761
+ let targetBlockNumber;
26762
+ try {
26763
+ targetBlockNumber = params.blockNumber ?? await params.client.getBlockNumber();
26764
+ } catch (error) {
26765
+ if (error instanceof PanopticError) throw error;
26766
+ throw new PanopticError("Failed to resolve the block for temporary-loan recovery", error instanceof Error ? error : new Error(String(error)));
26767
+ }
26768
+ let pool;
26769
+ try {
26770
+ pool = await getPool({
26771
+ client: params.client,
26772
+ poolAddress: params.poolAddress,
26773
+ chainId: params.chainId,
26774
+ blockNumber: targetBlockNumber
26775
+ });
26776
+ } catch (error) {
26777
+ if (error instanceof PanopticError) throw error;
26778
+ throw new PanopticError("Failed to load the pool for temporary-loan recovery", error instanceof Error ? error : new Error(String(error)));
26779
+ }
26780
+ const defaultLimits = tickLimits(pool.currentTick, params.slippageBps);
26781
+ const tickLimitLow = params.tickLimitLow ?? defaultLimits.low;
26782
+ const tickLimitHigh = params.tickLimitHigh ?? defaultLimits.high;
26783
+ if (tickLimitLow >= tickLimitHigh) return {
26784
+ available: false,
26785
+ reason: "invalid-tick-limits",
26786
+ detail: `tickLimitLow=${tickLimitLow} >= tickLimitHigh=${tickLimitHigh}`
26787
+ };
26788
+ const token0 = pool.collateralTracker0.token;
26789
+ const token1 = pool.collateralTracker1.token;
26790
+ const tokenIndexFor = (address) => {
26791
+ const normalized = address.toLowerCase();
26792
+ if (normalized === token0.toLowerCase() || normalized === pool.collateralTracker0.address.toLowerCase()) return 0n;
26793
+ if (normalized === token1.toLowerCase() || normalized === pool.collateralTracker1.address.toLowerCase()) return 1n;
26794
+ return null;
26795
+ };
26796
+ const loanTokenIndex = tokenIndexFor(initialShortfall.tokenAddress);
26797
+ if (loanTokenIndex === null) return {
26798
+ available: false,
26799
+ reason: "unsupported-token",
26800
+ detail: `${initialShortfall.tokenAddress} is neither collateral token of ${params.poolAddress}`
26801
+ };
26802
+ const repaymentTokenIndex = loanTokenIndex === 0n ? 1n : 0n;
26803
+ const loanToken = loanTokenIndex === 0n ? token0 : token1;
26804
+ const repaymentToken = repaymentTokenIndex === 0n ? token0 : token1;
26805
+ const collisionIds = Array.from(new Set([
26806
+ ...params.existingPositionIds,
26807
+ ...params.dispatch.positionIdList,
26808
+ ...params.dispatch.finalPositionIdList
26809
+ ]));
26810
+ for (let attempt = 0n; attempt < MAX_RECOVERY_ATTEMPTS; attempt += 1n) {
26811
+ if (loanAmount > MAX_UINT128) return {
26812
+ available: false,
26813
+ reason: "recovery-unavailable",
26814
+ detail: `required loan amount ${loanAmount} exceeds uint128`
26815
+ };
26816
+ const loan = buildUniqueLoan(pool.poolId, loanTokenIndex, loanTokenIndex, pool.currentTick, pool.tickSpacing, collisionIds, loanAmount);
26817
+ const recoveredDispatch = buildTemporaryLoanRecoveryDispatch({
26818
+ dispatch: params.dispatch,
26819
+ loanTokenId: loan.tokenId,
26820
+ loanPositionSize: loan.adjustedSize,
26821
+ tickLimitLow,
26822
+ tickLimitHigh
26823
+ });
26824
+ const simulation = await simulateDispatch({
26825
+ client: params.client,
26826
+ poolAddress: params.poolAddress,
26827
+ account: params.account,
26828
+ existingPositionIdList: params.existingPositionIds,
26829
+ ...recoveredDispatch,
26830
+ blockNumber: targetBlockNumber
26831
+ });
26832
+ if (simulation.success && simulation.tokenFlow !== void 0) {
26833
+ const tokenFlow = simulation.tokenFlow;
26834
+ return {
26835
+ available: true,
26836
+ quote: {
26837
+ loanToken,
26838
+ repaymentToken,
26839
+ loanTokenIndex,
26840
+ loanAmount,
26841
+ loanPositionSize: loan.adjustedSize,
26842
+ loanTokenId: loan.tokenId,
26843
+ slippageBps: params.slippageBps,
26844
+ netLoanTokenChange: loanTokenIndex === 0n ? tokenFlow.delta0 : tokenFlow.delta1,
26845
+ netRepaymentTokenChange: repaymentTokenIndex === 0n ? tokenFlow.delta0 : tokenFlow.delta1,
26846
+ dispatch: recoveredDispatch,
26847
+ simulation: {
26848
+ ...simulation,
26849
+ tokenFlow
26850
+ },
26851
+ tokenFlow,
26852
+ _meta: simulation._meta
26853
+ }
26854
+ };
26855
+ }
26856
+ if (simulation.success) return {
26857
+ available: false,
26858
+ reason: "recovery-unavailable",
26859
+ detail: "temporary-loan simulation returned no token flow"
26860
+ };
26861
+ const shortfall = getNotEnoughTokensError(simulation.error);
26862
+ if (shortfall === null) return {
26863
+ available: false,
26864
+ reason: "recovery-unavailable",
26865
+ detail: `temporary-loan dispatch reverted: ${simulation.error.message}`,
26866
+ error: simulation.error
26867
+ };
26868
+ const shortfallIndex = tokenIndexFor(shortfall.tokenAddress);
26869
+ if (shortfallIndex !== loanTokenIndex) return {
26870
+ available: false,
26871
+ reason: "repayment-token-shortfall",
26872
+ detail: `repayment token ${shortfall.tokenAddress} requested ${shortfall.assetsRequested}, balance ${shortfall.assetBalance}`,
26873
+ error: simulation.error
26874
+ };
26875
+ const residual = shortfall.assetsRequested - shortfall.assetBalance;
26876
+ const geometricGrowth = (loanAmount + LOAN_GROWTH_DENOMINATOR - 1n) / LOAN_GROWTH_DENOMINATOR;
26877
+ loanAmount += residual > geometricGrowth ? residual : geometricGrowth;
26878
+ }
26879
+ return {
26880
+ available: false,
26881
+ reason: "recovery-unavailable",
26882
+ detail: `temporary-loan recovery remained short after ${MAX_RECOVERY_ATTEMPTS} attempts`,
26883
+ error: new PanopticError("Could not size the temporary loan within the quote attempt limit")
26884
+ };
26885
+ }
26886
+
26471
26887
  //#endregion
26472
26888
  //#region src/panoptic/v2/abis/poolManager.ts
26473
26889
  /**
@@ -33299,5 +33715,5 @@ function useTxEventConfirmation({ txHash, poolAddress = zeroAddress, collateralT
33299
33715
  }
33300
33716
 
33301
33717
  //#endregion
33302
- 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, PanopticProvider, 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, isCowSupportedChain, 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, mutationEffects, openPosition, openPositionAndWait, optimizeTokenIdRiskPartners, oracleEpochAt, packMarketState, panopticPoolV2Abi, parseBps, parseCollateralLog, parsePanopticError, parsePoolLog, parseTokenAmount, parseTokenListId, parseWad, pokeOracle, pokeOracleAndWait, previewBorrow, previewDeposit, previewMint, previewRedeem, previewUnwrap, previewWithdraw, previewWrap, priceToTick, publicBroadcaster, queryKeys, 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, useAccountCollateral, useAccountGreeks, useAccountPremia, useAccountSummaryBasic, useAccountSummaryRisk, useAddPendingPosition, useApprove, useApproveErc20ForCow, useApproveErc20ForPermit2, useApprovePool, useApproveRouterViaPermit2, useBatchDispatch as useBatchDispatchHook, useBorrow as useBorrowHook, useCancelCowOrder, useCheckCowApproval, useCheckRouterApproval, useChunkSpreads, useClearTrackedPositions, useClosePosition as useClosePositionHook, useClosedPositions, useCollateralBreakdown, useCollateralData, useConfirmPendingPosition, useCowOrderStatus, useCurrentRates, useDeployNewPool as useDeployNewPoolHook, useDeposit as useDepositHook, useDispatch as useDispatchHook, useEstimateCollateralRequired, useEventPoller, useEventSubscription, useFactoryConstructMetadata, useFactoryOwnerOf, useFactoryTokenURI, useFailPendingPosition, useFlowNeutralTokenId, useForceExercise as useForceExerciseHook, useGuardianUnlockState, useInterestState, useIsLiquidatable, useLiquidate as useLiquidateHook, useLiquidationPrices, useMarginBuffer, useMaxPositionSize, useMaxRedeem, useMaxWithdrawable, useMinePoolAddress as useMinePoolAddressHook, useMintShares, useNativeTokenPrice, useNetLiquidationValue, useNetLiquidationValues, useOpenPosition as useOpenPositionHook, useOpenPositionPreview, useOptimizeRiskPartners, useOracleState, usePanopticContext, usePanopticPoolAddress, usePokeOracle as usePokeOracleHook, usePool, usePoolLiquidities, usePosition, usePositionGreeks, usePositions, usePositionsWithPremia, usePreviewBorrow, usePreviewDeposit, usePreviewMint, usePreviewRedeem, usePreviewWithdraw, usePriceHistory, useQuoteCowSwap, useQuoteSwapExactInViaRouter, useQuoteSwapExactOutViaRouter, useRealizedPnL, useRedeem as useRedeemHook, useRepay as useRepayHook, useResolveUniswapV4PoolKey, useRiskParameters, useRollPosition as useRollPositionHook, useSafeMode, useSettleAccumulatedPremia as useSettleAccumulatedPremiaHook, useSimulateBatchDispatch, useSimulateClosePosition, useSimulateDeployNewPool, useSimulateDeposit, useSimulateDispatch, useSimulateForceExercise, useSimulateLiquidate, useSimulateOpenPosition, useSimulateSFPMBurn, useSimulateSFPMMint, useSimulateSettle, useSimulateSwapExactIn, useSimulateSwapExactOut, useSimulateWithdraw, useSmartRepay as useSmartRepayHook, useStreamiaHistory, useSubmitCowOrder, useSupply as useSupplyHook, useSwapExactIn, useSwapExactInViaRouter, useSwapExactOut, useSwapExactOutViaRouter, useSyncPositions, useSyncStatus, useTrackedPositionIds, useTradeHistory, useTxEventConfirmation, useUniswapFeeHistory, useUniswapV3PoolInfo, useUniswapV3PoolLiquidities, useUniswapV4PoolBasicState, useUniswapV4PoolInfo, useUniswapV4PoolLiquidities, useUnsupply as useUnsupplyHook, useUnwrapWeth, useUnwrapXstock, useUtilization, useValidateBuilderCode, useWatchEvents, useWithdraw as useWithdrawHook, useWithdrawWithPositions as useWithdrawWithPositionsHook, useWrapEth, useWrapXstock, utilizationBpsToWad, utilizationPctToWad, validateBatch, validateBuilderCode, validatePoolId, verifyBlockContinuity, watchEvents, wethWrapAbi, withdraw, withdrawAndWait, withdrawWithPositions, withdrawWithPositionsAndWait, wrapEth, wrapEthAndWait, wrapXstock, wrapXstockAndWait, xstockWrapperAbi };
33718
+ 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, PanopticProvider, 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, buildTemporaryLoanRecoveryDispatch, 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, isCowSupportedChain, 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, mutationEffects, 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, queryKeys, quoteOneTokenFlow, quoteSfpmSwap, quoteTemporaryLoanRecovery, 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, useAccountCollateral, useAccountGreeks, useAccountPremia, useAccountSummaryBasic, useAccountSummaryRisk, useAddPendingPosition, useApprove, useApproveErc20ForCow, useApproveErc20ForPermit2, useApprovePool, useApproveRouterViaPermit2, useBatchDispatch as useBatchDispatchHook, useBorrow as useBorrowHook, useCancelCowOrder, useCheckCowApproval, useCheckRouterApproval, useChunkSpreads, useClearTrackedPositions, useClosePosition as useClosePositionHook, useClosedPositions, useCollateralBreakdown, useCollateralData, useConfirmPendingPosition, useCowOrderStatus, useCurrentRates, useDeployNewPool as useDeployNewPoolHook, useDeposit as useDepositHook, useDispatch as useDispatchHook, useEstimateCollateralRequired, useEventPoller, useEventSubscription, useFactoryConstructMetadata, useFactoryOwnerOf, useFactoryTokenURI, useFailPendingPosition, useFlowNeutralTokenId, useForceExercise as useForceExerciseHook, useGuardianUnlockState, useInterestState, useIsLiquidatable, useLiquidate as useLiquidateHook, useLiquidationPrices, useMarginBuffer, useMaxPositionSize, useMaxRedeem, useMaxWithdrawable, useMinePoolAddress as useMinePoolAddressHook, useMintShares, useNativeTokenPrice, useNetLiquidationValue, useNetLiquidationValues, useOpenPosition as useOpenPositionHook, useOpenPositionPreview, useOptimizeRiskPartners, useOracleState, usePanopticContext, usePanopticPoolAddress, usePokeOracle as usePokeOracleHook, usePool, usePoolLiquidities, usePosition, usePositionGreeks, usePositions, usePositionsWithPremia, usePreviewBorrow, usePreviewDeposit, usePreviewMint, usePreviewRedeem, usePreviewWithdraw, usePriceHistory, useQuoteCowSwap, useQuoteSwapExactInViaRouter, useQuoteSwapExactOutViaRouter, useRealizedPnL, useRedeem as useRedeemHook, useRepay as useRepayHook, useResolveUniswapV4PoolKey, useRiskParameters, useRollPosition as useRollPositionHook, useSafeMode, useSettleAccumulatedPremia as useSettleAccumulatedPremiaHook, useSimulateBatchDispatch, useSimulateClosePosition, useSimulateDeployNewPool, useSimulateDeposit, useSimulateDispatch, useSimulateForceExercise, useSimulateLiquidate, useSimulateOpenPosition, useSimulateSFPMBurn, useSimulateSFPMMint, useSimulateSettle, useSimulateSwapExactIn, useSimulateSwapExactOut, useSimulateWithdraw, useSmartRepay as useSmartRepayHook, useStreamiaHistory, useSubmitCowOrder, useSupply as useSupplyHook, useSwapExactIn, useSwapExactInViaRouter, useSwapExactOut, useSwapExactOutViaRouter, useSyncPositions, useSyncStatus, useTrackedPositionIds, useTradeHistory, useTxEventConfirmation, useUniswapFeeHistory, useUniswapV3PoolInfo, useUniswapV3PoolLiquidities, useUniswapV4PoolBasicState, useUniswapV4PoolInfo, useUniswapV4PoolLiquidities, useUnsupply as useUnsupplyHook, useUnwrapWeth, useUnwrapXstock, useUtilization, useValidateBuilderCode, useWatchEvents, useWithdraw as useWithdrawHook, useWithdrawWithPositions as useWithdrawWithPositionsHook, useWrapEth, useWrapXstock, utilizationBpsToWad, utilizationPctToWad, validateBatch, validateBuilderCode, validatePoolId, verifyBlockContinuity, watchEvents, wethWrapAbi, withdraw, withdrawAndWait, withdrawWithPositions, withdrawWithPositionsAndWait, wrapEth, wrapEthAndWait, wrapXstock, wrapXstockAndWait, xstockWrapperAbi };
33303
33719
  //# sourceMappingURL=react-public.js.map