@strobelabs/perpcity-sdk 0.1.5 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -36
- package/dist/index.d.mts +3949 -37
- package/dist/index.d.ts +3949 -37
- package/dist/index.js +900 -213
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +894 -211
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -3
package/dist/index.mjs
CHANGED
|
@@ -1,51 +1,53 @@
|
|
|
1
1
|
// src/context.ts
|
|
2
|
-
import { publicActions } from "viem";
|
|
3
2
|
import { GraphQLClient } from "graphql-request";
|
|
3
|
+
|
|
4
|
+
// src/deployments.ts
|
|
5
|
+
var DEPLOYMENTS = {
|
|
6
|
+
// Base Sepolia
|
|
7
|
+
[84532]: {
|
|
8
|
+
perpManager: "0x59F1766b77fd67af6c80217C2025A0D536998000",
|
|
9
|
+
usdc: "0xC1a5D4E99BB224713dd179eA9CA2Fa6600706210",
|
|
10
|
+
goldskyPublic: "https://api.goldsky.com/api/public/project_cmbawn40q70fj01ws4jmsfj7f/subgraphs/perp-city/36ac28e6-20250925_150813/gn",
|
|
11
|
+
goldskyPrivate: "https://api.goldsky.com/api/private/project_cmbawn40q70fj01ws4jmsfj7f/subgraphs/perp-city/36ac28e6-20250925_150813/gn"
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
// src/context.ts
|
|
16
|
+
import { publicActions } from "viem";
|
|
4
17
|
var PerpCityContext = class {
|
|
5
18
|
constructor(config) {
|
|
6
19
|
this.walletClient = config.walletClient.extend(publicActions);
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
20
|
+
const chainId = this.validateChainId();
|
|
21
|
+
const deployments = DEPLOYMENTS[chainId];
|
|
22
|
+
const headers = {};
|
|
23
|
+
let goldskyEndpoint;
|
|
24
|
+
if (config.goldskyBearerToken) {
|
|
25
|
+
headers.authorization = `Bearer ${config.goldskyBearerToken}`;
|
|
26
|
+
goldskyEndpoint = deployments.goldskyPrivate;
|
|
27
|
+
} else {
|
|
28
|
+
goldskyEndpoint = deployments.goldskyPublic;
|
|
29
|
+
}
|
|
30
|
+
this.goldskyClient = new GraphQLClient(goldskyEndpoint, {
|
|
31
|
+
headers
|
|
32
|
+
});
|
|
11
33
|
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
this.context = context;
|
|
21
|
-
this.perpId = perpId;
|
|
22
|
-
this.positionId = positionId;
|
|
34
|
+
validateChainId() {
|
|
35
|
+
const chainId = this.walletClient.chain?.id;
|
|
36
|
+
if (!chainId) throw new Error(`Chain ID is not set.`);
|
|
37
|
+
if (!DEPLOYMENTS[chainId]) throw new Error(`Unsupported chainId: ${chainId}.`);
|
|
38
|
+
return chainId;
|
|
39
|
+
}
|
|
40
|
+
deployments() {
|
|
41
|
+
return DEPLOYMENTS[this.validateChainId()];
|
|
23
42
|
}
|
|
24
|
-
// async closePosition(params: ClosePositionParams): Promise<Position | null> {
|
|
25
|
-
// const contractParams = {
|
|
26
|
-
// positionId: this.positionId,
|
|
27
|
-
// minAmt0Out: scale6Decimals(params.minAmt0Out),
|
|
28
|
-
// minAmt1Out: scale6Decimals(params.minAmt1Out),
|
|
29
|
-
// maxAmt1In: scale6Decimals(params.maxAmt1In),
|
|
30
|
-
// };
|
|
31
|
-
// const { result, request } = await this.context.walletClient.extend(publicActions).simulateContract({
|
|
32
|
-
// address: this.context.perpManagerAddress,
|
|
33
|
-
// abi: this.context.perpManagerAbi,
|
|
34
|
-
// functionName: 'closePosition',
|
|
35
|
-
// args: [this.perpId, contractParams],
|
|
36
|
-
// account: this.context.walletClient.account,
|
|
37
|
-
// });
|
|
38
|
-
// await this.context.walletClient.writeContract(request);
|
|
39
|
-
// const takerPositionId = result[0];
|
|
40
|
-
// if (takerPositionId === 0n) {
|
|
41
|
-
// return null;
|
|
42
|
-
// }
|
|
43
|
-
// return new Position(this.context, this.perpId, takerPositionId);
|
|
44
|
-
// }
|
|
45
43
|
};
|
|
46
44
|
|
|
45
|
+
// src/entities/openPosition.ts
|
|
46
|
+
import { publicActions as publicActions2 } from "viem";
|
|
47
|
+
|
|
47
48
|
// src/utils/constants.ts
|
|
48
|
-
var
|
|
49
|
+
var NUMBER_1E6 = 1e6;
|
|
50
|
+
var BIGINT_1E6 = 1000000n;
|
|
49
51
|
var Q96 = 79228162514264337593543950336n;
|
|
50
52
|
|
|
51
53
|
// src/utils/conversions.ts
|
|
@@ -53,183 +55,58 @@ function priceToSqrtPriceX96(price) {
|
|
|
53
55
|
if (price > Number.MAX_SAFE_INTEGER) {
|
|
54
56
|
throw new Error("Price too large");
|
|
55
57
|
}
|
|
56
|
-
const scaledSqrtPrice = Math.sqrt(price) *
|
|
57
|
-
return BigInt(Math.floor(scaledSqrtPrice)) * Q96 / BigInt(
|
|
58
|
+
const scaledSqrtPrice = Math.sqrt(price) * NUMBER_1E6;
|
|
59
|
+
return BigInt(Math.floor(scaledSqrtPrice)) * Q96 / BigInt(NUMBER_1E6);
|
|
58
60
|
}
|
|
59
61
|
function scale6Decimals(amount) {
|
|
60
|
-
if (amount > Number.MAX_SAFE_INTEGER /
|
|
62
|
+
if (amount > Number.MAX_SAFE_INTEGER / NUMBER_1E6) {
|
|
61
63
|
throw new Error("Amount too large");
|
|
62
64
|
}
|
|
63
|
-
return BigInt(Math.floor(amount *
|
|
65
|
+
return BigInt(Math.floor(amount * NUMBER_1E6));
|
|
64
66
|
}
|
|
65
|
-
function
|
|
66
|
-
return BigInt(scale6Decimals(amount)) * Q96 / BigInt(
|
|
67
|
+
function scaleToX96(amount) {
|
|
68
|
+
return BigInt(scale6Decimals(amount)) * Q96 / BigInt(NUMBER_1E6);
|
|
69
|
+
}
|
|
70
|
+
function scaleFromX96(valueX96) {
|
|
71
|
+
const valueScaled6Decimals = valueX96 * BIGINT_1E6 / Q96;
|
|
72
|
+
if (valueScaled6Decimals > Number.MAX_SAFE_INTEGER) {
|
|
73
|
+
throw new Error("Value too large");
|
|
74
|
+
}
|
|
75
|
+
return Number(valueScaled6Decimals) / NUMBER_1E6;
|
|
67
76
|
}
|
|
68
77
|
function priceToTick(price, roundDown) {
|
|
69
|
-
const
|
|
70
|
-
return roundDown ? Math.floor(
|
|
78
|
+
const logPrice = Math.log(price) / Math.log(1.0001);
|
|
79
|
+
return roundDown ? Math.floor(logPrice) : Math.ceil(logPrice);
|
|
80
|
+
}
|
|
81
|
+
function sqrtPriceX96ToPrice(sqrtPriceX96) {
|
|
82
|
+
const priceX96 = sqrtPriceX96 * sqrtPriceX96 / Q96;
|
|
83
|
+
return scaleFromX96(priceX96);
|
|
84
|
+
}
|
|
85
|
+
function marginRatioToLeverage(marginRatio) {
|
|
86
|
+
return NUMBER_1E6 / marginRatio;
|
|
87
|
+
}
|
|
88
|
+
function scaleFrom6Decimals(value) {
|
|
89
|
+
return value / NUMBER_1E6;
|
|
71
90
|
}
|
|
72
91
|
|
|
73
92
|
// src/utils/approve.ts
|
|
74
|
-
import { erc20Abi
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
const
|
|
78
|
-
|
|
93
|
+
import { erc20Abi } from "viem";
|
|
94
|
+
var DEFAULT_CONFIRMATIONS = 2;
|
|
95
|
+
async function approveUsdc(context, amount, confirmations = DEFAULT_CONFIRMATIONS) {
|
|
96
|
+
const deployments = context.deployments();
|
|
97
|
+
const { request } = await context.walletClient.simulateContract({
|
|
98
|
+
address: deployments.usdc,
|
|
79
99
|
abi: erc20Abi,
|
|
80
100
|
functionName: "approve",
|
|
81
|
-
args: [
|
|
101
|
+
args: [deployments.perpManager, amount],
|
|
82
102
|
account: context.walletClient.account
|
|
83
103
|
});
|
|
84
104
|
const hash = await context.walletClient.writeContract(request);
|
|
85
|
-
await context.walletClient.
|
|
86
|
-
confirmations
|
|
105
|
+
await context.walletClient.waitForTransactionReceipt({
|
|
106
|
+
confirmations,
|
|
87
107
|
hash
|
|
88
108
|
});
|
|
89
109
|
}
|
|
90
|
-
async function getUsdcAddress(context) {
|
|
91
|
-
return await context.walletClient.extend(publicActions2).readContract({
|
|
92
|
-
address: context.perpManagerAddress,
|
|
93
|
-
abi: context.perpManagerAbi,
|
|
94
|
-
functionName: "USDC"
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// src/utils/liquidity.ts
|
|
99
|
-
import { publicActions as publicActions3 } from "viem";
|
|
100
|
-
async function estimateLiquidity(context, tickLower, tickUpper, usdScaled) {
|
|
101
|
-
return await context.walletClient.extend(publicActions3).readContract({
|
|
102
|
-
address: context.perpManagerAddress,
|
|
103
|
-
abi: context.perpManagerAbi,
|
|
104
|
-
functionName: "estimateLiquidityForAount1",
|
|
105
|
-
args: [tickLower, tickUpper, usdScaled]
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// src/entities/perp.ts
|
|
110
|
-
import { nearestUsableTick } from "@uniswap/v3-sdk";
|
|
111
|
-
var Perp = class {
|
|
112
|
-
constructor(context, id) {
|
|
113
|
-
this.context = context;
|
|
114
|
-
this.id = id;
|
|
115
|
-
}
|
|
116
|
-
// READS
|
|
117
|
-
async getTickSpacing() {
|
|
118
|
-
const tickSpacing = await this.context.walletClient.extend(publicActions4).readContract({
|
|
119
|
-
address: this.context.perpManagerAddress,
|
|
120
|
-
abi: this.context.perpManagerAbi,
|
|
121
|
-
functionName: "tickSpacing",
|
|
122
|
-
args: [this.id]
|
|
123
|
-
});
|
|
124
|
-
return Number(tickSpacing);
|
|
125
|
-
}
|
|
126
|
-
// WRITES
|
|
127
|
-
async approveAndOpenMakerPosition(params) {
|
|
128
|
-
await approveUsdc(this.context, scale6Decimals(params.margin));
|
|
129
|
-
return await this.openMakerPosition(params);
|
|
130
|
-
}
|
|
131
|
-
async approveAndOpenTakerPosition(params) {
|
|
132
|
-
await approveUsdc(this.context, scale6Decimals(params.margin));
|
|
133
|
-
return await this.openTakerPosition(params);
|
|
134
|
-
}
|
|
135
|
-
async openMakerPosition(params) {
|
|
136
|
-
const tickSpacing = await this.getTickSpacing();
|
|
137
|
-
const scaledUsd = scale6Decimals(params.margin);
|
|
138
|
-
const tickLower = BigInt(nearestUsableTick(priceToTick(params.priceLower, true), tickSpacing));
|
|
139
|
-
const tickUpper = BigInt(nearestUsableTick(priceToTick(params.priceUpper, false), tickSpacing));
|
|
140
|
-
const contractParams = {
|
|
141
|
-
margin: scaledUsd,
|
|
142
|
-
liquidity: await estimateLiquidity(this.context, tickLower, tickUpper, scaledUsd),
|
|
143
|
-
tickLower,
|
|
144
|
-
tickUpper,
|
|
145
|
-
maxAmt0In: scale6Decimals(params.maxAmt0In),
|
|
146
|
-
maxAmt1In: scale6Decimals(params.maxAmt1In)
|
|
147
|
-
};
|
|
148
|
-
const { result, request } = await this.context.walletClient.extend(publicActions4).simulateContract({
|
|
149
|
-
address: this.context.perpManagerAddress,
|
|
150
|
-
abi: this.context.perpManagerAbi,
|
|
151
|
-
functionName: "openMakerPosition",
|
|
152
|
-
args: [this.id, contractParams],
|
|
153
|
-
account: this.context.walletClient.account
|
|
154
|
-
});
|
|
155
|
-
await this.context.walletClient.writeContract(request);
|
|
156
|
-
return new Position(this.context, this.id, result);
|
|
157
|
-
}
|
|
158
|
-
async openTakerPosition(params) {
|
|
159
|
-
const contractParams = {
|
|
160
|
-
isLong: params.isLong,
|
|
161
|
-
margin: scale6Decimals(params.margin),
|
|
162
|
-
levX96: scaleX96(params.leverage),
|
|
163
|
-
unspecifiedAmountLimit: scale6Decimals(params.unspecifiedAmountLimit)
|
|
164
|
-
};
|
|
165
|
-
const { result, request } = await this.context.walletClient.extend(publicActions4).simulateContract({
|
|
166
|
-
address: this.context.perpManagerAddress,
|
|
167
|
-
abi: this.context.perpManagerAbi,
|
|
168
|
-
functionName: "openTakerPosition",
|
|
169
|
-
args: [this.id, contractParams],
|
|
170
|
-
account: this.context.walletClient.account
|
|
171
|
-
});
|
|
172
|
-
await this.context.walletClient.writeContract(request);
|
|
173
|
-
return new Position(this.context, this.id, result);
|
|
174
|
-
}
|
|
175
|
-
};
|
|
176
|
-
|
|
177
|
-
// src/entities/perp-collection.ts
|
|
178
|
-
var PerpCollection = class {
|
|
179
|
-
constructor(context, perps) {
|
|
180
|
-
this.context = context;
|
|
181
|
-
this.perps = perps;
|
|
182
|
-
}
|
|
183
|
-
};
|
|
184
|
-
|
|
185
|
-
// src/entities/perp-manager.ts
|
|
186
|
-
import { publicActions as publicActions5 } from "viem";
|
|
187
|
-
import { gql } from "graphql-request";
|
|
188
|
-
import { parse } from "graphql";
|
|
189
|
-
var PerpManager = class {
|
|
190
|
-
constructor(context) {
|
|
191
|
-
this.context = context;
|
|
192
|
-
}
|
|
193
|
-
// READS
|
|
194
|
-
async getPerps() {
|
|
195
|
-
const query = parse(gql`
|
|
196
|
-
{
|
|
197
|
-
perps {
|
|
198
|
-
id
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
`);
|
|
202
|
-
const response = await this.context.goldskyClient.request(query);
|
|
203
|
-
const perps = response.perps.map(
|
|
204
|
-
(perpData) => new Perp(this.context, perpData.id)
|
|
205
|
-
);
|
|
206
|
-
return new PerpCollection(this.context, perps);
|
|
207
|
-
}
|
|
208
|
-
// WRITES
|
|
209
|
-
async createPerp(params) {
|
|
210
|
-
const sqrtPriceX96 = priceToSqrtPriceX96(params.startingPrice);
|
|
211
|
-
const contractParams = {
|
|
212
|
-
startingSqrtPriceX96: sqrtPriceX96,
|
|
213
|
-
beacon: params.beacon
|
|
214
|
-
};
|
|
215
|
-
const { result, request } = await this.context.walletClient.extend(publicActions5).simulateContract({
|
|
216
|
-
address: this.context.perpManagerAddress,
|
|
217
|
-
abi: this.context.perpManagerAbi,
|
|
218
|
-
functionName: "createPerp",
|
|
219
|
-
args: [contractParams],
|
|
220
|
-
account: this.context.walletClient.account,
|
|
221
|
-
chain: this.context.walletClient.chain
|
|
222
|
-
});
|
|
223
|
-
await this.context.walletClient.writeContract(request);
|
|
224
|
-
return new Perp(this.context, result);
|
|
225
|
-
}
|
|
226
|
-
};
|
|
227
|
-
|
|
228
|
-
// src/addresses.ts
|
|
229
|
-
var PERP_MANAGER_BASE_SEPOLIA_ADDRESS = "0xA25aF834AA93Fe5Dca241955AF2383F04374c000";
|
|
230
|
-
|
|
231
|
-
// src/endpoints.ts
|
|
232
|
-
var GOLDSKY_BASE_SEPOLIA_URL = "https://api.goldsky.com/api/public/project_cmbawn40q70fj01ws4jmsfj7f/subgraphs/perp-city/3fe61683-20250915_170731/gn";
|
|
233
110
|
|
|
234
111
|
// src/abis/perp-manager.ts
|
|
235
112
|
var PERP_MANAGER_ABI = [
|
|
@@ -383,6 +260,22 @@ var PERP_MANAGER_ABI = [
|
|
|
383
260
|
"name": "UnexpectedRevertBytes",
|
|
384
261
|
"type": "error"
|
|
385
262
|
},
|
|
263
|
+
{
|
|
264
|
+
"inputs": [
|
|
265
|
+
{
|
|
266
|
+
"internalType": "int256",
|
|
267
|
+
"name": "perpDelta",
|
|
268
|
+
"type": "int256"
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
"internalType": "int256",
|
|
272
|
+
"name": "usdDelta",
|
|
273
|
+
"type": "int256"
|
|
274
|
+
}
|
|
275
|
+
],
|
|
276
|
+
"name": "ZeroSizePosition",
|
|
277
|
+
"type": "error"
|
|
278
|
+
},
|
|
386
279
|
{
|
|
387
280
|
"anonymous": false,
|
|
388
281
|
"inputs": [
|
|
@@ -428,6 +321,12 @@ var PERP_MANAGER_ABI = [
|
|
|
428
321
|
"internalType": "uint256",
|
|
429
322
|
"name": "startingSqrtPriceX96",
|
|
430
323
|
"type": "uint256"
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
"indexed": false,
|
|
327
|
+
"internalType": "uint256",
|
|
328
|
+
"name": "indexPriceX96",
|
|
329
|
+
"type": "uint256"
|
|
431
330
|
}
|
|
432
331
|
],
|
|
433
332
|
"name": "PerpCreated",
|
|
@@ -448,6 +347,30 @@ var PERP_MANAGER_ABI = [
|
|
|
448
347
|
"name": "posId",
|
|
449
348
|
"type": "uint256"
|
|
450
349
|
},
|
|
350
|
+
{
|
|
351
|
+
"indexed": false,
|
|
352
|
+
"internalType": "address",
|
|
353
|
+
"name": "holder",
|
|
354
|
+
"type": "address"
|
|
355
|
+
},
|
|
356
|
+
{
|
|
357
|
+
"indexed": false,
|
|
358
|
+
"internalType": "bool",
|
|
359
|
+
"name": "wasMaker",
|
|
360
|
+
"type": "bool"
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
"indexed": false,
|
|
364
|
+
"internalType": "int256",
|
|
365
|
+
"name": "perpDelta",
|
|
366
|
+
"type": "int256"
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
"indexed": false,
|
|
370
|
+
"internalType": "int256",
|
|
371
|
+
"name": "pnl",
|
|
372
|
+
"type": "int256"
|
|
373
|
+
},
|
|
451
374
|
{
|
|
452
375
|
"indexed": false,
|
|
453
376
|
"internalType": "bool",
|
|
@@ -459,6 +382,12 @@ var PERP_MANAGER_ABI = [
|
|
|
459
382
|
"internalType": "uint256",
|
|
460
383
|
"name": "sqrtPriceX96",
|
|
461
384
|
"type": "uint256"
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
"indexed": false,
|
|
388
|
+
"internalType": "int256",
|
|
389
|
+
"name": "fundingPremiumPerSecX96",
|
|
390
|
+
"type": "int256"
|
|
462
391
|
}
|
|
463
392
|
],
|
|
464
393
|
"name": "PositionClosed",
|
|
@@ -479,6 +408,12 @@ var PERP_MANAGER_ABI = [
|
|
|
479
408
|
"name": "posId",
|
|
480
409
|
"type": "uint256"
|
|
481
410
|
},
|
|
411
|
+
{
|
|
412
|
+
"indexed": false,
|
|
413
|
+
"internalType": "address",
|
|
414
|
+
"name": "holder",
|
|
415
|
+
"type": "address"
|
|
416
|
+
},
|
|
482
417
|
{
|
|
483
418
|
"indexed": false,
|
|
484
419
|
"internalType": "bool",
|
|
@@ -487,15 +422,21 @@ var PERP_MANAGER_ABI = [
|
|
|
487
422
|
},
|
|
488
423
|
{
|
|
489
424
|
"indexed": false,
|
|
490
|
-
"internalType": "
|
|
491
|
-
"name": "
|
|
492
|
-
"type": "
|
|
425
|
+
"internalType": "int256",
|
|
426
|
+
"name": "perpDelta",
|
|
427
|
+
"type": "int256"
|
|
493
428
|
},
|
|
494
429
|
{
|
|
495
430
|
"indexed": false,
|
|
496
431
|
"internalType": "uint256",
|
|
497
432
|
"name": "sqrtPriceX96",
|
|
498
433
|
"type": "uint256"
|
|
434
|
+
},
|
|
435
|
+
{
|
|
436
|
+
"indexed": false,
|
|
437
|
+
"internalType": "int256",
|
|
438
|
+
"name": "fundingPremiumPerSecX96",
|
|
439
|
+
"type": "int256"
|
|
499
440
|
}
|
|
500
441
|
],
|
|
501
442
|
"name": "PositionOpened",
|
|
@@ -652,7 +593,7 @@ var PERP_MANAGER_ABI = [
|
|
|
652
593
|
"type": "uint256"
|
|
653
594
|
}
|
|
654
595
|
],
|
|
655
|
-
"name": "
|
|
596
|
+
"name": "estimateLiquidityForAmount1",
|
|
656
597
|
"outputs": [
|
|
657
598
|
{
|
|
658
599
|
"internalType": "uint128",
|
|
@@ -663,6 +604,40 @@ var PERP_MANAGER_ABI = [
|
|
|
663
604
|
"stateMutability": "pure",
|
|
664
605
|
"type": "function"
|
|
665
606
|
},
|
|
607
|
+
{
|
|
608
|
+
"inputs": [
|
|
609
|
+
{
|
|
610
|
+
"internalType": "PoolId",
|
|
611
|
+
"name": "perpId",
|
|
612
|
+
"type": "bytes32"
|
|
613
|
+
}
|
|
614
|
+
],
|
|
615
|
+
"name": "fees",
|
|
616
|
+
"outputs": [
|
|
617
|
+
{
|
|
618
|
+
"internalType": "uint24",
|
|
619
|
+
"name": "creatorFee",
|
|
620
|
+
"type": "uint24"
|
|
621
|
+
},
|
|
622
|
+
{
|
|
623
|
+
"internalType": "uint24",
|
|
624
|
+
"name": "insurnaceFee",
|
|
625
|
+
"type": "uint24"
|
|
626
|
+
},
|
|
627
|
+
{
|
|
628
|
+
"internalType": "uint24",
|
|
629
|
+
"name": "lpFee",
|
|
630
|
+
"type": "uint24"
|
|
631
|
+
},
|
|
632
|
+
{
|
|
633
|
+
"internalType": "uint24",
|
|
634
|
+
"name": "liquidationFee",
|
|
635
|
+
"type": "uint24"
|
|
636
|
+
}
|
|
637
|
+
],
|
|
638
|
+
"stateMutability": "view",
|
|
639
|
+
"type": "function"
|
|
640
|
+
},
|
|
666
641
|
{
|
|
667
642
|
"inputs": [
|
|
668
643
|
{
|
|
@@ -1197,14 +1172,234 @@ var PERP_MANAGER_ABI = [
|
|
|
1197
1172
|
"internalType": "PoolId",
|
|
1198
1173
|
"name": "perpId",
|
|
1199
1174
|
"type": "bytes32"
|
|
1175
|
+
},
|
|
1176
|
+
{
|
|
1177
|
+
"components": [
|
|
1178
|
+
{
|
|
1179
|
+
"internalType": "uint256",
|
|
1180
|
+
"name": "margin",
|
|
1181
|
+
"type": "uint256"
|
|
1182
|
+
},
|
|
1183
|
+
{
|
|
1184
|
+
"internalType": "uint128",
|
|
1185
|
+
"name": "liquidity",
|
|
1186
|
+
"type": "uint128"
|
|
1187
|
+
},
|
|
1188
|
+
{
|
|
1189
|
+
"internalType": "int24",
|
|
1190
|
+
"name": "tickLower",
|
|
1191
|
+
"type": "int24"
|
|
1192
|
+
},
|
|
1193
|
+
{
|
|
1194
|
+
"internalType": "int24",
|
|
1195
|
+
"name": "tickUpper",
|
|
1196
|
+
"type": "int24"
|
|
1197
|
+
},
|
|
1198
|
+
{
|
|
1199
|
+
"internalType": "uint128",
|
|
1200
|
+
"name": "maxAmt0In",
|
|
1201
|
+
"type": "uint128"
|
|
1202
|
+
},
|
|
1203
|
+
{
|
|
1204
|
+
"internalType": "uint128",
|
|
1205
|
+
"name": "maxAmt1In",
|
|
1206
|
+
"type": "uint128"
|
|
1207
|
+
}
|
|
1208
|
+
],
|
|
1209
|
+
"internalType": "struct IPerpManager.OpenMakerPositionParams",
|
|
1210
|
+
"name": "params",
|
|
1211
|
+
"type": "tuple"
|
|
1200
1212
|
}
|
|
1201
1213
|
],
|
|
1202
|
-
"name": "
|
|
1214
|
+
"name": "quoteMakerPosition",
|
|
1203
1215
|
"outputs": [
|
|
1204
1216
|
{
|
|
1205
|
-
"internalType": "
|
|
1206
|
-
"name": "",
|
|
1207
|
-
"type": "
|
|
1217
|
+
"internalType": "bool",
|
|
1218
|
+
"name": "success",
|
|
1219
|
+
"type": "bool"
|
|
1220
|
+
},
|
|
1221
|
+
{
|
|
1222
|
+
"internalType": "int256",
|
|
1223
|
+
"name": "perpDelta",
|
|
1224
|
+
"type": "int256"
|
|
1225
|
+
},
|
|
1226
|
+
{
|
|
1227
|
+
"internalType": "int256",
|
|
1228
|
+
"name": "usdDelta",
|
|
1229
|
+
"type": "int256"
|
|
1230
|
+
},
|
|
1231
|
+
{
|
|
1232
|
+
"internalType": "uint256",
|
|
1233
|
+
"name": "creatorFeeAmt",
|
|
1234
|
+
"type": "uint256"
|
|
1235
|
+
},
|
|
1236
|
+
{
|
|
1237
|
+
"internalType": "uint256",
|
|
1238
|
+
"name": "insuranceFeeAmt",
|
|
1239
|
+
"type": "uint256"
|
|
1240
|
+
},
|
|
1241
|
+
{
|
|
1242
|
+
"internalType": "uint256",
|
|
1243
|
+
"name": "lpFeeAmt",
|
|
1244
|
+
"type": "uint256"
|
|
1245
|
+
}
|
|
1246
|
+
],
|
|
1247
|
+
"stateMutability": "nonpayable",
|
|
1248
|
+
"type": "function"
|
|
1249
|
+
},
|
|
1250
|
+
{
|
|
1251
|
+
"inputs": [
|
|
1252
|
+
{
|
|
1253
|
+
"internalType": "PoolId",
|
|
1254
|
+
"name": "perpId",
|
|
1255
|
+
"type": "bytes32"
|
|
1256
|
+
},
|
|
1257
|
+
{
|
|
1258
|
+
"components": [
|
|
1259
|
+
{
|
|
1260
|
+
"internalType": "bool",
|
|
1261
|
+
"name": "isLong",
|
|
1262
|
+
"type": "bool"
|
|
1263
|
+
},
|
|
1264
|
+
{
|
|
1265
|
+
"internalType": "uint256",
|
|
1266
|
+
"name": "margin",
|
|
1267
|
+
"type": "uint256"
|
|
1268
|
+
},
|
|
1269
|
+
{
|
|
1270
|
+
"internalType": "uint256",
|
|
1271
|
+
"name": "levX96",
|
|
1272
|
+
"type": "uint256"
|
|
1273
|
+
},
|
|
1274
|
+
{
|
|
1275
|
+
"internalType": "uint128",
|
|
1276
|
+
"name": "unspecifiedAmountLimit",
|
|
1277
|
+
"type": "uint128"
|
|
1278
|
+
}
|
|
1279
|
+
],
|
|
1280
|
+
"internalType": "struct IPerpManager.OpenTakerPositionParams",
|
|
1281
|
+
"name": "params",
|
|
1282
|
+
"type": "tuple"
|
|
1283
|
+
}
|
|
1284
|
+
],
|
|
1285
|
+
"name": "quoteTakerPosition",
|
|
1286
|
+
"outputs": [
|
|
1287
|
+
{
|
|
1288
|
+
"internalType": "bool",
|
|
1289
|
+
"name": "success",
|
|
1290
|
+
"type": "bool"
|
|
1291
|
+
},
|
|
1292
|
+
{
|
|
1293
|
+
"internalType": "int256",
|
|
1294
|
+
"name": "perpDelta",
|
|
1295
|
+
"type": "int256"
|
|
1296
|
+
},
|
|
1297
|
+
{
|
|
1298
|
+
"internalType": "int256",
|
|
1299
|
+
"name": "usdDelta",
|
|
1300
|
+
"type": "int256"
|
|
1301
|
+
},
|
|
1302
|
+
{
|
|
1303
|
+
"internalType": "uint256",
|
|
1304
|
+
"name": "creatorFeeAmt",
|
|
1305
|
+
"type": "uint256"
|
|
1306
|
+
},
|
|
1307
|
+
{
|
|
1308
|
+
"internalType": "uint256",
|
|
1309
|
+
"name": "insuranceFeeAmt",
|
|
1310
|
+
"type": "uint256"
|
|
1311
|
+
},
|
|
1312
|
+
{
|
|
1313
|
+
"internalType": "uint256",
|
|
1314
|
+
"name": "lpFeeAmt",
|
|
1315
|
+
"type": "uint256"
|
|
1316
|
+
}
|
|
1317
|
+
],
|
|
1318
|
+
"stateMutability": "nonpayable",
|
|
1319
|
+
"type": "function"
|
|
1320
|
+
},
|
|
1321
|
+
{
|
|
1322
|
+
"inputs": [
|
|
1323
|
+
{
|
|
1324
|
+
"internalType": "PoolId",
|
|
1325
|
+
"name": "perpId",
|
|
1326
|
+
"type": "bytes32"
|
|
1327
|
+
}
|
|
1328
|
+
],
|
|
1329
|
+
"name": "sqrtPriceX96",
|
|
1330
|
+
"outputs": [
|
|
1331
|
+
{
|
|
1332
|
+
"internalType": "uint160",
|
|
1333
|
+
"name": "sqrtPrice",
|
|
1334
|
+
"type": "uint160"
|
|
1335
|
+
}
|
|
1336
|
+
],
|
|
1337
|
+
"stateMutability": "view",
|
|
1338
|
+
"type": "function"
|
|
1339
|
+
},
|
|
1340
|
+
{
|
|
1341
|
+
"inputs": [
|
|
1342
|
+
{
|
|
1343
|
+
"internalType": "PoolId",
|
|
1344
|
+
"name": "perpId",
|
|
1345
|
+
"type": "bytes32"
|
|
1346
|
+
}
|
|
1347
|
+
],
|
|
1348
|
+
"name": "tickSpacing",
|
|
1349
|
+
"outputs": [
|
|
1350
|
+
{
|
|
1351
|
+
"internalType": "int24",
|
|
1352
|
+
"name": "",
|
|
1353
|
+
"type": "int24"
|
|
1354
|
+
}
|
|
1355
|
+
],
|
|
1356
|
+
"stateMutability": "view",
|
|
1357
|
+
"type": "function"
|
|
1358
|
+
},
|
|
1359
|
+
{
|
|
1360
|
+
"inputs": [
|
|
1361
|
+
{
|
|
1362
|
+
"internalType": "PoolId",
|
|
1363
|
+
"name": "perpId",
|
|
1364
|
+
"type": "bytes32"
|
|
1365
|
+
}
|
|
1366
|
+
],
|
|
1367
|
+
"name": "tradingBounds",
|
|
1368
|
+
"outputs": [
|
|
1369
|
+
{
|
|
1370
|
+
"internalType": "uint24",
|
|
1371
|
+
"name": "minOpeningMargin",
|
|
1372
|
+
"type": "uint24"
|
|
1373
|
+
},
|
|
1374
|
+
{
|
|
1375
|
+
"internalType": "uint24",
|
|
1376
|
+
"name": "minMakerMarginRatio",
|
|
1377
|
+
"type": "uint24"
|
|
1378
|
+
},
|
|
1379
|
+
{
|
|
1380
|
+
"internalType": "uint24",
|
|
1381
|
+
"name": "maxMakerMarginRatio",
|
|
1382
|
+
"type": "uint24"
|
|
1383
|
+
},
|
|
1384
|
+
{
|
|
1385
|
+
"internalType": "uint24",
|
|
1386
|
+
"name": "makerLiquidationMarginRatio",
|
|
1387
|
+
"type": "uint24"
|
|
1388
|
+
},
|
|
1389
|
+
{
|
|
1390
|
+
"internalType": "uint24",
|
|
1391
|
+
"name": "minTakerMarginRatio",
|
|
1392
|
+
"type": "uint24"
|
|
1393
|
+
},
|
|
1394
|
+
{
|
|
1395
|
+
"internalType": "uint24",
|
|
1396
|
+
"name": "maxTakerMarginRatio",
|
|
1397
|
+
"type": "uint24"
|
|
1398
|
+
},
|
|
1399
|
+
{
|
|
1400
|
+
"internalType": "uint24",
|
|
1401
|
+
"name": "takerLiquidationMarginRatio",
|
|
1402
|
+
"type": "uint24"
|
|
1208
1403
|
}
|
|
1209
1404
|
],
|
|
1210
1405
|
"stateMutability": "view",
|
|
@@ -1231,6 +1426,490 @@ var PERP_MANAGER_ABI = [
|
|
|
1231
1426
|
}
|
|
1232
1427
|
];
|
|
1233
1428
|
|
|
1429
|
+
// src/utils/liquidity.ts
|
|
1430
|
+
async function estimateLiquidity(context, tickLower, tickUpper, usdScaled) {
|
|
1431
|
+
return await context.walletClient.readContract({
|
|
1432
|
+
address: context.deployments().perpManager,
|
|
1433
|
+
abi: PERP_MANAGER_ABI,
|
|
1434
|
+
functionName: "estimateLiquidityForAmount1",
|
|
1435
|
+
args: [tickLower, tickUpper, usdScaled]
|
|
1436
|
+
});
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
// src/entities/openPosition.ts
|
|
1440
|
+
var OpenPosition = class _OpenPosition {
|
|
1441
|
+
constructor(context, perpId, positionId) {
|
|
1442
|
+
this.context = context;
|
|
1443
|
+
this.perpId = perpId;
|
|
1444
|
+
this.positionId = positionId;
|
|
1445
|
+
}
|
|
1446
|
+
async closePosition(params) {
|
|
1447
|
+
const contractParams = {
|
|
1448
|
+
posId: this.positionId,
|
|
1449
|
+
minAmt0Out: scale6Decimals(params.minAmt0Out),
|
|
1450
|
+
minAmt1Out: scale6Decimals(params.minAmt1Out),
|
|
1451
|
+
maxAmt1In: scale6Decimals(params.maxAmt1In)
|
|
1452
|
+
};
|
|
1453
|
+
const { result, request } = await this.context.walletClient.extend(publicActions2).simulateContract({
|
|
1454
|
+
address: this.context.deployments().perpManager,
|
|
1455
|
+
abi: PERP_MANAGER_ABI,
|
|
1456
|
+
functionName: "closePosition",
|
|
1457
|
+
args: [this.perpId, contractParams],
|
|
1458
|
+
account: this.context.walletClient.account
|
|
1459
|
+
});
|
|
1460
|
+
await this.context.walletClient.writeContract(request);
|
|
1461
|
+
return result === null ? null : new _OpenPosition(this.context, this.perpId, result);
|
|
1462
|
+
}
|
|
1463
|
+
async liveDetails() {
|
|
1464
|
+
const { result, request } = await this.context.walletClient.simulateContract({
|
|
1465
|
+
address: this.context.deployments().perpManager,
|
|
1466
|
+
abi: PERP_MANAGER_ABI,
|
|
1467
|
+
functionName: "livePositionDetails",
|
|
1468
|
+
args: [this.perpId, this.positionId],
|
|
1469
|
+
account: this.context.walletClient.account
|
|
1470
|
+
});
|
|
1471
|
+
return {
|
|
1472
|
+
pnl: scaleFrom6Decimals(Number(result[0])),
|
|
1473
|
+
fundingPayment: scaleFrom6Decimals(Number(result[1])),
|
|
1474
|
+
effectiveMargin: scaleFrom6Decimals(Number(result[2])),
|
|
1475
|
+
isLiquidatable: result[3]
|
|
1476
|
+
};
|
|
1477
|
+
}
|
|
1478
|
+
};
|
|
1479
|
+
|
|
1480
|
+
// src/entities/perp.ts
|
|
1481
|
+
import { nearestUsableTick } from "@uniswap/v3-sdk";
|
|
1482
|
+
import { gql } from "graphql-request";
|
|
1483
|
+
import { parse } from "graphql";
|
|
1484
|
+
var Perp = class {
|
|
1485
|
+
constructor(context, id) {
|
|
1486
|
+
this.context = context;
|
|
1487
|
+
this.id = id;
|
|
1488
|
+
}
|
|
1489
|
+
// READS
|
|
1490
|
+
async tickSpacing() {
|
|
1491
|
+
return await this.context.walletClient.readContract({
|
|
1492
|
+
address: this.context.deployments().perpManager,
|
|
1493
|
+
abi: PERP_MANAGER_ABI,
|
|
1494
|
+
functionName: "tickSpacing",
|
|
1495
|
+
args: [this.id]
|
|
1496
|
+
});
|
|
1497
|
+
}
|
|
1498
|
+
async mark() {
|
|
1499
|
+
const sqrtPriceX96 = await this.context.walletClient.readContract({
|
|
1500
|
+
address: this.context.deployments().perpManager,
|
|
1501
|
+
abi: PERP_MANAGER_ABI,
|
|
1502
|
+
functionName: "sqrtPriceX96",
|
|
1503
|
+
args: [this.id]
|
|
1504
|
+
});
|
|
1505
|
+
return sqrtPriceX96ToPrice(sqrtPriceX96);
|
|
1506
|
+
}
|
|
1507
|
+
async index() {
|
|
1508
|
+
const beacon = await this.beacon();
|
|
1509
|
+
const query = parse(gql`
|
|
1510
|
+
query ($beaconAddr: Bytes!) {
|
|
1511
|
+
beaconSnapshots(
|
|
1512
|
+
first: 1
|
|
1513
|
+
orderBy: timestamp
|
|
1514
|
+
orderDirection: desc
|
|
1515
|
+
where: { beacon: $beaconAddr }
|
|
1516
|
+
) { indexPrice }
|
|
1517
|
+
}
|
|
1518
|
+
`);
|
|
1519
|
+
const response = await this.context.goldskyClient.request(query, { beaconAddr: beacon });
|
|
1520
|
+
return Number(response.beaconSnapshots[0].indexPrice);
|
|
1521
|
+
}
|
|
1522
|
+
async beacon() {
|
|
1523
|
+
const query = parse(gql`
|
|
1524
|
+
query ($perpId: Bytes!) {
|
|
1525
|
+
perp(id: $perpId) {
|
|
1526
|
+
beacon { id }
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
`);
|
|
1530
|
+
const response = await this.context.goldskyClient.request(query, { perpId: this.id });
|
|
1531
|
+
return response.perp.beacon.id;
|
|
1532
|
+
}
|
|
1533
|
+
async lastIndexUpdate() {
|
|
1534
|
+
const beacon = await this.beacon();
|
|
1535
|
+
const query = parse(gql`
|
|
1536
|
+
query ($beaconAddr: Bytes!) {
|
|
1537
|
+
beaconSnapshots(
|
|
1538
|
+
first: 1
|
|
1539
|
+
orderBy: timestamp
|
|
1540
|
+
orderDirection: desc
|
|
1541
|
+
where: { beacon: $beaconAddr }
|
|
1542
|
+
) { timestamp }
|
|
1543
|
+
}
|
|
1544
|
+
`);
|
|
1545
|
+
const response = await this.context.goldskyClient.request(query, { beaconAddr: beacon });
|
|
1546
|
+
return Number(response.beaconSnapshots[0].timestamp);
|
|
1547
|
+
}
|
|
1548
|
+
async openInterest() {
|
|
1549
|
+
const query = parse(gql`
|
|
1550
|
+
query ($perpId: Bytes!) {
|
|
1551
|
+
perpSnapshots(
|
|
1552
|
+
first: 1
|
|
1553
|
+
orderBy: timestamp
|
|
1554
|
+
orderDirection: desc
|
|
1555
|
+
where: { perp: $perpId }
|
|
1556
|
+
) {
|
|
1557
|
+
takerLongNotional
|
|
1558
|
+
takerShortNotional
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
`);
|
|
1562
|
+
const response = await this.context.goldskyClient.request(query, { perpId: this.id });
|
|
1563
|
+
return {
|
|
1564
|
+
takerLongNotional: Number(response.perpSnapshots[0].takerLongNotional),
|
|
1565
|
+
takerShortNotional: Number(response.perpSnapshots[0].takerShortNotional)
|
|
1566
|
+
};
|
|
1567
|
+
}
|
|
1568
|
+
async markTimeSeries() {
|
|
1569
|
+
const query = parse(gql`
|
|
1570
|
+
query ($perpId: Bytes!) {
|
|
1571
|
+
perpSnapshots(
|
|
1572
|
+
orderBy: timestamp
|
|
1573
|
+
orderDirection: asc
|
|
1574
|
+
where: { perp: $perpId }
|
|
1575
|
+
) {
|
|
1576
|
+
timestamp
|
|
1577
|
+
markPrice
|
|
1578
|
+
}
|
|
1579
|
+
}
|
|
1580
|
+
`);
|
|
1581
|
+
const response = await this.context.goldskyClient.request(query, { perpId: this.id });
|
|
1582
|
+
return response.perpSnapshots.map((snapshot) => ({
|
|
1583
|
+
timestamp: Number(snapshot.timestamp),
|
|
1584
|
+
value: Number(snapshot.markPrice)
|
|
1585
|
+
}));
|
|
1586
|
+
}
|
|
1587
|
+
async indexTimeSeries() {
|
|
1588
|
+
const beacon = await this.beacon();
|
|
1589
|
+
const query = parse(gql`
|
|
1590
|
+
query ($beaconAddr: Bytes!) {
|
|
1591
|
+
beaconSnapshots(
|
|
1592
|
+
orderBy: timestamp
|
|
1593
|
+
orderDirection: asc
|
|
1594
|
+
where: { beacon: $beaconAddr }
|
|
1595
|
+
) {
|
|
1596
|
+
timestamp
|
|
1597
|
+
indexPrice
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
`);
|
|
1601
|
+
const response = await this.context.goldskyClient.request(query, { beaconAddr: beacon });
|
|
1602
|
+
return response.beaconSnapshots.map((snapshot) => ({
|
|
1603
|
+
timestamp: Number(snapshot.timestamp),
|
|
1604
|
+
value: Number(snapshot.indexPrice)
|
|
1605
|
+
}));
|
|
1606
|
+
}
|
|
1607
|
+
async fundingRate() {
|
|
1608
|
+
const query = parse(gql`
|
|
1609
|
+
query ($perpId: Bytes!) {
|
|
1610
|
+
perpSnapshots(
|
|
1611
|
+
first: 1
|
|
1612
|
+
orderBy: timestamp
|
|
1613
|
+
orderDirection: desc
|
|
1614
|
+
where: { perp: $perpId }
|
|
1615
|
+
) {
|
|
1616
|
+
fundingRate
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
`);
|
|
1620
|
+
const response = await this.context.goldskyClient.request(query, { perpId: this.id });
|
|
1621
|
+
return Number(response.perpSnapshots[0].fundingRate);
|
|
1622
|
+
}
|
|
1623
|
+
async bounds() {
|
|
1624
|
+
const result = await this.context.walletClient.readContract({
|
|
1625
|
+
address: this.context.deployments().perpManager,
|
|
1626
|
+
abi: PERP_MANAGER_ABI,
|
|
1627
|
+
functionName: "tradingBounds",
|
|
1628
|
+
args: [this.id]
|
|
1629
|
+
});
|
|
1630
|
+
return {
|
|
1631
|
+
minMargin: Number(result[0]),
|
|
1632
|
+
minTakerLeverage: marginRatioToLeverage(result[5]),
|
|
1633
|
+
maxTakerLeverage: marginRatioToLeverage(result[4])
|
|
1634
|
+
};
|
|
1635
|
+
}
|
|
1636
|
+
// TODO
|
|
1637
|
+
async maxTakerNotional(isLong) {
|
|
1638
|
+
return 0;
|
|
1639
|
+
}
|
|
1640
|
+
async simulateTaker(params) {
|
|
1641
|
+
const contractParams = {
|
|
1642
|
+
isLong: params.isLong,
|
|
1643
|
+
margin: scale6Decimals(params.margin),
|
|
1644
|
+
levX96: scaleToX96(params.leverage),
|
|
1645
|
+
unspecifiedAmountLimit: scale6Decimals(params.unspecifiedAmountLimit)
|
|
1646
|
+
};
|
|
1647
|
+
const { result, request } = await this.context.walletClient.simulateContract({
|
|
1648
|
+
address: this.context.deployments().perpManager,
|
|
1649
|
+
abi: PERP_MANAGER_ABI,
|
|
1650
|
+
functionName: "quoteTakerPosition",
|
|
1651
|
+
args: [this.id, contractParams],
|
|
1652
|
+
account: this.context.walletClient.account
|
|
1653
|
+
});
|
|
1654
|
+
return {
|
|
1655
|
+
success: result[0],
|
|
1656
|
+
size: scaleFrom6Decimals(Math.abs(Number(result[1]))),
|
|
1657
|
+
notional: scaleFrom6Decimals(Math.abs(Number(result[2]))),
|
|
1658
|
+
creatorFeeAmt: scaleFrom6Decimals(Number(result[3])),
|
|
1659
|
+
insuranceFeeAmt: scaleFrom6Decimals(Number(result[4])),
|
|
1660
|
+
lpFeeAmt: scaleFrom6Decimals(Number(result[5]))
|
|
1661
|
+
};
|
|
1662
|
+
}
|
|
1663
|
+
async allMakerPositions() {
|
|
1664
|
+
const query = parse(gql`
|
|
1665
|
+
query ($perpId: Bytes!) {
|
|
1666
|
+
openPositions(
|
|
1667
|
+
where: { perp: $perpId, isMaker: true }
|
|
1668
|
+
) {
|
|
1669
|
+
perp { id }
|
|
1670
|
+
inContractPosId
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
`);
|
|
1674
|
+
const response = await this.context.goldskyClient.request(query, { perpId: this.id });
|
|
1675
|
+
return response.openPositions.map((position) => new OpenPosition(this.context, position.perp.id, position.inContractPosId));
|
|
1676
|
+
}
|
|
1677
|
+
async allTakerPositions() {
|
|
1678
|
+
const query = parse(gql`
|
|
1679
|
+
query ($perpId: Bytes!) {
|
|
1680
|
+
openPositions(
|
|
1681
|
+
where: { perp: $perpId, isMaker: false }
|
|
1682
|
+
) {
|
|
1683
|
+
perp { id }
|
|
1684
|
+
inContractPosId
|
|
1685
|
+
}
|
|
1686
|
+
}
|
|
1687
|
+
`);
|
|
1688
|
+
const response = await this.context.goldskyClient.request(query, { perpId: this.id });
|
|
1689
|
+
return response.openPositions.map((position) => new OpenPosition(this.context, position.perp.id, position.inContractPosId));
|
|
1690
|
+
}
|
|
1691
|
+
async totalOpenMakerPnl() {
|
|
1692
|
+
const positions = await this.allMakerPositions();
|
|
1693
|
+
const liveDetails = await Promise.all(positions.map((position) => position.liveDetails()));
|
|
1694
|
+
return liveDetails.reduce((acc, detail) => acc + detail.pnl - detail.fundingPayment, 0);
|
|
1695
|
+
}
|
|
1696
|
+
async totalOpenTakerPnl() {
|
|
1697
|
+
const positions = await this.allTakerPositions();
|
|
1698
|
+
const liveDetails = await Promise.all(positions.map((position) => position.liveDetails()));
|
|
1699
|
+
return liveDetails.reduce((acc, detail) => acc + detail.pnl - detail.fundingPayment, 0);
|
|
1700
|
+
}
|
|
1701
|
+
async fees() {
|
|
1702
|
+
const result = await this.context.walletClient.readContract({
|
|
1703
|
+
address: this.context.deployments().perpManager,
|
|
1704
|
+
abi: PERP_MANAGER_ABI,
|
|
1705
|
+
functionName: "fees",
|
|
1706
|
+
args: [this.id]
|
|
1707
|
+
});
|
|
1708
|
+
return {
|
|
1709
|
+
creatorFee: scaleFrom6Decimals(result[0]),
|
|
1710
|
+
insuranceFee: scaleFrom6Decimals(result[1]),
|
|
1711
|
+
lpFee: scaleFrom6Decimals(result[2]),
|
|
1712
|
+
liquidationFee: scaleFrom6Decimals(result[3])
|
|
1713
|
+
};
|
|
1714
|
+
}
|
|
1715
|
+
async openInterestTimeSeries() {
|
|
1716
|
+
const query = parse(gql`
|
|
1717
|
+
query ($perpId: Bytes!) {
|
|
1718
|
+
perpSnapshots(
|
|
1719
|
+
orderBy: timestamp
|
|
1720
|
+
orderDirection: asc
|
|
1721
|
+
where: { perp: $perpId }
|
|
1722
|
+
) {
|
|
1723
|
+
timestamp
|
|
1724
|
+
takerLongNotional
|
|
1725
|
+
takerShortNotional
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
`);
|
|
1729
|
+
const response = await this.context.goldskyClient.request(query, { perpId: this.id });
|
|
1730
|
+
return response.perpSnapshots.map((snapshot) => ({
|
|
1731
|
+
timestamp: Number(snapshot.timestamp),
|
|
1732
|
+
value: {
|
|
1733
|
+
takerLongNotional: Number(snapshot.takerLongNotional),
|
|
1734
|
+
takerShortNotional: Number(snapshot.takerShortNotional)
|
|
1735
|
+
}
|
|
1736
|
+
}));
|
|
1737
|
+
}
|
|
1738
|
+
async fundingRateTimeSeries() {
|
|
1739
|
+
const query = parse(gql`
|
|
1740
|
+
query ($perpId: Bytes!) {
|
|
1741
|
+
perpSnapshots(
|
|
1742
|
+
orderBy: timestamp
|
|
1743
|
+
orderDirection: asc
|
|
1744
|
+
where: { perp: $perpId }
|
|
1745
|
+
) {
|
|
1746
|
+
timestamp
|
|
1747
|
+
fundingRate
|
|
1748
|
+
}
|
|
1749
|
+
}
|
|
1750
|
+
`);
|
|
1751
|
+
const response = await this.context.goldskyClient.request(query, { perpId: this.id });
|
|
1752
|
+
return response.perpSnapshots.map((snapshot) => ({
|
|
1753
|
+
timestamp: Number(snapshot.timestamp),
|
|
1754
|
+
value: Number(snapshot.fundingRate)
|
|
1755
|
+
}));
|
|
1756
|
+
}
|
|
1757
|
+
// WRITES
|
|
1758
|
+
async approveAndOpenMakerPosition(params) {
|
|
1759
|
+
await approveUsdc(this.context, scale6Decimals(params.margin));
|
|
1760
|
+
return await this.openMakerPosition(params);
|
|
1761
|
+
}
|
|
1762
|
+
async approveAndOpenTakerPosition(params) {
|
|
1763
|
+
await approveUsdc(this.context, scale6Decimals(params.margin));
|
|
1764
|
+
return await this.openTakerPosition(params);
|
|
1765
|
+
}
|
|
1766
|
+
async openMakerPosition(params) {
|
|
1767
|
+
const deployments = this.context.deployments();
|
|
1768
|
+
const tickSpacing = await this.tickSpacing();
|
|
1769
|
+
const scaledUsd = scale6Decimals(params.margin);
|
|
1770
|
+
const tickLower = nearestUsableTick(priceToTick(params.priceLower, true), tickSpacing);
|
|
1771
|
+
const tickUpper = nearestUsableTick(priceToTick(params.priceUpper, false), tickSpacing);
|
|
1772
|
+
const contractParams = {
|
|
1773
|
+
margin: scaledUsd,
|
|
1774
|
+
liquidity: await estimateLiquidity(this.context, tickLower, tickUpper, scaledUsd),
|
|
1775
|
+
tickLower,
|
|
1776
|
+
tickUpper,
|
|
1777
|
+
maxAmt0In: scale6Decimals(params.maxAmt0In),
|
|
1778
|
+
maxAmt1In: scale6Decimals(params.maxAmt1In)
|
|
1779
|
+
};
|
|
1780
|
+
const { result, request } = await this.context.walletClient.simulateContract({
|
|
1781
|
+
address: deployments.perpManager,
|
|
1782
|
+
abi: PERP_MANAGER_ABI,
|
|
1783
|
+
functionName: "openMakerPosition",
|
|
1784
|
+
args: [this.id, contractParams],
|
|
1785
|
+
account: this.context.walletClient.account
|
|
1786
|
+
});
|
|
1787
|
+
await this.context.walletClient.writeContract(request);
|
|
1788
|
+
return new OpenPosition(this.context, this.id, result);
|
|
1789
|
+
}
|
|
1790
|
+
async openTakerPosition(params) {
|
|
1791
|
+
const contractParams = {
|
|
1792
|
+
isLong: params.isLong,
|
|
1793
|
+
margin: scale6Decimals(params.margin),
|
|
1794
|
+
levX96: scaleToX96(params.leverage),
|
|
1795
|
+
unspecifiedAmountLimit: scale6Decimals(params.unspecifiedAmountLimit)
|
|
1796
|
+
};
|
|
1797
|
+
const { result, request } = await this.context.walletClient.simulateContract({
|
|
1798
|
+
address: this.context.deployments().perpManager,
|
|
1799
|
+
abi: PERP_MANAGER_ABI,
|
|
1800
|
+
functionName: "openTakerPosition",
|
|
1801
|
+
args: [this.id, contractParams],
|
|
1802
|
+
account: this.context.walletClient.account
|
|
1803
|
+
});
|
|
1804
|
+
await this.context.walletClient.writeContract(request);
|
|
1805
|
+
return new OpenPosition(this.context, this.id, result);
|
|
1806
|
+
}
|
|
1807
|
+
};
|
|
1808
|
+
|
|
1809
|
+
// src/entities/perp-manager.ts
|
|
1810
|
+
import { gql as gql2 } from "graphql-request";
|
|
1811
|
+
import { parse as parse2 } from "graphql";
|
|
1812
|
+
var PerpManager = class {
|
|
1813
|
+
constructor(context) {
|
|
1814
|
+
this.context = context;
|
|
1815
|
+
}
|
|
1816
|
+
// READS
|
|
1817
|
+
async getPerps() {
|
|
1818
|
+
const query = parse2(gql2`
|
|
1819
|
+
{
|
|
1820
|
+
perps {
|
|
1821
|
+
id
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
`);
|
|
1825
|
+
const response = await this.context.goldskyClient.request(query);
|
|
1826
|
+
return response.perps.map(
|
|
1827
|
+
(perpData) => new Perp(this.context, perpData.id)
|
|
1828
|
+
);
|
|
1829
|
+
}
|
|
1830
|
+
// WRITES
|
|
1831
|
+
async createPerp(params) {
|
|
1832
|
+
const sqrtPriceX96 = priceToSqrtPriceX96(params.startingPrice);
|
|
1833
|
+
const contractParams = {
|
|
1834
|
+
startingSqrtPriceX96: sqrtPriceX96,
|
|
1835
|
+
beacon: params.beacon
|
|
1836
|
+
};
|
|
1837
|
+
const { result, request } = await this.context.walletClient.simulateContract({
|
|
1838
|
+
address: this.context.deployments().perpManager,
|
|
1839
|
+
abi: PERP_MANAGER_ABI,
|
|
1840
|
+
functionName: "createPerp",
|
|
1841
|
+
args: [contractParams],
|
|
1842
|
+
account: this.context.walletClient.account
|
|
1843
|
+
});
|
|
1844
|
+
await this.context.walletClient.writeContract(request);
|
|
1845
|
+
return new Perp(this.context, result);
|
|
1846
|
+
}
|
|
1847
|
+
};
|
|
1848
|
+
|
|
1849
|
+
// src/entities/user.ts
|
|
1850
|
+
import { erc20Abi as erc20Abi2 } from "viem";
|
|
1851
|
+
import { parse as parse3 } from "graphql";
|
|
1852
|
+
import { gql as gql3 } from "graphql-request";
|
|
1853
|
+
var User = class {
|
|
1854
|
+
constructor(context) {
|
|
1855
|
+
this.context = context;
|
|
1856
|
+
if (!context.walletClient.account) throw new Error("Wallet client account not found");
|
|
1857
|
+
this.walletAddress = context.walletClient.account.address;
|
|
1858
|
+
}
|
|
1859
|
+
async usdcBalance() {
|
|
1860
|
+
const result = await this.context.walletClient.readContract({
|
|
1861
|
+
address: this.context.deployments().usdc,
|
|
1862
|
+
abi: erc20Abi2,
|
|
1863
|
+
functionName: "balanceOf",
|
|
1864
|
+
args: [this.walletAddress]
|
|
1865
|
+
});
|
|
1866
|
+
return scaleFrom6Decimals(Number(result));
|
|
1867
|
+
}
|
|
1868
|
+
async openPositions() {
|
|
1869
|
+
const query = parse3(gql3`
|
|
1870
|
+
query ($holder: Bytes!) {
|
|
1871
|
+
openPositions(
|
|
1872
|
+
where: { holder: $holder }
|
|
1873
|
+
) {
|
|
1874
|
+
perp { id }
|
|
1875
|
+
inContractPosId
|
|
1876
|
+
}
|
|
1877
|
+
}
|
|
1878
|
+
`);
|
|
1879
|
+
const response = await this.context.goldskyClient.request(query, { holder: this.walletAddress });
|
|
1880
|
+
return response.openPositions.map((position) => new OpenPosition(this.context, position.perp.id, position.inContractPosId));
|
|
1881
|
+
}
|
|
1882
|
+
async closedPositions() {
|
|
1883
|
+
const query = parse3(gql3`
|
|
1884
|
+
query ($holder: Bytes!) {
|
|
1885
|
+
closedPositions(
|
|
1886
|
+
where: { holder: $holder }
|
|
1887
|
+
) {
|
|
1888
|
+
perp { id }
|
|
1889
|
+
wasMaker
|
|
1890
|
+
wasLong
|
|
1891
|
+
pnlAtClose
|
|
1892
|
+
}
|
|
1893
|
+
}
|
|
1894
|
+
`);
|
|
1895
|
+
const response = await this.context.goldskyClient.request(query, { holder: this.walletAddress });
|
|
1896
|
+
return response.closedPositions.map((position) => ({
|
|
1897
|
+
perpId: position.perp.id,
|
|
1898
|
+
wasMaker: position.wasMaker,
|
|
1899
|
+
wasLong: position.wasLong,
|
|
1900
|
+
pnlAtClose: Number(position.pnlAtClose)
|
|
1901
|
+
}));
|
|
1902
|
+
}
|
|
1903
|
+
async realizedPnl() {
|
|
1904
|
+
return (await this.closedPositions()).reduce((acc, position) => acc + position.pnlAtClose, 0);
|
|
1905
|
+
}
|
|
1906
|
+
async unrealizedPnl() {
|
|
1907
|
+
const openPositions = await this.openPositions();
|
|
1908
|
+
const liveDetails = await Promise.all(openPositions.map((position) => position.liveDetails()));
|
|
1909
|
+
return liveDetails.reduce((acc, detail) => acc + detail.pnl - detail.fundingPayment, 0);
|
|
1910
|
+
}
|
|
1911
|
+
};
|
|
1912
|
+
|
|
1234
1913
|
// src/abis/beacon.ts
|
|
1235
1914
|
var BEACON_ABI = [
|
|
1236
1915
|
{
|
|
@@ -1488,21 +2167,25 @@ var BEACON_ABI = [
|
|
|
1488
2167
|
];
|
|
1489
2168
|
export {
|
|
1490
2169
|
BEACON_ABI,
|
|
1491
|
-
|
|
1492
|
-
|
|
2170
|
+
BIGINT_1E6,
|
|
2171
|
+
DEPLOYMENTS,
|
|
2172
|
+
NUMBER_1E6,
|
|
2173
|
+
OpenPosition,
|
|
1493
2174
|
PERP_MANAGER_ABI,
|
|
1494
|
-
PERP_MANAGER_BASE_SEPOLIA_ADDRESS,
|
|
1495
2175
|
Perp,
|
|
1496
2176
|
PerpCityContext,
|
|
1497
2177
|
PerpManager,
|
|
1498
|
-
Position,
|
|
1499
2178
|
Q96,
|
|
2179
|
+
User,
|
|
1500
2180
|
approveUsdc,
|
|
1501
2181
|
estimateLiquidity,
|
|
1502
|
-
|
|
2182
|
+
marginRatioToLeverage,
|
|
1503
2183
|
priceToSqrtPriceX96,
|
|
1504
2184
|
priceToTick,
|
|
1505
2185
|
scale6Decimals,
|
|
1506
|
-
|
|
2186
|
+
scaleFrom6Decimals,
|
|
2187
|
+
scaleFromX96,
|
|
2188
|
+
scaleToX96,
|
|
2189
|
+
sqrtPriceX96ToPrice
|
|
1507
2190
|
};
|
|
1508
2191
|
//# sourceMappingURL=index.mjs.map
|