@strobelabs/perpcity-sdk 0.1.1 → 0.1.2

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 CHANGED
@@ -1 +1,50 @@
1
- # perpcity-sdk
1
+ # perpcity-sdk
2
+
3
+ TypeScript SDK to interact with Perp City
4
+
5
+ ### Installation
6
+
7
+ ```
8
+ pnpm i @strobelabs/perpcity-sdk
9
+ ```
10
+
11
+ ### Getting Started
12
+
13
+ ```typescript
14
+ import { PerpCityContext, PerpManager } from '@strobelabs/perpcity-sdk';
15
+
16
+ const ctx = new PerpCityContext({
17
+ publicClient: publicClient,
18
+ walletClient: walletClient,
19
+ goldskyEndpoint: GOLDSKY_URL,
20
+ perpManagerAddress: PERP_MANAGER_ADDRESS,
21
+ perpManagerAbi: PERP_MANAGER_ABI,
22
+ beaconAbi: BEACON_ABI
23
+ });
24
+
25
+ const perpManager = new PerpManager(ctx);
26
+ ```
27
+
28
+ ### PerpManager Actions
29
+
30
+ #### Create a Perp
31
+
32
+ ```typescript
33
+ const perp = await perpManager.createPerp({
34
+ startingPrice: 12.3,
35
+ beacon: '0x123...'
36
+ });
37
+ ```
38
+
39
+ ### Open a Maker Position
40
+
41
+ ```typescript
42
+ const perp = await perpManager.openMakerPosition({
43
+ marign: 12.3,
44
+ liquidity: 123,
45
+
46
+ });
47
+ ```
48
+
49
+ ### Position Actions
50
+
package/dist/index.d.mts CHANGED
@@ -1,29 +1,78 @@
1
- import { PublicClient, WalletClient, Address, Abi } from 'viem';
1
+ import { PublicClient, WalletClient, Address, Abi, Hex } from 'viem';
2
+ import { GraphQLClient } from 'graphql-request';
2
3
 
