@d8x/perpetuals-sdk 0.0.1 → 0.0.3

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/src/marketData.ts CHANGED
@@ -1,17 +1,17 @@
1
1
  import {
2
- ExchangeInfo,
3
- NodeSDKConfig,
4
- MarginAccount,
5
- PoolState,
6
- PerpetualState,
7
- COLLATERAL_CURRENCY_BASE,
8
- COLLATERAL_CURRENCY_QUANTO,
9
- PERP_STATE_STR,
10
- ZERO_ADDRESS,
2
+ ExchangeInfo,
3
+ NodeSDKConfig,
4
+ MarginAccount,
5
+ PoolState,
6
+ PerpetualState,
7
+ COLLATERAL_CURRENCY_BASE,
8
+ COLLATERAL_CURRENCY_QUANTO,
9
+ PERP_STATE_STR,
10
+ ZERO_ADDRESS,
11
11
  } from "./nodeSDKTypes";
12
12
  import { BigNumber, BytesLike, ethers } from "ethers";
13
13
  import { floatToABK64x64, ABK64x64ToFloat } from "./d8XMath";
14
- import { fromBytes4HexString } from "./utils";
14
+ import { fromBytes4HexString, toBytes4 } from "./utils";
15
15
  import PerpetualDataHandler from "./perpetualDataHandler";
16
16
  import { SmartContractOrder, Order } from "./nodeSDKTypes";
17
17
 
@@ -20,130 +20,161 @@ import { SmartContractOrder, Order } from "./nodeSDKTypes";
20
20
  * No gas required for the queries here.
21
21
  */
