@d8x/perpetuals-sdk 0.0.44 → 0.0.46

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.
Files changed (41) hide show
  1. package/README.md +12 -0
  2. package/abi/MockTokenSwap.json +186 -0
  3. package/config/defaultConfig.json +36 -10
  4. package/config/mockSwap.json +4 -0
  5. package/config/oldConfig.json +2 -1
  6. package/config/priceFeedConfig.json +18 -0
  7. package/dist/accountTrade.d.ts +1 -0
  8. package/dist/accountTrade.js +31 -7
  9. package/dist/d8XMath.d.ts +6 -0
  10. package/dist/d8XMath.js +19 -1
  11. package/dist/liquidatorTool.d.ts +6 -4
  12. package/dist/liquidatorTool.js +13 -7
  13. package/dist/marketData.d.ts +6 -11
  14. package/dist/marketData.js +59 -41
  15. package/dist/nodeSDKTypes.d.ts +33 -2
  16. package/dist/nodeSDKTypes.js +3 -18
  17. package/dist/orderReferrerTool.d.ts +17 -7
  18. package/dist/orderReferrerTool.js +43 -13
  19. package/dist/perpetualDataHandler.d.ts +46 -6
  20. package/dist/perpetualDataHandler.js +143 -16
  21. package/dist/perpetualEventHandler.d.ts +0 -3
  22. package/dist/perpetualEventHandler.js +0 -3
  23. package/dist/priceFeeds.d.ts +115 -0
  24. package/dist/priceFeeds.js +373 -0
  25. package/dist/triangulator.d.ts +27 -0
  26. package/dist/triangulator.js +107 -0
  27. package/dist/version.d.ts +1 -1
  28. package/dist/version.js +1 -1
  29. package/package.json +3 -2
  30. package/src/accountTrade.ts +31 -8
  31. package/src/d8XMath.ts +47 -22
  32. package/src/index.ts +2 -0
  33. package/src/liquidatorTool.ts +28 -8
  34. package/src/marketData.ts +99 -61
  35. package/src/nodeSDKTypes.ts +30 -3
  36. package/src/orderReferrerTool.ts +56 -13
  37. package/src/perpetualDataHandler.ts +149 -21
  38. package/src/perpetualEventHandler.ts +0 -3
  39. package/src/priceFeeds.ts +371 -0
  40. package/src/triangulator.ts +105 -0
  41. package/src/version.ts +1 -1
@@ -7,6 +7,7 @@ import {
7
7
  SELL_SIDE,
8
8
  ZERO_ADDRESS,
9
9
  ZERO_ORDER_ID,
10
+ PriceFeedSubmission,
10
11
  } from "./nodeSDKTypes";
11
12
  import PerpetualDataHandler from "./perpetualDataHandler";
12
13
  import WriteAccessHandler from "./writeAccessHandler";