3
- type PerpCityContext = {
4
+ type PerpCityContextConfig = {
4
5
  publicClient: PublicClient;
5
6
  walletClient: WalletClient;
6
- addresses: {
7
- perpManager: Address;
8
- };
9
- abis: {
10
- perpManager: Abi;
11
- beacon: Abi;
12
- };
13
- endpoints: {
14
- goldsky: URL;
15
- };
7
+ goldskyEndpoint: string;
8
+ perpManagerAddress: Address;
9
+ perpManagerAbi: Abi;
10
+ beaconAbi: Abi;
16
11
  };
12
+ declare class PerpCityContext {
13
+ readonly publicClient: PublicClient;
14
+ readonly walletClient: WalletClient;
15
+ readonly goldskyClient: GraphQLClient;
16
+ readonly perpManagerAddress: Address;
17
+ readonly perpManagerAbi: Abi;
18
+ readonly beaconAbi: Abi;
19
+ constructor(config: PerpCityContextConfig);
20
+ }
17
21
 
18
- declare class PerpManager {
19
- readonly context: PerpCityContext;
20
- constructor(context: PerpCityContext);
21
- createPerp(startingPrice: number, beacon: Address): Promise<`0x${string}`>;
22
+ type ClosePositionParams = {
23
+ minAmt0Out: number;
24
+ minAmt1Out: number;
25
+ maxAmt1In: number;
26
+ };
27
+ declare class Position {
28
+ private readonly context;
29
+ private readonly perpId;
30
+ readonly positionId: bigint;
31
+ constructor(context: PerpCityContext, perpId: Hex, positionId: bigint);
32
+ closePosition(params: ClosePositionParams): Promise<Position | null>;
22
33
  }
23
34
 
35
+ type OpenMakerPositionParams = {
36
+ margin: number;
37
+ liquidity: number;
38
+ tickLower: number;
39
+ tickUpper: number;
40
+ maxAmt0In: number;
41
+ maxAmt1In: number;
42
+ };
43
+ type OpenTakerPositionParams = {
44
+ isLong: boolean;
45
+ margin: number;
46
+ leverage: number;
47
+ unspecifiedAmountLimit: number;
48
+ };
24
49
  declare class Perp {
50
+ private readonly context;
51
+ readonly id: Hex;
52
+ constructor(context: PerpCityContext, id: Hex);
53
+ openMakerPosition(params: OpenMakerPositionParams): Promise<Position>;
54
+ openTakerPosition(params: OpenTakerPositionParams): Promise<Position>;
55
+ }
56
+
57
+ declare class PerpCollection {
58
+ private readonly context;
59
+ readonly perps: Perp[];
60
+ constructor(context: PerpCityContext, perps: Perp[]);
61
+ }
62
+
63
+ type CreatePerpParams = {
64
+ startingPrice: number;
65
+ beacon: Address;
66
+ };
67
+ declare class PerpManager {
68
+ private readonly context;
69
+ constructor(context: PerpCityContext);
70
+ getPerps(): Promise<PerpCollection>;
71
+ createPerp(params: CreatePerpParams): Promise<Perp>;
25
72
  }
26
73
 
27
74
  declare function priceToSqrtPriceX96(price: number): bigint;
75
+ declare function scale6Decimals(amount: number): bigint;
76
+ declare function scaleX96(amount: number): bigint;
28
77
 
29
- export { Perp, type PerpCityContext, PerpManager, priceToSqrtPriceX96 };
78
+ export { type CreatePerpParams, type OpenMakerPositionParams, type OpenTakerPositionParams, Perp, PerpCityContext, type PerpCityContextConfig, PerpManager, priceToSqrtPriceX96, scale6Decimals, scaleX96 };
package/dist/index.d.ts CHANGED
@@ -1,29 +1,78 @@
1
- import { PublicClient, WalletClient, Address, Abi } from 'viem';
1
+ import { PublicClient, WalletClient, Address, Abi, Hex } from 'viem';
2
+ import { GraphQLClient } from 'graphql-request';
2
3
 
3
- type PerpCityContext = {
4
+ type PerpCityContextConfig = {
4
5
  publicClient: PublicClient;
5
6
  walletClient: WalletClient;
6
- addresses: {
7
- perpManager: Address;
8
- };
9
- abis: {
10
- perpManager: Abi;
11
- beacon: Abi;
12
- };
13
- endpoints: {
14
- goldsky: URL;
15
- };
7
+ goldskyEndpoint: string;
8
+ perpManagerAddress: Address;
9
+ perpManagerAbi: Abi;
10
+ beaconAbi: Abi;
16
11
  };
12
+ declare class PerpCityContext {
13
+ readonly publicClient: PublicClient;
14
+ readonly walletClient: WalletClient;
15
+ readonly goldskyClient: GraphQLClient;
16
+ readonly perpManagerAddress: Address;
17
+ readonly perpManagerAbi: Abi;
18
+ readonly beaconAbi: Abi;
19
+ constructor(config: PerpCityContextConfig);
20
+ }
17
21
 
18
- declare class PerpManager {
19
- readonly context: PerpCityContext;
20
- constructor(context: PerpCityContext);
21
- createPerp(startingPrice: number, beacon: Address): Promise<`0x${string}`>;
22
+ type ClosePositionParams = {
23
+ minAmt0Out: number;
24
+ minAmt1Out: number;
25
+ maxAmt1In: number;
26
+ };
27
+ declare class Position {
28
+ private readonly context;
29
+ private readonly perpId;
30
+ readonly positionId: bigint;
31
+ constructor(context: PerpCityContext, perpId: Hex, positionId: bigint);
32
+ closePosition(params: ClosePositionParams): Promise<Position | null>;
22
33
  }
23
34
 
35
+ type OpenMakerPositionParams = {
36
+ margin: number;
37
+ liquidity: number;
38
+ tickLower: number;
39
+ tickUpper: number;
40
+ maxAmt0In: number;
41
+ maxAmt1In: number;
42
+ };
43
+ type OpenTakerPositionParams = {
44
+ isLong: boolean;
45
+ margin: number;
46
+ leverage: number;
47
+ unspecifiedAmountLimit: number;
48
+ };
24
49
  declare class Perp {
50
+ private readonly context;
51
+ readonly id: Hex;
52
+ constructor(context: PerpCityContext, id: Hex);
53
+ openMakerPosition(params: OpenMakerPositionParams): Promise<Position>;
54
+ openTakerPosition(params: OpenTakerPositionParams): Promise<Position>;
55
+ }
56
+
57
+ declare class PerpCollection {
58
+ private readonly context;
59
+ readonly perps: Perp[];
60
+ constructor(context: PerpCityContext, perps: Perp[]);
61
+ }
62
+
63
+ type CreatePerpParams = {
64
+ startingPrice: number;
65
+ beacon: Address;
66
+ };
67
+ declare class PerpManager {
68
+ private readonly context;
69
+ constructor(context: PerpCityContext);
70
+ getPerps(): Promise<PerpCollection>;
71
+ createPerp(params: CreatePerpParams): Promise<Perp>;
25
72
  }
26
73
 
27
74
  declare function priceToSqrtPriceX96(price: number): bigint;
75
+ declare function scale6Decimals(amount: number): bigint;
76
+ declare function scaleX96(amount: number): bigint;
28
77
 
29
- export { Perp, type PerpCityContext, PerpManager, priceToSqrtPriceX96 };
78
+ export { type CreatePerpParams, type OpenMakerPositionParams, type OpenTakerPositionParams, Perp, PerpCityContext, type PerpCityContextConfig, PerpManager, priceToSqrtPriceX96, scale6Decimals, scaleX96 };
package/dist/index.js CHANGED
@@ -21,11 +21,27 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
23
  Perp: () => Perp,
24
+ PerpCityContext: () => PerpCityContext,
24
25
  PerpManager: () => PerpManager,
25
- priceToSqrtPriceX96: () => priceToSqrtPriceX96
26
+ priceToSqrtPriceX96: () => priceToSqrtPriceX96,
27
+ scale6Decimals: () => scale6Decimals,
28
+ scaleX96: () => scaleX96
26
29
  });
27
30
  module.exports = __toCommonJS(index_exports);
28
31
 
32
+ // src/context.ts
33
+ var import_graphql_request = require("graphql-request");
34
+ var PerpCityContext = class {
35
+ constructor(config) {
36
+ this.publicClient = config.publicClient;
37
+ this.walletClient = config.walletClient;
38
+ this.goldskyClient = new import_graphql_request.GraphQLClient(config.goldskyEndpoint);
39
+ this.perpManagerAddress = config.perpManagerAddress;
40
+ this.perpManagerAbi = config.perpManagerAbi;
41
+ this.beaconAbi = config.beaconAbi;
42
+ }
43
+ };
44
+
29
45
  // src/utils/constants.ts
30
46
  var DECIMAL_PRECISION_6 = 1e6;
31
47
  var Q96 = 79228162514264337593543950336n;
@@ -36,36 +52,150 @@ function priceToSqrtPriceX96(price) {
36
52
  throw new Error("Price too large");
37
53
  }
38
54
  const scaledSqrtPrice = Math.sqrt(price) * DECIMAL_PRECISION_6;
39
- return BigInt(Math.floor(scaledSqrtPrice)) * Q96;
55
+ return BigInt(Math.floor(scaledSqrtPrice)) * Q96 / BigInt(DECIMAL_PRECISION_6);
56
+ }
57
+ function scale6Decimals(amount) {
58
+ if (amount > Number.MAX_SAFE_INTEGER / DECIMAL_PRECISION_6) {
59
+ throw new Error("Amount too large");
60
+ }
61
+ return BigInt(Math.floor(amount * DECIMAL_PRECISION_6));
62
+ }
63
+ function scaleX96(amount) {
64
+ return BigInt(scale6Decimals(amount)) * Q96 / BigInt(DECIMAL_PRECISION_6);
40
65
  }
41
66
 
42
- // src/entities/perp-manager.ts
43
- var PerpManager = class {
44
- constructor(context) {
67
+ // src/entities/position.ts
68
+ var Position = class _Position {
69
+ constructor(context, perpId, positionId) {
45
70
  this.context = context;
71
+ this.perpId = perpId;
72
+ this.positionId = positionId;
46
73
  }
47
- // TODO: retuen a Promise<Perp> instead of the transaction hash
48
- async createPerp(startingPrice, beacon) {
49
- const sqrtPriceX96 = priceToSqrtPriceX96(startingPrice);
50
- const { request } = await this.context.publicClient.simulateContract({
51
- address: this.context.addresses.perpManager,
52
- abi: this.context.abis.perpManager,
53
- functionName: "createPerp",
54
- args: [sqrtPriceX96, beacon],
74
+ async closePosition(params) {
75
+ const contractParams = {
76
+ positionId: this.positionId,
77
+ minAmt0Out: scale6Decimals(params.minAmt0Out),
78
+ minAmt1Out: scale6Decimals(params.minAmt1Out),
79
+ maxAmt1In: scale6Decimals(params.maxAmt1In)
80
+ };
81
+ const { result, request } = await this.context.publicClient.simulateContract({
82
+ address: this.context.perpManagerAddress,
83
+ abi: this.context.perpManagerAbi,
84
+ functionName: "closePosition",
85
+ args: [this.perpId, contractParams],
55
86
  account: this.context.walletClient.account
56
87
  });
57
- const hash = await this.context.walletClient.writeContract(request);
58
- return hash;
88
+ await this.context.walletClient.writeContract(request);
89
+ const takerPositionId = result[0];
90
+ if (takerPositionId === 0n) {
91
+ return null;
92
+ }
93
+ return new _Position(this.context, this.perpId, takerPositionId);
59
94
  }
60
95
  };
61
96
 
62
97
  // src/entities/perp.ts
63
98
  var Perp = class {
99
+ constructor(context, id) {
100
+ this.context = context;
101
+ this.id = id;
102
+ }
103
+ // READS
104
+ // WRITES
105
+ // TODO: read tickspacing from perp to adjust tickLower and tickUpper
106
+ async openMakerPosition(params) {
107
+ const contractParams = {
108
+ margin: scale6Decimals(params.margin),
109
+ liquidity: BigInt(params.liquidity),
110
+ tickLower: BigInt(params.tickLower),
111
+ tickUpper: BigInt(params.tickUpper),
112
+ maxAmt0In: scale6Decimals(params.maxAmt0In),
113
+ maxAmt1In: scale6Decimals(params.maxAmt1In)
114
+ };
115
+ const { result, request } = await this.context.publicClient.simulateContract({
116
+ address: this.context.perpManagerAddress,
117
+ abi: this.context.perpManagerAbi,
118
+ functionName: "openMakerPosition",
119
+ args: [this.id, contractParams],
120
+ account: this.context.walletClient.account
121
+ });
122
+ await this.context.walletClient.writeContract(request);
123
+ return new Position(this.context, this.id, result[0]);
124
+ }
125
+ async openTakerPosition(params) {
126
+ const contractParams = {
127
+ isLong: params.isLong,
128
+ margin: scale6Decimals(params.margin),
129
+ leverage: scaleX96(params.leverage),
130
+ unspecifiedAmountLimit: scale6Decimals(params.unspecifiedAmountLimit)
131
+ };
132
+ const { result, request } = await this.context.publicClient.simulateContract({
133
+ address: this.context.perpManagerAddress,
134
+ abi: this.context.perpManagerAbi,
135
+ functionName: "openTakerPosition",
136
+ args: [this.id, contractParams],
137
+ account: this.context.walletClient.account
138
+ });
139
+ await this.context.walletClient.writeContract(request);
140
+ return new Position(this.context, this.id, result[0]);
141
+ }
142
+ };
143
+
144
+ // src/entities/perp-collection.ts
145
+ var PerpCollection = class {
146
+ constructor(context, perps) {
147
+ this.context = context;
148
+ this.perps = perps;
149
+ }
150
+ };
151
+
152
+ // src/entities/perp-manager.ts
153
+ var import_graphql_request2 = require("graphql-request");
154
+ var import_graphql = require("graphql");
155
+ var PerpManager = class {
156
+ constructor(context) {
157
+ this.context = context;
158
+ }
159
+ // READS
160
+ async getPerps() {
161
+ const query = (0, import_graphql.parse)(import_graphql_request2.gql`
162
+ {
163
+ perps {
164
+ id
165
+ }
166
+ }
167
+ `);
168
+ const response = await this.context.goldskyClient.request(query);
169
+ const perps = response.perps.map(
170
+ (perpData) => new Perp(this.context, perpData.id)
171
+ );
172
+ return new PerpCollection(this.context, perps);
173
+ }
174
+ // WRITES
175
+ async createPerp(params) {
176
+ const sqrtPriceX96 = priceToSqrtPriceX96(params.startingPrice);
177
+ const contractParams = {
178
+ startingSqrtPriceX96: sqrtPriceX96,
179
+ beacon: params.beacon
180
+ };
181
+ const { result, request } = await this.context.publicClient.simulateContract({
182
+ address: this.context.perpManagerAddress,
183
+ abi: this.context.perpManagerAbi,
184
+ functionName: "createPerp",
185
+ args: [contractParams],
186
+ account: this.context.walletClient.account
187
+ });
188
+ await this.context.walletClient.writeContract(request);
189
+ return new Perp(this.context, result[0]);
190
+ }
64
191
  };
65
192
  // Annotate the CommonJS export names for ESM import in node:
66
193
  0 && (module.exports = {
67
194
  Perp,
195
+ PerpCityContext,
68
196
  PerpManager,
69
- priceToSqrtPriceX96
197
+ priceToSqrtPriceX96,
198
+ scale6Decimals,
199
+ scaleX96
70
200
  });
71
201
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/utils/constants.ts","../src/utils/conversions.ts","../src/entities/perp-manager.ts","../src/entities/perp.ts"],"sourcesContent":["export * from './context';\nexport * from './entities';\nexport * from './utils';","export const DECIMAL_PRECISION_6: number = 1000000; // 1e6\n\nexport const Q96: bigint = 79228162514264337593543950336n; // 2^96\n","import { DECIMAL_PRECISION_6, Q96 } from \"./constants\";\n\nexport function priceToSqrtPriceX96(price: number): bigint {\n if (price > Number.MAX_SAFE_INTEGER) {\n throw new Error('Price too large');\n }\n\n const scaledSqrtPrice: number = Math.sqrt(price) * DECIMAL_PRECISION_6;\n return BigInt(Math.floor(scaledSqrtPrice)) * Q96;\n}","import { PerpCityContext } from \"../context\";\nimport { Perp } from \"./perp\";\nimport { priceToSqrtPriceX96 } from \"../utils\";\nimport type { Address } from \"viem\";\n\nexport class PerpManager {\n public readonly context: PerpCityContext;\n \n constructor(context: PerpCityContext) {\n this.context = context;\n }\n\n // TODO: retuen a Promise<Perp> instead of the transaction hash\n async createPerp(startingPrice: number, beacon: Address): Promise<`0x${string}`> {\n const sqrtPriceX96: bigint = priceToSqrtPriceX96(startingPrice);\n\n const { request } = await this.context.publicClient.simulateContract({\n address: this.context.addresses.perpManager,\n abi: this.context.abis.perpManager,\n functionName: 'createPerp',\n args: [sqrtPriceX96, beacon],\n account: this.context.walletClient.account,\n });\n\n const hash: `0x${string}` = await this.context.walletClient.writeContract(request);\n\n return hash;\n }\n}","\n\nexport class Perp {\n \n}"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAO,IAAM,sBAA8B;AAEpC,IAAM,MAAc;;;ACApB,SAAS,oBAAoB,OAAuB;AACzD,MAAI,QAAQ,OAAO,kBAAkB;AACnC,UAAM,IAAI,MAAM,iBAAiB;AAAA,EACnC;AAEA,QAAM,kBAA0B,KAAK,KAAK,KAAK,IAAI;AACnD,SAAO,OAAO,KAAK,MAAM,eAAe,CAAC,IAAI;AAC/C;;;ACJO,IAAM,cAAN,MAAkB;AAAA,EAGvB,YAAY,SAA0B;AACpC,SAAK,UAAU;AAAA,EACjB;AAAA;AAAA,EAGA,MAAM,WAAW,eAAuB,QAAyC;AAC/E,UAAM,eAAuB,oBAAoB,aAAa;AAE9D,UAAM,EAAE,QAAQ,IAAI,MAAM,KAAK,QAAQ,aAAa,iBAAiB;AAAA,MACnE,SAAS,KAAK,QAAQ,UAAU;AAAA,MAChC,KAAK,KAAK,QAAQ,KAAK;AAAA,MACvB,cAAc;AAAA,MACd,MAAM,CAAC,cAAc,MAAM;AAAA,MAC3B,SAAS,KAAK,QAAQ,aAAa;AAAA,IACrC,CAAC;AAED,UAAM,OAAsB,MAAM,KAAK,QAAQ,aAAa,cAAc,OAAO;AAEjF,WAAO;AAAA,EACT;AACF;;;AC1BO,IAAM,OAAN,MAAW;AAElB;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/context.ts","../src/utils/constants.ts","../src/utils/conversions.ts","../src/entities/position.ts","../src/entities/perp.ts","../src/entities/perp-collection.ts","../src/entities/perp-manager.ts"],"sourcesContent":["export * from './context';\nexport * from './entities';\nexport * from './utils';","import type { PublicClient, WalletClient, Abi, Address} from \"viem\";\nimport { GraphQLClient } from 'graphql-request'\n\nexport type PerpCityContextConfig = {\n publicClient: PublicClient;\n walletClient: WalletClient;\n goldskyEndpoint: string;\n perpManagerAddress: Address;\n perpManagerAbi: Abi;\n beaconAbi: Abi;\n}\n\nexport class PerpCityContext {\n public readonly publicClient: PublicClient;\n public readonly walletClient: WalletClient;\n // below will be hard-coded in addresses.ts, abis.ts, and endpoints.ts once they are frozen\n // they are currently being rapidly redeployed so can be specified by the sdk consumer for now\n public readonly goldskyClient: GraphQLClient;\n public readonly perpManagerAddress: Address;\n public readonly perpManagerAbi: Abi;\n public readonly beaconAbi: Abi;\n\n constructor(config: PerpCityContextConfig) {\n this.publicClient = config.publicClient;\n this.walletClient = config.walletClient;\n this.goldskyClient = new GraphQLClient(config.goldskyEndpoint);\n this.perpManagerAddress = config.perpManagerAddress;\n this.perpManagerAbi = config.perpManagerAbi;\n this.beaconAbi = config.beaconAbi;\n }\n}","export const DECIMAL_PRECISION_6: number = 1000000; // 1e6\n\nexport const Q96: bigint = 79228162514264337593543950336n; // 2^96\n","import { DECIMAL_PRECISION_6, Q96 } from \"./constants\";\n\nexport function priceToSqrtPriceX96(price: number): bigint {\n if (price > Number.MAX_SAFE_INTEGER) {\n throw new Error('Price too large');\n }\n\n const scaledSqrtPrice: number = Math.sqrt(price) * DECIMAL_PRECISION_6;\n return BigInt(Math.floor(scaledSqrtPrice)) * Q96 / BigInt(DECIMAL_PRECISION_6);\n}\n\nexport function scale6Decimals(amount: number): bigint {\n if (amount > Number.MAX_SAFE_INTEGER / DECIMAL_PRECISION_6) {\n throw new Error('Amount too large');\n }\n\n return BigInt(Math.floor(amount * DECIMAL_PRECISION_6));\n}\n\nexport function scaleX96(amount: number): bigint {\n return BigInt(scale6Decimals(amount)) * Q96 / BigInt(DECIMAL_PRECISION_6);\n}","import type { Hex } from \"viem\";\nimport { PerpCityContext } from \"../context\";\nimport { scale6Decimals } from \"../utils\";\n\nexport type ClosePositionParams = {\n minAmt0Out: number;\n minAmt1Out: number;\n maxAmt1In: number;\n }\n\nexport class Position {\n private readonly context: PerpCityContext;\n private readonly perpId: Hex;\n public readonly positionId: bigint;\n\n constructor(context: PerpCityContext, perpId: Hex, positionId: bigint) {\n this.context = context;\n this.perpId = perpId;\n this.positionId = positionId;\n }\n\n async closePosition(params: ClosePositionParams): Promise<Position | null> {\n const contractParams = {\n positionId: this.positionId,\n minAmt0Out: scale6Decimals(params.minAmt0Out),\n minAmt1Out: scale6Decimals(params.minAmt1Out),\n maxAmt1In: scale6Decimals(params.maxAmt1In),\n };\n \n const { result, request } = await this.context.publicClient.simulateContract({\n address: this.context.perpManagerAddress,\n abi: this.context.perpManagerAbi,\n functionName: 'closePosition',\n args: [this.perpId, contractParams],\n account: this.context.walletClient.account,\n });\n\n await this.context.walletClient.writeContract(request);\n\n const takerPositionId: bigint = result[0];\n\n if (takerPositionId === 0n) {\n return null;\n }\n \n return new Position(this.context, this.perpId, takerPositionId);\n }\n}","import type {Hex} from \"viem\";\nimport { PerpCityContext } from \"../context\";\nimport { Position } from \"./position\";\nimport { scale6Decimals, scaleX96 } from \"../utils\";\n\nexport type OpenMakerPositionParams = {\n margin: number;\n liquidity: number;\n tickLower: number;\n tickUpper: number;\n maxAmt0In: number;\n maxAmt1In: number;\n}\n\nexport type OpenTakerPositionParams = {\n isLong: boolean;\n margin: number;\n leverage: number;\n unspecifiedAmountLimit: number;\n \n}\n\nexport class Perp {\n private readonly context: PerpCityContext;\n public readonly id: Hex;\n\n constructor(context: PerpCityContext, id: Hex) {\n this.context = context;\n this.id = id;\n }\n\n // READS\n\n // WRITES\n\n // TODO: read tickspacing from perp to adjust tickLower and tickUpper\n async openMakerPosition(params: OpenMakerPositionParams): Promise<Position> {\n const contractParams = {\n margin: scale6Decimals(params.margin),\n liquidity: BigInt(params.liquidity),\n tickLower: BigInt(params.tickLower),\n tickUpper: BigInt(params.tickUpper),\n maxAmt0In: scale6Decimals(params.maxAmt0In),\n maxAmt1In: scale6Decimals(params.maxAmt1In),\n };\n\n const { result, request } = await this.context.publicClient.simulateContract({\n address: this.context.perpManagerAddress,\n abi: this.context.perpManagerAbi,\n functionName: 'openMakerPosition',\n args: [this.id, contractParams],\n account: this.context.walletClient.account,\n });\n\n await this.context.walletClient.writeContract(request);\n\n return new Position(this.context, this.id, result[0] as bigint);\n }\n\n async openTakerPosition(params: OpenTakerPositionParams): Promise<Position> {\n const contractParams = {\n isLong: params.isLong,\n margin: scale6Decimals(params.margin),\n leverage: scaleX96(params.leverage),\n unspecifiedAmountLimit: scale6Decimals(params.unspecifiedAmountLimit),\n };\n \n const { result, request } = await this.context.publicClient.simulateContract({\n address: this.context.perpManagerAddress,\n abi: this.context.perpManagerAbi,\n functionName: 'openTakerPosition',\n args: [this.id, contractParams],\n account: this.context.walletClient.account,\n });\n\n await this.context.walletClient.writeContract(request);\n\n return new Position(this.context, this.id, result[0] as bigint);\n }\n}","import { PerpCityContext } from \"../context\";\nimport { Perp } from \"./perp\";\n\n// In the future, this is where multicalls are used to fetch data across many perps\nexport class PerpCollection {\n private readonly context: PerpCityContext;\n public readonly perps: Perp[];\n\n constructor(context: PerpCityContext, perps: Perp[]) {\n this.context = context;\n this.perps = perps;\n }\n}","import { PerpCityContext } from \"../context\";\nimport { Perp } from \"./perp\";\nimport { priceToSqrtPriceX96 } from \"../utils\";\nimport { PerpCollection } from \"./perp-collection\";\nimport type { Address, Hex } from \"viem\";\nimport { gql } from \"graphql-request\";\nimport type { TypedDocumentNode } from '@graphql-typed-document-node/core';\nimport { parse } from 'graphql';\n\nexport type CreatePerpParams = {\n startingPrice: number;\n beacon: Address;\n}\n\nexport class PerpManager {\n private readonly context: PerpCityContext;\n \n constructor(context: PerpCityContext) {\n this.context = context;\n }\n\n // READS\n\n async getPerps(): Promise<PerpCollection> {\n const query: TypedDocumentNode<{ perps: { id: Hex }[] }, Record<any, never>> = parse(gql`\n {\n perps {\n id\n }\n }\n `)\n\n const response = await this.context.goldskyClient.request(query);\n \n const perps = response.perps.map((perpData: { id: Hex }) => \n new Perp(this.context, perpData.id as Hex)\n );\n\n return new PerpCollection(this.context, perps);\n }\n\n // WRITES\n\n async createPerp(params: CreatePerpParams): Promise<Perp> {\n const sqrtPriceX96: bigint = priceToSqrtPriceX96(params.startingPrice);\n\n const contractParams = {\n startingSqrtPriceX96: sqrtPriceX96,\n beacon: params.beacon,\n };\n\n const { result, request } = await this.context.publicClient.simulateContract({\n address: this.context.perpManagerAddress,\n abi: this.context.perpManagerAbi,\n functionName: 'createPerp',\n args: [contractParams],\n account: this.context.walletClient.account,\n });\n\n await this.context.walletClient.writeContract(request);\n\n return new Perp(this.context, result[0] as Hex);\n }\n}"],"mappings":"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","names":["import_graphql_request"]}
package/dist/index.mjs CHANGED
@@ -1,3 +1,16 @@
1
+ // src/context.ts
2
+ import { GraphQLClient } from "graphql-request";
3
+ var PerpCityContext = class {
4
+ constructor(config) {
5
+ this.publicClient = config.publicClient;
6
+ this.walletClient = config.walletClient;
7
+ this.goldskyClient = new GraphQLClient(config.goldskyEndpoint);
8
+ this.perpManagerAddress = config.perpManagerAddress;
9
+ this.perpManagerAbi = config.perpManagerAbi;
10
+ this.beaconAbi = config.beaconAbi;
11
+ }
12
+ };
13
+
1
14
  // src/utils/constants.ts
2
15
  var DECIMAL_PRECISION_6 = 1e6;
3
16
  var Q96 = 79228162514264337593543950336n;
@@ -8,35 +21,149 @@ function priceToSqrtPriceX96(price) {
8
21
  throw new Error("Price too large");
9
22
  }
10
23
  const scaledSqrtPrice = Math.sqrt(price) * DECIMAL_PRECISION_6;
11
- return BigInt(Math.floor(scaledSqrtPrice)) * Q96;
24
+ return BigInt(Math.floor(scaledSqrtPrice)) * Q96 / BigInt(DECIMAL_PRECISION_6);
25
+ }
26
+ function scale6Decimals(amount) {
27
+ if (amount > Number.MAX_SAFE_INTEGER / DECIMAL_PRECISION_6) {
28
+ throw new Error("Amount too large");
29
+ }
30
+ return BigInt(Math.floor(amount * DECIMAL_PRECISION_6));
31
+ }
32
+ function scaleX96(amount) {
33
+ return BigInt(scale6Decimals(amount)) * Q96 / BigInt(DECIMAL_PRECISION_6);
12
34
  }
13
35
 
14
- // src/entities/perp-manager.ts
15
- var PerpManager = class {
16
- constructor(context) {
36
+ // src/entities/position.ts
37
+ var Position = class _Position {
38
+ constructor(context, perpId, positionId) {
17
39
  this.context = context;
40
+ this.perpId = perpId;
41
+ this.positionId = positionId;
18
42
  }
19
- // TODO: retuen a Promise<Perp> instead of the transaction hash
20
- async createPerp(startingPrice, beacon) {
21
- const sqrtPriceX96 = priceToSqrtPriceX96(startingPrice);
22
- const { request } = await this.context.publicClient.simulateContract({
23
- address: this.context.addresses.perpManager,
24
- abi: this.context.abis.perpManager,
25
- functionName: "createPerp",
26
- args: [sqrtPriceX96, beacon],
43
+ async closePosition(params) {
44
+ const contractParams = {
45
+ positionId: this.positionId,
46
+ minAmt0Out: scale6Decimals(params.minAmt0Out),
47
+ minAmt1Out: scale6Decimals(params.minAmt1Out),
48
+ maxAmt1In: scale6Decimals(params.maxAmt1In)
49
+ };
50
+ const { result, request } = await this.context.publicClient.simulateContract({
51
+ address: this.context.perpManagerAddress,
52
+ abi: this.context.perpManagerAbi,
53
+ functionName: "closePosition",
54
+ args: [this.perpId, contractParams],
27
55
  account: this.context.walletClient.account
28
56
  });
29
- const hash = await this.context.walletClient.writeContract(request);
30
- return hash;
57
+ await this.context.walletClient.writeContract(request);
58
+ const takerPositionId = result[0];
59
+ if (takerPositionId === 0n) {
60
+ return null;
61
+ }
62
+ return new _Position(this.context, this.perpId, takerPositionId);
31
63
  }
32
64
  };
33
65
 
34
66
  // src/entities/perp.ts
35
67
  var Perp = class {
68
+ constructor(context, id) {
69
+ this.context = context;
70
+ this.id = id;
71
+ }
72
+ // READS
73
+ // WRITES
74
+ // TODO: read tickspacing from perp to adjust tickLower and tickUpper
75
+ async openMakerPosition(params) {
76
+ const contractParams = {
77
+ margin: scale6Decimals(params.margin),
78
+ liquidity: BigInt(params.liquidity),
79
+ tickLower: BigInt(params.tickLower),
80
+ tickUpper: BigInt(params.tickUpper),
81
+ maxAmt0In: scale6Decimals(params.maxAmt0In),
82
+ maxAmt1In: scale6Decimals(params.maxAmt1In)
83
+ };
84
+ const { result, request } = await this.context.publicClient.simulateContract({
85
+ address: this.context.perpManagerAddress,
86
+ abi: this.context.perpManagerAbi,
87
+ functionName: "openMakerPosition",
88
+ args: [this.id, contractParams],
89
+ account: this.context.walletClient.account
90
+ });
91
+ await this.context.walletClient.writeContract(request);
92
+ return new Position(this.context, this.id, result[0]);
93
+ }
94
+ async openTakerPosition(params) {
95
+ const contractParams = {
96
+ isLong: params.isLong,
97
+ margin: scale6Decimals(params.margin),
98
+ leverage: scaleX96(params.leverage),
99
+ unspecifiedAmountLimit: scale6Decimals(params.unspecifiedAmountLimit)
100
+ };
101
+ const { result, request } = await this.context.publicClient.simulateContract({
102
+ address: this.context.perpManagerAddress,
103
+ abi: this.context.perpManagerAbi,
104
+ functionName: "openTakerPosition",
105
+ args: [this.id, contractParams],
106
+ account: this.context.walletClient.account
107
+ });
108
+ await this.context.walletClient.writeContract(request);
109
+ return new Position(this.context, this.id, result[0]);
110
+ }
111
+ };
112
+
113
+ // src/entities/perp-collection.ts
114
+ var PerpCollection = class {
115
+ constructor(context, perps) {
116
+ this.context = context;
117
+ this.perps = perps;
118
+ }
119
+ };
120
+
121
+ // src/entities/perp-manager.ts
122
+ import { gql } from "graphql-request";
123
+ import { parse } from "graphql";
124
+ var PerpManager = class {
125
+ constructor(context) {
126
+ this.context = context;
127
+ }
128
+ // READS
129
+ async getPerps() {
130
+ const query = parse(gql`
131
+ {
132
+ perps {
133
+ id
134
+ }
135
+ }
136
+ `);
137
+ const response = await this.context.goldskyClient.request(query);
138
+ const perps = response.perps.map(
139
+ (perpData) => new Perp(this.context, perpData.id)
140
+ );
141
+ return new PerpCollection(this.context, perps);
142
+ }
143
+ // WRITES
144
+ async createPerp(params) {
145
+ const sqrtPriceX96 = priceToSqrtPriceX96(params.startingPrice);
146
+ const contractParams = {
147
+ startingSqrtPriceX96: sqrtPriceX96,
148
+ beacon: params.beacon
149
+ };
150
+ const { result, request } = await this.context.publicClient.simulateContract({
151
+ address: this.context.perpManagerAddress,
152
+ abi: this.context.perpManagerAbi,
153
+ functionName: "createPerp",
154
+ args: [contractParams],
155
+ account: this.context.walletClient.account
156
+ });
157
+ await this.context.walletClient.writeContract(request);
158
+ return new Perp(this.context, result[0]);
159
+ }
36
160
  };
37
161
  export {
38
162
  Perp,
163
+ PerpCityContext,
39
164
  PerpManager,
40
- priceToSqrtPriceX96
165
+ priceToSqrtPriceX96,
166
+ scale6Decimals,
167
+ scaleX96
41
168
  };
42
169
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils/constants.ts","../src/utils/conversions.ts","../src/entities/perp-manager.ts","../src/entities/perp.ts"],"sourcesContent":["export const DECIMAL_PRECISION_6: number = 1000000; // 1e6\n\nexport const Q96: bigint = 79228162514264337593543950336n; // 2^96\n","import { DECIMAL_PRECISION_6, Q96 } from \"./constants\";\n\nexport function priceToSqrtPriceX96(price: number): bigint {\n if (price > Number.MAX_SAFE_INTEGER) {\n throw new Error('Price too large');\n }\n\n const scaledSqrtPrice: number = Math.sqrt(price) * DECIMAL_PRECISION_6;\n return BigInt(Math.floor(scaledSqrtPrice)) * Q96;\n}","import { PerpCityContext } from \"../context\";\nimport { Perp } from \"./perp\";\nimport { priceToSqrtPriceX96 } from \"../utils\";\nimport type { Address } from \"viem\";\n\nexport class PerpManager {\n public readonly context: PerpCityContext;\n \n constructor(context: PerpCityContext) {\n this.context = context;\n }\n\n // TODO: retuen a Promise<Perp> instead of the transaction hash\n async createPerp(startingPrice: number, beacon: Address): Promise<`0x${string}`> {\n const sqrtPriceX96: bigint = priceToSqrtPriceX96(startingPrice);\n\n const { request } = await this.context.publicClient.simulateContract({\n address: this.context.addresses.perpManager,\n abi: this.context.abis.perpManager,\n functionName: 'createPerp',\n args: [sqrtPriceX96, beacon],\n account: this.context.walletClient.account,\n });\n\n const hash: `0x${string}` = await this.context.walletClient.writeContract(request);\n\n return hash;\n }\n}","\n\nexport class Perp {\n \n}"],"mappings":";AAAO,IAAM,sBAA8B;AAEpC,IAAM,MAAc;;;ACApB,SAAS,oBAAoB,OAAuB;AACzD,MAAI,QAAQ,OAAO,kBAAkB;AACnC,UAAM,IAAI,MAAM,iBAAiB;AAAA,EACnC;AAEA,QAAM,kBAA0B,KAAK,KAAK,KAAK,IAAI;AACnD,SAAO,OAAO,KAAK,MAAM,eAAe,CAAC,IAAI;AAC/C;;;ACJO,IAAM,cAAN,MAAkB;AAAA,EAGvB,YAAY,SAA0B;AACpC,SAAK,UAAU;AAAA,EACjB;AAAA;AAAA,EAGA,MAAM,WAAW,eAAuB,QAAyC;AAC/E,UAAM,eAAuB,oBAAoB,aAAa;AAE9D,UAAM,EAAE,QAAQ,IAAI,MAAM,KAAK,QAAQ,aAAa,iBAAiB;AAAA,MACnE,SAAS,KAAK,QAAQ,UAAU;AAAA,MAChC,KAAK,KAAK,QAAQ,KAAK;AAAA,MACvB,cAAc;AAAA,MACd,MAAM,CAAC,cAAc,MAAM;AAAA,MAC3B,SAAS,KAAK,QAAQ,aAAa;AAAA,IACrC,CAAC;AAED,UAAM,OAAsB,MAAM,KAAK,QAAQ,aAAa,cAAc,OAAO;AAEjF,WAAO;AAAA,EACT;AACF;;;AC1BO,IAAM,OAAN,MAAW;AAElB;","names":[]}
1
+ {"version":3,"sources":["../src/context.ts","../src/utils/constants.ts","../src/utils/conversions.ts","../src/entities/position.ts","../src/entities/perp.ts","../src/entities/perp-collection.ts","../src/entities/perp-manager.ts"],"sourcesContent":["import type { PublicClient, WalletClient, Abi, Address} from \"viem\";\nimport { GraphQLClient } from 'graphql-request'\n\nexport type PerpCityContextConfig = {\n publicClient: PublicClient;\n walletClient: WalletClient;\n goldskyEndpoint: string;\n perpManagerAddress: Address;\n perpManagerAbi: Abi;\n beaconAbi: Abi;\n}\n\nexport class PerpCityContext {\n public readonly publicClient: PublicClient;\n public readonly walletClient: WalletClient;\n // below will be hard-coded in addresses.ts, abis.ts, and endpoints.ts once they are frozen\n // they are currently being rapidly redeployed so can be specified by the sdk consumer for now\n public readonly goldskyClient: GraphQLClient;\n public readonly perpManagerAddress: Address;\n public readonly perpManagerAbi: Abi;\n public readonly beaconAbi: Abi;\n\n constructor(config: PerpCityContextConfig) {\n this.publicClient = config.publicClient;\n this.walletClient = config.walletClient;\n this.goldskyClient = new GraphQLClient(config.goldskyEndpoint);\n this.perpManagerAddress = config.perpManagerAddress;\n this.perpManagerAbi = config.perpManagerAbi;\n this.beaconAbi = config.beaconAbi;\n }\n}","export const DECIMAL_PRECISION_6: number = 1000000; // 1e6\n\nexport const Q96: bigint = 79228162514264337593543950336n; // 2^96\n","import { DECIMAL_PRECISION_6, Q96 } from \"./constants\";\n\nexport function priceToSqrtPriceX96(price: number): bigint {\n if (price > Number.MAX_SAFE_INTEGER) {\n throw new Error('Price too large');\n }\n\n const scaledSqrtPrice: number = Math.sqrt(price) * DECIMAL_PRECISION_6;\n return BigInt(Math.floor(scaledSqrtPrice)) * Q96 / BigInt(DECIMAL_PRECISION_6);\n}\n\nexport function scale6Decimals(amount: number): bigint {\n if (amount > Number.MAX_SAFE_INTEGER / DECIMAL_PRECISION_6) {\n throw new Error('Amount too large');\n }\n\n return BigInt(Math.floor(amount * DECIMAL_PRECISION_6));\n}\n\nexport function scaleX96(amount: number): bigint {\n return BigInt(scale6Decimals(amount)) * Q96 / BigInt(DECIMAL_PRECISION_6);\n}","import type { Hex } from \"viem\";\nimport { PerpCityContext } from \"../context\";\nimport { scale6Decimals } from \"../utils\";\n\nexport type ClosePositionParams = {\n minAmt0Out: number;\n minAmt1Out: number;\n maxAmt1In: number;\n }\n\nexport class Position {\n private readonly context: PerpCityContext;\n private readonly perpId: Hex;\n public readonly positionId: bigint;\n\n constructor(context: PerpCityContext, perpId: Hex, positionId: bigint) {\n this.context = context;\n this.perpId = perpId;\n this.positionId = positionId;\n }\n\n async closePosition(params: ClosePositionParams): Promise<Position | null> {\n const contractParams = {\n positionId: this.positionId,\n minAmt0Out: scale6Decimals(params.minAmt0Out),\n minAmt1Out: scale6Decimals(params.minAmt1Out),\n maxAmt1In: scale6Decimals(params.maxAmt1In),\n };\n \n const { result, request } = await this.context.publicClient.simulateContract({\n address: this.context.perpManagerAddress,\n abi: this.context.perpManagerAbi,\n functionName: 'closePosition',\n args: [this.perpId, contractParams],\n account: this.context.walletClient.account,\n });\n\n await this.context.walletClient.writeContract(request);\n\n const takerPositionId: bigint = result[0];\n\n if (takerPositionId === 0n) {\n return null;\n }\n \n return new Position(this.context, this.perpId, takerPositionId);\n }\n}","import type {Hex} from \"viem\";\nimport { PerpCityContext } from \"../context\";\nimport { Position } from \"./position\";\nimport { scale6Decimals, scaleX96 } from \"../utils\";\n\nexport type OpenMakerPositionParams = {\n margin: number;\n liquidity: number;\n tickLower: number;\n tickUpper: number;\n maxAmt0In: number;\n maxAmt1In: number;\n}\n\nexport type OpenTakerPositionParams = {\n isLong: boolean;\n margin: number;\n leverage: number;\n unspecifiedAmountLimit: number;\n \n}\n\nexport class Perp {\n private readonly context: PerpCityContext;\n public readonly id: Hex;\n\n constructor(context: PerpCityContext, id: Hex) {\n this.context = context;\n this.id = id;\n }\n\n // READS\n\n // WRITES\n\n // TODO: read tickspacing from perp to adjust tickLower and tickUpper\n async openMakerPosition(params: OpenMakerPositionParams): Promise<Position> {\n const contractParams = {\n margin: scale6Decimals(params.margin),\n liquidity: BigInt(params.liquidity),\n tickLower: BigInt(params.tickLower),\n tickUpper: BigInt(params.tickUpper),\n maxAmt0In: scale6Decimals(params.maxAmt0In),\n maxAmt1In: scale6Decimals(params.maxAmt1In),\n };\n\n const { result, request } = await this.context.publicClient.simulateContract({\n address: this.context.perpManagerAddress,\n abi: this.context.perpManagerAbi,\n functionName: 'openMakerPosition',\n args: [this.id, contractParams],\n account: this.context.walletClient.account,\n });\n\n await this.context.walletClient.writeContract(request);\n\n return new Position(this.context, this.id, result[0] as bigint);\n }\n\n async openTakerPosition(params: OpenTakerPositionParams): Promise<Position> {\n const contractParams = {\n isLong: params.isLong,\n margin: scale6Decimals(params.margin),\n leverage: scaleX96(params.leverage),\n unspecifiedAmountLimit: scale6Decimals(params.unspecifiedAmountLimit),\n };\n \n const { result, request } = await this.context.publicClient.simulateContract({\n address: this.context.perpManagerAddress,\n abi: this.context.perpManagerAbi,\n functionName: 'openTakerPosition',\n args: [this.id, contractParams],\n account: this.context.walletClient.account,\n });\n\n await this.context.walletClient.writeContract(request);\n\n return new Position(this.context, this.id, result[0] as bigint);\n }\n}","import { PerpCityContext } from \"../context\";\nimport { Perp } from \"./perp\";\n\n// In the future, this is where multicalls are used to fetch data across many perps\nexport class PerpCollection {\n private readonly context: PerpCityContext;\n public readonly perps: Perp[];\n\n constructor(context: PerpCityContext, perps: Perp[]) {\n this.context = context;\n this.perps = perps;\n }\n}","import { PerpCityContext } from \"../context\";\nimport { Perp } from \"./perp\";\nimport { priceToSqrtPriceX96 } from \"../utils\";\nimport { PerpCollection } from \"./perp-collection\";\nimport type { Address, Hex } from \"viem\";\nimport { gql } from \"graphql-request\";\nimport type { TypedDocumentNode } from '@graphql-typed-document-node/core';\nimport { parse } from 'graphql';\n\nexport type CreatePerpParams = {\n startingPrice: number;\n beacon: Address;\n}\n\nexport class PerpManager {\n private readonly context: PerpCityContext;\n \n constructor(context: PerpCityContext) {\n this.context = context;\n }\n\n // READS\n\n async getPerps(): Promise<PerpCollection> {\n const query: TypedDocumentNode<{ perps: { id: Hex }[] }, Record<any, never>> = parse(gql`\n {\n perps {\n id\n }\n }\n `)\n\n const response = await this.context.goldskyClient.request(query);\n \n const perps = response.perps.map((perpData: { id: Hex }) => \n new Perp(this.context, perpData.id as Hex)\n );\n\n return new PerpCollection(this.context, perps);\n }\n\n // WRITES\n\n async createPerp(params: CreatePerpParams): Promise<Perp> {\n const sqrtPriceX96: bigint = priceToSqrtPriceX96(params.startingPrice);\n\n const contractParams = {\n startingSqrtPriceX96: sqrtPriceX96,\n beacon: params.beacon,\n };\n\n const { result, request } = await this.context.publicClient.simulateContract({\n address: this.context.perpManagerAddress,\n abi: this.context.perpManagerAbi,\n functionName: 'createPerp',\n args: [contractParams],\n account: this.context.walletClient.account,\n });\n\n await this.context.walletClient.writeContract(request);\n\n return new Perp(this.context, result[0] as Hex);\n }\n}"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@strobelabs/perpcity-sdk",
3
3
  "author": "Strobe Labs",
4
4
  "description": "TypeScript SDK for interacting with Perp City contracts",
5
- "version": "0.1.1",
5
+ "version": "0.1.2",
6
6
  "license": "GPL-3.0",
7
7
  "main": "dist/index.js",
8
8
  "module": "dist/index.mjs",
@@ -19,6 +19,9 @@
19
19
  "typescript": "^5.9.2"
20
20
  },
21
21
  "dependencies": {
22
+ "@graphql-typed-document-node/core": "^3.2.0",
23
+ "graphql": "^16.11.0",
24
+ "graphql-request": "^7.2.0",
22
25
  "viem": "^2.37.6"
23
26
  }
24
27
  }