@panoptic-eng/sdk 1.0.32 → 1.0.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/dist/cow/index.d.ts +5 -5
  2. package/dist/cow/index.js +3 -3
  3. package/dist/cow/types.d.ts +2 -2
  4. package/dist/{cow-CLFJQ-OP.js → cow-aX6of9TS.js} +2 -2
  5. package/dist/{cow-CLFJQ-OP.js.map → cow-aX6of9TS.js.map} +1 -1
  6. package/dist/deployments.d.ts +25 -1
  7. package/dist/deployments.d.ts.map +1 -1
  8. package/dist/deployments.js +19 -1
  9. package/dist/deployments.js.map +1 -1
  10. package/dist/{index-DToj2-Vc.d.ts → index-CURXeKNy.d.ts} +412 -12
  11. package/dist/index-CURXeKNy.d.ts.map +1 -0
  12. package/dist/{index-DQS8Luef.d.ts → index-Cp-nCeV2.d.ts} +4 -4
  13. package/dist/{index-DQS8Luef.d.ts.map → index-Cp-nCeV2.d.ts.map} +1 -1
  14. package/dist/{index-CDCAju5o.d.ts → index-Zvu-rJpV.d.ts} +2 -2
  15. package/dist/{index-CDCAju5o.d.ts.map → index-Zvu-rJpV.d.ts.map} +1 -1
  16. package/dist/index.d.ts +384 -548
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +1619 -1718
  19. package/dist/index.js.map +1 -1
  20. package/dist/{irm-BNtE1Dmg.d.ts → irm-CmgC1Mqi.d.ts} +3 -3
  21. package/dist/{irm-BNtE1Dmg.d.ts.map → irm-CmgC1Mqi.d.ts.map} +1 -1
  22. package/dist/{irm-iAI9hTYQ.js → irm-DnM96-X4.js} +342 -3
  23. package/dist/irm-DnM96-X4.js.map +1 -0
  24. package/dist/panoptic/v2/greeks/index.d.ts +2 -2
  25. package/dist/panoptic/v2/index.d.ts +8 -8
  26. package/dist/panoptic/v2/index.js +6 -5
  27. package/dist/panoptic/v2/react-public.d.ts +13 -13
  28. package/dist/panoptic/v2/react-public.d.ts.map +1 -1
  29. package/dist/panoptic/v2/react-public.js +10 -9
  30. package/dist/panoptic/v2/react-public.js.map +1 -1
  31. package/dist/{position-Bmd_3nzI.d.ts → position-BqIgubs5.d.ts} +1 -1
  32. package/dist/{position-Bmd_3nzI.d.ts.map → position-BqIgubs5.d.ts.map} +1 -1
  33. package/dist/{position-ClU7GrTa.js → position-C8rkkKhi.js} +10 -4
  34. package/dist/{position-ClU7GrTa.js.map → position-C8rkkKhi.js.map} +1 -1
  35. package/dist/{oracle-DaNTHk7B.d.ts → quote-BxQkPBRg.d.ts} +57 -4
  36. package/dist/quote-BxQkPBRg.d.ts.map +1 -0
  37. package/dist/{router-DKFR_vnu.js → router-CS86ptMu.js} +6 -87
  38. package/dist/router-CS86ptMu.js.map +1 -0
  39. package/dist/router-DcoU2KmV.js +313 -0
  40. package/dist/router-DcoU2KmV.js.map +1 -0
  41. package/dist/{simulation-CUqERC5Y.d.ts → simulation-zM6-YUaw.d.ts} +2 -2
  42. package/dist/{simulation-CUqERC5Y.d.ts.map → simulation-zM6-YUaw.d.ts.map} +1 -1
  43. package/dist/{tx-BXXqhBHn.d.ts → tx-CbDQ58Io.d.ts} +1 -1
  44. package/dist/{tx-BXXqhBHn.d.ts.map → tx-CbDQ58Io.d.ts.map} +1 -1
  45. package/dist/{types-DgWfno7q.d.ts → types-BHdnurYr.d.ts} +2 -2
  46. package/dist/{types-DgWfno7q.d.ts.map → types-BHdnurYr.d.ts.map} +1 -1
  47. package/dist/{types-Cy6v8iPf.d.ts → types-D_jcYss-.d.ts} +5 -4
  48. package/dist/types-D_jcYss-.d.ts.map +1 -0
  49. package/dist/uniswap/index.d.ts +53 -100
  50. package/dist/uniswap/index.d.ts.map +1 -1
  51. package/dist/uniswap/index.js +4 -226
  52. package/dist/uniswap/index.js.map +1 -1
  53. package/dist/{v2-DHJxPCAp.js → v2-D0dEatKb.js} +841 -65
  54. package/dist/v2-D0dEatKb.js.map +1 -0
  55. package/dist/{writes-DQLGdkJ5.js → writes-AupZCK4M.js} +2 -2
  56. package/dist/{writes-DQLGdkJ5.js.map → writes-AupZCK4M.js.map} +1 -1
  57. package/dist/zodiac/index.d.ts +74 -1
  58. package/dist/zodiac/index.d.ts.map +1 -1
  59. package/dist/zodiac/index.js +213 -3
  60. package/dist/zodiac/index.js.map +1 -1
  61. package/package.json +1 -1
  62. package/dist/index-DToj2-Vc.d.ts.map +0 -1
  63. package/dist/irm-iAI9hTYQ.js.map +0 -1
  64. package/dist/oracle-DaNTHk7B.d.ts.map +0 -1
  65. package/dist/router-DKFR_vnu.js.map +0 -1
  66. package/dist/types-Cy6v8iPf.d.ts.map +0 -1
  67. package/dist/v2-DHJxPCAp.js.map +0 -1
@@ -1,8 +1,8 @@
1
- import { formatTokenAmount$1 as formatTokenAmount, getAccountCollateral$1 as getAccountCollateral, panopticQueryAbi, parseTokenAmount$1 as parseTokenAmount, readBlockAndAggregate, requireReturnData } from "./irm-iAI9hTYQ.js";
2
- import { AccountInsolventError$1 as AccountInsolventError, ChunkLimitError$1 as ChunkLimitError, DEFAULT_VEGOID$1 as DEFAULT_VEGOID, InvalidHistoryRangeError$1 as InvalidHistoryRangeError, NotEnoughTokensError$1 as NotEnoughTokensError, PanopticError, PanopticValidationError$1 as PanopticValidationError, StorageDataNotFoundError, collateralTrackerV2Abi, decodeLeftRightSigned$1 as decodeLeftRightSigned, decodeLeftRightUnsigned$1 as decodeLeftRightUnsigned, decodePosition, decodePositionBalance, decodeTickSpacing$1 as decodeTickSpacing, getBlockMeta$1 as getBlockMeta, getPool, panopticFactoryV3Abi, panopticFactoryV4Abi, panopticGuardianAbi, panopticPoolV2Abi, panopticQueryAbi$1, parsePanopticError$1 as parsePanopticError, riskEngineAbi, semiFungiblePositionManagerV3Abi, semiFungiblePositionManagerV4Abi, stateViewAbi, uniswapV3PoolAbi } from "./position-ClU7GrTa.js";
1
+ import { StateViewAbi, formatTokenAmount$1 as formatTokenAmount, getAccountCollateral$1 as getAccountCollateral, panopticQueryAbi, parseTokenAmount$1 as parseTokenAmount, readBlockAndAggregate, requireReturnData } from "./irm-DnM96-X4.js";
2
+ import { AccountInsolventError$1 as AccountInsolventError, ChunkLimitError$1 as ChunkLimitError, DEFAULT_VEGOID$1 as DEFAULT_VEGOID, InvalidHistoryRangeError$1 as InvalidHistoryRangeError, NotEnoughTokensError$1 as NotEnoughTokensError, PanopticError, PanopticValidationError$1 as PanopticValidationError, StorageDataNotFoundError, WrongUniswapPoolError$1 as WrongUniswapPoolError, collateralTrackerV2Abi, decodeLeftRightSigned$1 as decodeLeftRightSigned, decodeLeftRightUnsigned$1 as decodeLeftRightUnsigned, decodePosition, decodePositionBalance, decodeTickSpacing$1 as decodeTickSpacing, getBlockMeta$1 as getBlockMeta, getPool, panopticFactoryV3Abi, panopticFactoryV4Abi, panopticGuardianAbi, panopticPoolV2Abi, panopticQueryAbi$1, parsePanopticError$1 as parsePanopticError, riskEngineAbi, semiFungiblePositionManagerV3Abi, semiFungiblePositionManagerV4Abi, stateViewAbi, uniswapV3PoolAbi } from "./position-C8rkkKhi.js";
3
3
  import { MAX_TICK, MAX_TRACKED_CHUNKS, MIN_TICK, WAD, calculatePositionDelta, calculatePositionGamma, calculatePositionValue, priceToTick, sqrtPriceX96ToTick, tickLimits, tickToPrice, tickToPriceDecimalScaled, tickToSqrtPriceX96 } from "./greeks-GysWXct-.js";
