@d8x/perpetuals-sdk 0.6.3 → 0.6.5

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 (48) hide show
  1. package/dist/cjs/version.d.ts +1 -1
  2. package/dist/cjs/version.js +1 -1
  3. package/dist/esm/version.d.ts +1 -1
  4. package/dist/esm/version.js +1 -1
  5. package/package.json +5 -4
  6. package/src/abi/ERC20.json +288 -0
  7. package/src/abi/IPerpetualManager.json +5888 -0
  8. package/src/abi/LimitOrderBook.json +1062 -0
  9. package/src/abi/LimitOrderBookFactory.json +161 -0
  10. package/src/abi/MockTokenSwap.json +186 -0
  11. package/src/abi/ShareToken.json +428 -0
  12. package/src/accountTrade.ts +428 -0
  13. package/src/brokerTool.ts +555 -0
  14. package/src/config/defaultConfig.json +62 -0
  15. package/src/config/mockSwap.json +6 -0
  16. package/src/config/priceFeedConfig.json +104 -0
  17. package/src/config/symbolList.json +13 -0
  18. package/src/contracts/ERC20.ts +444 -0
  19. package/src/contracts/IPerpetualManager.ts +7227 -0
  20. package/src/contracts/LimitOrderBook.ts +1251 -0
  21. package/src/contracts/LimitOrderBookFactory.ts +348 -0
  22. package/src/contracts/MockTokenSwap.ts +373 -0
  23. package/src/contracts/ShareToken.ts +695 -0
  24. package/src/contracts/common.ts +44 -0
  25. package/src/contracts/factories/ERC20__factory.ts +306 -0
  26. package/src/contracts/factories/IPerpetualManager__factory.ts +5912 -0
  27. package/src/contracts/factories/LimitOrderBookFactory__factory.ts +189 -0
  28. package/src/contracts/factories/LimitOrderBook__factory.ts +1086 -0
  29. package/src/contracts/factories/MockTokenSwap__factory.ts +207 -0
  30. package/src/contracts/factories/ShareToken__factory.ts +449 -0
  31. package/src/contracts/factories/index.ts +9 -0
  32. package/src/contracts/index.ts +16 -0
  33. package/src/d8XMath.ts +376 -0
  34. package/src/index.ts +29 -0
  35. package/src/liquidatorTool.ts +270 -0
  36. package/src/liquidityProviderTool.ts +148 -0
  37. package/src/marketData.ts +1310 -0
  38. package/src/nodeSDKTypes.ts +332 -0
  39. package/src/orderReferrerTool.ts +516 -0
  40. package/src/perpetualDataHandler.ts +1161 -0
  41. package/src/perpetualEventHandler.ts +455 -0
  42. package/src/priceFeeds.ts +382 -0
  43. package/src/traderDigests.ts +86 -0
  44. package/src/traderInterface.ts +172 -0
  45. package/src/triangulator.ts +105 -0
  46. package/src/utils.ts +134 -0
  47. package/src/version.ts +1 -0
  48. package/src/writeAccessHandler.ts +139 -0
