@drift-labs/sdk 0.2.0-master.31 → 0.2.0-master.33

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 (99) hide show
  1. package/lib/accounts/bulkAccountLoader.js +2 -1
  2. package/lib/accounts/pollingClearingHouseAccountSubscriber.d.ts +2 -2
  3. package/lib/accounts/pollingClearingHouseAccountSubscriber.js +8 -8
  4. package/lib/accounts/types.d.ts +1 -0
  5. package/lib/accounts/webSocketClearingHouseAccountSubscriber.d.ts +2 -2
  6. package/lib/accounts/webSocketClearingHouseAccountSubscriber.js +7 -7
  7. package/lib/addresses/marketAddresses.js +1 -1
  8. package/lib/addresses/pda.d.ts +4 -4
  9. package/lib/addresses/pda.js +23 -22
  10. package/lib/admin.d.ts +30 -32
  11. package/lib/admin.js +111 -119
  12. package/lib/clearingHouse.d.ts +43 -34
  13. package/lib/clearingHouse.js +353 -193
  14. package/lib/clearingHouseConfig.d.ts +2 -2
  15. package/lib/clearingHouseUser.d.ts +26 -5
  16. package/lib/clearingHouseUser.js +151 -51
  17. package/lib/config.d.ts +2 -0
  18. package/lib/config.js +5 -1
  19. package/lib/constants/numericConstants.d.ts +1 -0
  20. package/lib/constants/numericConstants.js +3 -2
  21. package/lib/dlob/DLOB.d.ts +19 -11
  22. package/lib/dlob/DLOB.js +208 -107
  23. package/lib/dlob/DLOBNode.js +2 -10
  24. package/lib/dlob/NodeList.d.ts +1 -0
  25. package/lib/dlob/NodeList.js +6 -1
  26. package/lib/events/eventSubscriber.d.ts +1 -0
  27. package/lib/events/eventSubscriber.js +11 -4
  28. package/lib/events/fetchLogs.d.ts +3 -1
  29. package/lib/events/fetchLogs.js +13 -5
  30. package/lib/events/pollingLogProvider.js +1 -1
  31. package/lib/events/types.d.ts +1 -1
  32. package/lib/events/webSocketLogProvider.js +1 -1
  33. package/lib/factory/bigNum.d.ts +5 -4
  34. package/lib/factory/bigNum.js +36 -6
  35. package/lib/idl/clearing_house.json +1527 -1242
  36. package/lib/index.d.ts +3 -0
  37. package/lib/index.js +3 -0
  38. package/lib/math/amm.js +9 -9
  39. package/lib/math/exchangeStatus.d.ts +4 -0
  40. package/lib/math/exchangeStatus.js +18 -0
  41. package/lib/math/funding.js +10 -10
  42. package/lib/math/margin.js +6 -1
  43. package/lib/math/market.js +9 -9
  44. package/lib/math/orders.d.ts +7 -3
  45. package/lib/math/orders.js +39 -31
  46. package/lib/math/repeg.js +3 -3
  47. package/lib/math/spotBalance.js +3 -3
  48. package/lib/math/spotPosition.js +2 -2
  49. package/lib/serum/serumFulfillmentConfigMap.d.ts +10 -0
  50. package/lib/serum/serumFulfillmentConfigMap.js +17 -0
  51. package/lib/serum/serumSubscriber.d.ts +4 -0
  52. package/lib/serum/serumSubscriber.js +16 -1
  53. package/lib/types.d.ts +70 -85
  54. package/lib/types.js +12 -11
  55. package/lib/userMap/userMap.d.ts +17 -1
  56. package/lib/userMap/userMap.js +12 -0
  57. package/lib/userName.d.ts +1 -0
  58. package/lib/userName.js +3 -2
  59. package/package.json +1 -1
  60. package/src/accounts/bulkAccountLoader.ts +5 -1
  61. package/src/accounts/pollingClearingHouseAccountSubscriber.ts +9 -9
  62. package/src/accounts/webSocketClearingHouseAccountSubscriber.ts +8 -8
  63. package/src/addresses/marketAddresses.ts +2 -2
  64. package/src/addresses/pda.ts +20 -20
  65. package/src/admin.ts +246 -221
  66. package/src/clearingHouse.ts +556 -236
  67. package/src/clearingHouseConfig.ts +2 -2
  68. package/src/clearingHouseUser.ts +237 -87
  69. package/src/config.ts +8 -1
  70. package/src/constants/numericConstants.ts +5 -1
  71. package/src/dlob/DLOB.ts +290 -120
  72. package/src/dlob/DLOBNode.ts +2 -14
  73. package/src/dlob/NodeList.ts +7 -1
  74. package/src/events/eventSubscriber.ts +18 -4
  75. package/src/events/fetchLogs.ts +20 -5
  76. package/src/events/pollingLogProvider.ts +1 -1
  77. package/src/events/types.ts +2 -1
  78. package/src/events/webSocketLogProvider.ts +1 -1
  79. package/src/factory/bigNum.ts +59 -6
  80. package/src/idl/clearing_house.json +1527 -1242
  81. package/src/idl/pyth.json +98 -2
  82. package/src/index.ts +3 -0
  83. package/src/math/amm.ts +9 -9
  84. package/src/math/exchangeStatus.ts +31 -0
  85. package/src/math/funding.ts +20 -10
  86. package/src/math/margin.ts +7 -1
  87. package/src/math/market.ts +9 -9
  88. package/src/math/orders.ts +44 -29
  89. package/src/math/repeg.ts +3 -3
  90. package/src/math/spotBalance.ts +4 -4
  91. package/src/math/spotPosition.ts +2 -2
  92. package/src/serum/serumFulfillmentConfigMap.ts +26 -0
  93. package/src/serum/serumSubscriber.ts +20 -1
  94. package/src/types.ts +75 -61
  95. package/src/userMap/userMap.ts +25 -1
  96. package/src/userName.ts +2 -1
  97. package/tests/bn/test.ts +22 -1
  98. package/tests/dlob/helpers.ts +252 -81
  99. package/tests/dlob/test.ts +1115 -215
