@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/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
- this.goldskyClient = new GraphQLClient(config.goldskyEndpoint);
8
- this.perpManagerAddress = config.perpManagerAddress;
9
- this.perpManagerAbi = config.perpManagerAbi;
10
- this.beaconAbi = config.beaconAbi;
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
- // src/entities/perp.ts
15
- import { publicActions as publicActions4 } from "viem";
16
-
17
- // src/entities/position.ts
18
- var Position = class {
19
- constructor(context, perpId, positionId) {
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 DECIMAL_PRECISION_6 = 1e6;
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) * DECIMAL_PRECISION_6;
57
- return BigInt(Math.floor(scaledSqrtPrice)) * Q96 / BigInt(DECIMAL_PRECISION_6);
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 / DECIMAL_PRECISION_6) {
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 * DECIMAL_PRECISION_6));
65
+ return BigInt(Math.floor(amount * NUMBER_1E6));
64
66
  }
65
- function scaleX96(amount) {
66
- return BigInt(scale6Decimals(amount)) * Q96 / BigInt(DECIMAL_PRECISION_6);
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 logSqrtPrice = Math.log(Math.sqrt(price)) / Math.log(1.0001);
70
- return roundDown ? Math.floor(logSqrtPrice) : Math.ceil(logSqrtPrice);
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, publicActions as publicActions2 } from "viem";
75
- async function approveUsdc(context, amount) {
76
- const usdcAddress = await getUsdcAddress(context);
77
- const { request } = await context.walletClient.extend(publicActions2).simulateContract({
78
- address: usdcAddress,
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: [context.perpManagerAddress, amount],
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.extend(publicActions2).waitForTransactionReceipt({
86
- confirmations: 2,
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": "uint256",
491
- "name": "margin",
492
- "type": "uint256"
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": "estimateLiquidityForAount1",
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": "tickSpacing",
1214
+ "name": "quoteMakerPosition",
1203
1215
  "outputs": [
1204
1216
  {
1205
- "internalType": "int24",
1206
- "name": "",
1207
- "type": "int24"
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
- DECIMAL_PRECISION_6,
1492
- GOLDSKY_BASE_SEPOLIA_URL,
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
- getUsdcAddress,
2182
+ marginRatioToLeverage,
1503
2183
  priceToSqrtPriceX96,
1504
2184
  priceToTick,
1505
2185
  scale6Decimals,
1506
- scaleX96
2186
+ scaleFrom6Decimals,
2187
+ scaleFromX96,
2188
+ scaleToX96,
2189
+ sqrtPriceX96ToPrice
1507
2190
  };
1508
2191
  //# sourceMappingURL=index.mjs.map