@panoptic-eng/sdk 1.0.31 → 1.0.33

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 (68) 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 +21 -1
  7. package/dist/deployments.d.ts.map +1 -1
  8. package/dist/deployments.js +13 -1
  9. package/dist/deployments.js.map +1 -1
  10. package/dist/{index-DVtqKAH3.d.ts → index-CURXeKNy.d.ts} +440 -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-Cp-nCeV2.d.ts.map +1 -0
  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 +1 -1
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +2 -324
  19. package/dist/index.js.map +1 -1
  20. package/dist/{irm-BNtE1Dmg.d.ts → irm-Dw2agmGR.d.ts} +1 -1
  21. package/dist/{irm-BNtE1Dmg.d.ts.map → irm-Dw2agmGR.d.ts.map} +1 -1
  22. package/dist/{irm-iAI9hTYQ.js → irm-JPcV9ykY.js} +333 -3
  23. package/dist/irm-JPcV9ykY.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 +27 -13
  28. package/dist/panoptic/v2/react-public.d.ts.map +1 -1
  29. package/dist/panoptic/v2/react-public.js +44 -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-D3B5BKml.js → v2-B68I6nwv.js} +870 -65
  54. package/dist/v2-B68I6nwv.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-DQS8Luef.d.ts.map +0 -1
  63. package/dist/index-DVtqKAH3.d.ts.map +0 -1
  64. package/dist/irm-iAI9hTYQ.js.map +0 -1
  65. package/dist/oracle-DaNTHk7B.d.ts.map +0 -1
  66. package/dist/router-DKFR_vnu.js.map +0 -1
  67. package/dist/types-Cy6v8iPf.d.ts.map +0 -1
  68. package/dist/v2-D3B5BKml.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-JPcV9ykY.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) {
@@ -1596,6 +1867,35 @@ async function getMaxWithdrawable(params) {
1596
1867
  };
1597
1868
  }
1598
1869
  /**
1870
+ * Read the maximum number of shares an account can redeem from a CollateralTracker.
1871
+ *
1872
+ * This is the ERC4626 `maxRedeem(owner)`, i.e. `min(availableShares, balanceOf(owner))`,
1873
+ * and returns 0 when the account has open positions (legs). Redeeming exactly this many
1874
+ * shares burns the account's full (available) share balance, so a MAX withdraw leaves no
1875
+ * rounding dust — unlike an assets-based `withdraw`, which round-trips shares↔assets.
1876
+ *
1877
+ * @param params - The parameters
1878
+ * @returns Maximum redeemable shares with block metadata
1879
+ */
1880
+ async function getMaxRedeem(params) {
1881
+ const { client, collateralTrackerAddress, account, blockNumber } = params;
1882
+ const targetBlockNumber = blockNumber ?? await client.getBlockNumber();
1883
+ const [maxRedeem, _meta] = await Promise.all([client.readContract({
1884
+ address: collateralTrackerAddress,
1885
+ abi: collateralTrackerV2Abi,
1886
+ functionName: "maxRedeem",
1887
+ args: [account],
1888
+ blockNumber: targetBlockNumber
1889
+ }), getBlockMeta({
1890
+ client,
1891
+ blockNumber: targetBlockNumber
1892
+ })]);
1893
+ return {
1894
+ maxRedeem,
1895
+ _meta
1896
+ };
1897
+ }
1898
+ /**
1599
1899
  * Try to simulate a solvency-checked withdraw with the given amount.
1600
1900
  */