package/lib/dlob/DLOB.js CHANGED
@@ -3,19 +3,23 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DLOB = void 0;
4
4
  const NodeList_1 = require("./NodeList");
5
5
  const __1 = require("..");
6
+ const exchangeStatus_1 = require("../math/exchangeStatus");
6
7
  class DLOB {
7
8
  /**
8
9
  *
9
10
  * @param perpMarkets The perp markets to maintain a DLOB for
10
11
  * @param spotMarkets The spot markets to maintain a DLOB for
12
+ * @param userMap map of all users
11
13
  * @param silent set to true to prevent logging on inserts and removals
12
14
  */
13
- constructor(perpMarkets, spotMarkets, silent) {
15
+ constructor(perpMarkets, spotMarkets, stateAccount, userMap, silent) {
14
16
  this.openOrders = new Map();
15
17
  this.orderLists = new Map();
16
18
  this.marketIndexToAccount = new Map();
17
19
  this.silent = false;
18
20
  this.initialized = false;
21
+ this.stateAccount = stateAccount;
22
+ this.userMap = userMap;
19
23
  this.silent = silent;
20
24
  this.openOrders.set('perp', new Set());
21
25
  this.openOrders.set('spot', new Set());
@@ -68,35 +72,44 @@ class DLOB {
68
72
  });
69
73
  }
70
74
  }
75
+ clear() {
76
+ for (const marketType of this.openOrders.keys()) {
77
+ this.openOrders.get(marketType).clear();
78
+ }
79
+ this.openOrders.clear();
80
+ for (const marketType of this.orderLists.keys()) {
81
+ for (const marketIndex of this.orderLists.get(marketType).keys()) {
82
+ const marketNodeLists = this.orderLists
83
+ .get(marketType)
84
+ .get(marketIndex);
85
+ for (const side of Object.keys(marketNodeLists)) {
86
+ for (const orderType of Object.keys(marketNodeLists[side])) {
87
+ marketNodeLists[side][orderType].clear();
88
+ }
89
+ }
90
+ }
91
+ }
92
+ this.orderLists.clear();
93
+ for (const marketType of this.marketIndexToAccount.keys()) {
94
+ this.marketIndexToAccount.get(marketType).clear();
95
+ }
96
+ this.marketIndexToAccount.clear();
97
+ }
71
98
  /**
72
99
  * initializes a new DLOB instance
73
100
  *
74
- * @param clearingHouse The ClearingHouse instance to use for price data
75
101
  * @returns a promise that resolves when the DLOB is initialized
76
102
  */