4
- import { addLegToTokenId$1 as addLegToTokenId, buildBatchDispatchArgs$1 as buildBatchDispatchArgs, buildUniqueCredit$1 as buildUniqueCredit, countLegs$1 as countLegs, decodeAllLegs$1 as decodeAllLegs, decodeTokenId$1 as decodeTokenId, getClosedPositionsKey$1 as getClosedPositionsKey, getPendingPositionsKey$1 as getPendingPositionsKey, getPoolMetaKey$1 as getPoolMetaKey, getPositionMetaKey$1 as getPositionMetaKey, getPositionsKey$1 as getPositionsKey, getTrackedChunksKey$1 as getTrackedChunksKey, getTrackedPositionIds$1 as getTrackedPositionIds, jsonSerializer$1 as jsonSerializer, loadCheckpoint$1 as loadCheckpoint, resolveTokenIndex$1 as resolveTokenIndex, simulateOpenPosition$1 as simulateOpenPosition, simulateWithTokenFlow } from "./writes-DQLGdkJ5.js";
5
- import { ContractFunctionExecutionError, decodeFunctionResult, encodeAbiParameters, encodeFunctionData, getAddress, keccak256, zeroAddress } from "viem";
4
+ import { addLegToTokenId$1 as addLegToTokenId, buildBatchDispatchArgs$1 as buildBatchDispatchArgs, buildUniqueCredit$1 as buildUniqueCredit, countLegs$1 as countLegs, createTokenIdBuilder$1 as createTokenIdBuilder, decodeAllLegs$1 as decodeAllLegs, decodeTokenId$1 as decodeTokenId, getClosedPositionsKey$1 as getClosedPositionsKey, getPendingPositionsKey$1 as getPendingPositionsKey, getPoolMetaKey$1 as getPoolMetaKey, getPositionMetaKey$1 as getPositionMetaKey, getPositionsKey$1 as getPositionsKey, getTrackedChunksKey$1 as getTrackedChunksKey, getTrackedPositionIds$1 as getTrackedPositionIds, jsonSerializer$1 as jsonSerializer, loadCheckpoint$1 as loadCheckpoint, resolveTokenIndex$1 as resolveTokenIndex, simulateOpenPosition$1 as simulateOpenPosition, simulateWithTokenFlow } from "./writes-AupZCK4M.js";
5
+ import { BaseError, ContractFunctionExecutionError, ContractFunctionRevertedError, ExecutionRevertedError, decodeFunctionResult, encodeAbiParameters, encodeFunctionData, getAddress, keccak256, toHex, zeroAddress } from "viem";
6
6
  import { multicall } from "viem/actions";
7
7
 
8
8
  //#region src/panoptic/v2/clients/blocksByTimestamp.ts
@@ -203,6 +203,277 @@ function interpolateBlocks(startBlock, endBlock, points) {
203
203
  return Array.from({ length: points }, (_, i) => startBlock + range * BigInt(i) / BigInt(points - 1));
204
204
  }
205
205
 
