@d8x/perpetuals-sdk 0.0.45 → 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 (40) 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 +1 -1
  30. package/src/accountTrade.ts +31 -8
  31. package/src/d8XMath.ts +18 -0
  32. package/src/liquidatorTool.ts +28 -8
  33. package/src/marketData.ts +66 -41
  34. package/src/nodeSDKTypes.ts +30 -3
  35. package/src/orderReferrerTool.ts +56 -13
  36. package/src/perpetualDataHandler.ts +149 -21
  37. package/src/perpetualEventHandler.ts +0 -3
  38. package/src/priceFeeds.ts +371 -0
  39. package/src/triangulator.ts +105 -0
  40. package/src/version.ts +1 -1
@@ -1,5 +1,5 @@
1
1
  import WriteAccessHandler from "./writeAccessHandler";
2
- import { NodeSDKConfig } from "./nodeSDKTypes";
2
+ import { NodeSDKConfig, PriceFeedSubmission } from "./nodeSDKTypes";
3
3
  import { ethers } from "ethers";
4
4
 
5
5
  /**
@@ -37,6 +37,7 @@ export default class LiquidatorTool extends WriteAccessHandler {
37
37
  * @param {string} symbol Symbol of the form ETH-USD-MATIC.
38
38
  * @param {string} traderAddr Address of the trader to be liquidated.
39
39
  * @param {string=} liquidatorAddr Address to be credited if the liquidation succeeds.
40
+ * @param {PriceFeedSubmission} priceFeedData optional. VAA and timestamps for oracle. If not provided will query from REST API.
40
41
  * Defaults to the wallet used to execute the liquidation.
41
42
  * @example
42
43
  * import { LiquidatorTool, PerpetualDataHandler } from '@d8x/perpetuals-sdk';
@@ -59,7 +60,8 @@ export default class LiquidatorTool extends WriteAccessHandler {
59
60
  public async liquidateTrader(
60
61
  symbol: string,
61
62
  traderAddr: string,
62
- liquidatorAddr: string = ""
63
+ liquidatorAddr: string = "",
64
+ priceFeedData?: PriceFeedSubmission
63
65
  ): Promise<ethers.ContractTransaction> {
64
66
  // this operation spends gas, so signer is required
65
67
  if (this.proxyContract == null || this.signer == null) {
@@ -70,7 +72,13 @@ export default class LiquidatorTool extends WriteAccessHandler {
70
72
  liquidatorAddr = this.traderAddr;
71
73
  }
72
74
  let perpID = LiquidatorTool.symbolToPerpetualId(symbol, this.symbolToPerpStaticInfo);
73
- return await this._liquidateByAMM(perpID, liquidatorAddr, traderAddr, this.gasLimit);
75
+ if (priceFeedData == undefined) {
76
+ priceFeedData = await this.fetchLatestFeedPriceInfo(symbol);
77
+ }
78
+ return await this._liquidateByAMM(perpID, liquidatorAddr, traderAddr, priceFeedData, {
79
+ gasLimit: this.gasLimit,
80
+ value: this.PRICE_UPDATE_FEE_GWEI * priceFeedData.priceFeedVaas.length,
81
+ });
74
82
  }
75
83
 
76
84
  /**
@@ -116,13 +124,25 @@ export default class LiquidatorTool extends WriteAccessHandler {
116
124
  * @param perpetualId Perpetual id.
117
125
  * @param liquidatorAddr Address to be credited for the liquidation.
118
126
  * @param traderAddr Address of the trader to be liquidated.
119
- * @param gasLimit Gas limit.
127
+ * @param priceFeedData contains VAA and timestamps required
128
+ * @param options E.g., Gas limit, fee.
120
129
  * @ignore
121
130
  */