77
- async init(clearingHouse, userMap) {
103
+ async init() {
78
104
  if (this.initialized) {
79
105
  return false;
80
106
  }
81
- if (userMap) {
82
- // initialize the dlob with the user map (prevents hitting getProgramAccounts)
83
- for (const user of userMap.values()) {
84
- const userAccount = user.getUserAccount();
85
- const userAccountPubkey = user.getUserAccountPublicKey();
86
- for (const order of userAccount.orders) {
87
- this.insertOrder(order, userAccountPubkey);
88
- }
89
- }
90
- }
91
- else {
92
- const programAccounts = await clearingHouse.program.account.user.all();
93
- for (const programAccount of programAccounts) {
94
- // @ts-ignore
95
- const userAccount = programAccount.account;
96
- const userAccountPublicKey = programAccount.publicKey;
97
- for (const order of userAccount.orders) {
98
- this.insertOrder(order, userAccountPublicKey);
99
- }
107
+ // initialize the dlob with the user map (prevents hitting getProgramAccounts)
108
+ for (const user of this.userMap.values()) {
109
+ const userAccount = user.getUserAccount();
110
+ const userAccountPubkey = user.getUserAccountPublicKey();
111
+ for (const order of userAccount.orders) {
112
+ this.insertOrder(order, userAccountPubkey);
100
113
  }
101
114
  }
102
115
  this.initialized = true;
@@ -142,7 +155,7 @@ class DLOB {
142
155
  else if ((0, __1.isOneOfVariant)(order.orderType, ['market', 'triggerMarket'])) {
143
156
  type = 'market';
144
157
  }
145
- else if (!order.oraclePriceOffset.eq(__1.ZERO)) {
158
+ else if (order.oraclePriceOffset !== 0) {
146
159
  type = 'floatingLimit';
147
160
  }
148
161
  else {
@@ -161,13 +174,22 @@ class DLOB {
161
174
  }
162
175
  return this.orderLists.get(marketType).get(order.marketIndex)[type][subType];
163
176
  }
164
- findNodesToFill(marketIndex, vBid, vAsk, slot, marketType, oraclePriceData) {
177
+ findNodesToFill(marketIndex, fallbackBid, fallbackAsk, slot, ts, marketType, oraclePriceData) {
178
+ const marketAccount = this.marketIndexToAccount
179
+ .get((0, __1.getVariant)(marketType))
180
+ .get(marketIndex);
181
+ if ((0, exchangeStatus_1.fillPaused)(this.stateAccount, marketAccount)) {
182
+ return [];
183
+ }
165
184
  // Find all the crossing nodes
166
185
  const crossingNodesToFill = this.findCrossingNodesToFill(marketIndex, slot, marketType, oraclePriceData);
167
- const vAMMCrossingNodesToFill = this.findvAMMCrossingNodesToFill(marketIndex, vBid, vAsk, slot, marketType, oraclePriceData);
186
+ let fallbackCrossingNodesToFill = new Array();
187
+ if (!(0, exchangeStatus_1.ammPaused)(this.stateAccount, marketAccount)) {
188
+ fallbackCrossingNodesToFill = this.findFallbackCrossingNodesToFill(marketIndex, fallbackBid, fallbackAsk, slot, marketType, oraclePriceData);
189
+ }
168
190
  // get expired market nodes
169
- const marketNodesToFill = this.findExpiredMarketNodesToFill(marketIndex, slot, marketType);
170
- return crossingNodesToFill.concat(vAMMCrossingNodesToFill, marketNodesToFill);
191
+ const expiredNodesToFill = this.findExpiredNodesToFill(marketIndex, ts, marketType);
192
+ return crossingNodesToFill.concat(fallbackCrossingNodesToFill, expiredNodesToFill);
171
193
  }
172
194
  findCrossingNodesToFill(marketIndex, slot, marketType, oraclePriceData) {
173
195
  const nodesToFill = new Array();
@@ -203,7 +225,8 @@ class DLOB {
203
225
  }
204
226
  return nodesToFill;
205
227
  }
206
- findvAMMCrossingNodesToFill(marketIndex, vBid, vAsk, slot, marketType, oraclePriceData) {
228
+ findFallbackCrossingNodesToFill(marketIndex, fallbackBid, fallbackAsk, slot, marketType, oraclePriceData) {
229
+ var _a, _b;
207
230
  const nodesToFill = new Array();
208
231
  const askGenerator = this.getAsks(marketIndex, undefined, // dont include vask
209
232
  slot, marketType, oraclePriceData);
@@ -211,53 +234,79 @@ class DLOB {
211
234
  slot, marketType, oraclePriceData);
212
235
  let nextAsk = askGenerator.next();
213
236
  let nextBid = bidGenerator.next();
214
- // check for asks that cross vBid
215
- while (!nextAsk.done) {
237
+ // check for asks that cross fallbackBid
238
+ while (!nextAsk.done && fallbackBid !== undefined) {
216
239
  const askNode = nextAsk.value;
217
- const askPrice = askNode.getPrice(oraclePriceData, slot);
218
- if (askPrice.lte(vBid) && (0, __1.isAuctionComplete)(askNode.order, slot)) {
240
+ if ((0, __1.isVariant)(marketType, 'spot') && ((_a = askNode.order) === null || _a === void 0 ? void 0 : _a.postOnly)) {
241
+ nextAsk = askGenerator.next();
242
+ continue;
243
+ }
244
+ const askLimitPrice = (0, __1.getOptionalLimitPrice)(askNode.order, oraclePriceData, slot);
245
+ // order crosses if there is no limit price or it crosses fallback price
246
+ const crosses = askLimitPrice === undefined || askLimitPrice.lte(fallbackBid);
247
+ // fallback is available if auction is complete or it's a spot order
248
+ const fallbackAvailable = (0, __1.isVariant)(marketType, 'spot') || (0, __1.isAuctionComplete)(askNode.order, slot);
249
+ if (crosses && fallbackAvailable) {
219
250
  nodesToFill.push({
220
251
  node: askNode,
221
- makerNode: undefined, // filled by vAMM
252
+ makerNode: undefined, // filled by fallback
222
253
  });
223
254
  }
224
- else {
225
- break;
226
- }
227
255
  nextAsk = askGenerator.next();
228
256
  }
229
- // check for bids that cross vAsk
230
- while (!nextBid.done) {
257
+ // check for bids that cross fallbackAsk
258
+ while (!nextBid.done && fallbackAsk !== undefined) {
231
259
  const bidNode = nextBid.value;
232
- const bidPrice = bidNode.getPrice(oraclePriceData, slot);
233
- if (bidPrice.gte(vAsk) && (0, __1.isAuctionComplete)(bidNode.order, slot)) {
260
+ if ((0, __1.isVariant)(marketType, 'spot') && ((_b = bidNode.order) === null || _b === void 0 ? void 0 : _b.postOnly)) {
261
+ nextBid = bidGenerator.next();
262
+ continue;
263
+ }
264
+ const bidLimitPrice = (0, __1.getOptionalLimitPrice)(bidNode.order, oraclePriceData, slot);
265
+ // order crosses if there is no limit price or it crosses fallback price
266
+ const crosses = bidLimitPrice === undefined || bidLimitPrice.gte(fallbackAsk);
267
+ // fallback is available if auction is complete or it's a spot order
268
+ const fallbackAvailable = (0, __1.isVariant)(marketType, 'spot') || (0, __1.isAuctionComplete)(bidNode.order, slot);
269
+ if (crosses && fallbackAvailable) {
234
270
  nodesToFill.push({
235
271
  node: bidNode,
236
- makerNode: undefined, // filled by vAMM
272
+ makerNode: undefined, // filled by fallback
237
273
  });
238
274
  }
239
- else {
240
- break;
241
- }
242
275
  nextBid = bidGenerator.next();
243
276
  }
244
277
  return nodesToFill;
245
278
  }
246
- findExpiredMarketNodesToFill(marketIndex, slot, marketType) {
279
+ findExpiredNodesToFill(marketIndex, ts, marketType) {
247
280
  const nodesToFill = new Array();
248
- // Then see if there are orders to fill against vamm
249
- for (const marketBid of this.getMarketBids(marketIndex, marketType)) {
250
- if ((0, __1.isOrderExpired)(marketBid.order, slot)) {
251
- nodesToFill.push({
252
- node: marketBid,
253
- });
281
+ const marketTypeStr = (0, __1.getVariant)(marketType);
282
+ const nodeLists = this.orderLists.get(marketTypeStr).get(marketIndex);
283
+ // All bids/asks that can expire
284
+ const bidGenerators = [
285
+ nodeLists.limit.bid.getGenerator(),
286
+ nodeLists.floatingLimit.bid.getGenerator(),
287
+ nodeLists.market.bid.getGenerator(),
288
+ ];
289
+ const askGenerators = [
290
+ nodeLists.limit.ask.getGenerator(),
291
+ nodeLists.floatingLimit.ask.getGenerator(),
292
+ nodeLists.market.ask.getGenerator(),
293
+ ];
294
+ for (const bidGenerator of bidGenerators) {
295
+ for (const bid of bidGenerator) {
296
+ if ((0, __1.isOrderExpired)(bid.order, ts)) {
297
+ nodesToFill.push({
298
+ node: bid,
299
+ });
300
+ }
254
301
  }
255
302
  }
256
- for (const marketAsk of this.getMarketAsks(marketIndex, marketType)) {
257
- if ((0, __1.isOrderExpired)(marketAsk.order, slot)) {
258
- nodesToFill.push({
259
- node: marketAsk,
260
- });
303
+ for (const askGenerator of askGenerators) {
304
+ for (const ask of askGenerator) {
305
+ if ((0, __1.isOrderExpired)(ask.order, ts)) {
306
+ nodesToFill.push({
307
+ node: ask,
308
+ });
309
+ }
261
310
  }
262
311
  }
263
312
  return nodesToFill;
@@ -295,7 +344,7 @@ class DLOB {
295
344
  .get(marketIndex)
296
345
  .market.ask.getGenerator();
297
346
  }
298
- *getAsks(marketIndex, vAsk, slot, marketType, oraclePriceData) {
347
+ *getAsks(marketIndex, fallbackAsk, slot, marketType, oraclePriceData) {
299
348
  if ((0, __1.isVariant)(marketType, 'spot') && !oraclePriceData) {
300
349
  throw new Error('Must provide OraclePriceData to get spot asks');
301
350
  }
@@ -306,8 +355,11 @@ class DLOB {
306
355
  nodeLists.floatingLimit.ask.getGenerator(),
307
356
  nodeLists.market.ask.getGenerator(),
308
357
  ];
309
- if (marketTypeStr === 'perp' && vAsk) {
310
- generatorList.push((0, NodeList_1.getVammNodeGenerator)(vAsk));
358
+ if (marketTypeStr === 'perp' && fallbackAsk) {
359
+ generatorList.push((0, NodeList_1.getVammNodeGenerator)(fallbackAsk));
360
+ }
361
+ if (generatorList.length === 0) {
362
+ throw new Error('No ask generators found');
311
363
  }
312
364
  const askGenerators = generatorList.map((generator) => {
313
365
  return {
@@ -336,6 +388,15 @@ class DLOB {
336
388
  bestGenerator.next = bestGenerator.generator.next();
337
389
  continue;
338
390
  }
391
+ // skip order if user is being liquidated/bankrupt
392
+ if (bestGenerator.next.value.userAccount !== undefined) {
393
+ const user = this.userMap.get(bestGenerator.next.value.userAccount.toString());
394
+ if ((user === null || user === void 0 ? void 0 : user.getUserAccount().isBeingLiquidated) ||
395
+ (user === null || user === void 0 ? void 0 : user.getUserAccount().isBankrupt)) {
396
+ bestGenerator.next = bestGenerator.generator.next();
397
+ continue;
398
+ }
399
+ }
339
400
  yield bestGenerator.next.value;
340
401
  bestGenerator.next = bestGenerator.generator.next();
341
402
  }
@@ -344,7 +405,7 @@ class DLOB {
344
405
  }
345
406
  }
346
407
  }
347
- *getBids(marketIndex, vBid, slot, marketType, oraclePriceData) {
408
+ *getBids(marketIndex, fallbackBid, slot, marketType, oraclePriceData) {
348
409
  if ((0, __1.isVariant)(marketType, 'spot') && !oraclePriceData) {
349
410
  throw new Error('Must provide OraclePriceData to get spot bids');
350
411
  }
@@ -355,8 +416,11 @@ class DLOB {
355
416
  nodeLists.floatingLimit.bid.getGenerator(),
356
417
  nodeLists.market.bid.getGenerator(),
357
418
  ];
358
- if (marketTypeStr === 'perp' && vBid) {
359
- generatorList.push((0, NodeList_1.getVammNodeGenerator)(vBid));
419
+ if (marketTypeStr === 'perp' && fallbackBid) {
420
+ generatorList.push((0, NodeList_1.getVammNodeGenerator)(fallbackBid));
421
+ }
422
+ if (generatorList.length === 0) {
423
+ throw new Error('No bid generators found');
360
424
  }
361
425
  const bidGenerators = generatorList.map((generator) => {
362
426
  return {
@@ -364,7 +428,7 @@ class DLOB {
364
428
  generator,
365
429
  };
366
430
  });
367
- let bidsExhausted = false; // there will always be the vBid
431
+ let bidsExhausted = false; // there will always be the fallbackBid
368
432
  while (!bidsExhausted) {
369
433
  const bestGenerator = bidGenerators.reduce((bestGenerator, currentGenerator) => {
370
434
  if (currentGenerator.next.done) {
@@ -385,6 +449,15 @@ class DLOB {
385
449
  bestGenerator.next = bestGenerator.generator.next();
386
450
  continue;
387
451
  }
452
+ // skip order if user is being liquidated/bankrupt
453
+ if (bestGenerator.next.value.userAccount !== undefined) {
454
+ const user = this.userMap.get(bestGenerator.next.value.userAccount.toString());
455
+ if ((user === null || user === void 0 ? void 0 : user.getUserAccount().isBeingLiquidated) ||
456
+ (user === null || user === void 0 ? void 0 : user.getUserAccount().isBankrupt)) {
457
+ bestGenerator.next = bestGenerator.generator.next();
458
+ continue;
459
+ }
460
+ }
388
461
  yield bestGenerator.next.value;
389
462
  bestGenerator.next = bestGenerator.generator.next();
390
463
  }
@@ -405,9 +478,11 @@ class DLOB {
405
478
  }
406
479
  const bidOrder = bidNode.order;
407
480
  const askOrder = askNode.order;
408
- // Can't match two maker orders or if maker and taker are the same
409
- const makerIsTaker = bidNode.userAccount.equals(askNode.userAccount);
410
- if (makerIsTaker || (bidOrder.postOnly && askOrder.postOnly)) {
481
+ const bidUserAuthority = this.userMap.getUserAuthority(bidNode.userAccount.toString());
482
+ const askUserAuthority = this.userMap.getUserAuthority(askNode.userAccount.toString());
483
+ // Can't match orders from the same authority
484
+ const sameAuthority = bidUserAuthority.equals(askUserAuthority);
485
+ if (sameAuthority || (bidOrder.postOnly && askOrder.postOnly)) {
411
486
  // don't have a principle way to pick which one to exhaust,
412
487
  // exhaust each one 50% of the time so we can try each one against other orders
413
488
  const exhaustedSide = Math.random() < 0.5 ? 'bid' : 'ask';
@@ -416,6 +491,14 @@ class DLOB {
416
491
  exhaustedSide,
417
492
  };
418
493
  }
494
+ const { takerNode, makerNode, makerSide } = this.determineMakerAndTaker(askNode, bidNode);
495
+ // If order doesn't have limit price, cant be maker
496
+ if (!(0, __1.hasLimitPrice)(makerNode.order, slot)) {
497
+ return {
498
+ crossingNodes: [],
499
+ exhaustedSide: makerSide,
500
+ };
501
+ }
419
502
  const bidBaseRemaining = bidOrder.baseAssetAmount.sub(bidOrder.baseAssetAmountFilled);
420
503
  const askBaseRemaining = askOrder.baseAssetAmount.sub(askOrder.baseAssetAmountFilled);
421
504
  let exhaustedSide;
@@ -464,56 +547,74 @@ class DLOB {
464
547
  newAskOrder.baseAssetAmountFilled = askOrder.baseAssetAmount;
465
548
  this.getListForOrder(newAskOrder).update(newAskOrder, askNode.userAccount);
466
549
  }
467
- // Bid is maker
468
- if (bidOrder.postOnly) {
469
- return {
470
- crossingNodes: [
471
- {
472
- node: askNode,
473
- makerNode: bidNode,
474
- },
475
- ],
476
- exhaustedSide,
477
- };
478
- }
479
- // Ask is maker
480
- if (askOrder.postOnly) {
481
- return {
482
- crossingNodes: [
483
- {
484
- node: bidNode,
485
- makerNode: askNode,
486
- },
487
- ],
488
- exhaustedSide,
489
- };
490
- }
491
- // Both are takers
492
- // older order is maker
493
- const [olderNode, newerNode] = askOrder.ts.lt(bidOrder.ts)
494
- ? [askNode, bidNode]
495
- : [bidNode, askNode];
496
550
  return {
497
551
  crossingNodes: [
498
552
  {
499
- node: newerNode,
500
- makerNode: olderNode,
553
+ node: takerNode,
554
+ makerNode: makerNode,
501
555
  },
502
556
  ],
503
557
  exhaustedSide,
504
558
  };
505
559
  }
506
- getBestAsk(marketIndex, vAsk, slot, marketType, oraclePriceData) {
507
- return this.getAsks(marketIndex, vAsk, slot, marketType, oraclePriceData)
560
+ determineMakerAndTaker(askNode, bidNode) {
561
+ if (bidNode.order.postOnly) {
562
+ return {
563
+ takerNode: askNode,
564
+ makerNode: bidNode,
565
+ makerSide: 'bid',
566
+ };
567
+ }
568
+ else if (askNode.order.postOnly) {
569
+ return {
570
+ takerNode: bidNode,
571
+ makerNode: askNode,
572
+ makerSide: 'ask',
573
+ };
574
+ }
575
+ else if ((0, __1.isMarketOrder)(bidNode.order) && (0, __1.isLimitOrder)(askNode.order)) {
576
+ return {
577
+ takerNode: bidNode,
578
+ makerNode: askNode,
579
+ makerSide: 'ask',
580
+ };
581
+ }
582
+ else if ((0, __1.isMarketOrder)(askNode.order) && (0, __1.isLimitOrder)(bidNode.order)) {
583
+ return {
584
+ takerNode: askNode,
585
+ makerNode: bidNode,
586
+ makerSide: 'bid',
587
+ };
588
+ }
589
+ else if (askNode.order.slot.lt(bidNode.order.slot)) {
590
+ return {
591
+ takerNode: bidNode,
592
+ makerNode: askNode,
593
+ makerSide: 'ask',
594
+ };
595
+ }
596
+ else {
597
+ return {
598
+ takerNode: askNode,
599
+ makerNode: bidNode,
600
+ makerSide: 'bid',
601
+ };
602
+ }
603
+ }
604
+ getBestAsk(marketIndex, fallbackAsk, slot, marketType, oraclePriceData) {
605
+ return this.getAsks(marketIndex, fallbackAsk, slot, marketType, oraclePriceData)
508
606
  .next()
509
607
  .value.getPrice(oraclePriceData, slot);
510
608
  }
511
- getBestBid(marketIndex, vBid, slot, marketType, oraclePriceData) {
512
- return this.getBids(marketIndex, vBid, slot, marketType, oraclePriceData)
609
+ getBestBid(marketIndex, fallbackBid, slot, marketType, oraclePriceData) {
610
+ return this.getBids(marketIndex, fallbackBid, slot, marketType, oraclePriceData)
513
611
  .next()
514
612
  .value.getPrice(oraclePriceData, slot);
515
613
  }
516
614
  findNodesToTrigger(marketIndex, slot, oraclePrice, marketType) {
615
+ if ((0, exchangeStatus_1.exchangePaused)(this.stateAccount)) {
616
+ return [];
617
+ }
517
618
  const nodesToTrigger = [];
518
619
  const marketTypeStr = (0, __1.getVariant)(marketType);
519
620
  for (const node of this.orderLists
@@ -552,11 +653,11 @@ class DLOB {
552
653
  if ((0, __1.isVariant)(marketType, 'perp')) {
553
654
  const market = clearingHouse.getPerpMarketAccount(marketIndex);
554
655
  const slot = slotSubscriber.getSlot();
555
- const oraclePriceData = clearingHouse.getOracleDataForMarket(marketIndex);
556
- const vAsk = (0, __1.calculateAskPrice)(market, oraclePriceData);
557
- const vBid = (0, __1.calculateBidPrice)(market, oraclePriceData);
558
- const bestAsk = this.getBestAsk(marketIndex, vAsk, slot, marketType, oraclePriceData);
559
- const bestBid = this.getBestBid(marketIndex, vBid, slot, marketType, oraclePriceData);
656
+ const oraclePriceData = clearingHouse.getOracleDataForPerpMarket(marketIndex);
657
+ const fallbackAsk = (0, __1.calculateAskPrice)(market, oraclePriceData);
658
+ const fallbackBid = (0, __1.calculateBidPrice)(market, oraclePriceData);
659
+ const bestAsk = this.getBestAsk(marketIndex, fallbackAsk, slot, marketType, oraclePriceData);
660
+ const bestBid = this.getBestBid(marketIndex, fallbackBid, slot, marketType, oraclePriceData);
560
661
  const mid = bestAsk.add(bestBid).div(new __1.BN(2));
561
662
  const bidSpread = ((0, __1.convertToNumber)(bestBid, __1.PRICE_PRECISION) /
562
663
  (0, __1.convertToNumber)(oraclePriceData.price, __1.PRICE_PRECISION) -
@@ -573,7 +674,7 @@ class DLOB {
573
674
  }
574
675
  else if ((0, __1.isVariant)(marketType, 'spot')) {
575
676
  const slot = slotSubscriber.getSlot();
576
- const oraclePriceData = clearingHouse.getOracleDataForMarket(marketIndex);
677
+ const oraclePriceData = clearingHouse.getOracleDataForPerpMarket(marketIndex);
577
678
  const bestAsk = this.getBestAsk(marketIndex, undefined, slot, marketType, oraclePriceData);
578
679
  const bestBid = this.getBestBid(marketIndex, undefined, slot, marketType, oraclePriceData);
579
680
  const mid = bestAsk.add(bestBid).div(new __1.BN(2));
@@ -26,15 +26,7 @@ class OrderNode {
26
26
  return msg;
27
27
  }
28
28
  getPrice(oraclePriceData, slot) {
29
- if ((0, __1.isOneOfVariant)(this.order.marketType, ['spot'])) {
30
- return (0, __1.getLimitPrice)(this.order, oraclePriceData, slot);
31
- }
32
- else if ((0, __1.isOneOfVariant)(this.order.marketType, ['perp'])) {
33
- return (0, __1.getLimitPrice)(this.order, oraclePriceData, slot, this.market);
34
- }
35
- else {
36
- console.error(`Unknown market type: ${this.order.marketType}`);
37
- }
29
+ return (0, __1.getLimitPrice)(this.order, oraclePriceData, slot);
38
30
  }
39
31
  isBaseFilled() {
40
32
  return this.order.baseAssetAmountFilled.eq(this.order.baseAssetAmount);
@@ -52,7 +44,7 @@ class LimitOrderNode extends OrderNode {
52
44
  exports.LimitOrderNode = LimitOrderNode;
53
45
  class FloatingLimitOrderNode extends OrderNode {
54
46
  getSortValue(order) {
55
- return order.oraclePriceOffset;
47
+ return new __1.BN(order.oraclePriceOffset);
56
48
  }
57
49
  }
58
50
  exports.FloatingLimitOrderNode = FloatingLimitOrderNode;
@@ -14,6 +14,7 @@ export declare class NodeList<NodeType extends keyof DLOBNodeMap> implements DLO
14
14
  length: number;
15
15
  nodeMap: Map<string, DLOBNodeMap[NodeType]>;
16
16
  constructor(nodeType: NodeType, sortDirection: SortDirection);
17
+ clear(): void;
17
18
  insert(order: Order, marketType: MarketTypeStr, market: PerpMarketAccount | SpotMarketAccount, userAccount: PublicKey): void;
18
19
  prependNode(currentNode: DLOBNodeMap[NodeType], newNode: DLOBNodeMap[NodeType]): boolean;
19
20
  update(order: Order, userAccount: PublicKey): void;
@@ -14,6 +14,11 @@ class NodeList {
14
14
  this.length = 0;
15
15
  this.nodeMap = new Map();
16
16
  }
17
+ clear() {
18
+ this.head = undefined;
19
+ this.length = 0;
20
+ this.nodeMap.clear();
21
+ }
17
22
  insert(order, marketType, market, userAccount) {
18
23
  if ((0, __1.isVariant)(order.status, 'init')) {
19
24
  return;
@@ -59,7 +64,7 @@ class NodeList {
59
64
  const currentOrderSortPrice = currentNode.sortValue;
60
65
  const newOrderSortPrice = newNode.sortValue;
61
66
  if (newOrderSortPrice.eq(currentOrderSortPrice)) {
62
- return newOrder.ts.lt(currentOrder.ts);
67
+ return newOrder.slot.lt(currentOrder.slot);
63
68
  }
64
69
  if (this.sortDirection === 'asc') {
65
70
  return newOrderSortPrice.lt(currentOrderSortPrice);
@@ -16,6 +16,7 @@ export declare class EventSubscriber {
16
16
  private logProvider;
17
17
  eventEmitter: StrictEventEmitter<EventEmitter, EventSubscriberEvents>;
18
18
  private lastSeenSlot;
19
+ private lastSeenBlockTime;
19
20
  lastSeenTxSig: string;
20
21
  constructor(connection: Connection, program: Program, options?: EventSubscriptionOptions);
21
22
  subscribe(): Promise<boolean>;
@@ -35,8 +35,8 @@ class EventSubscriber {
35
35
  if (this.logProvider.isSubscribed()) {
36
36
  return true;
37
37
  }
38
- this.logProvider.subscribe((txSig, slot, logs) => {
39
- this.handleTxLogs(txSig, slot, logs);
38
+ this.logProvider.subscribe((txSig, slot, logs, mostRecentBlockTime) => {
39
+ this.handleTxLogs(txSig, slot, logs, mostRecentBlockTime);
40
40
  }, true);
41
41
  return true;
42
42
  }
@@ -46,13 +46,16 @@ class EventSubscriber {
46
46
  return false;
47
47
  }
48
48
  }
49
- handleTxLogs(txSig, slot, logs) {
49
+ handleTxLogs(txSig, slot, logs, mostRecentBlockTime) {
50
50
  if (this.txEventCache.has(txSig)) {
51
51
  return;
52
52
  }
53
53
  const wrappedEvents = this.parseEventsFromLogs(txSig, slot, logs);
54
54
  for (const wrappedEvent of wrappedEvents) {
55
55
  this.eventListMap.get(wrappedEvent.eventType).insert(wrappedEvent);
56
+ }
57
+ // dont emit event till we've added all the events to the eventListMap
58
+ for (const wrappedEvent of wrappedEvents) {
56
59
  this.eventEmitter.emit('newEvent', wrappedEvent);
57
60
  }
58
61
  if (this.awaitTxPromises.has(txSig)) {
@@ -63,6 +66,10 @@ class EventSubscriber {
63
66
  if (slot > this.lastSeenSlot) {
64
67
  this.lastSeenTxSig = txSig;
65
68
  }
69
+ if (this.lastSeenBlockTime === undefined ||
70
+ mostRecentBlockTime > this.lastSeenBlockTime) {
71
+ this.lastSeenBlockTime = mostRecentBlockTime;
72
+ }
66
73
  this.txEventCache.add(txSig, wrappedEvents);
67
74
  }
68
75
  async fetchPreviousTx(fetchMax) {
@@ -80,7 +87,7 @@ class EventSubscriber {
80
87
  txFetched += response.transactionLogs.length;
81
88
  beforeTx = response.earliestTx;
82
89
  for (const { txSig, slot, logs } of response.transactionLogs) {
83
- this.handleTxLogs(txSig, slot, logs);
90
+ this.handleTxLogs(txSig, slot, logs, response.mostRecentBlockTime);
84
91
  }
85
92
  }
86
93
  }
@@ -1,5 +1,5 @@
1
1
  import { Program } from '@project-serum/anchor';
2
- import { Connection, Finality, PublicKey, TransactionSignature } from '@solana/web3.js';
2
+ import { Connection, Finality, PublicKey, TransactionResponse, TransactionSignature } from '@solana/web3.js';
3
3
  import { WrappedEvents } from './types';
4
4
  declare type Log = {
5
5
  txSig: TransactionSignature;
@@ -12,11 +12,13 @@ declare type FetchLogsResponse = {
12
12
  earliestSlot: number;
13
13
  mostRecentSlot: number;
14
14
  transactionLogs: Log[];
15
+ mostRecentBlockTime: number | undefined;
15
16
  };
16
17
  export declare function fetchLogs(connection: Connection, programId: PublicKey, finality: Finality, beforeTx?: TransactionSignature, untilTx?: TransactionSignature, limit?: number): Promise<FetchLogsResponse>;
17
18
  export declare class LogParser {
18
19
  private program;
19
20
  constructor(program: Program);
21
+ parseEventsFromTransaction(transaction: TransactionResponse): WrappedEvents;
20
22
  parseEventsFromLogs(event: Log): WrappedEvents;
21
23
  }
22
24
  export {};