@panoptic-eng/sdk 1.0.63 → 1.0.65
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/README.md +1 -1
- package/dist/StateView-q4yMpBY6.js +323 -0
- package/dist/cow/index.d.ts +1 -1
- package/dist/cow/index.js +1 -1
- package/dist/cow/index.js.map +1 -1
- package/dist/{cow-BsSaoahK.js → cow-BU9YOHkh.js} +1 -1
- package/dist/index.d.ts +34 -34
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/{irm-BDlcUNgJ.js → irm-DGyKOQXz.js} +4 -326
- package/dist/panoptic/v2/index.d.ts +161 -137
- package/dist/panoptic/v2/index.d.ts.map +1 -1
- package/dist/panoptic/v2/index.js +61 -51
- package/dist/panoptic/v2/index.js.map +1 -1
- package/dist/panoptic/v2/react-public.d.ts +168 -144
- package/dist/panoptic/v2/react-public.d.ts.map +1 -1
- package/dist/panoptic/v2/react-public.js +64 -54
- package/dist/panoptic/v2/react-public.js.map +1 -1
- package/dist/{rates-BwZnK0tG.js → rates-Y0Pbv1_x.js} +4 -4
- package/dist/{router-pm9PDlyH.js → router-CheXbrfz.js} +2 -2
- package/dist/{router-Dejb6MWu.js → router-DxE-iwYF.js} +304 -6
- package/dist/{transactionFees-aXQlx-lq.js → transactionFees-BF033Mob.js} +1 -1
- package/dist/uniswap/index.d.ts +194 -4
- package/dist/uniswap/index.d.ts.map +1 -1
- package/dist/uniswap/index.js +1316 -789
- package/dist/uniswap/index.js.map +1 -1
- package/dist/{v2-ccGPXWvq.js → v2-QFSAShVj.js} +29 -316
- package/dist/{writes--fM_8ggw.js → writes-BA21eyGF.js} +38 -38
- package/dist/zodiac/index.d.ts +3 -3
- package/dist/zodiac/index.js +3 -3
- package/dist/zodiac/index.js.map +1 -1
- package/package.json +1 -1
- /package/dist/{chainDeployments-BhXMBZ4B.js → chainDeployments-Do4NaZA_.js} +0 -0
- /package/dist/{greeks-CNi1-cnp.js → greeks-CGtgLUyh.js} +0 -0
- /package/dist/{providers-1WdiaRbY.js → providers-DubVMHWU.js} +0 -0
package/dist/uniswap/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import Decimal from "decimal.js";
|
|
2
|
-
import { BaseError, ContractFunctionRevertedError, decodeAbiParameters, decodeErrorResult, decodeEventLog, encodeAbiParameters, encodeFunctionData, encodePacked, erc20Abi, getAbiItem, isAddressEqual, keccak256, maxUint256, parseAbi, toFunctionSelector, zeroAddress } from "viem";
|
|
2
|
+
import { BaseError, ContractFunctionRevertedError, ExecutionRevertedError, decodeAbiParameters, decodeErrorResult, decodeEventLog, encodeAbiParameters, encodeFunctionData, encodePacked, erc20Abi, getAbiItem, isAddressEqual, keccak256, maxUint256, parseAbi, toFunctionSelector, zeroAddress } from "viem";
|
|
3
|
+
import { GraphQLClient } from "graphql-request";
|
|
4
|
+
import { z } from "zod";
|
|
3
5
|
|
|
4
6
|
//#region src/uniswap/estimateLpFees.ts
|
|
5
7
|
const D$1 = Decimal.clone({ precision: 60 });
|
|
@@ -126,6 +128,28 @@ function estimateLpFees({ candles, ranges, feePips }) {
|
|
|
126
128
|
};
|
|
127
129
|
}
|
|
128
130
|
|
|
131
|
+
//#endregion
|
|
132
|
+
//#region src/panoptic/v2/clients/blockMeta.ts
|
|
133
|
+
/**
|
|
134
|
+
* Get block metadata for a given block.
|
|
135
|
+
* Used to tag all read results with same-block consistency proof.
|
|
136
|
+
*
|
|
137
|
+
* @param params - The parameters
|
|
138
|
+
* @returns Block metadata
|
|
139
|
+
*/
|
|
140
|
+
async function getBlockMeta(params) {
|
|
141
|
+
const { client, blockNumber } = params;
|
|
142
|
+
const block = await client.getBlock({
|
|
143
|
+
blockNumber,
|
|
144
|
+
includeTransactions: false
|
|
145
|
+
});
|
|
146
|
+
return {
|
|
147
|
+
blockNumber: block.number,
|
|
148
|
+
blockHash: block.hash,
|
|
149
|
+
blockTimestamp: block.timestamp
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
129
153
|
//#endregion
|
|
130
154
|
//#region src/panoptic/v2/utils/constants.ts
|
|
131
155
|
/**
|
|
@@ -158,7 +182,7 @@ const NO_UPPER_LIQUIDATION_TICK = (1n << 23n) - 1n;
|
|
|
158
182
|
|
|
159
183
|
//#endregion
|
|
160
184
|
//#region src/panoptic/v2/formatters/tick.ts
|
|
161
|
-
const Q192$
|
|
185
|
+
const Q192$6 = 1n << 192n;
|
|
162
186
|
const RAW_PRICE_PRECISION = 40n;
|
|
163
187
|
function pow10(exponent) {
|
|
164
188
|
if (exponent < 0n) throw new RangeError("Exponent must be non-negative");
|
|
@@ -216,7 +240,7 @@ const MAX_SQRT_PRICE_X96 = tickToSqrtPriceX96(MAX_TICK);
|
|
|
216
240
|
function getPriceRatioFromSqrtPriceX96(sqrtPriceX96) {
|
|
217
241
|
return {
|
|
218
242
|
numerator: sqrtPriceX96 * sqrtPriceX96,
|
|
219
|
-
denominator: Q192$
|
|
243
|
+
denominator: Q192$6
|
|
220
244
|
};
|
|
221
245
|
}
|
|
222
246
|
function getRawPriceRatio(tick) {
|
|
@@ -247,137 +271,872 @@ function tickToPrice(tick) {
|
|
|
247
271
|
}
|
|
248
272
|
|
|
249
273
|
//#endregion
|
|
250
|
-
//#region src/panoptic/v2/
|
|
251
|
-
/**
|
|
252
|
-
* Base error class for the Panoptic v2 SDK.
|
|
253
|
-
* @module v2/errors/base
|
|
254
|
-
*/
|
|
274
|
+
//#region src/panoptic/v2/abis/stateView.ts
|
|
255
275
|
/**
|
|
256
|
-
*
|
|
257
|
-
*
|
|
258
|
-
*
|
|
259
|
-
* @example
|
|
260
|
-
* ```typescript
|
|
261
|
-
* try {
|
|
262
|
-
* await openPosition(config, params)
|
|
263
|
-
* } catch (error) {
|
|
264
|
-
* if (error instanceof PanopticError) {
|
|
265
|
-
* console.log('Panoptic error:', error.name, error.message)
|
|
266
|
-
* console.log('Original cause:', error.cause)
|
|
267
|
-
* }
|
|
268
|
-
* }
|
|
269
|
-
* ```
|
|
276
|
+
* Minimal Uniswap V4 StateView ABI for fee growth reads.
|
|
277
|
+
* Only includes functions needed by streamia history.
|
|
270
278
|
*/
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
279
|
+
const stateViewAbi = [
|
|
280
|
+
{
|
|
281
|
+
inputs: [{
|
|
282
|
+
internalType: "PoolId",
|
|
283
|
+
name: "poolId",
|
|
284
|
+
type: "bytes32"
|
|
285
|
+
}],
|
|
286
|
+
name: "getSlot0",
|
|
287
|
+
outputs: [
|
|
288
|
+
{
|
|
289
|
+
internalType: "uint160",
|
|
290
|
+
name: "sqrtPriceX96",
|
|
291
|
+
type: "uint160"
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
internalType: "int24",
|
|
295
|
+
name: "tick",
|
|
296
|
+
type: "int24"
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
internalType: "uint24",
|
|
300
|
+
name: "protocolFee",
|
|
301
|
+
type: "uint24"
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
internalType: "uint24",
|
|
305
|
+
name: "lpFee",
|
|
306
|
+
type: "uint24"
|
|
307
|
+
}
|
|
308
|
+
],
|
|
309
|
+
stateMutability: "view",
|
|
310
|
+
type: "function"
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
inputs: [{
|
|
314
|
+
internalType: "PoolId",
|
|
315
|
+
name: "poolId",
|
|
316
|
+
type: "bytes32"
|
|
317
|
+
}],
|
|
318
|
+
name: "getFeeGrowthGlobals",
|
|
319
|
+
outputs: [{
|
|
320
|
+
internalType: "uint256",
|
|
321
|
+
name: "feeGrowthGlobal0",
|
|
322
|
+
type: "uint256"
|
|
323
|
+
}, {
|
|
324
|
+
internalType: "uint256",
|
|
325
|
+
name: "feeGrowthGlobal1",
|
|
326
|
+
type: "uint256"
|
|
327
|
+
}],
|
|
328
|
+
stateMutability: "view",
|
|
329
|
+
type: "function"
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
inputs: [{
|
|
333
|
+
internalType: "PoolId",
|
|
334
|
+
name: "poolId",
|
|
335
|
+
type: "bytes32"
|
|
336
|
+
}, {
|
|
337
|
+
internalType: "int24",
|
|
338
|
+
name: "tick",
|
|
339
|
+
type: "int24"
|
|
340
|
+
}],
|
|
341
|
+
name: "getTickInfo",
|
|
342
|
+
outputs: [
|
|
343
|
+
{
|
|
344
|
+
internalType: "uint128",
|
|
345
|
+
name: "liquidityGross",
|
|
346
|
+
type: "uint128"
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
internalType: "int128",
|
|
350
|
+
name: "liquidityNet",
|
|
351
|
+
type: "int128"
|
|
352
|
+
},
|
|
353
|
+
{
|
|
354
|
+
internalType: "uint256",
|
|
355
|
+
name: "feeGrowthOutside0X128",
|
|
356
|
+
type: "uint256"
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
internalType: "uint256",
|
|
360
|
+
name: "feeGrowthOutside1X128",
|
|
361
|
+
type: "uint256"
|
|
362
|
+
}
|
|
363
|
+
],
|
|
364
|
+
stateMutability: "view",
|
|
365
|
+
type: "function"
|
|
366
|
+
},
|
|
367
|
+
{
|
|
368
|
+
inputs: [{
|
|
369
|
+
internalType: "PoolId",
|
|
370
|
+
name: "poolId",
|
|
371
|
+
type: "bytes32"
|
|
372
|
+
}],
|
|
373
|
+
name: "getLiquidity",
|
|
374
|
+
outputs: [{
|
|
375
|
+
internalType: "uint128",
|
|
376
|
+
name: "liquidity",
|
|
377
|
+
type: "uint128"
|
|
378
|
+
}],
|
|
379
|
+
stateMutability: "view",
|
|
380
|
+
type: "function"
|
|
286
381
|
}
|
|
287
|
-
|
|
382
|
+
];
|
|
288
383
|
|
|
289
384
|
//#endregion
|
|
290
|
-
//#region src/panoptic/v2/
|
|
291
|
-
/**
|
|
292
|
-
* The account is not solvent enough to perform the desired action.
|
|
293
|
-
* @see Errors.sol:9
|
|
294
|
-
*/
|
|
295
|
-
var AccountInsolventError = class extends PanopticError {
|
|
296
|
-
name = "AccountInsolventError";
|
|
297
|
-
constructor(solvent, numberOfTicks, cause) {
|
|
298
|
-
super(`Account insolvent: solvent=${solvent}, numberOfTicks=${numberOfTicks}`, cause);
|
|
299
|
-
this.solvent = solvent;
|
|
300
|
-
this.numberOfTicks = numberOfTicks;
|
|
301
|
-
}
|
|
302
|
-
};
|
|
303
|
-
/**
|
|
304
|
-
* Position is still solvent and cannot be liquidated.
|
|
305
|
-
* @see Errors.sol:84
|
|
306
|
-
*/
|
|
307
|
-
var NotMarginCalledError = class extends PanopticError {
|
|
308
|
-
name = "NotMarginCalledError";
|
|
309
|
-
constructor(cause) {
|
|
310
|
-
super("Position is still solvent and cannot be liquidated", cause);
|
|
311
|
-
}
|
|
312
|
-
};
|
|
313
|
-
/**
|
|
314
|
-
* The user does not own enough assets to open/close a position.
|
|
315
|
-
* @see Errors.sol:78
|
|
316
|
-
*/
|
|
317
|
-
var NotEnoughTokensError = class extends PanopticError {
|
|
318
|
-
name = "NotEnoughTokensError";
|
|
319
|
-
constructor(tokenAddress, assetsRequested, assetBalance, cause) {
|
|
320
|
-
super(`Not enough tokens: requested=${assetsRequested}, balance=${assetBalance}, token=${tokenAddress}`, cause);
|
|
321
|
-
this.tokenAddress = tokenAddress;
|
|
322
|
-
this.assetsRequested = assetsRequested;
|
|
323
|
-
this.assetBalance = assetBalance;
|
|
324
|
-
}
|
|
325
|
-
};
|
|
326
|
-
/**
|
|
327
|
-
* There is not enough available liquidity in the chunk for a long leg.
|
|
328
|
-
* @see Errors.sol:75
|
|
329
|
-
*/
|
|
330
|
-
var NotEnoughLiquidityInChunkError = class extends PanopticError {
|
|
331
|
-
name = "NotEnoughLiquidityInChunkError";
|
|
332
|
-
constructor(cause) {
|
|
333
|
-
super("Not enough liquidity in chunk for long leg creation or short leg closure", cause);
|
|
334
|
-
}
|
|
335
|
-
};
|
|
336
|
-
/**
|
|
337
|
-
* There is not enough available liquidity to fulfill a credit.
|
|
338
|
-
* @see Errors.sol:47
|
|
339
|
-
*/
|
|
340
|
-
var InsufficientCreditLiquidityError = class extends PanopticError {
|
|
341
|
-
name = "InsufficientCreditLiquidityError";
|
|
342
|
-
constructor(cause) {
|
|
343
|
-
super("Insufficient credit liquidity available in the PanopticPool", cause);
|
|
344
|
-
}
|
|
345
|
-
};
|
|
346
|
-
/**
|
|
347
|
-
* The amount deposited is larger than the maximum permitted.
|
|
348
|
-
* @see Errors.sol:25
|
|
349
|
-
*/
|
|
350
|
-
var DepositTooLargeError = class extends PanopticError {
|
|
351
|
-
name = "DepositTooLargeError";
|
|
352
|
-
constructor(cause) {
|
|
353
|
-
super("Deposit amount exceeds maximum permitted", cause);
|
|
354
|
-
}
|
|
355
|
-
};
|
|
356
|
-
/**
|
|
357
|
-
* Attempted to withdraw/redeem less than a single asset.
|
|
358
|
-
* @see Errors.sol:16
|
|
359
|
-
*/
|
|
360
|
-
var BelowMinimumRedemptionError = class extends PanopticError {
|
|
361
|
-
name = "BelowMinimumRedemptionError";
|
|
362
|
-
constructor(cause) {
|
|
363
|
-
super("Redemption amount below minimum threshold", cause);
|
|
364
|
-
}
|
|
365
|
-
};
|
|
366
|
-
/**
|
|
367
|
-
* Attempted to withdraw/redeem more than available.
|
|
368
|
-
* @see Errors.sol:35
|
|
369
|
-
*/
|
|
370
|
-
var ExceedsMaximumRedemptionError = class extends PanopticError {
|
|
371
|
-
name = "ExceedsMaximumRedemptionError";
|
|
372
|
-
constructor(cause) {
|
|
373
|
-
super("Redemption exceeds maximum available (liquidity, shares, or open positions)", cause);
|
|
374
|
-
}
|
|
375
|
-
};
|
|
385
|
+
//#region src/panoptic/v2/abis/uniswapV3Pool.ts
|
|
376
386
|
/**
|
|
377
|
-
*
|
|
378
|
-
*
|
|
387
|
+
* Minimal Uniswap V3 Pool ABI for fee growth reads.
|
|
388
|
+
* Only includes functions needed by streamia history.
|
|
379
389
|
*/
|
|
380
|
-
|
|
390
|
+
const uniswapV3PoolAbi = [
|
|
391
|
+
{
|
|
392
|
+
inputs: [],
|
|
393
|
+
name: "slot0",
|
|
394
|
+
outputs: [
|
|
395
|
+
{
|
|
396
|
+
internalType: "uint160",
|
|
397
|
+
name: "sqrtPriceX96",
|
|
398
|
+
type: "uint160"
|
|
399
|
+
},
|
|
400
|
+
{
|
|
401
|
+
internalType: "int24",
|
|
402
|
+
name: "tick",
|
|
403
|
+
type: "int24"
|
|
404
|
+
},
|
|
405
|
+
{
|
|
406
|
+
internalType: "uint16",
|
|
407
|
+
name: "observationIndex",
|
|
408
|
+
type: "uint16"
|
|
409
|
+
},
|
|
410
|
+
{
|
|
411
|
+
internalType: "uint16",
|
|
412
|
+
name: "observationCardinality",
|
|
413
|
+
type: "uint16"
|
|
414
|
+
},
|
|
415
|
+
{
|
|
416
|
+
internalType: "uint16",
|
|
417
|
+
name: "observationCardinalityNext",
|
|
418
|
+
type: "uint16"
|
|
419
|
+
},
|
|
420
|
+
{
|
|
421
|
+
internalType: "uint8",
|
|
422
|
+
name: "feeProtocol",
|
|
423
|
+
type: "uint8"
|
|
424
|
+
},
|
|
425
|
+
{
|
|
426
|
+
internalType: "bool",
|
|
427
|
+
name: "unlocked",
|
|
428
|
+
type: "bool"
|
|
429
|
+
}
|
|
430
|
+
],
|
|
431
|
+
stateMutability: "view",
|
|
432
|
+
type: "function"
|
|
433
|
+
},
|
|
434
|
+
{
|
|
435
|
+
inputs: [],
|
|
436
|
+
name: "feeGrowthGlobal0X128",
|
|
437
|
+
outputs: [{
|
|
438
|
+
internalType: "uint256",
|
|
439
|
+
name: "",
|
|
440
|
+
type: "uint256"
|
|
441
|
+
}],
|
|
442
|
+
stateMutability: "view",
|
|
443
|
+
type: "function"
|
|
444
|
+
},
|
|
445
|
+
{
|
|
446
|
+
inputs: [],
|
|
447
|
+
name: "feeGrowthGlobal1X128",
|
|
448
|
+
outputs: [{
|
|
449
|
+
internalType: "uint256",
|
|
450
|
+
name: "",
|
|
451
|
+
type: "uint256"
|
|
452
|
+
}],
|
|
453
|
+
stateMutability: "view",
|
|
454
|
+
type: "function"
|
|
455
|
+
},
|
|
456
|
+
{
|
|
457
|
+
inputs: [{
|
|
458
|
+
internalType: "int24",
|
|
459
|
+
name: "",
|
|
460
|
+
type: "int24"
|
|
461
|
+
}],
|
|
462
|
+
name: "ticks",
|
|
463
|
+
outputs: [
|
|
464
|
+
{
|
|
465
|
+
internalType: "uint128",
|
|
466
|
+
name: "liquidityGross",
|
|
467
|
+
type: "uint128"
|
|
468
|
+
},
|
|
469
|
+
{
|
|
470
|
+
internalType: "int128",
|
|
471
|
+
name: "liquidityNet",
|
|
472
|
+
type: "int128"
|
|
473
|
+
},
|
|
474
|
+
{
|
|
475
|
+
internalType: "uint256",
|
|
476
|
+
name: "feeGrowthOutside0X128",
|
|
477
|
+
type: "uint256"
|
|
478
|
+
},
|
|
479
|
+
{
|
|
480
|
+
internalType: "uint256",
|
|
481
|
+
name: "feeGrowthOutside1X128",
|
|
482
|
+
type: "uint256"
|
|
483
|
+
},
|
|
484
|
+
{
|
|
485
|
+
internalType: "int56",
|
|
486
|
+
name: "tickCumulativeOutside",
|
|
487
|
+
type: "int56"
|
|
488
|
+
},
|
|
489
|
+
{
|
|
490
|
+
internalType: "uint160",
|
|
491
|
+
name: "secondsPerLiquidityOutsideX128",
|
|
492
|
+
type: "uint160"
|
|
493
|
+
},
|
|
494
|
+
{
|
|
495
|
+
internalType: "uint32",
|
|
496
|
+
name: "secondsOutside",
|
|
497
|
+
type: "uint32"
|
|
498
|
+
},
|
|
499
|
+
{
|
|
500
|
+
internalType: "bool",
|
|
501
|
+
name: "initialized",
|
|
502
|
+
type: "bool"
|
|
503
|
+
}
|
|
504
|
+
],
|
|
505
|
+
stateMutability: "view",
|
|
506
|
+
type: "function"
|
|
507
|
+
},
|
|
508
|
+
{
|
|
509
|
+
inputs: [],
|
|
510
|
+
name: "liquidity",
|
|
511
|
+
outputs: [{
|
|
512
|
+
internalType: "uint128",
|
|
513
|
+
name: "",
|
|
514
|
+
type: "uint128"
|
|
515
|
+
}],
|
|
516
|
+
stateMutability: "view",
|
|
517
|
+
type: "function"
|
|
518
|
+
},
|
|
519
|
+
{
|
|
520
|
+
inputs: [],
|
|
521
|
+
name: "fee",
|
|
522
|
+
outputs: [{
|
|
523
|
+
internalType: "uint24",
|
|
524
|
+
name: "",
|
|
525
|
+
type: "uint24"
|
|
526
|
+
}],
|
|
527
|
+
stateMutability: "view",
|
|
528
|
+
type: "function"
|
|
529
|
+
},
|
|
530
|
+
{
|
|
531
|
+
inputs: [],
|
|
532
|
+
name: "token0",
|
|
533
|
+
outputs: [{
|
|
534
|
+
internalType: "address",
|
|
535
|
+
name: "",
|
|
536
|
+
type: "address"
|
|
537
|
+
}],
|
|
538
|
+
stateMutability: "view",
|
|
539
|
+
type: "function"
|
|
540
|
+
},
|
|
541
|
+
{
|
|
542
|
+
inputs: [],
|
|
543
|
+
name: "token1",
|
|
544
|
+
outputs: [{
|
|
545
|
+
internalType: "address",
|
|
546
|
+
name: "",
|
|
547
|
+
type: "address"
|
|
548
|
+
}],
|
|
549
|
+
stateMutability: "view",
|
|
550
|
+
type: "function"
|
|
551
|
+
},
|
|
552
|
+
{
|
|
553
|
+
inputs: [],
|
|
554
|
+
name: "tickSpacing",
|
|
555
|
+
outputs: [{
|
|
556
|
+
internalType: "int24",
|
|
557
|
+
name: "",
|
|
558
|
+
type: "int24"
|
|
559
|
+
}],
|
|
560
|
+
stateMutability: "view",
|
|
561
|
+
type: "function"
|
|
562
|
+
}
|
|
563
|
+
];
|
|
564
|
+
|
|
565
|
+
//#endregion
|
|
566
|
+
//#region src/panoptic/v2/reads/priceHistory.ts
|
|
567
|
+
/**
|
|
568
|
+
* Get historical price data (tick + sqrtPriceX96) for a pool across multiple blocks.
|
|
569
|
+
*
|
|
570
|
+
* @param params - The parameters
|
|
571
|
+
* @returns Price snapshots at each block
|
|
572
|
+
*
|
|
573
|
+
* @example
|
|
574
|
+
* ```typescript
|
|
575
|
+
* const { snapshots } = await getPriceHistory({
|
|
576
|
+
* client,
|
|
577
|
+
* blockNumbers: [18000000n, 18000100n, 18000200n],
|
|
578
|
+
* poolConfig: { version: 'v3', poolAddress: '0x...' },
|
|
579
|
+
* })
|
|
580
|
+
*
|
|
581
|
+
* for (const snap of snapshots) {
|
|
582
|
+
* console.log(`Block ${snap.blockNumber}: tick=${snap.tick}`)
|
|
583
|
+
* }
|
|
584
|
+
* ```
|
|
585
|
+
*/
|
|
586
|
+
async function getPriceHistory(params) {
|
|
587
|
+
const { client, blockNumbers, poolConfig } = params;
|
|
588
|
+
if (blockNumbers.length === 0) {
|
|
589
|
+
const _meta$1 = params._meta ?? await getBlockMeta({ client });
|
|
590
|
+
return {
|
|
591
|
+
snapshots: [],
|
|
592
|
+
_meta: _meta$1
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
const slot0Requests = blockNumbers.map((bn) => fetchSlot0(client, bn, poolConfig));
|
|
596
|
+
const [slot0Results, _meta] = await Promise.all([Promise.all(slot0Requests), params._meta ? Promise.resolve(params._meta) : getBlockMeta({ client })]);
|
|
597
|
+
const snapshots = slot0Results.map((result, i) => ({
|
|
598
|
+
blockNumber: blockNumbers[i],
|
|
599
|
+
tick: result.tick,
|
|
600
|
+
sqrtPriceX96: result.sqrtPriceX96
|
|
601
|
+
}));
|
|
602
|
+
return {
|
|
603
|
+
snapshots,
|
|
604
|
+
_meta
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
async function fetchSlot0(client, blockNumber, poolConfig) {
|
|
608
|
+
if (poolConfig.version === "v3") {
|
|
609
|
+
const result = await client.readContract({
|
|
610
|
+
address: poolConfig.poolAddress,
|
|
611
|
+
abi: uniswapV3PoolAbi,
|
|
612
|
+
functionName: "slot0",
|
|
613
|
+
blockNumber
|
|
614
|
+
});
|
|
615
|
+
return {
|
|
616
|
+
sqrtPriceX96: result[0],
|
|
617
|
+
tick: result[1]
|
|
618
|
+
};
|
|
619
|
+
} else {
|
|
620
|
+
const result = await client.readContract({
|
|
621
|
+
address: poolConfig.stateViewAddress,
|
|
622
|
+
abi: stateViewAbi,
|
|
623
|
+
functionName: "getSlot0",
|
|
624
|
+
args: [poolConfig.poolId],
|
|
625
|
+
blockNumber
|
|
626
|
+
});
|
|
627
|
+
return {
|
|
628
|
+
sqrtPriceX96: result[0],
|
|
629
|
+
tick: result[1]
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
//#endregion
|
|
635
|
+
//#region src/panoptic/v2/reads/uniswapLpPosition.ts
|
|
636
|
+
const nfpmAbi = [{
|
|
637
|
+
type: "function",
|
|
638
|
+
name: "positions",
|
|
639
|
+
inputs: [{
|
|
640
|
+
name: "tokenId",
|
|
641
|
+
type: "uint256"
|
|
642
|
+
}],
|
|
643
|
+
outputs: [
|
|
644
|
+
{
|
|
645
|
+
name: "nonce",
|
|
646
|
+
type: "uint96"
|
|
647
|
+
},
|
|
648
|
+
{
|
|
649
|
+
name: "operator",
|
|
650
|
+
type: "address"
|
|
651
|
+
},
|
|
652
|
+
{
|
|
653
|
+
name: "token0",
|
|
654
|
+
type: "address"
|
|
655
|
+
},
|
|
656
|
+
{
|
|
657
|
+
name: "token1",
|
|
658
|
+
type: "address"
|
|
659
|
+
},
|
|
660
|
+
{
|
|
661
|
+
name: "fee",
|
|
662
|
+
type: "uint24"
|
|
663
|
+
},
|
|
664
|
+
{
|
|
665
|
+
name: "tickLower",
|
|
666
|
+
type: "int24"
|
|
667
|
+
},
|
|
668
|
+
{
|
|
669
|
+
name: "tickUpper",
|
|
670
|
+
type: "int24"
|
|
671
|
+
},
|
|
672
|
+
{
|
|
673
|
+
name: "liquidity",
|
|
674
|
+
type: "uint128"
|
|
675
|
+
},
|
|
676
|
+
{
|
|
677
|
+
name: "feeGrowthInside0LastX128",
|
|
678
|
+
type: "uint256"
|
|
679
|
+
},
|
|
680
|
+
{
|
|
681
|
+
name: "feeGrowthInside1LastX128",
|
|
682
|
+
type: "uint256"
|
|
683
|
+
},
|
|
684
|
+
{
|
|
685
|
+
name: "tokensOwed0",
|
|
686
|
+
type: "uint128"
|
|
687
|
+
},
|
|
688
|
+
{
|
|
689
|
+
name: "tokensOwed1",
|
|
690
|
+
type: "uint128"
|
|
691
|
+
}
|
|
692
|
+
],
|
|
693
|
+
stateMutability: "view"
|
|
694
|
+
}, {
|
|
695
|
+
type: "function",
|
|
696
|
+
name: "collect",
|
|
697
|
+
inputs: [{
|
|
698
|
+
name: "params",
|
|
699
|
+
type: "tuple",
|
|
700
|
+
components: [
|
|
701
|
+
{
|
|
702
|
+
name: "tokenId",
|
|
703
|
+
type: "uint256"
|
|
704
|
+
},
|
|
705
|
+
{
|
|
706
|
+
name: "recipient",
|
|
707
|
+
type: "address"
|
|
708
|
+
},
|
|
709
|
+
{
|
|
710
|
+
name: "amount0Max",
|
|
711
|
+
type: "uint128"
|
|
712
|
+
},
|
|
713
|
+
{
|
|
714
|
+
name: "amount1Max",
|
|
715
|
+
type: "uint128"
|
|
716
|
+
}
|
|
717
|
+
]
|
|
718
|
+
}],
|
|
719
|
+
outputs: [{
|
|
720
|
+
name: "amount0",
|
|
721
|
+
type: "uint256"
|
|
722
|
+
}, {
|
|
723
|
+
name: "amount1",
|
|
724
|
+
type: "uint256"
|
|
725
|
+
}],
|
|
726
|
+
stateMutability: "payable"
|
|
727
|
+
}];
|
|
728
|
+
const MAX_UINT128$3 = 2n ** 128n - 1n;
|
|
729
|
+
const MAX_UINT256$1 = 2n ** 256n - 1n;
|
|
730
|
+
const Q128 = 2n ** 128n;
|
|
731
|
+
/**
|
|
732
|
+
* Whether an error thrown by a viem contract call is an on-chain revert (as
|
|
733
|
+
* opposed to a transport/RPC failure such as a timeout or rate limit).
|
|
734
|
+
*/
|
|
735
|
+
function isContractRevert(error) {
|
|
736
|
+
return error instanceof BaseError && error.walk((err) => err instanceof ContractFunctionRevertedError || err instanceof ExecutionRevertedError) != null;
|
|
737
|
+
}
|
|
738
|
+
/**
|
|
739
|
+
* Fetch a Uniswap V3 LP position's state and uncollected fees.
|
|
740
|
+
*
|
|
741
|
+
* Fees come from simulating `collect` with max amounts as the owner — one
|
|
742
|
+
* eth_call returning the exact claimable amounts (tokensOwed + fee growth
|
|
743
|
+
* since the last poke). The simulation is best-effort: if it reverts (e.g.
|
|
744
|
+
* an empty position), fees fall back to 0.
|
|
745
|
+
*/
|
|
746
|
+
async function getUniswapV3LpPositionState(params) {
|
|
747
|
+
const { client, nfpmAddress, tokenId, owner, blockNumber } = params;
|
|
748
|
+
const _meta = await getBlockMeta({
|
|
749
|
+
client,
|
|
750
|
+
blockNumber
|
|
751
|
+
});
|
|
752
|
+
const position = await client.readContract({
|
|
753
|
+
address: nfpmAddress,
|
|
754
|
+
abi: nfpmAbi,
|
|
755
|
+
functionName: "positions",
|
|
756
|
+
args: [tokenId],
|
|
757
|
+
blockNumber: _meta.blockNumber
|
|
758
|
+
});
|
|
759
|
+
const [, , token0, token1, fee, tickLower, tickUpper, liquidity] = position;
|
|
760
|
+
let fees0 = 0n;
|
|
761
|
+
let fees1 = 0n;
|
|
762
|
+
try {
|
|
763
|
+
const { result } = await client.simulateContract({
|
|
764
|
+
address: nfpmAddress,
|
|
765
|
+
abi: nfpmAbi,
|
|
766
|
+
functionName: "collect",
|
|
767
|
+
args: [{
|
|
768
|
+
tokenId,
|
|
769
|
+
recipient: owner,
|
|
770
|
+
amount0Max: MAX_UINT128$3,
|
|
771
|
+
amount1Max: MAX_UINT128$3
|
|
772
|
+
}],
|
|
773
|
+
account: owner,
|
|
774
|
+
blockNumber: _meta.blockNumber
|
|
775
|
+
});
|
|
776
|
+
[fees0, fees1] = result;
|
|
777
|
+
} catch (error) {
|
|
778
|
+
if (!isContractRevert(error)) throw error;
|
|
779
|
+
}
|
|
780
|
+
return {
|
|
781
|
+
token0,
|
|
782
|
+
token1,
|
|
783
|
+
fee: Number(fee),
|
|
784
|
+
tickLower: Number(tickLower),
|
|
785
|
+
tickUpper: Number(tickUpper),
|
|
786
|
+
liquidity,
|
|
787
|
+
fees0,
|
|
788
|
+
fees1,
|
|
789
|
+
_meta
|
|
790
|
+
};
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
//#endregion
|
|
794
|
+
//#region src/uniswap/lpGreeks.ts
|
|
795
|
+
const Q96$4 = 1n << 96n;
|
|
796
|
+
const Q192$5 = 1n << 192n;
|
|
797
|
+
/**
|
|
798
|
+
* Compute the token0/token1 amounts backing `liquidity` over `[sqrtA, sqrtB]`
|
|
799
|
+
* at the current price `sqrtP`, mirroring Uniswap's `LiquidityAmounts`
|
|
800
|
+
* (`getAmountsForLiquidity`). All sqrt prices are X96. The current price is
|
|
801
|
+
* clamped into the range, so out-of-range positions collapse to a single token.
|
|
802
|
+
*/
|
|
803
|
+
function getAmountsForLiquidity(sqrtPriceX96, sqrtRatioAX96, sqrtRatioBX96, liquidity) {
|
|
804
|
+
const [sqrtA, sqrtB] = sqrtRatioAX96 <= sqrtRatioBX96 ? [sqrtRatioAX96, sqrtRatioBX96] : [sqrtRatioBX96, sqrtRatioAX96];
|
|
805
|
+
const sqrtC = sqrtPriceX96 < sqrtA ? sqrtA : sqrtPriceX96 > sqrtB ? sqrtB : sqrtPriceX96;
|
|
806
|
+
const amount0 = sqrtC < sqrtB ? liquidity * (sqrtB - sqrtC) * Q96$4 / (sqrtC * sqrtB) : 0n;
|
|
807
|
+
const amount1 = sqrtC > sqrtA ? liquidity * (sqrtC - sqrtA) / Q96$4 : 0n;
|
|
808
|
+
return {
|
|
809
|
+
amount0,
|
|
810
|
+
amount1
|
|
811
|
+
};
|
|
812
|
+
}
|
|
813
|
+
/**
|
|
814
|
+
* Greeks (value, delta, gamma) for a Uniswap LP position in the chosen asset
|
|
815
|
+
* frame. See the module docs for the unit conventions.
|
|
816
|
+
*
|
|
817
|
+
* Closed forms (in-range, numeraire = token1 / asset = token0):
|
|
818
|
+
* value = amount1 + amount0·P (P = sqrtP² / 2^192)
|
|
819
|
+
* delta = amount0 (= dV/dP)
|
|
820
|
+
* gamma = P²·d²V/dP² = -L·sqrt(P)/2 (short gamma)
|
|
821
|
+
* The `assetIndex = 1` frame is the symmetric inverse (numeraire = token0).
|
|
822
|
+
* Gamma is zero when the price is outside the range (no curvature there).
|
|
823
|
+
*/
|
|
824
|
+
function getLpGreeks(input) {
|
|
825
|
+
const { liquidity, tickLower, tickUpper, currentTick, assetIndex } = input;
|
|
826
|
+
const sqrtP = input.sqrtPriceX96 ?? tickToSqrtPriceX96(currentTick);
|
|
827
|
+
if (sqrtP <= 0n) throw new RangeError("Invalid sqrt price");
|
|
828
|
+
const sqrtA = tickToSqrtPriceX96(tickLower);
|
|
829
|
+
const sqrtB = tickToSqrtPriceX96(tickUpper);
|
|
830
|
+
const { amount0, amount1 } = getAmountsForLiquidity(sqrtP, sqrtA, sqrtB, liquidity);
|
|
831
|
+
const sqrtP2 = sqrtP * sqrtP;
|
|
832
|
+
const inRange = sqrtP > sqrtA && sqrtP < sqrtB;
|
|
833
|
+
if (assetIndex === 0) {
|
|
834
|
+
const value$1 = amount1 + amount0 * sqrtP2 / Q192$5;
|
|
835
|
+
const delta$1 = amount0;
|
|
836
|
+
const gamma$1 = inRange ? -(liquidity * sqrtP) / (2n * Q96$4) : 0n;
|
|
837
|
+
return {
|
|
838
|
+
value: value$1,
|
|
839
|
+
delta: delta$1,
|
|
840
|
+
gamma: gamma$1
|
|
841
|
+
};
|
|
842
|
+
}
|
|
843
|
+
const value = amount0 + amount1 * Q192$5 / sqrtP2;
|
|
844
|
+
const delta = amount1;
|
|
845
|
+
const gamma = inRange ? -(liquidity * Q96$4) / (2n * sqrtP) : 0n;
|
|
846
|
+
return {
|
|
847
|
+
value,
|
|
848
|
+
delta,
|
|
849
|
+
gamma
|
|
850
|
+
};
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
//#endregion
|
|
854
|
+
//#region src/uniswap/lpAccounting.ts
|
|
855
|
+
const Precision$2 = Decimal.clone({ precision: 80 });
|
|
856
|
+
const Q192$4 = (1n << 192n).toString();
|
|
857
|
+
function reconcileV3LpFees(events, claimable, endBlock) {
|
|
858
|
+
let collected0 = 0n;
|
|
859
|
+
let collected1 = 0n;
|
|
860
|
+
let removed0 = 0n;
|
|
861
|
+
let removed1 = 0n;
|
|
862
|
+
for (const event of events) {
|
|
863
|
+
if (event.amountSource !== "ExactEvent" || event.blockNumber > endBlock) throw new Error("Complete exact v3 history is required");
|
|
864
|
+
if (event.eventType === "Collect") {
|
|
865
|
+
collected0 += event.amount0;
|
|
866
|
+
collected1 += event.amount1;
|
|
867
|
+
} else if (event.eventType === "Burn") {
|
|
868
|
+
removed0 += event.amount0;
|
|
869
|
+
removed1 += event.amount1;
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
const fees0 = collected0 + claimable.amount0 - removed0;
|
|
873
|
+
const fees1 = collected1 + claimable.amount1 - removed1;
|
|
874
|
+
if (fees0 < 0n || fees1 < 0n) throw new Error("LP fee accounting does not reconcile");
|
|
875
|
+
return {
|
|
876
|
+
fees0,
|
|
877
|
+
fees1
|
|
878
|
+
};
|
|
879
|
+
}
|
|
880
|
+
/** V3 NFT cash-flow returns, including withdrawn principal still awaiting collection. */
|
|
881
|
+
function calculateV3LpAccounting({ events, inventory, claimable, prices, endBlock, token0Decimals, token1Decimals }) {
|
|
882
|
+
for (const decimals of [token0Decimals, token1Decimals]) if (!Number.isInteger(decimals) || decimals < 0 || decimals > 255) throw new RangeError("Invalid token decimals");
|
|
883
|
+
const priceByBlock = new Map(prices.map((point) => {
|
|
884
|
+
if (point.sqrtPriceX96 <= 0n) throw new RangeError("Invalid historical price");
|
|
885
|
+
return [point.blockNumber, new Precision$2(point.sqrtPriceX96.toString()).pow(2).div(Q192$4)];
|
|
886
|
+
}));
|
|
887
|
+
const priceAt = (block) => {
|
|
888
|
+
const price = priceByBlock.get(block);
|
|
889
|
+
if (!price) throw new Error("Historical price unavailable");
|
|
890
|
+
return price;
|
|
891
|
+
};
|
|
892
|
+
const { fees0, fees1 } = reconcileV3LpFees(events, claimable, endBlock);
|
|
893
|
+
const frame = (quoteIsToken0, decimals) => {
|
|
894
|
+
const scale = new Precision$2(10).pow(decimals);
|
|
895
|
+
const value = (amount0, amount1, block) => {
|
|
896
|
+
const price = priceAt(block);
|
|
897
|
+
return (quoteIsToken0 ? new Precision$2(amount0.toString()).plus(new Precision$2(amount1.toString()).div(price)) : new Precision$2(amount1.toString()).plus(new Precision$2(amount0.toString()).mul(price))).div(scale);
|
|
898
|
+
};
|
|
899
|
+
let deposited = new Precision$2(0);
|
|
900
|
+
let collected = new Precision$2(0);
|
|
901
|
+
for (const event of events) {
|
|
902
|
+
if (event.eventType === "Mint") deposited = deposited.plus(value(event.amount0, event.amount1, event.blockNumber));
|
|
903
|
+
if (event.eventType === "Collect") collected = collected.plus(value(event.amount0, event.amount1, event.blockNumber));
|
|
904
|
+
}
|
|
905
|
+
const endingValue = value(inventory.amount0 + claimable.amount0, inventory.amount1 + claimable.amount1, endBlock);
|
|
906
|
+
const pnl = endingValue.plus(collected).minus(deposited);
|
|
907
|
+
return {
|
|
908
|
+
pnl: pnl.toString(),
|
|
909
|
+
deposited: deposited.toString(),
|
|
910
|
+
pnlPercent: deposited.gt(0) ? pnl.div(deposited).mul(100).toString() : null
|
|
911
|
+
};
|
|
912
|
+
};
|
|
913
|
+
return {
|
|
914
|
+
fees0,
|
|
915
|
+
fees1,
|
|
916
|
+
claimable,
|
|
917
|
+
quote0: frame(true, token0Decimals),
|
|
918
|
+
quote1: frame(false, token1Decimals)
|
|
919
|
+
};
|
|
920
|
+
}
|
|
921
|
+
/** Reconcile v3 activity and historical prices at the subgraph's indexed block. */
|
|
922
|
+
async function getV3LpHistoryAccounting({ client, history, nfpmAddress, poolAddress, tickLower, tickUpper, token0Decimals, token1Decimals }) {
|
|
923
|
+
if (history.version !== 3) throw new Error("V4 fee collections are unavailable");
|
|
924
|
+
const tokenId = BigInt(history.positionId.slice(3));
|
|
925
|
+
const _meta = await getBlockMeta({
|
|
926
|
+
client,
|
|
927
|
+
blockNumber: history.blockNumber
|
|
928
|
+
});
|
|
929
|
+
if (_meta.blockHash.toLowerCase() !== history.blockHash.toLowerCase()) throw new Error("LP history checkpoint changed");
|
|
930
|
+
let liquidity = 0n;
|
|
931
|
+
for (const event of history.events) if (event.eventType !== "Collect") liquidity += event.eventType === "Mint" ? event.liquidity : -event.liquidity;
|
|
932
|
+
let claimable = {
|
|
933
|
+
amount0: 0n,
|
|
934
|
+
amount1: 0n
|
|
935
|
+
};
|
|
936
|
+
let owner;
|
|
937
|
+
try {
|
|
938
|
+
owner = await client.readContract({
|
|
939
|
+
address: nfpmAddress,
|
|
940
|
+
abi: parseAbi(["function ownerOf(uint256 tokenId) view returns (address)"]),
|
|
941
|
+
functionName: "ownerOf",
|
|
942
|
+
args: [tokenId],
|
|
943
|
+
blockNumber: history.blockNumber
|
|
944
|
+
});
|
|
945
|
+
} catch (error) {
|
|
946
|
+
const reverted = error instanceof BaseError && error.walk((cause) => cause instanceof ContractFunctionRevertedError || cause instanceof ExecutionRevertedError);
|
|
947
|
+
if (liquidity !== 0n || !(reverted instanceof ContractFunctionRevertedError || reverted instanceof ExecutionRevertedError)) throw error;
|
|
948
|
+
}
|
|
949
|
+
if (owner) {
|
|
950
|
+
const state = await getUniswapV3LpPositionState({
|
|
951
|
+
client,
|
|
952
|
+
nfpmAddress,
|
|
953
|
+
tokenId,
|
|
954
|
+
owner,
|
|
955
|
+
blockNumber: history.blockNumber
|
|
956
|
+
});
|
|
957
|
+
if (state.liquidity !== liquidity) throw new Error("LP liquidity history does not reconcile");
|
|
958
|
+
claimable = {
|
|
959
|
+
amount0: state.fees0,
|
|
960
|
+
amount1: state.fees1
|
|
961
|
+
};
|
|
962
|
+
}
|
|
963
|
+
const blocks = [...new Set([history.blockNumber, ...history.events.filter((event) => event.eventType !== "Burn").map((event) => event.blockNumber)])];
|
|
964
|
+
const feeTotals = reconcileV3LpFees(history.events, claimable, history.blockNumber);
|
|
965
|
+
const prices = [];
|
|
966
|
+
for (let index = 0; index < blocks.length; index += 16) {
|
|
967
|
+
const batch = blocks.slice(index, index + 16);
|
|
968
|
+
const result = await getPriceHistory({
|
|
969
|
+
client,
|
|
970
|
+
blockNumbers: batch,
|
|
971
|
+
poolConfig: {
|
|
972
|
+
version: "v3",
|
|
973
|
+
poolAddress
|
|
974
|
+
},
|
|
975
|
+
_meta
|
|
976
|
+
}).catch(() => null);
|
|
977
|
+
if (!result) return {
|
|
978
|
+
...feeTotals,
|
|
979
|
+
claimable,
|
|
980
|
+
quote0: null,
|
|
981
|
+
quote1: null,
|
|
982
|
+
blockNumber: history.blockNumber,
|
|
983
|
+
timestamp: _meta.blockTimestamp
|
|
984
|
+
};
|
|
985
|
+
result.snapshots.forEach((point, offset) => prices.push({
|
|
986
|
+
blockNumber: batch[offset],
|
|
987
|
+
sqrtPriceX96: point.sqrtPriceX96
|
|
988
|
+
}));
|
|
989
|
+
}
|
|
990
|
+
const endingPrice = prices.find((price) => price.blockNumber === history.blockNumber);
|
|
991
|
+
if (!endingPrice) throw new Error("Ending price unavailable");
|
|
992
|
+
const inventory = getAmountsForLiquidity(endingPrice.sqrtPriceX96, tickToSqrtPriceX96(tickLower), tickToSqrtPriceX96(tickUpper), liquidity);
|
|
993
|
+
return {
|
|
994
|
+
...calculateV3LpAccounting({
|
|
995
|
+
events: history.events,
|
|
996
|
+
inventory,
|
|
997
|
+
claimable,
|
|
998
|
+
prices,
|
|
999
|
+
endBlock: history.blockNumber,
|
|
1000
|
+
token0Decimals,
|
|
1001
|
+
token1Decimals
|
|
1002
|
+
}),
|
|
1003
|
+
blockNumber: history.blockNumber,
|
|
1004
|
+
timestamp: _meta.blockTimestamp
|
|
1005
|
+
};
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
//#endregion
|
|
1009
|
+
//#region src/panoptic/v2/errors/base.ts
|
|
1010
|
+
/**
|
|
1011
|
+
* Base error class for the Panoptic v2 SDK.
|
|
1012
|
+
* @module v2/errors/base
|
|
1013
|
+
*/
|
|
1014
|
+
/**
|
|
1015
|
+
* Base error class for all Panoptic SDK errors.
|
|
1016
|
+
* All errors thrown by the SDK extend this class.
|
|
1017
|
+
*
|
|
1018
|
+
* @example
|
|
1019
|
+
* ```typescript
|
|
1020
|
+
* try {
|
|
1021
|
+
* await openPosition(config, params)
|
|
1022
|
+
* } catch (error) {
|
|
1023
|
+
* if (error instanceof PanopticError) {
|
|
1024
|
+
* console.log('Panoptic error:', error.name, error.message)
|
|
1025
|
+
* console.log('Original cause:', error.cause)
|
|
1026
|
+
* }
|
|
1027
|
+
* }
|
|
1028
|
+
* ```
|
|
1029
|
+
*/
|
|
1030
|
+
var PanopticError = class extends Error {
|
|
1031
|
+
name = "PanopticError";
|
|
1032
|
+
/** The Solidity error name (e.g. 'PriceBoundFail', 'InputListFail'). Set by the parser. */
|
|
1033
|
+
errorName;
|
|
1034
|
+
/**
|
|
1035
|
+
* Creates a new PanopticError.
|
|
1036
|
+
*
|
|
1037
|
+
* @param message - Human-readable error message
|
|
1038
|
+
* @param cause - Optional underlying error that caused this error
|
|
1039
|
+
*/
|
|
1040
|
+
constructor(message, cause) {
|
|
1041
|
+
super(message);
|
|
1042
|
+
this.cause = cause;
|
|
1043
|
+
if (Error.captureStackTrace) Error.captureStackTrace(this, this.constructor);
|
|
1044
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
1045
|
+
}
|
|
1046
|
+
};
|
|
1047
|
+
|
|
1048
|
+
//#endregion
|
|
1049
|
+
//#region src/panoptic/v2/errors/contract.ts
|
|
1050
|
+
/**
|
|
1051
|
+
* The account is not solvent enough to perform the desired action.
|
|
1052
|
+
* @see Errors.sol:9
|
|
1053
|
+
*/
|
|
1054
|
+
var AccountInsolventError = class extends PanopticError {
|
|
1055
|
+
name = "AccountInsolventError";
|
|
1056
|
+
constructor(solvent, numberOfTicks, cause) {
|
|
1057
|
+
super(`Account insolvent: solvent=${solvent}, numberOfTicks=${numberOfTicks}`, cause);
|
|
1058
|
+
this.solvent = solvent;
|
|
1059
|
+
this.numberOfTicks = numberOfTicks;
|
|
1060
|
+
}
|
|
1061
|
+
};
|
|
1062
|
+
/**
|
|
1063
|
+
* Position is still solvent and cannot be liquidated.
|
|
1064
|
+
* @see Errors.sol:84
|
|
1065
|
+
*/
|
|
1066
|
+
var NotMarginCalledError = class extends PanopticError {
|
|
1067
|
+
name = "NotMarginCalledError";
|
|
1068
|
+
constructor(cause) {
|
|
1069
|
+
super("Position is still solvent and cannot be liquidated", cause);
|
|
1070
|
+
}
|
|
1071
|
+
};
|
|
1072
|
+
/**
|
|
1073
|
+
* The user does not own enough assets to open/close a position.
|
|
1074
|
+
* @see Errors.sol:78
|
|
1075
|
+
*/
|
|
1076
|
+
var NotEnoughTokensError = class extends PanopticError {
|
|
1077
|
+
name = "NotEnoughTokensError";
|
|
1078
|
+
constructor(tokenAddress, assetsRequested, assetBalance, cause) {
|
|
1079
|
+
super(`Not enough tokens: requested=${assetsRequested}, balance=${assetBalance}, token=${tokenAddress}`, cause);
|
|
1080
|
+
this.tokenAddress = tokenAddress;
|
|
1081
|
+
this.assetsRequested = assetsRequested;
|
|
1082
|
+
this.assetBalance = assetBalance;
|
|
1083
|
+
}
|
|
1084
|
+
};
|
|
1085
|
+
/**
|
|
1086
|
+
* There is not enough available liquidity in the chunk for a long leg.
|
|
1087
|
+
* @see Errors.sol:75
|
|
1088
|
+
*/
|
|
1089
|
+
var NotEnoughLiquidityInChunkError = class extends PanopticError {
|
|
1090
|
+
name = "NotEnoughLiquidityInChunkError";
|
|
1091
|
+
constructor(cause) {
|
|
1092
|
+
super("Not enough liquidity in chunk for long leg creation or short leg closure", cause);
|
|
1093
|
+
}
|
|
1094
|
+
};
|
|
1095
|
+
/**
|
|
1096
|
+
* There is not enough available liquidity to fulfill a credit.
|
|
1097
|
+
* @see Errors.sol:47
|
|
1098
|
+
*/
|
|
1099
|
+
var InsufficientCreditLiquidityError = class extends PanopticError {
|
|
1100
|
+
name = "InsufficientCreditLiquidityError";
|
|
1101
|
+
constructor(cause) {
|
|
1102
|
+
super("Insufficient credit liquidity available in the PanopticPool", cause);
|
|
1103
|
+
}
|
|
1104
|
+
};
|
|
1105
|
+
/**
|
|
1106
|
+
* The amount deposited is larger than the maximum permitted.
|
|
1107
|
+
* @see Errors.sol:25
|
|
1108
|
+
*/
|
|
1109
|
+
var DepositTooLargeError = class extends PanopticError {
|
|
1110
|
+
name = "DepositTooLargeError";
|
|
1111
|
+
constructor(cause) {
|
|
1112
|
+
super("Deposit amount exceeds maximum permitted", cause);
|
|
1113
|
+
}
|
|
1114
|
+
};
|
|
1115
|
+
/**
|
|
1116
|
+
* Attempted to withdraw/redeem less than a single asset.
|
|
1117
|
+
* @see Errors.sol:16
|
|
1118
|
+
*/
|
|
1119
|
+
var BelowMinimumRedemptionError = class extends PanopticError {
|
|
1120
|
+
name = "BelowMinimumRedemptionError";
|
|
1121
|
+
constructor(cause) {
|
|
1122
|
+
super("Redemption amount below minimum threshold", cause);
|
|
1123
|
+
}
|
|
1124
|
+
};
|
|
1125
|
+
/**
|
|
1126
|
+
* Attempted to withdraw/redeem more than available.
|
|
1127
|
+
* @see Errors.sol:35
|
|
1128
|
+
*/
|
|
1129
|
+
var ExceedsMaximumRedemptionError = class extends PanopticError {
|
|
1130
|
+
name = "ExceedsMaximumRedemptionError";
|
|
1131
|
+
constructor(cause) {
|
|
1132
|
+
super("Redemption exceeds maximum available (liquidity, shares, or open positions)", cause);
|
|
1133
|
+
}
|
|
1134
|
+
};
|
|
1135
|
+
/**
|
|
1136
|
+
* Mints/burns of a position returns no collateral requirement.
|
|
1137
|
+
* @see Errors.sol:141
|
|
1138
|
+
*/
|
|
1139
|
+
var ZeroCollateralRequirementError = class extends PanopticError {
|
|
381
1140
|
name = "ZeroCollateralRequirementError";
|
|
382
1141
|
constructor(cause) {
|
|
383
1142
|
super("Position mint/burn returned zero collateral requirement", cause);
|
|
@@ -586,7 +1345,7 @@ var NoLegsExercisableError = class extends PanopticError {
|
|
|
586
1345
|
var NotALongLegError = class extends PanopticError {
|
|
587
1346
|
name = "NotALongLegError";
|
|
588
1347
|
constructor(cause) {
|
|
589
|
-
super("Cannot settle
|
|
1348
|
+
super("Cannot settle streamia for non-long leg", cause);
|
|
590
1349
|
}
|
|
591
1350
|
};
|
|
592
1351
|
/**
|
|
@@ -4514,7 +5273,7 @@ function decodeAllLegs(tokenId) {
|
|
|
4514
5273
|
const POOL_ID_MASK$2 = (1n << 64n) - 1n;
|
|
4515
5274
|
const MAX_LEGS = TOKEN_ID_BITS.MAX_LEGS;
|
|
4516
5275
|
const MAX_OPTION_RATIO = LEG_LIMITS.MAX_RATIO;
|
|
4517
|
-
const Q192$
|
|
5276
|
+
const Q192$3 = 1n << 192n;
|
|
4518
5277
|
|
|
4519
5278
|
//#endregion
|
|
4520
5279
|
//#region src/panoptic/v2/formatters/rates.ts
|
|
@@ -4526,7 +5285,7 @@ const SECONDS_PER_YEAR = SECONDS_PER_DAY * DAYS_PER_YEAR;
|
|
|
4526
5285
|
//#region src/panoptic/v2/tokenId/generateOverlapping.ts
|
|
4527
5286
|
const MAX_UINT64 = (1n << 64n) - 1n;
|
|
4528
5287
|
const MAX_UINT128$2 = (1n << 128n) - 1n;
|
|
4529
|
-
const Q192$
|
|
5288
|
+
const Q192$2 = 1n << 192n;
|
|
4530
5289
|
|
|
4531
5290
|
//#endregion
|
|
4532
5291
|
//#region src/panoptic/v2/tokenId/split.ts
|
|
@@ -4536,7 +5295,7 @@ const MAX_UINT256 = (1n << 256n) - 1n;
|
|
|
4536
5295
|
|
|
4537
5296
|
//#endregion
|
|
4538
5297
|
//#region src/uniswap/lpDeposit.ts
|
|
4539
|
-
const Q96$
|
|
5298
|
+
const Q96$3 = 1n << 96n;
|
|
4540
5299
|
const ceilDiv = (n, d) => (n + d - 1n) / d;
|
|
4541
5300
|
/** Uniswap mint principal in raw token units, rounded up per leg, plus 5% headroom. */
|
|
4542
5301
|
function getLpDepositAmounts(ranges, sqrtPriceX96) {
|
|
@@ -4552,8 +5311,8 @@ function getLpDepositBreakdown(ranges, sqrtPriceX96) {
|
|
|
4552
5311
|
const lower = tickToSqrtPriceX96(BigInt(tickLower));
|
|
4553
5312
|
const upper = tickToSqrtPriceX96(BigInt(tickUpper));
|
|
4554
5313
|
const price = sqrtPriceX96 < lower ? lower : sqrtPriceX96 > upper ? upper : sqrtPriceX96;
|
|
4555
|
-
amount0 += ceilDiv(liquidity * Q96$
|
|
4556
|
-
amount1 += ceilDiv(liquidity * (price - lower), Q96$
|
|
5314
|
+
amount0 += ceilDiv(liquidity * Q96$3 * (upper - price), upper * price);
|
|
5315
|
+
amount1 += ceilDiv(liquidity * (price - lower), Q96$3);
|
|
4557
5316
|
}
|
|
4558
5317
|
const buffer = {
|
|
4559
5318
|
amount0: ceilDiv(amount0 * 5n, 100n),
|
|
@@ -4581,7 +5340,7 @@ function getUnhedgedLpRanges({ tokenId, positionSize, tickSpacing }) {
|
|
|
4581
5340
|
const lower = tickToSqrtPriceX96(tickLower);
|
|
4582
5341
|
const upper = tickToSqrtPriceX96(tickUpper);
|
|
4583
5342
|
const amount = positionSize * leg.optionRatio;
|
|
4584
|
-
const liquidity = leg.asset === 0n ? amount * (lower * upper / Q96$
|
|
5343
|
+
const liquidity = leg.asset === 0n ? amount * (lower * upper / Q96$3) / (upper - lower) : amount * Q96$3 / (upper - lower);
|
|
4585
5344
|
return liquidity <= 0n ? [] : [{
|
|
4586
5345
|
tickLower: Number(tickLower),
|
|
4587
5346
|
tickUpper: Number(tickUpper),
|
|
@@ -4602,7 +5361,7 @@ function getLpPositionFunding(params) {
|
|
|
4602
5361
|
});
|
|
4603
5362
|
const breakdown = getLpDepositBreakdown(ranges, sqrtPriceX96);
|
|
4604
5363
|
const priceSquared = valuationSqrtPriceX96 * valuationSqrtPriceX96;
|
|
4605
|
-
const value = ({ amount0, amount1 }) => quoteTokenIndex === 0 ? amount0 + ceilDiv(amount1 * Q96$
|
|
5364
|
+
const value = ({ amount0, amount1 }) => quoteTokenIndex === 0 ? amount0 + ceilDiv(amount1 * Q96$3 * Q96$3, priceSquared) : amount1 + ceilDiv(amount0 * priceSquared, Q96$3 * Q96$3);
|
|
4606
5365
|
const principalInQuote = value(breakdown.principal);
|
|
4607
5366
|
const totalInQuote = value(breakdown.total);
|
|
4608
5367
|
return {
|
|
@@ -4632,8 +5391,8 @@ function getLpFundingDeposit({ funding, availableInQuote, minimumInQuote = 0n, q
|
|
|
4632
5391
|
requiredInQuote,
|
|
4633
5392
|
shortfallInQuote,
|
|
4634
5393
|
defaultQuotePercent,
|
|
4635
|
-
amount0: quoteTokenIndex === 0 ? quoteAmount : ceilDiv(assetValue * Q96$
|
|
4636
|
-
amount1: quoteTokenIndex === 1 ? quoteAmount : ceilDiv(assetValue * priceSquared, Q96$
|
|
5394
|
+
amount0: quoteTokenIndex === 0 ? quoteAmount : ceilDiv(assetValue * Q96$3 * Q96$3, priceSquared),
|
|
5395
|
+
amount1: quoteTokenIndex === 1 ? quoteAmount : ceilDiv(assetValue * priceSquared, Q96$3 * Q96$3)
|
|
4637
5396
|
};
|
|
4638
5397
|
}
|
|
4639
5398
|
/** Largest raw size funded by available collateral, including native-token rounding. */
|
|
@@ -4655,66 +5414,6 @@ function getMaxLpPositionSize(params) {
|
|
|
4655
5414
|
return low;
|
|
4656
5415
|
}
|
|
4657
5416
|
|
|
4658
|
-
//#endregion
|
|
4659
|
-
//#region src/uniswap/lpGreeks.ts
|
|
4660
|
-
const Q96$3 = 1n << 96n;
|
|
4661
|
-
const Q192$2 = 1n << 192n;
|
|
4662
|
-
/**
|
|
4663
|
-
* Compute the token0/token1 amounts backing `liquidity` over `[sqrtA, sqrtB]`
|
|
4664
|
-
* at the current price `sqrtP`, mirroring Uniswap's `LiquidityAmounts`
|
|
4665
|
-
* (`getAmountsForLiquidity`). All sqrt prices are X96. The current price is
|
|
4666
|
-
* clamped into the range, so out-of-range positions collapse to a single token.
|
|
4667
|
-
*/
|
|
4668
|
-
function getAmountsForLiquidity(sqrtPriceX96, sqrtRatioAX96, sqrtRatioBX96, liquidity) {
|
|
4669
|
-
const [sqrtA, sqrtB] = sqrtRatioAX96 <= sqrtRatioBX96 ? [sqrtRatioAX96, sqrtRatioBX96] : [sqrtRatioBX96, sqrtRatioAX96];
|
|
4670
|
-
const sqrtC = sqrtPriceX96 < sqrtA ? sqrtA : sqrtPriceX96 > sqrtB ? sqrtB : sqrtPriceX96;
|
|
4671
|
-
const amount0 = sqrtC < sqrtB ? liquidity * (sqrtB - sqrtC) * Q96$3 / (sqrtC * sqrtB) : 0n;
|
|
4672
|
-
const amount1 = sqrtC > sqrtA ? liquidity * (sqrtC - sqrtA) / Q96$3 : 0n;
|
|
4673
|
-
return {
|
|
4674
|
-
amount0,
|
|
4675
|
-
amount1
|
|
4676
|
-
};
|
|
4677
|
-
}
|
|
4678
|
-
/**
|
|
4679
|
-
* Greeks (value, delta, gamma) for a Uniswap LP position in the chosen asset
|
|
4680
|
-
* frame. See the module docs for the unit conventions.
|
|
4681
|
-
*
|
|
4682
|
-
* Closed forms (in-range, numeraire = token1 / asset = token0):
|
|
4683
|
-
* value = amount1 + amount0·P (P = sqrtP² / 2^192)
|
|
4684
|
-
* delta = amount0 (= dV/dP)
|
|
4685
|
-
* gamma = P²·d²V/dP² = -L·sqrt(P)/2 (short gamma)
|
|
4686
|
-
* The `assetIndex = 1` frame is the symmetric inverse (numeraire = token0).
|
|
4687
|
-
* Gamma is zero when the price is outside the range (no curvature there).
|
|
4688
|
-
*/
|
|
4689
|
-
function getLpGreeks(input) {
|
|
4690
|
-
const { liquidity, tickLower, tickUpper, currentTick, assetIndex } = input;
|
|
4691
|
-
const sqrtP = input.sqrtPriceX96 ?? tickToSqrtPriceX96(currentTick);
|
|
4692
|
-
if (sqrtP <= 0n) throw new RangeError("Invalid sqrt price");
|
|
4693
|
-
const sqrtA = tickToSqrtPriceX96(tickLower);
|
|
4694
|
-
const sqrtB = tickToSqrtPriceX96(tickUpper);
|
|
4695
|
-
const { amount0, amount1 } = getAmountsForLiquidity(sqrtP, sqrtA, sqrtB, liquidity);
|
|
4696
|
-
const sqrtP2 = sqrtP * sqrtP;
|
|
4697
|
-
const inRange = sqrtP > sqrtA && sqrtP < sqrtB;
|
|
4698
|
-
if (assetIndex === 0) {
|
|
4699
|
-
const value$1 = amount1 + amount0 * sqrtP2 / Q192$2;
|
|
4700
|
-
const delta$1 = amount0;
|
|
4701
|
-
const gamma$1 = inRange ? -(liquidity * sqrtP) / (2n * Q96$3) : 0n;
|
|
4702
|
-
return {
|
|
4703
|
-
value: value$1,
|
|
4704
|
-
delta: delta$1,
|
|
4705
|
-
gamma: gamma$1
|
|
4706
|
-
};
|
|
4707
|
-
}
|
|
4708
|
-
const value = amount0 + amount1 * Q192$2 / sqrtP2;
|
|
4709
|
-
const delta = amount1;
|
|
4710
|
-
const gamma = inRange ? -(liquidity * Q96$3) / (2n * sqrtP) : 0n;
|
|
4711
|
-
return {
|
|
4712
|
-
value,
|
|
4713
|
-
delta,
|
|
4714
|
-
gamma
|
|
4715
|
-
};
|
|
4716
|
-
}
|
|
4717
|
-
|
|
4718
5417
|
//#endregion
|
|
4719
5418
|
//#region src/uniswap/lpFeeReturn.ts
|
|
4720
5419
|
const D = Decimal.clone({ precision: 60 });
|
|
@@ -4756,6 +5455,146 @@ function getLpFeeReturnSeries({ points, ranges, startTick }) {
|
|
|
4756
5455
|
});
|
|
4757
5456
|
}
|
|
4758
5457
|
|
|
5458
|
+
//#endregion
|
|
5459
|
+
//#region src/uniswap/lpHistory.ts
|
|
5460
|
+
const unsigned = z.string().regex(/^\d+$/).transform(BigInt);
|
|
5461
|
+
const eventFields = {
|
|
5462
|
+
id: z.string().min(1),
|
|
5463
|
+
hash: z.string().regex(/^0x[0-9a-fA-F]{64}$/),
|
|
5464
|
+
blockNumber: unsigned,
|
|
5465
|
+
logIndex: unsigned,
|
|
5466
|
+
timestamp: unsigned,
|
|
5467
|
+
amount0: unsigned,
|
|
5468
|
+
amount1: unsigned
|
|
5469
|
+
};
|
|
5470
|
+
const changeSchema = z.object({
|
|
5471
|
+
...eventFields,
|
|
5472
|
+
eventType: z.enum(["Mint", "Burn"]),
|
|
5473
|
+
liquidity: unsigned,
|
|
5474
|
+
amountSource: z.string().nullable()
|
|
5475
|
+
});
|
|
5476
|
+
const collectSchema = z.object({
|
|
5477
|
+
...eventFields,
|
|
5478
|
+
recipient: z.string().regex(/^0x[0-9a-fA-F]{40}$/)
|
|
5479
|
+
});
|
|
5480
|
+
const pageSchema = z.object({
|
|
5481
|
+
_meta: z.object({
|
|
5482
|
+
block: z.object({
|
|
5483
|
+
number: z.number().int().nonnegative(),
|
|
5484
|
+
hash: z.string().regex(/^0x[0-9a-fA-F]{64}$/)
|
|
5485
|
+
}),
|
|
5486
|
+
hasIndexingErrors: z.boolean()
|
|
5487
|
+
}),
|
|
5488
|
+
uniswapLpPosition: z.object({
|
|
5489
|
+
version: z.union([z.literal(3), z.literal(4)]),
|
|
5490
|
+
liquidity: unsigned,
|
|
5491
|
+
liquidityChanges: z.array(changeSchema),
|
|
5492
|
+
collects: z.array(collectSchema)
|
|
5493
|
+
}).nullable()
|
|
5494
|
+
});
|
|
5495
|
+
const HISTORY_QUERY = `
|
|
5496
|
+
query LpPositionActivity($positionId: ID!, $block: Block_height, $changeCursor: ID!, $collectCursor: ID!, $pageSize: Int!) {
|
|
5497
|
+
_meta(block: $block) { block { number hash } hasIndexingErrors }
|
|
5498
|
+
uniswapLpPosition(id: $positionId, block: $block) {
|
|
5499
|
+
version
|
|
5500
|
+
liquidity
|
|
5501
|
+
liquidityChanges(first: $pageSize, where: { id_gt: $changeCursor }, orderBy: id, orderDirection: asc) {
|
|
5502
|
+
id hash eventType liquidity amount0 amount1 amountSource blockNumber logIndex timestamp
|
|
5503
|
+
}
|
|
5504
|
+
collects(first: $pageSize, where: { id_gt: $collectCursor }, orderBy: id, orderDirection: asc) {
|
|
5505
|
+
id hash amount0 amount1 blockNumber logIndex timestamp recipient
|
|
5506
|
+
}
|
|
5507
|
+
}
|
|
5508
|
+
}
|
|
5509
|
+
`;
|
|
5510
|
+
/** Complete NFT activity at one indexed block, sorted by block and log index. */
|
|
5511
|
+
async function getLpPositionHistory({ url, positionId, signal }) {
|
|
5512
|
+
if (!/^v[34]-\d+$/.test(positionId)) throw new RangeError("Invalid LP position id");
|
|
5513
|
+
const client = new GraphQLClient(url, { signal });
|
|
5514
|
+
const events = [];
|
|
5515
|
+
let block = null;
|
|
5516
|
+
let changeCursor = "";
|
|
5517
|
+
let collectCursor = "";
|
|
5518
|
+
while (true) {
|
|
5519
|
+
const page = pageSchema.parse(await client.request(HISTORY_QUERY, {
|
|
5520
|
+
positionId,
|
|
5521
|
+
block,
|
|
5522
|
+
changeCursor,
|
|
5523
|
+
collectCursor,
|
|
5524
|
+
pageSize: 1e3
|
|
5525
|
+
}));
|
|
5526
|
+
if (page._meta.hasIndexingErrors) throw new Error("LP history indexing is incomplete");
|
|
5527
|
+
const position = page.uniswapLpPosition;
|
|
5528
|
+
if (!position) throw new Error("LP position history is unavailable");
|
|
5529
|
+
if (position.version !== Number(positionId[1])) throw new Error("LP position version mismatch");
|
|
5530
|
+
if (block && block.hash !== page._meta.block.hash) throw new Error("LP history checkpoint changed");
|
|
5531
|
+
block = { hash: page._meta.block.hash };
|
|
5532
|
+
const changes = position.liquidityChanges;
|
|
5533
|
+
const collects = position.collects;
|
|
5534
|
+
events.push(...changes, ...collects.map((event) => ({
|
|
5535
|
+
...event,
|
|
5536
|
+
eventType: "Collect",
|
|
5537
|
+
amountSource: "ExactEvent"
|
|
5538
|
+
})));
|
|
5539
|
+
const lastChange = changes.at(-1);
|
|
5540
|
+
const lastCollect = collects.at(-1);
|
|
5541
|
+
if (lastChange && lastChange.id <= changeCursor || lastCollect && lastCollect.id <= collectCursor) throw new Error("LP history pagination did not advance");
|
|
5542
|
+
changeCursor = lastChange?.id ?? changeCursor;
|
|
5543
|
+
collectCursor = lastCollect?.id ?? collectCursor;
|
|
5544
|
+
if (changes.length < 1e3 && collects.length < 1e3) {
|
|
5545
|
+
events.sort((a, b) => {
|
|
5546
|
+
const order = a.blockNumber - b.blockNumber || a.logIndex - b.logIndex;
|
|
5547
|
+
return order < 0n ? -1 : order > 0n ? 1 : 0;
|
|
5548
|
+
});
|
|
5549
|
+
let held = 0n;
|
|
5550
|
+
for (const event of events) {
|
|
5551
|
+
if (event.eventType === "Collect") continue;
|
|
5552
|
+
held += event.eventType === "Burn" ? -event.liquidity : event.liquidity;
|
|
5553
|
+
if (held < 0n) throw new Error("LP liquidity history is incomplete");
|
|
5554
|
+
}
|
|
5555
|
+
if (held !== position.liquidity || events.length === 0) throw new Error("LP liquidity history is incomplete");
|
|
5556
|
+
return {
|
|
5557
|
+
events,
|
|
5558
|
+
positionId,
|
|
5559
|
+
blockHash: page._meta.block.hash,
|
|
5560
|
+
blockNumber: BigInt(page._meta.block.number),
|
|
5561
|
+
version: position.version
|
|
5562
|
+
};
|
|
5563
|
+
}
|
|
5564
|
+
}
|
|
5565
|
+
}
|
|
5566
|
+
/** Token flows; v3 collects may include principal already recorded in removals. */
|
|
5567
|
+
function summarizeLpPositionHistory(events) {
|
|
5568
|
+
const totals = (eventType) => {
|
|
5569
|
+
let amount0 = 0n;
|
|
5570
|
+
let amount1 = 0n;
|
|
5571
|
+
let estimated = false;
|
|
5572
|
+
for (const event of events) {
|
|
5573
|
+
if (event.eventType !== eventType) continue;
|
|
5574
|
+
if (event.amountSource !== "ExactEvent" && event.amountSource !== "ExactSameTxSwap") {
|
|
5575
|
+
if (event.amountSource !== "ApproxExtsload") return {
|
|
5576
|
+
amount0: null,
|
|
5577
|
+
amount1: null,
|
|
5578
|
+
estimated: false
|
|
5579
|
+
};
|
|
5580
|
+
estimated = true;
|
|
5581
|
+
}
|
|
5582
|
+
amount0 += event.amount0;
|
|
5583
|
+
amount1 += event.amount1;
|
|
5584
|
+
}
|
|
5585
|
+
return {
|
|
5586
|
+
amount0,
|
|
5587
|
+
amount1,
|
|
5588
|
+
estimated
|
|
5589
|
+
};
|
|
5590
|
+
};
|
|
5591
|
+
return {
|
|
5592
|
+
added: totals("Mint"),
|
|
5593
|
+
removed: totals("Burn"),
|
|
5594
|
+
collected: totals("Collect")
|
|
5595
|
+
};
|
|
5596
|
+
}
|
|
5597
|
+
|
|
4759
5598
|
//#endregion
|
|
4760
5599
|
//#region src/panoptic/v2/greeks/positionVolatility.ts
|
|
4761
5600
|
const Precision$1 = Decimal.clone({ precision: 80 });
|
|
@@ -4842,11 +5681,13 @@ function valuePositionAccrual({ snapshots, quoteIsToken0, quoteDecimals }) {
|
|
|
4842
5681
|
const Precision = Decimal.clone({ precision: 80 });
|
|
4843
5682
|
const Q192$1 = (1n << 192n).toString();
|
|
4844
5683
|
/** Historical convexity and fee coverage at constant LP liquidity, in human quote units. */
|
|
4845
|
-
function calculateLpPositionVolatility({ liquidity, tickLower, tickUpper, quoteIsToken0, quoteDecimals, snapshots }) {
|
|
5684
|
+
function calculateLpPositionVolatility({ liquidity, tickLower, tickUpper, quoteIsToken0, quoteDecimals, snapshots, observations: priceObservations }) {
|
|
4846
5685
|
if (liquidity <= 0n || liquidity >= 1n << 128n) throw new RangeError("Invalid LP liquidity");
|
|
4847
5686
|
if (tickLower < -887272n || tickUpper > 887272n || tickLower >= tickUpper) throw new RangeError("Invalid LP range");
|
|
5687
|
+
if (snapshots.some((point, index) => index > 0 && point.blockTimestamp <= snapshots[index - 1].blockTimestamp)) throw new RangeError("Fee snapshots must have increasing timestamps");
|
|
4848
5688
|
const scale = new Decimal(10).pow(quoteDecimals);
|
|
4849
|
-
|
|
5689
|
+
if (priceObservations && (priceObservations.length < 2 || priceObservations[0].timestamp !== snapshots[0]?.blockTimestamp || priceObservations.at(-1)?.timestamp !== snapshots.at(-1)?.blockTimestamp)) throw new RangeError("Price and fee history must cover the same window");
|
|
5690
|
+
const observations = priceObservations ?? snapshots.map((snapshot) => ({
|
|
4850
5691
|
timestamp: snapshot.blockTimestamp,
|
|
4851
5692
|
price: quoteIsToken0 ? new Precision(Q192$1).div(new Precision(snapshot.sqrtPriceX96.toString()).pow(2)) : new Precision(snapshot.sqrtPriceX96.toString()).pow(2).div(Q192$1),
|
|
4852
5693
|
gamma: new Decimal(getLpGreeks({
|
|
@@ -4934,10 +5775,10 @@ function quoteTokenPrices(pools, quoteAddresses) {
|
|
|
4934
5775
|
*/
|
|
4935
5776
|
function normalCdf(x) {
|
|
4936
5777
|
if (!Number.isFinite(x)) return x > 0 ? 1 : 0;
|
|
4937
|
-
const z = x / Math.SQRT2;
|
|
4938
|
-
const t = 1 / (1 + .3275911 * Math.abs(z));
|
|
4939
|
-
const y = 1 - ((((1.061405429 * t - 1.453152027) * t + 1.421413741) * t - .284496736) * t + .254829592) * t * Math.exp(-z * z);
|
|
4940
|
-
const erf = z >= 0 ? y : -y;
|
|
5778
|
+
const z$1 = x / Math.SQRT2;
|
|
5779
|
+
const t = 1 / (1 + .3275911 * Math.abs(z$1));
|
|
5780
|
+
const y = 1 - ((((1.061405429 * t - 1.453152027) * t + 1.421413741) * t - .284496736) * t + .254829592) * t * Math.exp(-z$1 * z$1);
|
|
5781
|
+
const erf = z$1 >= 0 ? y : -y;
|
|
4941
5782
|
return .5 * (1 + erf);
|
|
4942
5783
|
}
|
|
4943
5784
|
/**
|
|
@@ -5168,104 +6009,21 @@ const V3_SWAP_EXACT_IN = 0;
|
|
|
5168
6009
|
const V3_SWAP_EXACT_OUT = 1;
|
|
5169
6010
|
/**
|
|
5170
6011
|
* Universal Router recipient sentinel: the router maps `address(1)` to
|
|
5171
|
-
* `msg.sender` (the `execute` caller). Uniswap `Constants.MSG_SENDER`.
|
|
5172
|
-
*/
|
|
5173
|
-
const MSG_SENDER = "0x0000000000000000000000000000000000000001";
|
|
5174
|
-
const UINT128_MAX$4 = (1n << 128n) - 1n;
|
|
5175
|
-
const v3ExactInInputAbi = [
|
|
5176
|
-
{
|
|
5177
|
-
name: "recipient",
|
|
5178
|
-
type: "address"
|
|
5179
|
-
},
|
|
5180
|
-
{
|
|
5181
|
-
name: "amountIn",
|
|
5182
|
-
type: "uint256"
|
|
5183
|
-
},
|
|
5184
|
-
{
|
|
5185
|
-
name: "amountOutMinimum",
|
|
5186
|
-
type: "uint256"
|
|
5187
|
-
},
|
|
5188
|
-
{
|
|
5189
|
-
name: "path",
|
|
5190
|
-
type: "bytes"
|
|
5191
|
-
},
|
|
5192
|
-
{
|
|
5193
|
-
name: "payerIsUser",
|
|
5194
|
-
type: "bool"
|
|
5195
|
-
}
|
|
5196
|
-
];
|
|
5197
|
-
function assertUint128$1(amount) {
|
|
5198
|
-
if (amount < 0n || amount > UINT128_MAX$4) throw new AmountExceedsUint128Error(amount);
|
|
5199
|
-
}
|
|
5200
|
-
/**
|
|
5201
|
-
* Encode the packed v3 single-hop path `tokenIn ++ fee(uint24) ++ tokenOut`.
|
|
5202
|
-
*/
|
|
5203
|
-
function encodeV3Path(tokenIn, fee, tokenOut) {
|
|
5204
|
-
if (fee < 0n || fee > 0xffffffn) throw new PanopticError(`v3 fee ${fee} exceeds uint24`);
|
|
5205
|
-
return encodePacked([
|
|
5206
|
-
"address",
|
|
5207
|
-
"uint24",
|
|
5208
|
-
"address"
|
|
5209
|
-
], [
|
|
5210
|
-
tokenIn,
|
|
5211
|
-
Number(fee),
|
|
5212
|
-
tokenOut
|
|
5213
|
-
]);
|
|
5214
|
-
}
|
|
5215
|
-
/**
|
|
5216
|
-
* Build the typed `execute(...)` args for an exact-in single-hop v3 swap whose
|
|
5217
|
-
* output is delivered to the `execute` caller (recipient = MSG_SENDER).
|
|
5218
|
-
*/
|
|
5219
|
-
function buildV3SwapExecuteArgs(args) {
|
|
5220
|
-
const { tokenIn, tokenOut, fee, amountIn, amountOutMinimum, deadline } = args;
|
|
5221
|
-
if (tokenIn === zeroAddress || tokenOut === zeroAddress) throw new PanopticError("native ETH is not supported by the v3 exact-in router builder");
|
|
5222
|
-
assertUint128$1(amountIn);
|
|
5223
|
-
assertUint128$1(amountOutMinimum);
|
|
5224
|
-
const path = encodeV3Path(tokenIn, fee, tokenOut);
|
|
5225
|
-
const input = encodeAbiParameters(v3ExactInInputAbi, [
|
|
5226
|
-
MSG_SENDER,
|
|
5227
|
-
amountIn,
|
|
5228
|
-
amountOutMinimum,
|
|
5229
|
-
path,
|
|
5230
|
-
true
|
|
5231
|
-
]);
|
|
5232
|
-
const commands = encodePacked(["uint8"], [V3_SWAP_EXACT_IN]);
|
|
5233
|
-
return {
|
|
5234
|
-
args: [
|
|
5235
|
-
commands,
|
|
5236
|
-
[input],
|
|
5237
|
-
deadline
|
|
5238
|
-
],
|
|
5239
|
-
value: 0n
|
|
5240
|
-
};
|
|
5241
|
-
}
|
|
5242
|
-
/**
|
|
5243
|
-
* Build the `execute(commands, inputs, deadline)` calldata for an exact-in
|
|
5244
|
-
* single-hop v3 swap.
|
|
5245
|
-
*/
|
|
5246
|
-
function buildV3SwapExecuteCalldata(args) {
|
|
5247
|
-
const { args: executeArgs, value } = buildV3SwapExecuteArgs(args);
|
|
5248
|
-
const data = encodeFunctionData({
|
|
5249
|
-
abi: universalRouterAbi,
|
|
5250
|
-
functionName: "execute",
|
|
5251
|
-
args: executeArgs
|
|
5252
|
-
});
|
|
5253
|
-
return {
|
|
5254
|
-
data,
|
|
5255
|
-
value
|
|
5256
|
-
};
|
|
5257
|
-
}
|
|
5258
|
-
const v3ExactOutInputAbi = [
|
|
6012
|
+
* `msg.sender` (the `execute` caller). Uniswap `Constants.MSG_SENDER`.
|
|
6013
|
+
*/
|
|
6014
|
+
const MSG_SENDER = "0x0000000000000000000000000000000000000001";
|
|
6015
|
+
const UINT128_MAX$4 = (1n << 128n) - 1n;
|
|
6016
|
+
const v3ExactInInputAbi = [
|
|
5259
6017
|
{
|
|
5260
6018
|
name: "recipient",
|
|
5261
6019
|
type: "address"
|
|
5262
6020
|
},
|
|
5263
6021
|
{
|
|
5264
|
-
name: "
|
|
6022
|
+
name: "amountIn",
|
|
5265
6023
|
type: "uint256"
|
|
5266
6024
|
},
|
|
5267
6025
|
{
|
|
5268
|
-
name: "
|
|
6026
|
+
name: "amountOutMinimum",
|
|
5269
6027
|
type: "uint256"
|
|
5270
6028
|
},
|
|
5271
6029
|
{
|
|
@@ -5277,547 +6035,316 @@ const v3ExactOutInputAbi = [
|
|
|
5277
6035
|
type: "bool"
|
|
5278
6036
|
}
|
|
5279
6037
|
];
|
|
6038
|
+
function assertUint128$1(amount) {
|
|
6039
|
+
if (amount < 0n || amount > UINT128_MAX$4) throw new AmountExceedsUint128Error(amount);
|
|
6040
|
+
}
|
|
5280
6041
|
/**
|
|
5281
|
-
*
|
|
5282
|
-
* The v3 exact-out path is reversed: `tokenOut ++ fee ++ tokenIn`.
|
|
6042
|
+
* Encode the packed v3 single-hop path `tokenIn ++ fee(uint24) ++ tokenOut`.
|
|
5283
6043
|
*/
|
|
5284
|
-
function
|
|
5285
|
-
|
|
5286
|
-
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
|
|
5290
|
-
|
|
6044
|
+
function encodeV3Path(tokenIn, fee, tokenOut) {
|
|
6045
|
+
if (fee < 0n || fee > 0xffffffn) throw new PanopticError(`v3 fee ${fee} exceeds uint24`);
|
|
6046
|
+
return encodePacked([
|
|
6047
|
+
"address",
|
|
6048
|
+
"uint24",
|
|
6049
|
+
"address"
|
|
6050
|
+
], [
|
|
6051
|
+
tokenIn,
|
|
6052
|
+
Number(fee),
|
|
6053
|
+
tokenOut
|
|
6054
|
+
]);
|
|
6055
|
+
}
|
|
6056
|
+
/**
|
|
6057
|
+
* Build the typed `execute(...)` args for an exact-in single-hop v3 swap whose
|
|
6058
|
+
* output is delivered to the `execute` caller (recipient = MSG_SENDER).
|
|
6059
|
+
*/
|
|
6060
|
+
function buildV3SwapExecuteArgs(args) {
|
|
6061
|
+
const { tokenIn, tokenOut, fee, amountIn, amountOutMinimum, deadline } = args;
|
|
6062
|
+
if (tokenIn === zeroAddress || tokenOut === zeroAddress) throw new PanopticError("native ETH is not supported by the v3 exact-in router builder");
|
|
6063
|
+
assertUint128$1(amountIn);
|
|
6064
|
+
assertUint128$1(amountOutMinimum);
|
|
6065
|
+
const path = encodeV3Path(tokenIn, fee, tokenOut);
|
|
6066
|
+
const input = encodeAbiParameters(v3ExactInInputAbi, [
|
|
5291
6067
|
MSG_SENDER,
|
|
5292
|
-
|
|
5293
|
-
|
|
6068
|
+
amountIn,
|
|
6069
|
+
amountOutMinimum,
|
|
5294
6070
|
path,
|
|
5295
6071
|
true
|
|
5296
6072
|
]);
|
|
5297
|
-
const commands = encodePacked(["uint8"], [
|
|
6073
|
+
const commands = encodePacked(["uint8"], [V3_SWAP_EXACT_IN]);
|
|
5298
6074
|
return {
|
|
5299
6075
|
args: [
|
|
5300
6076
|
commands,
|
|
5301
|
-
[input],
|
|
5302
|
-
deadline
|
|
5303
|
-
],
|
|
5304
|
-
value: 0n
|
|
5305
|
-
};
|
|
5306
|
-
}
|
|
5307
|
-
function buildV3ExactOutSwapExecuteCalldata(args) {
|
|
5308
|
-
const { args: executeArgs, value } = buildV3ExactOutSwapExecuteArgs(args);
|
|
5309
|
-
const data = encodeFunctionData({
|
|
5310
|
-
abi: universalRouterAbi,
|
|
5311
|
-
functionName: "execute",
|
|
5312
|
-
args: executeArgs
|
|
5313
|
-
});
|
|
5314
|
-
return {
|
|
5315
|
-
data,
|
|
5316
|
-
value
|
|
5317
|
-
};
|
|
5318
|
-
}
|
|
5319
|
-
|
|
5320
|
-
//#endregion
|
|
5321
|
-
//#region src/uniswap/v3/abis/quoterV2.ts
|
|
5322
|
-
/**
|
|
5323
|
-
* Minimal Uniswap v3 QuoterV2 ABI.
|
|
5324
|
-
*
|
|
5325
|
-
* QuoterV2 is revert/staticcall-based (nonpayable but intended for `eth_call` /
|
|
5326
|
-
* `simulateContract`), so it must be simulated, not read.
|
|
5327
|
-
* @module uniswap/v3/abis/quoterV2
|
|
5328
|
-
*/
|
|
5329
|
-
const quoterV2Abi = [{
|
|
5330
|
-
type: "function",
|
|
5331
|
-
name: "quoteExactInputSingle",
|
|
5332
|
-
stateMutability: "nonpayable",
|
|
5333
|
-
inputs: [{
|
|
5334
|
-
name: "params",
|
|
5335
|
-
type: "tuple",
|
|
5336
|
-
components: [
|
|
5337
|
-
{
|
|
5338
|
-
name: "tokenIn",
|
|
5339
|
-
type: "address"
|
|
5340
|
-
},
|
|
5341
|
-
{
|
|
5342
|
-
name: "tokenOut",
|
|
5343
|
-
type: "address"
|
|
5344
|
-
},
|
|
5345
|
-
{
|
|
5346
|
-
name: "amountIn",
|
|
5347
|
-
type: "uint256"
|
|
5348
|
-
},
|
|
5349
|
-
{
|
|
5350
|
-
name: "fee",
|
|
5351
|
-
type: "uint24"
|
|
5352
|
-
},
|
|
5353
|
-
{
|
|
5354
|
-
name: "sqrtPriceLimitX96",
|
|
5355
|
-
type: "uint160"
|
|
5356
|
-
}
|
|
5357
|
-
]
|
|
5358
|
-
}],
|
|
5359
|
-
outputs: [
|
|
5360
|
-
{
|
|
5361
|
-
name: "amountOut",
|
|
5362
|
-
type: "uint256"
|
|
5363
|
-
},
|
|
5364
|
-
{
|
|
5365
|
-
name: "sqrtPriceX96After",
|
|
5366
|
-
type: "uint160"
|
|
5367
|
-
},
|
|
5368
|
-
{
|
|
5369
|
-
name: "initializedTicksCrossed",
|
|
5370
|
-
type: "uint32"
|
|
5371
|
-
},
|
|
5372
|
-
{
|
|
5373
|
-
name: "gasEstimate",
|
|
5374
|
-
type: "uint256"
|
|
5375
|
-
}
|
|
5376
|
-
]
|
|
5377
|
-
}, {
|
|
5378
|
-
type: "function",
|
|
5379
|
-
name: "quoteExactOutputSingle",
|
|
5380
|
-
stateMutability: "nonpayable",
|
|
5381
|
-
inputs: [{
|
|
5382
|
-
name: "params",
|
|
5383
|
-
type: "tuple",
|
|
5384
|
-
components: [
|
|
5385
|
-
{
|
|
5386
|
-
name: "tokenIn",
|
|
5387
|
-
type: "address"
|
|
5388
|
-
},
|
|
5389
|
-
{
|
|
5390
|
-
name: "tokenOut",
|
|
5391
|
-
type: "address"
|
|
5392
|
-
},
|
|
5393
|
-
{
|
|
5394
|
-
name: "amount",
|
|
5395
|
-
type: "uint256"
|
|
5396
|
-
},
|
|
5397
|
-
{
|
|
5398
|
-
name: "fee",
|
|
5399
|
-
type: "uint24"
|
|
5400
|
-
},
|
|
5401
|
-
{
|
|
5402
|
-
name: "sqrtPriceLimitX96",
|
|
5403
|
-
type: "uint160"
|
|
5404
|
-
}
|
|
5405
|
-
]
|
|
5406
|
-
}],
|
|
5407
|
-
outputs: [
|
|
5408
|
-
{
|
|
5409
|
-
name: "amountIn",
|
|
5410
|
-
type: "uint256"
|
|
5411
|
-
},
|
|
5412
|
-
{
|
|
5413
|
-
name: "sqrtPriceX96After",
|
|
5414
|
-
type: "uint160"
|
|
5415
|
-
},
|
|
5416
|
-
{
|
|
5417
|
-
name: "initializedTicksCrossed",
|
|
5418
|
-
type: "uint32"
|
|
5419
|
-
},
|
|
5420
|
-
{
|
|
5421
|
-
name: "gasEstimate",
|
|
5422
|
-
type: "uint256"
|
|
5423
|
-
}
|
|
5424
|
-
]
|
|
5425
|
-
}];
|
|
5426
|
-
|
|
5427
|
-
//#endregion
|
|
5428
|
-
//#region src/uniswap/v3/router/quote.ts
|
|
5429
|
-
const BPS_DENOMINATOR$2 = 10000n;
|
|
5430
|
-
const UINT128_MAX$3 = (1n << 128n) - 1n;
|
|
5431
|
-
/**
|
|
5432
|
-
* Quote an exact-in v3 swap. Returns `null` when the quote reverts (e.g. the
|
|
5433
|
-
* pool does not exist / has no liquidity) so callers can skip that pool when
|
|
5434
|
-
* ranking venues, rather than aborting the whole cycle.
|
|
5435
|
-
*/
|
|
5436
|
-
async function quoteV3ExactIn(params) {
|
|
5437
|
-
const { client, chainId, tokenIn, tokenOut, fee, amountIn, slippageBps, blockNumber } = params;
|
|
5438
|
-
if (amountIn < 0n || amountIn > UINT128_MAX$3) throw new PanopticError(`amountIn ${amountIn} exceeds uint128 maximum`);
|
|
5439
|
-
assertSlippageBps$1(slippageBps);
|
|
5440
|
-
const { quoterV2 } = getUniswapV3Addresses(chainId, params.addresses);
|
|
5441
|
-
try {
|
|
5442
|
-
const { result } = await client.simulateContract({
|
|
5443
|
-
address: quoterV2,
|
|
5444
|
-
abi: quoterV2Abi,
|
|
5445
|
-
functionName: "quoteExactInputSingle",
|
|
5446
|
-
blockNumber,
|
|
5447
|
-
args: [{
|
|
5448
|
-
tokenIn,
|
|
5449
|
-
tokenOut,
|
|
5450
|
-
amountIn,
|
|
5451
|
-
fee: Number(fee),
|
|
5452
|
-
sqrtPriceLimitX96: 0n
|
|
5453
|
-
}]
|
|
5454
|
-
});
|
|
5455
|
-
const [amountOut, , , gasEstimate] = result;
|
|
5456
|
-
const amountOutMinimum = amountOut * (BPS_DENOMINATOR$2 - slippageBps) / BPS_DENOMINATOR$2;
|
|
5457
|
-
return {
|
|
5458
|
-
amountOut,
|
|
5459
|
-
amountOutMinimum,
|
|
5460
|
-
gasEstimate
|
|
5461
|
-
};
|
|
5462
|
-
} catch (err) {
|
|
5463
|
-
if (isRevert$1(err)) return null;
|
|
5464
|
-
throw err;
|
|
5465
|
-
}
|
|
5466
|
-
}
|
|
5467
|
-
/**
|
|
5468
|
-
* Quote an exact-out v3 swap. Returns `null` on revert (no pool / no liquidity).
|
|
5469
|
-
*/
|
|
5470
|
-
async function quoteV3ExactOut(params) {
|
|
5471
|
-
const { client, chainId, tokenIn, tokenOut, fee, amountOut, slippageBps, blockNumber } = params;
|
|
5472
|
-
if (amountOut < 0n || amountOut > UINT128_MAX$3) throw new PanopticError(`amountOut ${amountOut} exceeds uint128 maximum`);
|
|
5473
|
-
assertSlippageBps$1(slippageBps);
|
|
5474
|
-
const { quoterV2 } = getUniswapV3Addresses(chainId, params.addresses);
|
|
5475
|
-
try {
|
|
5476
|
-
const { result } = await client.simulateContract({
|
|
5477
|
-
address: quoterV2,
|
|
5478
|
-
abi: quoterV2Abi,
|
|
5479
|
-
functionName: "quoteExactOutputSingle",
|
|
5480
|
-
blockNumber,
|
|
5481
|
-
args: [{
|
|
5482
|
-
tokenIn,
|
|
5483
|
-
tokenOut,
|
|
5484
|
-
amount: amountOut,
|
|
5485
|
-
fee: Number(fee),
|
|
5486
|
-
sqrtPriceLimitX96: 0n
|
|
5487
|
-
}]
|
|
5488
|
-
});
|
|
5489
|
-
const [amountIn, , , gasEstimate] = result;
|
|
5490
|
-
const amountInMaximum = (amountIn * (BPS_DENOMINATOR$2 + slippageBps) + BPS_DENOMINATOR$2 - 1n) / BPS_DENOMINATOR$2;
|
|
5491
|
-
return {
|
|
5492
|
-
amountIn,
|
|
5493
|
-
amountInMaximum,
|
|
5494
|
-
gasEstimate
|
|
5495
|
-
};
|
|
5496
|
-
} catch (err) {
|
|
5497
|
-
if (isRevert$1(err)) return null;
|
|
5498
|
-
throw err;
|
|
5499
|
-
}
|
|
5500
|
-
}
|
|
5501
|
-
function assertSlippageBps$1(slippageBps) {
|
|
5502
|
-
if (slippageBps < 0n || slippageBps > BPS_DENOMINATOR$2) throw new PanopticError(`invalid slippageBps ${slippageBps}, must be 0..10000`);
|
|
5503
|
-
}
|
|
5504
|
-
function isRevert$1(err) {
|
|
5505
|
-
return err instanceof BaseError && err.walk((e) => e instanceof ContractFunctionRevertedError) instanceof ContractFunctionRevertedError;
|
|
6077
|
+
[input],
|
|
6078
|
+
deadline
|
|
6079
|
+
],
|
|
6080
|
+
value: 0n
|
|
6081
|
+
};
|
|
5506
6082
|
}
|
|
5507
|
-
|
|
5508
|
-
//#endregion
|
|
5509
|
-
//#region src/panoptic/v2/clients/blockMeta.ts
|
|
5510
6083
|
/**
|
|
5511
|
-
*
|
|
5512
|
-
*
|
|
5513
|
-
*
|
|
5514
|
-
* @param params - The parameters
|
|
5515
|
-
* @returns Block metadata
|
|
6084
|
+
* Build the `execute(commands, inputs, deadline)` calldata for an exact-in
|
|
6085
|
+
* single-hop v3 swap.
|
|
5516
6086
|
*/
|
|
5517
|
-
|
|
5518
|
-
const {
|
|
5519
|
-
const
|
|
5520
|
-
|
|
5521
|
-
|
|
6087
|
+
function buildV3SwapExecuteCalldata(args) {
|
|
6088
|
+
const { args: executeArgs, value } = buildV3SwapExecuteArgs(args);
|
|
6089
|
+
const data = encodeFunctionData({
|
|
6090
|
+
abi: universalRouterAbi,
|
|
6091
|
+
functionName: "execute",
|
|
6092
|
+
args: executeArgs
|
|
5522
6093
|
});
|
|
5523
6094
|
return {
|
|
5524
|
-
|
|
5525
|
-
|
|
5526
|
-
blockTimestamp: block.timestamp
|
|
6095
|
+
data,
|
|
6096
|
+
value
|
|
5527
6097
|
};
|
|
5528
6098
|
}
|
|
5529
|
-
|
|
5530
|
-
//#endregion
|
|
5531
|
-
//#region src/panoptic/v2/abis/stateView.ts
|
|
5532
|
-
/**
|
|
5533
|
-
* Minimal Uniswap V4 StateView ABI for fee growth reads.
|
|
5534
|
-
* Only includes functions needed by streamia history.
|
|
5535
|
-
*/
|
|
5536
|
-
const stateViewAbi = [
|
|
6099
|
+
const v3ExactOutInputAbi = [
|
|
5537
6100
|
{
|
|
5538
|
-
|
|
5539
|
-
|
|
5540
|
-
name: "poolId",
|
|
5541
|
-
type: "bytes32"
|
|
5542
|
-
}],
|
|
5543
|
-
name: "getSlot0",
|
|
5544
|
-
outputs: [
|
|
5545
|
-
{
|
|
5546
|
-
internalType: "uint160",
|
|
5547
|
-
name: "sqrtPriceX96",
|
|
5548
|
-
type: "uint160"
|
|
5549
|
-
},
|
|
5550
|
-
{
|
|
5551
|
-
internalType: "int24",
|
|
5552
|
-
name: "tick",
|
|
5553
|
-
type: "int24"
|
|
5554
|
-
},
|
|
5555
|
-
{
|
|
5556
|
-
internalType: "uint24",
|
|
5557
|
-
name: "protocolFee",
|
|
5558
|
-
type: "uint24"
|
|
5559
|
-
},
|
|
5560
|
-
{
|
|
5561
|
-
internalType: "uint24",
|
|
5562
|
-
name: "lpFee",
|
|
5563
|
-
type: "uint24"
|
|
5564
|
-
}
|
|
5565
|
-
],
|
|
5566
|
-
stateMutability: "view",
|
|
5567
|
-
type: "function"
|
|
6101
|
+
name: "recipient",
|
|
6102
|
+
type: "address"
|
|
5568
6103
|
},
|
|
5569
6104
|
{
|
|
5570
|
-
|
|
5571
|
-
|
|
5572
|
-
name: "poolId",
|
|
5573
|
-
type: "bytes32"
|
|
5574
|
-
}],
|
|
5575
|
-
name: "getFeeGrowthGlobals",
|
|
5576
|
-
outputs: [{
|
|
5577
|
-
internalType: "uint256",
|
|
5578
|
-
name: "feeGrowthGlobal0",
|
|
5579
|
-
type: "uint256"
|
|
5580
|
-
}, {
|
|
5581
|
-
internalType: "uint256",
|
|
5582
|
-
name: "feeGrowthGlobal1",
|
|
5583
|
-
type: "uint256"
|
|
5584
|
-
}],
|
|
5585
|
-
stateMutability: "view",
|
|
5586
|
-
type: "function"
|
|
6105
|
+
name: "amountOut",
|
|
6106
|
+
type: "uint256"
|
|
5587
6107
|
},
|
|
5588
6108
|
{
|
|
5589
|
-
|
|
5590
|
-
|
|
5591
|
-
name: "poolId",
|
|
5592
|
-
type: "bytes32"
|
|
5593
|
-
}, {
|
|
5594
|
-
internalType: "int24",
|
|
5595
|
-
name: "tick",
|
|
5596
|
-
type: "int24"
|
|
5597
|
-
}],
|
|
5598
|
-
name: "getTickInfo",
|
|
5599
|
-
outputs: [
|
|
5600
|
-
{
|
|
5601
|
-
internalType: "uint128",
|
|
5602
|
-
name: "liquidityGross",
|
|
5603
|
-
type: "uint128"
|
|
5604
|
-
},
|
|
5605
|
-
{
|
|
5606
|
-
internalType: "int128",
|
|
5607
|
-
name: "liquidityNet",
|
|
5608
|
-
type: "int128"
|
|
5609
|
-
},
|
|
5610
|
-
{
|
|
5611
|
-
internalType: "uint256",
|
|
5612
|
-
name: "feeGrowthOutside0X128",
|
|
5613
|
-
type: "uint256"
|
|
5614
|
-
},
|
|
5615
|
-
{
|
|
5616
|
-
internalType: "uint256",
|
|
5617
|
-
name: "feeGrowthOutside1X128",
|
|
5618
|
-
type: "uint256"
|
|
5619
|
-
}
|
|
5620
|
-
],
|
|
5621
|
-
stateMutability: "view",
|
|
5622
|
-
type: "function"
|
|
6109
|
+
name: "amountInMaximum",
|
|
6110
|
+
type: "uint256"
|
|
5623
6111
|
},
|
|
5624
6112
|
{
|
|
5625
|
-
|
|
5626
|
-
|
|
5627
|
-
|
|
5628
|
-
|
|
5629
|
-
|
|
5630
|
-
|
|
5631
|
-
outputs: [{
|
|
5632
|
-
internalType: "uint128",
|
|
5633
|
-
name: "liquidity",
|
|
5634
|
-
type: "uint128"
|
|
5635
|
-
}],
|
|
5636
|
-
stateMutability: "view",
|
|
5637
|
-
type: "function"
|
|
6113
|
+
name: "path",
|
|
6114
|
+
type: "bytes"
|
|
6115
|
+
},
|
|
6116
|
+
{
|
|
6117
|
+
name: "payerIsUser",
|
|
6118
|
+
type: "bool"
|
|
5638
6119
|
}
|
|
5639
6120
|
];
|
|
5640
|
-
|
|
5641
|
-
//#endregion
|
|
5642
|
-
//#region src/panoptic/v2/abis/uniswapV3Pool.ts
|
|
5643
6121
|
/**
|
|
5644
|
-
*
|
|
5645
|
-
*
|
|
6122
|
+
* Build the typed `execute(...)` args for an exact-out single-hop v3 swap.
|
|
6123
|
+
* The v3 exact-out path is reversed: `tokenOut ++ fee ++ tokenIn`.
|
|
5646
6124
|
*/
|
|
5647
|
-
|
|
5648
|
-
{
|
|
5649
|
-
|
|
5650
|
-
|
|
5651
|
-
|
|
5652
|
-
|
|
5653
|
-
|
|
5654
|
-
|
|
5655
|
-
|
|
5656
|
-
|
|
5657
|
-
|
|
5658
|
-
|
|
5659
|
-
|
|
5660
|
-
|
|
5661
|
-
|
|
5662
|
-
|
|
5663
|
-
|
|
5664
|
-
|
|
5665
|
-
|
|
5666
|
-
},
|
|
5667
|
-
{
|
|
5668
|
-
internalType: "uint16",
|
|
5669
|
-
name: "observationCardinality",
|
|
5670
|
-
type: "uint16"
|
|
5671
|
-
},
|
|
5672
|
-
{
|
|
5673
|
-
internalType: "uint16",
|
|
5674
|
-
name: "observationCardinalityNext",
|
|
5675
|
-
type: "uint16"
|
|
5676
|
-
},
|
|
5677
|
-
{
|
|
5678
|
-
internalType: "uint8",
|
|
5679
|
-
name: "feeProtocol",
|
|
5680
|
-
type: "uint8"
|
|
5681
|
-
},
|
|
5682
|
-
{
|
|
5683
|
-
internalType: "bool",
|
|
5684
|
-
name: "unlocked",
|
|
5685
|
-
type: "bool"
|
|
5686
|
-
}
|
|
6125
|
+
function buildV3ExactOutSwapExecuteArgs(args) {
|
|
6126
|
+
const { tokenIn, tokenOut, fee, amountOut, amountInMaximum, deadline } = args;
|
|
6127
|
+
if (tokenIn === zeroAddress || tokenOut === zeroAddress) throw new PanopticError("native ETH is not supported by the v3 exact-out router builder");
|
|
6128
|
+
assertUint128$1(amountOut);
|
|
6129
|
+
assertUint128$1(amountInMaximum);
|
|
6130
|
+
const path = encodeV3Path(tokenOut, fee, tokenIn);
|
|
6131
|
+
const input = encodeAbiParameters(v3ExactOutInputAbi, [
|
|
6132
|
+
MSG_SENDER,
|
|
6133
|
+
amountOut,
|
|
6134
|
+
amountInMaximum,
|
|
6135
|
+
path,
|
|
6136
|
+
true
|
|
6137
|
+
]);
|
|
6138
|
+
const commands = encodePacked(["uint8"], [V3_SWAP_EXACT_OUT]);
|
|
6139
|
+
return {
|
|
6140
|
+
args: [
|
|
6141
|
+
commands,
|
|
6142
|
+
[input],
|
|
6143
|
+
deadline
|
|
5687
6144
|
],
|
|
5688
|
-
|
|
5689
|
-
|
|
5690
|
-
|
|
5691
|
-
|
|
5692
|
-
|
|
5693
|
-
|
|
5694
|
-
|
|
5695
|
-
|
|
5696
|
-
|
|
5697
|
-
|
|
5698
|
-
|
|
5699
|
-
|
|
5700
|
-
|
|
5701
|
-
}
|
|
5702
|
-
|
|
5703
|
-
|
|
5704
|
-
|
|
5705
|
-
|
|
5706
|
-
|
|
5707
|
-
|
|
5708
|
-
|
|
5709
|
-
|
|
5710
|
-
|
|
5711
|
-
|
|
5712
|
-
|
|
5713
|
-
|
|
5714
|
-
|
|
5715
|
-
|
|
5716
|
-
|
|
5717
|
-
|
|
5718
|
-
|
|
5719
|
-
|
|
5720
|
-
|
|
6145
|
+
value: 0n
|
|
6146
|
+
};
|
|
6147
|
+
}
|
|
6148
|
+
function buildV3ExactOutSwapExecuteCalldata(args) {
|
|
6149
|
+
const { args: executeArgs, value } = buildV3ExactOutSwapExecuteArgs(args);
|
|
6150
|
+
const data = encodeFunctionData({
|
|
6151
|
+
abi: universalRouterAbi,
|
|
6152
|
+
functionName: "execute",
|
|
6153
|
+
args: executeArgs
|
|
6154
|
+
});
|
|
6155
|
+
return {
|
|
6156
|
+
data,
|
|
6157
|
+
value
|
|
6158
|
+
};
|
|
6159
|
+
}
|
|
6160
|
+
|
|
6161
|
+
//#endregion
|
|
6162
|
+
//#region src/uniswap/v3/abis/quoterV2.ts
|
|
6163
|
+
/**
|
|
6164
|
+
* Minimal Uniswap v3 QuoterV2 ABI.
|
|
6165
|
+
*
|
|
6166
|
+
* QuoterV2 is revert/staticcall-based (nonpayable but intended for `eth_call` /
|
|
6167
|
+
* `simulateContract`), so it must be simulated, not read.
|
|
6168
|
+
* @module uniswap/v3/abis/quoterV2
|
|
6169
|
+
*/
|
|
6170
|
+
const quoterV2Abi = [{
|
|
6171
|
+
type: "function",
|
|
6172
|
+
name: "quoteExactInputSingle",
|
|
6173
|
+
stateMutability: "nonpayable",
|
|
6174
|
+
inputs: [{
|
|
6175
|
+
name: "params",
|
|
6176
|
+
type: "tuple",
|
|
6177
|
+
components: [
|
|
5721
6178
|
{
|
|
5722
|
-
|
|
5723
|
-
|
|
5724
|
-
type: "uint128"
|
|
6179
|
+
name: "tokenIn",
|
|
6180
|
+
type: "address"
|
|
5725
6181
|
},
|
|
5726
6182
|
{
|
|
5727
|
-
|
|
5728
|
-
|
|
5729
|
-
type: "int128"
|
|
6183
|
+
name: "tokenOut",
|
|
6184
|
+
type: "address"
|
|
5730
6185
|
},
|
|
5731
6186
|
{
|
|
5732
|
-
|
|
5733
|
-
name: "feeGrowthOutside0X128",
|
|
6187
|
+
name: "amountIn",
|
|
5734
6188
|
type: "uint256"
|
|
5735
6189
|
},
|
|
5736
6190
|
{
|
|
5737
|
-
|
|
5738
|
-
|
|
5739
|
-
type: "uint256"
|
|
6191
|
+
name: "fee",
|
|
6192
|
+
type: "uint24"
|
|
5740
6193
|
},
|
|
5741
6194
|
{
|
|
5742
|
-
|
|
5743
|
-
|
|
5744
|
-
|
|
6195
|
+
name: "sqrtPriceLimitX96",
|
|
6196
|
+
type: "uint160"
|
|
6197
|
+
}
|
|
6198
|
+
]
|
|
6199
|
+
}],
|
|
6200
|
+
outputs: [
|
|
6201
|
+
{
|
|
6202
|
+
name: "amountOut",
|
|
6203
|
+
type: "uint256"
|
|
6204
|
+
},
|
|
6205
|
+
{
|
|
6206
|
+
name: "sqrtPriceX96After",
|
|
6207
|
+
type: "uint160"
|
|
6208
|
+
},
|
|
6209
|
+
{
|
|
6210
|
+
name: "initializedTicksCrossed",
|
|
6211
|
+
type: "uint32"
|
|
6212
|
+
},
|
|
6213
|
+
{
|
|
6214
|
+
name: "gasEstimate",
|
|
6215
|
+
type: "uint256"
|
|
6216
|
+
}
|
|
6217
|
+
]
|
|
6218
|
+
}, {
|
|
6219
|
+
type: "function",
|
|
6220
|
+
name: "quoteExactOutputSingle",
|
|
6221
|
+
stateMutability: "nonpayable",
|
|
6222
|
+
inputs: [{
|
|
6223
|
+
name: "params",
|
|
6224
|
+
type: "tuple",
|
|
6225
|
+
components: [
|
|
6226
|
+
{
|
|
6227
|
+
name: "tokenIn",
|
|
6228
|
+
type: "address"
|
|
5745
6229
|
},
|
|
5746
6230
|
{
|
|
5747
|
-
|
|
5748
|
-
|
|
5749
|
-
type: "uint160"
|
|
6231
|
+
name: "tokenOut",
|
|
6232
|
+
type: "address"
|
|
5750
6233
|
},
|
|
5751
6234
|
{
|
|
5752
|
-
|
|
5753
|
-
|
|
5754
|
-
type: "uint32"
|
|
6235
|
+
name: "amount",
|
|
6236
|
+
type: "uint256"
|
|
5755
6237
|
},
|
|
5756
6238
|
{
|
|
5757
|
-
|
|
5758
|
-
|
|
5759
|
-
|
|
6239
|
+
name: "fee",
|
|
6240
|
+
type: "uint24"
|
|
6241
|
+
},
|
|
6242
|
+
{
|
|
6243
|
+
name: "sqrtPriceLimitX96",
|
|
6244
|
+
type: "uint160"
|
|
5760
6245
|
}
|
|
5761
|
-
]
|
|
5762
|
-
|
|
5763
|
-
|
|
5764
|
-
|
|
5765
|
-
|
|
5766
|
-
|
|
5767
|
-
|
|
5768
|
-
|
|
5769
|
-
|
|
5770
|
-
|
|
5771
|
-
|
|
5772
|
-
|
|
5773
|
-
|
|
5774
|
-
|
|
5775
|
-
|
|
5776
|
-
|
|
5777
|
-
|
|
5778
|
-
|
|
5779
|
-
|
|
5780
|
-
|
|
5781
|
-
|
|
5782
|
-
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
|
|
5786
|
-
|
|
5787
|
-
|
|
5788
|
-
|
|
5789
|
-
|
|
5790
|
-
|
|
5791
|
-
|
|
5792
|
-
|
|
5793
|
-
|
|
5794
|
-
|
|
5795
|
-
|
|
5796
|
-
|
|
5797
|
-
|
|
5798
|
-
|
|
5799
|
-
|
|
5800
|
-
|
|
5801
|
-
|
|
5802
|
-
|
|
5803
|
-
|
|
5804
|
-
|
|
5805
|
-
|
|
5806
|
-
|
|
5807
|
-
|
|
5808
|
-
|
|
5809
|
-
|
|
5810
|
-
|
|
5811
|
-
|
|
5812
|
-
|
|
5813
|
-
|
|
5814
|
-
|
|
5815
|
-
|
|
5816
|
-
|
|
5817
|
-
|
|
5818
|
-
|
|
6246
|
+
]
|
|
6247
|
+
}],
|
|
6248
|
+
outputs: [
|
|
6249
|
+
{
|
|
6250
|
+
name: "amountIn",
|
|
6251
|
+
type: "uint256"
|
|
6252
|
+
},
|
|
6253
|
+
{
|
|
6254
|
+
name: "sqrtPriceX96After",
|
|
6255
|
+
type: "uint160"
|
|
6256
|
+
},
|
|
6257
|
+
{
|
|
6258
|
+
name: "initializedTicksCrossed",
|
|
6259
|
+
type: "uint32"
|
|
6260
|
+
},
|
|
6261
|
+
{
|
|
6262
|
+
name: "gasEstimate",
|
|
6263
|
+
type: "uint256"
|
|
6264
|
+
}
|
|
6265
|
+
]
|
|
6266
|
+
}];
|
|
6267
|
+
|
|
6268
|
+
//#endregion
|
|
6269
|
+
//#region src/uniswap/v3/router/quote.ts
|
|
6270
|
+
const BPS_DENOMINATOR$2 = 10000n;
|
|
6271
|
+
const UINT128_MAX$3 = (1n << 128n) - 1n;
|
|
6272
|
+
/**
|
|
6273
|
+
* Quote an exact-in v3 swap. Returns `null` when the quote reverts (e.g. the
|
|
6274
|
+
* pool does not exist / has no liquidity) so callers can skip that pool when
|
|
6275
|
+
* ranking venues, rather than aborting the whole cycle.
|
|
6276
|
+
*/
|
|
6277
|
+
async function quoteV3ExactIn(params) {
|
|
6278
|
+
const { client, chainId, tokenIn, tokenOut, fee, amountIn, slippageBps, blockNumber } = params;
|
|
6279
|
+
if (amountIn < 0n || amountIn > UINT128_MAX$3) throw new PanopticError(`amountIn ${amountIn} exceeds uint128 maximum`);
|
|
6280
|
+
assertSlippageBps$1(slippageBps);
|
|
6281
|
+
const { quoterV2 } = getUniswapV3Addresses(chainId, params.addresses);
|
|
6282
|
+
try {
|
|
6283
|
+
const { result } = await client.simulateContract({
|
|
6284
|
+
address: quoterV2,
|
|
6285
|
+
abi: quoterV2Abi,
|
|
6286
|
+
functionName: "quoteExactInputSingle",
|
|
6287
|
+
blockNumber,
|
|
6288
|
+
args: [{
|
|
6289
|
+
tokenIn,
|
|
6290
|
+
tokenOut,
|
|
6291
|
+
amountIn,
|
|
6292
|
+
fee: Number(fee),
|
|
6293
|
+
sqrtPriceLimitX96: 0n
|
|
6294
|
+
}]
|
|
6295
|
+
});
|
|
6296
|
+
const [amountOut, , , gasEstimate] = result;
|
|
6297
|
+
const amountOutMinimum = amountOut * (BPS_DENOMINATOR$2 - slippageBps) / BPS_DENOMINATOR$2;
|
|
6298
|
+
return {
|
|
6299
|
+
amountOut,
|
|
6300
|
+
amountOutMinimum,
|
|
6301
|
+
gasEstimate
|
|
6302
|
+
};
|
|
6303
|
+
} catch (err) {
|
|
6304
|
+
if (isRevert$1(err)) return null;
|
|
6305
|
+
throw err;
|
|
5819
6306
|
}
|
|
5820
|
-
|
|
6307
|
+
}
|
|
6308
|
+
/**
|
|
6309
|
+
* Quote an exact-out v3 swap. Returns `null` on revert (no pool / no liquidity).
|
|
6310
|
+
*/
|
|
6311
|
+
async function quoteV3ExactOut(params) {
|
|
6312
|
+
const { client, chainId, tokenIn, tokenOut, fee, amountOut, slippageBps, blockNumber } = params;
|
|
6313
|
+
if (amountOut < 0n || amountOut > UINT128_MAX$3) throw new PanopticError(`amountOut ${amountOut} exceeds uint128 maximum`);
|
|
6314
|
+
assertSlippageBps$1(slippageBps);
|
|
6315
|
+
const { quoterV2 } = getUniswapV3Addresses(chainId, params.addresses);
|
|
6316
|
+
try {
|
|
6317
|
+
const { result } = await client.simulateContract({
|
|
6318
|
+
address: quoterV2,
|
|
6319
|
+
abi: quoterV2Abi,
|
|
6320
|
+
functionName: "quoteExactOutputSingle",
|
|
6321
|
+
blockNumber,
|
|
6322
|
+
args: [{
|
|
6323
|
+
tokenIn,
|
|
6324
|
+
tokenOut,
|
|
6325
|
+
amount: amountOut,
|
|
6326
|
+
fee: Number(fee),
|
|
6327
|
+
sqrtPriceLimitX96: 0n
|
|
6328
|
+
}]
|
|
6329
|
+
});
|
|
6330
|
+
const [amountIn, , , gasEstimate] = result;
|
|
6331
|
+
const amountInMaximum = (amountIn * (BPS_DENOMINATOR$2 + slippageBps) + BPS_DENOMINATOR$2 - 1n) / BPS_DENOMINATOR$2;
|
|
6332
|
+
return {
|
|
6333
|
+
amountIn,
|
|
6334
|
+
amountInMaximum,
|
|
6335
|
+
gasEstimate
|
|
6336
|
+
};
|
|
6337
|
+
} catch (err) {
|
|
6338
|
+
if (isRevert$1(err)) return null;
|
|
6339
|
+
throw err;
|
|
6340
|
+
}
|
|
6341
|
+
}
|
|
6342
|
+
function assertSlippageBps$1(slippageBps) {
|
|
6343
|
+
if (slippageBps < 0n || slippageBps > BPS_DENOMINATOR$2) throw new PanopticError(`invalid slippageBps ${slippageBps}, must be 0..10000`);
|
|
6344
|
+
}
|
|
6345
|
+
function isRevert$1(err) {
|
|
6346
|
+
return err instanceof BaseError && err.walk((e) => e instanceof ContractFunctionRevertedError) instanceof ContractFunctionRevertedError;
|
|
6347
|
+
}
|
|
5821
6348
|
|
|
5822
6349
|
//#endregion
|
|
5823
6350
|
//#region src/panoptic/v2/utils/oraclePack.ts
|
|
@@ -7792,5 +8319,5 @@ async function swapExactOutViaRouterAndWait(params) {
|
|
|
7792
8319
|
}
|
|
7793
8320
|
|
|
7794
8321
|
//#endregion
|
|
7795
|
-
export { AmountExceedsUint128Error, InvalidSwapTokenError, LP_RANGE_HORIZONS_DAYS, LP_RANGE_HORIZONS_YEARS, MSG_SENDER, MissingSweepRecipientError, PERMIT2_ADDRESS, QuoterUnavailableError, SETTLE_ALL, SWAP_EXACT_IN_SINGLE, SWAP_EXACT_OUT_SINGLE, SWEEP, TAKE_ALL, UNISWAP_V3_ADDRESSES, UNISWAP_V4_ADDRESSES, UnsupportedChainError, V3_SWAP_EXACT_IN, V3_SWAP_EXACT_OUT, V4_SWAP, annualizeLpFeeReturn, approveErc20ForPermit2, approveErc20ForPermit2AndWait, approveRouterViaPermit2, approveRouterViaPermit2AndWait, buildV3ExactOutSwapExecuteArgs, buildV3ExactOutSwapExecuteCalldata, buildV3SwapExecuteArgs, buildV3SwapExecuteCalldata, buildV4ExactOutSwapExecuteArgs, buildV4ExactOutSwapExecuteCalldata, buildV4SwapExecuteArgs, buildV4SwapExecuteCalldata, calculateLpPositionVolatility, checkRouterApproval, encodeV3Path, estimateLpFees, expectedTimeInRange, getAmountsForLiquidity, getLpDepositAmounts, getLpDepositBreakdown, getLpFeeReturnSeries, getLpFundingDeposit, getLpGreeks, getLpPositionFunding, getMaxLpPositionSize, getUnhedgedLpRanges, getUniswapV3Addresses, getUniswapV3PoolBalances, getUniswapV4Addresses, normalCdf, pInRangeAt, quoteSwapExactInViaRouter, quoteSwapExactInViaV3Router, quoteSwapExactOutViaRouter, quoteSwapExactOutViaV3Router, quoteTokenPrices, quoteV3ExactIn, quoteV3ExactOut, quoteV4ExactInByPoolKey, rangeStats, resolveSwapRoute, resolveV3SwapRoute, swapExactInViaRouter, swapExactInViaRouterAndWait, swapExactInViaV3Router, swapExactOutViaRouter, swapExactOutViaRouterAndWait, swapExactOutViaV3Router };
|
|
8322
|
+
export { AmountExceedsUint128Error, InvalidSwapTokenError, LP_RANGE_HORIZONS_DAYS, LP_RANGE_HORIZONS_YEARS, MSG_SENDER, MissingSweepRecipientError, PERMIT2_ADDRESS, QuoterUnavailableError, SETTLE_ALL, SWAP_EXACT_IN_SINGLE, SWAP_EXACT_OUT_SINGLE, SWEEP, TAKE_ALL, UNISWAP_V3_ADDRESSES, UNISWAP_V4_ADDRESSES, UnsupportedChainError, V3_SWAP_EXACT_IN, V3_SWAP_EXACT_OUT, V4_SWAP, annualizeLpFeeReturn, approveErc20ForPermit2, approveErc20ForPermit2AndWait, approveRouterViaPermit2, approveRouterViaPermit2AndWait, buildV3ExactOutSwapExecuteArgs, buildV3ExactOutSwapExecuteCalldata, buildV3SwapExecuteArgs, buildV3SwapExecuteCalldata, buildV4ExactOutSwapExecuteArgs, buildV4ExactOutSwapExecuteCalldata, buildV4SwapExecuteArgs, buildV4SwapExecuteCalldata, calculateLpPositionVolatility, calculateV3LpAccounting, checkRouterApproval, encodeV3Path, estimateLpFees, expectedTimeInRange, getAmountsForLiquidity, getLpDepositAmounts, getLpDepositBreakdown, getLpFeeReturnSeries, getLpFundingDeposit, getLpGreeks, getLpPositionFunding, getLpPositionHistory, getMaxLpPositionSize, getUnhedgedLpRanges, getUniswapV3Addresses, getUniswapV3PoolBalances, getUniswapV4Addresses, getV3LpHistoryAccounting, normalCdf, pInRangeAt, quoteSwapExactInViaRouter, quoteSwapExactInViaV3Router, quoteSwapExactOutViaRouter, quoteSwapExactOutViaV3Router, quoteTokenPrices, quoteV3ExactIn, quoteV3ExactOut, quoteV4ExactInByPoolKey, rangeStats, resolveSwapRoute, resolveV3SwapRoute, summarizeLpPositionHistory, swapExactInViaRouter, swapExactInViaRouterAndWait, swapExactInViaV3Router, swapExactOutViaRouter, swapExactOutViaRouterAndWait, swapExactOutViaV3Router };
|
|
7796
8323
|
//# sourceMappingURL=index.js.map
|