@panoptic-eng/sdk 1.0.60 → 1.0.62
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.
- package/dist/{chainDeployments-CmDzAmZ3.js → chainDeployments-BhXMBZ4B.js} +50 -12
- package/dist/cow/index.js +1207 -484
- package/dist/cow/index.js.map +1 -1
- package/dist/{cow-CHb343la.js → cow-DTi3GjQz.js} +1 -1
- package/dist/deployments.d.ts +9 -1
- package/dist/deployments.d.ts.map +1 -1
- package/dist/deployments.js +50 -12
- package/dist/deployments.js.map +1 -1
- package/dist/index.d.ts +587 -75
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +577 -357
- package/dist/index.js.map +1 -1
- package/dist/{irm-BIaLQ-0n.js → irm-BDlcUNgJ.js} +2 -2
- package/dist/panoptic/v2/index.d.ts +169 -3
- package/dist/panoptic/v2/index.d.ts.map +1 -1
- package/dist/panoptic/v2/index.js +658 -107
- package/dist/panoptic/v2/index.js.map +1 -1
- package/dist/panoptic/v2/react-public.d.ts +177 -3
- package/dist/panoptic/v2/react-public.d.ts.map +1 -1
- package/dist/panoptic/v2/react-public.js +674 -113
- package/dist/panoptic/v2/react-public.js.map +1 -1
- package/dist/{rates-COAJpKRd.js → rates-BwZnK0tG.js} +498 -482
- package/dist/{router-B2fj4_X7.js → router-Y1OxL9Nt.js} +2 -2
- package/dist/{router-DliBF9FK.js → router-jMP7fVno.js} +2 -2
- package/dist/{transactionFees-CNbjAigT.js → transactionFees-aXQlx-lq.js} +11 -6
- package/dist/uniswap/index.js +724 -1
- package/dist/uniswap/index.js.map +1 -1
- package/dist/{v2-C3n8n1_i.js → v2-B27ILZl4.js} +518 -202
- package/dist/vault-transaction-fees.d.ts +1 -0
- package/dist/vault-transaction-fees.d.ts.map +1 -1
- package/dist/vault-transaction-fees.js +59 -16
- package/dist/vault-transaction-fees.js.map +1 -1
- package/dist/{writes-BTkqc_rn.js → writes-B8NGXzUG.js} +219 -38
- package/package.json +1 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { StateViewAbi, getAccountCollateral, panopticQueryAbi, readBlockAndAggregate, requireReturnData } from "./irm-
|
|
2
|
-
import { AccountInsolventError, ChunkLimitError, DEFAULT_VEGOID, InvalidHistoryRangeError, MulticallResultMissingError, NotEnoughTokensError, PanopticError, PanopticValidationError, SafeModeError, StaleDataError, StorageDataNotFoundError, UnhealthyPoolError, WrongUniswapPoolError, applyMintBufferPerToken, availableToBorrow, collateralTrackerV2Abi, decodeLeftRightSigned, decodeLeftRightUnsigned, decodePositionBalance, getBlockMeta, getClosedPositionsKey, getPendingPositionsKey, getPool, getPoolMetaKey, getPoolMetadata, getPositionMetaKey, getPositionsKey, getTrackedChunksKey, getTrackedPositionIds, isPanopticErrorType, jsonSerializer, mintableAfterBuffer, panopticFactoryV3Abi, panopticFactoryV4Abi, panopticGuardianAbi, panopticLiquidatorAbi, panopticPoolV2Abi, panopticQueryAbi$1, parsePanopticError, riskEngineAbi, semiFungiblePositionManagerV3Abi, semiFungiblePositionManagerV4Abi, stateViewAbi, uniswapV3PoolAbi } from "./rates-
|
|
1
|
+
import { StateViewAbi, getAccountCollateral, panopticQueryAbi$1 as panopticQueryAbi, readBlockAndAggregate, requireReturnData } from "./irm-BDlcUNgJ.js";
|
|
2
|
+
import { AccountInsolventError, ChunkLimitError, DEFAULT_VEGOID, InvalidHistoryRangeError, MulticallResultMissingError, NotEnoughTokensError, PanopticError, PanopticValidationError, SafeModeError, StaleDataError, StorageDataNotFoundError, UnhealthyPoolError, WrongUniswapPoolError, applyMintBufferPerToken, availableToBorrow, collateralTrackerV2Abi, decodeLeftRightSigned, decodeLeftRightUnsigned, decodePositionBalance, getBlockMeta, getClosedPositionsKey, getPendingPositionsKey, getPool, getPoolMetaKey, getPoolMetadata, getPositionMetaKey, getPositionsKey, getTrackedChunksKey, getTrackedPositionIds, isPanopticErrorType, jsonSerializer, mintableAfterBuffer, panopticFactoryV3Abi, panopticFactoryV4Abi, panopticGuardianAbi, panopticLiquidatorAbi, panopticPoolV2Abi, panopticQueryAbi as panopticQueryAbi$1, parsePanopticError, riskEngineAbi, semiFungiblePositionManagerV3Abi, semiFungiblePositionManagerV4Abi, stateViewAbi, uniswapV3PoolAbi } from "./rates-BwZnK0tG.js";
|
|
3
3
|
import { MAX_TICK, MAX_TRACKED_CHUNKS, MIN_TICK, NO_LOWER_LIQUIDATION_TICK, NO_UPPER_LIQUIDATION_TICK, WAD, calculatePositionDelta, calculatePositionDeltaDebtOnly, calculatePositionGamma, calculatePositionValue, priceToTick, sqrtPriceX96ToTick, tickLimits, tickToPriceDecimalScaled, tickToSqrtPriceX96, toVaultFrameAtTick } from "./greeks-CNi1-cnp.js";
|
|
4
|
-
import { addLegToTokenId, buildBatchDispatchArgs,
|
|
5
|
-
import { getLpPositionFunding, getMaxLpPositionSize } from "./router-
|
|
6
|
-
import { BaseError, ContractFunctionExecutionError, ContractFunctionRevertedError, ExecutionRevertedError, decodeFunctionResult, encodeAbiParameters, encodeFunctionData, getAddress, hexToString, keccak256, parseAbi, toHex, trim, zeroAddress } from "viem";
|
|
4
|
+
import { addLegToTokenId, buildBatchDispatchArgs, buildUniqueCredit, buildUniqueLoan, countLegs, createTokenIdBuilder, decodeAllLegs, decodeTokenId, getPoolTokensForSimulation, loadCheckpoint, resolveTokenIndex, simulateDispatch, simulateOpenPosition, simulateWithTokenFlow } from "./writes-B8NGXzUG.js";
|
|
5
|
+
import { getLpPositionFunding, getMaxLpPositionSize } from "./router-Y1OxL9Nt.js";
|
|
6
|
+
import { BaseError, ContractFunctionExecutionError, ContractFunctionRevertedError, ExecutionRevertedError, decodeFunctionResult, encodeAbiParameters, encodeFunctionData, getAddress, hexToString, keccak256, parseAbi, parseEventLogs, toHex, trim, zeroAddress } from "viem";
|
|
7
7
|
import { multicall } from "viem/actions";
|
|
8
8
|
import Decimal from "decimal.js";
|
|
9
9
|
|
|
@@ -539,159 +539,6 @@ async function assertLpPositionFunded(params) {
|
|
|
539
539
|
return funding;
|
|
540
540
|
}
|
|
541
541
|
|
|
542
|
-
//#endregion
|
|
543
|
-
//#region src/panoptic/v2/simulations/simulateDispatch.ts
|
|
544
|
-
/**
|
|
545
|
-
* Simulate a raw dispatch operation.
|
|
546
|
-
*
|
|
547
|
-
* Uses PanopticPool.multicall with getAssetsOf-dispatch-getAssetsOf pattern
|
|
548
|
-
* to measure exact collateral asset movements.
|
|
549
|
-
*
|
|
550
|
-
* @param params - Simulation parameters
|
|
551
|
-
* @returns Simulation result with dispatch data or error
|
|
552
|
-
*/
|
|
553
|
-
async function simulateDispatch(params) {
|
|
554
|
-
const { client, poolAddress, account, positionIdList, finalPositionIdList, positionSizes, tickAndSpreadLimits, usePremiaAsCollateral = false, builderCode = 0n, blockNumber, existingPositionIdList, measurePremia = false } = params;
|
|
555
|
-
const targetBlockNumber = blockNumber ?? await client.getBlockNumber();
|
|
556
|
-
const metaPromise = getBlockMeta({
|
|
557
|
-
client,
|
|
558
|
-
blockNumber: targetBlockNumber
|
|
559
|
-
});
|
|
560
|
-
try {
|
|
561
|
-
const callData = encodeFunctionData({
|
|
562
|
-
abi: panopticPoolV2Abi,
|
|
563
|
-
functionName: "dispatch",
|
|
564
|
-
args: [
|
|
565
|
-
positionIdList,
|
|
566
|
-
finalPositionIdList,
|
|
567
|
-
positionSizes,
|
|
568
|
-
tickAndSpreadLimits.map((t) => [
|
|
569
|
-
Number(t[0]),
|
|
570
|
-
Number(t[1]),
|
|
571
|
-
Number(t[2])
|
|
572
|
-
]),
|
|
573
|
-
usePremiaAsCollateral,
|
|
574
|
-
builderCode
|
|
575
|
-
]
|
|
576
|
-
});
|
|
577
|
-
const preFullPositionsCallData = existingPositionIdList !== void 0 ? encodeFunctionData({
|
|
578
|
-
abi: panopticPoolV2Abi,
|
|
579
|
-
functionName: "getFullPositionsData",
|
|
580
|
-
args: [
|
|
581
|
-
account,
|
|
582
|
-
false,
|
|
583
|
-
existingPositionIdList
|
|
584
|
-
]
|
|
585
|
-
}) : void 0;
|
|
586
|
-
const prePremiaCallData = measurePremia && existingPositionIdList !== void 0 ? encodeFunctionData({
|
|
587
|
-
abi: panopticPoolV2Abi,
|
|
588
|
-
functionName: "getFullPositionsData",
|
|
589
|
-
args: [
|
|
590
|
-
account,
|
|
591
|
-
true,
|
|
592
|
-
existingPositionIdList
|
|
593
|
-
]
|
|
594
|
-
}) : void 0;
|
|
595
|
-
const postFullPositionsCallData = encodeFunctionData({
|
|
596
|
-
abi: panopticPoolV2Abi,
|
|
597
|
-
functionName: "getFullPositionsData",
|
|
598
|
-
args: [
|
|
599
|
-
account,
|
|
600
|
-
false,
|
|
601
|
-
finalPositionIdList
|
|
602
|
-
]
|
|
603
|
-
});
|
|
604
|
-
const postPremiaCallData = measurePremia ? encodeFunctionData({
|
|
605
|
-
abi: panopticPoolV2Abi,
|
|
606
|
-
functionName: "getFullPositionsData",
|
|
607
|
-
args: [
|
|
608
|
-
account,
|
|
609
|
-
true,
|
|
610
|
-
finalPositionIdList
|
|
611
|
-
]
|
|
612
|
-
}) : void 0;
|
|
613
|
-
const flowResult = await simulateWithTokenFlow({
|
|
614
|
-
client,
|
|
615
|
-
poolAddress,
|
|
616
|
-
user: account,
|
|
617
|
-
callData: params.settleSequence?.targets.length ? encodeFunctionData({
|
|
618
|
-
abi: panopticPoolV2Abi,
|
|
619
|
-
functionName: "multicall",
|
|
620
|
-
args: [[...buildSettleSequenceCalls(params.settleSequence), callData]]
|
|
621
|
-
}) : callData,
|
|
622
|
-
blockNumber: targetBlockNumber,
|
|
623
|
-
preCallData: preFullPositionsCallData ? [preFullPositionsCallData, ...prePremiaCallData ? [prePremiaCallData] : []] : void 0,
|
|
624
|
-
postCallData: [postFullPositionsCallData, ...postPremiaCallData ? [postPremiaCallData] : []]
|
|
625
|
-
});
|
|
626
|
-
if (!flowResult.success || !flowResult.tokenFlow) throw flowResult.rawError ?? new PanopticError(flowResult.error || "Token flow simulation failed");
|
|
627
|
-
const tokenFlow = flowResult.tokenFlow;
|
|
628
|
-
const preSnapshot = existingPositionIdList ?? [];
|
|
629
|
-
const positionsCreated = finalPositionIdList.filter((id) => !preSnapshot.includes(id));
|
|
630
|
-
const positionsClosed = preSnapshot.filter((id) => !finalPositionIdList.includes(id));
|
|
631
|
-
const decodeFullPositions = (data$1) => {
|
|
632
|
-
if (!data$1) return null;
|
|
633
|
-
try {
|
|
634
|
-
const decoded = decodeFunctionResult({
|
|
635
|
-
abi: panopticPoolV2Abi,
|
|
636
|
-
functionName: "getFullPositionsData",
|
|
637
|
-
data: data$1
|
|
638
|
-
});
|
|
639
|
-
const shortPremium = decodeLeftRightUnsigned(decoded[0]);
|
|
640
|
-
const longPremium = decodeLeftRightUnsigned(decoded[1]);
|
|
641
|
-
const reqs = decoded[3];
|
|
642
|
-
let collateralRequirements0 = 0n;
|
|
643
|
-
let collateralRequirements1 = 0n;
|
|
644
|
-
for (const packed of reqs) {
|
|
645
|
-
const r = decodeLeftRightUnsigned(packed);
|
|
646
|
-
collateralRequirements0 += r.right;
|
|
647
|
-
collateralRequirements1 += r.left;
|
|
648
|
-
}
|
|
649
|
-
return {
|
|
650
|
-
collateralRequirements0,
|
|
651
|
-
collateralRequirements1,
|
|
652
|
-
netPremia0: shortPremium.right - longPremium.right,
|
|
653
|
-
netPremia1: shortPremium.left - longPremium.left
|
|
654
|
-
};
|
|
655
|
-
} catch {
|
|
656
|
-
return null;
|
|
657
|
-
}
|
|
658
|
-
};
|
|
659
|
-
const prePositions = decodeFullPositions(flowResult.preCallResults?.[0]);
|
|
660
|
-
const postPositions = decodeFullPositions(flowResult.postCallResults?.[0]);
|
|
661
|
-
const prePremia = decodeFullPositions(flowResult.preCallResults?.[1]);
|
|
662
|
-
const postPremia = decodeFullPositions(flowResult.postCallResults?.[1]);
|
|
663
|
-
const _meta = await metaPromise;
|
|
664
|
-
const data = {
|
|
665
|
-
netAmount0: tokenFlow.delta0,
|
|
666
|
-
netAmount1: tokenFlow.delta1,
|
|
667
|
-
premiaReceived0: prePremia !== null && postPremia !== null ? prePremia.netPremia0 - postPremia.netPremia0 : null,
|
|
668
|
-
premiaReceived1: prePremia !== null && postPremia !== null ? prePremia.netPremia1 - postPremia.netPremia1 : null,
|
|
669
|
-
positionsCreated,
|
|
670
|
-
positionsClosed,
|
|
671
|
-
postCollateral0: tokenFlow.balanceAfter0,
|
|
672
|
-
postCollateral1: tokenFlow.balanceAfter1,
|
|
673
|
-
preMarginExcess0: prePositions === null ? null : tokenFlow.balanceBefore0 - prePositions.collateralRequirements0,
|
|
674
|
-
preMarginExcess1: prePositions === null ? null : tokenFlow.balanceBefore1 - prePositions.collateralRequirements1,
|
|
675
|
-
postMarginExcess0: postPositions === null ? null : tokenFlow.balanceAfter0 - postPositions.collateralRequirements0,
|
|
676
|
-
postMarginExcess1: postPositions === null ? null : tokenFlow.balanceAfter1 - postPositions.collateralRequirements1
|
|
677
|
-
};
|
|
678
|
-
return {
|
|
679
|
-
success: true,
|
|
680
|
-
data,
|
|
681
|
-
gasEstimate: flowResult.gasEstimate,
|
|
682
|
-
tokenFlow,
|
|
683
|
-
_meta
|
|
684
|
-
};
|
|
685
|
-
} catch (error) {
|
|
686
|
-
const _meta = await metaPromise;
|
|
687
|
-
return {
|
|
688
|
-
success: false,
|
|
689
|
-
error: error instanceof PanopticError ? error : new PanopticError(error instanceof Error ? error.message : "Simulation failed", error instanceof Error ? error : void 0),
|
|
690
|
-
_meta
|
|
691
|
-
};
|
|
692
|
-
}
|
|
693
|
-
}
|
|
694
|
-
|
|
695
542
|
//#endregion
|
|
696
543
|
//#region src/panoptic/v2/simulations/creditWrap.ts
|
|
697
544
|
/**
|
|
@@ -3549,6 +3396,73 @@ async function optimizeTokenIdRiskPartners(params) {
|
|
|
3549
3396
|
return optimizedTokenId;
|
|
3550
3397
|
}
|
|
3551
3398
|
|
|
3399
|
+
//#endregion
|
|
3400
|
+
//#region src/panoptic/v2/reads/tickNetWindows.ts
|
|
3401
|
+
/**
|
|
3402
|
+
* Wide liquidity distributions from several adjacent `getTickNets*` windows.
|
|
3403
|
+
*
|
|
3404
|
+
* @module v2/reads/tickNetWindows
|
|
3405
|
+
*/
|
|
3406
|
+
/** Absolute Uniswap V3/V4 tick bounds. */
|
|
3407
|
+
const TICK_MIN$1 = -887272;
|
|
3408
|
+
const TICK_MAX$1 = 887272;
|
|
3409
|
+
/**
|
|
3410
|
+
* Join ascending, edge-sharing windows into one distribution anchored to `windows[mainIndex]`.
|
|
3411
|
+
*
|
|
3412
|
+
* Each `getTickNets*` window is a running sum of liquidityNet from its own first tick (only the
|
|
3413
|
+
* window holding the current tick is rescaled to the pool's liquidity), so a neighbour's values
|
|
3414
|
+
* are off by a constant. Adjacent windows share their edge tick; the offset that makes that tick
|
|
3415
|
+
* agree is applied to the whole neighbour. Stitching stops at the first window that does not share
|
|
3416
|
+
* an edge with the one before it.
|
|
3417
|
+
*/
|
|
3418
|
+
function stitchTickNetWindows(windows, mainIndex) {
|
|
3419
|
+
const main = windows[mainIndex];
|
|
3420
|
+
if (!main) return {
|
|
3421
|
+
ticks: [],
|
|
3422
|
+
liquidityNets: []
|
|
3423
|
+
};
|
|
3424
|
+
const ticks = [...main.ticks];
|
|
3425
|
+
const nets = [...main.liquidityNets];
|
|
3426
|
+
for (let i = mainIndex + 1; i < windows.length; i++) {
|
|
3427
|
+
const window = windows[i];
|
|
3428
|
+
const edge = ticks.length - 1;
|
|
3429
|
+
if (!window.ticks.length || window.ticks[0] !== ticks[edge]) break;
|
|
3430
|
+
const offset = nets[edge] - window.liquidityNets[0];
|
|
3431
|
+
ticks.push(...window.ticks.slice(1));
|
|
3432
|
+
nets.push(...window.liquidityNets.slice(1).map((value) => value + offset));
|
|
3433
|
+
}
|
|
3434
|
+
for (let i = mainIndex - 1; i >= 0; i--) {
|
|
3435
|
+
const window = windows[i];
|
|
3436
|
+
const last = window.ticks.length - 1;
|
|
3437
|
+
if (last < 0 || window.ticks[last] !== ticks[0]) break;
|
|
3438
|
+
const offset = nets[0] - window.liquidityNets[last];
|
|
3439
|
+
ticks.unshift(...window.ticks.slice(0, last));
|
|
3440
|
+
nets.unshift(...window.liquidityNets.slice(0, last).map((value) => value + offset));
|
|
3441
|
+
}
|
|
3442
|
+
return {
|
|
3443
|
+
ticks,
|
|
3444
|
+
liquidityNets: nets
|
|
3445
|
+
};
|
|
3446
|
+
}
|
|
3447
|
+
/**
|
|
3448
|
+
* Read the main window around `startTick` plus up to `windowsPerSide` windows of `nTicks` on each
|
|
3449
|
+
* side, in parallel, and stitch them. Side windows that would cross the tick bounds are skipped.
|
|
3450
|
+
*/
|
|
3451
|
+
async function readTickNetWindows({ startTick, mainNTicks, nTicks, tickSpacing, windowsPerSide, read }) {
|
|
3452
|
+
const center = Math.trunc(startTick / tickSpacing) * tickSpacing;
|
|
3453
|
+
const half = Number(nTicks) * tickSpacing;
|
|
3454
|
+
const mainHalf = Number(mainNTicks) * tickSpacing;
|
|
3455
|
+
const sideCenters = (direction) => Array.from({ length: windowsPerSide }, (_, k) => center + direction * (mainHalf + half + 2 * half * k)).filter((side) => side - half >= TICK_MIN$1 && side + half <= TICK_MAX$1);
|
|
3456
|
+
const below = sideCenters(-1).reverse();
|
|
3457
|
+
const above = sideCenters(1);
|
|
3458
|
+
const windows = await Promise.all([
|
|
3459
|
+
...below.map((side) => read(side, nTicks)),
|
|
3460
|
+
read(startTick, mainNTicks),
|
|
3461
|
+
...above.map((side) => read(side, nTicks))
|
|
3462
|
+
]);
|
|
3463
|
+
return stitchTickNetWindows(windows, below.length);
|
|
3464
|
+
}
|
|
3465
|
+
|
|
3552
3466
|
//#endregion
|
|
3553
3467
|
//#region src/panoptic/v2/reads/liquidity.ts
|
|
3554
3468
|
/**
|
|
@@ -3566,24 +3480,35 @@ async function optimizeTokenIdRiskPartners(params) {
|
|
|
3566
3480
|
async function getPoolLiquidities(params) {
|
|
3567
3481
|
const { client, poolAddress, queryAddress, startTick, nTicks, blockNumber } = params;
|
|
3568
3482
|
const targetBlockNumber = blockNumber ?? params._meta?.blockNumber ?? await client.getBlockNumber();
|
|
3569
|
-
const
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3483
|
+
const windowsPerSide = params.windowsPerSide ?? 0;
|
|
3484
|
+
if (windowsPerSide > 0 && params.tickSpacing === void 0) throw new PanopticValidationError("getPoolLiquidities: tickSpacing is required with windowsPerSide");
|
|
3485
|
+
const [{ ticks, liquidityNets }, _meta] = await Promise.all([readTickNetWindows({
|
|
3486
|
+
startTick: Number(startTick),
|
|
3487
|
+
mainNTicks: nTicks,
|
|
3488
|
+
nTicks,
|
|
3489
|
+
tickSpacing: params.tickSpacing ?? 1,
|
|
3490
|
+
windowsPerSide,
|
|
3491
|
+
read: (centerTick, windowTicks) => client.readContract({
|
|
3492
|
+
address: queryAddress,
|
|
3493
|
+
abi: panopticQueryAbi,
|
|
3494
|
+
functionName: "getTickNets",
|
|
3495
|
+
args: [
|
|
3496
|
+
poolAddress,
|
|
3497
|
+
centerTick,
|
|
3498
|
+
windowTicks
|
|
3499
|
+
],
|
|
3500
|
+
blockNumber: targetBlockNumber
|
|
3501
|
+
}).then(([tickData, nets]) => ({
|
|
3502
|
+
ticks: tickData,
|
|
3503
|
+
liquidityNets: nets
|
|
3504
|
+
}))
|
|
3579
3505
|
}), params._meta ?? getBlockMeta({
|
|
3580
3506
|
client,
|
|
3581
3507
|
blockNumber: targetBlockNumber
|
|
3582
3508
|
})]);
|
|
3583
|
-
const [tickData, liquidityNets] = result;
|
|
3584
3509
|
return {
|
|
3585
|
-
ticks
|
|
3586
|
-
liquidityNets
|
|
3510
|
+
ticks,
|
|
3511
|
+
liquidityNets,
|
|
3587
3512
|
_meta
|
|
3588
3513
|
};
|
|
3589
3514
|
}
|
|
@@ -4475,6 +4400,32 @@ function computeUniswapFeesForBlock(blockData, legs) {
|
|
|
4475
4400
|
total1
|
|
4476
4401
|
};
|
|
4477
4402
|
}
|
|
4403
|
+
const MAX_UINT256$1 = 2n ** 256n - 1n;
|
|
4404
|
+
/**
|
|
4405
|
+
* Fee growth per unit of liquidity inside `[lowerTick, upperTick)` at one block,
|
|
4406
|
+
* wrapped to uint256 as the pool computes it. Diff two snapshots with
|
|
4407
|
+
* `feesFromFeeGrowthDelta` to get the fees a constant liquidity earned between them.
|
|
4408
|
+
*/
|
|
4409
|
+
function feeGrowthInsideX128(blockData, lowerTick, upperTick) {
|
|
4410
|
+
const lower = blockData.tickData.get(lowerTick);
|
|
4411
|
+
const upper = blockData.tickData.get(upperTick);
|
|
4412
|
+
if (!lower || !upper) return null;
|
|
4413
|
+
const { currentTick } = blockData;
|
|
4414
|
+
const inside = (global, lowerOutside, upperOutside) => {
|
|
4415
|
+
const below = currentTick >= lowerTick ? lowerOutside : global - lowerOutside;
|
|
4416
|
+
const above = currentTick < upperTick ? upperOutside : global - upperOutside;
|
|
4417
|
+
return global - below - above & MAX_UINT256$1;
|
|
4418
|
+
};
|
|
4419
|
+
return {
|
|
4420
|
+
feeGrowthInside0X128: inside(blockData.feeGrowthGlobal0, lower.feeGrowthOutside0, upper.feeGrowthOutside0),
|
|
4421
|
+
feeGrowthInside1X128: inside(blockData.feeGrowthGlobal1, lower.feeGrowthOutside1, upper.feeGrowthOutside1)
|
|
4422
|
+
};
|
|
4423
|
+
}
|
|
4424
|
+
/** Blocks before the chain's Multicall3 deployment need viem's deployless multicall. */
|
|
4425
|
+
function predatesMulticall3(client, blockNumber) {
|
|
4426
|
+
const deployedAt = client.chain?.contracts?.multicall3?.blockCreated;
|
|
4427
|
+
return blockNumber != null && deployedAt != null && blockNumber < BigInt(deployedAt);
|
|
4428
|
+
}
|
|
4478
4429
|
async function fetchUniswapBlockSnapshot(client, blockNumber, uniqueTicks, poolConfig) {
|
|
4479
4430
|
if (poolConfig.version === "v3") return fetchV3BlockSnapshot(client, poolConfig.poolAddress, blockNumber, uniqueTicks);
|
|
4480
4431
|
else return fetchV4BlockSnapshot(client, poolConfig.stateViewAddress, poolConfig.poolId, blockNumber, uniqueTicks);
|
|
@@ -4506,7 +4457,8 @@ async function fetchV3BlockSnapshot(client, poolAddress, blockNumber, uniqueTick
|
|
|
4506
4457
|
const results = await client.multicall({
|
|
4507
4458
|
contracts,
|
|
4508
4459
|
blockNumber,
|
|
4509
|
-
allowFailure: false
|
|
4460
|
+
allowFailure: false,
|
|
4461
|
+
deployless: predatesMulticall3(client, blockNumber)
|
|
4510
4462
|
});
|
|
4511
4463
|
const slot0Result = results[0];
|
|
4512
4464
|
const feeGrowthGlobal0 = results[1];
|
|
@@ -4515,6 +4467,7 @@ async function fetchV3BlockSnapshot(client, poolAddress, blockNumber, uniqueTick
|
|
|
4515
4467
|
for (let i = 0; i < uniqueTicks.length; i++) {
|
|
4516
4468
|
const tickResult = results[3 + i];
|
|
4517
4469
|
tickData.set(uniqueTicks[i], {
|
|
4470
|
+
liquidityGross: tickResult[0],
|
|
4518
4471
|
feeGrowthOutside0: tickResult[2],
|
|
4519
4472
|
feeGrowthOutside1: tickResult[3]
|
|
4520
4473
|
});
|
|
@@ -4551,7 +4504,8 @@ async function fetchV4BlockSnapshot(client, stateViewAddress, poolId, blockNumbe
|
|
|
4551
4504
|
const results = await client.multicall({
|
|
4552
4505
|
contracts,
|
|
4553
4506
|
blockNumber,
|
|
4554
|
-
allowFailure: false
|
|
4507
|
+
allowFailure: false,
|
|
4508
|
+
deployless: predatesMulticall3(client, blockNumber)
|
|
4555
4509
|
});
|
|
4556
4510
|
const slot0Result = results[0];
|
|
4557
4511
|
const feeGrowthResult = results[1];
|
|
@@ -4559,6 +4513,7 @@ async function fetchV4BlockSnapshot(client, stateViewAddress, poolId, blockNumbe
|
|
|
4559
4513
|
for (let i = 0; i < uniqueTicks.length; i++) {
|
|
4560
4514
|
const tickResult = results[2 + i];
|
|
4561
4515
|
tickData.set(uniqueTicks[i], {
|
|
4516
|
+
liquidityGross: tickResult[0],
|
|
4562
4517
|
feeGrowthOutside0: tickResult[2],
|
|
4563
4518
|
feeGrowthOutside1: tickResult[3]
|
|
4564
4519
|
});
|
|
@@ -4861,23 +4816,34 @@ function clampNTicks(startTick, tickSpacing, nTicks) {
|
|
|
4861
4816
|
* for the pool — only a deployed PanopticQuery on the chain.
|
|
4862
4817
|
*/
|
|
4863
4818
|
async function getUniswapV3PoolLiquidities(params) {
|
|
4864
|
-
const { client, poolAddress, queryAddress, startTick, nTicks } = params;
|
|
4865
|
-
|
|
4819
|
+
const { client, poolAddress, queryAddress, startTick, nTicks, windowsPerSide = 0 } = params;
|
|
4820
|
+
if (windowsPerSide > 0 && params.tickSpacing === void 0) throw new PanopticValidationError("getUniswapV3PoolLiquidities: tickSpacing is required with windowsPerSide");
|
|
4821
|
+
const safeN = clampNTicks(startTick, params.tickSpacing ?? 1, nTicks);
|
|
4866
4822
|
const _meta = await getBlockMeta({ client });
|
|
4867
|
-
const
|
|
4868
|
-
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
|
|
4872
|
-
|
|
4873
|
-
|
|
4874
|
-
|
|
4875
|
-
|
|
4876
|
-
|
|
4823
|
+
const { ticks, liquidityNets } = await readTickNetWindows({
|
|
4824
|
+
startTick,
|
|
4825
|
+
mainNTicks: safeN,
|
|
4826
|
+
nTicks,
|
|
4827
|
+
tickSpacing: params.tickSpacing ?? 1,
|
|
4828
|
+
windowsPerSide,
|
|
4829
|
+
read: (centerTick, windowTicks) => client.readContract({
|
|
4830
|
+
address: queryAddress,
|
|
4831
|
+
abi: panopticQueryAbi,
|
|
4832
|
+
functionName: "getTickNetsV3",
|
|
4833
|
+
args: [
|
|
4834
|
+
poolAddress,
|
|
4835
|
+
centerTick,
|
|
4836
|
+
windowTicks
|
|
4837
|
+
],
|
|
4838
|
+
blockNumber: _meta.blockNumber
|
|
4839
|
+
}).then(([windowTickData, windowNets]) => ({
|
|
4840
|
+
ticks: windowTickData,
|
|
4841
|
+
liquidityNets: windowNets
|
|
4842
|
+
}))
|
|
4877
4843
|
});
|
|
4878
4844
|
return {
|
|
4879
|
-
ticks
|
|
4880
|
-
liquidityNets
|
|
4845
|
+
ticks,
|
|
4846
|
+
liquidityNets,
|
|
4881
4847
|
_meta
|
|
4882
4848
|
};
|
|
4883
4849
|
}
|
|
@@ -5050,22 +5016,32 @@ async function getUniswapV4PoolLiquidities(params) {
|
|
|
5050
5016
|
const { client, queryAddress, poolManager, poolId, tickSpacing, startTick, nTicks } = params;
|
|
5051
5017
|
const safeN = clampNTicks(startTick, tickSpacing, nTicks);
|
|
5052
5018
|
const _meta = await getBlockMeta({ client });
|
|
5053
|
-
const
|
|
5054
|
-
|
|
5055
|
-
|
|
5056
|
-
|
|
5057
|
-
|
|
5058
|
-
|
|
5059
|
-
|
|
5060
|
-
|
|
5061
|
-
|
|
5062
|
-
|
|
5063
|
-
|
|
5064
|
-
|
|
5019
|
+
const { ticks, liquidityNets } = await readTickNetWindows({
|
|
5020
|
+
startTick,
|
|
5021
|
+
mainNTicks: safeN,
|
|
5022
|
+
nTicks,
|
|
5023
|
+
tickSpacing,
|
|
5024
|
+
windowsPerSide: params.windowsPerSide ?? 0,
|
|
5025
|
+
read: (centerTick, windowTicks) => client.readContract({
|
|
5026
|
+
address: queryAddress,
|
|
5027
|
+
abi: panopticQueryAbi,
|
|
5028
|
+
functionName: "getTickNetsV4",
|
|
5029
|
+
args: [
|
|
5030
|
+
poolManager,
|
|
5031
|
+
poolId,
|
|
5032
|
+
tickSpacing,
|
|
5033
|
+
centerTick,
|
|
5034
|
+
windowTicks
|
|
5035
|
+
],
|
|
5036
|
+
blockNumber: _meta.blockNumber
|
|
5037
|
+
}).then(([windowTickData, windowNets]) => ({
|
|
5038
|
+
ticks: windowTickData,
|
|
5039
|
+
liquidityNets: windowNets
|
|
5040
|
+
}))
|
|
5065
5041
|
});
|
|
5066
5042
|
return {
|
|
5067
|
-
ticks
|
|
5068
|
-
liquidityNets
|
|
5043
|
+
ticks,
|
|
5044
|
+
liquidityNets,
|
|
5069
5045
|
_meta
|
|
5070
5046
|
};
|
|
5071
5047
|
}
|
|
@@ -9927,6 +9903,131 @@ function marketRiskFromValues({ lower, current, upper }) {
|
|
|
9927
9903
|
};
|
|
9928
9904
|
}
|
|
9929
9905
|
|
|
9906
|
+
//#endregion
|
|
9907
|
+
//#region src/panoptic/v2/greeks/positionVolatility.ts
|
|
9908
|
+
const Precision = Decimal.clone({ precision: 80 });
|
|
9909
|
+
const Q96 = 1n << 96n;
|
|
9910
|
+
const YEAR_SECONDS = new Precision(31536e3);
|
|
9911
|
+
/** The premium-free position curve, expressed in one quote token's human units. */
|
|
9912
|
+
function preparePositionGamma({ tokenId, positionSize, quoteIsToken0, quoteDecimals }) {
|
|
9913
|
+
if (positionSize <= 0n || positionSize >= 1n << 128n) throw new RangeError("Invalid position size");
|
|
9914
|
+
if (!Number.isInteger(quoteDecimals) || quoteDecimals < 0 || quoteDecimals > 255) throw new RangeError("Invalid quote decimals");
|
|
9915
|
+
const decoded = decodeTokenId(tokenId);
|
|
9916
|
+
const chunks = decoded.legs.flatMap((leg) => {
|
|
9917
|
+
if (leg.width === 0n) return [];
|
|
9918
|
+
const width = leg.width * decoded.tickSpacing;
|
|
9919
|
+
const lowerTick = leg.strike - width / 2n;
|
|
9920
|
+
const upperTick = leg.strike + (width + 1n) / 2n;
|
|
9921
|
+
if (lowerTick < -887272n || upperTick > 887272n || lowerTick >= upperTick) throw new RangeError("Invalid position range");
|
|
9922
|
+
const lower = tickToSqrtPriceX96(lowerTick);
|
|
9923
|
+
const upper = tickToSqrtPriceX96(upperTick);
|
|
9924
|
+
const amount = positionSize * leg.optionRatio;
|
|
9925
|
+
const liquidity = leg.asset === 0n ? amount * (lower * upper / Q96) / (upper - lower) : amount * Q96 / (upper - lower);
|
|
9926
|
+
if (liquidity >= 1n << 128n) throw new RangeError("Liquidity exceeds uint128");
|
|
9927
|
+
return [{
|
|
9928
|
+
lowerTick: Number(lowerTick),
|
|
9929
|
+
upperTick: Number(upperTick),
|
|
9930
|
+
liquidity,
|
|
9931
|
+
isLong: leg.isLong
|
|
9932
|
+
}];
|
|
9933
|
+
});
|
|
9934
|
+
const scale = new Precision(10).pow(quoteDecimals);
|
|
9935
|
+
const atTick = (tick) => {
|
|
9936
|
+
const netLiquidity = chunks.reduce((sum$1, chunk) => tick >= BigInt(chunk.lowerTick) && tick < BigInt(chunk.upperTick) ? sum$1 + (chunk.isLong ? -chunk.liquidity : chunk.liquidity) : sum$1, 0n);
|
|
9937
|
+
const sqrt = new Precision(tickToSqrtPriceX96(tick).toString()).div(Q96.toString());
|
|
9938
|
+
const gamma = new Precision(netLiquidity.toString()).neg().div(2).div(scale);
|
|
9939
|
+
return quoteIsToken0 ? gamma.div(sqrt) : gamma.mul(sqrt);
|
|
9940
|
+
};
|
|
9941
|
+
const inRange = (low, high) => {
|
|
9942
|
+
if (low > high) throw new RangeError("Invalid candle range");
|
|
9943
|
+
const ticks = new Set([low, high]);
|
|
9944
|
+
for (const chunk of chunks) for (const boundary of [BigInt(chunk.lowerTick), BigInt(chunk.upperTick)]) {
|
|
9945
|
+
if (boundary >= low && boundary <= high) ticks.add(boundary);
|
|
9946
|
+
if (boundary - 1n >= low && boundary - 1n <= high) ticks.add(boundary - 1n);
|
|
9947
|
+
}
|
|
9948
|
+
return [...ticks].map(atTick);
|
|
9949
|
+
};
|
|
9950
|
+
return {
|
|
9951
|
+
chunks,
|
|
9952
|
+
atTick,
|
|
9953
|
+
inRange
|
|
9954
|
+
};
|
|
9955
|
+
}
|
|
9956
|
+
/** Historical whole-position diagnostics, with signs retained independently of eligibility. */
|
|
9957
|
+
function calculatePositionVolatilityMetrics({ observations, netPremium, baseFees, quoteDecimals }) {
|
|
9958
|
+
if (observations.length < 2) throw new RangeError("At least two price observations are required");
|
|
9959
|
+
if (!Number.isInteger(quoteDecimals) || quoteDecimals < 0 || quoteDecimals > 255) throw new RangeError("Invalid quote decimals");
|
|
9960
|
+
const points = observations.map((point) => ({
|
|
9961
|
+
...point,
|
|
9962
|
+
price: new Precision(point.price),
|
|
9963
|
+
gamma: new Precision(point.gamma),
|
|
9964
|
+
rangeGammas: (point.rangeGammas ?? []).map((gamma) => new Precision(gamma))
|
|
9965
|
+
}));
|
|
9966
|
+
for (const point of points) if (!point.price.isFinite() || point.price.lte(0) || !point.gamma.isFinite() || point.rangeGammas.some((gamma) => !gamma.isFinite())) throw new RangeError("Invalid price or gamma observation");
|
|
9967
|
+
const quantum = new Precision(10).pow(-quoteDecimals);
|
|
9968
|
+
const gammas = points.flatMap((point) => [point.gamma, ...point.rangeGammas]);
|
|
9969
|
+
const peak = gammas.reduce((max, gamma) => Precision.max(max, gamma.abs()), new Precision(0));
|
|
9970
|
+
const tolerance = Precision.max(quantum, peak.mul("1e-12"));
|
|
9971
|
+
const positive = gammas.some((gamma) => gamma.gt(tolerance));
|
|
9972
|
+
const negative = gammas.some((gamma) => gamma.lt(tolerance.neg()));
|
|
9973
|
+
const gammaSign = positive && negative ? "changing" : positive ? "positive" : negative ? "negative" : "zero";
|
|
9974
|
+
let exposure = new Precision(0);
|
|
9975
|
+
let signedConvexity = new Precision(0);
|
|
9976
|
+
let absoluteConvexity = new Precision(0);
|
|
9977
|
+
let years = new Precision(0);
|
|
9978
|
+
for (let i = 1; i < points.length; i++) {
|
|
9979
|
+
const previous = points[i - 1];
|
|
9980
|
+
const point = points[i];
|
|
9981
|
+
const seconds = point.timestamp - previous.timestamp;
|
|
9982
|
+
if (seconds <= 0n) throw new RangeError("Observations must have increasing timestamps");
|
|
9983
|
+
const elapsed = new Precision(seconds.toString()).div(YEAR_SECONDS);
|
|
9984
|
+
const squaredReturn = point.price.div(previous.price).ln().pow(2);
|
|
9985
|
+
exposure = exposure.plus(previous.gamma.abs().mul(elapsed));
|
|
9986
|
+
signedConvexity = signedConvexity.plus(previous.gamma.mul(squaredReturn).div(2));
|
|
9987
|
+
absoluteConvexity = absoluteConvexity.plus(previous.gamma.abs().mul(squaredReturn).div(2));
|
|
9988
|
+
years = years.plus(elapsed);
|
|
9989
|
+
}
|
|
9990
|
+
const sufficientExposure = exposure.gt(tolerance.mul(years).mul(100));
|
|
9991
|
+
const premium = netPremium === null ? null : new Precision(netPremium);
|
|
9992
|
+
const fees = baseFees == null ? null : new Precision(baseFees);
|
|
9993
|
+
if (premium !== null && !premium.isFinite() || fees !== null && !fees.isFinite()) throw new RangeError("Invalid accounting amount");
|
|
9994
|
+
const consistent = (amount) => gammaSign === "positive" ? amount.lte(quantum) : gammaSign === "negative" && amount.gte(quantum.neg());
|
|
9995
|
+
const reason = !sufficientExposure ? "insufficient-exposure" : gammaSign === "changing" ? "changing-sign" : premium === null ? "premium-unavailable" : !consistent(premium) ? "inconsistent-premium-direction" : null;
|
|
9996
|
+
const equivalent = (amount) => amount.abs().mul(2).div(exposure).sqrt().toString();
|
|
9997
|
+
const coverage = (amount) => absoluteConvexity.gt(quantum) ? amount.abs().div(absoluteConvexity).toString() : null;
|
|
9998
|
+
const eligibleFees = sufficientExposure && fees !== null && consistent(fees);
|
|
9999
|
+
return {
|
|
10000
|
+
gammaSign,
|
|
10001
|
+
exposure: exposure.toString(),
|
|
10002
|
+
signedConvexity: signedConvexity.toString(),
|
|
10003
|
+
absoluteConvexity: absoluteConvexity.toString(),
|
|
10004
|
+
netPremium: premium?.toString() ?? null,
|
|
10005
|
+
estimatedHedgedResult: premium?.plus(signedConvexity).toString() ?? null,
|
|
10006
|
+
weightedRealizedVolatility: sufficientExposure ? equivalent(absoluteConvexity) : null,
|
|
10007
|
+
premiumEquivalentVolatility: reason === null && premium !== null ? equivalent(premium) : null,
|
|
10008
|
+
premiumToConvexity: reason === null && premium !== null ? coverage(premium) : null,
|
|
10009
|
+
baseFees: fees?.toString() ?? null,
|
|
10010
|
+
feeEquivalentVolatility: eligibleFees ? equivalent(fees) : null,
|
|
10011
|
+
feeToConvexity: eligibleFees ? coverage(fees) : null,
|
|
10012
|
+
comparisonReason: reason
|
|
10013
|
+
};
|
|
10014
|
+
}
|
|
10015
|
+
/** Quote-value signed cumulative token increments, excluding revaluation of previous accrual. */
|
|
10016
|
+
function valuePositionAccrual({ snapshots, quoteIsToken0, quoteDecimals }) {
|
|
10017
|
+
let previous0 = 0n;
|
|
10018
|
+
let previous1 = 0n;
|
|
10019
|
+
let total = new Precision(0);
|
|
10020
|
+
for (const snapshot of snapshots) {
|
|
10021
|
+
const amount0 = new Precision((snapshot.token0 - previous0).toString());
|
|
10022
|
+
const amount1 = new Precision((snapshot.token1 - previous1).toString());
|
|
10023
|
+
const price = new Precision("1.0001").pow(snapshot.tick.toString());
|
|
10024
|
+
total = total.plus(quoteIsToken0 ? amount0.plus(amount1.div(price)) : amount1.plus(amount0.mul(price)));
|
|
10025
|
+
previous0 = snapshot.token0;
|
|
10026
|
+
previous1 = snapshot.token1;
|
|
10027
|
+
}
|
|
10028
|
+
return total.div(new Precision(10).pow(quoteDecimals));
|
|
10029
|
+
}
|
|
10030
|
+
|
|
9930
10031
|
//#endregion
|
|
9931
10032
|
//#region src/panoptic/v2/reads/collateralCurve.ts
|
|
9932
10033
|
/** Native-token margin inputs, independent of the tick used to display the curve. */
|
|
@@ -10120,4 +10221,219 @@ function preparePositionValueCurve(curve) {
|
|
|
10120
10221
|
}
|
|
10121
10222
|
|
|
10122
10223
|
//#endregion
|
|
10123
|
-
|
|
10224
|
+
//#region src/panoptic/v2/reads/positionVolatilityHistory.ts
|
|
10225
|
+
const signedSlot = (value) => BigInt.asIntN(128, value);
|
|
10226
|
+
const unpack = (value) => ({
|
|
10227
|
+
token0: signedSlot(value),
|
|
10228
|
+
token1: signedSlot(value >> 128n)
|
|
10229
|
+
});
|
|
10230
|
+
/** RPC accounting for exactly one mint lifecycle; candle prices are supplied by the application. */
|
|
10231
|
+
async function getPositionVolatilityHistory({ client, poolAddress, account, tokenId, mintBlock, endBlock, poolConfig, includeBaseFees = false }) {
|
|
10232
|
+
if (mintBlock <= 0n || endBlock <= mintBlock) throw new RangeError("Insufficient lifecycle history");
|
|
10233
|
+
const [mints, burns] = await Promise.all([client.getContractEvents({
|
|
10234
|
+
address: poolAddress,
|
|
10235
|
+
abi: panopticPoolV2Abi,
|
|
10236
|
+
eventName: "OptionMinted",
|
|
10237
|
+
args: {
|
|
10238
|
+
recipient: account,
|
|
10239
|
+
tokenId
|
|
10240
|
+
},
|
|
10241
|
+
fromBlock: mintBlock,
|
|
10242
|
+
toBlock: mintBlock,
|
|
10243
|
+
strict: true
|
|
10244
|
+
}), client.getContractEvents({
|
|
10245
|
+
address: poolAddress,
|
|
10246
|
+
abi: panopticPoolV2Abi,
|
|
10247
|
+
eventName: "OptionBurnt",
|
|
10248
|
+
args: {
|
|
10249
|
+
recipient: account,
|
|
10250
|
+
tokenId
|
|
10251
|
+
},
|
|
10252
|
+
fromBlock: mintBlock,
|
|
10253
|
+
toBlock: endBlock,
|
|
10254
|
+
strict: true
|
|
10255
|
+
})]);
|
|
10256
|
+
const mint = mints[0];
|
|
10257
|
+
if (mints.length !== 1 || !mint) throw new Error("Opening event is missing or ambiguous");
|
|
10258
|
+
const balance = decodePositionBalance(mint.args.balanceData);
|
|
10259
|
+
const close = burns.filter((burn) => burn.blockNumber > mintBlock || burn.logIndex > mint.logIndex).sort((a, b) => a.blockNumber === b.blockNumber ? a.logIndex - b.logIndex : a.blockNumber < b.blockNumber ? -1 : 1)[0];
|
|
10260
|
+
const finalBlock = close?.blockNumber ?? endBlock;
|
|
10261
|
+
if (finalBlock <= mintBlock) throw new Error("Intrablock lifecycle cannot be reconstructed");
|
|
10262
|
+
if (close && close.args.positionSize !== balance.positionSize) throw new Error("Position size changed");
|
|
10263
|
+
const [start, end, boundaryPrices] = await Promise.all([
|
|
10264
|
+
getBlockMeta({
|
|
10265
|
+
client,
|
|
10266
|
+
blockNumber: mintBlock
|
|
10267
|
+
}),
|
|
10268
|
+
getBlockMeta({
|
|
10269
|
+
client,
|
|
10270
|
+
blockNumber: finalBlock
|
|
10271
|
+
}),
|
|
10272
|
+
getPriceHistory({
|
|
10273
|
+
client,
|
|
10274
|
+
poolConfig,
|
|
10275
|
+
blockNumbers: [mintBlock, finalBlock]
|
|
10276
|
+
})
|
|
10277
|
+
]);
|
|
10278
|
+
const openingPrice = boundaryPrices.snapshots[0];
|
|
10279
|
+
const endingPrice = boundaryPrices.snapshots[1];
|
|
10280
|
+
if (!openingPrice || !endingPrice) throw new Error("Boundary prices unavailable");
|
|
10281
|
+
const pointCount = Math.min(200, Math.max(2, Number((end.blockTimestamp - start.blockTimestamp) / 900n) + 2));
|
|
10282
|
+
const blockNumbers = [...new Set(interpolateBlocks(mintBlock, finalBlock, pointCount))];
|
|
10283
|
+
const metadata = [];
|
|
10284
|
+
for (let i = 0; i < blockNumbers.length; i += 16) metadata.push(...await Promise.all(blockNumbers.slice(i, i + 16).map((blockNumber) => getBlockMeta({
|
|
10285
|
+
client,
|
|
10286
|
+
blockNumber
|
|
10287
|
+
}))));
|
|
10288
|
+
const timestamps = new Map(metadata.map((meta) => [meta.blockNumber, meta.blockTimestamp]));
|
|
10289
|
+
const chunks = preparePositionGamma({
|
|
10290
|
+
tokenId,
|
|
10291
|
+
positionSize: balance.positionSize,
|
|
10292
|
+
quoteIsToken0: false,
|
|
10293
|
+
quoteDecimals: 0
|
|
10294
|
+
}).chunks.filter((chunk) => chunk.liquidity > 0n);
|
|
10295
|
+
let premiumError = null;
|
|
10296
|
+
let feeError = includeBaseFees ? null : "Base fee history was not requested";
|
|
10297
|
+
let premium = null;
|
|
10298
|
+
let fees = null;
|
|
10299
|
+
try {
|
|
10300
|
+
const settlementLogs = await client.getContractEvents({
|
|
10301
|
+
address: poolAddress,
|
|
10302
|
+
abi: panopticPoolV2Abi,
|
|
10303
|
+
eventName: "PremiumSettled",
|
|
10304
|
+
args: {
|
|
10305
|
+
user: account,
|
|
10306
|
+
tokenId
|
|
10307
|
+
},
|
|
10308
|
+
fromBlock: mintBlock,
|
|
10309
|
+
toBlock: finalBlock,
|
|
10310
|
+
strict: true
|
|
10311
|
+
});
|
|
10312
|
+
const settled = [];
|
|
10313
|
+
const seen = new Set();
|
|
10314
|
+
for (const log of settlementLogs) {
|
|
10315
|
+
if (log.blockNumber === mintBlock && log.logIndex <= mint.logIndex) continue;
|
|
10316
|
+
if (close && log.blockNumber === finalBlock && log.logIndex >= close.logIndex) continue;
|
|
10317
|
+
const key = `${log.transactionHash}:${log.logIndex}`;
|
|
10318
|
+
if (seen.has(key)) continue;
|
|
10319
|
+
seen.add(key);
|
|
10320
|
+
const amounts = unpack(log.args.settledAmounts);
|
|
10321
|
+
settled.push({
|
|
10322
|
+
blockNumber: log.blockNumber,
|
|
10323
|
+
settled0: amounts.token0,
|
|
10324
|
+
settled1: amounts.token1
|
|
10325
|
+
});
|
|
10326
|
+
}
|
|
10327
|
+
if (close) {
|
|
10328
|
+
const receipt = await client.getTransactionReceipt({ hash: close.transactionHash });
|
|
10329
|
+
const liquidations = parseEventLogs({
|
|
10330
|
+
abi: panopticPoolV2Abi,
|
|
10331
|
+
logs: receipt.logs.filter((log) => log.address.toLowerCase() === poolAddress.toLowerCase()),
|
|
10332
|
+
eventName: "AccountLiquidated"
|
|
10333
|
+
});
|
|
10334
|
+
if (liquidations.some((event) => event.args.liquidatee.toLowerCase() === account.toLowerCase())) throw new Error("Liquidation premium requires haircut reconciliation");
|
|
10335
|
+
for (const packed of close.args.premiaByLeg) {
|
|
10336
|
+
const amounts = unpack(packed);
|
|
10337
|
+
settled.push({
|
|
10338
|
+
blockNumber: finalBlock,
|
|
10339
|
+
settled0: amounts.token0,
|
|
10340
|
+
settled1: amounts.token1
|
|
10341
|
+
});
|
|
10342
|
+
}
|
|
10343
|
+
}
|
|
10344
|
+
const readableBlocks = close ? blockNumbers.filter((block) => block < finalBlock) : blockNumbers;
|
|
10345
|
+
premium = [];
|
|
10346
|
+
for (let i = 0; i < readableBlocks.length; i += 16) {
|
|
10347
|
+
const history = await getStreamiaHistory({
|
|
10348
|
+
client,
|
|
10349
|
+
panopticPoolAddress: poolAddress,
|
|
10350
|
+
account,
|
|
10351
|
+
tokenId,
|
|
10352
|
+
blockNumbers: readableBlocks.slice(i, i + 16),
|
|
10353
|
+
legs: [],
|
|
10354
|
+
poolConfig,
|
|
10355
|
+
includeUniswapFees: false,
|
|
10356
|
+
settledEvents: settled,
|
|
10357
|
+
_meta: end
|
|
10358
|
+
});
|
|
10359
|
+
for (const snapshot of history.snapshots) {
|
|
10360
|
+
const timestamp = snapshot.blockNumber === void 0 ? void 0 : timestamps.get(snapshot.blockNumber);
|
|
10361
|
+
if (timestamp === void 0) throw new Error("Accounting timestamp missing");
|
|
10362
|
+
premium.push({
|
|
10363
|
+
timestamp,
|
|
10364
|
+
...snapshot.cumulativePanopticPremia
|
|
10365
|
+
});
|
|
10366
|
+
}
|
|
10367
|
+
}
|
|
10368
|
+
if (close) premium.push({
|
|
10369
|
+
timestamp: end.blockTimestamp,
|
|
10370
|
+
...settled.reduce((sum$1, event) => ({
|
|
10371
|
+
token0: sum$1.token0 + event.settled0,
|
|
10372
|
+
token1: sum$1.token1 + event.settled1
|
|
10373
|
+
}), {
|
|
10374
|
+
token0: 0n,
|
|
10375
|
+
token1: 0n
|
|
10376
|
+
})
|
|
10377
|
+
});
|
|
10378
|
+
const openingAccrual = premium[0];
|
|
10379
|
+
if (!openingAccrual || openingAccrual.timestamp !== start.blockTimestamp) throw new Error("Opening premium snapshot missing");
|
|
10380
|
+
premium = premium.map((snapshot) => ({
|
|
10381
|
+
timestamp: snapshot.timestamp,
|
|
10382
|
+
token0: snapshot.token0 - openingAccrual.token0,
|
|
10383
|
+
token1: snapshot.token1 - openingAccrual.token1
|
|
10384
|
+
}));
|
|
10385
|
+
} catch (error) {
|
|
10386
|
+
premium = null;
|
|
10387
|
+
premiumError = error instanceof Error ? error.message : "Premium history unavailable";
|
|
10388
|
+
}
|
|
10389
|
+
if (includeBaseFees) try {
|
|
10390
|
+
const feeBlocks = close ? blockNumbers.filter((block) => block < finalBlock) : blockNumbers;
|
|
10391
|
+
const data = [];
|
|
10392
|
+
for (let i = 0; i < feeBlocks.length; i += 16) data.push(...await fetchUniswapFeeData(client, feeBlocks.slice(i, i + 16), chunks, poolConfig));
|
|
10393
|
+
let total0 = 0n;
|
|
10394
|
+
let total1 = 0n;
|
|
10395
|
+
fees = [];
|
|
10396
|
+
for (let i = 0; i < data.length; i++) {
|
|
10397
|
+
const current = data[i];
|
|
10398
|
+
for (const chunk of chunks) {
|
|
10399
|
+
if ((current.tickData.get(chunk.lowerTick)?.liquidityGross ?? 0n) === 0n || (current.tickData.get(chunk.upperTick)?.liquidityGross ?? 0n) === 0n) throw new Error("LP range was uninitialized; complete base fee history is unavailable");
|
|
10400
|
+
if (i === 0) continue;
|
|
10401
|
+
const previous = feeGrowthInsideX128(data[i - 1], chunk.lowerTick, chunk.upperTick);
|
|
10402
|
+
const next = feeGrowthInsideX128(current, chunk.lowerTick, chunk.upperTick);
|
|
10403
|
+
if (!previous || !next) throw new Error("Range fee growth missing");
|
|
10404
|
+
if (BigInt.asUintN(256, next.feeGrowthInside0X128 - previous.feeGrowthInside0X128) > BigInt.asUintN(256, current.feeGrowthGlobal0 - data[i - 1].feeGrowthGlobal0) || BigInt.asUintN(256, next.feeGrowthInside1X128 - previous.feeGrowthInside1X128) > BigInt.asUintN(256, current.feeGrowthGlobal1 - data[i - 1].feeGrowthGlobal1)) throw new Error("Range fee growth is inconsistent with pool fee growth");
|
|
10405
|
+
const sign = chunk.isLong ? -1n : 1n;
|
|
10406
|
+
total0 += sign * feesFromFeeGrowthDelta(next.feeGrowthInside0X128, previous.feeGrowthInside0X128, chunk.liquidity);
|
|
10407
|
+
total1 += sign * feesFromFeeGrowthDelta(next.feeGrowthInside1X128, previous.feeGrowthInside1X128, chunk.liquidity);
|
|
10408
|
+
}
|
|
10409
|
+
fees.push({
|
|
10410
|
+
timestamp: metadata[i].blockTimestamp,
|
|
10411
|
+
token0: total0,
|
|
10412
|
+
token1: total1
|
|
10413
|
+
});
|
|
10414
|
+
}
|
|
10415
|
+
} catch (error) {
|
|
10416
|
+
fees = null;
|
|
10417
|
+
feeError = error instanceof Error ? error.message : "Base fee history unavailable";
|
|
10418
|
+
}
|
|
10419
|
+
return {
|
|
10420
|
+
start,
|
|
10421
|
+
end,
|
|
10422
|
+
positionSize: balance.positionSize,
|
|
10423
|
+
closed: close !== void 0,
|
|
10424
|
+
openingTick: BigInt(openingPrice.tick),
|
|
10425
|
+
endingTick: BigInt(endingPrice.tick),
|
|
10426
|
+
premium,
|
|
10427
|
+
fees,
|
|
10428
|
+
premiumError,
|
|
10429
|
+
feeError,
|
|
10430
|
+
accountingSamples: blockNumbers.length,
|
|
10431
|
+
maxAccountingIntervalSeconds: metadata.slice(1).reduce((maximum, meta, index) => {
|
|
10432
|
+
const interval = meta.blockTimestamp - metadata[index].blockTimestamp;
|
|
10433
|
+
return interval > maximum ? interval : maximum;
|
|
10434
|
+
}, 0n)
|
|
10435
|
+
};
|
|
10436
|
+
}
|
|
10437
|
+
|
|
10438
|
+
//#endregion
|
|
10439
|
+
export { DEFAULT_MIN_SWAP_RATIO_BPS, DEFAULT_RECONNECT_CONFIG, MARKET_INDICATOR_PERIODS, REQUIRED_BASE_ERROR_SENTINEL, STRIKE_LADDER_TARGET_STRIKES, VARIANCE_RATIO_LAG, addPendingPosition, addTrackedChunks, apportion, assertCanBurn, assertCanForceExercise, assertCanLiquidate, assertCanMint, assertFresh, assertHealthy, assertLpPositionFunded, assertTradeable, buildCreditWrappedDispatch, buildSfpmSwapCalldata, buildSfpmSwapPlan, buildTemporaryLoanRecoveryDispatch, buildTokenShortfallRecoveryDispatch, calculateAccountGreeksPure, calculateMarketIndicator, calculatePositionVolatilityMetrics, calculateSpreadWad, calculateVarianceProfile, checkCollateralAcrossTicks, classifyStrategyGroups, classifyStrike, cleanupStalePendingPositions, clearPendingPositions, clearTrackedChunks, clearTradeHistory, collateralCurveTicks, collateralRuleKindFor, computeV4PoolId, confirmPendingPosition, convertToAssets, convertToShares, createEventPoller, createEventSubscription, createFlowNeutralTokenId, decodePanopticTokenURI, emptyLiquidateParams, encodePoolKeyBytes, encodeV3PoolKeyBytes, ensureSfpmV3PoolInitialized, estimateBlockNumbers, estimateCollateralBreakdown, estimateCollateralRequired, failPendingPosition, feeGrowthInsideX128, feesFromFeeGrowthDelta, fetchSfpmV3PoolId, fetchUniswapFeeData, getAccountBuyingPower, getAccountGreeks, getAccountHistory, getChunkLiquidities, getChunkSpreads, getClosedPositions, getCollateralCurve, getCollateralCurveInputs, getCollateralRequiredBase, getCollateralSharePrices, getCollateralTotalAssetsBatch, getDeltaHedgeParams, getEnforcedTickLimits, getExecutableLpMaxSize, getFactoryConstructMetadata, getFactoryOwnerOf, getFactoryTokenURI, getGuardianUnlockState, getHedgeLimits, getItmAmounts, getMarginBuffer, getMaxPositionSize, getMaxRedeem, getMaxWithdrawable, getNativeTokenPrice, getNotEnoughTokensError, getOpenPositionPreview, getPanopticPoolAddress, getPanopticPoolFromPoolId, getPendingPositions, getPoolCollateralAddresses, getPoolLiquidities, getPortfolioValue, getPositionChunkData, getPositionEnrichmentData, getPositionValueCurve, getPositionVolatilityHistory, getPriceHistory, getRealizedPnL, getRequiredCreditForITM, getSafeMode, getStreamiaHistory, getSyncStatus, getTrackedChunks, getTradeHistory, getUniswapFeeHistory, getUniswapV3LpPositionState, getUniswapV3PoolFromId, getUniswapV3PoolInfo, getUniswapV3PoolLiquidities, getUniswapV4LpPositionState, getUniswapV4PoolBasicState, getUniswapV4PoolInfo, getUniswapV4PoolKeyFromId, getUniswapV4PoolLiquidities, interpolateBlocks, isGasError, isNonceError, isRetryableRpcError, isolateGroupTokenId, ladderStrikeSequence, logReturns, marketPnlInQuote, marketRiskFromValues, marketScenario, minePoolAddress, minePoolAddressLocalAsync, multicallRead, netLiquidationValueInQuote, optimizeTokenIdRiskPartners, parseCollateralLog, parsePoolLog, positionValueKey, positionValueTicks, prepareIndicatorCandles, preparePositionGamma, preparePositionValueCurve, previewDeposit, previewMint, previewRedeem, previewWithdraw, quoteLiquidation, quoteOneTokenFlow, quoteSfpmSwap, quoteTemporaryLoanRecovery, quoteTokenShortfallRecovery, realizedBeta, removeTrackedChunks, resolveBlockNumbers, resolveLadderStrike, resolvePanopticPoolFromPoolId, resolveUniswapV4PoolKey, saveClosedPosition, scaleCollateralRequired, scanChunks, screenAccountExact, simulateBatchDispatch, simulateClosePosition, simulateDeployNewPool, simulateDeposit, simulateForceExercise, simulateLiquidate, simulateSFPMBurn, simulateSFPMMint, simulateSwapExactIn, simulateSwapExactOut, simulateWithdraw, slippageBpsToTickDistance, valuePositionAccrual, watchEvents };
|