@d8x/perpetuals-sdk 0.1.12 → 0.2.0

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 (165) hide show
  1. package/dist/cjs/abi/testnet/IPerpetualManager.json +5215 -0
  2. package/dist/cjs/abi/testnet/LimitOrderBook.json +1075 -0
  3. package/dist/cjs/abi/zkevmTestnet/IPerpetualManager.json +5215 -0
  4. package/dist/cjs/abi/zkevmTestnet/LimitOrderBook.json +1075 -0
  5. package/dist/cjs/abi/zkevmTestnet/LimitOrderBookFactory.json +135 -0
  6. package/dist/cjs/accountTrade.js +441 -0
  7. package/dist/cjs/accountTrade.js.map +1 -0
  8. package/dist/{src → cjs}/brokerTool.js +31 -84
  9. package/dist/cjs/brokerTool.js.map +1 -0
  10. package/dist/cjs/config/defaultConfig.json +47 -0
  11. package/dist/cjs/config/mockSwap.json +4 -0
  12. package/dist/cjs/config/priceFeedConfig.json +104 -0
  13. package/dist/cjs/config/symbolList.json +13 -0
  14. package/dist/cjs/d8XMath.js.map +1 -0
  15. package/dist/cjs/index.js +29 -0
  16. package/dist/cjs/index.js.map +1 -0
  17. package/dist/cjs/liquidatorTool.js +287 -0
  18. package/dist/cjs/liquidatorTool.js.map +1 -0
  19. package/dist/{src → cjs}/liquidityProviderTool.js +12 -65
  20. package/dist/cjs/liquidityProviderTool.js.map +1 -0
  21. package/dist/{src → cjs}/marketData.js +52 -134
  22. package/dist/cjs/marketData.js.map +1 -0
  23. package/dist/{src → cjs}/nodeSDKTypes.d.ts +5 -5
  24. package/dist/cjs/nodeSDKTypes.js +64 -0
  25. package/dist/cjs/nodeSDKTypes.js.map +1 -0
  26. package/dist/{src → cjs}/orderReferrerTool.d.ts +12 -5
  27. package/dist/{src → cjs}/orderReferrerTool.js +114 -112
  28. package/dist/cjs/orderReferrerTool.js.map +1 -0
  29. package/dist/{src → cjs}/perpetualDataHandler.d.ts +1 -1
  30. package/dist/{src → cjs}/perpetualDataHandler.js +47 -109
  31. package/dist/cjs/perpetualDataHandler.js.map +1 -0
  32. package/dist/{src → cjs}/perpetualEventHandler.d.ts +3 -3
  33. package/dist/{src → cjs}/perpetualEventHandler.js +12 -74
  34. package/dist/cjs/perpetualEventHandler.js.map +1 -0
  35. package/dist/cjs/priceFeeds.js +466 -0
  36. package/dist/cjs/priceFeeds.js.map +1 -0
  37. package/dist/{src → cjs}/traderDigests.js +7 -43
  38. package/dist/cjs/traderDigests.js.map +1 -0
  39. package/dist/{src → cjs}/traderInterface.js +13 -66
  40. package/dist/cjs/traderInterface.js.map +1 -0
  41. package/dist/{src → cjs}/triangulator.js +2 -17
  42. package/dist/cjs/triangulator.js.map +1 -0
  43. package/dist/{src → cjs}/utils.js +3 -29
  44. package/dist/cjs/utils.js.map +1 -0
  45. package/dist/cjs/version.d.ts +1 -0
  46. package/dist/{src → cjs}/version.js +1 -1
  47. package/dist/cjs/version.js.map +1 -0
  48. package/dist/{src → cjs}/writeAccessHandler.js +12 -65
  49. package/dist/cjs/writeAccessHandler.js.map +1 -0
  50. package/dist/esm/abi/ERC20.json +288 -0
  51. package/dist/esm/abi/MockTokenSwap.json +186 -0
  52. package/dist/{abi/testnet → esm/abi/central-park}/IPerpetualManager.json +404 -214
  53. package/dist/{abi/testnet → esm/abi/central-park}/LimitOrderBook.json +197 -15
  54. package/dist/esm/abi/central-park/LimitOrderBookFactory.json +135 -0
  55. package/dist/esm/abi/testnet/IPerpetualManager.json +5215 -0
  56. package/dist/esm/abi/testnet/LimitOrderBook.json +1075 -0
  57. package/dist/esm/abi/testnet/LimitOrderBookFactory.json +135 -0
  58. package/dist/esm/abi/zkevmTestnet/IPerpetualManager.json +5215 -0
  59. package/dist/esm/abi/zkevmTestnet/LimitOrderBook.json +1075 -0
  60. package/dist/esm/abi/zkevmTestnet/LimitOrderBookFactory.json +135 -0
  61. package/dist/esm/accountTrade.d.ts +221 -0
  62. package/dist/{src → esm}/accountTrade.js +22 -93
  63. package/dist/esm/accountTrade.js.map +1 -0
  64. package/dist/esm/brokerTool.d.ts +318 -0
  65. package/dist/esm/brokerTool.js +572 -0
  66. package/dist/esm/brokerTool.js.map +1 -0
  67. package/dist/esm/config/defaultConfig.json +47 -0
  68. package/dist/esm/config/mockSwap.json +4 -0
  69. package/dist/esm/config/priceFeedConfig.json +104 -0
  70. package/dist/esm/config/symbolList.json +13 -0
  71. package/dist/esm/d8XMath.d.ts +122 -0
  72. package/dist/esm/d8XMath.js +247 -0
  73. package/dist/esm/d8XMath.js.map +1 -0
  74. package/{src/index.ts → dist/esm/index.d.ts} +1 -15
  75. package/dist/esm/index.js +16 -0
  76. package/dist/esm/index.js.map +1 -0
  77. package/dist/esm/liquidatorTool.d.ts +158 -0
  78. package/dist/{src → esm}/liquidatorTool.js +10 -65
  79. package/dist/esm/liquidatorTool.js.map +1 -0
  80. package/dist/esm/liquidityProviderTool.d.ts +126 -0
  81. package/dist/esm/liquidityProviderTool.js +218 -0
  82. package/dist/esm/liquidityProviderTool.js.map +1 -0
  83. package/dist/esm/marketData.d.ts +309 -0
  84. package/dist/esm/marketData.js +1007 -0
  85. package/dist/esm/marketData.js.map +1 -0
  86. package/dist/esm/nodeSDKTypes.d.ts +266 -0
  87. package/dist/esm/nodeSDKTypes.js +60 -0
  88. package/dist/esm/nodeSDKTypes.js.map +1 -0
  89. package/dist/esm/orderReferrerTool.d.ts +196 -0
  90. package/dist/esm/orderReferrerTool.js +491 -0
  91. package/dist/esm/orderReferrerTool.js.map +1 -0
  92. package/dist/esm/perpetualDataHandler.d.ts +220 -0
  93. package/dist/esm/perpetualDataHandler.js +1060 -0
  94. package/dist/esm/perpetualDataHandler.js.map +1 -0
  95. package/dist/esm/perpetualEventHandler.d.ts +179 -0
  96. package/dist/esm/perpetualEventHandler.js +435 -0
  97. package/dist/esm/perpetualEventHandler.js.map +1 -0
  98. package/dist/esm/priceFeeds.d.ts +115 -0
  99. package/dist/{src → esm}/priceFeeds.js +16 -83
  100. package/dist/esm/priceFeeds.js.map +1 -0
  101. package/dist/esm/traderDigests.d.ts +21 -0
  102. package/dist/esm/traderDigests.js +80 -0
  103. package/dist/esm/traderDigests.js.map +1 -0
  104. package/dist/esm/traderInterface.d.ts +79 -0
  105. package/dist/esm/traderInterface.js +196 -0
  106. package/dist/esm/traderInterface.js.map +1 -0
  107. package/dist/esm/triangulator.d.ts +27 -0
  108. package/dist/esm/triangulator.js +110 -0
  109. package/dist/esm/triangulator.js.map +1 -0
  110. package/dist/esm/utils.d.ts +59 -0
  111. package/dist/esm/utils.js +138 -0
  112. package/dist/esm/utils.js.map +1 -0
  113. package/dist/esm/version.d.ts +1 -0
  114. package/dist/esm/version.js +2 -0
  115. package/dist/esm/version.js.map +1 -0
  116. package/dist/esm/writeAccessHandler.d.ts +50 -0
  117. package/dist/esm/writeAccessHandler.js +157 -0
  118. package/dist/esm/writeAccessHandler.js.map +1 -0
  119. package/package.json +16 -26
  120. package/dist/bundle.js +0 -36793
  121. package/dist/config/defaultConfig.json +0 -47
  122. package/dist/config/mockSwap.json +0 -4
  123. package/dist/config/priceFeedConfig.json +0 -104
  124. package/dist/config/symbolList.json +0 -13
  125. package/dist/src/index.js +0 -45
  126. package/dist/src/nodeSDKTypes.js +0 -115
  127. package/dist/src/version.d.ts +0 -1
  128. package/module.d.ts +0 -1
  129. package/src/accountTrade.ts +0 -392
  130. package/src/brokerTool.ts +0 -507
  131. package/src/d8XMath.ts +0 -319
  132. package/src/liquidatorTool.ts +0 -258
  133. package/src/liquidityProviderTool.ts +0 -186
  134. package/src/marketData.ts +0 -946
  135. package/src/nodeSDKTypes.ts +0 -293
  136. package/src/orderReferrerTool.ts +0 -389
  137. package/src/perpetualDataHandler.ts +0 -1061
  138. package/src/perpetualEventHandler.ts +0 -455
  139. package/src/priceFeeds.ts +0 -381
  140. package/src/traderDigests.ts +0 -91
  141. package/src/traderInterface.ts +0 -159
  142. package/src/triangulator.ts +0 -105
  143. package/src/utils.ts +0 -134
  144. package/src/version.ts +0 -1
  145. package/src/writeAccessHandler.ts +0 -127
  146. /package/dist/{abi → cjs/abi}/ERC20.json +0 -0
  147. /package/dist/{abi → cjs/abi}/MockTokenSwap.json +0 -0
  148. /package/dist/{abi → cjs/abi}/central-park/IPerpetualManager.json +0 -0
  149. /package/dist/{abi → cjs/abi}/central-park/LimitOrderBook.json +0 -0
  150. /package/dist/{abi → cjs/abi}/central-park/LimitOrderBookFactory.json +0 -0
  151. /package/dist/{abi → cjs/abi}/testnet/LimitOrderBookFactory.json +0 -0
  152. /package/dist/{src → cjs}/accountTrade.d.ts +0 -0
  153. /package/dist/{src → cjs}/brokerTool.d.ts +0 -0
  154. /package/dist/{src → cjs}/d8XMath.d.ts +0 -0
  155. /package/dist/{src → cjs}/d8XMath.js +0 -0
  156. /package/dist/{src → cjs}/index.d.ts +0 -0
  157. /package/dist/{src → cjs}/liquidatorTool.d.ts +0 -0
  158. /package/dist/{src → cjs}/liquidityProviderTool.d.ts +0 -0
  159. /package/dist/{src → cjs}/marketData.d.ts +0 -0
  160. /package/dist/{src → cjs}/priceFeeds.d.ts +0 -0
  161. /package/dist/{src → cjs}/traderDigests.d.ts +0 -0
  162. /package/dist/{src → cjs}/traderInterface.d.ts +0 -0
  163. /package/dist/{src → cjs}/triangulator.d.ts +0 -0
  164. /package/dist/{src → cjs}/utils.d.ts +0 -0
  165. /package/dist/{src → cjs}/writeAccessHandler.d.ts +0 -0