206
+ //#endregion
207
+ //#region src/panoptic/v2/sfpmSwap/calldata.ts
208
+ /**
209
+ * Encode the `multicall([mint, burn])` for a swap plan.
210
+ *
211
+ * The order is always `[mint, burn]` — the ERC1155 must be minted before it is
212
+ * burned. Which call carries the inverted (swap) limits is decided in the plan.
213
+ */
214
+ function buildSfpmSwapCalldata(plan) {
215
+ const mintData = encodeFunctionData({
216
+ abi: semiFungiblePositionManagerV3Abi,
217
+ functionName: "mintTokenizedPosition",
218
+ args: [
219
+ plan.poolKey,
220
+ plan.tokenId,
221
+ plan.positionSize,
222
+ plan.mintTickLimits[0],
223
+ plan.mintTickLimits[1]
224
+ ]
225
+ });
226
+ const burnData = encodeFunctionData({
227
+ abi: semiFungiblePositionManagerV3Abi,
228
+ functionName: "burnTokenizedPosition",
229
+ args: [
230
+ plan.poolKey,
231
+ plan.tokenId,
232
+ plan.positionSize,
233
+ plan.burnTickLimits[0],
234
+ plan.burnTickLimits[1]
235
+ ]
236
+ });
237
+ const multicallData = encodeFunctionData({
238
+ abi: semiFungiblePositionManagerV3Abi,
239
+ functionName: "multicall",
240
+ args: [[mintData, burnData]]
241
+ });
242
+ return {
243
+ multicallData,
244
+ mintData,
245
+ burnData
246
+ };
247
+ }
248
+
249
+ //#endregion
250
+ //#region src/panoptic/v2/sfpmSwap/init.ts
251
+ /**
252
+ * Resolve the `uint64` SFPM poolId for a Uniswap v3 pool, initializing it if needed.
253
+ *
254
+ * `initializeAMMPool` is permissionless and idempotent and **returns the poolId**
255
+ * whether or not the pool was already registered, so a `simulateContract` call is
256
+ * enough to learn the id without sending a transaction. Always resolve the id this
257
+ * way rather than encoding it offline — the SFPM can collision-increment ids.
258
+ */
259
+ async function fetchSfpmV3PoolId(params) {
260
+ const { client, sfpmAddress, token0, token1, fee } = params;
261
+ const vegoid = params.vegoid ?? Number(DEFAULT_VEGOID);
262
+ const { result } = await client.simulateContract({
263
+ address: sfpmAddress,
264
+ abi: semiFungiblePositionManagerV3Abi,
265
+ functionName: "initializeAMMPool",
266
+ args: [
267
+ token0,
268
+ token1,
269
+ fee,
270
+ vegoid
271
+ ]
272
+ });
273
+ return BigInt(result);
274
+ }
275
+ /**
276
+ * Ensure a Uniswap v3 pool is registered on the SFPM, sending `initializeAMMPool`
277
+ * only if it is not already registered. Returns the resolved poolId.
278
+ *
279
+ * When `expectedPool` is given, the resolved id is checked to map back to it via
280
+ * `getUniswapV3PoolFromId` — guards against a wrong token/fee triple.
281
+ */
282
+ async function ensureSfpmV3PoolInitialized(params) {
283
+ const { client, wallet, sfpmAddress, token0, token1, fee, expectedPool } = params;
284
+ const vegoid = params.vegoid ?? Number(DEFAULT_VEGOID);
285
+ const poolId = await fetchSfpmV3PoolId({
286
+ client,
287
+ sfpmAddress,
288
+ token0,
289
+ token1,
290
+ fee,
291
+ vegoid
292
+ });
293
+ const registered = await client.readContract({
294
+ address: sfpmAddress,
295
+ abi: semiFungiblePositionManagerV3Abi,
296
+ functionName: "getUniswapV3PoolFromId",
297
+ args: [poolId]
298
+ });
299
+ let initialized = false;
300
+ let resolved = registered;
301
+ if (getAddress(registered) === zeroAddress) {
302
+ const account = wallet.account;
303
+ if (account === void 0) throw new PanopticError("wallet client has no account");
304
+ const hash = await wallet.writeContract({
305
+ account,
306
+ chain: wallet.chain ?? null,
307
+ address: sfpmAddress,
308
+ abi: semiFungiblePositionManagerV3Abi,
309
+ functionName: "initializeAMMPool",
310
+ args: [
311
+ token0,
312
+ token1,
313
+ fee,
314
+ vegoid
315
+ ]
316
+ });
317
+ await client.waitForTransactionReceipt({ hash });
318
+ initialized = true;
319
+ resolved = await client.readContract({
320
+ address: sfpmAddress,
321
+ abi: semiFungiblePositionManagerV3Abi,
322
+ functionName: "getUniswapV3PoolFromId",
323
+ args: [poolId]
324
+ });
325
+ }
326
+ if (expectedPool !== void 0 && getAddress(resolved) !== getAddress(expectedPool)) throw new WrongUniswapPoolError(new PanopticError(`SFPM poolId ${poolId} resolves to ${resolved}, expected ${expectedPool}`));
327
+ return {
328
+ poolId,
329
+ initialized
330
+ };
331
+ }
332
+
333
+ //#endregion
334
+ //#region src/panoptic/v2/sfpmSwap/plan.ts
335
+ /** Wide, non-inverted band for the paired (non-swapping) call — never triggers a swap. */
336
+ const WIDE_LIMITS = [Number(MIN_TICK) + 1, Number(MAX_TICK) - 1];
337
+ /**
338
+ * Convert a slippage tolerance in bps to a conservative Uniswap tick distance.
339
+ *
340
+ * Ticks are 1.0001^tick, so each tick ≈ 1 bps. This walks up powers of 1.0001
341
+ * until the cumulative price move covers `slippageBps`, matching the hedger-bot's
342
+ * dispatch-path helper so both swap paths agree on band width.
343
+ */
344
+ function slippageBpsToTickDistance(slippageBps) {
345
+ if (slippageBps < 0n || slippageBps > 1000n) throw new PanopticError(`slippage bps ${slippageBps} out of bounds (0..1000)`);
346
+ if (slippageBps === 0n) return 0n;
347
+ let numerator = 1n;
348
+ let denominator = 1n;
349
+ let ticks = 0n;
350
+ while (numerator * 10000n < denominator * (10000n + slippageBps)) {
351
+ numerator *= 10001n;
352
+ denominator *= 10000n;
353
+ ticks += 1n;
354
+ }
355
+ return ticks;
356
+ }
357
+ /**
358
+ * Build the swap plan.
359
+ *
360
+ * Mechanism (verified in the Phase 0 fork test):
361
+ * - A single-leg **loan** tokenId (width=0, isLong=false, `asset == tokenType`) moves
362
+ * exactly `positionSize` of the `tokenType` token when its call carries inverted
363
+ * tick limits (`low > high`); the paired call uses a wide band and moves nothing.
364
+ * - `exactIn`: swap on the **mint**; `tokenType` = the **input** token index.
365
+ * - `exactOut`: swap on the **burn** (isLong flips → exact-output); `tokenType` = the
366
+ * **output** token index.
367
+ *
368
+ * The inverted band is centered on `currentTick` at ±`slippageBpsToTickDistance`,
369
+ * which the SFPM re-sorts and enforces as an open interval on the post-swap tick.
370
+ */
371
+ function buildSfpmSwapPlan(params) {
372
+ const { sfpmAddress, poolAddress, poolId, kind, zeroForOne, amount, currentTick, slippageBps } = params;
373
+ if (amount <= 0n) throw new PanopticError(`swap amount must be positive (got ${amount})`);
374
+ const distance = slippageBpsToTickDistance(slippageBps);
375
+ if (distance < 1n) throw new PanopticError(`slippageBps ${slippageBps} yields a zero-width tick band; use a larger tolerance`);
376
+ const tokenType = kind === "exactIn" ? zeroForOne ? 0n : 1n : zeroForOne ? 1n : 0n;
377
+ const tokenId = createTokenIdBuilder(poolId).addLoan({
378
+ asset: tokenType,
379
+ tokenType,
380
+ strike: 0n
381
+ }).build();
382
+ const d = Number(distance);
383
+ const low = Math.min(Number(MAX_TICK), currentTick + d);
384
+ const high = Math.max(Number(MIN_TICK), currentTick - d);
385
+ if (low <= high) throw new PanopticError(`slippageBps ${slippageBps} at tick ${currentTick} yields a non-inverted band after clamping`);
386
+ const invertedLimits = [low, high];
387
+ const swapOn = kind === "exactIn" ? "mint" : "burn";
388
+ return {
389
+ sfpmAddress,
390
+ poolAddress,
391
+ poolKey: encodeAbiParameters([{ type: "address" }], [poolAddress]),
392
+ tokenId,
393
+ positionSize: amount,
394
+ mintTickLimits: swapOn === "mint" ? invertedLimits : WIDE_LIMITS,
395
+ burnTickLimits: swapOn === "burn" ? invertedLimits : WIDE_LIMITS,
396
+ swapOn,
397
+ kind
398
+ };
399
+ }
400
+
401
+ //#endregion
402
+ //#region src/panoptic/v2/sfpmSwap/quote.ts
403
+ const UINT128 = 1n << 128n;
404
+ const INT128_MAX = (1n << 127n) - 1n;
405
+ /** Extract the two signed 128-bit slots from a packed `LeftRightSigned` int256. */
406
+ function unpackLeftRightSigned(packed) {
407
+ const u = packed < 0n ? packed + (1n << 256n) : packed;
408
+ const toInt128 = (half) => half > INT128_MAX ? half - UINT128 : half;
409
+ return {
410
+ right: toInt128(u & UINT128 - 1n),
411
+ left: toInt128(u >> 128n & UINT128 - 1n)
412
+ };
413
+ }
414
+ /** Wrap an unknown thrown value as a PanopticError, decoding Panoptic reverts when possible. */
415
+ function toPanopticError(err) {
416
+ if (err instanceof PanopticError) return err;
417
+ const parsed = parsePanopticError(err);
418
+ if (parsed) return parsed.error;
419
+ return new PanopticError(err instanceof Error ? err.message : "SFPM swap simulation failed", err instanceof Error ? err : void 0);
420
+ }
421
+ /**
422
+ * Quote a swap by simulating `SFPM.multicall([mint, burn])` and decoding the swap
423
+ * call's `totalMoved` return. Authoritative — captures the width-0 loan-leg wei
424
+ * rounding a raw QuoterV2 quote would miss.
425
+ */
426
+ async function quoteSfpmSwap(params) {
427
+ const { client, plan, account, stateOverride, blockNumber } = params;
428
+ const { mintData, burnData } = buildSfpmSwapCalldata(plan);
429
+ const _meta = await getBlockMeta({
430
+ client,
431
+ blockNumber
432
+ });
433
+ try {
434
+ const { result } = await client.simulateContract({
435
+ account,
436
+ address: plan.sfpmAddress,
437
+ abi: semiFungiblePositionManagerV3Abi,
438
+ functionName: "multicall",
439
+ args: [[mintData, burnData]],
440
+ blockNumber,
441
+ stateOverride
442
+ });
443
+ const swapIndex = plan.swapOn === "mint" ? 0 : 1;
444
+ const decoded = decodeFunctionResult({
445
+ abi: semiFungiblePositionManagerV3Abi,
446
+ functionName: plan.swapOn === "mint" ? "mintTokenizedPosition" : "burnTokenizedPosition",
447
+ data: result[swapIndex]
448
+ });
449
+ const finalTick = Number(decoded[2]);
450
+ const { right, left } = unpackLeftRightSigned(decoded[1]);
451
+ const inSlot = [right, left].find((s) => s > 0n);
452
+ const outSlot = [right, left].find((s) => s < 0n);
453
+ if (inSlot === void 0 || outSlot === void 0) return {
454
+ success: false,
455
+ error: new PanopticError(`SFPM swap simulation moved no tokens (totalMoved=${decoded[1]})`),
456
+ _meta
457
+ };
458
+ return {
459
+ success: true,
460
+ data: {
461
+ amountIn: inSlot,
462
+ amountOut: -outSlot,
463
+ finalTick
464
+ },
465
+ gasEstimate: 0n,
466
+ _meta
467
+ };
468
+ } catch (err) {
469
+ return {
470
+ success: false,
471
+ error: toPanopticError(err),
472
+ _meta
473
+ };
474
+ }
475
+ }
476
+
206
477
  //#endregion
207
478
  //#region src/panoptic/v2/reads/sfpm.ts
208
479
  /**
@@ -1477,8 +1748,8 @@ async function createFlowNeutralTokenId(params) {
1477
1748
  if (tickBefore === null) throw new PanopticError("Cannot create flow-neutral position: current tick unavailable");
1478
1749
  const sqrtPX96 = tickToSqrtPriceX96(tickBefore);
1479
1750
  const valueAbs = (index, amount) => {
1480
- const abs = amount < 0n ? -amount : amount;
1481
- return index === 0n ? abs * sqrtPX96 * sqrtPX96 / Q192$1 : abs;
1751
+ const abs$1 = amount < 0n ? -amount : amount;
1752
+ return index === 0n ? abs$1 * sqrtPX96 * sqrtPX96 / Q192$1 : abs$1;
1482
1753
  };
1483
1754
  const flowFor = (index) => index === 0n ? credit.creditAmount0 : credit.creditAmount1;
1484
1755
  if (queryAddress !== void 0) {
@@ -2877,7 +3148,7 @@ function calculateAccountGreeksPure(params) {
2877
3148
  const MIN_TICK$1 = -887272n;
2878
3149
  const MAX_TICK$1 = 887272n;
2879
3150
  const FP96 = 1n << 96n;
2880
- const Q128$1 = 1n << 128n;
3151
+ const Q128$3 = 1n << 128n;
2881
3152
  /** Cap for a usage ratio with no collateral behind it. */
2882
3153
  const MAX_USAGE_BPS = 1000000n;
2883
3154
  const bigintMax = (a, b) => a > b ? a : b;
@@ -2895,16 +3166,16 @@ const MINT_BUFFER_DENOMINATOR = 10000000n;
2895
3166
  * Matches the on-chain `PanopticMath.convert0to1` truncation, with an
2896
3167
  * overflow-safe branch when `sqrtPriceX96^2` would not fit in uint256.
2897
3168
  */