@@ -0,0 +1,455 @@
1
+ import { BigNumber } from "@ethersproject/bignumber";
2
+ import { emitWarning } from "process";
3
+ import { ABK64x64ToFloat, mul64x64 } from "./d8XMath";
4
+ import MarketData from "./marketData";
5
+ import {
6
+ ExchangeInfo,
7
+ MarginAccount,
8
+ ONE_64x64,
9
+ Order,
10
+ OrderStruct,
11
+ PerpetualState,
12
+ SmartContractOrder,
13
+ TradeEvent,
14
+ } from "./nodeSDKTypes";
15
+
16
+ /**
17
+ * This class handles events and stores relevant variables
18
+ * as member variables. The events change the state of the member variables:
19
+ * mktData : MarketData relevant market data with current state (e.g. index price)
20
+ * ordersInPerpetual: Map<number, OrderStruct> all open orders for the given trader
21
+ * positionInPerpetual: Map<number, MarginAccount> all open positions for the given trader
22
+ *
23
+ *
24
+ * Get data:
25
+ * - getPerpetualData(perp id (string) or symbol) : PerpetualState. This is a reference!
26
+ * - getExchangeInfo() : ExchangeInfo. This is a reference!
27
+ * - getCurrentPositionRisk(perp id (string) or symbol) : MarginAccount. This is a reference!
28
+ * - getOrdersInPerpetualMap : Map<number, OrderStruct>. This is a reference!
29
+ * - getpositionInPerpetualMap : Map<number, MarginAccount>. This is a reference!
30
+ *
31
+ * Construct with a trader address and a marketData object
32
+ * Initialize to gather all the relevant data.
33
+ * Send event variables to event handler "on<EventName>" - this updates members
34
+ * - [x] onUpdateMarkPrice : emitted on proxy; updates markprice and index price data
35
+ * - [x] onUpdateUpdateFundingRate : emitted on proxy; sets funding rate
36
+ * - [x] onExecutionFailed : emitted on order book; removes an open order
37
+ * - [x] onPerpetualLimitOrderCancelled : emitted on order book; removes an open order
38
+ * - [x] onPerpetualLimitOrderCreated : emitted on order book; adds an open order to the data
39
+ * - [x] async onUpdateMarginAccount : emitted on proxy; updates position data and open interest
40
+ * - [x] onTrade : emitted on proxy; returns TradeEvent to be displayed
41
+ */
42
+ export default class PerpetualEventHandler {
43
+ // market data class
44
+ private mktData: MarketData;
45
+ // trader for which the data is tracked
46
+ private traderAddr: string;
47
+
48
+ // perpetual id to trader data
49
+ private ordersInPerpetual: Map<number, OrderStruct>;
50
+ private positionInPerpetual: Map<number, MarginAccount>;
51
+
52
+ // perpetual id to pool index in exchange info
53
+ private poolIndexForPerpetual: Map<number, number>;
54
+
55
+ // keep current state of the system in exchangeInfo
56
+ // data is updated when calling "onEvent"-functions
57
+ private exchangeInfo: ExchangeInfo | undefined;
58
+
59
+ constructor(mktData: MarketData, traderAddr: string) {
60
+ this.mktData = mktData;
61
+ this.traderAddr = traderAddr;
62
+ this.ordersInPerpetual = new Map<number, OrderStruct>();
63
+ this.positionInPerpetual = new Map<number, MarginAccount>();
64
+ this.poolIndexForPerpetual = new Map<number, number>();
65
+ }
66
+
67
+ /**
68
+ * Call this async function to initialize the
69
+ * market data
70
+ */
71
+ public async initialize() {
72
+ this.exchangeInfo = await this.mktData.exchangeInfo();
73
+ // loop through all pools and perpetuals and get
74
+ // open positions and open orders
75
+ for (let k = 0; k < this.exchangeInfo.pools.length; k++) {
76
+ let poolState = this.exchangeInfo.pools[k];
77
+ let poolSymbol = poolState.poolSymbol;
78
+ for (let j = 0; j < poolState.perpetuals.length; j++) {
79
+ let perpState: PerpetualState = poolState.perpetuals[j];
80
+ let perpSymbol = perpState.baseCurrency + "-" + perpState.quoteCurrency + "-" + poolSymbol;
81
+ let orders = await this.mktData.openOrders(this.traderAddr, perpSymbol);
82
+ let perpId = perpState.id;
83
+ this.ordersInPerpetual.set(perpId, orders[0]);
84
+ let position = (await this.mktData.positionRisk(this.traderAddr, perpSymbol))[0];
85
+ this.positionInPerpetual.set(perpId, position);
86
+ this.poolIndexForPerpetual.set(perpId, k);
87
+ }
88
+ }
89
+ }
90
+
91
+ /**
92
+ * Get the current exchange info
93
+ * @returns exchange info
94
+ */
95
+ public getExchangeInfo(): ExchangeInfo | undefined {
96
+ return this.exchangeInfo;
97
+ }
98
+
99
+ /**
100
+ * getOrdersInPerpetualMap
101
+ * @returns this.ordersInPerpetual
102
+ */
103
+ public getOrdersInPerpetualMap(): Map<number, OrderStruct> {
104
+ return this.ordersInPerpetual;
105
+ }
106
+
107
+ /**
108
+ * getpositionInPerpetualMap
109
+ * @returns this.positionInPerpetual
110
+ */
111
+ public getpositionInPerpetualMap(): Map<number, MarginAccount> {
112
+ return this.positionInPerpetual;
113
+ }
114
+
115
+ /**
116
+ * Get the data for a perpetual with a given index
117
+ * @param perpetualIdOrSymbol perpetual idx as string or symbol for which we want the data
118
+ * @returns perpetual data for this idx
119
+ */
120
+ public getPerpetualData(perpetualIdOrSymbol: string): PerpetualState | undefined {
121
+ let perpId = Number(perpetualIdOrSymbol);
122
+ if (isNaN(perpId)) {
123
+ perpId = this.mktData.getPerpIdFromSymbol(perpetualIdOrSymbol);
124
+ }
125
+ //uint24 perpetualId = uint24(_iPoolId) * 100_000 + iPerpetualIndex;
126
+ let poolIdx = this.poolIndexForPerpetual.get(perpId)!; //Math.floor(perpId / 100_000);
127
+ let perpetuals = this.exchangeInfo?.pools[poolIdx].perpetuals;
128
+ if (perpetuals == undefined) {
129
+ emitWarning(`exchangeInfo not found, initialize perpetualEventHandler`);
130
+ return undefined;
131
+ }
132
+ // find perpetual
133
+ let k;
134
+ for (k = 0; k < perpetuals?.length && perpetuals[k].id != perpId; k++);
135
+ if (perpetuals[k].id != perpId) {
136
+ emitWarning(`getPerpetualData: perpetual id ${perpId} not found`);
137
+ return undefined;
138
+ }
139
+ return perpetuals[k];
140
+ }
141
+
142
+ /**
143
+ * Get the trader's current position risk (margin account data)
144
+ * @param perpetualIdOrSymbol perpetual id as string ('100003') or symbol ('BTC-USD-MATIC')
145
+ * @returns undefined if no position or margin account (='position risk')
146
+ */
147
+ public getCurrentPositionRisk(perpetualIdOrSymbol: string): MarginAccount | undefined {
148
+ let perpId = Number(perpetualIdOrSymbol);
149
+ if (isNaN(perpId)) {
150
+ perpId = this.mktData.getPerpIdFromSymbol(perpetualIdOrSymbol);
151
+ }
152
+ return this.positionInPerpetual.get(perpId);
153
+ }
154
+
155
+ /**
156
+ * Update the following prices:
157
+ * - index price
158
+ * - collateral price
159
+ * - mid-price
160
+ * @param perpetualIdOrSymbol perpetual id as string ('100003') or symbol ('BTC-USD-MATIC')
161
+ */
162
+ public async updatePrices(perpetualIdOrSymbol: string) {
163
+ let perpId = Number(perpetualIdOrSymbol);
164
+ let symbol = perpetualIdOrSymbol;
165
+ if (!isNaN(perpId)) {
166
+ let sym = this.mktData.getSymbolFromPerpId(perpId);
167
+ if (sym == undefined) {
168
+ throw new Error(`Symbol not found for perpetual ${perpId}`);
169
+ }
170
+ symbol = sym;
171
+ }
172
+ let perpState: PerpetualState = await this.mktData.getPerpetualState(symbol);
173
+ let perp = this.getPerpetualData(symbol);
174
+ if (perp == undefined) {
175
+ throw new Error(`Perpetual not found: ${symbol}`);
176
+ }
177
+ perp.state = perpState.state;
178
+ perp.indexPrice = perpState.indexPrice;
179
+ perp.collToQuoteIndexPrice = perpState.collToQuoteIndexPrice;
180
+ perp.markPrice = perpState.markPrice;
181
+ perp.midPrice = perpState.midPrice;
182
+ perp.currentFundingRateBps = perpState.currentFundingRateBps;
183
+ perp.openInterestBC = perpState.openInterestBC;
184
+ perp.indexPrice = perpState.indexPrice;
185
+ perp.collToQuoteIndexPrice = perpState.collToQuoteIndexPrice;
186
+ }
187
+
188
+ /**
189
+ * Handle the event UpdateMarkPrice and update relevant
190
+ * data
191
+ * @param perpetualId perpetual Id
192
+ * @param fMarkPricePremium premium rate in ABDK format
193
+ * @param fSpotIndexPrice spot index price in ABDK format
194
+ * @returns void
195
+ */
196
+ public onUpdateMarkPrice(
197
+ perpetualId: number,
198
+ fMidPricePremium: BigNumber,
199
+ fMarkPricePremium: BigNumber,
200
+ fSpotIndexPrice: BigNumber
201
+ ): void {
202
+ let [newMidPrice, newMarkPrice, newIndexPrice] = PerpetualEventHandler.ConvertUpdateMarkPrice(
203
+ fMidPricePremium,
204
+ fMarkPricePremium,
205
+ fSpotIndexPrice
206
+ );
207
+ let perpetual = this.getPerpetualData(perpetualId.toString());
208
+ if (perpetual == undefined) {
209
+ return;
210
+ }
211
+ perpetual.midPrice = newMidPrice;
212
+ perpetual.markPrice = newMarkPrice;
213
+ perpetual.indexPrice = newIndexPrice;
214
+ }
215
+
216
+ /**
217
+ * Handle the event UpdateFundingRate and update relevant
218
+ * data
219
+ * UpdateFundingRate(uint24 indexed perpetualId, int128 fFundingRate)
220
+ * @param fFundingRate funding rate in ABDK format
221
+ */
222
+ public onUpdateUpdateFundingRate(perpetualId: number, fFundingRate: BigNumber): void {
223
+ let newRate = ABK64x64ToFloat(fFundingRate);
224
+ let perpetual = this.getPerpetualData(perpetualId.toString());
225
+ if (perpetual == undefined) {
226
+ return;
227
+ }
228
+ perpetual.currentFundingRateBps = newRate * 1e4;
229
+ }
230
+
231
+ /**
232
+ * event ExecutionFailed(
233
+ uint24 indexed perpetualId,
234
+ address indexed trader,
235
+ bytes32 digest,
236
+ string reason
237
+ );
238
+ * @param perpetualId id of the perpetual
239
+ * @param trader address of the trader
240
+ * @param digest digest of the order/cancel order
241
+ * @param reason reason why the execution failed
242
+ */
243
+ public onExecutionFailed(perpetualId: number, trader: string, digest: string, reason: string) {
244
+ if (trader != this.traderAddr) {
245
+ emitWarning(`onExecutionFailed: trader ${trader} not relevant`);
246
+ return;
247
+ }
248
+ // remove order from open orders
249
+ let orderStructs:
250
+ | {
251
+ orders: Order[];
252
+ orderIds: string[];
253
+ }
254
+ | undefined = this.ordersInPerpetual.get(perpetualId);
255
+ if (orderStructs == undefined) {
256
+ emitWarning(`onExecutionFailed: no order found for perpetual ${perpetualId}`);
257
+ return;
258
+ }
259
+ if (reason == "cancel delay required") {
260
+ // canceling failed. We don't remove the order
261
+ return;
262
+ }
263
+ PerpetualEventHandler.deleteOrder(orderStructs, digest);
264
+ }
265
+
266
+ /**
267
+ * Event emitted by perpetual proxy
268
+ * event PerpetualLimitOrderCancelled(bytes32 indexed orderHash);
269
+ * @param orderId string order id/digest
270
+ */
271
+ public onPerpetualLimitOrderCancelled(orderId: string) {
272
+ // remove order from open orders
273
+ let perpId: number | undefined = PerpetualEventHandler.findOrderForId(orderId, this.ordersInPerpetual);
274
+ if (perpId == undefined) {
275
+ emitWarning(`onPerpetualLimitOrderCancelled: no order found with id ${orderId}`);
276
+ return;
277
+ }
278
+ let orderStruct: OrderStruct | undefined = this.ordersInPerpetual.get(perpId);
279
+ PerpetualEventHandler.deleteOrder(orderStruct!, orderId);
280
+ }
281
+
282
+ /**
283
+ * event PerpetualLimitOrderCreated(
284
+ * uint24 indexed perpetualId,
285
+ * address indexed trader,
286
+ * address referrerAddr,
287
+ * address brokerAddr,
288
+ * Order order,
289
+ * bytes32 digest
290
+ *)
291
+ * @param perpetualId id of the perpetual
292
+ * @param trader address of the trader
293
+ * @param referrerAddr address of the referrer
294
+ * @param brokerAddr address of the broker
295
+ * @param Order order struct
296
+ * @param digest order id
297
+ */
298
+ public onPerpetualLimitOrderCreated(
299
+ perpetualId: number,
300
+ trader: string,
301
+ _referrerAddr: string,
302
+ _brokerAddr: string,
303
+ Order: SmartContractOrder,
304
+ digest: string
305
+ ): void {
306
+ if (trader != this.traderAddr) {
307
+ emitWarning(`onPerpetualLimitOrderCreated: trader ${trader} not relevant`);
308
+ return;
309
+ }
310
+ let order: Order = this.mktData.smartContractOrderToOrder(Order);
311
+ let orderStruct: OrderStruct | undefined = this.ordersInPerpetual.get(perpetualId);
312
+ if (orderStruct == undefined) {
313
+ // no order for this perpetual so far
314
+ this.ordersInPerpetual.set(perpetualId, { orders: [order], orderIds: [digest] });
315
+ } else {
316
+ orderStruct.orderIds.push(digest);
317
+ orderStruct.orders.push(order);
318
+ }
319
+ }
320
+
321
+ /**
322
+ * This function is async -> queries the margin account
323
+ * @param perpetualId id of the perpetual
324
+ * @param trader trader address
325
+ * @param positionId position id
326
+ * @param fPositionBC position size in base currency
327
+ * @param fCashCC margin collateral in margin account
328
+ * @param fLockedInValueQC pos*average opening price
329
+ * @param fFundingPaymentCC funding payment made
330
+ * @param fOpenInterestBC open interest
331
+ */
332
+ public async onUpdateMarginAccount(
333
+ perpetualId: number,
334
+ trader: string,
335
+ _positionId: string,
336
+ _fPositionBC: BigNumber,
337
+ _fCashCC: BigNumber,
338
+ _fLockedInValueQC: BigNumber,
339
+ _fFundingPaymentCC: BigNumber,
340
+ fOpenInterestBC: BigNumber
341
+ ): Promise<void> {
342
+ let perpetual = this.getPerpetualData(perpetualId.toString());
343
+ if (perpetual == undefined) {
344
+ emitWarning(`onUpdateMarginAccount: perpetual ${perpetualId} not found`);
345
+ return;
346
+ }
347
+ perpetual.openInterestBC = ABK64x64ToFloat(fOpenInterestBC);
348
+ if (trader != this.traderAddr) {
349
+ return;
350
+ }
351
+ let perpetualIdStr = perpetualId.toString();
352
+ let margin = (await this.mktData.positionRisk(this.traderAddr, perpetualIdStr))[0];
353
+ this.positionInPerpetual.set(perpetualId, margin);
354
+ }
355
+
356
+ /**
357
+ *
358
+ * @param perpetualId perpetual id
359
+ * @param trader trader address
360
+ * @param positionId position id
361
+ * @param order order struct
362
+ * @param orderDigest order id
363
+ * @param newPositionSizeBC new pos size in base currency ABDK
364
+ * @param price price in ABDK format
365
+ * @returns trade event data in regular number format
366
+ */
367
+ public onTrade(
368
+ perpetualId: number,
369
+ _trader: string,
370
+ positionId: string,
371
+ _order: SmartContractOrder,
372
+ orderDigest: string,
373
+ newPositionSizeBC: BigNumber,
374
+ _price: BigNumber
375
+ ): TradeEvent {
376
+ // remove order digest from open orders
377
+ let orderStructs = this.ordersInPerpetual.get(perpetualId);
378
+ if (orderStructs == undefined) {
379
+ emitWarning(`onTrade: executed order not found ${orderDigest}`);
380
+ } else {
381
+ PerpetualEventHandler.deleteOrder(orderStructs, orderDigest);
382
+ }
383
+ // return transformed trade info
384
+ return {
385
+ perpetualId: perpetualId,
386
+ positionId: positionId,
387
+ orderId: orderDigest,
388
+ newPositionSizeBC: ABK64x64ToFloat(newPositionSizeBC),
389
+ executionPrice: ABK64x64ToFloat(newPositionSizeBC),
390
+ };
391
+ }
392
+
393
+ /**
394
+ * static function to find the number of the OrderStruct with given orderId
395
+ * @param orderId id/digest of order
396
+ * @param orderMap mapping for perpetualId->OrderStruct
397
+ * @returns id of perpetual that contains order with id = orderId or undefined
398
+ */
399
+ private static findOrderForId(orderId: string, orderMap: Map<number, OrderStruct>): number | undefined {
400
+ /*orderMapMap<number, {
401
+ orders: Order[];
402
+ orderIds: string[];*/
403
+ for (const perpId of orderMap.keys()) {
404
+ let orderStructs = orderMap.get(perpId);
405
+ if (orderStructs?.orderIds.includes(orderId)) {
406
+ return perpId;
407
+ }
408
+ }
409
+ return undefined;
410
+ }
411
+
412
+ /**
413
+ * Delete the order with given id from the class member data
414
+ * @param orderStructs array of order struct as stored for the trader and a given perpetual
415
+ * @param orderId digest/order id
416
+ * @returns void
417
+ */
418
+ private static deleteOrder(orderStructs: OrderStruct, orderId: string): void {
419
+ // find order
420
+ let k;
421
+ for (k = 0; k < orderStructs.orderIds.length && orderStructs.orderIds[k] != orderId; k++);
422
+ if (orderStructs.orderIds[k] != orderId) {
423
+ emitWarning(`deleteOrder: no order found with digest ${orderId}`);
424
+ return;
425
+ }
426
+ // delete element k on reference of orders
427
+ orderStructs.orders[k] = orderStructs.orders[orderStructs.orders.length - 1];
428
+ orderStructs.orders.pop();
429
+ orderStructs.orderIds[k] = orderStructs.orderIds[orderStructs.orderIds.length - 1];
430
+ orderStructs.orderIds.pop();
431
+ }
432
+
433
+ /**
434
+ * UpdateMarkPrice(
435
+ * uint24 indexed perpetualId,
436
+ * int128 fMarkPricePremium,
437
+ * int128 fSpotIndexPrice
438
+ * )
439
+ * @param fMarkPricePremium premium rate in ABDK format
440
+ * @param fSpotIndexPrice spot index price in ABDK format
441
+ * @returns mark price and spot index in float
442
+ */
443
+ private static ConvertUpdateMarkPrice(
444
+ fMidPricePremium: BigNumber,
445
+ fMarkPricePremium: BigNumber,
446
+ fSpotIndexPrice: BigNumber
447
+ ): [number, number, number] {
448
+ let fMarkPrice = mul64x64(fSpotIndexPrice, ONE_64x64.add(fMarkPricePremium));
449
+ let fMidPrice = mul64x64(fSpotIndexPrice, ONE_64x64.add(fMidPricePremium));
450
+ let midPrice = ABK64x64ToFloat(fMidPrice);
451
+ let markPrice = ABK64x64ToFloat(fMarkPrice);
452
+ let indexPrice = ABK64x64ToFloat(fSpotIndexPrice);
453
+ return [midPrice, markPrice, indexPrice];
454
+ }
455
+ }