@panoptic-eng/sdk 1.0.37 → 1.0.38

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 (85) hide show
  1. package/dist/cow/index.d.ts +4 -5
  2. package/dist/cow/index.js +3 -3
  3. package/dist/cow/types.d.ts +3 -2
  4. package/dist/{cow-aX6of9TS.js → cow-DB8siv6M.js} +2 -2
  5. package/dist/{cow-aX6of9TS.js.map → cow-DB8siv6M.js.map} +1 -1
  6. package/dist/deployments.d.ts +3 -2
  7. package/dist/deployments.d.ts.map +1 -1
  8. package/dist/deployments.js +4 -0
  9. package/dist/deployments.js.map +1 -1
  10. package/dist/{position-C8rkkKhi.js → getTrackedPositionIds-zYPIs5dt.js} +1086 -34
  11. package/dist/getTrackedPositionIds-zYPIs5dt.js.map +1 -0
  12. package/dist/hypoVault/vaultDisplayNames.d.ts +2 -0
  13. package/dist/hypoVault/vaultDisplayNames.js +4 -0
  14. package/dist/{index-CURXeKNy.d.ts → index-BhEBx83X.d.ts} +405 -351
  15. package/dist/index-BhEBx83X.d.ts.map +1 -0
  16. package/dist/index-Bo9tHtD7.d.ts +1901 -0
  17. package/dist/index-Bo9tHtD7.d.ts.map +1 -0
  18. package/dist/{index-Cp-nCeV2.d.ts → index-CttjEZ-S.d.ts} +3 -4
  19. package/dist/index-CttjEZ-S.d.ts.map +1 -0
  20. package/dist/{index-Zvu-rJpV.d.ts → index-DktZq9Kb.d.ts} +2 -2
  21. package/dist/{index-Zvu-rJpV.d.ts.map → index-DktZq9Kb.d.ts.map} +1 -1
  22. package/dist/index.d.ts +159 -39
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +319 -66
  25. package/dist/index.js.map +1 -1
  26. package/dist/{irm-DnM96-X4.js → irm-ChkvYphK.js} +3 -318
  27. package/dist/irm-ChkvYphK.js.map +1 -0
  28. package/dist/{irm-CmgC1Mqi.d.ts → irm-D2WTHtuo.d.ts} +3 -30
  29. package/dist/{irm-CmgC1Mqi.d.ts.map → irm-D2WTHtuo.d.ts.map} +1 -1
  30. package/dist/irm-D5plRhpB.d.ts +32 -0
  31. package/dist/irm-D5plRhpB.d.ts.map +1 -0
  32. package/dist/panoptic/v2/greeks/index.d.ts +3 -2
  33. package/dist/panoptic/v2/index.d.ts +7 -8
  34. package/dist/panoptic/v2/index.js +7 -6
  35. package/dist/panoptic/v2/react-public.d.ts +27 -11
  36. package/dist/panoptic/v2/react-public.d.ts.map +1 -1
  37. package/dist/panoptic/v2/react-public.js +48 -8
  38. package/dist/panoptic/v2/react-public.js.map +1 -1
  39. package/dist/panoptic/v2/types/index.d.ts +3 -0
  40. package/dist/panoptic/v2/types/index.js +0 -0
  41. package/dist/quote-DeHNUR0Z.d.ts +52 -0
  42. package/dist/quote-DeHNUR0Z.d.ts.map +1 -0
  43. package/dist/{router-CS86ptMu.js → router-BCUJkY8j.js} +3 -3
  44. package/dist/{router-CS86ptMu.js.map → router-BCUJkY8j.js.map} +1 -1
  45. package/dist/{router-DcoU2KmV.js → router-tCRa0ekE.js} +2 -2
  46. package/dist/{router-DcoU2KmV.js.map → router-tCRa0ekE.js.map} +1 -1
  47. package/dist/src-CRAtPwWk.js +321 -0
  48. package/dist/src-CRAtPwWk.js.map +1 -0
  49. package/dist/test/index.d.ts +2 -2
  50. package/dist/test/index.d.ts.map +1 -1
  51. package/dist/transactionFees-CVi2TrJh.js +175 -0
  52. package/dist/transactionFees-CVi2TrJh.js.map +1 -0
  53. package/dist/transactionFees-DzDyt50k.d.ts +63 -0
  54. package/dist/transactionFees-DzDyt50k.d.ts.map +1 -0
  55. package/dist/{types-D_jcYss-.d.ts → types-Cad5m4Td.d.ts} +3 -5
  56. package/dist/types-Cad5m4Td.d.ts.map +1 -0
  57. package/dist/{types-BHdnurYr.d.ts → types-DM0nxlZr.d.ts} +2 -2
  58. package/dist/{types-BHdnurYr.d.ts.map → types-DM0nxlZr.d.ts.map} +1 -1
  59. package/dist/uniswap/index.d.ts +4 -5
  60. package/dist/uniswap/index.d.ts.map +1 -1
  61. package/dist/uniswap/index.js +4 -4
  62. package/dist/{v2-D0dEatKb.js → v2-BxtzpUnV.js} +3267 -2675
  63. package/dist/v2-BxtzpUnV.js.map +1 -0
  64. package/dist/vault-transaction-fees.d.ts +2 -0
  65. package/dist/vault-transaction-fees.js +3 -0
  66. package/dist/vaultDisplayNames-CKpz51Hu.js +0 -0
  67. package/dist/vaultDisplayNames-Q6htUcch.d.ts +2 -0
  68. package/dist/{writes-AupZCK4M.js → writes-D3bj1wth.js} +326 -1292
  69. package/dist/writes-D3bj1wth.js.map +1 -0
  70. package/package.json +5 -1
  71. package/dist/index-CURXeKNy.d.ts.map +0 -1
  72. package/dist/index-Cp-nCeV2.d.ts.map +0 -1
  73. package/dist/irm-DnM96-X4.js.map +0 -1
  74. package/dist/position-BqIgubs5.d.ts +0 -199
  75. package/dist/position-BqIgubs5.d.ts.map +0 -1
  76. package/dist/position-C8rkkKhi.js.map +0 -1
  77. package/dist/quote-BxQkPBRg.d.ts +0 -718
  78. package/dist/quote-BxQkPBRg.d.ts.map +0 -1
  79. package/dist/simulation-zM6-YUaw.d.ts +0 -258
  80. package/dist/simulation-zM6-YUaw.d.ts.map +0 -1
  81. package/dist/tx-CbDQ58Io.d.ts +0 -367
  82. package/dist/tx-CbDQ58Io.d.ts.map +0 -1
  83. package/dist/types-D_jcYss-.d.ts.map +0 -1
  84. package/dist/v2-D0dEatKb.js.map +0 -1
  85. package/dist/writes-AupZCK4M.js.map +0 -1
@@ -1,6 +1,6 @@
1
- import { BatchValidationError$1 as BatchValidationError, DEFAULT_VEGOID$1 as DEFAULT_VEGOID, InputListFailError$1 as InputListFailError, InvalidTickLimitsError, InvalidTokenIdParameterError$1 as InvalidTokenIdParameterError, LEG_BITS$1 as LEG_BITS, LEG_LIMITS$1 as LEG_LIMITS, LEG_MASKS$1 as LEG_MASKS, LoanSlotExhaustedError$1 as LoanSlotExhaustedError, MaxRetriesExceededError$1 as MaxRetriesExceededError, MissingPositionIdsError$1 as MissingPositionIdsError, NoLoanPositionsError, OracleRateLimitedError$1 as OracleRateLimitedError, PanopticError, ProviderLagError$1 as ProviderLagError, STRIKE_CONVERSION_FACTOR, SafeModeError$1 as SafeModeError, StaleDataError$1 as StaleDataError, SwapTokenMismatchError$1 as SwapTokenMismatchError, SyncTimeoutError$1 as SyncTimeoutError, TOKEN_ID_BITS$1 as TOKEN_ID_BITS, UnhealthyPoolError$1 as UnhealthyPoolError, collateralTrackerV2Abi, createTxResult, decodeLeftRightUnsigned$1 as decodeLeftRightUnsigned, getBlockMeta$1 as getBlockMeta, getPool, getPoolMetadata, getPositions$1 as getPositions, panopticErrorsAbi, panopticFactoryV3Abi, panopticFactoryV4Abi, panopticPoolV2Abi, parsePanopticError$1 as parsePanopticError, submitWrite } from "./position-C8rkkKhi.js";
2
- import { REORG_DEPTH, SCHEMA_VERSION, STORAGE_PREFIX, calculatePositionGreeks, tickLimits } from "./greeks-GysWXct-.js";
3
- import { decodeFunctionData, decodeFunctionResult, encodeFunctionData, erc20Abi, getAddress, parseAbi, toFunctionSelector } from "viem";
1
+ import { BatchValidationError$1 as BatchValidationError, DEFAULT_VEGOID$1 as DEFAULT_VEGOID, InputListFailError$1 as InputListFailError, InvalidTickLimitsError, InvalidTokenIdParameterError$1 as InvalidTokenIdParameterError, LEG_BITS$1 as LEG_BITS, LEG_LIMITS$1 as LEG_LIMITS, LEG_MASKS$1 as LEG_MASKS, LoanSlotExhaustedError$1 as LoanSlotExhaustedError, MaxRetriesExceededError$1 as MaxRetriesExceededError, MissingPositionIdsError$1 as MissingPositionIdsError, NoLoanPositionsError, OracleRateLimitedError$1 as OracleRateLimitedError, PanopticError$1 as PanopticError, PositionSnapshotNotFoundError$1 as PositionSnapshotNotFoundError, ProviderLagError$1 as ProviderLagError, STRIKE_CONVERSION_FACTOR, SwapTokenMismatchError$1 as SwapTokenMismatchError, SyncTimeoutError$1 as SyncTimeoutError, TOKEN_ID_BITS$1 as TOKEN_ID_BITS, collateralTrackerV2Abi, createTxResult, decodeLeftRightUnsigned$1 as decodeLeftRightUnsigned, decodeOracleTiming$1 as decodeOracleTiming, getBlockMeta$1 as getBlockMeta, getOpenPositionIds$1 as getOpenPositionIds, getPool$1 as getPool, getPoolMetaKey$1 as getPoolMetaKey, getPoolMetadata$1 as getPoolMetadata, getPositionMetaKey$1 as getPositionMetaKey, getPositions$1 as getPositions, getPositionsKey$1 as getPositionsKey, getSyncCheckpointKey$1 as getSyncCheckpointKey, getTrackedPositionIds$1 as getTrackedPositionIds, jsonSerializer$1 as jsonSerializer, oracleEpochAt$1 as oracleEpochAt, panopticErrorsAbi, panopticFactoryV3Abi, panopticFactoryV4Abi, panopticPoolV2Abi, parsePanopticError$1 as parsePanopticError, submitWrite } from "./getTrackedPositionIds-zYPIs5dt.js";
2
+ import { REORG_DEPTH, calculatePositionGreeks, tickLimits } from "./greeks-GysWXct-.js";
3
+ import { decodeFunctionResult, encodeFunctionData, erc20Abi, getAbiItem } from "viem";
4
4
 
5
5
  //#region src/panoptic/v2/writes/broadcaster.ts