122
- public async _liquidateByAMM(perpetualId: number, liquidatorAddr: string, traderAddr: string, gasLimit: number) {
123
- return await this.proxyContract!.liquidateByAMM(perpetualId, liquidatorAddr, traderAddr, {
124
- gasLimit: gasLimit,
125
- });
131
+ public async _liquidateByAMM(
132
+ perpetualId: number,
133
+ liquidatorAddr: string,
134
+ traderAddr: string,
135
+ priceFeedData: PriceFeedSubmission,
136
+ options: object
137
+ ) {
138
+ return await this.proxyContract!.liquidateByAMM(
139
+ perpetualId,
140
+ liquidatorAddr,
141
+ traderAddr,
142
+ priceFeedData.priceFeedVaas,
143
+ priceFeedData.timestamps,
144
+ options
145
+ );
126
146
  }
127
147
 
128
148
  /**
package/src/marketData.ts CHANGED
@@ -32,6 +32,7 @@ import {
32
32
  ZERO_ADDRESS,
33
33
  } from "./nodeSDKTypes";
34
34
  import PerpetualDataHandler from "./perpetualDataHandler";
35
+ import PriceFeeds from "./priceFeeds";
35
36
  import { contractSymbolToSymbol, toBytes4 } from "./utils";
36
37
 
37
38
  /**
@@ -145,7 +146,7 @@ export default class MarketData extends PerpetualDataHandler {
145
146
  if (this.proxyContract == null) {
146
147
  throw Error("no proxy contract initialized. Use createProxyInstance().");
147
148
  }
148
- return await MarketData._exchangeInfo(this.proxyContract, this.poolStaticInfos, this.symbolList);
149
+ return await MarketData._exchangeInfo(this.proxyContract, this.poolStaticInfos, this.symbolToPerpStaticInfo, this.symbolList, this.priceFeedGetter);
149
150
  }
150
151
 
151
152
  /**
@@ -431,25 +432,6 @@ export default class MarketData extends PerpetualDataHandler {
431
432
  return newPositionRisk;
432
433
  }
433
434
 
434
- /**
435
- * Gets the pool index (in exchangeInfo) corresponding to a given symbol.
436
- * @param symbol Symbol of the form ETH-USD-MATIC
437
- * @returns Pool index
438
- */
439
- public getPoolIndexFromSymbol(symbol: string): number {
440
- let pools = this.poolStaticInfos!;
441
- let poolId = this.getPoolIdFromSymbol(symbol);
442
- let k = 0;
443
- while (k < pools.length) {
444
- if (pools[k].poolId == poolId) {
445
- // pool found
446
- return k;
447
- }
448
- k++;
449
- }
450
- return -1;
451
- }
452
-
453
435
  /**
454
436
  * Gets the wallet balance in the collateral currency corresponding to a given perpetual symbol.
455
437
  * @param address Address to check
@@ -563,11 +545,15 @@ export default class MarketData extends PerpetualDataHandler {
563
545
  *
564
546
  * @returns mark price
565
547
  */
566
- public async getMarkPrice(symbol: string): Promise<number> {
548
+ public async getMarkPrice(symbol: string, indexPrices?: [number, number]): Promise<number> {
567
549
  if (this.proxyContract == null) {
568
550
  throw Error("no proxy contract initialized. Use createProxyInstance().");
569
551
  }
570
- return await PerpetualDataHandler._queryPerpetualMarkPrice(symbol, this.symbolToPerpStaticInfo, this.proxyContract);
552
+ if(indexPrices==undefined) {
553
+ let obj = await this.priceFeedGetter.fetchPricesForPerpetual(symbol);
554
+ indexPrices = [obj.idxPrices[0], obj.idxPrices[1]];
555
+ }
556
+ return await PerpetualDataHandler._queryPerpetualMarkPrice(symbol, this.symbolToPerpStaticInfo, this.proxyContract, indexPrices);
571
557
  }
572
558
 
573
559
  /**
@@ -590,31 +576,43 @@ export default class MarketData extends PerpetualDataHandler {
590
576
  *
591
577
  * @returns price (number)
592
578
  */
593
- public async getPerpetualPrice(symbol: string, quantity: number): Promise<number> {
579
+ public async getPerpetualPrice(symbol: string, quantity: number, indexPrices?:[number, number]): Promise<number> {
594
580
  if (this.proxyContract == null) {
595
581
  throw Error("no proxy contract initialized. Use createProxyInstance().");
596
582
  }
583
+ if (indexPrices==undefined) {
584
+ // fetch from API
585
+ let obj = await this.priceFeedGetter.fetchPricesForPerpetual(symbol);
586
+ indexPrices = [obj.idxPrices[0], obj.idxPrices[1]];
587
+ }
597
588
  return await PerpetualDataHandler._queryPerpetualPrice(
598
589
  symbol,
599
590
  quantity,
600
591
  this.symbolToPerpStaticInfo,
601
- this.proxyContract
592
+ this.proxyContract,
593
+ indexPrices
602
594
  );
603
595
  }
604
596
 
605
597
  /**
606
598
  * Query recent perpetual state from blockchain
607
599
  * @param symbol symbol of the form ETH-USD-MATIC
600
+ * @param indexPrices S2 and S3 prices/isMarketOpen if not provided fetch via REST API
608
601
  * @returns PerpetualState reference
609
602
  */
610
- public async getPerpetualState(symbol: string): Promise<PerpetualState> {
603
+ public async getPerpetualState(symbol: string, indexPriceInfo?: [number, number, boolean, boolean]): Promise<PerpetualState> {
611
604
  if (this.proxyContract == null) {
612
605
  throw Error("no proxy contract initialized. Use createProxyInstance().");
613
606
  }
607
+ if (indexPriceInfo==undefined) {
608
+ let obj = await this.priceFeedGetter.fetchPricesForPerpetual(symbol);
609
+ indexPriceInfo = [obj.idxPrices[0], obj.idxPrices[1], obj.mktClosed[0], obj.mktClosed[1]];
610
+ }
614
611
  let state: PerpetualState = await PerpetualDataHandler._queryPerpetualState(
615
612
  symbol,
616
613
  this.symbolToPerpStaticInfo,
617
- this.proxyContract
614
+ this.proxyContract,
615
+ indexPriceInfo
618
616
  );
619
617
  return state;
620
618
  }
@@ -640,6 +638,7 @@ export default class MarketData extends PerpetualDataHandler {
640
638
  S2Symbol: perpInfo.S2Symbol,
641
639
  S3Symbol: perpInfo.S3Symbol,
642
640
  lotSizeBC: perpInfo.lotSizeBC,
641
+ priceIds: perpInfo.priceIds,
643
642
  };
644
643
  return res;
645
644
  }
@@ -726,24 +725,30 @@ export default class MarketData extends PerpetualDataHandler {
726
725
  return balanceCC - initalMarginCC;
727
726
  }
728
727
 
729
- public async getPythIds(symbol: string): Promise<string[]> {
730
- if (this.proxyContract == null) {
731
- throw Error("no proxy contract initialized. Use createProxyInstance().");
732
- }
733
- let perpId = this.getPerpIdFromSymbol(symbol);
734
- let idsB32 = await this.proxyContract.getPythIds(perpId);
735
- return idsB32.apply((id: string) => ethers.utils.parseBytes32String(id));
736
- }
737
-
738
728
  public static async _exchangeInfo(
739
729
  _proxyContract: ethers.Contract,
740
730
  _poolStaticInfos: Array<PoolStaticInfo>,
741
- _symbolList: Map<string, string>
731
+ _symbolToPerpStaticInfo : Map<string, PerpetualStaticInfo>,
732
+ _symbolList: Map<string, string>,
733
+ _priceFeedGetter: PriceFeeds
742
734
  ): Promise<ExchangeInfo> {
743
735
  let nestedPerpetualIDs = await PerpetualDataHandler.getNestedPerpetualIds(_proxyContract);
744
736
  let factory = await _proxyContract.getOracleFactory();
745
737
  let info: ExchangeInfo = { pools: [], oracleFactoryAddr: factory, proxyAddr: _proxyContract.address };
746
738
  const numPools = nestedPerpetualIDs.length;
739
+
740
+ // get all prices
741
+ let allSym = new Set<string>();
742
+ for(let perpSymbol of _symbolToPerpStaticInfo.keys()) {
743
+ let sInfo : PerpetualStaticInfo | undefined = _symbolToPerpStaticInfo.get(perpSymbol);
744
+ allSym.add(sInfo!.S2Symbol);
745
+ if(sInfo!.S3Symbol!='') {
746
+ allSym.add(sInfo!.S3Symbol);
747
+ }
748
+ }
749
+ let allSymArr = Array.from(allSym.values());
750
+ let idxPriceMap : Map<string, [number,boolean]> = await _priceFeedGetter.fetchPrices(allSymArr);
751
+
747
752
  for (var j = 0; j < numPools; j++) {
748
753
  let perpetualIDs = nestedPerpetualIDs[j];
749
754
  let pool = await _proxyContract.getLiquidityPool(j + 1);
@@ -759,17 +764,36 @@ export default class MarketData extends PerpetualDataHandler {
759
764
  brokerCollateralLotSize: ABK64x64ToFloat(pool.fBrokerCollateralLotSize),
760
765
  perpetuals: [],
761
766
  };
767
+ let poolSymbol = PoolState.poolSymbol;
762
768
  for (var k = 0; k < perpetualIDs.length; k++) {
763
769
  let perp = await _proxyContract.getPerpetual(perpetualIDs[k]);
764
- let fIndexS2 = await _proxyContract.getOraclePrice([perp.S2BaseCCY, perp.S2QuoteCCY]);
765
- let fMidPrice = await _proxyContract.queryPerpetualPrice(perpetualIDs[k], BigNumber.from(0));
766
- let indexS2 = ABK64x64ToFloat(fIndexS2);
770
+ let symS2 = contractSymbolToSymbol(perp.S2BaseCCY, _symbolList)+"-"+contractSymbolToSymbol(perp.S2QuoteCCY, _symbolList);
771
+ let symS3 = contractSymbolToSymbol(perp.S3BaseCCY, _symbolList)+"-"+contractSymbolToSymbol(perp.S3QuoteCCY, _symbolList);
772
+ let perpSymbol = symS2+"-"+poolSymbol;
773
+ //console.log("perpsymbol=",perpSymbol);
774
+ let res = idxPriceMap.get(symS2)
775
+ if (res==undefined) {
776
+ throw new Error(`Price for index ${symS2} could not be fetched - config issue`);
777
+ }
778
+ let [indexS2, isS2MktClosed] : [number, boolean] = [res[0], res[1]];
767
779
  let indexS3 = 1;
780
+ let isS3MktClosed = false;
768
781
  if (perp.eCollateralCurrency == COLLATERAL_CURRENCY_BASE) {
769
782
  indexS3 = indexS2;
770
783
  } else if (perp.eCollateralCurrency == COLLATERAL_CURRENCY_QUANTO) {
771
- indexS3 = ABK64x64ToFloat(await _proxyContract.getOraclePrice([perp.S3BaseCCY, perp.S3QuoteCCY]));
784
+ res = idxPriceMap.get(symS3);
785
+ if (res==undefined) {
786
+ throw new Error(`Price for index ${symS3} could not be fetched - config issue`);
787
+ } else {
788
+ indexS3 = res[0];
789
+ isS3MktClosed = res[1];
790
+ }
772
791
  }
792
+ let fMidPrice = await _proxyContract.queryPerpetualPrice(perpetualIDs[k], BigNumber.from(0), [
793
+ floatToABK64x64(indexS2),
794
+ floatToABK64x64(indexS3)
795
+ ]);
796
+
773
797
  let markPremiumRate = ABK64x64ToFloat(perp.currentMarkPremiumRate.fPrice);
774
798
  let currentFundingRateBps = 1e4 * ABK64x64ToFloat(perp.fCurrentFundingRate);
775
799
  let state = PERP_STATE_STR[perp.state];
@@ -784,7 +808,8 @@ export default class MarketData extends PerpetualDataHandler {
784
808
  midPrice: ABK64x64ToFloat(fMidPrice),
785
809
  currentFundingRateBps: currentFundingRateBps,
786
810
  openInterestBC: ABK64x64ToFloat(perp.fOpenInterest),
787
- maxPositionBC: ABK64x64ToFloat(perp.fMaxPositionBC),
811
+ maxPositionBC: Infinity,
812
+ isMarketClosed: isS2MktClosed || isS3MktClosed
788
813
  };
789
814
  PoolState.perpetuals.push(PerpetualState);
790
815
  }
@@ -1,10 +1,11 @@
1
1
  import { BigNumber, BigNumberish, BytesLike, constants, ContractTransaction } from "ethers";
2
- export const DEFAULT_CONFIG_TESTNET = "../config/defaultConfig.json";
3
- export const DEFAULT_CONFIG_MAINNET = "notthereyet";
2
+ import { NumberLiteralType } from "typescript";
3
+ export const DEFAULT_CONFIG = "../config/defaultConfig.json";
4
4
  export const DEFAULT_CONFIG_TESTNET_NAME = "testnet";
5
5
  export const DEFAULT_CONFIG_MAINNET_NAME = "mainnet";
6
6
 
7
7
  export const ERC20_ABI = require("../abi/ERC20.json");
8
+ export const MOCK_TOKEN_SWAP_ABI = require("../abi/MockTokenSwap.json");
8
9
  export const COLLATERAL_CURRENCY_QUOTE = 0;
9
10
  export const COLLATERAL_CURRENCY_BASE = 1;
10
11
  export const COLLATERAL_CURRENCY_QUANTO = 2;
@@ -33,6 +34,8 @@ export const BUY_SIDE = "BUY";
33
34
  export const SELL_SIDE = "SELL";
34
35
  export const CLOSED_SIDE = "CLOSED";
35
36
  export interface NodeSDKConfig {
37
+ name: string | undefined;
38
+ version: number;
36
39
  nodeURL: string;
37
40
  proxyAddr: string;
38
41
  proxyABILocation: string;
@@ -40,6 +43,7 @@ export interface NodeSDKConfig {
40
43
  limitOrderBookABILocation: string;
41
44
  limitOrderBookFactoryABILocation: string;
42
45
  symbolListLocation: string;
46
+ priceFeedConfigNetwork: string;
43
47
  gasLimit?: number | undefined;
44
48
  }
45
49
 
@@ -81,6 +85,7 @@ export interface PerpetualStaticInfo {
81
85
  S2Symbol: string;
82
86
  S3Symbol: string;
83
87
  lotSizeBC: number;
88
+ priceIds: string[];
84
89
  }
85
90
 
86
91
  /**
@@ -135,6 +140,7 @@ export interface PerpetualState {
135
140
  currentFundingRateBps: number;
136
141
  openInterestBC: number;
137
142
  maxPositionBC: number;
143
+ isMarketClosed: boolean;
138
144
  }
139
145
 
140
146
  export interface OrderResponse {
@@ -148,7 +154,7 @@ export interface OrderStruct {
148
154
  }
149
155
 
150
156
  export interface Order {
151
- symbol: string;
157
+ symbol: string;//symbol of the form ETH-USD-MATIC
152
158
  side: string;
153
159
  type: string;
154
160
  quantity: number;
@@ -204,3 +210,24 @@ export interface SmartContractOrder {
204
210
  uint256 iDeadline;
205
211
  uint256 createdTimestamp;
206
212
  */
213
+
214
+ export interface PriceFeedConfig {
215
+ network: string;
216
+ ids: Array<{ symbol: string; id: string; type: string; origin: string }>;
217
+ endpoints: Array<{ type: string; endpoint: string }>;
218
+ }
219
+
220
+ export interface PriceFeedSubmission {
221
+ symbols: string[];
222
+ priceFeedVaas: string[];
223
+ prices: number[];
224
+ isMarketClosed: boolean[];
225
+ timestamps: number[];
226
+ }
227
+
228
+ export interface PriceFeedFormat {
229
+ conf: BigNumber;
230
+ expo: number;
231
+ price: BigNumber;
232
+ publish_time: number;
233
+ }
@@ -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?