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