6
6
  /**
@@ -341,822 +341,6 @@ async function redeemAndWait(params) {
341
341
  return result.wait();
342
342
  }
343
343
 
344
- //#endregion
345
- //#region src/panoptic/v2/storage/fileStorage.ts
346
- /**
347
- * Creates a file-based storage adapter for Node.js environments.
348
- * Stores data as JSON files in the specified directory.
349
- *
350
- * The directory will be created if it doesn't exist.
351
- *
352
- * @param directory - Path to the storage directory
353
- *
354
- * @example
355
- * ```typescript
356
- * import { createFileStorage, createConfig } from 'panoptic-v2-sdk'
357
- *
358
- * // Node.js only
359
- * const config = createConfig({
360
- * chainId: 1n,
361
- * transport: http('...'),
362
- * poolAddress: '0x...',
363
- * storage: createFileStorage('./panoptic-cache'),
364
- * })
365
- * ```
366
- */
367
- function createFileStorage(directory) {
368
- let fsPromises = null;
369
- let pathModule = null;
370
- let initialized = false;
371
- const ensureInitialized = async () => {
372
- if (initialized) return;
373
- fsPromises = await import("node:fs/promises");
374
- pathModule = await import("node:path");
375
- await fsPromises.mkdir(directory, { recursive: true });
376
- initialized = true;
377
- };
378
- const getFilePath = (key) => {
379
- if (!pathModule) throw new Error("File storage not initialized");
380
- const safeKey = Buffer.from(key).toString("base64url");
381
- return pathModule.join(directory, `${safeKey}.json`);
382
- };
383
- return {
384
- async get(key) {
385
- await ensureInitialized();
386
- if (!fsPromises) throw new Error("File storage not initialized");
387
- try {
388
- const filePath = getFilePath(key);
389
- const content = await fsPromises.readFile(filePath, "utf-8");
390
- return content;
391
- } catch (error) {
392
- if (error.code === "ENOENT") return null;
393
- throw error;
394
- }
395
- },
396
- async set(key, value) {
397
- await ensureInitialized();
398
- if (!fsPromises) throw new Error("File storage not initialized");
399
- const filePath = getFilePath(key);
400
- await fsPromises.writeFile(filePath, value, "utf-8");
401
- },
402
- async delete(key) {
403
- await ensureInitialized();
404
- if (!fsPromises) throw new Error("File storage not initialized");
405
- try {
406
- const filePath = getFilePath(key);
407
- await fsPromises.unlink(filePath);
408
- } catch (error) {
409
- if (error.code !== "ENOENT") throw error;
410
- }
411
- },
412
- async has(key) {
413
- await ensureInitialized();
414
- if (!fsPromises) throw new Error("File storage not initialized");
415
- try {
416
- const filePath = getFilePath(key);
417
- await fsPromises.access(filePath);
418
- return true;
419
- } catch {
420
- return false;
421
- }
422
- },
423
- async keys(prefix) {
424
- await ensureInitialized();
425
- if (!fsPromises || !pathModule) throw new Error("File storage not initialized");
426
- const result = [];
427
- try {
428
- const files = await fsPromises.readdir(directory);
429
- for (const file of files) if (file.endsWith(".json")) {
430
- const safeKey = file.slice(0, -5);
431
- try {
432
- const key = Buffer.from(safeKey, "base64url").toString();
433
- if (key.startsWith(prefix)) result.push(key);
434
- } catch {}
435
- }
436
- } catch (error) {
437
- if (error.code !== "ENOENT") throw error;
438
- }
439
- return result;
440
- },
441
- async clear(prefix) {
442
- await ensureInitialized();
443
- if (!fsPromises) throw new Error("File storage not initialized");
444
- const keysToDelete = await this.keys(prefix);
445
- for (const key of keysToDelete) await this.delete(key);
446
- }
447
- };
448
- }
449
-
450
- //#endregion
451
- //#region src/panoptic/v2/storage/keys.ts
452
- /**
453
- * Get the schema version key.
454
- */
455
- function getSchemaVersionKey() {
456
- return `${STORAGE_PREFIX}:schemaVersion`;
457
- }
458
- /**
459
- * Get the base prefix for a specific pool.
460
- */
461
- function getPoolPrefix(chainId, poolAddress) {
462
- return `${STORAGE_PREFIX}:v${SCHEMA_VERSION}:chain${chainId}:pool${poolAddress.toLowerCase()}`;
463
- }
464
- /**
465
- * Get the key for tracked position IDs.
466
- */
467
- function getPositionsKey(chainId, poolAddress, account) {
468
- return `${getPoolPrefix(chainId, poolAddress)}:positions:${account.toLowerCase()}`;
469
- }
470
- /**
471
- * Get the key for position mint metadata.
472
- */
473
- function getPositionMetaKey(chainId, poolAddress, tokenId) {
474
- return `${getPoolPrefix(chainId, poolAddress)}:positionMeta:${tokenId.toString()}`;
475
- }
476
- /**
477
- * Get the key for sync checkpoint.
478
- */
479
- function getSyncCheckpointKey(chainId, poolAddress, account) {
480
- return `${getPoolPrefix(chainId, poolAddress)}:sync:${account.toLowerCase()}`;
481
- }
482
- /**
483
- * Get the key for closed positions.
484
- */
485
- function getClosedPositionsKey(chainId, poolAddress, account) {
486
- return `${getPoolPrefix(chainId, poolAddress)}:closed:${account.toLowerCase()}`;
487
- }
488
- /**
489
- * Get the key for tracked chunks.
490
- */
491
- function getTrackedChunksKey(chainId, poolAddress) {
492
- return `${getPoolPrefix(chainId, poolAddress)}:chunks`;
493
- }
494
- /**
495
- * Get the key for pending positions.
496
- */
497
- function getPendingPositionsKey(chainId, poolAddress, account) {
498
- return `${getPoolPrefix(chainId, poolAddress)}:pending:${account.toLowerCase()}`;
499
- }
500
- /**
501
- * Get the key for pool metadata (tickSpacing, etc.).
502
- */
503
- function getPoolMetaKey(chainId, poolAddress) {
504
- return `${getPoolPrefix(chainId, poolAddress)}:poolMeta`;
505
- }
506
-
507
- //#endregion
508
- //#region src/panoptic/v2/storage/memoryStorage.ts
509
- /**
510
- * Creates an in-memory storage adapter.
511
- * Useful for testing and development.
512
- *
513
- * Data is lost when the process exits.
514
- *
515
- * @example
516
- * ```typescript
517
- * import { createMemoryStorage, createConfig } from 'panoptic-v2-sdk'
518
- *
519
- * const config = createConfig({
520
- * chainId: 1n,
521
- * transport: http('...'),
522
- * poolAddress: '0x...',
523
- * storage: createMemoryStorage(),
524
- * })
525
- * ```
526
- */
527
- function createMemoryStorage() {
528
- const store = new Map();
529
- return {
530
- async get(key) {
531
- return store.get(key) ?? null;
532
- },
533
- async set(key, value) {
534
- store.set(key, value);
535
- },
536
- async delete(key) {
537
- store.delete(key);
538
- },
539
- async has(key) {
540
- return store.has(key);
541
- },
542
- async keys(prefix) {
543
- const result = [];
544
- for (const key of store.keys()) if (key.startsWith(prefix)) result.push(key);
545
- return result;
546
- },
547
- async clear(prefix) {
548
- const keysToDelete = [];
549
- for (const key of store.keys()) if (key.startsWith(prefix)) keysToDelete.push(key);
550
- for (const key of keysToDelete) store.delete(key);
551
- }
552
- };
553
- }
554
-
555
- //#endregion
556
- //#region src/panoptic/v2/storage/serializer.ts
557
- /**
558
- * Type guard to check if a value is a tagged BigInt.
559
- */
560
- function isBigIntTagged(value) {
561
- return value !== null && typeof value === "object" && "__type" in value && value.__type === "bigint" && "value" in value && typeof value.value === "string";
562
- }
563
- /**
564
- * Replacer function for JSON.stringify that tags BigInt values.
565
- */
566
- function replacer(_key, value) {
567
- if (typeof value === "bigint") return {
568
- __type: "bigint",
569
- value: value.toString()
570
- };
571
- return value;
572
- }
573
- /**
574
- * Reviver function for JSON.parse that restores tagged BigInt values.
575
- */
576
- function reviver(_key, value) {
577
- if (isBigIntTagged(value)) return BigInt(value.value);
578
- return value;
579
- }
580
- /**
581
- * BigInt-safe JSON serializer for the Panoptic v2 SDK.
582
- *
583
- * This serializer handles BigInt values by tagging them with a special format
584
- * that can be restored on parse. The format is compatible with superjson
585
- * patterns used in Next.js and Remix for SSR hydration.
586
- *
587
- * @example
588
- * ```typescript
589
- * import { jsonSerializer } from 'panoptic-v2-sdk'
590
- *
591
- * const data = { amount: 1000000000000000000n, nested: { value: 42n } }
592
- *
593
- * // Serialize to JSON string
594
- * const json = jsonSerializer.stringify(data)
595
- * // '{"amount":{"__type":"bigint","value":"1000000000000000000"},"nested":{"value":{"__type":"bigint","value":"42"}}}'
596
- *
597
- * // Parse back to original types
598
- * const parsed = jsonSerializer.parse(json)
599
- * // { amount: 1000000000000000000n, nested: { value: 42n } }
600
- * ```
601
- */
602
- const jsonSerializer = {
603
- stringify(value, space) {
604
- return JSON.stringify(value, replacer, space);
605
- },
606
- parse(text) {
607
- return JSON.parse(text, reviver);
608
- }
609
- };
610
-
611
- //#endregion
612
- //#region src/panoptic/v2/sync/snapshotRecovery.ts
613
- const LOG_SCAN_WINDOW = 10000n;
614
- const rolesExecutionAbi = parseAbi(["function execTransactionWithRole(address to, uint256 value, bytes data, uint8 operation, bytes32 roleKey, bool shouldRevert)"]);
615
- /**
616
- * Recover position snapshot from the last dispatch transaction.
617
- * This is the primary recovery method - it finds the most recent dispatch()
618
- * call and extracts the finalPositionIdList from the calldata.
619
- *
620
- * @param params - Recovery parameters
621
- * @returns Snapshot recovery result
622
- */
623
- async function recoverSnapshot(params) {
624
- const { client, poolAddress, account, toBlock } = params;
625
- const latestBlock = toBlock ?? await client.getBlockNumber();
626
- const searchFromBlock = params.fromBlock ?? 0n;
627
- if (latestBlock < searchFromBlock) return null;
628
- try {
629
- const events = await getSnapshotEventsForWindow({
630
- client,
631
- poolAddress,
632
- account,
633
- fromBlock: searchFromBlock,
634
- toBlock: latestBlock
635
- });
636
- return await recoverSnapshotFromEvents({
637
- client,
638
- account,
639
- events
640
- });
641
- } catch (error) {
642
- if (!isRangeLimitError(error)) throw error;
643
- }
644
- let windowToBlock = latestBlock;
645
- while (true) {
646
- const windowFromBlock = windowToBlock - searchFromBlock + 1n > LOG_SCAN_WINDOW ? windowToBlock - LOG_SCAN_WINDOW + 1n : searchFromBlock;
647
- const events = await getSnapshotEventsForWindow({
648
- client,
649
- poolAddress,
650
- account,
651
- fromBlock: windowFromBlock,
652
- toBlock: windowToBlock
653
- });
654
- const snapshot = await recoverSnapshotFromEvents({
655
- client,
656
- account,
657
- events
658
- });
659
- if (snapshot) return snapshot;
660
- if (windowFromBlock === searchFromBlock) break;
661
- windowToBlock = windowFromBlock - 1n;
662
- }
663
- return null;
664
- }
665
- /**
666
- * Detect provider errors that indicate the requested getLogs block range was
667
- * too wide (so the caller should retry with smaller windows). Covers the common
668
- * phrasings used by Alchemy, Infura, and other JSON-RPC providers.
669
- */
670
- function isRangeLimitError(error) {
671
- const parts = [];
672
- let current = error;
673
- let depth = 0;
674
- while (current && typeof current === "object" && depth < 5) {
675
- const obj = current;
676
- if (typeof obj.message === "string") parts.push(obj.message);
677
- if (typeof obj.details === "string") parts.push(obj.details);
678
- current = obj.cause;
679
- depth += 1;
680
- }
681
- if (typeof error === "string") parts.push(error);
682
- const message = parts.join(" ").toLowerCase();
683
- if (!message) return false;
684
- return message.includes("block range") || message.includes("range is too large") || message.includes("range too large") || message.includes("query returned more than") || message.includes("too many results") || message.includes("log response size exceeded") || message.includes("exceeds the limit") || message.includes("range") && message.includes("limit");
685
- }
686
- async function getSnapshotEventsForWindow(params) {
687
- const { client, poolAddress, account, fromBlock, toBlock } = params;
688
- const [mintEvents, burnEvents, forceExerciseEvents, liquidationEvents] = await Promise.all([
689
- client.getLogs({
690
- address: poolAddress,
691
- event: {
692
- type: "event",
693
- name: "OptionMinted",
694
- inputs: [
695
- {
696
- type: "address",
697
- name: "recipient",
698
- indexed: true
699
- },
700
- {
701
- type: "uint256",
702
- name: "tokenId",
703
- indexed: true
704
- },
705
- {
706
- type: "uint256",
707
- name: "balanceData",
708
- indexed: false
709
- }
710
- ]
711
- },
712
- args: { recipient: account },
713
- fromBlock,
714
- toBlock
715
- }),
716
- client.getLogs({
717
- address: poolAddress,
718
- event: {
719
- type: "event",
720
- name: "OptionBurnt",
721
- inputs: [
722
- {
723
- type: "address",
724
- name: "recipient",
725
- indexed: true
726
- },
727
- {
728
- type: "uint128",
729
- name: "positionSize",
730
- indexed: false
731
- },
732
- {
733
- type: "uint256",
734
- name: "tokenId",
735
- indexed: true
736
- },
737
- {
738
- type: "int256[4]",
739
- name: "premiaByLeg",
740
- indexed: false
741
- }
742
- ]
743
- },
744
- args: { recipient: account },
745
- fromBlock,
746
- toBlock
747
- }),
748
- client.getLogs({
749
- address: poolAddress,
750
- event: {
751
- type: "event",
752
- name: "ForcedExercised",
753
- inputs: [
754
- {
755
- type: "address",
756
- name: "exercisor",
757
- indexed: true
758
- },
759
- {
760
- type: "address",
761
- name: "user",
762
- indexed: true
763
- },
764
- {
765
- type: "uint256",
766
- name: "tokenId",
767
- indexed: true
768
- },
769
- {
770
- type: "int256",
771
- name: "exerciseFee",
772
- indexed: false
773
- }
774
- ]
775
- },
776
- args: { user: account },
777
- fromBlock,
778
- toBlock
779
- }),
780
- client.getLogs({
781
- address: poolAddress,
782
- event: {
783
- type: "event",
784
- name: "AccountLiquidated",
785
- inputs: [
786
- {
787
- type: "address",
788
- name: "liquidator",
789
- indexed: true
790
- },
791
- {
792
- type: "address",
793
- name: "liquidatee",
794
- indexed: true
795
- },
796
- {
797
- type: "int256",
798
- name: "bonusAmounts",
799
- indexed: false
800
- }
801
- ]
802
- },
803
- args: { liquidatee: account },
804
- fromBlock,
805
- toBlock
806
- })
807
- ]);
808
- return [
809
- ...mintEvents,
810
- ...burnEvents,
811
- ...forceExerciseEvents,
812
- ...liquidationEvents
813
- ];
814
- }
815
- async function recoverSnapshotFromEvents(params) {
816
- const { client, account, events } = params;
817
- const allEvents = events.filter(isSnapshotCandidateEvent).sort((a, b) => {
818
- const blockDiff = Number(b.blockNumber - a.blockNumber);
819
- if (blockDiff !== 0) return blockDiff;
820
- return Number(b.logIndex - a.logIndex);
821
- });
822
- const seen = new Set();
823
- const uniqueEvents = allEvents.filter((e) => {
824
- if (seen.has(e.transactionHash)) return false;
825
- seen.add(e.transactionHash);
826
- return true;
827
- });
828
- for (const event of uniqueEvents) try {
829
- const [tx, block] = await Promise.all([client.getTransaction({ hash: event.transactionHash }), client.getBlock({ blockNumber: event.blockNumber })]);
830
- const candidates = decodeAllDispatchCalldata(tx.input);
831
- const decoded = selectDispatchForAccount(candidates, account, tx.from) ?? selectDispatchProvenByAccountEvent(candidates);
832
- if (!decoded) continue;
833
- return {
834
- success: true,
835
- positionIds: decoded.positionIds,
836
- blockNumber: event.blockNumber,
837
- blockHash: block.hash,
838
- transactionHash: event.transactionHash
839
- };
840
- } catch {}
841
- return null;
842
- }
843
- function isSnapshotCandidateEvent(event) {
844
- return event.transactionHash !== null && event.blockNumber !== null && event.logIndex !== null;
845
- }
846
- function decodeSnapshotTransaction(params) {
847
- const { account, pool, rolesContext } = params;
848
- let dispatchInput = params.input;
849
- let dispatchSender = params.from;
850
- if (rolesContext) {
851
- if (params.to === null || getAddress(params.to) !== getAddress(rolesContext.modifier) || getAddress(params.from) !== getAddress(rolesContext.member)) return null;
852
- try {
853
- const decodedRoleCall = decodeFunctionData({
854
- abi: rolesExecutionAbi,
855
- data: params.input
856
- });
857
- const [target, value, data, operation, roleKey, shouldRevert] = decodedRoleCall.args;
858
- if (!pool || getAddress(target) !== getAddress(pool) || value !== 0n || operation !== 0 || roleKey.toLowerCase() !== rolesContext.roleKey.toLowerCase() || !shouldRevert) return null;
859
- dispatchInput = data;
860
- if (!account) return null;
861
- dispatchSender = account;
862
- } catch {
863
- return null;
864
- }
865
- } else if (pool && (params.to === null || getAddress(params.to) !== getAddress(pool))) return null;
866
- const candidates = decodeAllDispatchCalldata(dispatchInput);
867
- if (candidates.length === 0) return null;
868
- return account ? selectDispatchForAccount(candidates, account, dispatchSender) : candidates[candidates.length - 1];
869
- }
870
- /**
871
- * Recover position snapshot from a specific dispatch transaction hash.
872
- *
873
- * This is an O(1) alternative to {@link recoverSnapshot} when you already
874
- * know the tx hash of the most recent dispatch. It fetches the transaction,
875
- * decodes the `finalPositionIdList` from the calldata, and returns the result
876
- * — no event scanning required.
877
- *
878
- * @param params - Recovery parameters including the transaction hash
879
- * @returns Snapshot recovery result, or null if the tx is not a dispatch call
880
- *
881
- * @example
882
- * ```typescript
883
- * const snapshot = await recoverSnapshotFromTx({
884
- * client,
885
- * transactionHash: '0xabc...',
886
- * })
887
- * if (snapshot) {
888
- * console.log('Open positions:', snapshot.positionIds)
889
- * }
890
- * ```
891
- */
892
- async function recoverSnapshotFromTx(params) {
893
- const { client, transactionHash, account, pool, rolesContext } = params;
894
- const tx = await client.getTransaction({ hash: transactionHash });
895
- if (tx.blockNumber == null) return null;
896
- const decoded = decodeSnapshotTransaction({
897
- input: tx.input,
898
- from: tx.from,
899
- to: tx.to,
900
- account,
901
- pool,
902
- rolesContext
903
- });
904
- if (!decoded) return null;
905
- const blockNumber = tx.blockNumber;
906
- const block = await client.getBlock({ blockNumber });
907
- return {
908
- success: true,
909
- positionIds: decoded.positionIds,
910
- blockNumber,
911
- blockHash: block.hash,
912
- transactionHash
913
- };
914
- }
915
- /**
916
- * Decode position IDs from dispatch calldata.
917
- *
918
- * When the tx embeds multiple dispatch/dispatchFrom calls (e.g. a force-exercise
919
- * multicall), this returns the last one — prefer {@link decodeAllDispatchCalldata}
920
- * plus {@link selectDispatchForAccount} when the account context matters.
921
- *
922
- * @param input - Transaction input data
923
- * @returns Decoded dispatch data or null if not a dispatch call
924
- */
925
- function decodeDispatchCalldata(input) {
926
- const all = decodeAllDispatchCalldata(input);
927
- return all.length > 0 ? all[all.length - 1] : null;
928
- }
929
- /**
930
- * Decode every dispatch/dispatchFrom call embedded in transaction calldata.
931
- *
932
- * A single tx can carry more than one such call — most importantly a force-exercise
933
- * `multicall` that bundles the exercisor's own `dispatch` together with the victim's
934
- * `dispatchFrom`. Returning all candidates lets the caller select the one attributable
935
- * to the relevant account ({@link selectDispatchForAccount}).
936
- *
937
- * @param input - Transaction input data
938
- * @returns All decoded dispatch calls, in calldata order (may be empty)
939
- */
940
- function decodeAllDispatchCalldata(input) {
941
- const direct = decodeDirectDispatch(input);
942
- if (direct) return [direct];
943
- return decodeWrappedDispatch(input);
944
- }
945
- /**
946
- * Select the dispatch call attributable to `account` from a set of candidates.
947
- *
948
- * - `dispatchFrom`: matches when `targetAccount === account` (builder/exercisor acting
949
- * on behalf of the account).
950
- * - `dispatch` (no `targetAccount`): matches only when the tx sender is the account.
951
- *
952
- * Returns the LAST matching candidate (in calldata order), or null when none are
953
- * attributable to `account`. The last dispatch reflects the final post-transaction
954
- * state when a single tx contains multiple dispatches for the same account (e.g. a
955
- * multicall). Matching also prevents accepting a third party's `dispatch` (e.g. the
956
- * exercisor's own, bundled in a force-exercise multicall) as the account's snapshot.
957
- *
958
- * @param candidates - Decoded dispatch calls from a single tx, in calldata order
959
- * @param account - Account whose position list we are recovering
960
- * @param txFrom - The transaction sender (`tx.from`)
961
- */
962
- function selectDispatchForAccount(candidates, account, txFrom) {
963
- const acct = account.toLowerCase();
964
- let match = null;
965
- for (const candidate of candidates) if (candidate.targetAccount) {
966
- if (candidate.targetAccount.toLowerCase() === acct) match = candidate;
967
- } else if (txFrom.toLowerCase() === acct) match = candidate;
968
- return match;
969
- }
970
- function selectDispatchProvenByAccountEvent(candidates) {
971
- let match = null;
972
- for (const candidate of candidates) if (candidate.targetAccount === void 0) match = candidate;
973
- return match;
974
- }
975
- /**
976
- * Try to decode input as a direct dispatch or dispatchFrom call.
977
- */
978
- function decodeDirectDispatch(input) {
979
- try {
980
- const decoded = decodeFunctionData({
981
- abi: panopticPoolV2Abi,
982
- data: input
983
- });
984
- if (decoded.functionName === "dispatch") {
985
- const args = decoded.args;
986
- return { positionIds: [...args[1]] };
987
- }
988
- if (decoded.functionName === "dispatchFrom") {
989
- const args = decoded.args;
990
- return {
991
- positionIds: [...args[3]],
992
- targetAccount: args[1]
993
- };
994
- }
995
- return null;
996
- } catch {
997
- return null;
998
- }
999
- }
1000
- /**
1001
- * 4-byte function selectors for dispatch/dispatchFrom, derived from the generated ABI.
1002
- * Used for scanning raw calldata inside smart contract wallet wrappers.
1003
- */
1004
- const DISPATCH_SELECTOR = toFunctionSelector(panopticPoolV2Abi.find((e) => e.type === "function" && e.name === "dispatch")).slice(2);
1005
- const DISPATCH_FROM_SELECTOR = toFunctionSelector(panopticPoolV2Abi.find((e) => e.type === "function" && e.name === "dispatchFrom")).slice(2);
1006
- /**
1007
- * Scan raw transaction input for embedded dispatch calldata.
1008
- *
1009
- * Smart contract wallets (Safe, Turnkey, ERC-4337) and vault managers
1010
- * wrap dispatch calls inside executeBatch → manage → dispatch chains.
1011
- * Instead of decoding each wrapper layer, we scan the raw hex for the
1012
- * dispatch function selector and attempt to decode from that offset.
1013
- *
1014
- * This handles arbitrary nesting depth without knowing wrapper ABIs.
1015
- */
1016
- function decodeWrappedDispatch(input) {
1017
- const hex = input.slice(2).toLowerCase();
1018
- const selectors = [DISPATCH_SELECTOR, DISPATCH_FROM_SELECTOR];
1019
- const results = [];
1020
- let offset = 0;
1021
- while (offset < hex.length) {
1022
- let earliestIdx = -1;
1023
- for (const selector of selectors) {
1024
- const idx = hex.indexOf(selector, offset);
1025
- if (idx !== -1 && (earliestIdx === -1 || idx < earliestIdx)) earliestIdx = idx;
1026
- }
1027
- if (earliestIdx === -1) break;
1028
- const candidate = `0x${hex.slice(earliestIdx)}`;
1029
- const result = decodeDirectDispatch(candidate);
1030
- if (result) results.push(result);
1031
- offset = earliestIdx + 8;
1032
- }
1033
- return results;
1034
- }
1035
-
1036
- //#endregion
1037
- //#region src/panoptic/v2/sync/getTrackedPositionIds.ts
1038
- /**
1039
- * Verify that a recovered position-id list still matches the account's current
1040
- * on-chain state. `getFullPositionsData` reverts when the list contains a
1041
- * tokenId the account no longer holds, so a successful call is proof the list
1042
- * is live. Returns `false` on revert so the caller can fall back to an
1043
- * authoritative event scan instead of trusting a stale checkpoint.
1044
- */
1045
- async function isPositionListLive(params) {
1046
- const { client, poolAddress, account, positionIds, blockNumber } = params;
1047
- if (positionIds.length === 0) return true;
1048
- try {
1049
- await client.readContract({
1050
- address: poolAddress,
1051
- abi: panopticPoolV2Abi,
1052
- functionName: "getFullPositionsData",
1053
- args: [
1054
- account,
1055
- false,
1056
- positionIds
1057
- ],
1058
- blockNumber
1059
- });
1060
- return true;
1061
- } catch {
1062
- return false;
1063
- }
1064
- }
1065
- /**
1066
- * Get all tracked position IDs for an account from local cache.
1067
- *
1068
- * This function reads from the storage adapter without making RPC calls.
1069
- * The positions returned may include closed positions if the cache is stale.
1070
- * Use `getPositions()` for authoritative on-chain data.
1071
- *
1072
- * @param params - Query parameters
1073
- * @returns Array of position token IDs
1074
- */
1075
- async function getTrackedPositionIds(params) {
1076
- const { chainId, poolAddress, account, storage } = params;
1077
- const key = getPositionsKey(chainId, poolAddress, account);
1078
- const data = await storage.get(key);
1079
- if (!data) return [];
1080
- try {
1081
- const positionIds = jsonSerializer.parse(data);
1082
- return positionIds;
1083
- } catch {
1084
- return [];
1085
- }
1086
- }
1087
- /**
1088
- * Check if a specific position ID is tracked.
1089
- *
1090
- * @param params - Query parameters
1091
- * @param tokenId - Token ID to check
1092
- * @returns Whether the position is tracked
1093
- */
1094
- async function isPositionTracked(params, tokenId) {
1095
- const positionIds = await getTrackedPositionIds(params);
1096
- return positionIds.some((id) => id === tokenId);
1097
- }
1098
- /**
1099
- * Clear all tracked positions for an account.
1100
- *
1101
- * @param params - Query parameters
1102
- */
1103
- async function clearTrackedPositions(params) {
1104
- const { chainId, poolAddress, account, storage } = params;
1105
- const key = getPositionsKey(chainId, poolAddress, account);
1106
- await storage.delete(key);
1107
- }
1108
- /**
1109
- * Get the authoritative list of open position IDs from the chain.
1110
- *
1111
- * Recovers the `finalPositionIdList` from the account's most recent
1112
- * `dispatch()` transaction calldata. This is the on-chain–validated set of
1113
- * open positions (verified against `s_positionsHash` by the contract) and
1114
- * requires only a few RPC calls regardless of account history length.
1115
- *
1116
- * This is the recommended way to get position IDs for `existingPositionIds`
1117
- * (openPosition) and `positionIdList` (closePosition).
1118
- *
1119
- * If `storage` is provided, the local cache is updated with the result.
1120
- *
1121
- * @param params - Query parameters
1122
- * @returns Array of currently open position token IDs, or null if no snapshot was found
1123
- */
1124
- async function getOpenPositionIds(params) {
1125
- const { client, chainId, poolAddress, account, storage, lastDispatchTxHash, fromBlock, toBlock, rolesContext } = params;
1126
- let snapshot = null;
1127
- if (lastDispatchTxHash) try {
1128
- const candidate = await recoverSnapshotFromTx({
1129
- client,
1130
- transactionHash: lastDispatchTxHash,
1131
- account,
1132
- pool: poolAddress,
1133
- rolesContext
1134
- });
1135
- if (candidate && await isPositionListLive({
1136
- client,
1137
- poolAddress,
1138
- account,
1139
- positionIds: candidate.positionIds,
1140
- blockNumber: toBlock
1141
- })) snapshot = candidate;
1142
- } catch {
1143
- snapshot = null;
1144
- }
1145
- if (!snapshot) snapshot = await recoverSnapshot({
1146
- client,
1147
- poolAddress,
1148
- account,
1149
- fromBlock,
1150
- toBlock
1151
- });
1152
- if (!snapshot) return null;
1153
- if (storage) {
1154
- const key = getPositionsKey(chainId, poolAddress, account);
1155
- await storage.set(key, jsonSerializer.stringify(snapshot.positionIds));
1156
- }
1157
- return snapshot.positionIds;
1158
- }
1159
-
1160
344
  //#endregion