@@ -1,1061 +0,0 @@
1
- import { ethers, BigNumber, ContractInterface } from "ethers";
2
- import {
3
- NodeSDKConfig,
4
- MAX_64x64,
5
- Order,
6
- SmartContractOrder,
7
- CollaterlCCY,
8
- PerpetualStaticInfo,
9
- COLLATERAL_CURRENCY_BASE,
10
- COLLATERAL_CURRENCY_QUOTE,
11
- BUY_SIDE,
12
- SELL_SIDE,
13
- CLOSED_SIDE,
14
- ORDER_MAX_DURATION_SEC,
15
- ZERO_ADDRESS,
16
- ORDER_TYPE_LIMIT,
17
- ORDER_TYPE_MARKET,
18
- ORDER_TYPE_STOP_MARKET,
19
- ORDER_TYPE_STOP_LIMIT,
20
- MASK_LIMIT_ORDER,
21
- MASK_CLOSE_ONLY,
22
- MASK_KEEP_POS_LEVERAGE,
23
- MASK_MARKET_ORDER,
24
- MASK_STOP_ORDER,
25
- MarginAccount,
26
- PoolStaticInfo,
27
- ONE_64x64,
28
- PERP_STATE_STR,
29
- PerpetualState,
30
- DEFAULT_CONFIG,
31
- DEFAULT_CONFIG_MAINNET_NAME,
32
- PriceFeedSubmission,
33
- loadABIs,
34
- SYMBOL_LIST,
35
- ClientOrder,
36
- ZERO_ORDER_ID,
37
- } from "./nodeSDKTypes";
38
- import {
39
- fromBytes4HexString,
40
- to4Chars,
41
- combineFlags,
42
- containsFlag,
43
- contractSymbolToSymbol,
44
- symbol4BToLongSymbol,
45
- } from "./utils";
46
- import {
47
- ABK64x64ToFloat,
48
- floatToABK64x64,
49
- div64x64,
50
- calculateLiquidationPriceCollateralQuanto,
51
- calculateLiquidationPriceCollateralBase,
52
- calculateLiquidationPriceCollateralQuote,
53
- } from "./d8XMath";
54
- import PriceFeeds from "./priceFeeds";
55
-
56
- /**
57
- * Parent class for MarketData and WriteAccessHandler that handles
58
- * common data and chain operations.
59
- */
60
- export default class PerpetualDataHandler {
61
- PRICE_UPDATE_FEE_GWEI = 1;
62
- //map symbol of the form ETH-USD-MATIC into perpetual ID and other static info
63
- //this is initialized in the createProxyInstance function
64
- protected symbolToPerpStaticInfo: Map<string, PerpetualStaticInfo>;
65
- protected poolStaticInfos: Array<PoolStaticInfo>;
66
- protected symbolList: Map<string, string>;
67
-
68
- //map margin token of the form MATIC or ETH or USDC into
69
- //the address of the margin token
70
- protected symbolToTokenAddrMap: Map<string, string>;
71
- protected chainId: number;
72
- protected proxyContract: ethers.Contract | null = null;
73
- protected proxyABI: ethers.ContractInterface;
74
- protected proxyAddr: string;
75
- // limit order book
76
- protected lobFactoryContract: ethers.Contract | null = null;
77
- protected lobFactoryABI: ethers.ContractInterface;
78
- protected lobFactoryAddr: string | undefined;
79
- protected lobABI: ethers.ContractInterface;
80
- protected nodeURL: string;
81
- protected provider: ethers.providers.Provider | null = null;
82
-
83
- private signerOrProvider: ethers.Signer | ethers.providers.Provider | null = null;
84
- protected priceFeedGetter: PriceFeeds;
85
-
86
- // pools are numbered consecutively starting at 1
87
- // nestedPerpetualIDs contains an array for each pool
88
- // each pool-array contains perpetual ids
89
- protected nestedPerpetualIDs: number[][];
90
-
91
- public constructor(config: NodeSDKConfig) {
92
- this.symbolToPerpStaticInfo = new Map<string, PerpetualStaticInfo>();
93
- this.poolStaticInfos = new Array<PoolStaticInfo>();
94
- this.symbolToTokenAddrMap = new Map<string, string>();
95
- this.nestedPerpetualIDs = new Array<Array<number>>();
96
- this.chainId = config.chainId;
97
- this.proxyAddr = config.proxyAddr;
98
- this.nodeURL = config.nodeURL;
99
- this.proxyABI = config.proxyABI!;
100
- this.lobFactoryABI = config.lobFactoryABI!;
101
- this.lobABI = config.lobABI!;
102
- this.symbolList = SYMBOL_LIST;
103
- this.priceFeedGetter = new PriceFeeds(this, config.priceFeedConfigNetwork);
104
- }
105
-
106
- protected async initContractsAndData(signerOrProvider: ethers.Signer | ethers.providers.Provider) {
107
- this.signerOrProvider = signerOrProvider;
108
- // check network
109
- let network: ethers.providers.Network;
110
- try {
111
- if (signerOrProvider instanceof ethers.Signer) {
112
- network = await signerOrProvider.provider!.getNetwork();
113
- } else {
114
- network = await signerOrProvider.getNetwork();
115
- }
116
- } catch (error: any) {
117
- console.log(error);
118
- throw new Error(`Unable to connect to network.`);
119
- }
120
- if (network.chainId !== this.chainId) {
121
- throw new Error(`Provider: chain id ${network.chainId} does not match config (${this.chainId})`);
122
- }
123
- this.proxyContract = new ethers.Contract(this.proxyAddr, this.proxyABI, signerOrProvider);
124
- this.lobFactoryAddr = await this.proxyContract.getOrderBookFactoryAddress();
125
- this.lobFactoryContract = new ethers.Contract(this.lobFactoryAddr!, this.lobFactoryABI, signerOrProvider);
126
- await this._fillSymbolMaps(this.proxyContract);
127
- }
128
-
129
- /**
130
- * Returns the order-book contract for the symbol if found or fails
131
- * @param symbol symbol of the form ETH-USD-MATIC
132
- * @returns order book contract for the perpetual
133
- */
134
- public getOrderBookContract(symbol: string): ethers.Contract {
135
- let orderBookAddr = this.symbolToPerpStaticInfo.get(symbol)?.limitOrderBookAddr;
136
- if (orderBookAddr == "" || orderBookAddr == undefined || this.signerOrProvider == null) {
137
- throw Error(`no limit order book found for ${symbol} or no signer`);
138
- }
139
- let lobContract = new ethers.Contract(orderBookAddr, this.lobABI, this.signerOrProvider);
140
- return lobContract;
141
- }
142
-
143
- /**
144
- * Called when initializing. This function fills this.symbolToTokenAddrMap,
145
- * and this.nestedPerpetualIDs and this.symbolToPerpStaticInfo
146
- *
147
- */
148
- protected async _fillSymbolMaps(proxyContract: ethers.Contract) {
149
- if (proxyContract == null || this.lobFactoryContract == null) {
150
- throw Error("proxy or limit order book not defined");
151
- }
152
- let oracleFactoryAddr = await proxyContract.getOracleFactory();
153
- this.nestedPerpetualIDs = await PerpetualDataHandler.getNestedPerpetualIds(proxyContract);
154
- let requiredPairs = new Set<string>();
155
- for (let j = 0; j < this.nestedPerpetualIDs.length; j++) {
156
- let pool = await proxyContract.getLiquidityPool(j + 1);
157
- let poolMarginTokenAddr = pool.marginTokenAddress;
158
- let perpetualIDs = this.nestedPerpetualIDs[j];
159
- let poolCCY: string | undefined = undefined;
160
- let currentSymbols: string[] = [];
161
- let currentSymbolsS3: string[] = [];
162
- let currentLimitOrderBookAddr: string[] = [];
163
- let ccy: CollaterlCCY[] = [];
164
- let initRate: number[] = [];
165
- let mgnRate: number[] = [];
166
- let lotSizes: number[] = [];
167
- let refRebates: number[] = [];
168
-
169
- for (let k = 0; k < perpetualIDs.length; k++) {
170
- let perp = await proxyContract.getPerpetual(perpetualIDs[k]);
171
- let base = contractSymbolToSymbol(perp.S2BaseCCY, this.symbolList);
172
- let quote = contractSymbolToSymbol(perp.S2QuoteCCY, this.symbolList);
173
- let base3 = contractSymbolToSymbol(perp.S3BaseCCY, this.symbolList);
174
- let quote3 = contractSymbolToSymbol(perp.S3QuoteCCY, this.symbolList);
175
- let sym = base + "-" + quote;
176
- let sym3 = base3 + "-" + quote3;
177
- requiredPairs.add(sym);
178
- if (sym3 != "-") {
179
- requiredPairs.add(sym3);
180
- } else {
181
- sym3 = "";
182
- }
183
- currentSymbols.push(sym);
184
- currentSymbolsS3.push(sym3);
185
- initRate.push(ABK64x64ToFloat(perp.fInitialMarginRate));
186
- mgnRate.push(ABK64x64ToFloat(perp.fMaintenanceMarginRate));
187
- lotSizes.push(ABK64x64ToFloat(perp.fLotSizeBC));
188
- refRebates.push(ABK64x64ToFloat(perp.fReferralRebateCC));
189
- // try to find a limit order book
190
- let lobAddr = await this.lobFactoryContract.getOrderBookAddress(perpetualIDs[k]);
191
- currentLimitOrderBookAddr.push(lobAddr);
192
-
193
- // we find out the pool currency by looking at all perpetuals
194
- // unless for quanto perpetuals, we know the pool currency
195
- // from the perpetual. This fails if we have a pool with only
196
- // quanto perpetuals
197
- if (perp.eCollateralCurrency == COLLATERAL_CURRENCY_BASE) {
198
- poolCCY = poolCCY ?? base;
199
- ccy.push(CollaterlCCY.BASE);
200
- } else if (perp.eCollateralCurrency == COLLATERAL_CURRENCY_QUOTE) {
201
- poolCCY = poolCCY ?? quote;
202
- ccy.push(CollaterlCCY.QUOTE);
203
- } else {
204
- poolCCY = poolCCY ?? base3;
205
- ccy.push(CollaterlCCY.QUANTO);
206
- }
207
- }
208
- if (perpetualIDs.length == 0) {
209
- continue;
210
- }
211
-
212
- let info: PoolStaticInfo = {
213
- poolId: j + 1,
214
- poolMarginSymbol: poolCCY!,
215
- poolMarginTokenAddr: poolMarginTokenAddr,
216
- shareTokenAddr: pool.shareTokenAddress,
217
- oracleFactoryAddr: oracleFactoryAddr,
218
- };
219
- this.poolStaticInfos.push(info);
220
- let currentSymbols3 = currentSymbols.map((x) => x + "-" + poolCCY);
221
- // push into map
222
- for (let k = 0; k < perpetualIDs.length; k++) {
223
- // add price IDs
224
- let idsB32, isPyth;
225
- [idsB32, isPyth] = await proxyContract.getPriceInfo(perpetualIDs[k]);
226
- this.symbolToPerpStaticInfo.set(currentSymbols3[k], {
227
- id: perpetualIDs[k],
228
- limitOrderBookAddr: currentLimitOrderBookAddr[k],
229
- initialMarginRate: initRate[k],
230
- maintenanceMarginRate: mgnRate[k],
231
- collateralCurrencyType: ccy[k],
232
- S2Symbol: currentSymbols[k],
233
- S3Symbol: currentSymbolsS3[k],
234
- lotSizeBC: lotSizes[k],
235
- referralRebate: refRebates[k],
236
- priceIds: idsB32,
237
- });
238
- }
239
- // push margin token address into map
240
- this.symbolToTokenAddrMap.set(poolCCY!, poolMarginTokenAddr);
241
- }
242
- // pre-calculate all triangulation paths so we can easily get from
243
- // the prices of price-feeds to the index price required, e.g.
244
- // BTC-USDC : BTC-USD / USDC-USD
245
- this.priceFeedGetter.initializeTriangulations(requiredPairs);
246
- }
247
-
248
- /**
249
- * Get pool symbol given a pool Id.
250
- * @param {number} poolId Pool Id.
251
- * @returns {symbol} Pool symbol, e.g. "USDC".
252
- */
253
- public getSymbolFromPoolId(poolId: number): string {
254
- return PerpetualDataHandler._getSymbolFromPoolId(poolId, this.poolStaticInfos);
255
- }
256
-
257
- /**
258
- * Get pool Id given a pool symbol.
259
- * @param {string} symbol Pool symbol.
260
- * @returns {number} Pool Id.
261
- */
262
- public getPoolIdFromSymbol(symbol: string): number {
263
- return PerpetualDataHandler._getPoolIdFromSymbol(symbol, this.poolStaticInfos);
264
- }
265
-
266
- /**
267
- * Get perpetual Id given a perpetual symbol.
268
- * @param {string} symbol Perpetual symbol, e.g. "BTC-USD-MATIC".
269
- * @returns {number} Perpetual Id.
270
- */
271
- public getPerpIdFromSymbol(symbol: string): number {
272
- return PerpetualDataHandler.symbolToPerpetualId(symbol, this.symbolToPerpStaticInfo);
273
- }
274
-
275
- /**
276
- * Get the symbol in long format of the perpetual id
277
- * @param perpId perpetual id
278
- */
279
- public getSymbolFromPerpId(perpId: number): string | undefined {
280
- return PerpetualDataHandler.perpetualIdToSymbol(perpId, this.symbolToPerpStaticInfo);
281
- }
282
-
283
- public symbol4BToLongSymbol(sym: string): string {
284
- return symbol4BToLongSymbol(sym, this.symbolList);
285
- }
286
-
287
- /**
288
- * Get PriceFeedSubmission data required for blockchain queries that involve price data, and the corresponding
289
- * triangulated prices for the indices S2 and S3
290
- * @param symbol pool symbol of the form "ETH-USD-MATIC"
291
- * @returns PriceFeedSubmission and prices for S2 and S3. [S2price, 0] if S3 not defined.
292
- */
293
- public async fetchPriceSubmissionInfoForPerpetual(
294
- symbol: string
295
- ): Promise<{ submission: PriceFeedSubmission; pxS2S3: [number, number] }> {
296
- // fetch prices from required price-feeds (REST)
297
- return await this.priceFeedGetter.fetchFeedPriceInfoAndIndicesForPerpetual(symbol);
298
- }
299
-
300
- /**
301
- * Get the symbols required as indices for the given perpetual
302
- * @param symbol of the form ETH-USD-MATIC, specifying the perpetual
303
- * @returns name of underlying index prices, e.g. ["MATIC-USD", ""]
304
- */
305
- public getIndexSymbols(symbol: string): [string, string] {
306
- // get index
307
- let staticInfo = this.symbolToPerpStaticInfo.get(symbol);
308
- if (staticInfo == undefined) {
309
- throw new Error(`No static info for perpetual with symbol ${symbol}`);
310
- }
311
- return [staticInfo.S2Symbol, staticInfo.S3Symbol];
312
- }
313
-
314
- /**
315
- * Get the latest prices for a given perpetual from the offchain oracle
316
- * networks
317
- * @param symbol perpetual symbol of the form BTC-USD-MATIC
318
- * @returns array of price feed updates that can be submitted to the smart contract
319
- * and corresponding price information
320
- */
321
- public async fetchLatestFeedPriceInfo(symbol: string): Promise<PriceFeedSubmission> {
322
- return await this.priceFeedGetter.fetchLatestFeedPriceInfoForPerpetual(symbol);
323
- }
324
-
325
- /**
326
- * Get list of required pyth price source IDs for given perpetual
327
- * @param symbol perpetual symbol, e.g., BTC-USD-MATIC
328
- * @returns list of required pyth price sources for this perpetual
329
- */
330
- public getPriceIds(symbol: string): string[] {
331
- let perpInfo = this.symbolToPerpStaticInfo.get(symbol);
332
- if (perpInfo == undefined) {
333
- throw Error(`Perpetual with symbol ${symbol} not found. Check symbol or use createProxyInstance().`);
334
- }
335
- return perpInfo.priceIds;
336
- }
337
-
338
- protected static _getSymbolFromPoolId(poolId: number, staticInfos: PoolStaticInfo[]): string {
339
- let idx = poolId - 1;
340
- return staticInfos[idx].poolMarginSymbol;
341
- }
342
-
343
- protected static _getPoolIdFromSymbol(symbol: string, staticInfos: PoolStaticInfo[]): number {
344
- let symbols = symbol.split("-");
345
- //in case user provided ETH-USD-MATIC instead of MATIC; or similar
346
- if (symbols.length == 3) {
347
- symbol = symbols[2];
348
- }
349
- let j = 0;
350
- while (j < staticInfos.length && staticInfos[j].poolMarginSymbol != symbol) {
351
- j++;
352
- }
353
- if (j == staticInfos.length) {
354
- throw new Error(`no pool found for symbol ${symbol}`);
355
- }
356
- return j + 1;
357
- }
358
-
359
- public static async getNestedPerpetualIds(_proxyContract: ethers.Contract): Promise<number[][]> {
360
- let poolCount = await _proxyContract.getPoolCount();
361
- let poolIds: number[][] = new Array(poolCount);
362
- for (let i = 1; i < poolCount + 1; i++) {
363
- let perpetualCount = await _proxyContract.getPerpetualCountInPool(i);
364
- poolIds[i - 1] = new Array(perpetualCount);
365
- for (let j = 0; j < perpetualCount; j++) {
366
- let id = await _proxyContract.getPerpetualId(i, j);
367
- poolIds[i - 1][j] = id;
368
- }
369
- }
370
- return poolIds;
371
- }
372
-
373
- public static buildMarginAccountFromState(
374
- symbol: string,
375
- traderState: ethers.BigNumber[],
376
- symbolToPerpStaticInfo: Map<string, PerpetualStaticInfo>,
377
- _pxS2S3: [number, number]
378
- ): MarginAccount {
379
- const idx_cash = 3;
380
- const idx_notional = 4;
381
- const idx_locked_in = 5;
382
- const idx_mark_price = 8;
383
- const idx_lvg = 7;
384
- const idx_s3 = 9;
385
- let isEmpty = traderState[idx_notional].eq(0);
386
- let cash = ABK64x64ToFloat(traderState[idx_cash]);
387
- let S2Liq = 0,
388
- S3Liq = 0,
389
- tau = Infinity,
390
- pnl = 0,
391
- unpaidFundingCC = 0,
392
- fLockedIn = BigNumber.from(0),
393
- side = CLOSED_SIDE,
394
- entryPrice = 0;
395
- if (!isEmpty) {
396
- [S2Liq, S3Liq, tau, pnl, unpaidFundingCC] = PerpetualDataHandler._calculateLiquidationPrice(
397
- symbol,
398
- traderState,
399
- _pxS2S3[0],
400
- symbolToPerpStaticInfo
401
- );
402
- fLockedIn = traderState[idx_locked_in];
403
- side = traderState[idx_locked_in].gt(0) ? BUY_SIDE : SELL_SIDE;
404
- entryPrice = ABK64x64ToFloat(div64x64(fLockedIn, traderState[idx_notional]));
405
- }
406
- let mgn: MarginAccount = {
407
- symbol: symbol,
408
- positionNotionalBaseCCY: isEmpty ? 0 : ABK64x64ToFloat(traderState[idx_notional].abs()),
409
- side: isEmpty ? CLOSED_SIDE : side,
410
- entryPrice: isEmpty ? 0 : entryPrice,
411
- leverage: isEmpty ? 0 : ABK64x64ToFloat(traderState[idx_lvg]),
412
- markPrice: ABK64x64ToFloat(traderState[idx_mark_price].abs()),
413
- unrealizedPnlQuoteCCY: isEmpty ? 0 : pnl,
414
- unrealizedFundingCollateralCCY: isEmpty ? 0 : unpaidFundingCC,
415
- collateralCC: cash,
416
- liquidationLvg: isEmpty ? 0 : 1 / tau,
417
- liquidationPrice: isEmpty ? [0, 0] : [S2Liq, S3Liq],
418
- collToQuoteConversion: ABK64x64ToFloat(traderState[idx_s3]),
419
- };
420
- return mgn;
421
- }
422
-
423
- public static async getMarginAccount(
424
- traderAddr: string,
425
- symbol: string,
426
- symbolToPerpStaticInfo: Map<string, PerpetualStaticInfo>,
427
- _proxyContract: ethers.Contract,
428
- _pxS2S3: [number, number]
429
- ): Promise<MarginAccount> {
430
- let perpId = Number(symbol);
431
- if (isNaN(perpId)) {
432
- perpId = PerpetualDataHandler.symbolToPerpetualId(symbol, symbolToPerpStaticInfo);
433
- }
434
- let traderState = await _proxyContract.getTraderState(
435
- perpId,
436
- traderAddr,
437
- _pxS2S3.map((x) => floatToABK64x64(x))
438
- );
439
- return PerpetualDataHandler.buildMarginAccountFromState(symbol, traderState, symbolToPerpStaticInfo, _pxS2S3);
440
- }
441
-
442
- protected static async _queryPerpetualPrice(
443
- symbol: string,
444
- tradeAmount: number,
445
- symbolToPerpStaticInfo: Map<string, PerpetualStaticInfo>,
446
- _proxyContract: ethers.Contract,
447
- indexPrices: [number, number]
448
- ): Promise<number> {
449
- let perpId = PerpetualDataHandler.symbolToPerpetualId(symbol, symbolToPerpStaticInfo);
450
- let fIndexPrices = indexPrices.map((x) => floatToABK64x64(x == undefined || Number.isNaN(x) ? 0 : x));
451
- let fPrice = await _proxyContract.queryPerpetualPrice(perpId, floatToABK64x64(tradeAmount), fIndexPrices);
452
- return ABK64x64ToFloat(fPrice);
453
- }
454
-
455
- protected static async _queryPerpetualMarkPrice(
456
- symbol: string,
457
- symbolToPerpStaticInfo: Map<string, PerpetualStaticInfo>,
458
- _proxyContract: ethers.Contract,
459
- indexPrices: [number, number]
460
- ): Promise<number> {
461
- let perpId = PerpetualDataHandler.symbolToPerpetualId(symbol, symbolToPerpStaticInfo);
462
- let [S2, S3] = indexPrices.map((x) => floatToABK64x64(x == undefined || Number.isNaN(x) ? 0 : x));
463
- let ammState = await _proxyContract.getAMMState(perpId, [S2, S3]);
464
- return ABK64x64ToFloat(ammState[6].mul(ONE_64x64.add(ammState[8])).div(ONE_64x64));
465
- }
466
-
467
- protected static async _queryPerpetualState(
468
- symbol: string,
469
- symbolToPerpStaticInfo: Map<string, PerpetualStaticInfo>,
470
- _proxyContract: ethers.Contract,
471
- indexPrices: [number, number, boolean, boolean]
472
- ): Promise<PerpetualState> {
473
- let perpId = PerpetualDataHandler.symbolToPerpetualId(symbol, symbolToPerpStaticInfo);
474
- let staticInfo = symbolToPerpStaticInfo.get(symbol)!;
475
- let ccy = symbol.split("-");
476
- let [S2, S3] = [indexPrices[0], indexPrices[1]];
477
- if (staticInfo.collateralCurrencyType == CollaterlCCY.BASE) {
478
- S3 = S2;
479
- } else if (staticInfo.collateralCurrencyType == CollaterlCCY.QUOTE) {
480
- S3 = 1;
481
- }
482
- let ammState = await _proxyContract.getAMMState(perpId, [S2, S3].map(floatToABK64x64));
483
- let markPrice = S2 * (1 + ABK64x64ToFloat(ammState[8]));
484
- let state: PerpetualState = {
485
- id: perpId,
486
- state: PERP_STATE_STR[ammState[13]],
487
- baseCurrency: ccy[0],
488
- quoteCurrency: ccy[1],
489
- indexPrice: S2,
490
- collToQuoteIndexPrice: S3,
491
- markPrice: markPrice,
492
- midPrice: ABK64x64ToFloat(ammState[10]),
493
- currentFundingRateBps: ABK64x64ToFloat(ammState[14]) * 1e4,
494
- openInterestBC: ABK64x64ToFloat(ammState[11]),
495
- isMarketClosed: indexPrices[2] || indexPrices[3],
496
- };
497
- return state;
498
- }
499
-
500
- /**
501
- * Liquidation price
502
- * @param symbol symbol of the form BTC-USD-MATIC
503
- * @param traderState BigInt array according to smart contract
504
- * @param S2 number, index price S2
505
- * @param symbolToPerpStaticInfo mapping symbol->PerpStaticInfo
506
- * @returns liquidation mark-price, corresponding collateral/quote conversion
507
- */
508
- protected static _calculateLiquidationPrice(
509
- symbol: string,
510
- traderState: BigNumber[],
511
- S2: number,
512
- symbolToPerpStaticInfo: Map<string, PerpetualStaticInfo>
513
- ): [number, number, number, number, number] {
514
- const idx_availableCashCC = 2;
515
- const idx_cash = 3;
516
- const idx_notional = 4;
517
- const idx_locked_in = 5;
518
- const idx_mark_price = 8;
519
- const idx_s3 = 9;
520
- const idx_s2 = 10;
521
- let S2Liq: number;
522
- let S3Liq: number = ABK64x64ToFloat(traderState[idx_s3]);
523
- let perpInfo: PerpetualStaticInfo | undefined = symbolToPerpStaticInfo.get(symbol);
524
- if (perpInfo == undefined) {
525
- throw new Error(`no info for perpetual ${symbol}`);
526
- }
527
- let tau = perpInfo.maintenanceMarginRate;
528
- let lockedInValueQC = ABK64x64ToFloat(traderState[idx_locked_in]);
529
- let position = ABK64x64ToFloat(traderState[idx_notional]);
530
- let cashCC = ABK64x64ToFloat(traderState[idx_availableCashCC]);
531
- let Sm = ABK64x64ToFloat(traderState[idx_mark_price]);
532
- let unpaidFundingCC = ABK64x64ToFloat(traderState[idx_availableCashCC].sub(traderState[idx_cash]));
533
- let unpaidFunding = unpaidFundingCC;
534
-
535
- if (perpInfo.collateralCurrencyType == CollaterlCCY.BASE) {
536
- S2Liq = calculateLiquidationPriceCollateralBase(lockedInValueQC, position, cashCC, tau);
537
- S3Liq = S2Liq;
538
- unpaidFunding = unpaidFunding / S2;
539
- } else if (perpInfo.collateralCurrencyType == CollaterlCCY.QUANTO) {
540
- let S3 = S3Liq;
541
- S3Liq = S3;
542
- S2Liq = calculateLiquidationPriceCollateralQuanto(lockedInValueQC, position, cashCC, tau, S3, Sm);
543
- unpaidFunding = unpaidFunding / S3;
544
- } else {
545
- S2Liq = calculateLiquidationPriceCollateralQuote(lockedInValueQC, position, cashCC, tau);
546
- }
547
- // floor at 0
548
- S2Liq = S2Liq < 0 ? 0 : S2Liq;
549
- S3Liq = S3Liq && S3Liq < 0 ? 0 : S3Liq;
550
- // account cash + pnl = avail cash + pos Sm - L = margin balance
551
- let pnl = position * Sm - lockedInValueQC + unpaidFunding;
552
- return [S2Liq, S3Liq, tau, pnl, unpaidFundingCC];
553
- }
554
-
555
- /**
556
- * Finds the perpetual id for a symbol of the form
557
- * <base>-<quote>-<collateral>. The function first converts the
558
- * token names into bytes4 representation
559
- * @param symbol symbol (e.g., BTC-USD-MATC)
560
- * @param symbolToPerpStaticInfo map that contains the bytes4-symbol to PerpetualStaticInfo
561
- * including id mapping
562
- * @returns perpetual id or it fails
563
- */
564
- protected static symbolToPerpetualId(
565
- symbol: string,
566
- symbolToPerpStaticInfo: Map<string, PerpetualStaticInfo>
567
- ): number {
568
- let id = symbolToPerpStaticInfo.get(symbol)?.id;
569
- if (id == undefined) {
570
- throw Error(`No perpetual found for symbol ${symbol}`);
571
- }
572
- return id;
573
- }
574
-
575
- /**
576
- * Find the long symbol ("ETH-USD-MATIC") of the given perpetual id
577
- * @param id perpetual id
578
- * @param symbolToPerpStaticInfo map that contains the bytes4-symbol to PerpetualStaticInfo
579
- * @returns symbol string or undefined
580
- */
581
- protected static perpetualIdToSymbol(
582
- id: number,
583
- symbolToPerpStaticInfo: Map<string, PerpetualStaticInfo>
584
- ): string | undefined {
585
- let symbol;
586
- for (symbol of symbolToPerpStaticInfo.keys()) {
587
- if (symbolToPerpStaticInfo.get(symbol)?.id == id) {
588
- return symbol;
589
- }
590
- }
591
- return undefined;
592
- }
593
-
594
- protected static symbolToBytes4Symbol(symbol: string): string {
595
- //split by dashes BTC-USD-MATIC
596
- let symbols: string[] = symbol.split("-");
597
- if (symbols.length != 3) {
598
- throw Error(`Symbol ${symbol} not valid. Expecting CCY-CCY-CCY format`);
599
- }
600
- //transform into bytes4 currencies (without the space): "BTC", "USD", "MATC"
601
- symbols = symbols.map((x) => {
602
- let v = to4Chars(x);
603
- v = v.replace(/\0/g, "");
604
- return v;
605
- });
606
- // concatenate and find perpetual Id in map
607
- return symbols[0] + "-" + symbols[1] + "-" + symbols[2];
608
- }
609
-
610
- private static _getByValue(map: any, searchValue: any, valueField: any) {
611
- for (let [key, value] of map.entries()) {
612
- if (value[valueField] === searchValue) {
613
- return key;
614
- }
615
- }
616
- return undefined;
617
- }
618
-
619
- protected static fromSmartContractOrder(
620
- order: SmartContractOrder,
621
- symbolToPerpInfoMap: Map<string, PerpetualStaticInfo>
622
- ): Order {
623
- // find symbol of perpetual id
624
- let symbol = PerpetualDataHandler._getByValue(symbolToPerpInfoMap, order.iPerpetualId, "id");
625
- if (symbol == undefined) {
626
- throw Error(`Perpetual id ${order.iPerpetualId} not found. Check with marketData.exchangeInfo().`);
627
- }
628
- let side = order.fAmount > 0 ? BUY_SIDE : SELL_SIDE;
629
- let limitPrice, stopPrice;
630
- let fLimitPrice: BigNumber | undefined = BigNumber.from(order.fLimitPrice);
631
- if (fLimitPrice.eq(0)) {
632
- limitPrice = side == BUY_SIDE ? undefined : 0;
633
- } else if (fLimitPrice.eq(MAX_64x64)) {
634
- limitPrice = side == BUY_SIDE ? Infinity : undefined;
635
- } else {
636
- limitPrice = ABK64x64ToFloat(fLimitPrice);
637
- }
638
- let fStopPrice: BigNumber | undefined = BigNumber.from(order.fTriggerPrice);
639
- if (fStopPrice.eq(0) || fStopPrice.eq(MAX_64x64)) {
640
- stopPrice = undefined;
641
- } else {
642
- stopPrice = ABK64x64ToFloat(fStopPrice);
643
- }
644
- let userOrder: Order = {
645
- symbol: symbol!,
646
- side: side,
647
- type: PerpetualDataHandler._flagToOrderType(order),
648
- quantity: Math.abs(ABK64x64ToFloat(BigNumber.from(order.fAmount))),
649
- reduceOnly: containsFlag(BigNumber.from(order.flags), MASK_CLOSE_ONLY),
650
- limitPrice: limitPrice,
651
- keepPositionLvg: containsFlag(BigNumber.from(order.flags), MASK_KEEP_POS_LEVERAGE),
652
- brokerFeeTbps: order.brokerFeeTbps == 0 ? undefined : Number(order.brokerFeeTbps),
653
- brokerAddr: order.brokerAddr == ZERO_ADDRESS ? undefined : order.brokerAddr,
654
- brokerSignature: order.brokerSignature == "0x" ? undefined : order.brokerSignature,
655
- stopPrice: stopPrice,
656
- leverage: ABK64x64ToFloat(BigNumber.from(order.fLeverage)),
657
- deadline: Number(order.iDeadline),
658
- timestamp: Number(order.createdTimestamp),
659
- submittedBlock: Number(order.submittedBlock),
660
- };
661
- return userOrder;
662
- }
663
- /**
664
- * Transform the convenient form of the order into a smart-contract accepted type of order
665
- * @param order order type
666
- * @param traderAddr address of the trader
667
- * @param symbolToPerpetualMap mapping of symbol to perpetual Id
668
- * @returns SmartContractOrder
669
- */
670
- protected static toSmartContractOrder(
671
- order: Order,
672
- traderAddr: string,
673
- perpStaticInfo: Map<string, PerpetualStaticInfo>
674
- ): SmartContractOrder {
675
- let flags = PerpetualDataHandler._orderTypeToFlag(order);
676
-
677
- let brokerSig = order.brokerSignature == undefined ? [] : order.brokerSignature;
678
- let perpetualId = PerpetualDataHandler.symbolToPerpetualId(order.symbol, perpStaticInfo);
679
- let fAmount: BigNumber;
680
- if (order.side == BUY_SIDE) {
681
- fAmount = floatToABK64x64(Math.abs(order.quantity));
682
- } else if (order.side == SELL_SIDE) {
683
- fAmount = floatToABK64x64(-Math.abs(order.quantity));
684
- } else {
685
- throw Error(`invalid side in order spec, use ${BUY_SIDE} or ${SELL_SIDE}`);
686
- }
687
- let fLimitPrice: BigNumber;
688
- if (order.limitPrice == undefined) {
689
- // we need to set the limit price to infinity or zero for
690
- // the trade to go through
691
- // Also: stop orders always have limits set, so even for this case
692
- // we set the limit to 0 or infinity
693
- fLimitPrice = order.side == BUY_SIDE ? MAX_64x64 : BigNumber.from(0);
694
- } else {
695
- fLimitPrice = floatToABK64x64(order.limitPrice);
696
- }
697
-
698
- let iDeadline = order.deadline == undefined ? Date.now() / 1000 + ORDER_MAX_DURATION_SEC : order.deadline;
699
- let fTriggerPrice = order.stopPrice == undefined ? BigNumber.from(0) : floatToABK64x64(order.stopPrice);
700
-
701
- let smOrder: SmartContractOrder = {
702
- flags: flags,
703
- iPerpetualId: BigNumber.from(perpetualId),
704
- brokerFeeTbps: order.brokerFeeTbps == undefined ? BigNumber.from(0) : BigNumber.from(order.brokerFeeTbps),
705
- traderAddr: traderAddr,
706
- brokerAddr: order.brokerAddr == undefined ? ZERO_ADDRESS : order.brokerAddr,
707
- referrerAddr: ZERO_ADDRESS,
708
- brokerSignature: brokerSig,
709
- fAmount: fAmount,
710
- fLimitPrice: fLimitPrice,
711
- fTriggerPrice: fTriggerPrice,
712
- fLeverage: order.leverage == undefined ? BigNumber.from(0) : floatToABK64x64(order.leverage),
713
- iDeadline: BigNumber.from(Math.round(iDeadline)),
714
- createdTimestamp: BigNumber.from(Math.round(order.timestamp)),
715
- submittedBlock: 0,
716
- };
717
- return smOrder;
718
- }
719
-
720
- /**
721
- * Converts a smart contract order to a client order
722
- * @param scOrder Smart contract order
723
- * @param parentChildIds Optional parent-child dependency
724
- * @returns Client order that can be submitted to the corresponding LOB
725
- */
726
- public static fromSmartContratOrderToClientOrder(
727
- scOrder: SmartContractOrder,
728
- parentChildIds?: [string, string]
729
- ): ClientOrder {
730
- return {
731
- flags: scOrder.flags,
732
- iPerpetualId: scOrder.iPerpetualId,
733
- brokerFeeTbps: scOrder.brokerFeeTbps,
734
- traderAddr: scOrder.traderAddr,
735
- brokerAddr: scOrder.brokerAddr,
736
- referrerAddr: scOrder.referrerAddr,
737
- brokerSignature: scOrder.brokerSignature,
738
- fAmount: scOrder.fAmount,
739
- fLimitPrice: scOrder.fLimitPrice,
740
- fTriggerPrice: scOrder.fTriggerPrice,
741
- fLeverage: scOrder.fLeverage,
742
- iDeadline: scOrder.iDeadline,
743
- createdTimestamp: scOrder.createdTimestamp,
744
- parentChildDigest1: parentChildIds ? parentChildIds[0] : ZERO_ORDER_ID,
745
- parentChildDigest2: parentChildIds ? parentChildIds[1] : ZERO_ORDER_ID,
746
- };
747
- }
748
-
749
- /**
750
- * Converts a user-friendly order to a client order
751
- * @param order Order
752
- * @param parentChildIds Optional parent-child dependency
753
- * @returns Client order that can be submitted to the corresponding LOB
754
- */
755
- public static toClientOrder(
756
- order: Order,
757
- traderAddr: string,
758
- perpStaticInfo: Map<string, PerpetualStaticInfo>,
759
- parentChildIds?: [string, string]
760
- ): ClientOrder {
761
- const scOrder = PerpetualDataHandler.toSmartContractOrder(order, traderAddr, perpStaticInfo);
762
- return PerpetualDataHandler.fromSmartContratOrderToClientOrder(scOrder, parentChildIds);
763
- }
764
-
765
- /**
766
- * Converts an order as stored in the LOB smart contract into a user-friendly order type
767
- * @param obOrder Order-book contract order type
768
- * @returns User friendly order struct
769
- */
770
- public static fromClientOrder(obOrder: ClientOrder, perpStaticInfo: Map<string, PerpetualStaticInfo>): Order {
771
- const scOrder = {
772
- flags: obOrder.flags,
773
- iPerpetualId: obOrder.iPerpetualId,
774
- brokerFeeTbps: obOrder.brokerFeeTbps,
775
- traderAddr: obOrder.traderAddr,
776
- brokerAddr: obOrder.brokerAddr,
777
- referrerAddr: obOrder.referrerAddr,
778
- brokerSignature: obOrder.brokerSignature,
779
- fAmount: obOrder.fAmount,
780
- fLimitPrice: obOrder.fLimitPrice,
781
- fTriggerPrice: obOrder.fTriggerPrice,
782
- fLeverage: obOrder.fLeverage,
783
- iDeadline: obOrder.iDeadline,
784
- createdTimestamp: obOrder.createdTimestamp,
785
- } as SmartContractOrder;
786
- const order = PerpetualDataHandler.fromSmartContractOrder(scOrder, perpStaticInfo);
787
- if (obOrder.parentChildDigest1 != ZERO_ORDER_ID || obOrder.parentChildDigest2 != ZERO_ORDER_ID) {
788
- order.parentChildOrderIds = [obOrder.parentChildDigest1, obOrder.parentChildDigest2];
789
- }
790
- return order;
791
- }
792
-
793
- private static _flagToOrderType(order: SmartContractOrder): string {
794
- let flag = BigNumber.from(order.flags);
795
- let isLimit = containsFlag(flag, MASK_LIMIT_ORDER);
796
- let hasLimit = !BigNumber.from(order.fLimitPrice).eq(0) || !BigNumber.from(order.fLimitPrice).eq(MAX_64x64);
797
- let isStop = containsFlag(flag, MASK_STOP_ORDER);
798
-
799
- if (isStop && hasLimit) {
800
- return ORDER_TYPE_STOP_LIMIT;
801
- } else if (isStop && !hasLimit) {
802
- return ORDER_TYPE_STOP_MARKET;
803
- } else if (isLimit && !isStop) {
804
- return ORDER_TYPE_LIMIT;
805
- } else {
806
- return ORDER_TYPE_MARKET;
807
- }
808
- }
809
-
810
- /**
811
- * Determine the correct order flags based on the order-properties.
812
- * Checks for some misspecifications.
813
- * @param order order type
814
- * @returns BigNumber flags
815
- */
816
- private static _orderTypeToFlag(order: Order): BigNumber {
817
- let flag: BigNumber;
818
- order.type = order.type.toUpperCase();
819
- switch (order.type) {
820
- case ORDER_TYPE_LIMIT:
821
- flag = MASK_LIMIT_ORDER;
822
- break;
823
- case ORDER_TYPE_MARKET:
824
- flag = MASK_MARKET_ORDER;
825
- break;
826
- case ORDER_TYPE_STOP_MARKET:
827
- flag = MASK_STOP_ORDER;
828
- break;
829
- case ORDER_TYPE_STOP_LIMIT:
830
- flag = MASK_STOP_ORDER;
831
- break;
832
- default: {
833
- throw Error(`Order type ${order.type} not found.`);
834
- }
835
- }
836
- if (order.keepPositionLvg != undefined && order.keepPositionLvg) {
837
- flag = combineFlags(flag, MASK_KEEP_POS_LEVERAGE);
838
- }
839
- if (order.reduceOnly != undefined && order.reduceOnly) {
840
- flag = combineFlags(flag, MASK_CLOSE_ONLY);
841
- }
842
- if ((order.type == ORDER_TYPE_LIMIT || order.type == ORDER_TYPE_STOP_LIMIT) && order.limitPrice == undefined) {
843
- throw Error(`Order type ${order.type} requires limit price.`);
844
- }
845
- if ((order.type == ORDER_TYPE_STOP_MARKET || order.type == ORDER_TYPE_STOP_LIMIT) && order.stopPrice == undefined) {
846
- throw Error(`Order type ${order.type} requires trigger price.`);
847
- }
848
- if ((order.type == ORDER_TYPE_MARKET || order.type == ORDER_TYPE_LIMIT) && order.stopPrice != undefined) {
849
- throw Error(`Order type ${order.type} has no trigger price.`);
850
- }
851
- if (order.type != ORDER_TYPE_STOP_LIMIT && order.type != ORDER_TYPE_STOP_MARKET && order.stopPrice != undefined) {
852
- throw Error(`Order type ${order.type} has no trigger price.`);
853
- }
854
- return flag;
855
- }
856
-
857
- protected static _getLotSize(symbol: string, symbolToPerpStaticInfo: Map<string, PerpetualStaticInfo>): number {
858
- let perpInfo: PerpetualStaticInfo | undefined = symbolToPerpStaticInfo.get(symbol);
859
- if (perpInfo == undefined) {
860
- throw new Error(`no info for perpetual ${symbol}`);
861
- }
862
- return perpInfo.lotSizeBC;
863
- }
864
-
865
- protected static _getMinimalPositionSize(
866
- symbol: string,
867
- symbolToPerpStaticInfo: Map<string, PerpetualStaticInfo>
868
- ): number {
869
- return 10 * PerpetualDataHandler._getLotSize(symbol, symbolToPerpStaticInfo);
870
- }
871
-
872
- /**
873
- * Get NodeSDKConfig from a chain ID, known config name, or custom file location..
874
- * @param configNameOrfileLocation Name of a known default config, or chain ID, or json-file with required variables for config
875
- * @param version Config version number. Defaults to highest version if name or chain ID are not unique
876
- * @returns NodeSDKConfig
877
- */
878
- public static readSDKConfig(configNameOrChainIdOrFileLocation: string | number, version?: number): NodeSDKConfig {
879
- let config: NodeSDKConfig | undefined;
880
- if (typeof configNameOrChainIdOrFileLocation === "number") {
881
- // user entered a chain ID
882
- config = this.getConfigByChainId(configNameOrChainIdOrFileLocation, version);
883
- } else if (typeof configNameOrChainIdOrFileLocation === "string") {
884
- if (/\.json$/.test(configNameOrChainIdOrFileLocation)) {
885
- // user entered a string that ends in .json
886
- config = this.getConfigByLocation(configNameOrChainIdOrFileLocation);
887
- } else {
888
- // user entered a name
889
- config = this.getConfigByName(configNameOrChainIdOrFileLocation, version);
890
- }
891
- } else {
892
- // error
893
- throw Error(`Please specify a chain ID, config name, or custom file location.`);
894
- }
895
- if (config == undefined) {
896
- throw Error(`Config ${configNameOrChainIdOrFileLocation} not found.`);
897
- }
898
- return config;
899
- }
900
-
901
- /**
902
- * Get a NodeSDKConfig from its name
903
- * @param name Name of the known config
904
- * @param version Version of the config. Defaults to highest available.
905
- * @returns NodeSDKConfig
906
- */
907
- protected static getConfigByName(name: string, version?: number): NodeSDKConfig | undefined {
908
- let configFile = DEFAULT_CONFIG.filter((c: any) => c.name == name);
909
- if (configFile.length == 0) {
910
- throw Error(`No SDK config found with name ${name}.`);
911
- }
912
- if (configFile.length == 1) {
913
- return configFile[0];
914
- } else {
915
- if (version === undefined) {
916
- configFile = configFile.sort((conf) => -conf.version);
917
- return configFile[0];
918
- } else {
919
- return configFile.find((conf) => conf.version === version);
920
- }
921
- }
922
- }
923
-
924
- /**
925
- * Get a NodeSDKConfig from a json file.
926
- * @param filename Location of the file
927
- * @param version Version of the config. Defaults to highest available.
928
- * @returns NodeSDKConfig
929
- */
930
- protected static getConfigByLocation(filename: string) {
931
- // file path: this throws a warning during build - that's ok, it just won't work in react apps
932
- // eslint-disable-next-line
933
- let configFile = require(filename) as NodeSDKConfig;
934
- loadABIs(configFile);
935
- return configFile;
936
- }
937
-
938
- /**
939
- * Get a NodeSDKConfig from its chain Id
940
- * @param chainId Chain Id
941
- * @param version Version of the config. Defaults to highest available.
942
- * @returns NodeSDKConfig
943
- */
944
- protected static getConfigByChainId(chainId: number, version?: number) {
945
- let configFile = DEFAULT_CONFIG.filter((c: any) => c.chainId == chainId);
946
- if (configFile.length == 0) {
947
- throw Error(`No SDK config found for chain ID ${chainId}.`);
948
- }
949
- if (configFile.length == 1) {
950
- return configFile[0];
951
- } else {
952
- if (version === undefined) {
953
- configFile = configFile.sort((conf) => -conf.version);
954
- return configFile[0];
955
- } else {
956
- return configFile.find((conf) => conf.version === version);
957
- }
958
- }
959
- }
960
-
961
- /**
962
- * Get the ABI of a function in a given contract
963
- * @param contract A contract instance, e.g. this.proxyContract
964
- * @param functionName Name of the function whose ABI we want
965
- * @returns Function ABI as a single JSON string
966
- */
967
- protected static _getABIFromContract(contract: ethers.Contract, functionName: string): string {
968
- const FormatTypes = ethers.utils.FormatTypes;
969
- return contract.interface.getFunction(functionName).format(FormatTypes.full);
970
- }
971
-
972
- /**
973
- * Gets the pool index (in exchangeInfo) corresponding to a given symbol.
974
- * @param symbol Symbol of the form ETH-USD-MATIC
975
- * @returns Pool index
976
- */
977
- public getPoolIndexFromSymbol(symbol: string): number {
978
- let pools = this.poolStaticInfos!;
979
- let poolId = PerpetualDataHandler._getPoolIdFromSymbol(symbol, this.poolStaticInfos);
980
- let k = 0;
981
- while (k < pools.length) {
982
- if (pools[k].poolId == poolId) {
983
- // pool found
984
- return k;
985
- }
986
- k++;
987
- }
988
- return -1;
989
- }
990
-
991
- public getMarginTokenFromSymbol(symbol: string): string | undefined {
992
- let pools = this.poolStaticInfos!;
993
- let poolId = PerpetualDataHandler._getPoolIdFromSymbol(symbol, this.poolStaticInfos);
994
- let k = 0;
995
- while (k < pools.length) {
996
- if (pools[k].poolId == poolId) {
997
- // pool found
998
- return pools[k].poolMarginTokenAddr;
999
- }
1000
- k++;
1001
- }
1002
- return undefined;
1003
- }
1004
-
1005
- public getABI(contract: string): ethers.ContractInterface | undefined {
1006
- switch (contract) {
1007
- case "proxy":
1008
- return this.proxyABI;
1009
- case "lob":
1010
- return this.lobABI;
1011
- default:
1012
- return undefined;
1013
- }
1014
- }
1015
-
1016
- /**
1017
- * Performs basic validity checks on a given order
1018
- * @param order Order struct
1019
- * @param traderAccount Trader account
1020
- * @param perpStaticInfo Symbol to perpetual info map
1021
- */
1022
- protected static checkOrder(
1023
- order: Order,
1024
- traderAccount: MarginAccount,
1025
- perpStaticInfo: Map<string, PerpetualStaticInfo>
1026
- ) {
1027
- // this throws error if not found
1028
- let perpetualId = PerpetualDataHandler.symbolToPerpetualId(order.symbol, perpStaticInfo);
1029
-
1030
- // check side
1031
- if (order.side != BUY_SIDE && order.side != SELL_SIDE) {
1032
- throw Error(`order side must be ${BUY_SIDE} or ${SELL_SIDE}`);
1033
- }
1034
-
1035
- // check amount
1036
- let lotSize = perpStaticInfo.get(order.symbol)!.lotSizeBC;
1037
- let curPos =
1038
- traderAccount.side == CLOSED_SIDE
1039
- ? 0
1040
- : (traderAccount.side == BUY_SIDE ? 1 : -1) * traderAccount.positionNotionalBaseCCY;
1041
- let newPos = curPos + (order.side == BUY_SIDE ? 1 : -1) * order.quantity;
1042
- if (Math.abs(order.quantity) < lotSize || (Math.abs(newPos) >= lotSize && Math.abs(newPos) < 10 * lotSize)) {
1043
- throw Error(`trade amount too small: ${order.quantity} ${perpStaticInfo.get(order.symbol)!.S2Symbol}`);
1044
- }
1045
-
1046
- // check limit price
1047
- if (order.side == BUY_SIDE && order.limitPrice != undefined && order.limitPrice <= 0) {
1048
- throw Error(`invalid limit price for buy order: ${order.limitPrice}`);
1049
- }
1050
-
1051
- // broker fee
1052
- if (order.brokerFeeTbps != undefined && order.brokerFeeTbps < 0) {
1053
- throw Error(`invalid broker fee: ${order.brokerFeeTbps / 10} bps`);
1054
- }
1055
-
1056
- // stop price
1057
- if (order.stopPrice != undefined && order.stopPrice < 0) {
1058
- throw Error(`invalid stop price: ${order.stopPrice}`);
1059
- }
1060
- }
1061
- }