@@ -46,8 +47,9 @@ export default class OrderReferrerTool extends WriteAccessHandler {
46
47
  * Executes an order by symbol and ID. This action interacts with the blockchain and incurs gas costs.
47
48
  * @param {string} symbol Symbol of the form ETH-USD-MATIC.
48
49
  * @param {string} orderId ID of the order to be executed.
49
- * @param {string=} referrerAddr Address of the wallet to be credited for executing the order,
50
- * if different from the one submitting this transaction.
50
+ * @param {string=} referrerAddr optional address of the wallet to be credited for executing the order, if different from the one submitting this transaction.
51
+ * @param {number=} nonce optional nonce
52
+ * @param {PriceFeedSubmission=} submission optional signed prices obtained via PriceFeeds::fetchLatestFeedPriceInfoForPerpetual
51
53
  * @example
52
54
  * import { OrderReferrerTool, PerpetualDataHandler, Order } from "@d8x/perpetuals-sdk";
53
55
  * async function main() {
@@ -79,7 +81,8 @@ export default class OrderReferrerTool extends WriteAccessHandler {
79
81
  symbol: string,
80
82
  orderId: string,
81
83
  referrerAddr?: string,
82
- nonce?: number
84
+ nonce?: number,
85
+ submission?: PriceFeedSubmission
83
86
  ): Promise<ethers.ContractTransaction> {
84
87
  if (this.proxyContract == null || this.signer == null) {
85
88
  throw Error("no proxy contract or wallet initialized. Use createProxyInstance().");
@@ -88,8 +91,22 @@ export default class OrderReferrerTool extends WriteAccessHandler {
88
91
  if (typeof referrerAddr == "undefined") {
89
92
  referrerAddr = this.traderAddr;
90
93
  }
91
- const options = { gasLimit: this.gasLimit, nonce: nonce };
92
- return await orderBookSC.executeOrder(orderId, referrerAddr, options);
94
+ if (submission == undefined) {
95
+ submission = await this.priceFeedGetter.fetchLatestFeedPriceInfoForPerpetual(symbol);
96
+ }
97
+ const options = {
98
+ gasLimit: this.gasLimit,
99
+ nonce: nonce,
100
+ value: this.PRICE_UPDATE_FEE_GWEI * submission?.priceFeedVaas.length,
101
+ };
102
+ console.log(submission);
103
+ return await orderBookSC.executeOrder(
104
+ orderId,
105
+ referrerAddr,
106
+ submission?.priceFeedVaas,
107
+ submission?.timestamps,
108
+ options
109
+ );
93
110
  }
94
111
 
95
112
  /**
@@ -224,6 +241,8 @@ export default class OrderReferrerTool extends WriteAccessHandler {
224
241
  /**
225
242
  * Check if a conditional order can be executed
226
243
  * @param order order structure
244
+ * @param indexPrices pair of index prices S2 and S3. S3 set to zero if not required. If undefined
245
+ * the function will fetch the latest prices from the REST API
227
246
  * @example
228
247
  * import { OrderReferrerTool, PerpetualDataHandler } from '@d8x/perpetuals-sdk';
229
248
  * async function main() {
@@ -241,26 +260,39 @@ export default class OrderReferrerTool extends WriteAccessHandler {
241
260
  * main();
242
261
  * @returns true if order can be executed for the current state of the perpetuals
243
262
  */
244
- public async isTradeable(order: Order): Promise<boolean> {
263
+ public async isTradeable(order: Order, indexPrices?: [number, number]): Promise<boolean> {
245
264
  if (this.proxyContract == null) {
246
265
  throw Error("no proxy contract initialized. Use createProxyInstance().");
247
266
  }
267
+ if (indexPrices == undefined) {
268
+ let obj = await this.priceFeedGetter.fetchPricesForPerpetual(order.symbol);
269
+ indexPrices = [obj.idxPrices[0], obj.idxPrices[1]];
270
+ }
248
271
  let orderPrice = await PerpetualDataHandler._queryPerpetualPrice(
249
272
  order.symbol,
250
273
  order.quantity,
251
274
  this.symbolToPerpStaticInfo,
252
- this.proxyContract
275
+ this.proxyContract,
276
+ indexPrices
253
277
  );
254
278
  let markPrice = await PerpetualDataHandler._queryPerpetualMarkPrice(
255
279
  order.symbol,
256
280
  this.symbolToPerpStaticInfo,
257
- this.proxyContract
281
+ this.proxyContract,
282
+ indexPrices
258
283
  );
259
284
  let block = await this.provider!.getBlockNumber();
260
285
  return OrderReferrerTool._isTradeable(order, orderPrice, markPrice, block, this.symbolToPerpStaticInfo);
261
286
  }
262
287
 
263
- public async isTradeableBatch(orders: Order[]): Promise<boolean[]> {
288
+ /**
289
+ * Check for a batch of orders on the same perpetual whether they can be traded
290
+ * @param orders orders belonging to 1 perpetual
291
+ * @param indexPrice S2,S3-index prices for the given perpetual. Will fetch prices from REST API
292
+ * if not defined.
293
+ * @returns array of tradeable boolean
294
+ */
295
+ public async isTradeableBatch(orders: Order[], indexPrices?: [number, number, boolean, boolean]): Promise<boolean[]> {
264
296
  if (orders.length == 0) {
265
297
  return [];
266
298
  }
@@ -270,20 +302,31 @@ export default class OrderReferrerTool extends WriteAccessHandler {
270
302
  if (orders.filter((o) => o.symbol == orders[0].symbol).length < orders.length) {
271
303
  throw Error("all orders in a batch must have the same symbol");
272
304
  }
305
+ if (indexPrices == undefined) {
306
+ let obj = await this.priceFeedGetter.fetchPricesForPerpetual(orders[0].symbol);
307
+ indexPrices = [obj.idxPrices[0], obj.idxPrices[1], obj.mktClosed[0], obj.mktClosed[1]];
308
+ }
309
+ if (indexPrices[2] || indexPrices[3]) {
310
+ // market closed
311
+ return orders.map((o) => false);
312
+ }
313
+
273
314
  let orderPrice = await Promise.all(
274
315
  orders.map((o) =>
275
316
  PerpetualDataHandler._queryPerpetualPrice(
276
317
  o.symbol,
277
318
  o.quantity,
278
319
  this.symbolToPerpStaticInfo,
279
- this.proxyContract!
320
+ this.proxyContract!,
321
+ [indexPrices![0], indexPrices![1]]
280
322
  )
281
323
  )
282
324
  );
283
325
  let markPrice = await PerpetualDataHandler._queryPerpetualMarkPrice(
284
326
  orders[0].symbol,
285
327
  this.symbolToPerpStaticInfo,
286
- this.proxyContract
328
+ this.proxyContract,
329
+ [indexPrices![0], indexPrices![1]]
287
330
  );
288
331
  let block = await this.provider!.getBlockNumber();
289
332
  return orders.map((o, idx) =>
@@ -293,7 +336,7 @@ export default class OrderReferrerTool extends WriteAccessHandler {
293
336
 
294
337
  public static _isTradeable(
295
338
  order: Order,
296
- orderPrice: number,
339
+ tradePrice: number,
297
340
  markPrice: number,
298
341
  block: number,
299
342
  symbolToPerpInfoMap: Map<string, PerpetualStaticInfo>
@@ -316,7 +359,7 @@ export default class OrderReferrerTool extends WriteAccessHandler {
316
359
  return false;
317
360
  }
318
361
  let limitPrice = order.limitPrice!;
319
- if ((order.side == BUY_SIDE && orderPrice > limitPrice) || (order.side == SELL_SIDE && orderPrice < limitPrice)) {
362
+ if ((order.side == BUY_SIDE && tradePrice > limitPrice) || (order.side == SELL_SIDE && tradePrice < limitPrice)) {
320
363
  return false;
321
364
  }
322
365
  // do we need to check trigger/stop?
@@ -24,13 +24,12 @@ import {
24
24
  MASK_STOP_ORDER,
25
25
  MarginAccount,
26
26
  PoolStaticInfo,
27
- DEFAULT_CONFIG_MAINNET_NAME,
28
- DEFAULT_CONFIG_MAINNET,
29
- DEFAULT_CONFIG_TESTNET_NAME,
30
- DEFAULT_CONFIG_TESTNET,
31
27
  ONE_64x64,
32
28
  PERP_STATE_STR,
33
29
  PerpetualState,
30
+ DEFAULT_CONFIG,
31
+ DEFAULT_CONFIG_MAINNET_NAME,
32
+ PriceFeedSubmission,
34
33
  } from "./nodeSDKTypes";
35
34
  import {
36
35
  fromBytes4HexString,
@@ -48,12 +47,14 @@ import {
48
47
  calculateLiquidationPriceCollateralBase,
49
48
  calculateLiquidationPriceCollateralQuote,
50
49
  } from "./d8XMath";
50
+ import PriceFeeds from "./priceFeeds";
51
51
 
52
52
  /**
53
53
  * Parent class for MarketData and WriteAccessHandler that handles
54
54
  * common data and chain operations.
55
55
  */
56
56
  export default class PerpetualDataHandler {
57
+ PRICE_UPDATE_FEE_GWEI = 1;
57
58
  //map symbol of the form ETH-USD-MATIC into perpetual ID and other static info
58
59
  //this is initialized in the createProxyInstance function
59
60
  protected symbolToPerpStaticInfo: Map<string, PerpetualStaticInfo>;
@@ -76,6 +77,7 @@ export default class PerpetualDataHandler {
76
77
  protected provider: ethers.providers.JsonRpcProvider | null = null;
77
78
 
78
79
  private signerOrProvider: ethers.Signer | ethers.providers.Provider | null = null;
80
+ protected priceFeedGetter : PriceFeeds;
79
81
 
80
82
  // pools are numbered consecutively starting at 1
81
83
  // nestedPerpetualIDs contains an array for each pool
@@ -94,6 +96,7 @@ export default class PerpetualDataHandler {
94
96
  this.lobFactoryABI = require(config.limitOrderBookFactoryABILocation);
95
97
  this.lobABI = require(config.limitOrderBookABILocation);
96
98
  this.symbolList = new Map<string, string>(Object.entries(require(config.symbolListLocation)));
99
+ this.priceFeedGetter = new PriceFeeds(this, config.priceFeedConfigNetwork);
97
100
  }
98
101
 
99
102
  protected async initContractsAndData(signerOrProvider: ethers.Signer | ethers.providers.Provider) {
@@ -127,6 +130,7 @@ export default class PerpetualDataHandler {
127
130
  throw Error("proxy or limit order book not defined");
128
131
  }
129
132
  this.nestedPerpetualIDs = await PerpetualDataHandler.getNestedPerpetualIds(proxyContract);
133
+ let requiredPairs = new Set<string>();
130
134
  for (let j = 0; j < this.nestedPerpetualIDs.length; j++) {
131
135
  let pool = await proxyContract.getLiquidityPool(j + 1);
132
136
  let poolMarginTokenAddr = pool.marginTokenAddress;
@@ -146,8 +150,16 @@ export default class PerpetualDataHandler {
146
150
  let quote = contractSymbolToSymbol(perp.S2QuoteCCY, this.symbolList);
147
151
  let base3 = contractSymbolToSymbol(perp.S3BaseCCY, this.symbolList);
148
152
  let quote3 = contractSymbolToSymbol(perp.S3QuoteCCY, this.symbolList);
149
- currentSymbols.push(base + "-" + quote);
150
- currentSymbolsS3.push(base3 + "-" + quote3);
153
+ let sym = base + "-" + quote;
154
+ let sym3 = base3 + "-" + quote3;
155
+ requiredPairs.add(sym);
156
+ if (sym3!="-") {
157
+ requiredPairs.add(sym3);
158
+ } else {
159
+ sym3 = "";
160
+ }
161
+ currentSymbols.push(sym);
162
+ currentSymbolsS3.push(sym3);
151
163
  initRate.push(ABK64x64ToFloat(perp.fInitialMarginRate));
152
164
  mgnRate.push(ABK64x64ToFloat(perp.fMaintenanceMarginRate));
153
165
  lotSizes.push(ABK64x64ToFloat(perp.fLotSizeBC));
@@ -186,6 +198,10 @@ export default class PerpetualDataHandler {
186
198
  let currentSymbols3 = currentSymbols.map((x) => x + "-" + poolCCY);
187
199
  // push into map
188
200
  for (let k = 0; k < perpetualIDs.length; k++) {
201
+ // add price IDs
202
+ let idsB32, isPyth;
203
+ [idsB32, isPyth] = await proxyContract.getPriceInfo(perpetualIDs[k]);
204
+
189
205
  this.symbolToPerpStaticInfo.set(currentSymbols3[k], {
190
206
  id: perpetualIDs[k],
191
207
  limitOrderBookAddr: currentLimitOrderBookAddr[k],
@@ -195,11 +211,16 @@ export default class PerpetualDataHandler {
195
211
  S2Symbol: currentSymbols[k],
196
212
  S3Symbol: currentSymbolsS3[k],
197
213
  lotSizeBC: lotSizes[k],
214
+ priceIds: idsB32,
198
215
  });
199
216
  }
200
217
  // push margin token address into map
201
218
  this.symbolToTokenAddrMap.set(poolCCY!, poolMarginTokenAddr);
202
219
  }
220
+ // pre-calculate all triangulation paths so we can easily get from
221
+ // the prices of price-feeds to the index price required, e.g.
222
+ // BTC-USDC : BTC-USD / USDC-USD
223
+ this.priceFeedGetter.initializeTriangulations(requiredPairs);
203
224
  }
204
225
 
205
226
  /**
@@ -241,6 +262,55 @@ export default class PerpetualDataHandler {
241
262
  return symbol4BToLongSymbol(sym, this.symbolList);
242
263
  }
243
264
 
265
+ /**
266
+ * Get PriceFeedSubmission data required for blockchain queries that involve price data, and the corresponding
267
+ * triangulated prices for the indices S2 and S3
268
+ * @param symbol pool symbol of the form "ETH-USD-MATIC"
269
+ * @returns PriceFeedSubmission and prices for S2 and S3. [S2price, 0] if S3 not defined.
270
+ */
271
+ public async fetchPriceSubmissionInfoForPerpetual(symbol: string) : Promise<{submission : PriceFeedSubmission, pxS2S3 : [number,number]}> {
272
+ // fetch prices from required price-feeds (REST)
273
+ return await this.priceFeedGetter.fetchFeedPriceInfoAndIndicesForPerpetual(symbol);
274
+ }
275
+
276
+ /**
277
+ * Get the symbols required as indices for the given perpetual
278
+ * @param symbol of the form ETH-USD-MATIC, specifying the perpetual
279
+ * @returns name of underlying index prices, e.g. ["MATIC-USD", ""]
280
+ */
281
+ public getIndexSymbols(symbol: string) : [string, string] {
282
+ // get index
283
+ let staticInfo = this.symbolToPerpStaticInfo.get(symbol);
284
+ if (staticInfo==undefined) {
285
+ throw new Error(`No static info for perpetual with symbol ${symbol}`);
286
+ }
287
+ return [staticInfo.S2Symbol,staticInfo.S3Symbol];
288
+ }
289
+
290
+ /**
291
+ * Get the latest prices for a given perpetual from the offchain oracle
292
+ * networks
293
+ * @param symbol perpetual symbol of the form BTC-USD-MATIC
294
+ * @returns array of price feed updates that can be submitted to the smart contract
295
+ * and corresponding price information
296
+ */
297
+ public async fetchLatestFeedPriceInfo(symbol: string) : Promise<PriceFeedSubmission> {
298
+ return await this.priceFeedGetter.fetchLatestFeedPriceInfoForPerpetual(symbol);
299
+ }
300
+
301
+ /**
302
+ * Get list of required pyth price source IDs for given perpetual
303
+ * @param symbol perpetual symbol, e.g., BTC-USD-MATIC
304
+ * @returns list of required pyth price sources for this perpetual
305
+ */
306
+ public getPriceIds(symbol: string): string[] {
307
+ let perpInfo = this.symbolToPerpStaticInfo.get(symbol);
308
+ if (perpInfo == undefined) {
309
+ throw Error(`Perpetual with symbol ${symbol} not found. Check symbol or use createProxyInstance().`);
310
+ }
311
+ return perpInfo.priceIds;
312
+ }
313
+
244
314
  protected static _getSymbolFromPoolId(poolId: number, staticInfos: PoolStaticInfo[]): string {
245
315
  let idx = poolId - 1;
246
316
  return staticInfos[idx].poolMarginSymbol;
@@ -334,31 +404,37 @@ export default class PerpetualDataHandler {
334
404
  symbol: string,
335
405
  tradeAmount: number,
336
406
  symbolToPerpStaticInfo: Map<string, PerpetualStaticInfo>,
337
- _proxyContract: ethers.Contract
407
+ _proxyContract: ethers.Contract,
408
+ indexPrices: [number, number]
338
409
  ): Promise<number> {
339
410
  let perpId = PerpetualDataHandler.symbolToPerpetualId(symbol, symbolToPerpStaticInfo);
340
- let fPrice = await _proxyContract.queryPerpetualPrice(perpId, floatToABK64x64(tradeAmount));
411
+ let fIndexPrices = indexPrices.map(x=>floatToABK64x64(x));
412
+ let fPrice = await _proxyContract.queryPerpetualPrice(perpId, floatToABK64x64(tradeAmount), fIndexPrices);
341
413
  return ABK64x64ToFloat(fPrice);
342
414
  }
343
415
 
344
416
  protected static async _queryPerpetualMarkPrice(
345
417
  symbol: string,
346
418
  symbolToPerpStaticInfo: Map<string, PerpetualStaticInfo>,
347
- _proxyContract: ethers.Contract
419
+ _proxyContract: ethers.Contract,
420
+ indexPrices: [number, number]
348
421
  ): Promise<number> {
349
422
  let perpId = PerpetualDataHandler.symbolToPerpetualId(symbol, symbolToPerpStaticInfo);
350
- let ammState = await _proxyContract.getAMMState(perpId);
423
+ let [S2, S3] = indexPrices.map(x=>floatToABK64x64(x));
424
+ let ammState = await _proxyContract.getAMMState(perpId, [S2, S3]);
351
425
  return ABK64x64ToFloat(ammState[6].mul(ONE_64x64.add(ammState[8])).div(ONE_64x64));
352
426
  }
353
427
 
354
428
  protected static async _queryPerpetualState(
355
429
  symbol: string,
356
430
  symbolToPerpStaticInfo: Map<string, PerpetualStaticInfo>,
357
- _proxyContract: ethers.Contract
431
+ _proxyContract: ethers.Contract,
432
+ indexPrices: [number, number, boolean, boolean]
358
433
  ): Promise<PerpetualState> {
359
434
  let perpId = PerpetualDataHandler.symbolToPerpetualId(symbol, symbolToPerpStaticInfo);
360
435
  let ccy = symbol.split("-");
361
- let ammState = await _proxyContract.getAMMState(perpId);
436
+ let [S2, S3] = [indexPrices[0], indexPrices[1]];
437
+ let ammState = await _proxyContract.getAMMState(perpId, [S2, S3].map(x=>floatToABK64x64(x)));
362
438
  let markPrice = ABK64x64ToFloat(ammState[6].mul(ONE_64x64.add(ammState[8])).div(ONE_64x64));
363
439
  let state = {
364
440
  id: perpId,
@@ -372,6 +448,7 @@ export default class PerpetualDataHandler {
372
448
  currentFundingRateBps: ABK64x64ToFloat(ammState[14]) * 1e4,
373
449
  openInterestBC: ABK64x64ToFloat(ammState[11]),
374
450
  maxPositionBC: ABK64x64ToFloat(ammState[12]),
451
+ isMarketClosed: indexPrices[2] || indexPrices[3]
375
452
  };
376
453
  if (symbolToPerpStaticInfo.get(symbol)?.collateralCurrencyType == CollaterlCCY.BASE) {
377
454
  state.collToQuoteIndexPrice = state.indexPrice;
@@ -677,17 +754,35 @@ export default class PerpetualDataHandler {
677
754
 
678
755
  /**
679
756
  * Read config file into NodeSDKConfig interface
680
- * @param fileLocation json-file with required variables for config
757
+ * @param configNameOrfileLocation json-file with required variables for config, or name of a default known config
681
758
  * @returns NodeSDKConfig
682
759
  */
683
- public static readSDKConfig(fileLocation: string): NodeSDKConfig {
684
- if (fileLocation == DEFAULT_CONFIG_MAINNET_NAME) {
685
- fileLocation = DEFAULT_CONFIG_MAINNET;
686
- } else if (fileLocation == DEFAULT_CONFIG_TESTNET_NAME) {
687
- fileLocation = DEFAULT_CONFIG_TESTNET;
688
- }
689
- let configFile = require(fileLocation);
690
- let config: NodeSDKConfig = <NodeSDKConfig>configFile;
760
+ public static readSDKConfig(configNameOrFileLocation: string, version?: string): NodeSDKConfig {
761
+ let config;
762
+ if (/\.json$/.test(configNameOrFileLocation)) {
763
+ // file path
764
+ let configFile = require(configNameOrFileLocation);
765
+ config = <NodeSDKConfig>configFile;
766
+ } else {
767
+ // name
768
+ let configFile = require(DEFAULT_CONFIG);
769
+ configFile = configFile.filter((c: any) => c.name == configNameOrFileLocation);
770
+ if (configFile.length == 0) {
771
+ throw Error(`Config name ${configNameOrFileLocation} not found.`);
772
+ } else if (configFile.length > 1) {
773
+ // TODO: pick one with highest version, unless version is given
774
+ throw Error(`Config name ${configNameOrFileLocation} not unique.`);
775
+ }
776
+ for (let configItem of configFile) {
777
+ if (configItem.name == configNameOrFileLocation) {
778
+ config = <NodeSDKConfig>configItem;
779
+ break;
780
+ }
781
+ }
782
+ }
783
+ if (config == undefined) {
784
+ throw Error(`Config file ${configNameOrFileLocation} not found.`);
785
+ }
691
786
  return config;
692
787
  }
693
788
 
@@ -701,4 +796,37 @@ export default class PerpetualDataHandler {
701
796
  const FormatTypes = ethers.utils.FormatTypes;
702
797
  return contract.interface.getFunction(functionName).format(FormatTypes.full);
703
798
  }
799
+
800
+ /**
801
+ * Gets the pool index (in exchangeInfo) corresponding to a given symbol.
802
+ * @param symbol Symbol of the form ETH-USD-MATIC
803
+ * @returns Pool index
804
+ */
805
+ public getPoolIndexFromSymbol(symbol: string): number {
806
+ let pools = this.poolStaticInfos!;
807
+ let poolId = PerpetualDataHandler._getPoolIdFromSymbol(symbol, this.poolStaticInfos);
808
+ let k = 0;
809
+ while (k < pools.length) {
810
+ if (pools[k].poolId == poolId) {
811
+ // pool found
812
+ return k;
813
+ }
814
+ k++;
815
+ }
816
+ return -1;
817
+ }
818
+
819
+ public getMarginTokenFromSymbol(symbol: string): string | undefined {
820
+ let pools = this.poolStaticInfos!;
821
+ let poolId = PerpetualDataHandler._getPoolIdFromSymbol(symbol, this.poolStaticInfos);
822
+ let k = 0;
823
+ while (k < pools.length) {
824
+ if (pools[k].poolId == poolId) {
825
+ // pool found
826
+ return pools[k].poolMarginTokenAddr;
827
+ }
828
+ k++;
829
+ }
830
+ return undefined;
831
+ }
704
832
  }
@@ -20,9 +20,6 @@ import {
20
20
  * ordersInPerpetual: Map<number, OrderStruct> all open orders for the given trader
21
21
  * positionInPerpetual: Map<number, MarginAccount> all open positions for the given trader
22
22
  *
23
- * TODO:
24
- * - update functions for midprice & index & collateral prices without event
25
- * - testing
26
23
  *
27
24
  * Get data:
28
25
  * - getPerpetualData(perp id (string) or symbol) : PerpetualState. This is a reference!