1161
345
  //#region src/panoptic/v2/writes/position.ts
1162
346
  /**
@@ -1694,231 +878,158 @@ async function liquidateAndWait(params) {
1694
878
  }
1695
879
 
1696
880
  //#endregion
1697
- //#region src/panoptic/v2/bot/index.ts
1698
- /**
1699
- * Assert that data is fresh (not stale).
1700
- *
1701
- * Throws StaleDataError if the data's block timestamp is older than maxAgeSeconds.
1702
- *
1703
- * @param data - Any SDK data with _meta field
1704
- * @param maxAgeSeconds - Maximum allowed age in seconds
1705
- * @param currentTimestamp - Current timestamp (defaults to Date.now() / 1000)
1706
- * @throws StaleDataError if data is stale
1707
- *
1708
- * @example
1709
- * ```typescript
1710
- * const pool = await getPool({ client, poolAddress })
1711
- * assertFresh(pool, 60) // Must be < 60 seconds old
1712
- * ```
1713
- */
1714
- function assertFresh(data, maxAgeSeconds, currentTimestamp) {
1715
- const maxAge = BigInt(maxAgeSeconds);
1716
- const now = currentTimestamp !== void 0 ? BigInt(currentTimestamp) : BigInt(Math.floor(Date.now() / 1e3));
1717
- const blockTimestamp = data._meta.blockTimestamp;
1718
- const age = now - blockTimestamp;
1719
- if (age > maxAge) throw new StaleDataError(blockTimestamp, now, age);
1720
- }
1721
- /**
1722
- * Assert that a pool is healthy.
1723
- *
1724
- * Throws UnhealthyPoolError if the pool health status is not 'active'.
1725
- *
1726
- * @param pool - Pool data from getPool()
1727
- * @throws UnhealthyPoolError if pool is unhealthy (low_liquidity or paused)
1728
- *
1729
- * @example
1730
- * ```typescript
1731
- * const pool = await getPool({ client, poolAddress })
1732
- * assertHealthy(pool) // Throws if pool is paused or has low liquidity
1733
- * ```
1734
- */
1735
- function assertHealthy(pool) {
1736
- if (pool.healthStatus !== "active") throw new UnhealthyPoolError(pool.healthStatus);
1737
- }
1738
- /**
1739
- * Assert that trading is allowed on a pool.
1740
- *
1741
- * Checks both pool health and safe mode status. Throws appropriate errors
1742
- * if trading is restricted.
1743
- *
1744
- * @param pool - Pool data from getPool()
1745
- * @param safeMode - Optional SafeModeState from getSafeMode()
1746
- * @throws UnhealthyPoolError if pool is unhealthy
1747
- * @throws SafeModeError if pool is in restricted or emergency safe mode
1748
- *
1749
- * @example
1750
- * ```typescript
1751
- * const pool = await getPool({ client, poolAddress })
1752
- * const safeMode = await getSafeMode({ client, poolAddress })
1753
- * assertTradeable(pool, safeMode)
1754
- * ```
1755
- */
1756
- function assertTradeable(pool, safeMode) {
1757
- assertHealthy(pool);
1758
- if (safeMode && safeMode.mode !== "normal") throw new SafeModeError(safeMode.mode, safeMode.reason ?? "Trading restricted");
1759
- }
1760
- /**
1761
- * Assert that minting is allowed.
1762
- *
1763
- * @param safeMode - SafeModeState from getSafeMode()
1764
- * @throws SafeModeError if minting is not allowed
1765
- *
1766
- * @example
1767
- * ```typescript
1768
- * const safeMode = await getSafeMode({ client, poolAddress })
1769
- * assertCanMint(safeMode)
1770
- * await openPosition({ ... })
1771
- * ```
1772
- */
1773
- function assertCanMint(safeMode) {
1774
- if (!safeMode.canMint) throw new SafeModeError(safeMode.mode, safeMode.reason ?? "Minting not allowed");
1775
- }
1776
- /**
1777
- * Assert that burning is allowed.
1778
- *
1779
- * @param safeMode - SafeModeState from getSafeMode()
1780
- * @throws SafeModeError if burning is not allowed
1781
- */
1782
- function assertCanBurn(safeMode) {
1783
- if (!safeMode.canBurn) throw new SafeModeError(safeMode.mode, safeMode.reason ?? "Burning not allowed");
1784
- }
1785
- /**
1786
- * Assert that liquidations are allowed.
1787
- *
1788
- * @param safeMode - SafeModeState from getSafeMode()
1789
- * @throws SafeModeError if liquidations are not allowed
1790
- */
1791
- function assertCanLiquidate(safeMode) {
1792
- if (!safeMode.canLiquidate) throw new SafeModeError(safeMode.mode, safeMode.reason ?? "Liquidations not allowed");
1793
- }
1794
- /**
1795
- * Assert that force exercise is allowed.
1796
- *
1797
- * @param safeMode - SafeModeState from getSafeMode()
1798
- * @throws SafeModeError if force exercise is not allowed
1799
- */
1800
- function assertCanForceExercise(safeMode) {
1801
- if (!safeMode.canForceExercise) throw new SafeModeError(safeMode.mode, safeMode.reason ?? "Force exercise not allowed");
1802
- }
1803
- /**
1804
- * Common retryable RPC error codes.
1805
- */
1806
- const RETRYABLE_RPC_CODES = new Set([
1807
- -32e3,
1808
- -32001,
1809
- -32002,
1810
- -32003,
1811
- -32005,
1812
- -32097,
1813
- -32098,
1814
- -32099
1815
- ]);
1816
- /**
1817
- * Common retryable error message patterns.
1818
- */
1819
- const RETRYABLE_PATTERNS = [
1820
- /timeout/i,
1821
- /timed out/i,
1822
- /rate limit/i,
1823
- /too many requests/i,
1824
- /429/i,
1825
- /503/i,
1826
- /504/i,
1827
- /502/i,
1828
- /connection refused/i,
1829
- /connection reset/i,
1830
- /network error/i,
1831
- /econnreset/i,
1832
- /econnrefused/i,
1833
- /etimedout/i,
1834
- /socket hang up/i,
1835
- /temporarily unavailable/i,
1836
- /service unavailable/i,
1837
- /server error/i,
1838
- /internal error/i,
1839
- /nonce too low/i,
1840
- /replacement transaction underpriced/i,
1841
- /already known/i
1842
- ];
1843
- /**
1844
- * Check if an error is a retryable RPC error.
1845
- *
1846
- * Returns true for transient errors that may succeed on retry:
1847
- * - Timeouts
1848
- * - Rate limits
1849
- * - Connection errors
1850
- * - Server errors (5xx)
1851
- * - Nonce errors (recoverable with correct nonce)
1852
- *
1853
- * @param error - The error to check
1854
- * @returns True if the error is likely retryable
1855
- *
1856
- * @example
1857
- * ```typescript
1858
- * try {
1859
- * await openPosition({ ... })
1860
- * } catch (error) {
1861
- * if (isRetryableRpcError(error)) {
1862
- * // Wait and retry
1863
- * await sleep(1000)
1864
- * await openPosition({ ... })
1865
- * } else {
1866
- * throw error // Non-retryable, propagate
1867
- * }
1868
- * }
1869
- * ```
1870
- */
1871
- function isRetryableRpcError(error) {
1872
- if (error === null || error === void 0) return false;
1873
- if (typeof error === "object") {
1874
- const errorObj = error;
1875
- if (typeof errorObj.code === "number" && RETRYABLE_RPC_CODES.has(errorObj.code)) return true;
1876
- if (errorObj.cause && isRetryableRpcError(errorObj.cause)) return true;
1877
- if (errorObj.details && typeof errorObj.details === "object") {
1878
- const details = errorObj.details;
1879
- if (typeof details.code === "number" && RETRYABLE_RPC_CODES.has(details.code)) return true;
881
+ //#region src/panoptic/v2/sync/eventReconstruction.ts
882
+ /**
883
+ * Reconstruct position history from events.
884
+ * This is the fallback method when snapshot recovery fails.
885
+ * It scans all OptionMinted and OptionBurnt events to build the position set.
886
+ *
887
+ * @param params - Reconstruction parameters
888
+ * @returns Reconstruction result with open and closed positions
889
+ */
890
+ async function reconstructFromEvents(params) {
891
+ const { client, poolAddress, account, fromBlock, toBlock, batchSize = 10000n, onProgress } = params;
892
+ const mintEvents = [];
893
+ const burnEvents = [];
894
+ const totalBlocks = toBlock - fromBlock + 1n;
895
+ const safeBatchSize = batchSize > 0n ? batchSize : 10000n;
896
+ const scanRange = async (rangeFromBlock, rangeToBlock) => {
897
+ const [mints, burns] = await Promise.all([client.getLogs({
898
+ address: poolAddress,
899
+ event: OPTION_MINTED_EVENT,
900
+ args: { recipient: account },
901
+ fromBlock: rangeFromBlock,
902
+ toBlock: rangeToBlock
903
+ }), client.getLogs({
904
+ address: poolAddress,
905
+ event: OPTION_BURNT_EVENT,
906
+ args: { recipient: account },
907
+ fromBlock: rangeFromBlock,
908
+ toBlock: rangeToBlock
909
+ })]);
910
+ for (const mint$1 of mints) {
911
+ const balanceData = mint$1.args.balanceData;
912
+ const positionSize = balanceData & (1n << 128n) - 1n;
913
+ mintEvents.push({
914
+ tokenId: mint$1.args.tokenId,
915
+ positionSize,
916
+ blockNumber: mint$1.blockNumber,
917
+ blockHash: mint$1.blockHash,
918
+ transactionHash: mint$1.transactionHash,
919
+ logIndex: mint$1.logIndex
920
+ });
921
+ }
922
+ for (const burn of burns) burnEvents.push({
923
+ tokenId: burn.args.tokenId,
924
+ positionSize: burn.args.positionSize,
925
+ blockNumber: burn.blockNumber,
926
+ blockHash: burn.blockHash,
927
+ transactionHash: burn.transactionHash,
928
+ logIndex: burn.logIndex
929
+ });
930
+ const blocksProcessed = rangeToBlock - fromBlock + 1n;
931
+ const progress = totalBlocks > 0n ? blocksProcessed * 100n / totalBlocks : 100n;
932
+ onProgress?.({
933
+ currentBlock: rangeToBlock,
934
+ targetBlock: toBlock,
935
+ positionsFound: BigInt(mintEvents.length),
936
+ progress: progress > 100n ? 100n : progress
937
+ });
938
+ };
939
+ try {
940
+ await scanRange(fromBlock, toBlock);
941
+ } catch (error) {
942
+ if (!isRangeLimitError(error)) throw error;
943
+ let currentBlock = fromBlock;
944
+ while (currentBlock <= toBlock) {
945
+ const endBlock = currentBlock + safeBatchSize - 1n > toBlock ? toBlock : currentBlock + safeBatchSize - 1n;
946
+ await scanRange(currentBlock, endBlock);
947
+ currentBlock = endBlock + 1n;
1880
948
  }
1881
949
  }
1882
- const message = getErrorMessage(error);
1883
- if (message) {
1884
- for (const pattern of RETRYABLE_PATTERNS) if (pattern.test(message)) return true;
950
+ const positionMap = new Map();
951
+ const allEvents = [...mintEvents.map((e) => ({
952
+ ...e,
953
+ type: "mint"
954
+ })), ...burnEvents.map((e) => ({
955
+ ...e,
956
+ type: "burn"
957
+ }))].sort((a, b) => {
958
+ const blockDiff = Number(a.blockNumber - b.blockNumber);
959
+ if (blockDiff !== 0) return blockDiff;
960
+ return a.logIndex - b.logIndex;
961
+ });
962
+ for (const event of allEvents) {
963
+ const current = positionMap.get(event.tokenId) ?? 0n;
964
+ if (event.type === "mint") positionMap.set(event.tokenId, current + event.positionSize);
965
+ else positionMap.set(event.tokenId, current - event.positionSize);
1885
966
  }
1886
- return false;
967
+ const openPositions = [];
968
+ const closedPositions = [];
969
+ for (const [tokenId, size] of positionMap) if (size > 0n) openPositions.push(tokenId);
970
+ else closedPositions.push(tokenId);
971
+ const lastBlock = await client.getBlock({ blockNumber: toBlock });
972
+ return {
973
+ openPositions,
974
+ closedPositions,
975
+ blocksScanned: toBlock - fromBlock + 1n,
976
+ lastBlock: toBlock,
977
+ lastBlockHash: lastBlock.hash
978
+ };
1887
979
  }
1888
- /**
1889
- * Extract error message from various error types.
1890
- */
1891
- function getErrorMessage(error) {
1892
- if (typeof error === "string") return error;
1893
- if (error instanceof Error) return error.message;
1894
- if (typeof error === "object" && error !== null) {
1895
- const errorObj = error;
1896
- if (typeof errorObj.message === "string") return errorObj.message;
1897
- if (typeof errorObj.shortMessage === "string") return errorObj.shortMessage;
980
+ const OPTION_MINTED_EVENT = getAbiItem({
981
+ abi: panopticPoolV2Abi,
982
+ name: "OptionMinted"
983
+ });
984
+ const OPTION_BURNT_EVENT = getAbiItem({
985
+ abi: panopticPoolV2Abi,
986
+ name: "OptionBurnt"
987
+ });
988
+ function isRangeLimitError(error) {
989
+ const messages = [];
990
+ let current = error;
991
+ for (let depth = 0; current && typeof current === "object" && depth < 5; depth += 1) {
992
+ if ("message" in current && typeof current.message === "string") messages.push(current.message);
993
+ if ("details" in current && typeof current.details === "string") messages.push(current.details);
994
+ current = "cause" in current ? current.cause : void 0;
1898
995
  }
1899
- return void 0;
1900
- }
1901
- /**
1902
- * Check if an error is a nonce-related error that can be recovered.
1903
- *
1904
- * @param error - The error to check
1905
- * @returns True if this is a nonce error
1906
- */
1907
- function isNonceError(error) {
1908
- const message = getErrorMessage(error);
1909
- if (!message) return false;
1910
- return /nonce too low/i.test(message) || /nonce too high/i.test(message) || /already known/i.test(message);
996
+ if (typeof error === "string") messages.push(error);
997
+ const message = messages.join(" ").toLowerCase();
998
+ return message.includes("block range") || message.includes("range is too large") || message.includes("range too large") || message.includes("query returned more than") || message.includes("too many results") || message.includes("log response size exceeded") || message.includes("exceeds the limit") || message.includes("range") && message.includes("limit");
1911
999
  }
1912
1000
  /**
1913
- * Check if an error is a gas-related error that can be recovered.
1001
+ * Get the deployment block for a pool.
1002
+ * This searches for the first PoolInitialized event.
1914
1003
  *
1915
- * @param error - The error to check
1916
- * @returns True if this is a gas error
1917
- */
1918
- function isGasError(error) {
1919
- const message = getErrorMessage(error);
1920
- if (!message) return false;
1921
- return /replacement transaction underpriced/i.test(message) || /gas too low/i.test(message) || /intrinsic gas too low/i.test(message) || /max fee per gas less than block base fee/i.test(message);
1004
+ * @param client - viem public client
1005
+ * @param poolAddress - Pool address
1006
+ * @returns Deployment block number or null if not found
1007
+ */
1008
+ async function getPoolDeploymentBlock(client, poolAddress) {
1009
+ const currentBlock = await client.getBlockNumber();
1010
+ let low = 0n;
1011
+ let high = currentBlock;
1012
+ let foundBlock = null;
1013
+ const scanRange = 10000n;
1014
+ while (low <= high) {
1015
+ const mid = (low + high) / 2n;
1016
+ const rangeEnd = mid + scanRange > high ? high : mid + scanRange;
1017
+ try {
1018
+ const logs = await client.getLogs({
1019
+ address: poolAddress,
1020
+ fromBlock: mid,
1021
+ toBlock: rangeEnd
1022
+ });
1023
+ if (logs.length > 0) {
1024
+ const earliest = logs[0].blockNumber;
1025
+ if (foundBlock === null || earliest < foundBlock) foundBlock = earliest;
1026
+ high = earliest - 1n;
1027
+ } else low = rangeEnd + 1n;
1028
+ } catch {
1029
+ high = mid + (rangeEnd - mid) / 2n;
1030
+ }
1031
+ }
1032
+ return foundBlock;
1922
1033
  }
1923
1034
 
1924
1035
  //#endregion
@@ -2087,47 +1198,45 @@ async function syncPositions(params) {
2087
1198
  fromBlock: checkpoint?.lastBlock ?? params.fromBlock
2088
1199
  });
2089
1200
  let positionIds;
2090
- if (snapshotResult === null && checkpoint && checkpoint.positionIds.length > 0) positionIds = checkpoint.positionIds;
2091
- else positionIds = snapshotResult ?? [];
2092
- if (positionIds.length === 0) {
2093
- const hasAnyEvents = await accountHasPositionEvents({
1201
+ if (snapshotResult === null) {
1202
+ const fromBlock = params.fromBlock ?? await getPoolDeploymentBlock(client, poolAddress);
1203
+ if (fromBlock === null) throw new PositionSnapshotNotFoundError(new Error("Pool deployment block not found"));
1204
+ const reconstruction = await reconstructFromEvents({
2094
1205
  client,
2095
1206
  poolAddress,
2096
1207
  account,
2097
- fromBlock: params.fromBlock,
2098
- toBlock
1208
+ fromBlock,
1209
+ toBlock,
1210
+ batchSize: params.maxLogsPerQuery,
1211
+ onProgress: (progress) => {
1212
+ checkTimeout();
1213
+ onUpdate?.({
1214
+ type: "progress",
1215
+ blockNumber: progress.currentBlock,
1216
+ progress: {
1217
+ current: progress.currentBlock,
1218
+ total: progress.targetBlock
1219
+ }
1220
+ });
1221
+ }
2099
1222
  });
2100
- if (!hasAnyEvents) {
2101
- const finalBlock$1 = await client.getBlock({ blockNumber: toBlock });
2102
- const poolMetaKey$1 = getPoolMetaKey(chainId, poolAddress);
2103
- const existingPoolMeta$1 = await storage.get(poolMetaKey$1);
2104
- if (!existingPoolMeta$1) await fetchAndStorePoolMeta(client, poolAddress, poolMetaKey$1, storage);
2105
- await saveCheckpoint({
2106
- storage,
2107
- chainId,
2108
- poolAddress,
2109
- account,
2110
- lastBlock: toBlock,
2111
- lastBlockHash: finalBlock$1.hash,
2112
- positionIds: []
2113
- });
2114
- const positionsKey$1 = getPositionsKey(chainId, poolAddress, account);
2115
- await storage.set(positionsKey$1, jsonSerializer.stringify([]));
2116
- return {
2117
- lastSyncedBlock: toBlock,
2118
- lastSyncedBlockHash: finalBlock$1.hash,
2119
- positionCount: 0n,
2120
- positionIds: [],
2121
- incremental: false,
2122
- durationMs: BigInt(Date.now() - startTime)
2123
- };
2124
- }
1223
+ positionIds = reconstruction.openPositions;
1224
+ } else positionIds = snapshotResult;
1225
+ const finalBlock = await client.getBlock({ blockNumber: toBlock });
1226
+ if (positionIds.length === 0) {
1227
+ await persistPoolAndCheckpoint(params, toBlock, finalBlock.hash, []);
1228
+ const positionsKey$1 = getPositionsKey(chainId, poolAddress, account);
1229
+ await storage.set(positionsKey$1, jsonSerializer.stringify([]));
1230
+ return {
1231
+ lastSyncedBlock: toBlock,
1232
+ lastSyncedBlockHash: finalBlock.hash,
1233
+ positionCount: 0n,
1234
+ positionIds: [],
1235
+ incremental: !!checkpoint,
1236
+ durationMs: BigInt(Date.now() - startTime)
1237
+ };
2125
1238
  }
2126
1239
  checkTimeout();
2127
- const finalBlock = await client.getBlock({ blockNumber: toBlock });
2128
- const poolMetaKey = getPoolMetaKey(chainId, poolAddress);
2129
- const existingPoolMeta = await storage.get(poolMetaKey);
2130
- if (!existingPoolMeta) await fetchAndStorePoolMeta(client, poolAddress, poolMetaKey, storage);
2131
1240
  if (positionIds.length > 0) {
2132
1241
  const { positions } = await getPositions({
2133
1242
  client,
@@ -2152,15 +1261,7 @@ async function syncPositions(params) {
2152
1261
  await storage.set(positionMetaKey, jsonSerializer.stringify(storedData));
2153
1262
  }
2154
1263
  }
2155
- await saveCheckpoint({
2156
- storage,
2157
- chainId,
2158
- poolAddress,
2159
- account,
2160
- lastBlock: toBlock,
2161
- lastBlockHash: finalBlock.hash,
2162
- positionIds
2163
- });
1264
+ await persistPoolAndCheckpoint(params, toBlock, finalBlock.hash, positionIds);
2164
1265
  const positionsKey = getPositionsKey(chainId, poolAddress, account);
2165
1266
  await storage.set(positionsKey, jsonSerializer.stringify(positionIds));
2166
1267
  return {
@@ -2172,143 +1273,20 @@ async function syncPositions(params) {
2172
1273
  durationMs: BigInt(Date.now() - startTime)
2173
1274
  };
2174
1275
  }
2175
- /**
2176
- * Quick check if an account has ANY position events.
2177
- * Checks OptionMinted, OptionBurnt, ForcedExercised (user), and AccountLiquidated (liquidatee).
2178
- * All checked fields are indexed so this is fast.
2179
- *
2180
- * **Warning:** RPC index lag can cause false negatives for recently minted positions.
2181
- * If the RPC node's event index is behind the chain tip, this function may return
2182
- * `false` even though the account has just minted a position. Callers should either
2183
- * wait a few blocks after minting before calling syncPositions, or skip this
2184
- * optimization (by providing a `snapshotTxHash`) when freshness is critical.
2185
- *
2186
- * @param params - Check parameters
2187
- * @returns true if account has any position events, false otherwise
2188
- */
2189
- async function accountHasPositionEvents(params) {
2190
- const { client, poolAddress, account, fromBlock = 0n, toBlock } = params;
2191
- const [mintEvents, burnEvents, forceExerciseEvents, liquidationEvents] = await Promise.all([
2192
- withRetry(() => client.getLogs({
2193
- address: poolAddress,
2194
- event: {
2195
- type: "event",
2196
- name: "OptionMinted",
2197
- inputs: [
2198
- {
2199
- type: "address",
2200
- name: "recipient",
2201
- indexed: true
2202
- },
2203
- {
2204
- type: "uint256",
2205
- name: "tokenId",
2206
- indexed: true
2207
- },
2208
- {
2209
- type: "uint256",
2210
- name: "balanceData",
2211
- indexed: false
2212
- }
2213
- ]
2214
- },
2215
- args: { recipient: account },
2216
- fromBlock,
2217
- toBlock
2218
- })),
2219
- withRetry(() => client.getLogs({
2220
- address: poolAddress,
2221
- event: {
2222
- type: "event",
2223
- name: "OptionBurnt",
2224
- inputs: [
2225
- {
2226
- type: "address",
2227
- name: "recipient",
2228
- indexed: true
2229
- },
2230
- {
2231
- type: "uint256",
2232
- name: "tokenId",
2233
- indexed: true
2234
- },
2235
- {
2236
- type: "uint256",
2237
- name: "positionSize",
2238
- indexed: false
2239
- },
2240
- {
2241
- type: "int256[4]",
2242
- name: "premiaByLeg",
2243
- indexed: false
2244
- }
2245
- ]
2246
- },
2247
- args: { recipient: account },
2248
- fromBlock,
2249
- toBlock
2250
- })),
2251
- withRetry(() => client.getLogs({
2252
- address: poolAddress,
2253
- event: {
2254
- type: "event",
2255
- name: "ForcedExercised",
2256
- inputs: [
2257
- {
2258
- type: "address",
2259
- name: "exercisor",
2260
- indexed: true
2261
- },
2262
- {
2263
- type: "address",
2264
- name: "user",
2265
- indexed: true
2266
- },
2267
- {
2268
- type: "uint256",
2269
- name: "tokenId",
2270
- indexed: true
2271
- },
2272
- {
2273
- type: "int256",
2274
- name: "exerciseFee",
2275
- indexed: false
2276
- }
2277
- ]
2278
- },
2279
- args: { user: account },
2280
- fromBlock,
2281
- toBlock
2282
- })),
2283
- withRetry(() => client.getLogs({
2284
- address: poolAddress,
2285
- event: {
2286
- type: "event",
2287
- name: "AccountLiquidated",
2288
- inputs: [
2289
- {
2290
- type: "address",
2291
- name: "liquidator",
2292
- indexed: true
2293
- },
2294
- {
2295
- type: "address",
2296
- name: "liquidatee",
2297
- indexed: true
2298
- },
2299
- {
2300
- type: "int256",
2301
- name: "bonusAmounts",
2302
- indexed: false
2303
- }
2304
- ]
2305
- },
2306
- args: { liquidatee: account },
2307
- fromBlock,
2308
- toBlock
2309
- }))
2310
- ]);
2311
- return mintEvents.length > 0 || burnEvents.length > 0 || forceExerciseEvents.length > 0 || liquidationEvents.length > 0;
1276
+ async function persistPoolAndCheckpoint(params, lastBlock, lastBlockHash, positionIds) {
1277
+ const { account, chainId, client, poolAddress, storage } = params;
1278
+ const poolMetaKey = getPoolMetaKey(chainId, poolAddress);
1279
+ const existingPoolMeta = await storage.get(poolMetaKey);
1280
+ if (!existingPoolMeta) await fetchAndStorePoolMeta(client, poolAddress, poolMetaKey, storage);
1281
+ await saveCheckpoint({
1282
+ storage,
1283
+ chainId,
1284
+ poolAddress,
1285
+ account,
1286
+ lastBlock,
1287
+ lastBlockHash,
1288
+ positionIds
1289
+ });
2312
1290
  }
2313
1291
  /**
2314
1292
  * Fetch full pool metadata and store it.
@@ -2335,27 +1313,6 @@ async function fetchAndStorePoolMeta(client, poolAddress, poolMetaKey, storage)
2335
1313
  };
2336
1314
  await storage.set(poolMetaKey, jsonSerializer.stringify(poolMeta));
2337
1315
  }
2338
- /**
2339
- * Retry helper with exponential backoff for transient RPC errors.
2340
- *
2341
- * @param fn - Async function to retry
2342
- * @param maxRetries - Maximum number of retries (default: 3)
2343
- * @returns The result of the function
2344
- */
2345
- async function withRetry(fn, maxRetries = 3) {
2346
- let lastError;
2347
- for (let attempt = 0; attempt <= maxRetries; attempt++) try {
2348
- return await fn();
2349
- } catch (error) {
2350
- lastError = error;
2351
- if (attempt < maxRetries && isRetryableRpcError(error)) {
2352
- await new Promise((resolve) => setTimeout(resolve, 1e3 * 2 ** attempt));
2353
- continue;
2354
- }
2355
- throw error;
2356
- }
2357
- throw lastError;
2358
- }
2359
1316
 
2360
1317
  //#endregion
2361
1318
  //#region src/panoptic/v2/writes/forceExercise.ts
@@ -2515,10 +1472,10 @@ async function pokeOracle(params) {
2515
1472
  functionName: "getOracleTicks"
2516
1473
  }), client.getBlock()]);
2517
1474
  const oraclePack = oracleData[4];
2518
- const epoch = oraclePack >> 232n;
2519
- const currentEpoch = block.timestamp / 64n;
2520
- if (currentEpoch <= epoch) {
2521
- const lastUpdate = epoch * 64n;
1475
+ const { epoch } = decodeOracleTiming(oraclePack, block.timestamp);
1476
+ const currentEpoch = oracleEpochAt(block.timestamp);
1477
+ if (currentEpoch === epoch) {
1478
+ const lastUpdate = epoch << 6n;
2522
1479
  throw new OracleRateLimitedError(lastUpdate, block.timestamp);
2523
1480
  }
2524
1481
  }
@@ -3139,6 +2096,44 @@ function buildUniqueCredit(poolId, asset, tokenType, currentTick, tickSpacing, e
3139
2096
 
3140
2097
  //#endregion
3141
2098
  //#region src/panoptic/v2/writes/swap.ts
2099
+ /** Build an atomic credit mint/burn swap without fetching or sending anything. */
2100
+ function buildCreditSwapCall(params) {
2101
+ const amount = params.kind === "exactIn" ? params.amountIn : params.amountOut;
2102
+ if (amount <= 0n) throw new Error("credit swap amount must be positive");
2103
+ if (params.tokenIndex !== 0n && params.tokenIndex !== 1n) throw new Error("credit swap tokenIndex must be 0 or 1");
2104
+ const { low, high } = tickLimits(params.currentTick, params.slippageBps);
2105
+ const { tokenId: creditTokenId, adjustedSize } = buildUniqueCredit(params.poolId, params.tokenIndex, params.tokenIndex, params.currentTick, params.tickSpacing, params.existingPositionIds, amount);
2106
+ const ascending = [
2107
+ Number(low),
2108
+ Number(high),
2109
+ 0
2110
+ ];
2111
+ const descending = [
2112
+ Number(high),
2113
+ Number(low),
2114
+ 0
2115
+ ];
2116
+ const limits = params.kind === "exactIn" ? [ascending, descending] : [descending, ascending];
2117
+ const args = [
2118
+ [creditTokenId, creditTokenId],
2119
+ [...params.existingPositionIds],
2120
+ [adjustedSize, 0n],
2121
+ limits,
2122
+ false,
2123
+ params.builderCode ?? 0n
2124
+ ];
2125
+ return {
2126
+ to: params.poolAddress,
2127
+ data: encodeFunctionData({
2128
+ abi: panopticPoolV2Abi,
2129
+ functionName: "dispatch",
2130
+ args
2131
+ }),
2132
+ creditTokenId,
2133
+ adjustedSize,
2134
+ args
2135
+ };
2136
+ }
3142
2137
  /**
3143
2138
  * Swap tokens using Panoptic's exact-output mechanism.
3144
2139
  *
@@ -3172,22 +2167,18 @@ async function swapExactOut(params) {
3172
2167
  const token0 = pool.collateralTracker0.token;
3173
2168
  const token1 = pool.collateralTracker1.token;
3174
2169
  const tokenOutIndex = resolveTokenIndex(tokenOut, token0, token1);
3175
- const tokenType = tokenOutIndex;
3176
- const { low: tickLimitLow, high: tickLimitHigh } = tickLimits(pool.currentTick, slippageBps);
3177
- const { tokenId: creditTokenId, adjustedSize } = buildUniqueCredit(pool.poolId, tokenOutIndex, tokenType, pool.currentTick, pool.tickSpacing, positionIds, amountOut);
3178
- const mintTickLimits = [
3179
- Number(tickLimitHigh),
3180
- Number(tickLimitLow),
3181
- 0
3182
- ];
3183
- const burnTickLimits = [
3184
- Number(tickLimitLow),
3185
- Number(tickLimitHigh),
3186
- 0
3187
- ];
3188
- const positionIdList = [creditTokenId, creditTokenId];
3189
- const finalPositionIdList = [...positionIds];
3190
- const positionSizes = [adjustedSize, 0n];
2170
+ const call = buildCreditSwapCall({
2171
+ kind: "exactOut",
2172
+ poolAddress,
2173
+ poolId: pool.poolId,
2174
+ currentTick: pool.currentTick,
2175
+ tickSpacing: pool.tickSpacing,
2176
+ existingPositionIds: positionIds,
2177
+ tokenIndex: tokenOutIndex,
2178
+ amountOut,
2179
+ slippageBps,
2180
+ builderCode
2181
+ });
3191
2182
  try {
3192
2183
  return await submitWrite({
3193
2184
  client,
@@ -3196,14 +2187,7 @@ async function swapExactOut(params) {
3196
2187
  address: poolAddress,
3197
2188
  abi: panopticPoolV2Abi,
3198
2189
  functionName: "dispatch",
3199
- args: [
3200
- positionIdList,
3201
- finalPositionIdList,
3202
- positionSizes,
3203
- [mintTickLimits, burnTickLimits],
3204
- false,
3205
- builderCode
3206
- ],
2190
+ args: call.args,
3207
2191
  txOverrides
3208
2192
  });
3209
2193
  } catch (error) {
@@ -3253,22 +2237,18 @@ async function swapExactIn(params) {
3253
2237
  const token0 = pool.collateralTracker0.token;
3254
2238
  const token1 = pool.collateralTracker1.token;
3255
2239
  const tokenInIndex = resolveTokenIndex(tokenIn, token0, token1);
3256
- const tokenType = tokenInIndex;
3257
- const { low: tickLimitLow, high: tickLimitHigh } = tickLimits(pool.currentTick, slippageBps);
3258
- const { tokenId: creditTokenId, adjustedSize } = buildUniqueCredit(pool.poolId, tokenInIndex, tokenType, pool.currentTick, pool.tickSpacing, positionIds, amountIn);
3259
- const mintTickLimits = [
3260
- Number(tickLimitLow),
3261
- Number(tickLimitHigh),
3262
- 0
3263
- ];
3264
- const burnTickLimits = [
3265
- Number(tickLimitHigh),
3266
- Number(tickLimitLow),
3267
- 0
3268
- ];
3269
- const positionIdList = [creditTokenId, creditTokenId];
3270
- const finalPositionIdList = [...positionIds];
3271
- const positionSizes = [adjustedSize, 0n];
2240
+ const call = buildCreditSwapCall({
2241
+ kind: "exactIn",
2242
+ poolAddress,
2243
+ poolId: pool.poolId,
2244
+ currentTick: pool.currentTick,
2245
+ tickSpacing: pool.tickSpacing,
2246
+ existingPositionIds: positionIds,
2247
+ tokenIndex: tokenInIndex,
2248
+ amountIn,
2249
+ slippageBps,
2250
+ builderCode
2251
+ });
3272
2252
  try {
3273
2253
  return await submitWrite({
3274
2254
  client,
@@ -3277,14 +2257,7 @@ async function swapExactIn(params) {
3277
2257
  address: poolAddress,
3278
2258
  abi: panopticPoolV2Abi,
3279
2259
  functionName: "dispatch",
3280
- args: [
3281
- positionIdList,
3282
- finalPositionIdList,
3283
- positionSizes,
3284
- [mintTickLimits, burnTickLimits],
3285
- false,
3286
- builderCode
3287
- ],
2260
+ args: call.args,
3288
2261
  txOverrides
3289
2262
  });
3290
2263
  } catch (error) {
@@ -3873,6 +2846,67 @@ async function simulateWithTokenFlow(params) {
3873
2846
  };
3874
2847
  }
3875
2848
  }
2849
+ /**
2850
+ * Get pool token addresses for reference.
2851
+ *
2852
+ * @param params - Parameters
2853
+ * @returns Pool token addresses
2854
+ */
2855
+ async function getPoolTokensForSimulation(params) {
2856
+ const { client, poolAddress, blockNumber } = params;
2857
+ const poolAbi = [{
2858
+ type: "function",
2859
+ name: "collateralToken0",
2860
+ inputs: [],
2861
+ outputs: [{ type: "address" }],
2862
+ stateMutability: "view"
2863
+ }, {
2864
+ type: "function",
2865
+ name: "collateralToken1",
2866
+ inputs: [],
2867
+ outputs: [{ type: "address" }],
2868
+ stateMutability: "view"
2869
+ }];
2870
+ const collateralAbi = [{
2871
+ type: "function",
2872
+ name: "asset",
2873
+ inputs: [],
2874
+ outputs: [{ type: "address" }],
2875
+ stateMutability: "view"
2876
+ }];
2877
+ const [ct0, ct1] = await client.multicall({
2878
+ contracts: [{
2879
+ address: poolAddress,
2880
+ abi: poolAbi,
2881
+ functionName: "collateralToken0"
2882
+ }, {
2883
+ address: poolAddress,
2884
+ abi: poolAbi,
2885
+ functionName: "collateralToken1"
2886
+ }],
2887
+ blockNumber,
2888
+ allowFailure: false
2889
+ });
2890
+ const [token0, token1] = await client.multicall({
2891
+ contracts: [{
2892
+ address: ct0,
2893
+ abi: collateralAbi,
2894
+ functionName: "asset"
2895
+ }, {
2896
+ address: ct1,
2897
+ abi: collateralAbi,
2898
+ functionName: "asset"
2899
+ }],
2900
+ blockNumber,
2901
+ allowFailure: false
2902
+ });
2903
+ return {
2904
+ token0,
2905
+ token1,
2906
+ collateralTracker0: ct0,
2907
+ collateralTracker1: ct1
2908
+ };
2909
+ }
3876
2910
 
3877
2911
  //#endregion
3878
2912
  //#region src/panoptic/v2/simulations/simulateOpenPosition.ts
@@ -4572,5 +3606,5 @@ async function smartRepayAndWait(params) {
4572
3606
  }
4573
3607
 
4574
3608
  //#endregion
4575
- export { addLegToTokenId as addLegToTokenId$1, approve as approve$1, approveAndWait as approveAndWait$1, approvePool as approvePool$1, assertCanBurn as assertCanBurn$1, assertCanForceExercise as assertCanForceExercise$1, assertCanLiquidate as assertCanLiquidate$1, assertCanMint as assertCanMint$1, assertFresh as assertFresh$1, assertHealthy as assertHealthy$1, assertTradeable as assertTradeable$1, borrow as borrow$1, borrowAndWait as borrowAndWait$1, buildBatchDispatchArgs as buildBatchDispatchArgs$1, buildOpenPositionCalldata as buildOpenPositionCalldata$1, buildUniqueCredit as buildUniqueCredit$1, buildUniqueLoan as buildUniqueLoan$1, buildUniqueWidthZeroLeg as buildUniqueWidthZeroLeg$1, calculateResyncBlock as calculateResyncBlock$1, cancelTransaction as cancelTransaction$1, checkApproval as checkApproval$1, clearCheckpoint as clearCheckpoint$1, clearTrackedPositions as clearTrackedPositions$1, closePosition as closePosition$1, closePositionAndWait as closePositionAndWait$1, countLegs as countLegs$1, createFileStorage as createFileStorage$1, createMemoryStorage as createMemoryStorage$1, createNonceManager as createNonceManager$1, createTokenIdBuilder as createTokenIdBuilder$1, decodeAllDispatchCalldata as decodeAllDispatchCalldata$1, decodeAllLegs as decodeAllLegs$1, decodeDispatchCalldata as decodeDispatchCalldata$1, decodeLeg as decodeLeg$1, decodePoolId as decodePoolId$1, decodeTickSpacing as decodeTickSpacing$2, decodeTokenId as decodeTokenId$1, decodeVegoid as decodeVegoid$1, deployNewPool as deployNewPool$1, deployNewPoolAndWait as deployNewPoolAndWait$1, deposit as deposit$1, depositAndWait as depositAndWait$1, detectReorg as detectReorg$1, dispatch as dispatch$1, dispatchAndWait as dispatchAndWait$1, encodeLeg as encodeLeg$1, encodePoolId as encodePoolId$1, encodeV4PoolId as encodeV4PoolId$1, executeBatchDispatch as executeBatchDispatch$1, executeBatchDispatchAndWait as executeBatchDispatchAndWait$1, forceExercise as forceExercise$1, forceExerciseAndWait as forceExerciseAndWait$1, getAssetIndex as getAssetIndex$1, getClosedPositionsKey as getClosedPositionsKey$1, getOpenPositionIds as getOpenPositionIds$1, getPendingPositionsKey as getPendingPositionsKey$1, getPoolMetaKey as getPoolMetaKey$1, getPoolPrefix as getPoolPrefix$1, getPositionMetaKey as getPositionMetaKey$1, getPositionsKey as getPositionsKey$1, getSchemaVersionKey as getSchemaVersionKey$1, getSyncCheckpointKey as getSyncCheckpointKey$1, getTrackedChunksKey as getTrackedChunksKey$1, getTrackedPositionIds as getTrackedPositionIds$1, hasLoanOrCredit as hasLoanOrCredit$1, hasLongLeg as hasLongLeg$1, isCredit as isCredit$1, isCreditLeg as isCreditLeg$1, isGasError as isGasError$1, isInputListFailError as isInputListFailError$1, isLoan as isLoan$1, isLoanLeg as isLoanLeg$1, isNonceError as isNonceError$1, isPositionTracked as isPositionTracked$1, isRetryableRpcError as isRetryableRpcError$1, isShortOnly as isShortOnly$1, isSpread as isSpread$1, jsonSerializer as jsonSerializer$1, liquidate as liquidate$1, liquidateAndWait as liquidateAndWait$1, loadCheckpoint as loadCheckpoint$1, mint as mint$1, mintAndWait as mintAndWait$1, openPosition as openPosition$1, openPositionAndWait as openPositionAndWait$1, pokeOracle as pokeOracle$1, pokeOracleAndWait as pokeOracleAndWait$1, previewBorrow as previewBorrow$1, previewUnwrap as previewUnwrap$1, previewWrap as previewWrap$1, publicBroadcaster as publicBroadcaster$1, recoverSnapshot as recoverSnapshot$1, recoverSnapshotFromTx as recoverSnapshotFromTx$1, redeem as redeem$1, redeemAndWait as redeemAndWait$1, repay as repay$1, repayAndWait as repayAndWait$1, resolveTokenIndex as resolveTokenIndex$1, rollPosition as rollPosition$1, rollPositionAndWait as rollPositionAndWait$1, saveCheckpoint as saveCheckpoint$1, selectDispatchForAccount as selectDispatchForAccount$1, settleAccumulatedPremia as settleAccumulatedPremia$1, settleAccumulatedPremiaAndWait as settleAccumulatedPremiaAndWait$1, simulateOpenPosition as simulateOpenPosition$1, simulateWithTokenFlow, smartRepay as smartRepay$1, smartRepayAndWait as smartRepayAndWait$1, speedUpTransaction as speedUpTransaction$1, supply as supply$1, supplyAndWait as supplyAndWait$1, swapExactIn as swapExactIn$1, swapExactInAndWait as swapExactInAndWait$1, swapExactOut as swapExactOut$1, swapExactOutAndWait as swapExactOutAndWait$1, syncPositions as syncPositions$1, unsupply as unsupply$1, unsupplyAndWait as unsupplyAndWait$1, unwrapWeth as unwrapWeth$1, unwrapWethAndWait as unwrapWethAndWait$1, unwrapXstock as unwrapXstock$1, unwrapXstockAndWait as unwrapXstockAndWait$1, validateBatch as validateBatch$1, validatePoolId as validatePoolId$1, verifyBlockContinuity as verifyBlockContinuity$1, wethWrapAbi as wethWrapAbi$1, withdraw as withdraw$1, withdrawAndWait as withdrawAndWait$1, withdrawWithPositions as withdrawWithPositions$1, withdrawWithPositionsAndWait as withdrawWithPositionsAndWait$1, wrapEth as wrapEth$1, wrapEthAndWait as wrapEthAndWait$1, wrapXstock as wrapXstock$1, wrapXstockAndWait as wrapXstockAndWait$1, xstockWrapperAbi as xstockWrapperAbi$1 };
4576
- //# sourceMappingURL=writes-AupZCK4M.js.map
3609
+ export { addLegToTokenId as addLegToTokenId$1, approve as approve$1, approveAndWait as approveAndWait$1, approvePool as approvePool$1, borrow as borrow$1, borrowAndWait as borrowAndWait$1, buildBatchDispatchArgs as buildBatchDispatchArgs$1, buildCreditSwapCall as buildCreditSwapCall$1, buildOpenPositionCalldata as buildOpenPositionCalldata$1, buildUniqueCredit as buildUniqueCredit$1, buildUniqueLoan as buildUniqueLoan$1, buildUniqueWidthZeroLeg as buildUniqueWidthZeroLeg$1, calculateResyncBlock as calculateResyncBlock$1, cancelTransaction as cancelTransaction$1, checkApproval as checkApproval$1, clearCheckpoint as clearCheckpoint$1, closePosition as closePosition$1, closePositionAndWait as closePositionAndWait$1, countLegs as countLegs$1, createNonceManager as createNonceManager$1, createTokenIdBuilder as createTokenIdBuilder$1, decodeAllLegs as decodeAllLegs$1, decodeLeg as decodeLeg$1, decodePoolId as decodePoolId$1, decodeTickSpacing as decodeTickSpacing$2, decodeTokenId as decodeTokenId$1, decodeVegoid as decodeVegoid$1, deployNewPool as deployNewPool$1, deployNewPoolAndWait as deployNewPoolAndWait$1, deposit as deposit$1, depositAndWait as depositAndWait$1, detectReorg as detectReorg$1, dispatch as dispatch$1, dispatchAndWait as dispatchAndWait$1, encodeLeg as encodeLeg$1, encodePoolId as encodePoolId$1, encodeV4PoolId as encodeV4PoolId$1, executeBatchDispatch as executeBatchDispatch$1, executeBatchDispatchAndWait as executeBatchDispatchAndWait$1, forceExercise as forceExercise$1, forceExerciseAndWait as forceExerciseAndWait$1, getAssetIndex as getAssetIndex$1, getPoolDeploymentBlock as getPoolDeploymentBlock$1, getPoolTokensForSimulation, hasLoanOrCredit as hasLoanOrCredit$1, hasLongLeg as hasLongLeg$1, isCredit as isCredit$1, isCreditLeg as isCreditLeg$1, isInputListFailError as isInputListFailError$1, isLoan as isLoan$1, isLoanLeg as isLoanLeg$1, isShortOnly as isShortOnly$1, isSpread as isSpread$1, liquidate as liquidate$1, liquidateAndWait as liquidateAndWait$1, loadCheckpoint as loadCheckpoint$1, mint as mint$1, mintAndWait as mintAndWait$1, openPosition as openPosition$1, openPositionAndWait as openPositionAndWait$1, pokeOracle as pokeOracle$1, pokeOracleAndWait as pokeOracleAndWait$1, previewBorrow as previewBorrow$1, previewUnwrap as previewUnwrap$1, previewWrap as previewWrap$1, publicBroadcaster as publicBroadcaster$1, reconstructFromEvents as reconstructFromEvents$1, redeem as redeem$1, redeemAndWait as redeemAndWait$1, repay as repay$1, repayAndWait as repayAndWait$1, resolveTokenIndex as resolveTokenIndex$1, rollPosition as rollPosition$1, rollPositionAndWait as rollPositionAndWait$1, saveCheckpoint as saveCheckpoint$1, settleAccumulatedPremia as settleAccumulatedPremia$1, settleAccumulatedPremiaAndWait as settleAccumulatedPremiaAndWait$1, simulateOpenPosition as simulateOpenPosition$1, simulateWithTokenFlow as simulateWithTokenFlow$1, smartRepay as smartRepay$1, smartRepayAndWait as smartRepayAndWait$1, speedUpTransaction as speedUpTransaction$1, supply as supply$1, supplyAndWait as supplyAndWait$1, swapExactIn as swapExactIn$1, swapExactInAndWait as swapExactInAndWait$1, swapExactOut as swapExactOut$1, swapExactOutAndWait as swapExactOutAndWait$1, syncPositions as syncPositions$1, unsupply as unsupply$1, unsupplyAndWait as unsupplyAndWait$1, unwrapWeth as unwrapWeth$1, unwrapWethAndWait as unwrapWethAndWait$1, unwrapXstock as unwrapXstock$1, unwrapXstockAndWait as unwrapXstockAndWait$1, validateBatch as validateBatch$1, validatePoolId as validatePoolId$1, verifyBlockContinuity as verifyBlockContinuity$1, wethWrapAbi as wethWrapAbi$1, withdraw as withdraw$1, withdrawAndWait as withdrawAndWait$1, withdrawWithPositions as withdrawWithPositions$1, withdrawWithPositionsAndWait as withdrawWithPositionsAndWait$1, wrapEth as wrapEth$1, wrapEthAndWait as wrapEthAndWait$1, wrapXstock as wrapXstock$1, wrapXstockAndWait as wrapXstockAndWait$1, xstockWrapperAbi as xstockWrapperAbi$1 };
3610
+ //# sourceMappingURL=writes-D3bj1wth.js.map