@strobelabs/perpcity-sdk 0.5.1 → 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.mjs → index.cjs} +543 -61
- package/dist/index.cjs.map +1 -0
- package/dist/{index.d.mts → index.d.cts} +100 -7
- package/dist/index.d.ts +100 -7
- package/dist/index.js +428 -154
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
- package/dist/index.mjs.map +0 -1
|
@@ -1,3 +1,121 @@
|
|
|
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
|
+
DEFAULT_MAKER_SLIPPAGE_TOLERANCE: () => DEFAULT_MAKER_SLIPPAGE_TOLERANCE,
|
|
37
|
+
ErrorCategory: () => ErrorCategory,
|
|
38
|
+
ErrorSource: () => ErrorSource,
|
|
39
|
+
INT256_THRESHOLD: () => INT256_THRESHOLD,
|
|
40
|
+
InsufficientFundsError: () => InsufficientFundsError,
|
|
41
|
+
MAX_PRICE: () => MAX_PRICE,
|
|
42
|
+
MAX_TICK: () => MAX_TICK,
|
|
43
|
+
MAX_UINT128: () => MAX_UINT128,
|
|
44
|
+
MIN_PRICE: () => MIN_PRICE,
|
|
45
|
+
MIN_TICK: () => MIN_TICK,
|
|
46
|
+
NUMBER_1E6: () => NUMBER_1E6,
|
|
47
|
+
OpenPosition: () => OpenPosition,
|
|
48
|
+
PERP_MANAGER_ABI: () => PERP_MANAGER_ABI,
|
|
49
|
+
PerpCityContext: () => PerpCityContext,
|
|
50
|
+
PerpCityError: () => PerpCityError,
|
|
51
|
+
Q96: () => Q96,
|
|
52
|
+
RPCError: () => RPCError,
|
|
53
|
+
TransactionRejectedError: () => TransactionRejectedError,
|
|
54
|
+
UINT256_MAX: () => UINT256_MAX,
|
|
55
|
+
ValidationError: () => ValidationError,
|
|
56
|
+
adjustMargin: () => adjustMargin,
|
|
57
|
+
adjustNotional: () => adjustNotional,
|
|
58
|
+
applySlippage: () => applySlippage,
|
|
59
|
+
approveUsdc: () => approveUsdc,
|
|
60
|
+
calculateAlignedTicks: () => calculateAlignedTicks,
|
|
61
|
+
calculateClosePositionParams: () => calculateClosePositionParams,
|
|
62
|
+
calculateEntryPrice: () => calculateEntryPrice,
|
|
63
|
+
calculateLeverage: () => calculateLeverage,
|
|
64
|
+
calculateLiquidationPrice: () => calculateLiquidationPrice,
|
|
65
|
+
calculateLiquidityForTargetRatio: () => calculateLiquidityForTargetRatio,
|
|
66
|
+
calculatePnlPercentage: () => calculatePnlPercentage,
|
|
67
|
+
calculatePositionSize: () => calculatePositionSize,
|
|
68
|
+
calculatePositionValue: () => calculatePositionValue,
|
|
69
|
+
closePosition: () => closePosition,
|
|
70
|
+
closePositionWithQuote: () => closePositionWithQuote,
|
|
71
|
+
convertFundingDiffX96ToPercentPerPeriod: () => convertFundingDiffX96ToPercentPerPeriod,
|
|
72
|
+
convertFundingPerSecondX96ToPercentPerDay: () => convertFundingPerSecondX96ToPercentPerDay,
|
|
73
|
+
convertFundingPerSecondX96ToPercentPerMinute: () => convertFundingPerSecondX96ToPercentPerMinute,
|
|
74
|
+
createPerp: () => createPerp,
|
|
75
|
+
estimateLiquidity: () => estimateLiquidity,
|
|
76
|
+
getFundingRate: () => getFundingRate,
|
|
77
|
+
getIndexTWAP: () => getIndexTWAP,
|
|
78
|
+
getIndexValue: () => getIndexValue,
|
|
79
|
+
getPerpBeacon: () => getPerpBeacon,
|
|
80
|
+
getPerpBounds: () => getPerpBounds,
|
|
81
|
+
getPerpFees: () => getPerpFees,
|
|
82
|
+
getPerpMark: () => getPerpMark,
|
|
83
|
+
getPerpTickSpacing: () => getPerpTickSpacing,
|
|
84
|
+
getPositionEffectiveMargin: () => getPositionEffectiveMargin,
|
|
85
|
+
getPositionFundingPayment: () => getPositionFundingPayment,
|
|
86
|
+
getPositionId: () => getPositionId,
|
|
87
|
+
getPositionIsLiquidatable: () => getPositionIsLiquidatable,
|
|
88
|
+
getPositionIsLong: () => getPositionIsLong,
|
|
89
|
+
getPositionIsMaker: () => getPositionIsMaker,
|
|
90
|
+
getPositionLiveDetails: () => getPositionLiveDetails,
|
|
91
|
+
getPositionLiveDetailsFromContract: () => getPositionLiveDetailsFromContract,
|
|
92
|
+
getPositionPerpId: () => getPositionPerpId,
|
|
93
|
+
getPositionPnl: () => getPositionPnl,
|
|
94
|
+
getRpcUrl: () => getRpcUrl,
|
|
95
|
+
getUsdcAllowance: () => getUsdcAllowance,
|
|
96
|
+
getUserOpenPositions: () => getUserOpenPositions,
|
|
97
|
+
getUserUsdcBalance: () => getUserUsdcBalance,
|
|
98
|
+
getUserWalletAddress: () => getUserWalletAddress,
|
|
99
|
+
marginRatioToLeverage: () => marginRatioToLeverage,
|
|
100
|
+
openMakerPosition: () => openMakerPosition,
|
|
101
|
+
openTakerPosition: () => openTakerPosition,
|
|
102
|
+
parseContractError: () => parseContractError,
|
|
103
|
+
priceToSqrtPriceX96: () => priceToSqrtPriceX96,
|
|
104
|
+
priceToTick: () => priceToTick,
|
|
105
|
+
quoteClosePosition: () => quoteClosePosition,
|
|
106
|
+
quoteOpenMakerPosition: () => quoteOpenMakerPosition,
|
|
107
|
+
quoteTakerPosition: () => quoteTakerPosition,
|
|
108
|
+
scale6Decimals: () => scale6Decimals,
|
|
109
|
+
scaleFrom6Decimals: () => scaleFrom6Decimals,
|
|
110
|
+
scaleFromX96: () => scaleFromX96,
|
|
111
|
+
scaleToX96: () => scaleToX96,
|
|
112
|
+
sqrtPriceX96ToPrice: () => sqrtPriceX96ToPrice,
|
|
113
|
+
tickToPrice: () => tickToPrice,
|
|
114
|
+
uint256ToInt256: () => uint256ToInt256,
|
|
115
|
+
withErrorHandling: () => withErrorHandling
|
|
116
|
+
});
|
|
117
|
+
module.exports = __toCommonJS(index_exports);
|
|
118
|
+
|
|
1
119
|
// src/abis/beacon.ts
|
|
2
120
|
var BEACON_ABI = [
|
|
3
121
|
{
|
|
@@ -1777,19 +1895,14 @@ var PERP_MANAGER_ABI = [
|
|
|
1777
1895
|
type: "int256"
|
|
1778
1896
|
},
|
|
1779
1897
|
{
|
|
1780
|
-
internalType: "
|
|
1898
|
+
internalType: "uint256",
|
|
1781
1899
|
name: "netMargin",
|
|
1782
|
-
type: "
|
|
1900
|
+
type: "uint256"
|
|
1783
1901
|
},
|
|
1784
1902
|
{
|
|
1785
1903
|
internalType: "bool",
|
|
1786
1904
|
name: "wasLiquidated",
|
|
1787
1905
|
type: "bool"
|
|
1788
|
-
},
|
|
1789
|
-
{
|
|
1790
|
-
internalType: "uint256",
|
|
1791
|
-
name: "notional",
|
|
1792
|
-
type: "uint256"
|
|
1793
1906
|
}
|
|
1794
1907
|
],
|
|
1795
1908
|
stateMutability: "nonpayable",
|
|
@@ -2266,13 +2379,8 @@ var PERP_MANAGER_ABI = [
|
|
|
2266
2379
|
];
|
|
2267
2380
|
|
|
2268
2381
|
// src/context.ts
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
createPublicClient,
|
|
2272
|
-
erc20Abi as erc20Abi2,
|
|
2273
|
-
formatUnits,
|
|
2274
|
-
http
|
|
2275
|
-
} from "viem";
|
|
2382
|
+
var import_ttlcache = __toESM(require("@isaacs/ttlcache"), 1);
|
|
2383
|
+
var import_viem3 = require("viem");
|
|
2276
2384
|
|
|
2277
2385
|
// src/abis/fees.ts
|
|
2278
2386
|
var FEES_ABI = [
|
|
@@ -2353,13 +2461,22 @@ var MARGIN_RATIOS_ABI = [
|
|
|
2353
2461
|
];
|
|
2354
2462
|
|
|
2355
2463
|
// src/utils/approve.ts
|
|
2356
|
-
|
|
2464
|
+
var import_viem = require("viem");
|
|
2357
2465
|
var DEFAULT_CONFIRMATIONS = 2;
|
|
2466
|
+
async function getUsdcAllowance(context, owner) {
|
|
2467
|
+
const deployments = context.deployments();
|
|
2468
|
+
return context.publicClient.readContract({
|
|
2469
|
+
address: deployments.usdc,
|
|
2470
|
+
abi: import_viem.erc20Abi,
|
|
2471
|
+
functionName: "allowance",
|
|
2472
|
+
args: [owner, deployments.perpManager]
|
|
2473
|
+
});
|
|
2474
|
+
}
|
|
2358
2475
|
async function approveUsdc(context, amount, confirmations = DEFAULT_CONFIRMATIONS) {
|
|
2359
2476
|
const deployments = context.deployments();
|
|
2360
2477
|
const { request } = await context.publicClient.simulateContract({
|
|
2361
2478
|
address: deployments.usdc,
|
|
2362
|
-
abi: erc20Abi,
|
|
2479
|
+
abi: import_viem.erc20Abi,
|
|
2363
2480
|
functionName: "approve",
|
|
2364
2481
|
args: [deployments.perpManager, amount],
|
|
2365
2482
|
account: context.walletClient.account
|
|
@@ -2379,6 +2496,8 @@ var MIN_TICK = -887272;
|
|
|
2379
2496
|
var MAX_TICK = 887272;
|
|
2380
2497
|
var MIN_PRICE = 1e-6;
|
|
2381
2498
|
var MAX_PRICE = 1e6;
|
|
2499
|
+
var UINT256_MAX = (1n << 256n) - 1n;
|
|
2500
|
+
var INT256_THRESHOLD = 1n << 255n;
|
|
2382
2501
|
|
|
2383
2502
|
// src/utils/conversions.ts
|
|
2384
2503
|
function priceToSqrtPriceX96(price) {
|
|
@@ -2422,6 +2541,12 @@ function priceToTick(price, roundDown) {
|
|
|
2422
2541
|
function tickToPrice(tick) {
|
|
2423
2542
|
return 1.0001 ** tick;
|
|
2424
2543
|
}
|
|
2544
|
+
function uint256ToInt256(value) {
|
|
2545
|
+
if (value >= INT256_THRESHOLD) {
|
|
2546
|
+
return value - UINT256_MAX - 1n;
|
|
2547
|
+
}
|
|
2548
|
+
return value;
|
|
2549
|
+
}
|
|
2425
2550
|
function sqrtPriceX96ToPrice(sqrtPriceX96) {
|
|
2426
2551
|
const priceX96 = sqrtPriceX96 * sqrtPriceX96 / Q96;
|
|
2427
2552
|
return scaleFromX96(priceX96);
|
|
@@ -2437,7 +2562,7 @@ function scaleFrom6Decimals(value) {
|
|
|
2437
2562
|
}
|
|
2438
2563
|
|
|
2439
2564
|
// src/utils/errors.ts
|
|
2440
|
-
|
|
2565
|
+
var import_viem2 = require("viem");
|
|
2441
2566
|
var ErrorCategory = /* @__PURE__ */ ((ErrorCategory2) => {
|
|
2442
2567
|
ErrorCategory2["USER_ERROR"] = "USER_ERROR";
|
|
2443
2568
|
ErrorCategory2["STATE_ERROR"] = "STATE_ERROR";
|
|
@@ -2495,9 +2620,9 @@ function parseContractError(error) {
|
|
|
2495
2620
|
if (error instanceof PerpCityError) {
|
|
2496
2621
|
return error;
|
|
2497
2622
|
}
|
|
2498
|
-
if (error instanceof BaseError) {
|
|
2499
|
-
const revertError = error.walk((err) => err instanceof ContractFunctionRevertedError);
|
|
2500
|
-
if (revertError instanceof ContractFunctionRevertedError) {
|
|
2623
|
+
if (error instanceof import_viem2.BaseError) {
|
|
2624
|
+
const revertError = error.walk((err) => err instanceof import_viem2.ContractFunctionRevertedError);
|
|
2625
|
+
if (revertError instanceof import_viem2.ContractFunctionRevertedError) {
|
|
2501
2626
|
const errorName = revertError.data?.errorName ?? "Unknown";
|
|
2502
2627
|
const args = revertError.data?.args ?? [];
|
|
2503
2628
|
const { message, debug } = formatContractError(errorName, args);
|
|
@@ -2662,7 +2787,7 @@ function formatContractError(errorName, args) {
|
|
|
2662
2787
|
};
|
|
2663
2788
|
case "MinimumAmountInsufficient":
|
|
2664
2789
|
return {
|
|
2665
|
-
message:
|
|
2790
|
+
message: "Slippage tolerance exceeded. The position's value moved unfavorably during execution. Try increasing your slippage tolerance.",
|
|
2666
2791
|
debug: { source, category: "USER_ERROR" /* USER_ERROR */ }
|
|
2667
2792
|
};
|
|
2668
2793
|
case "PriceImpactTooHigh":
|
|
@@ -2941,6 +3066,23 @@ async function withErrorHandling(fn, context) {
|
|
|
2941
3066
|
}
|
|
2942
3067
|
}
|
|
2943
3068
|
|
|
3069
|
+
// src/utils/funding.ts
|
|
3070
|
+
var PRECISION = 10n ** 18n;
|
|
3071
|
+
function convertFundingDiffX96ToPercentPerPeriod(fundingDiffX96, interval, periodSeconds) {
|
|
3072
|
+
const scaledRate = fundingDiffX96 * periodSeconds * PRECISION / (Q96 * interval);
|
|
3073
|
+
return Number(scaledRate) / Number(PRECISION);
|
|
3074
|
+
}
|
|
3075
|
+
function convertFundingPerSecondX96ToPercentPerMinute(fundingPerSecondX96) {
|
|
3076
|
+
const SECONDS_PER_MINUTE = 60n;
|
|
3077
|
+
const scaledRate = fundingPerSecondX96 * SECONDS_PER_MINUTE * PRECISION / Q96;
|
|
3078
|
+
return Number(scaledRate) / Number(PRECISION);
|
|
3079
|
+
}
|
|
3080
|
+
function convertFundingPerSecondX96ToPercentPerDay(fundingPerSecondX96) {
|
|
3081
|
+
const SECONDS_PER_DAY = 86400n;
|
|
3082
|
+
const scaledRate = fundingPerSecondX96 * SECONDS_PER_DAY * PRECISION / Q96;
|
|
3083
|
+
return Number(scaledRate) / Number(PRECISION);
|
|
3084
|
+
}
|
|
3085
|
+
|
|
2944
3086
|
// src/utils/liquidity.ts
|
|
2945
3087
|
async function estimateLiquidity(_context, tickLower, tickUpper, usdScaled) {
|
|
2946
3088
|
if (tickLower >= tickUpper) {
|
|
@@ -3043,16 +3185,16 @@ function getRpcUrl(config = {}) {
|
|
|
3043
3185
|
// src/context.ts
|
|
3044
3186
|
var PerpCityContext = class {
|
|
3045
3187
|
constructor(config) {
|
|
3046
|
-
this.configCache = new
|
|
3188
|
+
this.configCache = new import_ttlcache.default({ ttl: 5 * 60 * 1e3 });
|
|
3047
3189
|
this.walletClient = config.walletClient;
|
|
3048
3190
|
if (!config.walletClient.chain?.id) {
|
|
3049
3191
|
throw new Error(
|
|
3050
3192
|
"PerpCityContext: walletClient.chain must be defined with a numeric id. Ensure your walletClient was created with a chain parameter."
|
|
3051
3193
|
);
|
|
3052
3194
|
}
|
|
3053
|
-
this.publicClient = createPublicClient({
|
|
3195
|
+
this.publicClient = (0, import_viem3.createPublicClient)({
|
|
3054
3196
|
chain: config.walletClient.chain,
|
|
3055
|
-
transport: http(config.rpcUrl, { batch: true })
|
|
3197
|
+
transport: (0, import_viem3.http)(config.rpcUrl, { batch: true })
|
|
3056
3198
|
});
|
|
3057
3199
|
this._deployments = config.deployments;
|
|
3058
3200
|
}
|
|
@@ -3134,8 +3276,7 @@ var PerpCityContext = class {
|
|
|
3134
3276
|
const tickSpacing = Number(cfg.key.tickSpacing);
|
|
3135
3277
|
let sqrtPriceX96;
|
|
3136
3278
|
if (markPrice) {
|
|
3137
|
-
|
|
3138
|
-
sqrtPriceX96 = BigInt(Math.floor(sqrtPrice * 2 ** 96));
|
|
3279
|
+
sqrtPriceX96 = priceToSqrtPriceX96(markPrice);
|
|
3139
3280
|
} else {
|
|
3140
3281
|
sqrtPriceX96 = await this.publicClient.readContract({
|
|
3141
3282
|
address: this.deployments().perpManager,
|
|
@@ -3220,7 +3361,7 @@ var PerpCityContext = class {
|
|
|
3220
3361
|
async fetchUserData(userAddress, positions) {
|
|
3221
3362
|
const usdcBalance = await this.publicClient.readContract({
|
|
3222
3363
|
address: this.deployments().usdc,
|
|
3223
|
-
abi:
|
|
3364
|
+
abi: import_viem3.erc20Abi,
|
|
3224
3365
|
functionName: "balanceOf",
|
|
3225
3366
|
args: [userAddress]
|
|
3226
3367
|
});
|
|
@@ -3238,7 +3379,7 @@ var PerpCityContext = class {
|
|
|
3238
3379
|
);
|
|
3239
3380
|
return {
|
|
3240
3381
|
walletAddress: userAddress,
|
|
3241
|
-
usdcBalance: Number(formatUnits(usdcBalance, 6)),
|
|
3382
|
+
usdcBalance: Number((0, import_viem3.formatUnits)(usdcBalance, 6)),
|
|
3242
3383
|
openPositions: openPositionsData
|
|
3243
3384
|
};
|
|
3244
3385
|
}
|
|
@@ -3257,9 +3398,10 @@ var PerpCityContext = class {
|
|
|
3257
3398
|
);
|
|
3258
3399
|
}
|
|
3259
3400
|
return {
|
|
3260
|
-
pnl: Number(formatUnits(pnl, 6)),
|
|
3261
|
-
|
|
3262
|
-
|
|
3401
|
+
pnl: Number((0, import_viem3.formatUnits)(pnl, 6)),
|
|
3402
|
+
// Negate so positive = user receives funding
|
|
3403
|
+
fundingPayment: -Number((0, import_viem3.formatUnits)(funding, 6)),
|
|
3404
|
+
effectiveMargin: Number((0, import_viem3.formatUnits)(uint256ToInt256(netMargin), 6)),
|
|
3263
3405
|
isLiquidatable: wasLiquidated
|
|
3264
3406
|
};
|
|
3265
3407
|
}, `fetchPositionLiveDetailsFromContract for position ${positionId}`);
|
|
@@ -3303,29 +3445,35 @@ var PerpCityContext = class {
|
|
|
3303
3445
|
args: [positionId]
|
|
3304
3446
|
});
|
|
3305
3447
|
const resultArray = result;
|
|
3306
|
-
const [perpId, margin, entryPerpDelta, entryUsdDelta, , , , marginRatios] = resultArray;
|
|
3448
|
+
const [perpId, margin, entryPerpDelta, entryUsdDelta, , , , marginRatios, makerDetailsRaw] = resultArray;
|
|
3307
3449
|
const zeroPerpId = `0x${"0".repeat(64)}`;
|
|
3308
3450
|
if (perpId === zeroPerpId) {
|
|
3309
3451
|
throw new Error(`Position ${positionId} does not exist`);
|
|
3310
3452
|
}
|
|
3453
|
+
const isMaker = makerDetailsRaw != null && (makerDetailsRaw.tickLower !== 0 || makerDetailsRaw.tickUpper !== 0);
|
|
3311
3454
|
return {
|
|
3312
3455
|
perpId,
|
|
3313
3456
|
positionId,
|
|
3314
|
-
margin: Number(formatUnits(margin, 6)),
|
|
3457
|
+
margin: Number((0, import_viem3.formatUnits)(margin, 6)),
|
|
3315
3458
|
entryPerpDelta,
|
|
3316
3459
|
entryUsdDelta,
|
|
3317
3460
|
marginRatios: {
|
|
3318
3461
|
min: Number(marginRatios.min),
|
|
3319
3462
|
max: Number(marginRatios.max),
|
|
3320
3463
|
liq: Number(marginRatios.liq)
|
|
3321
|
-
}
|
|
3464
|
+
},
|
|
3465
|
+
makerDetails: isMaker ? {
|
|
3466
|
+
unlockTimestamp: Number(makerDetailsRaw.unlockTimestamp),
|
|
3467
|
+
tickLower: makerDetailsRaw.tickLower,
|
|
3468
|
+
tickUpper: makerDetailsRaw.tickUpper
|
|
3469
|
+
} : null
|
|
3322
3470
|
};
|
|
3323
3471
|
}, `getPositionRawData for position ${positionId}`);
|
|
3324
3472
|
}
|
|
3325
3473
|
};
|
|
3326
3474
|
|
|
3327
3475
|
// src/functions/open-position.ts
|
|
3328
|
-
|
|
3476
|
+
var import_viem4 = require("viem");
|
|
3329
3477
|
var OpenPosition = class _OpenPosition {
|
|
3330
3478
|
constructor(context, perpId, positionId, isLong, isMaker, txHash) {
|
|
3331
3479
|
this.context = context;
|
|
@@ -3362,7 +3510,7 @@ var OpenPosition = class _OpenPosition {
|
|
|
3362
3510
|
let _wasFullyClosed = false;
|
|
3363
3511
|
for (const log of receipt.logs) {
|
|
3364
3512
|
try {
|
|
3365
|
-
const openedDecoded = decodeEventLog({
|
|
3513
|
+
const openedDecoded = (0, import_viem4.decodeEventLog)({
|
|
3366
3514
|
abi: PERP_MANAGER_ABI,
|
|
3367
3515
|
data: log.data,
|
|
3368
3516
|
topics: log.topics,
|
|
@@ -3376,7 +3524,7 @@ var OpenPosition = class _OpenPosition {
|
|
|
3376
3524
|
}
|
|
3377
3525
|
} catch (_e) {
|
|
3378
3526
|
try {
|
|
3379
|
-
const closedDecoded = decodeEventLog({
|
|
3527
|
+
const closedDecoded = (0, import_viem4.decodeEventLog)({
|
|
3380
3528
|
abi: PERP_MANAGER_ABI,
|
|
3381
3529
|
data: log.data,
|
|
3382
3530
|
topics: log.topics,
|
|
@@ -3424,9 +3572,9 @@ var OpenPosition = class _OpenPosition {
|
|
|
3424
3572
|
);
|
|
3425
3573
|
}
|
|
3426
3574
|
return {
|
|
3427
|
-
pnl: Number(
|
|
3428
|
-
fundingPayment: Number(
|
|
3429
|
-
effectiveMargin: Number(
|
|
3575
|
+
pnl: Number((0, import_viem4.formatUnits)(pnl, 6)),
|
|
3576
|
+
fundingPayment: Number((0, import_viem4.formatUnits)(funding, 6)),
|
|
3577
|
+
effectiveMargin: Number((0, import_viem4.formatUnits)(uint256ToInt256(netMargin), 6)),
|
|
3430
3578
|
isLiquidatable: wasLiquidated
|
|
3431
3579
|
};
|
|
3432
3580
|
}, `liveDetails for position ${this.positionId}`);
|
|
@@ -3434,6 +3582,8 @@ var OpenPosition = class _OpenPosition {
|
|
|
3434
3582
|
};
|
|
3435
3583
|
|
|
3436
3584
|
// src/functions/perp.ts
|
|
3585
|
+
var TWAVG_WINDOW = 3600;
|
|
3586
|
+
var INTERVAL = 86400n;
|
|
3437
3587
|
function getPerpMark(perpData) {
|
|
3438
3588
|
return perpData.mark;
|
|
3439
3589
|
}
|
|
@@ -3449,9 +3599,58 @@ function getPerpFees(perpData) {
|
|
|
3449
3599
|
function getPerpTickSpacing(perpData) {
|
|
3450
3600
|
return perpData.tickSpacing;
|
|
3451
3601
|
}
|
|
3602
|
+
async function getFundingRate(context, perpId) {
|
|
3603
|
+
return withErrorHandling(async () => {
|
|
3604
|
+
const perpManagerAddr = context.deployments().perpManager;
|
|
3605
|
+
const cfg = await context.getPerpConfig(perpId);
|
|
3606
|
+
const [twAvgSqrtMarkX96, twAvgIndexX96] = await Promise.all([
|
|
3607
|
+
context.publicClient.readContract({
|
|
3608
|
+
address: perpManagerAddr,
|
|
3609
|
+
abi: PERP_MANAGER_ABI,
|
|
3610
|
+
functionName: "timeWeightedAvgSqrtPriceX96",
|
|
3611
|
+
args: [perpId, TWAVG_WINDOW]
|
|
3612
|
+
}),
|
|
3613
|
+
context.publicClient.readContract({
|
|
3614
|
+
address: cfg.beacon,
|
|
3615
|
+
abi: BEACON_ABI,
|
|
3616
|
+
functionName: "twAvg",
|
|
3617
|
+
args: [TWAVG_WINDOW]
|
|
3618
|
+
})
|
|
3619
|
+
]);
|
|
3620
|
+
const twAvgMarkX96 = twAvgSqrtMarkX96 * twAvgSqrtMarkX96 / Q96;
|
|
3621
|
+
const fundingDiffX96 = twAvgMarkX96 - twAvgIndexX96;
|
|
3622
|
+
const fundingPerSecondX96 = fundingDiffX96 / INTERVAL;
|
|
3623
|
+
return {
|
|
3624
|
+
ratePerDay: convertFundingDiffX96ToPercentPerPeriod(fundingDiffX96, INTERVAL, 86400n),
|
|
3625
|
+
ratePerMinute: convertFundingDiffX96ToPercentPerPeriod(fundingDiffX96, INTERVAL, 60n),
|
|
3626
|
+
rawX96: fundingPerSecondX96
|
|
3627
|
+
};
|
|
3628
|
+
}, `getFundingRate for perp ${perpId}`);
|
|
3629
|
+
}
|
|
3630
|
+
async function getIndexValue(context, perpId) {
|
|
3631
|
+
return withErrorHandling(async () => {
|
|
3632
|
+
const perpData = await context.getPerpData(perpId);
|
|
3633
|
+
return await context.publicClient.readContract({
|
|
3634
|
+
address: perpData.beacon,
|
|
3635
|
+
abi: BEACON_ABI,
|
|
3636
|
+
functionName: "index"
|
|
3637
|
+
});
|
|
3638
|
+
}, `getIndexValue for perp ${perpId}`);
|
|
3639
|
+
}
|
|
3640
|
+
async function getIndexTWAP(context, perpId, secondsAgo) {
|
|
3641
|
+
return withErrorHandling(async () => {
|
|
3642
|
+
const perpData = await context.getPerpData(perpId);
|
|
3643
|
+
return await context.publicClient.readContract({
|
|
3644
|
+
address: perpData.beacon,
|
|
3645
|
+
abi: BEACON_ABI,
|
|
3646
|
+
functionName: "twAvg",
|
|
3647
|
+
args: [secondsAgo]
|
|
3648
|
+
});
|
|
3649
|
+
}, `getIndexTWAP for perp ${perpId}`);
|
|
3650
|
+
}
|
|
3452
3651
|
|
|
3453
3652
|
// src/functions/perp-manager.ts
|
|
3454
|
-
|
|
3653
|
+
var import_viem5 = require("viem");
|
|
3455
3654
|
async function createPerp(context, params) {
|
|
3456
3655
|
return withErrorHandling(async () => {
|
|
3457
3656
|
const deployments = context.deployments();
|
|
@@ -3485,7 +3684,7 @@ async function createPerp(context, params) {
|
|
|
3485
3684
|
}
|
|
3486
3685
|
for (const log of receipt.logs) {
|
|
3487
3686
|
try {
|
|
3488
|
-
const decoded =
|
|
3687
|
+
const decoded = (0, import_viem5.decodeEventLog)({
|
|
3489
3688
|
abi: PERP_MANAGER_ABI,
|
|
3490
3689
|
data: log.data,
|
|
3491
3690
|
topics: log.topics,
|
|
@@ -3522,7 +3721,7 @@ async function openTakerPosition(context, perpId, params) {
|
|
|
3522
3721
|
const requiredAmount = marginScaled + totalFees;
|
|
3523
3722
|
const currentAllowance = await context.publicClient.readContract({
|
|
3524
3723
|
address: context.deployments().usdc,
|
|
3525
|
-
abi:
|
|
3724
|
+
abi: import_viem5.erc20Abi,
|
|
3526
3725
|
functionName: "allowance",
|
|
3527
3726
|
args: [context.walletClient.account.address, context.deployments().perpManager],
|
|
3528
3727
|
blockTag: "latest"
|
|
@@ -3555,7 +3754,7 @@ async function openTakerPosition(context, perpId, params) {
|
|
|
3555
3754
|
let takerPosId = null;
|
|
3556
3755
|
for (const log of receipt.logs) {
|
|
3557
3756
|
try {
|
|
3558
|
-
const decoded =
|
|
3757
|
+
const decoded = (0, import_viem5.decodeEventLog)({
|
|
3559
3758
|
abi: PERP_MANAGER_ABI,
|
|
3560
3759
|
data: log.data,
|
|
3561
3760
|
topics: log.topics
|
|
@@ -3643,11 +3842,17 @@ async function quoteOpenMakerPosition(context, perpId, params) {
|
|
|
3643
3842
|
return { perpDelta, usdDelta };
|
|
3644
3843
|
}, "quoteOpenMakerPosition");
|
|
3645
3844
|
}
|
|
3646
|
-
function applySlippage(delta, slippageTolerance) {
|
|
3647
|
-
if (delta >= 0n) return 0n;
|
|
3648
|
-
const absDelta = -delta;
|
|
3845
|
+
function applySlippage(delta, slippageTolerance, fallbackRef) {
|
|
3649
3846
|
const slippageBps = BigInt(Math.ceil(slippageTolerance * 1e4));
|
|
3650
|
-
|
|
3847
|
+
if (delta < 0n) {
|
|
3848
|
+
const absDelta = -delta;
|
|
3849
|
+
return absDelta + absDelta * slippageBps / 10000n;
|
|
3850
|
+
}
|
|
3851
|
+
if (fallbackRef !== void 0 && fallbackRef !== 0n) {
|
|
3852
|
+
const absRef = fallbackRef < 0n ? -fallbackRef : fallbackRef;
|
|
3853
|
+
return absRef * slippageBps / 10000n;
|
|
3854
|
+
}
|
|
3855
|
+
return 0n;
|
|
3651
3856
|
}
|
|
3652
3857
|
async function openMakerPosition(context, perpId, params) {
|
|
3653
3858
|
return withErrorHandling(async () => {
|
|
@@ -3673,12 +3878,12 @@ async function openMakerPosition(context, perpId, params) {
|
|
|
3673
3878
|
} else {
|
|
3674
3879
|
const quote = await quoteOpenMakerPosition(context, perpId, params);
|
|
3675
3880
|
const slippage = params.slippageTolerance ?? DEFAULT_MAKER_SLIPPAGE_TOLERANCE;
|
|
3676
|
-
maxAmt0In = applySlippage(quote.perpDelta, slippage);
|
|
3677
|
-
maxAmt1In = applySlippage(quote.usdDelta, slippage);
|
|
3881
|
+
maxAmt0In = applySlippage(quote.perpDelta, slippage, quote.usdDelta);
|
|
3882
|
+
maxAmt1In = applySlippage(quote.usdDelta, slippage, quote.perpDelta);
|
|
3678
3883
|
}
|
|
3679
3884
|
const currentAllowance = await context.publicClient.readContract({
|
|
3680
3885
|
address: context.deployments().usdc,
|
|
3681
|
-
abi:
|
|
3886
|
+
abi: import_viem5.erc20Abi,
|
|
3682
3887
|
functionName: "allowance",
|
|
3683
3888
|
args: [context.walletClient.account.address, context.deployments().perpManager],
|
|
3684
3889
|
blockTag: "latest"
|
|
@@ -3712,7 +3917,7 @@ async function openMakerPosition(context, perpId, params) {
|
|
|
3712
3917
|
let makerPosId = null;
|
|
3713
3918
|
for (const log of receipt.logs) {
|
|
3714
3919
|
try {
|
|
3715
|
-
const decoded =
|
|
3920
|
+
const decoded = (0, import_viem5.decodeEventLog)({
|
|
3716
3921
|
abi: PERP_MANAGER_ABI,
|
|
3717
3922
|
data: log.data,
|
|
3718
3923
|
topics: log.topics
|
|
@@ -3730,9 +3935,109 @@ async function openMakerPosition(context, perpId, params) {
|
|
|
3730
3935
|
return new OpenPosition(context, perpId, makerPosId, void 0, true, txHash);
|
|
3731
3936
|
}, "openMakerPosition");
|
|
3732
3937
|
}
|
|
3938
|
+
async function quoteTakerPosition(context, perpId, params) {
|
|
3939
|
+
return withErrorHandling(async () => {
|
|
3940
|
+
if (params.margin <= 0) {
|
|
3941
|
+
throw new Error("Margin must be greater than 0");
|
|
3942
|
+
}
|
|
3943
|
+
if (params.leverage <= 0) {
|
|
3944
|
+
throw new Error("Leverage must be greater than 0");
|
|
3945
|
+
}
|
|
3946
|
+
const marginScaled = scale6Decimals(params.margin);
|
|
3947
|
+
const marginRatio = Math.floor(NUMBER_1E6 / params.leverage);
|
|
3948
|
+
const result = await context.publicClient.simulateContract({
|
|
3949
|
+
address: context.deployments().perpManager,
|
|
3950
|
+
abi: PERP_MANAGER_ABI,
|
|
3951
|
+
functionName: "quoteOpenTakerPosition",
|
|
3952
|
+
args: [
|
|
3953
|
+
perpId,
|
|
3954
|
+
{
|
|
3955
|
+
holder: params.holder,
|
|
3956
|
+
isLong: params.isLong,
|
|
3957
|
+
margin: marginScaled,
|
|
3958
|
+
marginRatio,
|
|
3959
|
+
unspecifiedAmountLimit: params.isLong ? 0n : (1n << 128n) - 1n
|
|
3960
|
+
}
|
|
3961
|
+
],
|
|
3962
|
+
account: params.holder
|
|
3963
|
+
});
|
|
3964
|
+
const [unexpectedReason, perpDelta, usdDelta] = result.result;
|
|
3965
|
+
if (unexpectedReason && unexpectedReason !== "0x") {
|
|
3966
|
+
let errorName = "Quote simulation failed";
|
|
3967
|
+
try {
|
|
3968
|
+
const decoded = (0, import_viem5.decodeErrorResult)({
|
|
3969
|
+
abi: PERP_MANAGER_ABI,
|
|
3970
|
+
data: unexpectedReason
|
|
3971
|
+
});
|
|
3972
|
+
errorName = decoded.errorName;
|
|
3973
|
+
} catch {
|
|
3974
|
+
}
|
|
3975
|
+
throw new Error(errorName);
|
|
3976
|
+
}
|
|
3977
|
+
const absPerpDelta = perpDelta < 0n ? -perpDelta : perpDelta;
|
|
3978
|
+
const absUsdDelta = usdDelta < 0n ? -usdDelta : usdDelta;
|
|
3979
|
+
if (absPerpDelta === 0n) {
|
|
3980
|
+
throw new Error("Zero position size");
|
|
3981
|
+
}
|
|
3982
|
+
const fillPrice = Number(absUsdDelta) / Number(absPerpDelta);
|
|
3983
|
+
return { perpDelta, usdDelta, fillPrice };
|
|
3984
|
+
}, "quoteTakerPosition");
|
|
3985
|
+
}
|
|
3986
|
+
async function adjustMargin(context, positionId, marginDelta) {
|
|
3987
|
+
return withErrorHandling(async () => {
|
|
3988
|
+
const deployments = context.deployments();
|
|
3989
|
+
if (marginDelta > 0n) {
|
|
3990
|
+
const currentAllowance = await context.publicClient.readContract({
|
|
3991
|
+
address: deployments.usdc,
|
|
3992
|
+
abi: import_viem5.erc20Abi,
|
|
3993
|
+
functionName: "allowance",
|
|
3994
|
+
args: [context.walletClient.account.address, deployments.perpManager],
|
|
3995
|
+
blockTag: "latest"
|
|
3996
|
+
});
|
|
3997
|
+
if (currentAllowance < marginDelta) {
|
|
3998
|
+
await approveUsdc(context, marginDelta);
|
|
3999
|
+
}
|
|
4000
|
+
}
|
|
4001
|
+
const { request } = await context.publicClient.simulateContract({
|
|
4002
|
+
address: deployments.perpManager,
|
|
4003
|
+
abi: PERP_MANAGER_ABI,
|
|
4004
|
+
functionName: "adjustMargin",
|
|
4005
|
+
args: [{ posId: positionId, marginDelta }],
|
|
4006
|
+
account: context.walletClient.account
|
|
4007
|
+
});
|
|
4008
|
+
const txHash = await context.walletClient.writeContract(request);
|
|
4009
|
+
return { txHash };
|
|
4010
|
+
}, `adjustMargin for position ${positionId}`);
|
|
4011
|
+
}
|
|
4012
|
+
async function adjustNotional(context, positionId, usdDelta, perpLimit) {
|
|
4013
|
+
return withErrorHandling(async () => {
|
|
4014
|
+
const deployments = context.deployments();
|
|
4015
|
+
if (usdDelta > 0n) {
|
|
4016
|
+
const currentAllowance = await context.publicClient.readContract({
|
|
4017
|
+
address: deployments.usdc,
|
|
4018
|
+
abi: import_viem5.erc20Abi,
|
|
4019
|
+
functionName: "allowance",
|
|
4020
|
+
args: [context.walletClient.account.address, deployments.perpManager],
|
|
4021
|
+
blockTag: "latest"
|
|
4022
|
+
});
|
|
4023
|
+
if (currentAllowance < usdDelta) {
|
|
4024
|
+
await approveUsdc(context, usdDelta);
|
|
4025
|
+
}
|
|
4026
|
+
}
|
|
4027
|
+
const { request } = await context.publicClient.simulateContract({
|
|
4028
|
+
address: deployments.perpManager,
|
|
4029
|
+
abi: PERP_MANAGER_ABI,
|
|
4030
|
+
functionName: "adjustNotional",
|
|
4031
|
+
args: [{ posId: positionId, usdDelta, perpLimit }],
|
|
4032
|
+
account: context.walletClient.account
|
|
4033
|
+
});
|
|
4034
|
+
const txHash = await context.walletClient.writeContract(request);
|
|
4035
|
+
return { txHash };
|
|
4036
|
+
}, `adjustNotional for position ${positionId}`);
|
|
4037
|
+
}
|
|
3733
4038
|
|
|
3734
4039
|
// src/functions/position.ts
|
|
3735
|
-
|
|
4040
|
+
var import_viem6 = require("viem");
|
|
3736
4041
|
function getPositionPerpId(positionData) {
|
|
3737
4042
|
return positionData.perpId;
|
|
3738
4043
|
}
|
|
@@ -3760,6 +4065,131 @@ function getPositionEffectiveMargin(positionData) {
|
|
|
3760
4065
|
function getPositionIsLiquidatable(positionData) {
|
|
3761
4066
|
return positionData.liveDetails.isLiquidatable;
|
|
3762
4067
|
}
|
|
4068
|
+
async function quoteClosePosition(context, positionId) {
|
|
4069
|
+
return withErrorHandling(async () => {
|
|
4070
|
+
const result = await context.publicClient.readContract({
|
|
4071
|
+
address: context.deployments().perpManager,
|
|
4072
|
+
abi: PERP_MANAGER_ABI,
|
|
4073
|
+
functionName: "quoteClosePosition",
|
|
4074
|
+
args: [positionId]
|
|
4075
|
+
});
|
|
4076
|
+
const [unexpectedReason, pnl, funding, netMargin, wasLiquidated] = result;
|
|
4077
|
+
if (unexpectedReason && unexpectedReason !== "0x") {
|
|
4078
|
+
let errorName = "Quote simulation failed";
|
|
4079
|
+
try {
|
|
4080
|
+
const decoded = (0, import_viem6.decodeErrorResult)({
|
|
4081
|
+
abi: PERP_MANAGER_ABI,
|
|
4082
|
+
data: unexpectedReason
|
|
4083
|
+
});
|
|
4084
|
+
errorName = decoded.errorName;
|
|
4085
|
+
} catch {
|
|
4086
|
+
}
|
|
4087
|
+
throw new Error(errorName);
|
|
4088
|
+
}
|
|
4089
|
+
return {
|
|
4090
|
+
pnl: Number((0, import_viem6.formatUnits)(pnl, 6)),
|
|
4091
|
+
// Negate so positive = user receives funding, matching live details convention
|
|
4092
|
+
funding: -Number((0, import_viem6.formatUnits)(funding, 6)),
|
|
4093
|
+
netMargin: Number((0, import_viem6.formatUnits)(uint256ToInt256(netMargin), 6)),
|
|
4094
|
+
wasLiquidated
|
|
4095
|
+
};
|
|
4096
|
+
}, `quoteClosePosition for position ${positionId}`);
|
|
4097
|
+
}
|
|
4098
|
+
async function closePositionWithQuote(context, _perpId, positionId, slippageTolerance = 0.01) {
|
|
4099
|
+
return withErrorHandling(async () => {
|
|
4100
|
+
const rawData = await context.getPositionRawData(positionId);
|
|
4101
|
+
const isMaker = rawData.makerDetails !== null;
|
|
4102
|
+
const result = await context.publicClient.readContract({
|
|
4103
|
+
address: context.deployments().perpManager,
|
|
4104
|
+
abi: PERP_MANAGER_ABI,
|
|
4105
|
+
functionName: "quoteClosePosition",
|
|
4106
|
+
args: [positionId]
|
|
4107
|
+
});
|
|
4108
|
+
const [unexpectedReason, quotePnl] = result;
|
|
4109
|
+
if (unexpectedReason && unexpectedReason !== "0x") {
|
|
4110
|
+
throw new Error("Quote failed \u2014 position may be invalid or already closed");
|
|
4111
|
+
}
|
|
4112
|
+
let contractParams = {
|
|
4113
|
+
posId: positionId,
|
|
4114
|
+
minAmt0Out: 0n,
|
|
4115
|
+
minAmt1Out: 0n,
|
|
4116
|
+
maxAmt1In: 0n
|
|
4117
|
+
};
|
|
4118
|
+
const canonicalPerpId = rawData.perpId;
|
|
4119
|
+
if (!isMaker) {
|
|
4120
|
+
const isLong = rawData.entryPerpDelta > 0n;
|
|
4121
|
+
const slippageBps = BigInt(Math.ceil(slippageTolerance * 1e4));
|
|
4122
|
+
const absEntryUsd = rawData.entryUsdDelta < 0n ? -rawData.entryUsdDelta : rawData.entryUsdDelta;
|
|
4123
|
+
if (isLong) {
|
|
4124
|
+
const swapRevenue = quotePnl + absEntryUsd;
|
|
4125
|
+
const minOut = swapRevenue > 0n ? swapRevenue - swapRevenue * slippageBps / 10000n : 0n;
|
|
4126
|
+
contractParams = {
|
|
4127
|
+
posId: positionId,
|
|
4128
|
+
minAmt0Out: 0n,
|
|
4129
|
+
minAmt1Out: minOut,
|
|
4130
|
+
maxAmt1In: 0n
|
|
4131
|
+
};
|
|
4132
|
+
} else {
|
|
4133
|
+
const swapCost = absEntryUsd - quotePnl;
|
|
4134
|
+
const maxIn = swapCost > 0n ? swapCost + swapCost * slippageBps / 10000n : 0n;
|
|
4135
|
+
contractParams = {
|
|
4136
|
+
posId: positionId,
|
|
4137
|
+
minAmt0Out: 0n,
|
|
4138
|
+
minAmt1Out: 0n,
|
|
4139
|
+
maxAmt1In: maxIn
|
|
4140
|
+
};
|
|
4141
|
+
}
|
|
4142
|
+
}
|
|
4143
|
+
const { request } = await context.publicClient.simulateContract({
|
|
4144
|
+
address: context.deployments().perpManager,
|
|
4145
|
+
abi: PERP_MANAGER_ABI,
|
|
4146
|
+
functionName: "closePosition",
|
|
4147
|
+
args: [contractParams],
|
|
4148
|
+
account: context.walletClient.account,
|
|
4149
|
+
gas: 500000n
|
|
4150
|
+
});
|
|
4151
|
+
const txHash = await context.walletClient.writeContract(request);
|
|
4152
|
+
const receipt = await context.publicClient.waitForTransactionReceipt({
|
|
4153
|
+
hash: txHash
|
|
4154
|
+
});
|
|
4155
|
+
if (receipt.status === "reverted") {
|
|
4156
|
+
throw new Error(`Transaction reverted. Hash: ${txHash}`);
|
|
4157
|
+
}
|
|
4158
|
+
let newPositionId = null;
|
|
4159
|
+
for (const log of receipt.logs) {
|
|
4160
|
+
try {
|
|
4161
|
+
const decoded = (0, import_viem6.decodeEventLog)({
|
|
4162
|
+
abi: PERP_MANAGER_ABI,
|
|
4163
|
+
data: log.data,
|
|
4164
|
+
topics: log.topics,
|
|
4165
|
+
eventName: "PositionOpened"
|
|
4166
|
+
});
|
|
4167
|
+
const eventPerpId = decoded.args.perpId.toLowerCase();
|
|
4168
|
+
const eventPosId = decoded.args.posId;
|
|
4169
|
+
if (eventPerpId === canonicalPerpId.toLowerCase() && eventPosId !== positionId) {
|
|
4170
|
+
newPositionId = eventPosId;
|
|
4171
|
+
break;
|
|
4172
|
+
}
|
|
4173
|
+
} catch (_e) {
|
|
4174
|
+
}
|
|
4175
|
+
}
|
|
4176
|
+
if (!newPositionId) {
|
|
4177
|
+
return { position: null, txHash };
|
|
4178
|
+
}
|
|
4179
|
+
return {
|
|
4180
|
+
position: {
|
|
4181
|
+
perpId: canonicalPerpId,
|
|
4182
|
+
positionId: newPositionId,
|
|
4183
|
+
liveDetails: await getPositionLiveDetailsFromContract(
|
|
4184
|
+
context,
|
|
4185
|
+
canonicalPerpId,
|
|
4186
|
+
newPositionId
|
|
4187
|
+
)
|
|
4188
|
+
},
|
|
4189
|
+
txHash
|
|
4190
|
+
};
|
|
4191
|
+
}, `closePositionWithQuote for position ${positionId}`);
|
|
4192
|
+
}
|
|
3763
4193
|
async function closePosition(context, perpId, positionId, params) {
|
|
3764
4194
|
return withErrorHandling(async () => {
|
|
3765
4195
|
const contractParams = {
|
|
@@ -3778,14 +4208,16 @@ async function closePosition(context, perpId, positionId, params) {
|
|
|
3778
4208
|
// Provide explicit gas limit to avoid estimation issues
|
|
3779
4209
|
});
|
|
3780
4210
|
const txHash = await context.walletClient.writeContract(request);
|
|
3781
|
-
const receipt = await context.publicClient.waitForTransactionReceipt({
|
|
4211
|
+
const receipt = await context.publicClient.waitForTransactionReceipt({
|
|
4212
|
+
hash: txHash
|
|
4213
|
+
});
|
|
3782
4214
|
if (receipt.status === "reverted") {
|
|
3783
4215
|
throw new Error(`Transaction reverted. Hash: ${txHash}`);
|
|
3784
4216
|
}
|
|
3785
4217
|
let newPositionId = null;
|
|
3786
4218
|
for (const log of receipt.logs) {
|
|
3787
4219
|
try {
|
|
3788
|
-
const decoded =
|
|
4220
|
+
const decoded = (0, import_viem6.decodeEventLog)({
|
|
3789
4221
|
abi: PERP_MANAGER_ABI,
|
|
3790
4222
|
data: log.data,
|
|
3791
4223
|
topics: log.topics,
|
|
@@ -3828,9 +4260,10 @@ async function getPositionLiveDetailsFromContract(context, _perpId, positionId)
|
|
|
3828
4260
|
);
|
|
3829
4261
|
}
|
|
3830
4262
|
return {
|
|
3831
|
-
pnl: Number(
|
|
3832
|
-
|
|
3833
|
-
|
|
4263
|
+
pnl: Number((0, import_viem6.formatUnits)(pnl, 6)),
|
|
4264
|
+
// Negate so positive = user receives funding, matching quoteClosePosition convention
|
|
4265
|
+
fundingPayment: -Number((0, import_viem6.formatUnits)(funding, 6)),
|
|
4266
|
+
effectiveMargin: Number((0, import_viem6.formatUnits)(uint256ToInt256(netMargin), 6)),
|
|
3834
4267
|
isLiquidatable: wasLiquidated
|
|
3835
4268
|
};
|
|
3836
4269
|
}, `getPositionLiveDetailsFromContract for position ${positionId}`);
|
|
@@ -3874,6 +4307,34 @@ function calculateLiquidationPrice(rawData, isLong) {
|
|
|
3874
4307
|
return liqPrice;
|
|
3875
4308
|
}
|
|
3876
4309
|
}
|
|
4310
|
+
function calculatePnlPercentage(pnl, funding, margin) {
|
|
4311
|
+
const totalPnl = pnl + funding;
|
|
4312
|
+
const initialMargin = margin - totalPnl;
|
|
4313
|
+
if (initialMargin <= 0) return 0;
|
|
4314
|
+
return totalPnl / initialMargin * 100;
|
|
4315
|
+
}
|
|
4316
|
+
function calculateClosePositionParams(opts) {
|
|
4317
|
+
if (opts.isMaker) {
|
|
4318
|
+
return { minAmt0Out: 0, minAmt1Out: 0, maxAmt1In: 0 };
|
|
4319
|
+
}
|
|
4320
|
+
if (typeof opts.isLong !== "boolean") {
|
|
4321
|
+
throw new Error("isLong must be explicitly set for taker positions");
|
|
4322
|
+
}
|
|
4323
|
+
const absNotional = Math.abs(opts.notional);
|
|
4324
|
+
if (opts.isLong) {
|
|
4325
|
+
return {
|
|
4326
|
+
minAmt0Out: 0,
|
|
4327
|
+
minAmt1Out: Math.max(0, absNotional * (1 - opts.slippagePercent / 100)),
|
|
4328
|
+
maxAmt1In: 0
|
|
4329
|
+
};
|
|
4330
|
+
} else {
|
|
4331
|
+
return {
|
|
4332
|
+
minAmt0Out: 0,
|
|
4333
|
+
minAmt1Out: 0,
|
|
4334
|
+
maxAmt1In: absNotional * (1 + opts.slippagePercent / 100)
|
|
4335
|
+
};
|
|
4336
|
+
}
|
|
4337
|
+
}
|
|
3877
4338
|
|
|
3878
4339
|
// src/functions/user.ts
|
|
3879
4340
|
function getUserUsdcBalance(userData) {
|
|
@@ -3885,15 +4346,19 @@ function getUserOpenPositions(userData) {
|
|
|
3885
4346
|
function getUserWalletAddress(userData) {
|
|
3886
4347
|
return userData.walletAddress;
|
|
3887
4348
|
}
|
|
3888
|
-
export
|
|
4349
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
4350
|
+
0 && (module.exports = {
|
|
3889
4351
|
BEACON_ABI,
|
|
3890
4352
|
BIGINT_1E6,
|
|
3891
4353
|
ContractError,
|
|
4354
|
+
DEFAULT_MAKER_SLIPPAGE_TOLERANCE,
|
|
3892
4355
|
ErrorCategory,
|
|
3893
4356
|
ErrorSource,
|
|
4357
|
+
INT256_THRESHOLD,
|
|
3894
4358
|
InsufficientFundsError,
|
|
3895
4359
|
MAX_PRICE,
|
|
3896
4360
|
MAX_TICK,
|
|
4361
|
+
MAX_UINT128,
|
|
3897
4362
|
MIN_PRICE,
|
|
3898
4363
|
MIN_TICK,
|
|
3899
4364
|
NUMBER_1E6,
|
|
@@ -3904,18 +4369,31 @@ export {
|
|
|
3904
4369
|
Q96,
|
|
3905
4370
|
RPCError,
|
|
3906
4371
|
TransactionRejectedError,
|
|
4372
|
+
UINT256_MAX,
|
|
3907
4373
|
ValidationError,
|
|
4374
|
+
adjustMargin,
|
|
4375
|
+
adjustNotional,
|
|
4376
|
+
applySlippage,
|
|
3908
4377
|
approveUsdc,
|
|
3909
4378
|
calculateAlignedTicks,
|
|
4379
|
+
calculateClosePositionParams,
|
|
3910
4380
|
calculateEntryPrice,
|
|
3911
4381
|
calculateLeverage,
|
|
3912
4382
|
calculateLiquidationPrice,
|
|
3913
4383
|
calculateLiquidityForTargetRatio,
|
|
4384
|
+
calculatePnlPercentage,
|
|
3914
4385
|
calculatePositionSize,
|
|
3915
4386
|
calculatePositionValue,
|
|
3916
4387
|
closePosition,
|
|
4388
|
+
closePositionWithQuote,
|
|
4389
|
+
convertFundingDiffX96ToPercentPerPeriod,
|
|
4390
|
+
convertFundingPerSecondX96ToPercentPerDay,
|
|
4391
|
+
convertFundingPerSecondX96ToPercentPerMinute,
|
|
3917
4392
|
createPerp,
|
|
3918
4393
|
estimateLiquidity,
|
|
4394
|
+
getFundingRate,
|
|
4395
|
+
getIndexTWAP,
|
|
4396
|
+
getIndexValue,
|
|
3919
4397
|
getPerpBeacon,
|
|
3920
4398
|
getPerpBounds,
|
|
3921
4399
|
getPerpFees,
|
|
@@ -3932,6 +4410,7 @@ export {
|
|
|
3932
4410
|
getPositionPerpId,
|
|
3933
4411
|
getPositionPnl,
|
|
3934
4412
|
getRpcUrl,
|
|
4413
|
+
getUsdcAllowance,
|
|
3935
4414
|
getUserOpenPositions,
|
|
3936
4415
|
getUserUsdcBalance,
|
|
3937
4416
|
getUserWalletAddress,
|
|
@@ -3941,13 +4420,16 @@ export {
|
|
|
3941
4420
|
parseContractError,
|
|
3942
4421
|
priceToSqrtPriceX96,
|
|
3943
4422
|
priceToTick,
|
|
4423
|
+
quoteClosePosition,
|
|
3944
4424
|
quoteOpenMakerPosition,
|
|
4425
|
+
quoteTakerPosition,
|
|
3945
4426
|
scale6Decimals,
|
|
3946
4427
|
scaleFrom6Decimals,
|
|
3947
4428
|
scaleFromX96,
|
|
3948
4429
|
scaleToX96,
|
|
3949
4430
|
sqrtPriceX96ToPrice,
|
|
3950
4431
|
tickToPrice,
|
|
4432
|
+
uint256ToInt256,
|
|
3951
4433
|
withErrorHandling
|
|
3952
|
-
};
|
|
3953
|
-
//# sourceMappingURL=index.
|
|
4434
|
+
});
|
|
4435
|
+
//# sourceMappingURL=index.cjs.map
|