1601
1901
  async function tryWithdrawSimulation(params) {
@@ -2848,7 +3148,7 @@ function calculateAccountGreeksPure(params) {
2848
3148
  const MIN_TICK$1 = -887272n;
2849
3149
  const MAX_TICK$1 = 887272n;
2850
3150
  const FP96 = 1n << 96n;
2851
- const Q128$1 = 1n << 128n;
3151
+ const Q128$3 = 1n << 128n;
2852
3152
  /** Cap for a usage ratio with no collateral behind it. */
2853
3153
  const MAX_USAGE_BPS = 1000000n;
2854
3154
  const bigintMax = (a, b) => a > b ? a : b;
@@ -2866,16 +3166,16 @@ const MINT_BUFFER_DENOMINATOR = 10000000n;
2866
3166
  * Matches the on-chain `PanopticMath.convert0to1` truncation, with an
2867
3167
  * overflow-safe branch when `sqrtPriceX96^2` would not fit in uint256.
2868
3168
  */
2869
- function convert0to1$1(amount, sqrtPriceX96) {
2870
- 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;
2871
3171
  const sp2Hi = sqrtPriceX96 * sqrtPriceX96 >> 64n;
2872
3172
  return amount * sp2Hi >> 128n;
2873
3173
  }
2874
3174
  /**
2875
3175
  * Convert a token1 amount to its token0-equivalent at the given sqrtPriceX96.
2876
3176
  */
2877
- function convert1to0$1(amount, sqrtPriceX96) {
2878
- if (sqrtPriceX96 < Q128$1) {
3177
+ function convert1to0$2(amount, sqrtPriceX96) {
3178
+ if (sqrtPriceX96 < Q128$3) {
2879
3179
  const denom = sqrtPriceX96 * sqrtPriceX96;
2880
3180
  return amount * (1n << 192n) / denom;
2881
3181
  }
@@ -3055,10 +3355,10 @@ async function getMarginBuffer(params) {
3055
3355
  }
3056
3356
  const sqrtPriceX96 = tickToSqrtPriceX96(currentTick);
3057
3357
  const denominatedInToken = sqrtPriceX96 < FP96 ? 0 : 1;
3058
- const currentMargin0 = assets0 + convert1to0$1(assets1, sqrtPriceX96);
3059
- const requiredMargin0 = required0Native + convert1to0$1(required1Native, sqrtPriceX96);
3060
- const currentMargin1 = assets1 + convert0to1$1(assets0, sqrtPriceX96);
3061
- 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);
3062
3362
  const buffer0 = currentMargin0 - requiredMargin0;
3063
3363
  const buffer1 = currentMargin1 - requiredMargin1;
3064
3364
  const bufferPercent0 = requiredMargin0 === 0n ? null : buffer0 * 10000n / requiredMargin0;
@@ -3334,14 +3634,14 @@ async function getCollateralTotalAssetsBatch(client, collateralTrackerAddresses,
3334
3634
 
3335
3635
  //#endregion
3336
3636
  //#region src/panoptic/v2/reads/buyingPower.ts
3337
- const Q128 = 1n << 128n;
3338
- function convert0to1(amount, sqrtPriceX96) {
3339
- 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;
3340
3640
  const sp2Hi = sqrtPriceX96 * sqrtPriceX96 >> 64n;
3341
3641
  return amount * sp2Hi >> 128n;
3342
3642
  }
3343
- function convert1to0(amount, sqrtPriceX96) {
3344
- if (sqrtPriceX96 < Q128) {
3643
+ function convert1to0$1(amount, sqrtPriceX96) {
3644
+ if (sqrtPriceX96 < Q128$2) {
3345
3645
  const denom = sqrtPriceX96 * sqrtPriceX96;
3346
3646
  return amount * (1n << 192n) / denom;
3347
3647
  }
@@ -3462,10 +3762,10 @@ async function getAccountBuyingPower(params) {
3462
3762
  }
3463
3763
  }
3464
3764
  const sqrtPriceX96 = tickToSqrtPriceX96(currentTick);
3465
- const collateralBalance0 = assets0 + convert1to0(assets1, sqrtPriceX96);
3466
- const requiredCollateral0 = required0Native + convert1to0(required1Native, sqrtPriceX96);
3467
- const collateralBalance1 = assets1 + convert0to1(assets0, sqrtPriceX96);
3468
- 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);
3469
3769
  return {
3470
3770
  collateralBalance0,
3471
3771
  requiredCollateral0,
@@ -3475,6 +3775,68 @@ async function getAccountBuyingPower(params) {
3475
3775
  };
3476
3776
  }
3477
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
+
3478
3840
  //#endregion
3479
3841
  //#region src/panoptic/v2/simulations/simulateDispatch.ts
3480
3842
  /**
@@ -3600,7 +3962,7 @@ async function simulateDispatch(params) {
3600
3962
 
3601
3963
  //#endregion
3602
3964
  //#region src/panoptic/v2/simulations/tokenShortfallRecovery.ts
3603
- const BPS_DENOMINATOR = 10000n;
3965
+ const BPS_DENOMINATOR$1 = 10000n;
3604
3966
  const MAX_RECOVERY_QUOTE_ATTEMPTS = 8;
3605
3967
  /**
3606
3968
  * Wrap a dispatch with a temporary credit leg that sources the shortfall.
@@ -3613,39 +3975,11 @@ const MAX_RECOVERY_QUOTE_ATTEMPTS = 8;
3613
3975
  * token's utilization — the case that fails today on a >94% utilized tracker.
3614
3976
  */
3615
3977
  function buildTokenShortfallRecoveryDispatch(params) {
3616
- const { dispatch, creditTokenId, creditPositionSize, tickLimitLow, tickLimitHigh } = params;
3617
- const low = tickLimitLow <= tickLimitHigh ? tickLimitLow : tickLimitHigh;
3618
- const high = tickLimitLow <= tickLimitHigh ? tickLimitHigh : tickLimitLow;
3619
- const mintLimits = [
3620
- high,
3621
- low,
3622
- 0n
3623
- ];
3624
- const burnLimits = [
3625
- low,
3626
- high,
3627
- 0n
3628
- ];
3629
- return {
3630
- positionIdList: [
3631
- creditTokenId,
3632
- ...dispatch.positionIdList,
3633
- creditTokenId
3634
- ],
3635
- finalPositionIdList: [...dispatch.finalPositionIdList],
3636
- positionSizes: [
3637
- creditPositionSize,
3638
- ...dispatch.positionSizes,
3639
- 0n
3640
- ],
3641
- tickAndSpreadLimits: [
3642
- mintLimits,
3643
- ...dispatch.tickAndSpreadLimits,
3644
- burnLimits
3645
- ],
3646
- usePremiaAsCollateral: dispatch.usePremiaAsCollateral,
3647
- builderCode: dispatch.builderCode
3648
- };
3978
+ return buildCreditWrappedDispatch({
3979
+ ...params,
3980
+ direction: "exact-out",
3981
+ placement: "straddle"
3982
+ });
3649
3983
  }
3650
3984
  /**
3651
3985
  * Extract a fully-decoded `NotEnoughTokens` revert from an arbitrary error.
@@ -3668,7 +4002,7 @@ function getNotEnoughTokensError(error) {
3668
4002
  }
3669
4003
  function maximumAmountIn(estimatedAmountIn, slippageBps) {
3670
4004
  if (slippageBps < 0n) throw new PanopticError("slippageBps must be non-negative");
3671
- return (estimatedAmountIn * (BPS_DENOMINATOR + slippageBps) + BPS_DENOMINATOR - 1n) / BPS_DENOMINATOR;
4005
+ return (estimatedAmountIn * (BPS_DENOMINATOR$1 + slippageBps) + BPS_DENOMINATOR$1 - 1n) / BPS_DENOMINATOR$1;
3672
4006
  }
3673
4007
  function getInputAmount(tokenFlow, tokenInIndex) {
3674
4008
  const delta = tokenInIndex === 0n ? tokenFlow.delta0 : tokenFlow.delta1;
@@ -3693,10 +4027,10 @@ async function quoteTokenShortfallRecovery(params) {
3693
4027
  reason: "invalid-shortfall",
3694
4028
  detail: `requested=${shortfallError.assetsRequested} <= balance=${shortfallError.assetBalance}`
3695
4029
  };
3696
- if (params.slippageBps < 0n || params.slippageBps > BPS_DENOMINATOR) return {
4030
+ if (params.slippageBps < 0n || params.slippageBps > BPS_DENOMINATOR$1) return {
3697
4031
  available: false,
3698
4032
  reason: "invalid-slippage",
3699
- detail: `slippageBps=${params.slippageBps} is outside [0, ${BPS_DENOMINATOR}]`
4033
+ detail: `slippageBps=${params.slippageBps} is outside [0, ${BPS_DENOMINATOR$1}]`
3700
4034
  };
3701
4035
  const tickLimitLow = params.tickLimitLow ?? MIN_TICK;
3702
4036
  const tickLimitHigh = params.tickLimitHigh ?? MAX_TICK;
@@ -4030,7 +4364,14 @@ async function getUniswapFeeHistory(params) {
4030
4364
  _meta: _meta$1
4031
4365
  };
4032
4366
  }
4033
- 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
+ ]);
4034
4375
  let initialFees0 = null;
4035
4376
  let initialFees1 = null;
4036
4377
  const snapshots = blockData.map((bd, i) => {
@@ -4040,7 +4381,8 @@ async function getUniswapFeeHistory(params) {
4040
4381
  initialFees1 = total1;
4041
4382
  }
4042
4383
  return {
4043
- blockNumber: blockNumbers[i],
4384
+ blockNumber: blockMetadata[i].blockNumber,
4385
+ blockTimestamp: blockMetadata[i].blockTimestamp,
4044
4386
  fees: {
4045
4387
  token0: total0 - initialFees0,
4046
4388
  token1: total1 - initialFees1
@@ -4639,6 +4981,224 @@ async function getUniswapV4PoolLiquidities(params) {
4639
4981
  };
4640
4982
  }
4641
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
+
4642
5202
  //#endregion
4643
5203
  //#region src/panoptic/v2/reads/priceHistory.ts
4644
5204
  /**
@@ -6569,6 +7129,251 @@ async function simulateSwapExactIn(params) {
6569
7129
  }
6570
7130
  }
6571
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
+
6572
7377
  //#endregion
6573
7378
  //#region src/panoptic/v2/abis/poolManager.ts
6574
7379
  /**
@@ -7703,5 +8508,5 @@ function createEventPoller(params) {
7703
8508
  }
7704
8509
 
7705
8510
  //#endregion
7706
- 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, 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 };
7707
- //# sourceMappingURL=v2-D3B5BKml.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-B68I6nwv.js.map