2898
- function convert0to1$1(amount, sqrtPriceX96) {
2899
- if (sqrtPriceX96 < Q128$1) return amount * sqrtPriceX96 * sqrtPriceX96 >> 192n;
3169
+ function convert0to1$2(amount, sqrtPriceX96) {
3170
+ if (sqrtPriceX96 < Q128$3) return amount * sqrtPriceX96 * sqrtPriceX96 >> 192n;
2900
3171
  const sp2Hi = sqrtPriceX96 * sqrtPriceX96 >> 64n;
2901
3172
  return amount * sp2Hi >> 128n;
2902
3173
  }
2903
3174
  /**
2904
3175
  * Convert a token1 amount to its token0-equivalent at the given sqrtPriceX96.
2905
3176
  */
2906
- function convert1to0$1(amount, sqrtPriceX96) {
2907
- if (sqrtPriceX96 < Q128$1) {
3177
+ function convert1to0$2(amount, sqrtPriceX96) {
3178
+ if (sqrtPriceX96 < Q128$3) {
2908
3179
  const denom = sqrtPriceX96 * sqrtPriceX96;
2909
3180
  return amount * (1n << 192n) / denom;
2910
3181
  }
@@ -3084,10 +3355,10 @@ async function getMarginBuffer(params) {
3084
3355
  }
3085
3356
  const sqrtPriceX96 = tickToSqrtPriceX96(currentTick);
3086
3357
  const denominatedInToken = sqrtPriceX96 < FP96 ? 0 : 1;
3087
- const currentMargin0 = assets0 + convert1to0$1(assets1, sqrtPriceX96);
3088
- const requiredMargin0 = required0Native + convert1to0$1(required1Native, sqrtPriceX96);
3089
- const currentMargin1 = assets1 + convert0to1$1(assets0, sqrtPriceX96);
3090
- const requiredMargin1 = required1Native + convert0to1$1(required0Native, sqrtPriceX96);
3358
+ const currentMargin0 = assets0 + convert1to0$2(assets1, sqrtPriceX96);
3359
+ const requiredMargin0 = required0Native + convert1to0$2(required1Native, sqrtPriceX96);
3360
+ const currentMargin1 = assets1 + convert0to1$2(assets0, sqrtPriceX96);
3361
+ const requiredMargin1 = required1Native + convert0to1$2(required0Native, sqrtPriceX96);
3091
3362
  const buffer0 = currentMargin0 - requiredMargin0;
3092
3363
  const buffer1 = currentMargin1 - requiredMargin1;
3093
3364
  const bufferPercent0 = requiredMargin0 === 0n ? null : buffer0 * 10000n / requiredMargin0;
@@ -3363,14 +3634,14 @@ async function getCollateralTotalAssetsBatch(client, collateralTrackerAddresses,
3363
3634
 
3364
3635
  //#endregion
3365
3636
  //#region src/panoptic/v2/reads/buyingPower.ts
3366
- const Q128 = 1n << 128n;
3367
- function convert0to1(amount, sqrtPriceX96) {
3368
- if (sqrtPriceX96 < Q128) return amount * sqrtPriceX96 * sqrtPriceX96 >> 192n;
3637
+ const Q128$2 = 1n << 128n;
3638
+ function convert0to1$1(amount, sqrtPriceX96) {
3639
+ if (sqrtPriceX96 < Q128$2) return amount * sqrtPriceX96 * sqrtPriceX96 >> 192n;
3369
3640
  const sp2Hi = sqrtPriceX96 * sqrtPriceX96 >> 64n;
3370
3641
  return amount * sp2Hi >> 128n;
3371
3642
  }
3372
- function convert1to0(amount, sqrtPriceX96) {
3373
- if (sqrtPriceX96 < Q128) {
3643
+ function convert1to0$1(amount, sqrtPriceX96) {
3644
+ if (sqrtPriceX96 < Q128$2) {
3374
3645
  const denom = sqrtPriceX96 * sqrtPriceX96;
3375
3646
  return amount * (1n << 192n) / denom;
3376
3647
  }
@@ -3491,10 +3762,10 @@ async function getAccountBuyingPower(params) {
3491
3762
  }
3492
3763
  }
3493
3764
  const sqrtPriceX96 = tickToSqrtPriceX96(currentTick);
3494
- const collateralBalance0 = assets0 + convert1to0(assets1, sqrtPriceX96);
3495
- const requiredCollateral0 = required0Native + convert1to0(required1Native, sqrtPriceX96);
3496
- const collateralBalance1 = assets1 + convert0to1(assets0, sqrtPriceX96);
3497
- const requiredCollateral1 = required1Native + convert0to1(required0Native, sqrtPriceX96);
3765
+ const collateralBalance0 = assets0 + convert1to0$1(assets1, sqrtPriceX96);
3766
+ const requiredCollateral0 = required0Native + convert1to0$1(required1Native, sqrtPriceX96);
3767
+ const collateralBalance1 = assets1 + convert0to1$1(assets0, sqrtPriceX96);
3768
+ const requiredCollateral1 = required1Native + convert0to1$1(required0Native, sqrtPriceX96);
3498
3769
  return {
3499
3770
  collateralBalance0,
3500
3771
  requiredCollateral0,
@@ -3504,6 +3775,68 @@ async function getAccountBuyingPower(params) {
3504
3775
  };
3505
3776
  }
3506
3777
 
3778
+ //#endregion
3779
+ //#region src/panoptic/v2/simulations/creditWrap.ts
3780
+ /**
3781
+ * Wrap a dispatch with a temporary credit leg that is opened and closed in the
3782
+ * same transaction, netting to a swap.
3783
+ *
3784
+ * `swapAtMint` is not a calldata flag — it is the ORDER of the tick-limit pair:
3785
+ * descending `[high, low]` turns the swap on, ascending `[low, high]` leaves it
3786
+ * off. Exactly one of the two legs carries it, and which one is what makes the
3787
+ * swap exact-in vs exact-out.
3788
+ */
3789
+ function buildCreditWrappedDispatch(params) {
3790
+ const { dispatch, creditTokenId, creditPositionSize, direction, placement } = params;
3791
+ const low = params.tickLimitLow <= params.tickLimitHigh ? params.tickLimitLow : params.tickLimitHigh;
3792
+ const high = params.tickLimitLow <= params.tickLimitHigh ? params.tickLimitHigh : params.tickLimitLow;
3793
+ const swapping = [
3794
+ high,
3795
+ low,
3796
+ 0n
3797
+ ];
3798
+ const notSwapping = [
3799
+ low,
3800
+ high,
3801
+ 0n
3802
+ ];
3803
+ const mintLimits = direction === "exact-out" ? swapping : notSwapping;
3804
+ const burnLimits = direction === "exact-out" ? notSwapping : swapping;
3805
+ const wrapped = {
3806
+ positionIdList: placement === "straddle" ? [
3807
+ creditTokenId,
3808
+ ...dispatch.positionIdList,
3809
+ creditTokenId
3810
+ ] : [
3811
+ ...dispatch.positionIdList,
3812
+ creditTokenId,
3813
+ creditTokenId
3814
+ ],
3815
+ finalPositionIdList: [...dispatch.finalPositionIdList],
3816
+ positionSizes: placement === "straddle" ? [
3817
+ creditPositionSize,
3818
+ ...dispatch.positionSizes,
3819
+ 0n
3820
+ ] : [
3821
+ ...dispatch.positionSizes,
3822
+ creditPositionSize,
3823
+ 0n
3824
+ ],
3825
+ tickAndSpreadLimits: placement === "straddle" ? [
3826
+ mintLimits,
3827
+ ...dispatch.tickAndSpreadLimits,
3828
+ burnLimits
3829
+ ] : [
3830
+ ...dispatch.tickAndSpreadLimits,
3831
+ mintLimits,
3832
+ burnLimits
3833
+ ],
3834
+ usePremiaAsCollateral: dispatch.usePremiaAsCollateral,
3835
+ builderCode: dispatch.builderCode
3836
+ };
3837
+ return wrapped;
3838
+ }
3839
+
3507
3840
  //#endregion
3508
3841
  //#region src/panoptic/v2/simulations/simulateDispatch.ts
3509
3842
  /**
@@ -3629,7 +3962,7 @@ async function simulateDispatch(params) {
3629
3962
 
3630
3963
  //#endregion
3631
3964
  //#region src/panoptic/v2/simulations/tokenShortfallRecovery.ts
3632
- const BPS_DENOMINATOR = 10000n;
3965
+ const BPS_DENOMINATOR$1 = 10000n;
3633
3966
  const MAX_RECOVERY_QUOTE_ATTEMPTS = 8;
3634
3967
  /**
3635
3968
  * Wrap a dispatch with a temporary credit leg that sources the shortfall.
@@ -3642,39 +3975,11 @@ const MAX_RECOVERY_QUOTE_ATTEMPTS = 8;
3642
3975
  * token's utilization — the case that fails today on a >94% utilized tracker.
3643
3976
  */
3644
3977
  function buildTokenShortfallRecoveryDispatch(params) {
3645
- const { dispatch, creditTokenId, creditPositionSize, tickLimitLow, tickLimitHigh } = params;
3646
- const low = tickLimitLow <= tickLimitHigh ? tickLimitLow : tickLimitHigh;
3647
- const high = tickLimitLow <= tickLimitHigh ? tickLimitHigh : tickLimitLow;
3648
- const mintLimits = [
3649
- high,
3650
- low,
3651
- 0n
3652
- ];
3653
- const burnLimits = [
3654
- low,
3655
- high,
3656
- 0n
3657
- ];
3658
- return {
3659
- positionIdList: [
3660
- creditTokenId,
3661
- ...dispatch.positionIdList,
3662
- creditTokenId
3663
- ],
3664
- finalPositionIdList: [...dispatch.finalPositionIdList],
3665
- positionSizes: [
3666
- creditPositionSize,
3667
- ...dispatch.positionSizes,
3668
- 0n
3669
- ],
3670
- tickAndSpreadLimits: [
3671
- mintLimits,
3672
- ...dispatch.tickAndSpreadLimits,
3673
- burnLimits
3674
- ],
3675
- usePremiaAsCollateral: dispatch.usePremiaAsCollateral,
3676
- builderCode: dispatch.builderCode
3677
- };
3978
+ return buildCreditWrappedDispatch({
3979
+ ...params,
3980
+ direction: "exact-out",
3981
+ placement: "straddle"
3982
+ });
3678
3983
  }
3679
3984
  /**
3680
3985
  * Extract a fully-decoded `NotEnoughTokens` revert from an arbitrary error.
@@ -3697,7 +4002,7 @@ function getNotEnoughTokensError(error) {
3697
4002
  }
3698
4003
  function maximumAmountIn(estimatedAmountIn, slippageBps) {
3699
4004
  if (slippageBps < 0n) throw new PanopticError("slippageBps must be non-negative");
3700
- return (estimatedAmountIn * (BPS_DENOMINATOR + slippageBps) + BPS_DENOMINATOR - 1n) / BPS_DENOMINATOR;
4005
+ return (estimatedAmountIn * (BPS_DENOMINATOR$1 + slippageBps) + BPS_DENOMINATOR$1 - 1n) / BPS_DENOMINATOR$1;
3701
4006
  }
3702
4007
  function getInputAmount(tokenFlow, tokenInIndex) {
3703
4008
  const delta = tokenInIndex === 0n ? tokenFlow.delta0 : tokenFlow.delta1;
@@ -3722,10 +4027,10 @@ async function quoteTokenShortfallRecovery(params) {
3722
4027
  reason: "invalid-shortfall",
3723
4028
  detail: `requested=${shortfallError.assetsRequested} <= balance=${shortfallError.assetBalance}`
3724
4029
  };
3725
- if (params.slippageBps < 0n || params.slippageBps > BPS_DENOMINATOR) return {
4030
+ if (params.slippageBps < 0n || params.slippageBps > BPS_DENOMINATOR$1) return {
3726
4031
  available: false,
3727
4032
  reason: "invalid-slippage",
3728
- detail: `slippageBps=${params.slippageBps} is outside [0, ${BPS_DENOMINATOR}]`
4033
+ detail: `slippageBps=${params.slippageBps} is outside [0, ${BPS_DENOMINATOR$1}]`
3729
4034
  };
3730
4035
  const tickLimitLow = params.tickLimitLow ?? MIN_TICK;
3731
4036
  const tickLimitHigh = params.tickLimitHigh ?? MAX_TICK;
@@ -4059,7 +4364,14 @@ async function getUniswapFeeHistory(params) {
4059
4364
  _meta: _meta$1
4060
4365
  };
4061
4366
  }
4062
- const [blockData, _meta] = await Promise.all([fetchUniswapFeeData(client, blockNumbers, legs, poolConfig), params._meta ? Promise.resolve(params._meta) : getBlockMeta({ client })]);
4367
+ const [blockData, blockMetadata, _meta] = await Promise.all([
4368
+ fetchUniswapFeeData(client, blockNumbers, legs, poolConfig),
4369
+ Promise.all(blockNumbers.map((blockNumber) => getBlockMeta({
4370
+ client,
4371
+ blockNumber
4372
+ }))),
4373
+ params._meta ? Promise.resolve(params._meta) : getBlockMeta({ client })
4374
+ ]);
4063
4375
  let initialFees0 = null;
4064
4376
  let initialFees1 = null;
4065
4377
  const snapshots = blockData.map((bd, i) => {
@@ -4069,7 +4381,8 @@ async function getUniswapFeeHistory(params) {
4069
4381
  initialFees1 = total1;
4070
4382
  }
4071
4383
  return {
4072
- blockNumber: blockNumbers[i],
4384
+ blockNumber: blockMetadata[i].blockNumber,
4385
+ blockTimestamp: blockMetadata[i].blockTimestamp,
4073
4386
  fees: {
4074
4387
  token0: total0 - initialFees0,
4075
4388
  token1: total1 - initialFees1
@@ -4668,6 +4981,224 @@ async function getUniswapV4PoolLiquidities(params) {
4668
4981
  };
4669
4982
  }
4670
4983
 
4984
+ //#endregion
4985
+ //#region src/panoptic/v2/reads/uniswapLpPosition.ts
4986
+ const nfpmAbi = [{
4987
+ type: "function",
4988
+ name: "positions",
4989
+ inputs: [{
4990
+ name: "tokenId",
4991
+ type: "uint256"
4992
+ }],
4993
+ outputs: [
4994
+ {
4995
+ name: "nonce",
4996
+ type: "uint96"
4997
+ },
4998
+ {
4999
+ name: "operator",
5000
+ type: "address"
5001
+ },
5002
+ {
5003
+ name: "token0",
5004
+ type: "address"
5005
+ },
5006
+ {
5007
+ name: "token1",
5008
+ type: "address"
5009
+ },
5010
+ {
5011
+ name: "fee",
5012
+ type: "uint24"
5013
+ },
5014
+ {
5015
+ name: "tickLower",
5016
+ type: "int24"
5017
+ },
5018
+ {
5019
+ name: "tickUpper",
5020
+ type: "int24"
5021
+ },
5022
+ {
5023
+ name: "liquidity",
5024
+ type: "uint128"
5025
+ },
5026
+ {
5027
+ name: "feeGrowthInside0LastX128",
5028
+ type: "uint256"
5029
+ },
5030
+ {
5031
+ name: "feeGrowthInside1LastX128",
5032
+ type: "uint256"
5033
+ },
5034
+ {
5035
+ name: "tokensOwed0",
5036
+ type: "uint128"
5037
+ },
5038
+ {
5039
+ name: "tokensOwed1",
5040
+ type: "uint128"
5041
+ }
5042
+ ],
5043
+ stateMutability: "view"
5044
+ }, {
5045
+ type: "function",
5046
+ name: "collect",
5047
+ inputs: [{
5048
+ name: "params",
5049
+ type: "tuple",
5050
+ components: [
5051
+ {
5052
+ name: "tokenId",
5053
+ type: "uint256"
5054
+ },
5055
+ {
5056
+ name: "recipient",
5057
+ type: "address"
5058
+ },
5059
+ {
5060
+ name: "amount0Max",
5061
+ type: "uint128"
5062
+ },
5063
+ {
5064
+ name: "amount1Max",
5065
+ type: "uint128"
5066
+ }
5067
+ ]
5068
+ }],
5069
+ outputs: [{
5070
+ name: "amount0",
5071
+ type: "uint256"
5072
+ }, {
5073
+ name: "amount1",
5074
+ type: "uint256"
5075
+ }],
5076
+ stateMutability: "payable"
5077
+ }];
5078
+ const MAX_UINT128 = 2n ** 128n - 1n;
5079
+ const MAX_UINT256 = 2n ** 256n - 1n;
5080
+ const Q128$1 = 2n ** 128n;
5081
+ /**
5082
+ * Whether an error thrown by a viem contract call is an on-chain revert (as
5083
+ * opposed to a transport/RPC failure such as a timeout or rate limit).
5084
+ */
5085
+ function isContractRevert(error) {
5086
+ return error instanceof BaseError && error.walk((err) => err instanceof ContractFunctionRevertedError || err instanceof ExecutionRevertedError) != null;
5087
+ }
5088
+ /**
5089
+ * Fetch a Uniswap V3 LP position's state and uncollected fees.
5090
+ *
5091
+ * Fees come from simulating `collect` with max amounts as the owner — one
5092
+ * eth_call returning the exact claimable amounts (tokensOwed + fee growth
5093
+ * since the last poke). The simulation is best-effort: if it reverts (e.g.
5094
+ * an empty position), fees fall back to 0.
5095
+ */
5096
+ async function getUniswapV3LpPositionState(params) {
5097
+ const { client, nfpmAddress, tokenId, owner, blockNumber } = params;
5098
+ const _meta = await getBlockMeta({
5099
+ client,
5100
+ blockNumber
5101
+ });
5102
+ const position = await client.readContract({
5103
+ address: nfpmAddress,
5104
+ abi: nfpmAbi,
5105
+ functionName: "positions",
5106
+ args: [tokenId],
5107
+ blockNumber: _meta.blockNumber
5108
+ });
5109
+ const [, , token0, token1, fee, tickLower, tickUpper, liquidity] = position;
5110
+ let fees0 = 0n;
5111
+ let fees1 = 0n;
5112
+ try {
5113
+ const { result } = await client.simulateContract({
5114
+ address: nfpmAddress,
5115
+ abi: nfpmAbi,
5116
+ functionName: "collect",
5117
+ args: [{
5118
+ tokenId,
5119
+ recipient: owner,
5120
+ amount0Max: MAX_UINT128,
5121
+ amount1Max: MAX_UINT128
5122
+ }],
5123
+ account: owner,
5124
+ blockNumber: _meta.blockNumber
5125
+ });
5126
+ [fees0, fees1] = result;
5127
+ } catch (error) {
5128
+ if (!isContractRevert(error)) throw error;
5129
+ }
5130
+ return {
5131
+ token0,
5132
+ token1,
5133
+ fee: Number(fee),
5134
+ tickLower: Number(tickLower),
5135
+ tickUpper: Number(tickUpper),
5136
+ liquidity,
5137
+ fees0,
5138
+ fees1,
5139
+ _meta
5140
+ };
5141
+ }
5142
+ /**
5143
+ * Uncollected fees from a feeGrowthInside delta, mirroring v4-core's
5144
+ * `Position.calculatePositionFeesAccrued`: the subtraction wraps around
5145
+ * uint256 (feeGrowthInside can legitimately underflow in-protocol).
5146
+ */
5147
+ function feesFromFeeGrowthDelta(feeGrowthInsideCurrentX128, feeGrowthInsideLastX128, liquidity) {
5148
+ const delta = feeGrowthInsideCurrentX128 - feeGrowthInsideLastX128 & MAX_UINT256;
5149
+ return delta * liquidity / Q128$1;
5150
+ }
5151
+ /**
5152
+ * Fetch a Uniswap V4 LP position's state and uncollected fees via StateView.
5153
+ *
5154
+ * The position inside PoolManager is keyed by (positionManager, tickLower,
5155
+ * tickUpper, salt) where posm uses `bytes32(tokenId)` as the salt. Fees on
5156
+ * pools with fee-taking hooks may be approximate.
5157
+ */
5158
+ async function getUniswapV4LpPositionState(params) {
5159
+ const { client, stateViewAddress, positionManagerAddress, poolId, tokenId, tickLower, tickUpper, blockNumber } = params;
5160
+ const salt = toHex(tokenId, { size: 32 });
5161
+ const _meta = await getBlockMeta({
5162
+ client,
5163
+ blockNumber
5164
+ });
5165
+ const [positionInfo, feeGrowthInside] = await client.multicall({
5166
+ allowFailure: false,
5167
+ blockNumber: _meta.blockNumber,
5168
+ contracts: [{
5169
+ address: stateViewAddress,
5170
+ abi: StateViewAbi,
5171
+ functionName: "getPositionInfo",
5172
+ args: [
5173
+ poolId,
5174
+ positionManagerAddress,
5175
+ tickLower,
5176
+ tickUpper,
5177
+ salt
5178
+ ]
5179
+ }, {
5180
+ address: stateViewAddress,
5181
+ abi: StateViewAbi,
5182
+ functionName: "getFeeGrowthInside",
5183
+ args: [
5184
+ poolId,
5185
+ tickLower,
5186
+ tickUpper
5187
+ ]
5188
+ }]
5189
+ });
5190
+ const [liquidity, feeGrowthInside0LastX128, feeGrowthInside1LastX128] = positionInfo;
5191
+ const [feeGrowthInside0X128, feeGrowthInside1X128] = feeGrowthInside;
5192
+ return {
5193
+ liquidity,
5194
+ tickLower,
5195
+ tickUpper,
5196
+ fees0: feesFromFeeGrowthDelta(feeGrowthInside0X128, feeGrowthInside0LastX128, liquidity),
5197
+ fees1: feesFromFeeGrowthDelta(feeGrowthInside1X128, feeGrowthInside1LastX128, liquidity),
5198
+ _meta
5199
+ };
5200
+ }
5201
+
4671
5202
  //#endregion
4672
5203
  //#region src/panoptic/v2/reads/priceHistory.ts
4673
5204
  /**
@@ -6598,6 +7129,251 @@ async function simulateSwapExactIn(params) {
6598
7129
  }
6599
7130
  }
6600
7131
 
7132
+ //#endregion
7133
+ //#region src/panoptic/v2/utils/priceConvert.ts
7134
+ const Q128 = 1n << 128n;
7135
+ /**
7136
+ * A non-positive price has no meaningful conversion, and `convert1to0` would
7137
+ * divide by zero — surface that as a typed SDK error rather than a RangeError
7138
+ * thrown from inside the arithmetic.
7139
+ */
7140
+ function assertPositivePrice(sqrtPriceX96) {
7141
+ if (sqrtPriceX96 <= 0n) throw new PanopticError(`sqrtPriceX96 must be positive, got ${sqrtPriceX96}`);
7142
+ }
7143
+ /** Convert a token0 amount to its token1-equivalent at the given sqrtPriceX96. */
7144
+ function convert0to1(amount, sqrtPriceX96) {
7145
+ assertPositivePrice(sqrtPriceX96);
7146
+ if (sqrtPriceX96 < Q128) return amount * sqrtPriceX96 * sqrtPriceX96 >> 192n;
7147
+ const sp2Hi = sqrtPriceX96 * sqrtPriceX96 >> 64n;
7148
+ return amount * sp2Hi >> 128n;
7149
+ }
7150
+ /** Convert a token1 amount to its token0-equivalent at the given sqrtPriceX96. */
7151
+ function convert1to0(amount, sqrtPriceX96) {
7152
+ assertPositivePrice(sqrtPriceX96);
7153
+ if (sqrtPriceX96 < Q128) {
7154
+ const denom = sqrtPriceX96 * sqrtPriceX96;
7155
+ return amount * (1n << 192n) / denom;
7156
+ }
7157
+ const sp2Hi = sqrtPriceX96 * sqrtPriceX96 >> 64n;
7158
+ return amount * (1n << 128n) / sp2Hi;
7159
+ }
7160
+ /**
7161
+ * Convert an amount denominated in `fromTokenIndex` into the other token's
7162
+ * terms, so the two sides of a pool flow can be compared on one scale.
7163
+ */
7164
+ function convertToTokenIndex(amount, fromTokenIndex, toTokenIndex, sqrtPriceX96) {
7165
+ if (fromTokenIndex === toTokenIndex) return amount;
7166
+ return fromTokenIndex === 0n ? convert0to1(amount, sqrtPriceX96) : convert1to0(amount, sqrtPriceX96);
7167
+ }
7168
+
7169
+ //#endregion
7170
+ //#region src/panoptic/v2/simulations/oneTokenFlow.ts
7171
+ const BPS_DENOMINATOR = 10000n;
7172
+ /**
7173
+ * Default {@link OneTokenFlowQuoteParams.minSwapRatioBps}: 0.5%.
7174
+ *
7175
+ * A swap pays a pool fee of roughly 5-30 bps plus slippage, so a non-target
7176
+ * flow below this fraction of the target flow cannot pay for itself — while
7177
+ * still leaving any residual small enough not to surprise someone who asked to
7178
+ * settle in one token.
7179
+ */
7180
+ const DEFAULT_MIN_SWAP_RATIO_BPS = 50n;
7181
+ function deltaAt(tokenFlow, index) {
7182
+ return index === 0n ? tokenFlow.delta0 : tokenFlow.delta1;
7183
+ }
7184
+ function balanceBeforeAt(tokenFlow, index) {
7185
+ return index === 0n ? tokenFlow.balanceBefore0 : tokenFlow.balanceBefore1;
7186
+ }
7187
+ function abs(value) {
7188
+ return value < 0n ? -value : value;
7189
+ }
7190
+ function padForSlippage(amount, slippageBps) {
7191
+ return (amount * (BPS_DENOMINATOR + slippageBps) + BPS_DENOMINATOR - 1n) / BPS_DENOMINATOR;
7192
+ }
7193
+ /**
7194
+ * Quote a dispatch wrapped so its net flow lands in `targetTokenIndex` only.
7195
+ *
7196
+ * Three simulations, all pinned to one block and with no convergence loop:
7197
+ *
7198
+ * 1. the user's dispatch as-is, to measure the non-target flow to cancel;
7199
+ * 2. the credit legs alone, to price the swap and check the source balance;
7200
+ * 3. the wrapped dispatch, whose token flow is what the user is shown and what
7201
+ * the residual dust is read from.
7202
+ */
7203
+ async function quoteOneTokenFlow(params) {
7204
+ const { targetTokenIndex } = params;
7205
+ const minSwapRatioBps = params.minSwapRatioBps ?? DEFAULT_MIN_SWAP_RATIO_BPS;
7206
+ if (targetTokenIndex !== 0n && targetTokenIndex !== 1n) return {
7207
+ available: false,
7208
+ reason: "invalid-target-token",
7209
+ detail: `targetTokenIndex=${targetTokenIndex} is neither 0 nor 1`
7210
+ };
7211
+ if (params.slippageBps < 0n || params.slippageBps > BPS_DENOMINATOR) return {
7212
+ available: false,
7213
+ reason: "invalid-slippage",
7214
+ detail: `slippageBps=${params.slippageBps} is outside [0, ${BPS_DENOMINATOR}]`
7215
+ };
7216
+ const tickLimitLow = params.tickLimitLow ?? MIN_TICK;
7217
+ const tickLimitHigh = params.tickLimitHigh ?? MAX_TICK;
7218
+ if (tickLimitLow >= tickLimitHigh) return {
7219
+ available: false,
7220
+ reason: "invalid-tick-limits",
7221
+ detail: `tickLimitLow=${tickLimitLow} >= tickLimitHigh=${tickLimitHigh}`
7222
+ };
7223
+ const otherTokenIndex = targetTokenIndex === 0n ? 1n : 0n;
7224
+ const targetBlockNumber = params.blockNumber ?? await params.client.getBlockNumber();
7225
+ const simulateArgs = {
7226
+ client: params.client,
7227
+ poolAddress: params.poolAddress,
7228
+ account: params.account,
7229
+ existingPositionIdList: params.existingPositionIds,
7230
+ blockNumber: targetBlockNumber
7231
+ };
7232
+ const pool = await getPool({
7233
+ client: params.client,
7234
+ poolAddress: params.poolAddress,
7235
+ chainId: params.chainId,
7236
+ blockNumber: targetBlockNumber
7237
+ });
7238
+ const token0 = pool.collateralTracker0.token;
7239
+ const token1 = pool.collateralTracker1.token;
7240
+ const targetToken = targetTokenIndex === 0n ? token0 : token1;
7241
+ const otherToken = otherTokenIndex === 0n ? token0 : token1;
7242
+ const otherTracker = otherTokenIndex === 0n ? pool.collateralTracker0.address : pool.collateralTracker1.address;
7243
+ const baseSimulation = await simulateDispatch({
7244
+ ...simulateArgs,
7245
+ ...params.dispatch
7246
+ });
7247
+ let swapAmount;
7248
+ let direction;
7249
+ let baseTargetDelta = null;
7250
+ if (baseSimulation.success && baseSimulation.tokenFlow !== void 0) {
7251
+ baseTargetDelta = deltaAt(baseSimulation.tokenFlow, targetTokenIndex);
7252
+ const otherDelta = deltaAt(baseSimulation.tokenFlow, otherTokenIndex);
7253
+ if (otherDelta === 0n) return {
7254
+ available: false,
7255
+ reason: "already-single-token",
7256
+ detail: "the dispatch does not move the non-target token"
7257
+ };
7258
+ direction = otherDelta < 0n ? "exact-out" : "exact-in";
7259
+ swapAmount = abs(otherDelta);
7260
+ } else {
7261
+ const shortfall = baseSimulation.success ? null : getNotEnoughTokensError(baseSimulation.error);
7262
+ if (shortfall === null) return {
7263
+ available: false,
7264
+ reason: "base-simulation-failed",
7265
+ detail: baseSimulation.success ? "base simulation returned no token flow" : `base simulation reverted: ${baseSimulation.error.message}`,
7266
+ error: baseSimulation.success ? void 0 : baseSimulation.error
7267
+ };
7268
+ if (shortfall.tokenAddress.toLowerCase() !== otherTracker.toLowerCase()) return {
7269
+ available: false,
7270
+ reason: "base-simulation-failed",
7271
+ detail: `shortfall is in ${shortfall.tokenAddress}, not the non-target collateral ${otherTracker}`,
7272
+ error: baseSimulation.success ? void 0 : baseSimulation.error
7273
+ };
7274
+ const pending = shortfall.assetsRequested - shortfall.assetBalance;
7275
+ if (pending <= 0n) return {
7276
+ available: false,
7277
+ reason: "base-simulation-failed",
7278
+ detail: `requested=${shortfall.assetsRequested} <= balance=${shortfall.assetBalance}`
7279
+ };
7280
+ direction = "exact-out";
7281
+ swapAmount = pending;
7282
+ }
7283
+ if (baseTargetDelta !== null && minSwapRatioBps > 0n) {
7284
+ const swapInTargetTerms = convertToTokenIndex(swapAmount, otherTokenIndex, targetTokenIndex, pool.sqrtPriceX96);
7285
+ if (swapInTargetTerms * BPS_DENOMINATOR < minSwapRatioBps * abs(baseTargetDelta)) return {
7286
+ available: false,
7287
+ reason: "below-threshold",
7288
+ detail: `non-target flow ${swapInTargetTerms} (in target token terms) is below ${minSwapRatioBps}bps of the target flow ${abs(baseTargetDelta)}`
7289
+ };
7290
+ }
7291
+ const collisionIds = Array.from(new Set([
7292
+ ...params.existingPositionIds,
7293
+ ...params.dispatch.positionIdList,
7294
+ ...params.dispatch.finalPositionIdList
7295
+ ]));
7296
+ const credit = buildUniqueCredit(pool.poolId, otherTokenIndex, otherTokenIndex, pool.currentTick, pool.tickSpacing, collisionIds, swapAmount);
7297
+ const placement = direction === "exact-out" ? "straddle" : "append";
7298
+ const creditWrapArgs = {
7299
+ creditTokenId: credit.tokenId,
7300
+ creditPositionSize: credit.adjustedSize,
7301
+ tickLimitLow,
7302
+ tickLimitHigh,
7303
+ direction,
7304
+ placement
7305
+ };
7306
+ let estimatedCounterAmount = 0n;
7307
+ let maximumAmountIn$1 = 0n;
7308
+ if (direction === "exact-out") {
7309
+ const swapOnlyDispatch = buildCreditWrappedDispatch({
7310
+ ...creditWrapArgs,
7311
+ dispatch: {
7312
+ positionIdList: [],
7313
+ finalPositionIdList: [...params.existingPositionIds],
7314
+ positionSizes: [],
7315
+ tickAndSpreadLimits: [],
7316
+ usePremiaAsCollateral: false,
7317
+ builderCode: 0n
7318
+ }
7319
+ });
7320
+ const swapSimulation = await simulateDispatch({
7321
+ ...simulateArgs,
7322
+ ...swapOnlyDispatch
7323
+ });
7324
+ if (!swapSimulation.success || swapSimulation.tokenFlow === void 0) return {
7325
+ available: false,
7326
+ reason: "swap-unavailable",
7327
+ detail: swapSimulation.success ? "swap-only simulation returned no token flow" : `swap-only simulation reverted: ${swapSimulation.error.message}`,
7328
+ error: swapSimulation.success ? void 0 : swapSimulation.error
7329
+ };
7330
+ estimatedCounterAmount = abs(deltaAt(swapSimulation.tokenFlow, targetTokenIndex));
7331
+ maximumAmountIn$1 = padForSlippage(estimatedCounterAmount, params.slippageBps);
7332
+ const sourceBalance = balanceBeforeAt(swapSimulation.tokenFlow, targetTokenIndex);
7333
+ if (sourceBalance < maximumAmountIn$1) return {
7334
+ available: false,
7335
+ reason: "swap-unavailable",
7336
+ detail: `source balance ${sourceBalance} < maximumAmountIn ${maximumAmountIn$1} (estimated ${estimatedCounterAmount}, slippageBps ${params.slippageBps})`,
7337
+ error: new PanopticError("Insufficient collateral to fund the one-token-out swap")
7338
+ };
7339
+ }
7340
+ const wrappedDispatch = buildCreditWrappedDispatch({
7341
+ ...creditWrapArgs,
7342
+ dispatch: params.dispatch
7343
+ });
7344
+ const wrappedSimulation = await simulateDispatch({
7345
+ ...simulateArgs,
7346
+ ...wrappedDispatch
7347
+ });
7348
+ if (!wrappedSimulation.success || wrappedSimulation.tokenFlow === void 0) return {
7349
+ available: false,
7350
+ reason: "wrap-unavailable",
7351
+ detail: wrappedSimulation.success ? "wrapped dispatch simulation returned no token flow" : `wrapped dispatch reverted: ${wrappedSimulation.error.message}`,
7352
+ error: wrappedSimulation.success ? void 0 : wrappedSimulation.error
7353
+ };
7354
+ if (direction === "exact-in" && baseTargetDelta !== null) estimatedCounterAmount = abs(deltaAt(wrappedSimulation.tokenFlow, targetTokenIndex) - baseTargetDelta);
7355
+ return {
7356
+ available: true,
7357
+ quote: {
7358
+ targetToken,
7359
+ otherToken,
7360
+ targetTokenIndex,
7361
+ otherTokenIndex,
7362
+ direction,
7363
+ swapAmount,
7364
+ estimatedCounterAmount,
7365
+ maximumAmountIn: maximumAmountIn$1,
7366
+ slippageBps: params.slippageBps,
7367
+ netTargetChange: deltaAt(wrappedSimulation.tokenFlow, targetTokenIndex),
7368
+ residualOtherChange: deltaAt(wrappedSimulation.tokenFlow, otherTokenIndex),
7369
+ creditTokenId: credit.tokenId,
7370
+ dispatch: wrappedDispatch,
7371
+ tokenFlow: wrappedSimulation.tokenFlow,
7372
+ _meta: wrappedSimulation._meta
7373
+ }
7374
+ };
7375
+ }
7376
+
6601
7377
  //#endregion
6602
7378
  //#region src/panoptic/v2/abis/poolManager.ts
6603
7379
  /**
@@ -7732,5 +8508,5 @@ function createEventPoller(params) {
7732
8508
  }
7733
8509
 
7734
8510
  //#endregion
7735
- export { DEFAULT_RECONNECT_CONFIG, REQUIRED_BASE_ERROR_SENTINEL, addPendingPosition, addTrackedChunks, buildTokenShortfallRecoveryDispatch, calculateAccountGreeksPure, calculateSpreadWad, checkCollateralAcrossTicks, cleanupStalePendingPositions, clearPendingPositions, clearTrackedChunks, clearTradeHistory, computeV4PoolId, confirmPendingPosition, convertToAssets, convertToShares, createEventPoller, createEventSubscription, createFlowNeutralTokenId, createPoolFormatters, decodePanopticTokenURI, encodePoolKeyBytes, encodeV3PoolKeyBytes, estimateBlockNumbers, estimateCollateralRequired, failPendingPosition, formatBlockNumber, formatBps, formatCompact, formatDatetime, formatDuration, formatDurationSeconds, formatFeeTier, formatGas, formatGwei, formatPoolIdHex, formatRatioPercent, formatTimestamp, formatTimestampLocale, formatTokenIdHex, formatTokenIdShort, formatTxHash, formatUtilization, formatWei, getAccountBuyingPower, getAccountGreeks, getAccountHistory, getAccountPremia, getChunkLiquidities, getChunkSpreads, getClosedPositions, getCollateralSharePrices, getCollateralTotalAssetsBatch, getDeltaHedgeParams, getEnforcedTickLimits, getFactoryConstructMetadata, getFactoryOwnerOf, getFactoryTokenURI, getGuardianUnlockState, getItmAmounts, getMarginBuffer, getMaxPositionSize, getMaxRedeem, getMaxWithdrawable, getNativeTokenPrice, getNotEnoughTokensError, getOpenPositionPreview, getPanopticPoolAddress, getPanopticPoolFromPoolId, getPendingPositions, getPoolDeploymentBlock, getPoolDisplayId, getPoolLiquidities, getPortfolioValue, getPositionChunkData, getPositionEnrichmentData, getPositionsWithPremia, getPriceHistory, getRealizedPnL, getRequiredCreditForITM, getSafeMode, getStreamiaHistory, getSyncStatus, getTokenListId, getTrackedChunks, getTradeHistory, getUniswapFeeHistory, getUniswapV3PoolFromId, getUniswapV3PoolInfo, getUniswapV3PoolLiquidities, getUniswapV4PoolBasicState, getUniswapV4PoolInfo, getUniswapV4PoolKeyFromId, getUniswapV4PoolLiquidities, interpolateBlocks, minePoolAddress, minePoolAddressLocalAsync, multicallRead, optimizeTokenIdRiskPartners, parseBps, parseCollateralLog, parsePoolLog, parseTokenListId, previewDeposit, previewMint, previewRedeem, previewWithdraw, quoteTokenShortfallRecovery, reconstructFromEvents, removeTrackedChunks, resolveBlockNumbers, resolvePanopticPoolFromPoolId, resolveUniswapV4PoolKey, saveClosedPosition, scanChunks, simulateBatchDispatch, simulateClosePosition, simulateDeployNewPool, simulateDeposit, simulateDispatch, simulateForceExercise, simulateLiquidate, simulateSFPMBurn, simulateSFPMMint, simulateSettle, simulateSwapExactIn, simulateSwapExactOut, simulateWithdraw, truncateAddress, watchEvents };
7736
- //# sourceMappingURL=v2-DHJxPCAp.js.map
8511
+ export { DEFAULT_MIN_SWAP_RATIO_BPS, DEFAULT_RECONNECT_CONFIG, REQUIRED_BASE_ERROR_SENTINEL, addPendingPosition, addTrackedChunks, buildCreditWrappedDispatch, buildSfpmSwapCalldata, buildSfpmSwapPlan, buildTokenShortfallRecoveryDispatch, calculateAccountGreeksPure, calculateSpreadWad, checkCollateralAcrossTicks, cleanupStalePendingPositions, clearPendingPositions, clearTrackedChunks, clearTradeHistory, computeV4PoolId, confirmPendingPosition, convertToAssets, convertToShares, createEventPoller, createEventSubscription, createFlowNeutralTokenId, createPoolFormatters, decodePanopticTokenURI, encodePoolKeyBytes, encodeV3PoolKeyBytes, ensureSfpmV3PoolInitialized, estimateBlockNumbers, estimateCollateralRequired, failPendingPosition, feesFromFeeGrowthDelta, fetchSfpmV3PoolId, formatBlockNumber, formatBps, formatCompact, formatDatetime, formatDuration, formatDurationSeconds, formatFeeTier, formatGas, formatGwei, formatPoolIdHex, formatRatioPercent, formatTimestamp, formatTimestampLocale, formatTokenIdHex, formatTokenIdShort, formatTxHash, formatUtilization, formatWei, getAccountBuyingPower, getAccountGreeks, getAccountHistory, getAccountPremia, getChunkLiquidities, getChunkSpreads, getClosedPositions, getCollateralSharePrices, getCollateralTotalAssetsBatch, getDeltaHedgeParams, getEnforcedTickLimits, getFactoryConstructMetadata, getFactoryOwnerOf, getFactoryTokenURI, getGuardianUnlockState, getItmAmounts, getMarginBuffer, getMaxPositionSize, getMaxRedeem, getMaxWithdrawable, getNativeTokenPrice, getNotEnoughTokensError, getOpenPositionPreview, getPanopticPoolAddress, getPanopticPoolFromPoolId, getPendingPositions, getPoolDeploymentBlock, getPoolDisplayId, getPoolLiquidities, getPortfolioValue, getPositionChunkData, getPositionEnrichmentData, getPositionsWithPremia, getPriceHistory, getRealizedPnL, getRequiredCreditForITM, getSafeMode, getStreamiaHistory, getSyncStatus, getTokenListId, getTrackedChunks, getTradeHistory, getUniswapFeeHistory, getUniswapV3LpPositionState, getUniswapV3PoolFromId, getUniswapV3PoolInfo, getUniswapV3PoolLiquidities, getUniswapV4LpPositionState, getUniswapV4PoolBasicState, getUniswapV4PoolInfo, getUniswapV4PoolKeyFromId, getUniswapV4PoolLiquidities, interpolateBlocks, minePoolAddress, minePoolAddressLocalAsync, multicallRead, optimizeTokenIdRiskPartners, parseBps, parseCollateralLog, parsePoolLog, parseTokenListId, previewDeposit, previewMint, previewRedeem, previewWithdraw, quoteOneTokenFlow, quoteSfpmSwap, quoteTokenShortfallRecovery, reconstructFromEvents, removeTrackedChunks, resolveBlockNumbers, resolvePanopticPoolFromPoolId, resolveUniswapV4PoolKey, saveClosedPosition, scanChunks, simulateBatchDispatch, simulateClosePosition, simulateDeployNewPool, simulateDeposit, simulateDispatch, simulateForceExercise, simulateLiquidate, simulateSFPMBurn, simulateSFPMMint, simulateSettle, simulateSwapExactIn, simulateSwapExactOut, simulateWithdraw, slippageBpsToTickDistance, truncateAddress, watchEvents };
8512
+ //# sourceMappingURL=v2-D0dEatKb.js.map