@strobelabs/perpcity-sdk 0.5.0 → 0.6.0

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/index.js CHANGED
@@ -1,95 +1,3 @@
1
- "use strict";
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __export = (target, all) => {
9
- for (var name in all)
10
- __defProp(target, name, { get: all[name], enumerable: true });
11
- };
12
- var __copyProps = (to, from, except, desc) => {
13
- if (from && typeof from === "object" || typeof from === "function") {
14
- for (let key of __getOwnPropNames(from))
15
- if (!__hasOwnProp.call(to, key) && key !== except)
16
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
- }
18
- return to;
19
- };
20
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
- // If the importer is in node compatibility mode or this is not an ESM
22
- // file that has been converted to a CommonJS file using a Babel-
23
- // compatible transform (i.e. "__esModule" has not been set), then set
24
- // "default" to the CommonJS "module.exports" for node compatibility.
25
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
- mod
27
- ));
28
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
-
30
- // src/index.ts
31
- var index_exports = {};
32
- __export(index_exports, {
33
- BEACON_ABI: () => BEACON_ABI,
34
- BIGINT_1E6: () => BIGINT_1E6,
35
- ContractError: () => ContractError,
36
- ErrorCategory: () => ErrorCategory,
37
- ErrorSource: () => ErrorSource,
38
- InsufficientFundsError: () => InsufficientFundsError,
39
- NUMBER_1E6: () => NUMBER_1E6,
40
- OpenPosition: () => OpenPosition,
41
- PERP_MANAGER_ABI: () => PERP_MANAGER_ABI,
42
- PerpCityContext: () => PerpCityContext,
43
- PerpCityError: () => PerpCityError,
44
- Q96: () => Q96,
45
- RPCError: () => RPCError,
46
- TransactionRejectedError: () => TransactionRejectedError,
47
- ValidationError: () => ValidationError,
48
- approveUsdc: () => approveUsdc,
49
- calculateEntryPrice: () => calculateEntryPrice,
50
- calculateLeverage: () => calculateLeverage,
51
- calculateLiquidationPrice: () => calculateLiquidationPrice,
52
- calculateLiquidityForTargetRatio: () => calculateLiquidityForTargetRatio,
53
- calculatePositionSize: () => calculatePositionSize,
54
- calculatePositionValue: () => calculatePositionValue,
55
- closePosition: () => closePosition,
56
- createPerp: () => createPerp,
57
- estimateLiquidity: () => estimateLiquidity,
58
- getPerpBeacon: () => getPerpBeacon,
59
- getPerpBounds: () => getPerpBounds,
60
- getPerpFees: () => getPerpFees,
61
- getPerpMark: () => getPerpMark,
62
- getPerpTickSpacing: () => getPerpTickSpacing,
63
- getPositionEffectiveMargin: () => getPositionEffectiveMargin,
64
- getPositionFundingPayment: () => getPositionFundingPayment,
65
- getPositionId: () => getPositionId,
66
- getPositionIsLiquidatable: () => getPositionIsLiquidatable,
67
- getPositionIsLong: () => getPositionIsLong,
68
- getPositionIsMaker: () => getPositionIsMaker,
69
- getPositionLiveDetails: () => getPositionLiveDetails,
70
- getPositionLiveDetailsFromContract: () => getPositionLiveDetailsFromContract,
71
- getPositionPerpId: () => getPositionPerpId,
72
- getPositionPnl: () => getPositionPnl,
73
- getRpcUrl: () => getRpcUrl,
74
- getUserOpenPositions: () => getUserOpenPositions,
75
- getUserUsdcBalance: () => getUserUsdcBalance,
76
- getUserWalletAddress: () => getUserWalletAddress,
77
- marginRatioToLeverage: () => marginRatioToLeverage,
78
- openMakerPosition: () => openMakerPosition,
79
- openTakerPosition: () => openTakerPosition,
80
- parseContractError: () => parseContractError,
81
- priceToSqrtPriceX96: () => priceToSqrtPriceX96,
82
- priceToTick: () => priceToTick,
83
- scale6Decimals: () => scale6Decimals,
84
- scaleFrom6Decimals: () => scaleFrom6Decimals,
85
- scaleFromX96: () => scaleFromX96,
86
- scaleToX96: () => scaleToX96,
87
- sqrtPriceX96ToPrice: () => sqrtPriceX96ToPrice,
88
- tickToPrice: () => tickToPrice,
89
- withErrorHandling: () => withErrorHandling
90
- });
91
- module.exports = __toCommonJS(index_exports);
92
-
93
1
  // src/abis/beacon.ts