22
22
  export default class MarketData extends PerpetualDataHandler {
23
- public constructor(config: NodeSDKConfig) {
24
- super(config);
25
- }
23
+ public constructor(config: NodeSDKConfig) {
24
+ super(config);
25
+ }
26
26
 
27
- public async createProxyInstance() {
28
- this.provider = new ethers.providers.JsonRpcProvider(this.nodeURL);
29
- await this.initContractsAndData(this.provider);
30
- }
27
+ public async createProxyInstance() {
28
+ this.provider = new ethers.providers.JsonRpcProvider(this.nodeURL);
29
+ await this.initContractsAndData(this.provider);
30
+ }
31
31
 
32
- public async exchangeInfo(): Promise<ExchangeInfo> {
33
- if (this.proxyContract == null) {
34
- throw Error("no proxy contract initialized. Use createProxyInstance().");
35
- }
36
- return await MarketData._exchangeInfo(this.proxyContract);
32
+ /**
33
+ * Information about the products traded in the exchange.
34
+ * @returns {ExchangeInfo} Array of static data for all the pools and perpetuals in the system.
35
+ */
36
+ public async exchangeInfo(): Promise<ExchangeInfo> {
37
+ if (this.proxyContract == null) {
38
+ throw Error("no proxy contract initialized. Use createProxyInstance().");
37
39
  }
40
+ return await MarketData._exchangeInfo(this.proxyContract);
41
+ }
42
+
43
+ /**
44
+ * All open orders for a trader-address and a symbol.
45
+ * @param {string} traderAddr Address of the trader for which we get the open orders.
46
+ * @param {string} symbol Symbol of the form ETH-USD-MATIC.
47
+ * @returns {Array<Array<Order>, Array<string>>} Array of open orders and corresponding order-ids.
48
+ */
49
+ public async openOrders(traderAddr: string, symbol: string): Promise<{ orders: Order[]; orderIds: string[] }> {
50
+ // open orders requested only for given symbol
51
+ let orderBookContract = this.getOrderBookContract(symbol);
52
+ let [orders, digests] = await Promise.all([
53
+ this.openOrdersOnOrderBook(traderAddr, orderBookContract),
54
+ this.orderIdsOfTrader(traderAddr, orderBookContract),
55
+ ]);
56
+ return { orders: orders, orderIds: digests };
57
+ }
38
58
 
39
- /**
40
- * Get all open orders for a trader-address and a symbol
41
- * @param traderAddr address of the trader for which we get the open order
42
- * @param symbol symbol of the form ETH-USD-MATIC
43
- * @returns array of open orders and corresponding order-ids
44
- */
45
- public async openOrders(traderAddr: string, symbol: string): Promise<{ orders: Order[]; orderIds: string[] }> {
46
- // open orders requested only for given symbol
47
- let orderBookContract = this.getOrderBookContract(symbol);
48
- let [orders, digests] = await Promise.all([
49
- this.openOrdersOnOrderBook(traderAddr, orderBookContract),
50
- this.orderIdsOfTrader(traderAddr, orderBookContract),
51
- ]);
52
- return { orders: orders, orderIds: digests };
59
+ /**
60
+ * Information about the position open by a given trader in a given perpetual contract.
61
+ * @param {string} traderAddr Address of the trader for which we get the position risk.
62
+ * @param {string} symbol Symbol of the form ETH-USD-MATIC.
63
+ * @returns {MarginAccount}
64
+ */
65
+ public async positionRisk(traderAddr: string, symbol: string): Promise<MarginAccount> {
66
+ if (this.proxyContract == null) {
67
+ throw Error("no proxy contract initialized. Use createProxyInstance().");
53
68
  }
69
+ let mgnAcct = await PerpetualDataHandler.getMarginAccount(
70
+ traderAddr,
71
+ symbol,
72
+ this.symbolToPerpStaticInfo,
73
+ this.proxyContract
74
+ );
75
+ return mgnAcct;
76
+ }
54
77
 
55
- public async positionRisk(traderAddr: string, symbol: string): Promise<MarginAccount> {
56
- if (this.proxyContract == null) {
57
- throw Error("no proxy contract initialized. Use createProxyInstance().");
58
- }
59
- let mgnAcct = await PerpetualDataHandler.getMarginAccount(traderAddr, symbol, this.symbolToPerpStaticInfo, this.proxyContract);
60
- return mgnAcct;
78
+ /**
79
+ * Uses the Oracle(s) in the exchange to get the latest price of a given index in a given currency, if a route exists.
80
+ * @param {string} base Index name, e.g. ETH.
81
+ * @param {string} quote Quote currency, e.g. USD.
82
+ * @returns {number} Price of index in given currency.
83
+ */
84
+ public async getOraclePrice(base: string, quote: string): Promise<number | undefined> {
85
+ if (this.proxyContract == null) {
86
+ throw Error("no proxy contract initialized. Use createProxyInstance().");
61
87
  }
88
+ let px = await this.proxyContract.getOraclePrice([toBytes4(base), toBytes4(quote)]);
89
+ return px == undefined ? undefined : ABK64x64ToFloat(px);
90
+ }
62
91
 
63
- /**
64
- * Query smart contract to get user orders and convert to user friendly order format
65
- * @param traderAddr address of trader
66
- * @param orderBookContract instance of order book
67
- * @returns array of user friendly order struct
68
- */
69
- protected async openOrdersOnOrderBook(traderAddr: string, orderBookContract: ethers.Contract): Promise<Order[]> {
70
- let orders: SmartContractOrder[] = await orderBookContract.getOrders(traderAddr, 0, 15);
71
- //eliminate empty orders and map to user friendly orders
72
- let userFriendlyOrders: Order[] = new Array<Order>();
73
- let k = 0;
74
- while (k < orders.length && orders[k].traderAddr != ZERO_ADDRESS) {
75
- userFriendlyOrders.push(PerpetualDataHandler.fromSmartContractOrder(orders[k], this.symbolToPerpStaticInfo));
76
- k++;
77
- }
78
- return userFriendlyOrders;
92
+ /**
93
+ * Query smart contract to get user orders and convert to user friendly order format.
94
+ * @param {string} traderAddr Address of trader.
95
+ * @param {ethers.Contract} orderBookContract Instance of order book.
96
+ * @returns {Order[]} Array of user friendly order struct.
97
+ * @ignore
98
+ */
99
+ protected async openOrdersOnOrderBook(traderAddr: string, orderBookContract: ethers.Contract): Promise<Order[]> {
100
+ let orders: SmartContractOrder[] = await orderBookContract.getOrders(traderAddr, 0, 15);
101
+ //eliminate empty orders and map to user friendly orders
102
+ let userFriendlyOrders: Order[] = new Array<Order>();
103
+ let k = 0;
104
+ while (k < orders.length && orders[k].traderAddr != ZERO_ADDRESS) {
105
+ userFriendlyOrders.push(PerpetualDataHandler.fromSmartContractOrder(orders[k], this.symbolToPerpStaticInfo));
106
+ k++;
79
107
  }
108
+ return userFriendlyOrders;
109
+ }
80
110
 
81
- /**
82
- *
83
- * @param traderAddr address of the trader
84
- * @param orderBookContract instance of order book contract
85
- * @returns array of order-id's
86
- */
87
- protected async orderIdsOfTrader(traderAddr: string, orderBookContract: ethers.Contract): Promise<string[]> {
88
- let digestsRaw: string[] = await orderBookContract.limitDigestsOfTrader(traderAddr, 0, 15);
89
- let k: number = 0;
90
- let digests: string[] = [];
91
- while (k < digestsRaw.length && BigNumber.from(digestsRaw[k]).gt(0)) {
92
- digests.push(digestsRaw[k]);
93
- k++;
94
- }
95
- return digests;
111
+ /**
112
+ *
113
+ * @param traderAddr address of the trader
114
+ * @param orderBookContract instance of order book contract
115
+ * @returns array of order-id's
116
+ * @ignore
117
+ */
118
+ protected async orderIdsOfTrader(traderAddr: string, orderBookContract: ethers.Contract): Promise<string[]> {
119
+ let digestsRaw: string[] = await orderBookContract.limitDigestsOfTrader(traderAddr, 0, 15);
120
+ let k: number = 0;
121
+ let digests: string[] = [];
122
+ while (k < digestsRaw.length && BigNumber.from(digestsRaw[k]).gt(0)) {
123
+ digests.push(digestsRaw[k]);
124
+ k++;
96
125
  }
126
+ return digests;
127
+ }
97
128
 
98
- public static async _exchangeInfo(_proxyContract: ethers.Contract): Promise<ExchangeInfo> {
99
- let nestedPerpetualIDs = await PerpetualDataHandler.getNestedPerpetualIds(_proxyContract);
100
- let info: ExchangeInfo = { pools: [] };
101
- const numPools = nestedPerpetualIDs.length;
102
- for (var j = 0; j < numPools; j++) {
103
- let perpetualIDs = nestedPerpetualIDs[j];
104
- let pool = await _proxyContract.getLiquidityPool(j + 1);
105
- let PoolState: PoolState = {
106
- isRunning: pool.isRunning,
107
- marginTokenAddr: pool.marginTokenAddress,
108
- poolShareTokenAddr: pool.shareTokenAddress,
109
- defaultFundCashCC: ABK64x64ToFloat(pool.fDefaultFundCashCC),
110
- pnlParticipantCashCC: ABK64x64ToFloat(pool.fPnLparticipantsCashCC),
111
- totalAMMFundCashCC: ABK64x64ToFloat(pool.fAMMFundCashCC),
112
- totalTargetAMMFundSizeCC: ABK64x64ToFloat(pool.fTargetAMMFundSize),
113
- brokerCollateralLotSize: ABK64x64ToFloat(pool.fBrokerCollateralLotSize),
114
- perpetuals: [],
115
- };
116
- for (var k = 0; k < perpetualIDs.length; k++) {
117
- let perp = await _proxyContract.getPerpetual(perpetualIDs[k]);
118
- let fIndexS2 = await _proxyContract.getOraclePrice([perp.S2BaseCCY, perp.S2QuoteCCY]);
119
- let indexS2 = ABK64x64ToFloat(fIndexS2);
120
- let indexS3 = 1;
121
- if (perp.eCollateralCurrency == COLLATERAL_CURRENCY_BASE) {
122
- indexS3 = indexS2;
123
- } else if (perp.eCollateralCurrency == COLLATERAL_CURRENCY_QUANTO) {
124
- indexS3 = ABK64x64ToFloat(await _proxyContract.getOraclePrice([perp.S3BaseCCY, perp.S3QuoteCCY]));
125
- }
126
- let markPremiumRate = ABK64x64ToFloat(perp.currentMarkPremiumRate.fPrice);
127
- let currentFundingRateBps = 1e4 * ABK64x64ToFloat(perp.fCurrentFundingRate);
128
- let state = PERP_STATE_STR[perp.state];
129
- let PerpetualState: PerpetualState = {
130
- id: perp.id,
131
- state: state,
132
- baseCurrency: fromBytes4HexString(perp.S2BaseCCY),
133
- quoteCurrency: fromBytes4HexString(perp.S2QuoteCCY),
134
- indexPrice: indexS2,
135
- collToQuoteIndexPrice: indexS3,
136
- markPrice: indexS2 * (1 + markPremiumRate),
137
- currentFundingRateBps: currentFundingRateBps,
138
- initialMarginRate: ABK64x64ToFloat(perp.fInitialMarginRate),
139
- maintenanceMarginRate: ABK64x64ToFloat(perp.fMaintenanceMarginRate),
140
- openInterestBC: ABK64x64ToFloat(perp.fOpenInterest),
141
- maxPositionBC: ABK64x64ToFloat(perp.fMaxPositionBC),
142
- };
143
- PoolState.perpetuals.push(PerpetualState);
144
- }
145
- info.pools.push(PoolState);
129
+ public static async _exchangeInfo(_proxyContract: ethers.Contract): Promise<ExchangeInfo> {
130
+ let nestedPerpetualIDs = await PerpetualDataHandler.getNestedPerpetualIds(_proxyContract);
131
+ let info: ExchangeInfo = { pools: [] };
132
+ const numPools = nestedPerpetualIDs.length;
133
+ for (var j = 0; j < numPools; j++) {
134
+ let perpetualIDs = nestedPerpetualIDs[j];
135
+ let pool = await _proxyContract.getLiquidityPool(j + 1);
136
+ let PoolState: PoolState = {
137
+ isRunning: pool.isRunning,
138
+ marginTokenAddr: pool.marginTokenAddress,
139
+ poolShareTokenAddr: pool.shareTokenAddress,
140
+ defaultFundCashCC: ABK64x64ToFloat(pool.fDefaultFundCashCC),
141
+ pnlParticipantCashCC: ABK64x64ToFloat(pool.fPnLparticipantsCashCC),
142
+ totalAMMFundCashCC: ABK64x64ToFloat(pool.fAMMFundCashCC),
143
+ totalTargetAMMFundSizeCC: ABK64x64ToFloat(pool.fTargetAMMFundSize),
144
+ brokerCollateralLotSize: ABK64x64ToFloat(pool.fBrokerCollateralLotSize),
145
+ perpetuals: [],
146
+ };
147
+ for (var k = 0; k < perpetualIDs.length; k++) {
148
+ let perp = await _proxyContract.getPerpetual(perpetualIDs[k]);
149
+ let fIndexS2 = await _proxyContract.getOraclePrice([perp.S2BaseCCY, perp.S2QuoteCCY]);
150
+ let indexS2 = ABK64x64ToFloat(fIndexS2);
151
+ let indexS3 = 1;
152
+ if (perp.eCollateralCurrency == COLLATERAL_CURRENCY_BASE) {
153
+ indexS3 = indexS2;
154
+ } else if (perp.eCollateralCurrency == COLLATERAL_CURRENCY_QUANTO) {
155
+ indexS3 = ABK64x64ToFloat(await _proxyContract.getOraclePrice([perp.S3BaseCCY, perp.S3QuoteCCY]));
146
156
  }
147
- return info;
157
+ let markPremiumRate = ABK64x64ToFloat(perp.currentMarkPremiumRate.fPrice);
158
+ let currentFundingRateBps = 1e4 * ABK64x64ToFloat(perp.fCurrentFundingRate);
159
+ let state = PERP_STATE_STR[perp.state];
160
+ let PerpetualState: PerpetualState = {
161
+ id: perp.id,
162
+ state: state,
163
+ baseCurrency: fromBytes4HexString(perp.S2BaseCCY),
164
+ quoteCurrency: fromBytes4HexString(perp.S2QuoteCCY),
165
+ indexPrice: indexS2,
166
+ collToQuoteIndexPrice: indexS3,
167
+ markPrice: indexS2 * (1 + markPremiumRate),
168
+ currentFundingRateBps: currentFundingRateBps,
169
+ initialMarginRate: ABK64x64ToFloat(perp.fInitialMarginRate),
170
+ maintenanceMarginRate: ABK64x64ToFloat(perp.fMaintenanceMarginRate),
171
+ openInterestBC: ABK64x64ToFloat(perp.fOpenInterest),
172
+ maxPositionBC: ABK64x64ToFloat(perp.fMaxPositionBC),
173
+ };
174
+ PoolState.perpetuals.push(PerpetualState);
175
+ }
176
+ info.pools.push(PoolState);
148
177
  }
178
+ return info;
179
+ }
149
180
  }
@@ -1,17 +1,143 @@
1
1
  import WriteAccessHandler from "./writeAccessHandler";
2
- import { NodeSDKConfig } from "./nodeSDKTypes";
2
+ import { BUY_SIDE, NodeSDKConfig, Order, SELL_SIDE, ZERO_ADDRESS } from "./nodeSDKTypes";
3
+ import { ethers } from "ethers";
3
4
 
4
5
  /**
5
- * OrderReferrerTool
6
- * Methods to refer orders from the limit order book
6
+ * Methods to execute existing orders from the limit order book.
7
7
  */
8
8
  export default class OrderReferrerTool extends WriteAccessHandler {
9
9
  /**
10
- * Constructor
11
- * @param config configuration
12
- * @param privateKey private key of account that trades
10
+ * Constructor.
11
+ * @param {NodeSDKConfig} config Configuration object.
12
+ * @param {string} privateKey Private key of the wallet that executes the conditional orders.
13
13
  */
14
14
  public constructor(config: NodeSDKConfig, privateKey: string) {
15
15
  super(config, privateKey);
16
16
  }
17
+
18
+ /**
19
+ * Executes an order by symbol and ID. This action interacts with the blockchain and incurs in gas costs.
20
+ * @param {string} symbol Symbol of the form ETH-USD-MATIC.
21
+ * @param {string} orderId ID of the order to be executed.
22
+ * @param {string=} referrerAddr Address of the wallet to be credited for executing the order,
23
+ * if different from the one submitting this transaction.
24
+ * @returns Transaction object.
25
+ */
26
+ public async executeOrder(
27
+ symbol: string,
28
+ orderId: string,
29
+ referrerAddr?: string
30
+ ): Promise<ethers.providers.TransactionResponse> {
31
+ if (this.proxyContract == null || this.signer == null) {
32
+ throw Error("no proxy contract or wallet initialized. Use createProxyInstance().");
33
+ }
34
+ const orderBookSC = this.getOrderBookContract(symbol);
35
+ if (typeof referrerAddr == "undefined") {
36
+ referrerAddr = this.traderAddr;
37
+ }
38
+ return await orderBookSC.executeLimitOrderByDigest(orderId, referrerAddr);
39
+ }
40
+
41
+ /**
42
+ * All the orders in the order book for a given symbol that are currently open.
43
+ * @param {string} symbol Symbol of the form ETH-USD-MATIC.
44
+ * @returns Array with all open orders and their IDs.
45
+ */
46
+ public async getAllOpenOrders(symbol: string): Promise<[Order[], string[]]> {
47
+ let totalOrders = await this.numberOfOpenOrders(symbol);
48
+ return await this.pollLimitOrders(symbol, totalOrders);
49
+ }
50
+
51
+ /**
52
+ * Total number of limit orders for this symbol, excluding those that have been cancelled/removed.
53
+ * @param {string} symbol Symbol of the form ETH-USD-MATIC.
54
+ * @returns {number} Number of open orders.
55
+ */
56
+ public async numberOfOpenOrders(symbol: string): Promise<number> {
57
+ if (this.proxyContract == null) {
58
+ throw Error("no proxy contract initialized. Use createProxyInstance().");
59
+ }
60
+ const orderBookSC = this.getOrderBookContract(symbol);
61
+ return await orderBookSC.numberOfOrderBookDigests();
62
+ }
63
+
64
+ /**
65
+ * Get a list of active conditional orders in the order book.
66
+ * This a read-only action and does not incur in gas costs.
67
+ * @param {string} symbol Symbol of the form ETH-USD-MATIC.
68
+ * @param {number} numElements Maximum number of orders to poll.
69
+ * @param {string=} startAfter Optional order ID from where to start polling. Defaults to the first order.
70
+ * @returns Array of orders and corresponding order IDs
71
+ */
72
+ public async pollLimitOrders(symbol: string, numElements: number, startAfter?: string): Promise<[Order[], string[]]> {
73
+ if (this.proxyContract == null) {
74
+ throw Error("no proxy contract initialized. Use createProxyInstance().");
75
+ }
76
+ if (typeof startAfter == "undefined") {
77
+ startAfter = ethers.constants.HashZero;
78
+ }
79
+ const orderBookSC = this.getOrderBookContract(symbol);
80
+ let [orders, orderIds] = await orderBookSC.pollLimitOrders(startAfter, numElements);
81
+ let userFriendlyOrders: Order[] = new Array<Order>();
82
+ let k = 0;
83
+ while (k < orders.length && orders[k].traderAddr != ZERO_ADDRESS) {
84
+ userFriendlyOrders.push(WriteAccessHandler.fromSmartContractOrder(orders[k], this.symbolToPerpStaticInfo));
85
+ k++;
86
+ }
87
+ return [userFriendlyOrders, orderIds];
88
+ }
89
+
90
+ public async isTradeable(order: Order) {
91
+ if (this.proxyContract == null) {
92
+ throw Error("no proxy contract initialized. Use createProxyInstance().");
93
+ }
94
+ if (order.limitPrice == undefined) {
95
+ throw Error("order does not have a limit price");
96
+ }
97
+ // check expiration date
98
+ if (order.deadline != undefined && order.deadline < Date.now()) {
99
+ return false;
100
+ }
101
+ // check limit price
102
+ let orderPrice = await WriteAccessHandler._queryPerpetualPrice(
103
+ order.symbol,
104
+ order.quantity,
105
+ this.symbolToPerpStaticInfo,
106
+ this.proxyContract
107
+ );
108
+ if (
109
+ (order.side == BUY_SIDE && orderPrice > order.limitPrice) ||
110
+ (order.side == SELL_SIDE && orderPrice < order.limitPrice)
111
+ ) {
112
+ return false;
113
+ }
114
+ // do we need to check trigger/stop?
115
+ if (order.stopPrice == undefined) {
116
+ // nothing to check, order is tradeable
117
+ return true;
118
+ }
119
+ // we need the mark price to check
120
+ let markPrice = await WriteAccessHandler._queryPerpetualMarkPrice(
121
+ order.symbol,
122
+ this.symbolToPerpStaticInfo,
123
+ this.proxyContract
124
+ );
125
+ if (
126
+ (order.side == BUY_SIDE && markPrice < order.stopPrice) ||
127
+ (order.side == SELL_SIDE && markPrice > order.stopPrice)
128
+ ) {
129
+ return false;
130
+ }
131
+ // all checks passed -> order is tradeable
132
+ return true;
133
+ }
134
+
135
+ /**
136
+ * TODO:
137
+ * - [x] executeLimitOrderByDigest
138
+ * - [x] pollLimitOrders
139
+ * - [x] isTradeable
140
+ * - [ ] get all limit orders
141
+ * - [ ] tests
142
+ */
17
143
  }
@@ -28,6 +28,7 @@ import {
28
28
  DEFAULT_CONFIG_MAINNET,
29
29
  DEFAULT_CONFIG_TESTNET_NAME,
30
30
  DEFAULT_CONFIG_TESTNET,
31
+ ONE_64x64,
31
32
  } from "./nodeSDKTypes";
32
33
  import { fromBytes4HexString, to4Chars, combineFlags, containsFlag } from "./utils";
33
34
  import {
@@ -282,6 +283,27 @@ export default class PerpetualDataHandler {
282
283
  return mgn;
283
284
  }
284
285
 
286
+ protected static async _queryPerpetualPrice(
287
+ symbol: string,
288
+ tradeAmount: number,
289
+ symbolToPerpStaticInfo: Map<string, PerpetualStaticInfo>,
290
+ _proxyContract: ethers.Contract
291
+ ): Promise<number> {
292
+ let perpId = PerpetualDataHandler.symbolToPerpetualId(symbol, symbolToPerpStaticInfo);
293
+ let fPrice = await _proxyContract.queryPerpetualPrice(perpId, floatToABK64x64(tradeAmount));
294
+ return ABK64x64ToFloat(fPrice);
295
+ }
296
+
297
+ protected static async _queryPerpetualMarkPrice(
298
+ symbol: string,
299
+ symbolToPerpStaticInfo: Map<string, PerpetualStaticInfo>,
300
+ _proxyContract: ethers.Contract
301
+ ): Promise<number> {
302
+ let perpId = PerpetualDataHandler.symbolToPerpetualId(symbol, symbolToPerpStaticInfo);
303
+ let ammState = await _proxyContract.getAMMState(perpId);
304
+ return ABK64x64ToFloat(ammState[6].mul(ONE_64x64.add(ammState[8])));
305
+ }
306
+
285
307
  /**
286
308
  * Liquidation price
287
309
  * @param cleanSymbol symbol after calling symbolToBytes4Symbol
@@ -368,12 +390,13 @@ export default class PerpetualDataHandler {
368
390
  return symbols[0] + "-" + symbols[1] + "-" + symbols[2];
369
391
  }
370
392
 
371
- private static _getByValue(map: any, searchValue: any) {
393
+ private static _getByValue(map: any, searchValue: any, valueField: any) {
372
394
  for (let [key, value] of map.entries()) {
373
- if (value === searchValue) {
395
+ if (value[valueField] === searchValue) {
374
396
  return key;
375
397
  }
376
398
  }
399
+ return undefined;
377
400
  }
378
401
 
379
402
  protected static fromSmartContractOrder(
@@ -381,7 +404,10 @@ export default class PerpetualDataHandler {
381
404
  symbolToPerpInfoMap: Map<string, PerpetualStaticInfo>
382
405
  ): Order {
383
406
  // find symbol of perpetual id
384
- let symbol = PerpetualDataHandler._getByValue(symbolToPerpInfoMap, order.iPerpetualId);
407
+ let symbol = PerpetualDataHandler._getByValue(symbolToPerpInfoMap, order.iPerpetualId, "id");
408
+ if (symbol == undefined) {
409
+ throw Error(`Perpetual id ${order.iPerpetualId} not found. Check with marketData.exchangeInfo().`);
410
+ }
385
411
  let side = order.fAmount > 0 ? BUY_SIDE : SELL_SIDE;
386
412
  let limitPrice, stopPrice;
387
413
  let fLimitPrice: BigNumber | undefined = BigNumber.from(order.fLimitPrice);
@@ -397,16 +423,16 @@ export default class PerpetualDataHandler {
397
423
  stopPrice = ABK64x64ToFloat(fStopPrice);
398
424
  }
399
425
  let userOrder: Order = {
400
- symbol: symbol,
426
+ symbol: symbol!,
401
427
  side: side,
402
428
  type: PerpetualDataHandler._flagToOrderType(order),
403
429
  quantity: Math.abs(ABK64x64ToFloat(BigNumber.from(order.fAmount))),
404
430
  reduceOnly: containsFlag(BigNumber.from(order.flags), MASK_CLOSE_ONLY),
405
431
  limitPrice: limitPrice,
406
432
  keepPositionLvg: containsFlag(BigNumber.from(order.flags), MASK_KEEP_POS_LEVERAGE),
407
- brokerFeeTbps: Number(order.brokerFeeTbps),
408
- brokerAddr: order.brokerAddr,
409
- brokerSignature: order.brokerSignature,
433
+ brokerFeeTbps: order.brokerFeeTbps == 0 ? undefined : Number(order.brokerFeeTbps),
434
+ brokerAddr: order.brokerAddr == ZERO_ADDRESS ? undefined : order.brokerAddr,
435
+ brokerSignature: order.brokerSignature == "0x" ? undefined : order.brokerSignature,
410
436
  stopPrice: stopPrice,
411
437
  leverage: ABK64x64ToFloat(BigNumber.from(order.fLeverage)),
412
438
  deadline: Number(order.iDeadline),
package/src/utils.ts CHANGED
@@ -1,32 +1,33 @@
1
1
  import { BigNumber } from "ethers";
2
-
3
- const ethers = require("ethers");
2
+ /**
3
+ * @module utils
4
+ */
4
5
 
5
6
  function _isVocal(char: string) {
6
- char = char.toLowerCase();
7
- return char == "a" || char == "e" || char == "i" || char == "o" || char == "u";
7
+ char = char.toLowerCase();
8
+ return char == "a" || char == "e" || char == "i" || char == "o" || char == "u";
8
9
  }
9
10
 
10
11
  /**
11
12
  *
12
- * @param s string to shorten/extend to 4 characters
13
- * @returns string with 4 characters (or characters + null chars)
13
+ * @param {string} s String to shorten/extend to 4 characters
14
+ * @returns {string} String with 4 characters (or characters + null chars)
14
15
  */
15
16
  export function to4Chars(s: string) {
16
- while (s.length < 4) {
17
- s = s + "\0";
17
+ while (s.length < 4) {
18
+ s = s + "\0";
19
+ }
20
+ let k = s.length - 1;
21
+ while (s.length > 4 && k >= 0) {
22
+ // chop off vocals from the end of string
23
+ // e.g. MATIC -> MATC
24
+ if (_isVocal(s.charAt(k))) {
25
+ s = s.substring(0, k) + s.substring(k + 1, s.length);
18
26
  }
19
- let k = s.length - 1;
20
- while (s.length > 4 && k >= 0) {
21
- // chop off vocals from the end of string
22
- // e.g. MATIC -> MATC
23
- if (_isVocal(s.charAt(k))) {
24
- s = s.substring(0, k) + s.substring(k + 1, s.length);
25
- }
26
- k--;
27
- }
28
- s = s.substring(0, 4);
29
- return s;
27
+ k--;
28
+ }
29
+ s = s.substring(0, 4);
30
+ return s;
30
31
  }
31
32
 
32
33
  /**
@@ -35,49 +36,49 @@ export function to4Chars(s: string) {
35
36
  * 4 characters.
36
37
  * Resulting buffer can be used with smart contract to
37
38
  * identify tokens (BTC, USDC, MATIC etc.)
38
- * @param s string to encode into bytes4
39
- * @returns buffer
39
+ * @param {string} s String to encode into bytes4
40
+ * @returns {Buffer} 4-character bytes4.
40
41
  */
41
42
  export function toBytes4(s: string): Buffer {
42
- s = to4Chars(s);
43
- let valBuff: Buffer = Buffer.from(s, "ascii");
44
- return valBuff;
43
+ s = to4Chars(s);
44
+ let valBuff: Buffer = Buffer.from(s, "ascii");
45
+ return valBuff;
45
46
  }
46
47
 
47
48
  /**
48
49
  * Decodes a buffer encoded with toBytes4 into
49
50
  * a string. The string is the result of to4Chars of the
50
51
  * originally encoded string stripped from null-chars
51
- * @param b correctly encoded bytes4 buffer using toBytes4
52
- * @returns string decoded into to4Chars-type string without null characters
52
+ * @param {Buffer} b Correctly encoded bytes4 buffer using toBytes4
53
+ * @returns {string} String decoded into to4Chars-type string without null characters
53
54
  */
54
55
  export function fromBytes4(b: Buffer): string {
55
- let val: string = b.toString("ascii");
56
- val = val.replace(/\0/g, "");
57
- return val;
56
+ let val: string = b.toString("ascii");
57
+ val = val.replace(/\0/g, "");
58
+ return val;
58
59
  }
59
60
 
60
61
  /**
61
62
  * Decodes the bytes4 encoded string received from the
62
63
  * smart contract as a hex-number in string-format
63
- * @param s string representing a hex-number ("0x...")
64
- * @returns x of to4Chars(x) stripped from null-chars,
64
+ * @param {string} s string representing a hex-number ("0x...")
65
+ * @returns {string} x of to4Chars(x) stripped from null-chars,
65
66
  * where x was originally encoded and
66
67
  * returned by the smart contract as bytes4
67
68
  */
68
69
  export function fromBytes4HexString(s: string): string {
69
- let res = "";
70
- for (let k = 2; k < s.length; k = k + 2) {
71
- res = res + String.fromCharCode(parseInt(s.substring(k, k + 2), 16));
72
- }
73
- res = res.replace(/\0/g, "");
74
- return res;
70
+ let res = "";
71
+ for (let k = 2; k < s.length; k = k + 2) {
72
+ res = res + String.fromCharCode(parseInt(s.substring(k, k + 2), 16));
73
+ }
74
+ res = res.replace(/\0/g, "");
75
+ return res;
75
76
  }
76
77
 
77
78
  export function combineFlags(f1: BigNumber, f2: BigNumber): BigNumber {
78
- return BigNumber.from(parseInt(f1.toString()) | parseInt(f2.toString()));
79
+ return BigNumber.from(parseInt(f1.toString()) | parseInt(f2.toString()));
79
80
  }
80
81
 
81
82
  export function containsFlag(f1: BigNumber, f2: BigNumber): boolean {
82
- return (parseInt(f1.toString()) & parseInt(f2.toString())) > 0;
83
+ return (parseInt(f1.toString()) & parseInt(f2.toString())) > 0;
83
84
  }