94
2
  var BEACON_ABI = [
95
3
  {
@@ -1869,19 +1777,14 @@ var PERP_MANAGER_ABI = [
1869
1777
  type: "int256"
1870
1778
  },
1871
1779
  {
1872
- internalType: "int256",
1780
+ internalType: "uint256",
1873
1781
  name: "netMargin",
1874
- type: "int256"
1782
+ type: "uint256"
1875
1783
  },
1876
1784
  {
1877
1785
  internalType: "bool",
1878
1786
  name: "wasLiquidated",
1879
1787
  type: "bool"
1880
- },
1881
- {
1882
- internalType: "uint256",
1883
- name: "notional",
1884
- type: "uint256"
1885
1788
  }
1886
1789
  ],
1887
1790
  stateMutability: "nonpayable",
@@ -2358,8 +2261,13 @@ var PERP_MANAGER_ABI = [
2358
2261
  ];
2359
2262
 
2360
2263
  // src/context.ts
2361
- var import_ttlcache = __toESM(require("@isaacs/ttlcache"));
2362
- var import_viem3 = require("viem");
2264
+ import TTLCache from "@isaacs/ttlcache";
2265
+ import {
2266
+ createPublicClient,
2267
+ erc20Abi as erc20Abi2,
2268
+ formatUnits,
2269
+ http
2270
+ } from "viem";
2363
2271
 
2364
2272
  // src/abis/fees.ts
2365
2273
  var FEES_ABI = [
@@ -2440,13 +2348,22 @@ var MARGIN_RATIOS_ABI = [
2440
2348
  ];
2441
2349
 
2442
2350
  // src/utils/approve.ts
2443
- var import_viem = require("viem");
2351
+ import { erc20Abi } from "viem";
2444
2352
  var DEFAULT_CONFIRMATIONS = 2;
2353
+ async function getUsdcAllowance(context, owner) {
2354
+ const deployments = context.deployments();
2355
+ return context.publicClient.readContract({
2356
+ address: deployments.usdc,
2357
+ abi: erc20Abi,
2358
+ functionName: "allowance",
2359
+ args: [owner, deployments.perpManager]
2360
+ });
2361
+ }
2445
2362
  async function approveUsdc(context, amount, confirmations = DEFAULT_CONFIRMATIONS) {
2446
2363
  const deployments = context.deployments();
2447
2364
  const { request } = await context.publicClient.simulateContract({
2448
2365
  address: deployments.usdc,
2449
- abi: import_viem.erc20Abi,
2366
+ abi: erc20Abi,
2450
2367
  functionName: "approve",
2451
2368
  args: [deployments.perpManager, amount],
2452
2369
  account: context.walletClient.account
@@ -2462,6 +2379,12 @@ async function approveUsdc(context, amount, confirmations = DEFAULT_CONFIRMATION
2462
2379
  var NUMBER_1E6 = 1e6;
2463
2380
  var BIGINT_1E6 = 1000000n;
2464
2381
  var Q96 = 79228162514264337593543950336n;
2382
+ var MIN_TICK = -887272;
2383
+ var MAX_TICK = 887272;
2384
+ var MIN_PRICE = 1e-6;
2385
+ var MAX_PRICE = 1e6;
2386
+ var UINT256_MAX = (1n << 256n) - 1n;
2387
+ var INT256_THRESHOLD = 1n << 255n;
2465
2388
 
2466
2389
  // src/utils/conversions.ts
2467
2390
  function priceToSqrtPriceX96(price) {
@@ -2494,12 +2417,23 @@ function priceToTick(price, roundDown) {
2494
2417
  if (price <= 0) {
2495
2418
  throw new Error("Price must be positive");
2496
2419
  }
2420
+ if (price < MIN_PRICE || price > MAX_PRICE) {
2421
+ throw new Error(
2422
+ `Price ${price} is outside the representable range [${MIN_PRICE}, ${MAX_PRICE}]`
2423
+ );
2424
+ }
2497
2425
  const logPrice = Math.log(price) / Math.log(1.0001);
2498
2426
  return roundDown ? Math.floor(logPrice) : Math.ceil(logPrice);
2499
2427
  }
2500
2428
  function tickToPrice(tick) {
2501
2429
  return 1.0001 ** tick;
2502
2430
  }
2431
+ function uint256ToInt256(value) {
2432
+ if (value >= INT256_THRESHOLD) {
2433
+ return value - UINT256_MAX - 1n;
2434
+ }
2435
+ return value;
2436
+ }
2503
2437
  function sqrtPriceX96ToPrice(sqrtPriceX96) {
2504
2438
  const priceX96 = sqrtPriceX96 * sqrtPriceX96 / Q96;
2505
2439
  return scaleFromX96(priceX96);
@@ -2515,7 +2449,7 @@ function scaleFrom6Decimals(value) {
2515
2449
  }
2516
2450
 
2517
2451
  // src/utils/errors.ts
2518
- var import_viem2 = require("viem");
2452
+ import { BaseError, ContractFunctionRevertedError } from "viem";
2519
2453
  var ErrorCategory = /* @__PURE__ */ ((ErrorCategory2) => {
2520
2454
  ErrorCategory2["USER_ERROR"] = "USER_ERROR";
2521
2455
  ErrorCategory2["STATE_ERROR"] = "STATE_ERROR";
@@ -2573,9 +2507,9 @@ function parseContractError(error) {
2573
2507
  if (error instanceof PerpCityError) {
2574
2508
  return error;
2575
2509
  }
2576
- if (error instanceof import_viem2.BaseError) {
2577
- const revertError = error.walk((err) => err instanceof import_viem2.ContractFunctionRevertedError);
2578
- if (revertError instanceof import_viem2.ContractFunctionRevertedError) {
2510
+ if (error instanceof BaseError) {
2511
+ const revertError = error.walk((err) => err instanceof ContractFunctionRevertedError);
2512
+ if (revertError instanceof ContractFunctionRevertedError) {
2579
2513
  const errorName = revertError.data?.errorName ?? "Unknown";
2580
2514
  const args = revertError.data?.args ?? [];
2581
2515
  const { message, debug } = formatContractError(errorName, args);
@@ -2740,7 +2674,7 @@ function formatContractError(errorName, args) {
2740
2674
  };
2741
2675
  case "MinimumAmountInsufficient":
2742
2676
  return {
2743
- message: `Minimum amount not met. Required: ${args[0]}, Received: ${args[1]}`,
2677
+ message: "Slippage tolerance exceeded. The position's value moved unfavorably during execution. Try increasing your slippage tolerance.",
2744
2678
  debug: { source, category: "USER_ERROR" /* USER_ERROR */ }
2745
2679
  };
2746
2680
  case "PriceImpactTooHigh":
@@ -3019,6 +2953,23 @@ async function withErrorHandling(fn, context) {
3019
2953
  }
3020
2954
  }
3021
2955
 
2956
+ // src/utils/funding.ts
2957
+ var PRECISION = 10n ** 18n;
2958
+ function convertFundingDiffX96ToPercentPerPeriod(fundingDiffX96, interval, periodSeconds) {
2959
+ const scaledRate = fundingDiffX96 * periodSeconds * PRECISION / (Q96 * interval);
2960
+ return Number(scaledRate) / Number(PRECISION);
2961
+ }
2962
+ function convertFundingPerSecondX96ToPercentPerMinute(fundingPerSecondX96) {
2963
+ const SECONDS_PER_MINUTE = 60n;
2964
+ const scaledRate = fundingPerSecondX96 * SECONDS_PER_MINUTE * PRECISION / Q96;
2965
+ return Number(scaledRate) / Number(PRECISION);
2966
+ }
2967
+ function convertFundingPerSecondX96ToPercentPerDay(fundingPerSecondX96) {
2968
+ const SECONDS_PER_DAY = 86400n;
2969
+ const scaledRate = fundingPerSecondX96 * SECONDS_PER_DAY * PRECISION / Q96;
2970
+ return Number(scaledRate) / Number(PRECISION);
2971
+ }
2972
+
3022
2973
  // src/utils/liquidity.ts
3023
2974
  async function estimateLiquidity(_context, tickLower, tickUpper, usdScaled) {
3024
2975
  if (tickLower >= tickUpper) {
@@ -3121,16 +3072,16 @@ function getRpcUrl(config = {}) {
3121
3072
  // src/context.ts
3122
3073
  var PerpCityContext = class {
3123
3074
  constructor(config) {
3124
- this.configCache = new import_ttlcache.default({ ttl: 5 * 60 * 1e3 });
3075
+ this.configCache = new TTLCache({ ttl: 5 * 60 * 1e3 });
3125
3076
  this.walletClient = config.walletClient;
3126
3077
  if (!config.walletClient.chain?.id) {
3127
3078
  throw new Error(
3128
3079
  "PerpCityContext: walletClient.chain must be defined with a numeric id. Ensure your walletClient was created with a chain parameter."
3129
3080
  );
3130
3081
  }
3131
- this.publicClient = (0, import_viem3.createPublicClient)({
3082
+ this.publicClient = createPublicClient({
3132
3083
  chain: config.walletClient.chain,
3133
- transport: (0, import_viem3.http)(config.rpcUrl, { batch: true })
3084
+ transport: http(config.rpcUrl, { batch: true })
3134
3085
  });
3135
3086
  this._deployments = config.deployments;
3136
3087
  }
@@ -3212,8 +3163,7 @@ var PerpCityContext = class {
3212
3163
  const tickSpacing = Number(cfg.key.tickSpacing);
3213
3164
  let sqrtPriceX96;
3214
3165
  if (markPrice) {
3215
- const sqrtPrice = Math.sqrt(markPrice);
3216
- sqrtPriceX96 = BigInt(Math.floor(sqrtPrice * 2 ** 96));
3166
+ sqrtPriceX96 = priceToSqrtPriceX96(markPrice);
3217
3167
  } else {
3218
3168
  sqrtPriceX96 = await this.publicClient.readContract({
3219
3169
  address: this.deployments().perpManager,
@@ -3298,7 +3248,7 @@ var PerpCityContext = class {
3298
3248
  async fetchUserData(userAddress, positions) {
3299
3249
  const usdcBalance = await this.publicClient.readContract({
3300
3250
  address: this.deployments().usdc,
3301
- abi: import_viem3.erc20Abi,
3251
+ abi: erc20Abi2,
3302
3252
  functionName: "balanceOf",
3303
3253
  args: [userAddress]
3304
3254
  });
@@ -3316,7 +3266,7 @@ var PerpCityContext = class {
3316
3266
  );
3317
3267
  return {
3318
3268
  walletAddress: userAddress,
3319
- usdcBalance: Number((0, import_viem3.formatUnits)(usdcBalance, 6)),
3269
+ usdcBalance: Number(formatUnits(usdcBalance, 6)),
3320
3270
  openPositions: openPositionsData
3321
3271
  };
3322
3272
  }
@@ -3335,9 +3285,10 @@ var PerpCityContext = class {
3335
3285
  );
3336
3286
  }
3337
3287
  return {
3338
- pnl: Number((0, import_viem3.formatUnits)(pnl, 6)),
3339
- fundingPayment: Number((0, import_viem3.formatUnits)(funding, 6)),
3340
- effectiveMargin: Number((0, import_viem3.formatUnits)(netMargin, 6)),
3288
+ pnl: Number(formatUnits(pnl, 6)),
3289
+ // Negate so positive = user receives funding
3290
+ fundingPayment: -Number(formatUnits(funding, 6)),
3291
+ effectiveMargin: Number(formatUnits(uint256ToInt256(netMargin), 6)),
3341
3292
  isLiquidatable: wasLiquidated
3342
3293
  };
3343
3294
  }, `fetchPositionLiveDetailsFromContract for position ${positionId}`);
@@ -3381,29 +3332,35 @@ var PerpCityContext = class {
3381
3332
  args: [positionId]
3382
3333
  });
3383
3334
  const resultArray = result;
3384
- const [perpId, margin, entryPerpDelta, entryUsdDelta, , , , marginRatios] = resultArray;
3335
+ const [perpId, margin, entryPerpDelta, entryUsdDelta, , , , marginRatios, makerDetailsRaw] = resultArray;
3385
3336
  const zeroPerpId = `0x${"0".repeat(64)}`;
3386
3337
  if (perpId === zeroPerpId) {
3387
3338
  throw new Error(`Position ${positionId} does not exist`);
3388
3339
  }
3340
+ const isMaker = makerDetailsRaw != null && (makerDetailsRaw.tickLower !== 0 || makerDetailsRaw.tickUpper !== 0);
3389
3341
  return {
3390
3342
  perpId,
3391
3343
  positionId,
3392
- margin: Number((0, import_viem3.formatUnits)(margin, 6)),
3344
+ margin: Number(formatUnits(margin, 6)),
3393
3345
  entryPerpDelta,
3394
3346
  entryUsdDelta,
3395
3347
  marginRatios: {
3396
3348
  min: Number(marginRatios.min),
3397
3349
  max: Number(marginRatios.max),
3398
3350
  liq: Number(marginRatios.liq)
3399
- }
3351
+ },
3352
+ makerDetails: isMaker ? {
3353
+ unlockTimestamp: Number(makerDetailsRaw.unlockTimestamp),
3354
+ tickLower: makerDetailsRaw.tickLower,
3355
+ tickUpper: makerDetailsRaw.tickUpper
3356
+ } : null
3400
3357
  };
3401
3358
  }, `getPositionRawData for position ${positionId}`);
3402
3359
  }
3403
3360
  };
3404
3361
 
3405
3362
  // src/functions/open-position.ts
3406
- var import_viem4 = require("viem");
3363
+ import { decodeEventLog, formatUnits as formatUnits2 } from "viem";
3407
3364
  var OpenPosition = class _OpenPosition {
3408
3365
  constructor(context, perpId, positionId, isLong, isMaker, txHash) {
3409
3366
  this.context = context;
@@ -3440,7 +3397,7 @@ var OpenPosition = class _OpenPosition {
3440
3397
  let _wasFullyClosed = false;
3441
3398
  for (const log of receipt.logs) {
3442
3399
  try {
3443
- const openedDecoded = (0, import_viem4.decodeEventLog)({
3400
+ const openedDecoded = decodeEventLog({
3444
3401
  abi: PERP_MANAGER_ABI,
3445
3402
  data: log.data,
3446
3403
  topics: log.topics,
@@ -3454,7 +3411,7 @@ var OpenPosition = class _OpenPosition {
3454
3411
  }
3455
3412
  } catch (_e) {
3456
3413
  try {
3457
- const closedDecoded = (0, import_viem4.decodeEventLog)({
3414
+ const closedDecoded = decodeEventLog({
3458
3415
  abi: PERP_MANAGER_ABI,
3459
3416
  data: log.data,
3460
3417
  topics: log.topics,
@@ -3502,9 +3459,9 @@ var OpenPosition = class _OpenPosition {
3502
3459
  );
3503
3460
  }
3504
3461
  return {
3505
- pnl: Number((0, import_viem4.formatUnits)(pnl, 6)),
3506
- fundingPayment: Number((0, import_viem4.formatUnits)(funding, 6)),
3507
- effectiveMargin: Number((0, import_viem4.formatUnits)(netMargin, 6)),
3462
+ pnl: Number(formatUnits2(pnl, 6)),
3463
+ fundingPayment: Number(formatUnits2(funding, 6)),
3464
+ effectiveMargin: Number(formatUnits2(uint256ToInt256(netMargin), 6)),
3508
3465
  isLiquidatable: wasLiquidated
3509
3466
  };
3510
3467
  }, `liveDetails for position ${this.positionId}`);
@@ -3512,6 +3469,8 @@ var OpenPosition = class _OpenPosition {
3512
3469
  };
3513
3470
 
3514
3471
  // src/functions/perp.ts
3472
+ var TWAVG_WINDOW = 3600;
3473
+ var INTERVAL = 86400n;
3515
3474
  function getPerpMark(perpData) {
3516
3475
  return perpData.mark;
3517
3476
  }
@@ -3527,9 +3486,58 @@ function getPerpFees(perpData) {
3527
3486
  function getPerpTickSpacing(perpData) {
3528
3487
  return perpData.tickSpacing;
3529
3488
  }
3489
+ async function getFundingRate(context, perpId) {
3490
+ return withErrorHandling(async () => {
3491
+ const perpManagerAddr = context.deployments().perpManager;
3492
+ const cfg = await context.getPerpConfig(perpId);
3493
+ const [twAvgSqrtMarkX96, twAvgIndexX96] = await Promise.all([
3494
+ context.publicClient.readContract({
3495
+ address: perpManagerAddr,
3496
+ abi: PERP_MANAGER_ABI,
3497
+ functionName: "timeWeightedAvgSqrtPriceX96",
3498
+ args: [perpId, TWAVG_WINDOW]
3499
+ }),
3500
+ context.publicClient.readContract({
3501
+ address: cfg.beacon,
3502
+ abi: BEACON_ABI,
3503
+ functionName: "twAvg",
3504
+ args: [TWAVG_WINDOW]
3505
+ })
3506
+ ]);
3507
+ const twAvgMarkX96 = twAvgSqrtMarkX96 * twAvgSqrtMarkX96 / Q96;
3508
+ const fundingDiffX96 = twAvgMarkX96 - twAvgIndexX96;
3509
+ const fundingPerSecondX96 = fundingDiffX96 / INTERVAL;
3510
+ return {
3511
+ ratePerDay: convertFundingDiffX96ToPercentPerPeriod(fundingDiffX96, INTERVAL, 86400n),
3512
+ ratePerMinute: convertFundingDiffX96ToPercentPerPeriod(fundingDiffX96, INTERVAL, 60n),
3513
+ rawX96: fundingPerSecondX96
3514
+ };
3515
+ }, `getFundingRate for perp ${perpId}`);
3516
+ }
3517
+ async function getIndexValue(context, perpId) {
3518
+ return withErrorHandling(async () => {
3519
+ const perpData = await context.getPerpData(perpId);
3520
+ return await context.publicClient.readContract({
3521
+ address: perpData.beacon,
3522
+ abi: BEACON_ABI,
3523
+ functionName: "index"
3524
+ });
3525
+ }, `getIndexValue for perp ${perpId}`);
3526
+ }
3527
+ async function getIndexTWAP(context, perpId, secondsAgo) {
3528
+ return withErrorHandling(async () => {
3529
+ const perpData = await context.getPerpData(perpId);
3530
+ return await context.publicClient.readContract({
3531
+ address: perpData.beacon,
3532
+ abi: BEACON_ABI,
3533
+ functionName: "twAvg",
3534
+ args: [secondsAgo]
3535
+ });
3536
+ }, `getIndexTWAP for perp ${perpId}`);
3537
+ }
3530
3538
 
3531
3539
  // src/functions/perp-manager.ts
3532
- var import_viem5 = require("viem");
3540
+ import { decodeErrorResult, decodeEventLog as decodeEventLog2, erc20Abi as erc20Abi3 } from "viem";
3533
3541
  async function createPerp(context, params) {
3534
3542
  return withErrorHandling(async () => {
3535
3543
  const deployments = context.deployments();
@@ -3555,13 +3563,15 @@ async function createPerp(context, params) {
3555
3563
  account: context.walletClient.account
3556
3564
  });
3557
3565
  const txHash = await context.walletClient.writeContract(request);
3558
- const receipt = await context.publicClient.waitForTransactionReceipt({ hash: txHash });
3566
+ const receipt = await context.publicClient.waitForTransactionReceipt({
3567
+ hash: txHash
3568
+ });
3559
3569
  if (receipt.status === "reverted") {
3560
3570
  throw new Error(`Transaction reverted. Hash: ${txHash}`);
3561
3571
  }
3562
3572
  for (const log of receipt.logs) {
3563
3573
  try {
3564
- const decoded = (0, import_viem5.decodeEventLog)({
3574
+ const decoded = decodeEventLog2({
3565
3575
  abi: PERP_MANAGER_ABI,
3566
3576
  data: log.data,
3567
3577
  topics: log.topics,
@@ -3598,7 +3608,7 @@ async function openTakerPosition(context, perpId, params) {
3598
3608
  const requiredAmount = marginScaled + totalFees;
3599
3609
  const currentAllowance = await context.publicClient.readContract({
3600
3610
  address: context.deployments().usdc,
3601
- abi: import_viem5.erc20Abi,
3611
+ abi: erc20Abi3,
3602
3612
  functionName: "allowance",
3603
3613
  args: [context.walletClient.account.address, context.deployments().perpManager],
3604
3614
  blockTag: "latest"
@@ -3622,14 +3632,16 @@ async function openTakerPosition(context, perpId, params) {
3622
3632
  account: context.walletClient.account
3623
3633
  });
3624
3634
  const txHash = await context.walletClient.writeContract(request);
3625
- const receipt = await context.publicClient.waitForTransactionReceipt({ hash: txHash });
3635
+ const receipt = await context.publicClient.waitForTransactionReceipt({
3636
+ hash: txHash
3637
+ });
3626
3638
  if (receipt.status === "reverted") {
3627
3639
  throw new Error(`Transaction reverted. Hash: ${txHash}`);
3628
3640
  }
3629
3641
  let takerPosId = null;
3630
3642
  for (const log of receipt.logs) {
3631
3643
  try {
3632
- const decoded = (0, import_viem5.decodeEventLog)({
3644
+ const decoded = decodeEventLog2({
3633
3645
  abi: PERP_MANAGER_ABI,
3634
3646
  data: log.data,
3635
3647
  topics: log.topics
@@ -3647,6 +3659,88 @@ async function openTakerPosition(context, perpId, params) {
3647
3659
  return new OpenPosition(context, perpId, takerPosId, params.isLong, false, txHash);
3648
3660
  }, "openTakerPosition");
3649
3661
  }
3662
+ function buildMakerContractParams(context, marginScaled, params, alignedTickLower, alignedTickUpper, maxAmt0In, maxAmt1In) {
3663
+ return {
3664
+ holder: context.walletClient.account.address,
3665
+ margin: marginScaled,
3666
+ liquidity: params.liquidity,
3667
+ tickLower: alignedTickLower,
3668
+ tickUpper: alignedTickUpper,
3669
+ maxAmt0In,
3670
+ maxAmt1In
3671
+ };
3672
+ }
3673
+ function calculateAlignedTicks(priceLower, priceUpper, tickSpacing) {
3674
+ const tickLower = priceToTick(priceLower, true);
3675
+ const tickUpper = priceToTick(priceUpper, false);
3676
+ const alignedTickLower = Math.floor(tickLower / tickSpacing) * tickSpacing;
3677
+ const alignedTickUpper = Math.ceil(tickUpper / tickSpacing) * tickSpacing;
3678
+ if (alignedTickLower < MIN_TICK) {
3679
+ throw new Error(
3680
+ `Lower tick ${alignedTickLower} is below MIN_TICK (${MIN_TICK}). Increase priceLower.`
3681
+ );
3682
+ }
3683
+ if (alignedTickUpper > MAX_TICK) {
3684
+ throw new Error(
3685
+ `Upper tick ${alignedTickUpper} exceeds MAX_TICK (${MAX_TICK}). Decrease priceUpper.`
3686
+ );
3687
+ }
3688
+ if (alignedTickLower === alignedTickUpper) {
3689
+ throw new Error(
3690
+ "Price range too narrow: lower and upper ticks are equal after alignment. Widen the range."
3691
+ );
3692
+ }
3693
+ return { alignedTickLower, alignedTickUpper };
3694
+ }
3695
+ function alignMakerTicks(params, tickSpacing) {
3696
+ return calculateAlignedTicks(params.priceLower, params.priceUpper, tickSpacing);
3697
+ }
3698
+ var DEFAULT_MAKER_SLIPPAGE_TOLERANCE = 0.01;
3699
+ var MAX_UINT128 = 2n ** 128n - 1n;
3700
+ async function quoteOpenMakerPosition(context, perpId, params) {
3701
+ return withErrorHandling(async () => {
3702
+ if (params.margin <= 0) {
3703
+ throw new Error("Margin must be greater than 0");
3704
+ }
3705
+ if (params.priceLower >= params.priceUpper) {
3706
+ throw new Error("priceLower must be less than priceUpper");
3707
+ }
3708
+ const marginScaled = scale6Decimals(params.margin);
3709
+ const perpData = await context.getPerpData(perpId);
3710
+ const { alignedTickLower, alignedTickUpper } = alignMakerTicks(params, perpData.tickSpacing);
3711
+ const contractParams = buildMakerContractParams(
3712
+ context,
3713
+ marginScaled,
3714
+ params,
3715
+ alignedTickLower,
3716
+ alignedTickUpper,
3717
+ MAX_UINT128,
3718
+ MAX_UINT128
3719
+ );
3720
+ const [unexpectedReason, perpDelta, usdDelta] = await context.publicClient.readContract({
3721
+ address: context.deployments().perpManager,
3722
+ abi: PERP_MANAGER_ABI,
3723
+ functionName: "quoteOpenMakerPosition",
3724
+ args: [perpId, contractParams]
3725
+ });
3726
+ if (unexpectedReason !== "0x") {
3727
+ throw new Error(`Quote failed: ${unexpectedReason}`);
3728
+ }
3729
+ return { perpDelta, usdDelta };
3730
+ }, "quoteOpenMakerPosition");
3731
+ }
3732
+ function applySlippage(delta, slippageTolerance, fallbackRef) {
3733
+ const slippageBps = BigInt(Math.ceil(slippageTolerance * 1e4));
3734
+ if (delta < 0n) {
3735
+ const absDelta = -delta;
3736
+ return absDelta + absDelta * slippageBps / 10000n;
3737
+ }
3738
+ if (fallbackRef !== void 0 && fallbackRef !== 0n) {
3739
+ const absRef = fallbackRef < 0n ? -fallbackRef : fallbackRef;
3740
+ return absRef * slippageBps / 10000n;
3741
+ }
3742
+ return 0n;
3743
+ }
3650
3744
  async function openMakerPosition(context, perpId, params) {
3651
3745
  return withErrorHandling(async () => {
3652
3746
  if (params.margin <= 0) {
@@ -3656,11 +3750,27 @@ async function openMakerPosition(context, perpId, params) {
3656
3750
  throw new Error("priceLower must be less than priceUpper");
3657
3751
  }
3658
3752
  const marginScaled = scale6Decimals(params.margin);
3659
- const maxAmt0InScaled = typeof params.maxAmt0In === "bigint" ? params.maxAmt0In : scale6Decimals(params.maxAmt0In);
3660
- const maxAmt1InScaled = typeof params.maxAmt1In === "bigint" ? params.maxAmt1In : scale6Decimals(params.maxAmt1In);
3753
+ const perpData = await context.getPerpData(perpId);
3754
+ const { alignedTickLower, alignedTickUpper } = alignMakerTicks(params, perpData.tickSpacing);
3755
+ let maxAmt0In;
3756
+ let maxAmt1In;
3757
+ if (params.maxAmt0In === void 0 !== (params.maxAmt1In === void 0)) {
3758
+ throw new Error(
3759
+ "Both maxAmt0In and maxAmt1In must be provided together or neither. Omit both to use automatic quote-based slippage calculation."
3760
+ );
3761
+ }
3762
+ if (params.maxAmt0In !== void 0 && params.maxAmt1In !== void 0) {
3763
+ maxAmt0In = typeof params.maxAmt0In === "bigint" ? params.maxAmt0In : scale6Decimals(params.maxAmt0In);
3764
+ maxAmt1In = typeof params.maxAmt1In === "bigint" ? params.maxAmt1In : scale6Decimals(params.maxAmt1In);
3765
+ } else {
3766
+ const quote = await quoteOpenMakerPosition(context, perpId, params);
3767
+ const slippage = params.slippageTolerance ?? DEFAULT_MAKER_SLIPPAGE_TOLERANCE;
3768
+ maxAmt0In = applySlippage(quote.perpDelta, slippage, quote.usdDelta);
3769
+ maxAmt1In = applySlippage(quote.usdDelta, slippage, quote.perpDelta);
3770
+ }
3661
3771
  const currentAllowance = await context.publicClient.readContract({
3662
3772
  address: context.deployments().usdc,
3663
- abi: import_viem5.erc20Abi,
3773
+ abi: erc20Abi3,
3664
3774
  functionName: "allowance",
3665
3775
  args: [context.walletClient.account.address, context.deployments().perpManager],
3666
3776
  blockTag: "latest"
@@ -3668,21 +3778,15 @@ async function openMakerPosition(context, perpId, params) {
3668
3778
  if (currentAllowance < marginScaled) {
3669
3779
  await approveUsdc(context, marginScaled);
3670
3780
  }
3671
- const perpData = await context.getPerpData(perpId);
3672
- const tickLower = priceToTick(params.priceLower, true);
3673
- const tickUpper = priceToTick(params.priceUpper, false);
3674
- const tickSpacing = perpData.tickSpacing;
3675
- const alignedTickLower = Math.floor(tickLower / tickSpacing) * tickSpacing;
3676
- const alignedTickUpper = Math.ceil(tickUpper / tickSpacing) * tickSpacing;
3677
- const contractParams = {
3678
- holder: context.walletClient.account.address,
3679
- margin: marginScaled,
3680
- liquidity: params.liquidity,
3681
- tickLower: alignedTickLower,
3682
- tickUpper: alignedTickUpper,
3683
- maxAmt0In: maxAmt0InScaled,
3684
- maxAmt1In: maxAmt1InScaled
3685
- };
3781
+ const contractParams = buildMakerContractParams(
3782
+ context,
3783
+ marginScaled,
3784
+ params,
3785
+ alignedTickLower,
3786
+ alignedTickUpper,
3787
+ maxAmt0In,
3788
+ maxAmt1In
3789
+ );
3686
3790
  const { request } = await context.publicClient.simulateContract({
3687
3791
  address: context.deployments().perpManager,
3688
3792
  abi: PERP_MANAGER_ABI,
@@ -3691,14 +3795,16 @@ async function openMakerPosition(context, perpId, params) {
3691
3795
  account: context.walletClient.account
3692
3796
  });
3693
3797
  const txHash = await context.walletClient.writeContract(request);
3694
- const receipt = await context.publicClient.waitForTransactionReceipt({ hash: txHash });
3798
+ const receipt = await context.publicClient.waitForTransactionReceipt({
3799
+ hash: txHash
3800
+ });
3695
3801
  if (receipt.status === "reverted") {
3696
3802
  throw new Error(`Transaction reverted. Hash: ${txHash}`);
3697
3803
  }
3698
3804
  let makerPosId = null;
3699
3805
  for (const log of receipt.logs) {
3700
3806
  try {
3701
- const decoded = (0, import_viem5.decodeEventLog)({
3807
+ const decoded = decodeEventLog2({
3702
3808
  abi: PERP_MANAGER_ABI,
3703
3809
  data: log.data,
3704
3810
  topics: log.topics
@@ -3716,9 +3822,109 @@ async function openMakerPosition(context, perpId, params) {
3716
3822
  return new OpenPosition(context, perpId, makerPosId, void 0, true, txHash);
3717
3823
  }, "openMakerPosition");
3718
3824
  }
3825
+ async function quoteTakerPosition(context, perpId, params) {
3826
+ return withErrorHandling(async () => {
3827
+ if (params.margin <= 0) {
3828
+ throw new Error("Margin must be greater than 0");
3829
+ }
3830
+ if (params.leverage <= 0) {
3831
+ throw new Error("Leverage must be greater than 0");
3832
+ }
3833
+ const marginScaled = scale6Decimals(params.margin);
3834
+ const marginRatio = Math.floor(NUMBER_1E6 / params.leverage);
3835
+ const result = await context.publicClient.simulateContract({
3836
+ address: context.deployments().perpManager,
3837
+ abi: PERP_MANAGER_ABI,
3838
+ functionName: "quoteOpenTakerPosition",
3839
+ args: [
3840
+ perpId,
3841
+ {
3842
+ holder: params.holder,
3843
+ isLong: params.isLong,
3844
+ margin: marginScaled,
3845
+ marginRatio,
3846
+ unspecifiedAmountLimit: params.isLong ? 0n : (1n << 128n) - 1n
3847
+ }
3848
+ ],
3849
+ account: params.holder
3850
+ });
3851
+ const [unexpectedReason, perpDelta, usdDelta] = result.result;
3852
+ if (unexpectedReason && unexpectedReason !== "0x") {
3853
+ let errorName = "Quote simulation failed";
3854
+ try {
3855
+ const decoded = decodeErrorResult({
3856
+ abi: PERP_MANAGER_ABI,
3857
+ data: unexpectedReason
3858
+ });
3859
+ errorName = decoded.errorName;
3860
+ } catch {
3861
+ }
3862
+ throw new Error(errorName);
3863
+ }
3864
+ const absPerpDelta = perpDelta < 0n ? -perpDelta : perpDelta;
3865
+ const absUsdDelta = usdDelta < 0n ? -usdDelta : usdDelta;
3866
+ if (absPerpDelta === 0n) {
3867
+ throw new Error("Zero position size");
3868
+ }
3869
+ const fillPrice = Number(absUsdDelta) / Number(absPerpDelta);
3870
+ return { perpDelta, usdDelta, fillPrice };
3871
+ }, "quoteTakerPosition");
3872
+ }
3873
+ async function adjustMargin(context, positionId, marginDelta) {
3874
+ return withErrorHandling(async () => {
3875
+ const deployments = context.deployments();
3876
+ if (marginDelta > 0n) {
3877
+ const currentAllowance = await context.publicClient.readContract({
3878
+ address: deployments.usdc,
3879
+ abi: erc20Abi3,
3880
+ functionName: "allowance",
3881
+ args: [context.walletClient.account.address, deployments.perpManager],
3882
+ blockTag: "latest"
3883
+ });
3884
+ if (currentAllowance < marginDelta) {
3885
+ await approveUsdc(context, marginDelta);
3886
+ }
3887
+ }
3888
+ const { request } = await context.publicClient.simulateContract({
3889
+ address: deployments.perpManager,
3890
+ abi: PERP_MANAGER_ABI,
3891
+ functionName: "adjustMargin",
3892
+ args: [{ posId: positionId, marginDelta }],
3893
+ account: context.walletClient.account
3894
+ });
3895
+ const txHash = await context.walletClient.writeContract(request);
3896
+ return { txHash };
3897
+ }, `adjustMargin for position ${positionId}`);
3898
+ }
3899
+ async function adjustNotional(context, positionId, usdDelta, perpLimit) {
3900
+ return withErrorHandling(async () => {
3901
+ const deployments = context.deployments();
3902
+ if (usdDelta > 0n) {
3903
+ const currentAllowance = await context.publicClient.readContract({
3904
+ address: deployments.usdc,
3905
+ abi: erc20Abi3,
3906
+ functionName: "allowance",
3907
+ args: [context.walletClient.account.address, deployments.perpManager],
3908
+ blockTag: "latest"
3909
+ });
3910
+ if (currentAllowance < usdDelta) {
3911
+ await approveUsdc(context, usdDelta);
3912
+ }
3913
+ }
3914
+ const { request } = await context.publicClient.simulateContract({
3915
+ address: deployments.perpManager,
3916
+ abi: PERP_MANAGER_ABI,
3917
+ functionName: "adjustNotional",
3918
+ args: [{ posId: positionId, usdDelta, perpLimit }],
3919
+ account: context.walletClient.account
3920
+ });
3921
+ const txHash = await context.walletClient.writeContract(request);
3922
+ return { txHash };
3923
+ }, `adjustNotional for position ${positionId}`);
3924
+ }
3719
3925
 
3720
3926
  // src/functions/position.ts
3721
- var import_viem6 = require("viem");
3927
+ import { decodeErrorResult as decodeErrorResult2, decodeEventLog as decodeEventLog3, formatUnits as formatUnits3 } from "viem";
3722
3928
  function getPositionPerpId(positionData) {
3723
3929
  return positionData.perpId;
3724
3930
  }
@@ -3746,6 +3952,131 @@ function getPositionEffectiveMargin(positionData) {
3746
3952
  function getPositionIsLiquidatable(positionData) {
3747
3953
  return positionData.liveDetails.isLiquidatable;
3748
3954
  }
3955
+ async function quoteClosePosition(context, positionId) {
3956
+ return withErrorHandling(async () => {
3957
+ const result = await context.publicClient.readContract({
3958
+ address: context.deployments().perpManager,
3959
+ abi: PERP_MANAGER_ABI,
3960
+ functionName: "quoteClosePosition",
3961
+ args: [positionId]
3962
+ });
3963
+ const [unexpectedReason, pnl, funding, netMargin, wasLiquidated] = result;
3964
+ if (unexpectedReason && unexpectedReason !== "0x") {
3965
+ let errorName = "Quote simulation failed";
3966
+ try {
3967
+ const decoded = decodeErrorResult2({
3968
+ abi: PERP_MANAGER_ABI,
3969
+ data: unexpectedReason
3970
+ });
3971
+ errorName = decoded.errorName;
3972
+ } catch {
3973
+ }
3974
+ throw new Error(errorName);
3975
+ }
3976
+ return {
3977
+ pnl: Number(formatUnits3(pnl, 6)),
3978
+ // Negate so positive = user receives funding, matching live details convention
3979
+ funding: -Number(formatUnits3(funding, 6)),
3980
+ netMargin: Number(formatUnits3(uint256ToInt256(netMargin), 6)),
3981
+ wasLiquidated
3982
+ };
3983
+ }, `quoteClosePosition for position ${positionId}`);
3984
+ }
3985
+ async function closePositionWithQuote(context, _perpId, positionId, slippageTolerance = 0.01) {
3986
+ return withErrorHandling(async () => {
3987
+ const rawData = await context.getPositionRawData(positionId);
3988
+ const isMaker = rawData.makerDetails !== null;
3989
+ const result = await context.publicClient.readContract({
3990
+ address: context.deployments().perpManager,
3991
+ abi: PERP_MANAGER_ABI,
3992
+ functionName: "quoteClosePosition",
3993
+ args: [positionId]
3994
+ });
3995
+ const [unexpectedReason, quotePnl] = result;
3996
+ if (unexpectedReason && unexpectedReason !== "0x") {
3997
+ throw new Error("Quote failed \u2014 position may be invalid or already closed");
3998
+ }
3999
+ let contractParams = {
4000
+ posId: positionId,
4001
+ minAmt0Out: 0n,
4002
+ minAmt1Out: 0n,
4003
+ maxAmt1In: 0n
4004
+ };
4005
+ const canonicalPerpId = rawData.perpId;
4006
+ if (!isMaker) {
4007
+ const isLong = rawData.entryPerpDelta > 0n;
4008
+ const slippageBps = BigInt(Math.ceil(slippageTolerance * 1e4));
4009
+ const absEntryUsd = rawData.entryUsdDelta < 0n ? -rawData.entryUsdDelta : rawData.entryUsdDelta;
4010
+ if (isLong) {
4011
+ const swapRevenue = quotePnl + absEntryUsd;
4012
+ const minOut = swapRevenue > 0n ? swapRevenue - swapRevenue * slippageBps / 10000n : 0n;
4013
+ contractParams = {
4014
+ posId: positionId,
4015
+ minAmt0Out: 0n,
4016
+ minAmt1Out: minOut,
4017
+ maxAmt1In: 0n
4018
+ };
4019
+ } else {
4020
+ const swapCost = absEntryUsd - quotePnl;
4021
+ const maxIn = swapCost > 0n ? swapCost + swapCost * slippageBps / 10000n : 0n;
4022
+ contractParams = {
4023
+ posId: positionId,
4024
+ minAmt0Out: 0n,
4025
+ minAmt1Out: 0n,
4026
+ maxAmt1In: maxIn
4027
+ };
4028
+ }
4029
+ }
4030
+ const { request } = await context.publicClient.simulateContract({
4031
+ address: context.deployments().perpManager,
4032
+ abi: PERP_MANAGER_ABI,
4033
+ functionName: "closePosition",
4034
+ args: [contractParams],
4035
+ account: context.walletClient.account,
4036
+ gas: 500000n
4037
+ });
4038
+ const txHash = await context.walletClient.writeContract(request);
4039
+ const receipt = await context.publicClient.waitForTransactionReceipt({
4040
+ hash: txHash
4041
+ });
4042
+ if (receipt.status === "reverted") {
4043
+ throw new Error(`Transaction reverted. Hash: ${txHash}`);
4044
+ }
4045
+ let newPositionId = null;
4046
+ for (const log of receipt.logs) {
4047
+ try {
4048
+ const decoded = decodeEventLog3({
4049
+ abi: PERP_MANAGER_ABI,
4050
+ data: log.data,
4051
+ topics: log.topics,
4052
+ eventName: "PositionOpened"
4053
+ });
4054
+ const eventPerpId = decoded.args.perpId.toLowerCase();
4055
+ const eventPosId = decoded.args.posId;
4056
+ if (eventPerpId === canonicalPerpId.toLowerCase() && eventPosId !== positionId) {
4057
+ newPositionId = eventPosId;
4058
+ break;
4059
+ }
4060
+ } catch (_e) {
4061
+ }
4062
+ }
4063
+ if (!newPositionId) {
4064
+ return { position: null, txHash };
4065
+ }
4066
+ return {
4067
+ position: {
4068
+ perpId: canonicalPerpId,
4069
+ positionId: newPositionId,
4070
+ liveDetails: await getPositionLiveDetailsFromContract(
4071
+ context,
4072
+ canonicalPerpId,
4073
+ newPositionId
4074
+ )
4075
+ },
4076
+ txHash
4077
+ };
4078
+ }, `closePositionWithQuote for position ${positionId}`);
4079
+ }
3749
4080
  async function closePosition(context, perpId, positionId, params) {
3750
4081
  return withErrorHandling(async () => {
3751
4082
  const contractParams = {
@@ -3764,14 +4095,16 @@ async function closePosition(context, perpId, positionId, params) {
3764
4095
  // Provide explicit gas limit to avoid estimation issues
3765
4096
  });
3766
4097
  const txHash = await context.walletClient.writeContract(request);
3767
- const receipt = await context.publicClient.waitForTransactionReceipt({ hash: txHash });
4098
+ const receipt = await context.publicClient.waitForTransactionReceipt({
4099
+ hash: txHash
4100
+ });
3768
4101
  if (receipt.status === "reverted") {
3769
4102
  throw new Error(`Transaction reverted. Hash: ${txHash}`);
3770
4103
  }
3771
4104
  let newPositionId = null;
3772
4105
  for (const log of receipt.logs) {
3773
4106
  try {
3774
- const decoded = (0, import_viem6.decodeEventLog)({
4107
+ const decoded = decodeEventLog3({
3775
4108
  abi: PERP_MANAGER_ABI,
3776
4109
  data: log.data,
3777
4110
  topics: log.topics,
@@ -3814,9 +4147,10 @@ async function getPositionLiveDetailsFromContract(context, _perpId, positionId)
3814
4147
  );
3815
4148
  }
3816
4149
  return {
3817
- pnl: Number((0, import_viem6.formatUnits)(pnl, 6)),
3818
- fundingPayment: Number((0, import_viem6.formatUnits)(funding, 6)),
3819
- effectiveMargin: Number((0, import_viem6.formatUnits)(netMargin, 6)),
4150
+ pnl: Number(formatUnits3(pnl, 6)),
4151
+ // Negate so positive = user receives funding, matching quoteClosePosition convention
4152
+ fundingPayment: -Number(formatUnits3(funding, 6)),
4153
+ effectiveMargin: Number(formatUnits3(uint256ToInt256(netMargin), 6)),
3820
4154
  isLiquidatable: wasLiquidated
3821
4155
  };
3822
4156
  }, `getPositionLiveDetailsFromContract for position ${positionId}`);
@@ -3860,6 +4194,34 @@ function calculateLiquidationPrice(rawData, isLong) {
3860
4194
  return liqPrice;
3861
4195
  }
3862
4196
  }
4197
+ function calculatePnlPercentage(pnl, funding, margin) {
4198
+ const totalPnl = pnl + funding;
4199
+ const initialMargin = margin - totalPnl;
4200
+ if (initialMargin <= 0) return 0;
4201
+ return totalPnl / initialMargin * 100;
4202
+ }
4203
+ function calculateClosePositionParams(opts) {
4204
+ if (opts.isMaker) {
4205
+ return { minAmt0Out: 0, minAmt1Out: 0, maxAmt1In: 0 };
4206
+ }
4207
+ if (typeof opts.isLong !== "boolean") {
4208
+ throw new Error("isLong must be explicitly set for taker positions");
4209
+ }
4210
+ const absNotional = Math.abs(opts.notional);
4211
+ if (opts.isLong) {
4212
+ return {
4213
+ minAmt0Out: 0,
4214
+ minAmt1Out: Math.max(0, absNotional * (1 - opts.slippagePercent / 100)),
4215
+ maxAmt1In: 0
4216
+ };
4217
+ } else {
4218
+ return {
4219
+ minAmt0Out: 0,
4220
+ minAmt1Out: 0,
4221
+ maxAmt1In: absNotional * (1 + opts.slippagePercent / 100)
4222
+ };
4223
+ }
4224
+ }
3863
4225
 
3864
4226
  // src/functions/user.ts
3865
4227
  function getUserUsdcBalance(userData) {
@@ -3871,14 +4233,20 @@ function getUserOpenPositions(userData) {
3871
4233
  function getUserWalletAddress(userData) {
3872
4234
  return userData.walletAddress;
3873
4235
  }
3874
- // Annotate the CommonJS export names for ESM import in node:
3875
- 0 && (module.exports = {
4236
+ export {
3876
4237
  BEACON_ABI,
3877
4238
  BIGINT_1E6,
3878
4239
  ContractError,
4240
+ DEFAULT_MAKER_SLIPPAGE_TOLERANCE,
3879
4241
  ErrorCategory,
3880
4242
  ErrorSource,
4243
+ INT256_THRESHOLD,
3881
4244
  InsufficientFundsError,
4245
+ MAX_PRICE,
4246
+ MAX_TICK,
4247
+ MAX_UINT128,
4248
+ MIN_PRICE,
4249
+ MIN_TICK,
3882
4250
  NUMBER_1E6,
3883
4251
  OpenPosition,
3884
4252
  PERP_MANAGER_ABI,
@@ -3887,17 +4255,31 @@ function getUserWalletAddress(userData) {
3887
4255
  Q96,
3888
4256
  RPCError,
3889
4257
  TransactionRejectedError,
4258
+ UINT256_MAX,
3890
4259
  ValidationError,
4260
+ adjustMargin,
4261
+ adjustNotional,
4262
+ applySlippage,
3891
4263
  approveUsdc,
4264
+ calculateAlignedTicks,
4265
+ calculateClosePositionParams,
3892
4266
  calculateEntryPrice,
3893
4267
  calculateLeverage,
3894
4268
  calculateLiquidationPrice,
3895
4269
  calculateLiquidityForTargetRatio,
4270
+ calculatePnlPercentage,
3896
4271
  calculatePositionSize,
3897
4272
  calculatePositionValue,
3898
4273
  closePosition,
4274
+ closePositionWithQuote,
4275
+ convertFundingDiffX96ToPercentPerPeriod,
4276
+ convertFundingPerSecondX96ToPercentPerDay,
4277
+ convertFundingPerSecondX96ToPercentPerMinute,
3899
4278
  createPerp,
3900
4279
  estimateLiquidity,
4280
+ getFundingRate,
4281
+ getIndexTWAP,
4282
+ getIndexValue,
3901
4283
  getPerpBeacon,
3902
4284
  getPerpBounds,
3903
4285
  getPerpFees,
@@ -3914,6 +4296,7 @@ function getUserWalletAddress(userData) {
3914
4296
  getPositionPerpId,
3915
4297
  getPositionPnl,
3916
4298
  getRpcUrl,
4299
+ getUsdcAllowance,
3917
4300
  getUserOpenPositions,
3918
4301
  getUserUsdcBalance,
3919
4302
  getUserWalletAddress,
@@ -3923,12 +4306,16 @@ function getUserWalletAddress(userData) {
3923
4306
  parseContractError,
3924
4307
  priceToSqrtPriceX96,
3925
4308
  priceToTick,
4309
+ quoteClosePosition,
4310
+ quoteOpenMakerPosition,
4311
+ quoteTakerPosition,
3926
4312
  scale6Decimals,
3927
4313
  scaleFrom6Decimals,
3928
4314
  scaleFromX96,
3929
4315
  scaleToX96,
3930
4316
  sqrtPriceX96ToPrice,
3931
4317
  tickToPrice,
4318
+ uint256ToInt256,
3932
4319
  withErrorHandling
3933
- });
4320
+ };
3934
4321
  //# sourceMappingURL=index.js.map