@drift-labs/sdk 0.2.0-master.32 → 0.2.0-master.34

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 (108) 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 +18 -11
  22. package/lib/dlob/DLOB.js +179 -107
  23. package/lib/dlob/DLOBNode.js +2 -10
  24. package/lib/dlob/NodeList.js +1 -1
  25. package/lib/events/eventSubscriber.d.ts +1 -0
  26. package/lib/events/eventSubscriber.js +11 -4
  27. package/lib/events/fetchLogs.d.ts +3 -1
  28. package/lib/events/fetchLogs.js +13 -5
  29. package/lib/events/pollingLogProvider.js +1 -1
  30. package/lib/events/types.d.ts +1 -1
  31. package/lib/events/webSocketLogProvider.js +1 -1
  32. package/lib/factory/bigNum.d.ts +5 -4
  33. package/lib/factory/bigNum.js +36 -6
  34. package/lib/idl/clearing_house.json +1540 -1246
  35. package/lib/index.d.ts +3 -0
  36. package/lib/index.js +3 -0
  37. package/lib/math/amm.js +9 -9
  38. package/lib/math/exchangeStatus.d.ts +4 -0
  39. package/lib/math/exchangeStatus.js +18 -0
  40. package/lib/math/funding.js +10 -10
  41. package/lib/math/margin.js +6 -1
  42. package/lib/math/market.js +9 -9
  43. package/lib/math/orders.d.ts +7 -3
  44. package/lib/math/orders.js +39 -31
  45. package/lib/math/repeg.js +3 -3
  46. package/lib/math/spotBalance.js +3 -3
  47. package/lib/math/spotPosition.js +2 -2
  48. package/lib/serum/serumFulfillmentConfigMap.d.ts +10 -0
  49. package/lib/serum/serumFulfillmentConfigMap.js +17 -0
  50. package/lib/serum/serumSubscriber.d.ts +4 -0
  51. package/lib/serum/serumSubscriber.js +16 -1
  52. package/lib/types.d.ts +60 -75
  53. package/lib/types.js +2 -1
  54. package/lib/userMap/userMap.d.ts +17 -1
  55. package/lib/userMap/userMap.js +12 -0
  56. package/lib/userName.d.ts +1 -0
  57. package/lib/userName.js +3 -2
  58. package/package.json +1 -1
  59. package/src/accounts/bulkAccountLoader.ts +5 -1
  60. package/src/accounts/pollingClearingHouseAccountSubscriber.ts +9 -9
  61. package/src/accounts/webSocketClearingHouseAccountSubscriber.ts +8 -8
  62. package/src/addresses/marketAddresses.ts +2 -2
  63. package/src/addresses/pda.ts +20 -20
  64. package/src/admin.ts +246 -221
  65. package/src/assert/assert.js +9 -0
  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 +257 -120
  72. package/src/dlob/DLOBNode.ts +2 -14
  73. package/src/dlob/NodeList.ts +1 -1
  74. package/src/events/eventList.js +77 -0
  75. package/src/events/eventSubscriber.ts +18 -4
  76. package/src/events/fetchLogs.ts +20 -5
  77. package/src/events/pollingLogProvider.ts +1 -1
  78. package/src/events/types.ts +2 -1
  79. package/src/events/webSocketLogProvider.ts +1 -1
  80. package/src/examples/makeTradeExample.js +157 -0
  81. package/src/factory/bigNum.ts +59 -6
  82. package/src/idl/clearing_house.json +1540 -1246
  83. package/src/idl/pyth.json +98 -2
  84. package/src/index.ts +3 -0
  85. package/src/math/amm.ts +9 -9
  86. package/src/math/exchangeStatus.ts +31 -0
  87. package/src/math/funding.ts +20 -10
  88. package/src/math/margin.ts +7 -1
  89. package/src/math/market.ts +9 -9
  90. package/src/math/orders.ts +44 -29
  91. package/src/math/repeg.ts +3 -3
  92. package/src/math/spotBalance.ts +4 -4
  93. package/src/math/spotPosition.ts +2 -2
  94. package/src/serum/serumFulfillmentConfigMap.ts +26 -0
  95. package/src/serum/serumSubscriber.ts +20 -1
  96. package/src/token/index.js +38 -0
  97. package/src/tx/types.js +2 -0
  98. package/src/tx/utils.js +17 -0
  99. package/src/types.ts +65 -51
  100. package/src/userMap/userMap.ts +25 -1
  101. package/src/userName.ts +2 -1
  102. package/src/util/computeUnits.js +27 -0
  103. package/src/util/getTokenAddress.js +9 -0
  104. package/src/util/promiseTimeout.js +14 -0
  105. package/src/util/tps.js +27 -0
  106. package/tests/bn/test.ts +22 -1
  107. package/tests/dlob/helpers.ts +252 -81
  108. package/tests/dlob/test.ts +1040 -219
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());
@@ -94,32 +98,18 @@ class DLOB {
94
98
  /**
95
99
  * initializes a new DLOB instance
96
100
  *
97
- * @param clearingHouse The ClearingHouse instance to use for price data
98
101
  * @returns a promise that resolves when the DLOB is initialized
99
102
  */
100
- async init(clearingHouse, userMap) {
103
+ async init() {
101
104
  if (this.initialized) {
102
105
  return false;
103
106
  }
104
- if (userMap) {
105
- // initialize the dlob with the user map (prevents hitting getProgramAccounts)
106
- for (const user of userMap.values()) {
107
- const userAccount = user.getUserAccount();
108
- const userAccountPubkey = user.getUserAccountPublicKey();
109
- for (const order of userAccount.orders) {
110
- this.insertOrder(order, userAccountPubkey);
111
- }
112
- }
113
- }
114
- else {
115
- const programAccounts = await clearingHouse.program.account.user.all();
116
- for (const programAccount of programAccounts) {
117
- // @ts-ignore
118
- const userAccount = programAccount.account;
119
- const userAccountPublicKey = programAccount.publicKey;
120
- for (const order of userAccount.orders) {
121
- this.insertOrder(order, userAccountPublicKey);
122
- }
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);
123
113
  }
124
114
  }
125
115
  this.initialized = true;
@@ -165,7 +155,7 @@ class DLOB {
165
155
  else if ((0, __1.isOneOfVariant)(order.orderType, ['market', 'triggerMarket'])) {
166
156
  type = 'market';
167
157
  }
168
- else if (!order.oraclePriceOffset.eq(__1.ZERO)) {
158
+ else if (order.oraclePriceOffset !== 0) {
169
159
  type = 'floatingLimit';
170
160
  }
171
161
  else {
@@ -184,13 +174,22 @@ class DLOB {
184
174
  }
185
175
  return this.orderLists.get(marketType).get(order.marketIndex)[type][subType];
186
176
  }
187
- 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
+ }
188
184
  // Find all the crossing nodes
189
185
  const crossingNodesToFill = this.findCrossingNodesToFill(marketIndex, slot, marketType, oraclePriceData);
190
- 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
+ }
191
190
  // get expired market nodes
192
- const marketNodesToFill = this.findExpiredMarketNodesToFill(marketIndex, slot, marketType);
193
- return crossingNodesToFill.concat(vAMMCrossingNodesToFill, marketNodesToFill);
191
+ const expiredNodesToFill = this.findExpiredNodesToFill(marketIndex, ts, marketType);
192
+ return crossingNodesToFill.concat(fallbackCrossingNodesToFill, expiredNodesToFill);
194
193
  }
195
194
  findCrossingNodesToFill(marketIndex, slot, marketType, oraclePriceData) {
196
195
  const nodesToFill = new Array();
@@ -226,7 +225,8 @@ class DLOB {
226
225
  }
227
226
  return nodesToFill;
228
227
  }
229
- findvAMMCrossingNodesToFill(marketIndex, vBid, vAsk, slot, marketType, oraclePriceData) {
228
+ findFallbackCrossingNodesToFill(marketIndex, fallbackBid, fallbackAsk, slot, marketType, oraclePriceData) {
229
+ var _a, _b;
230
230
  const nodesToFill = new Array();
231
231
  const askGenerator = this.getAsks(marketIndex, undefined, // dont include vask
232
232
  slot, marketType, oraclePriceData);
@@ -234,53 +234,79 @@ class DLOB {
234
234
  slot, marketType, oraclePriceData);
235
235
  let nextAsk = askGenerator.next();
236
236
  let nextBid = bidGenerator.next();
237
- // check for asks that cross vBid
238
- while (!nextAsk.done) {
237
+ // check for asks that cross fallbackBid
238
+ while (!nextAsk.done && fallbackBid !== undefined) {
239
239
  const askNode = nextAsk.value;
240
- const askPrice = askNode.getPrice(oraclePriceData, slot);
241
- 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) {
242
250
  nodesToFill.push({
243
251
  node: askNode,
244
- makerNode: undefined, // filled by vAMM
252
+ makerNode: undefined, // filled by fallback
245
253
  });
246
254
  }
247
- else {
248
- break;
249
- }
250
255
  nextAsk = askGenerator.next();
251
256
  }
252
- // check for bids that cross vAsk
253
- while (!nextBid.done) {
257
+ // check for bids that cross fallbackAsk
258
+ while (!nextBid.done && fallbackAsk !== undefined) {
254
259
  const bidNode = nextBid.value;
255
- const bidPrice = bidNode.getPrice(oraclePriceData, slot);
256
- 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) {
257
270
  nodesToFill.push({
258
271
  node: bidNode,
259
- makerNode: undefined, // filled by vAMM
272
+ makerNode: undefined, // filled by fallback
260
273
  });
261
274
  }
262
- else {
263
- break;
264
- }
265
275
  nextBid = bidGenerator.next();
266
276
  }
267
277
  return nodesToFill;
268
278
  }
269
- findExpiredMarketNodesToFill(marketIndex, slot, marketType) {
279
+ findExpiredNodesToFill(marketIndex, ts, marketType) {
270
280
  const nodesToFill = new Array();
271
- // Then see if there are orders to fill against vamm
272
- for (const marketBid of this.getMarketBids(marketIndex, marketType)) {
273
- if ((0, __1.isOrderExpired)(marketBid.order, slot)) {
274
- nodesToFill.push({
275
- node: marketBid,
276
- });
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
+ }
277
301
  }
278
302
  }
279
- for (const marketAsk of this.getMarketAsks(marketIndex, marketType)) {
280
- if ((0, __1.isOrderExpired)(marketAsk.order, slot)) {
281
- nodesToFill.push({
282
- node: marketAsk,
283
- });
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
+ }
284
310
  }
285
311
  }
286
312
  return nodesToFill;
@@ -318,7 +344,7 @@ class DLOB {
318
344
  .get(marketIndex)
319
345
  .market.ask.getGenerator();
320
346
  }
321
- *getAsks(marketIndex, vAsk, slot, marketType, oraclePriceData) {
347
+ *getAsks(marketIndex, fallbackAsk, slot, marketType, oraclePriceData) {
322
348
  if ((0, __1.isVariant)(marketType, 'spot') && !oraclePriceData) {
323
349
  throw new Error('Must provide OraclePriceData to get spot asks');
324
350
  }
@@ -329,8 +355,8 @@ class DLOB {
329
355
  nodeLists.floatingLimit.ask.getGenerator(),
330
356
  nodeLists.market.ask.getGenerator(),
331
357
  ];
332
- if (marketTypeStr === 'perp' && vAsk) {
333
- generatorList.push((0, NodeList_1.getVammNodeGenerator)(vAsk));
358
+ if (marketTypeStr === 'perp' && fallbackAsk) {
359
+ generatorList.push((0, NodeList_1.getVammNodeGenerator)(fallbackAsk));
334
360
  }
335
361
  if (generatorList.length === 0) {
336
362
  throw new Error('No ask generators found');
@@ -362,6 +388,15 @@ class DLOB {
362
388
  bestGenerator.next = bestGenerator.generator.next();
363
389
  continue;
364
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
+ }
365
400
  yield bestGenerator.next.value;
366
401
  bestGenerator.next = bestGenerator.generator.next();
367
402
  }
@@ -370,7 +405,7 @@ class DLOB {
370
405
  }
371
406
  }
372
407
  }
373
- *getBids(marketIndex, vBid, slot, marketType, oraclePriceData) {
408
+ *getBids(marketIndex, fallbackBid, slot, marketType, oraclePriceData) {
374
409
  if ((0, __1.isVariant)(marketType, 'spot') && !oraclePriceData) {
375
410
  throw new Error('Must provide OraclePriceData to get spot bids');
376
411
  }
@@ -381,8 +416,8 @@ class DLOB {
381
416
  nodeLists.floatingLimit.bid.getGenerator(),
382
417
  nodeLists.market.bid.getGenerator(),
383
418
  ];
384
- if (marketTypeStr === 'perp' && vBid) {
385
- generatorList.push((0, NodeList_1.getVammNodeGenerator)(vBid));
419
+ if (marketTypeStr === 'perp' && fallbackBid) {
420
+ generatorList.push((0, NodeList_1.getVammNodeGenerator)(fallbackBid));
386
421
  }
387
422
  if (generatorList.length === 0) {
388
423
  throw new Error('No bid generators found');
@@ -393,7 +428,7 @@ class DLOB {
393
428
  generator,
394
429
  };
395
430
  });
396
- let bidsExhausted = false; // there will always be the vBid
431
+ let bidsExhausted = false; // there will always be the fallbackBid
397
432
  while (!bidsExhausted) {
398
433
  const bestGenerator = bidGenerators.reduce((bestGenerator, currentGenerator) => {
399
434
  if (currentGenerator.next.done) {
@@ -414,6 +449,15 @@ class DLOB {
414
449
  bestGenerator.next = bestGenerator.generator.next();
415
450
  continue;
416
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
+ }
417
461
  yield bestGenerator.next.value;
418
462
  bestGenerator.next = bestGenerator.generator.next();
419
463
  }
@@ -434,9 +478,11 @@ class DLOB {
434
478
  }
435
479
  const bidOrder = bidNode.order;
436
480
  const askOrder = askNode.order;
437
- // Can't match two maker orders or if maker and taker are the same
438
- const makerIsTaker = bidNode.userAccount.equals(askNode.userAccount);
439
- 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)) {
440
486
  // don't have a principle way to pick which one to exhaust,
441
487
  // exhaust each one 50% of the time so we can try each one against other orders
442
488
  const exhaustedSide = Math.random() < 0.5 ? 'bid' : 'ask';
@@ -445,6 +491,14 @@ class DLOB {
445
491
  exhaustedSide,
446
492
  };
447
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
+ }
448
502
  const bidBaseRemaining = bidOrder.baseAssetAmount.sub(bidOrder.baseAssetAmountFilled);
449
503
  const askBaseRemaining = askOrder.baseAssetAmount.sub(askOrder.baseAssetAmountFilled);
450
504
  let exhaustedSide;
@@ -493,56 +547,74 @@ class DLOB {
493
547
  newAskOrder.baseAssetAmountFilled = askOrder.baseAssetAmount;
494
548
  this.getListForOrder(newAskOrder).update(newAskOrder, askNode.userAccount);
495
549
  }
496
- // Bid is maker
497
- if (bidOrder.postOnly) {
498
- return {
499
- crossingNodes: [
500
- {
501
- node: askNode,
502
- makerNode: bidNode,
503
- },
504
- ],
505
- exhaustedSide,
506
- };
507
- }
508
- // Ask is maker
509
- if (askOrder.postOnly) {
510
- return {
511
- crossingNodes: [
512
- {
513
- node: bidNode,
514
- makerNode: askNode,
515
- },
516
- ],
517
- exhaustedSide,
518
- };
519
- }
520
- // Both are takers
521
- // older order is maker
522
- const [olderNode, newerNode] = askOrder.ts.lt(bidOrder.ts)
523
- ? [askNode, bidNode]
524
- : [bidNode, askNode];
525
550
  return {
526
551
  crossingNodes: [
527
552
  {
528
- node: newerNode,
529
- makerNode: olderNode,
553
+ node: takerNode,
554
+ makerNode: makerNode,
530
555
  },
531
556
  ],
532
557
  exhaustedSide,
533
558
  };
534
559
  }
535
- getBestAsk(marketIndex, vAsk, slot, marketType, oraclePriceData) {
536
- 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)
537
606
  .next()
538
607
  .value.getPrice(oraclePriceData, slot);
539
608
  }
540
- getBestBid(marketIndex, vBid, slot, marketType, oraclePriceData) {
541
- return this.getBids(marketIndex, vBid, slot, marketType, oraclePriceData)
609
+ getBestBid(marketIndex, fallbackBid, slot, marketType, oraclePriceData) {
610
+ return this.getBids(marketIndex, fallbackBid, slot, marketType, oraclePriceData)
542
611
  .next()
543
612
  .value.getPrice(oraclePriceData, slot);
544
613
  }
545
614
  findNodesToTrigger(marketIndex, slot, oraclePrice, marketType) {
615
+ if ((0, exchangeStatus_1.exchangePaused)(this.stateAccount)) {
616
+ return [];
617
+ }
546
618
  const nodesToTrigger = [];
547
619
  const marketTypeStr = (0, __1.getVariant)(marketType);
548
620
  for (const node of this.orderLists
@@ -581,11 +653,11 @@ class DLOB {
581
653
  if ((0, __1.isVariant)(marketType, 'perp')) {
582
654
  const market = clearingHouse.getPerpMarketAccount(marketIndex);
583
655
  const slot = slotSubscriber.getSlot();
584
- const oraclePriceData = clearingHouse.getOracleDataForMarket(marketIndex);
585
- const vAsk = (0, __1.calculateAskPrice)(market, oraclePriceData);
586
- const vBid = (0, __1.calculateBidPrice)(market, oraclePriceData);
587
- const bestAsk = this.getBestAsk(marketIndex, vAsk, slot, marketType, oraclePriceData);
588
- 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);
589
661
  const mid = bestAsk.add(bestBid).div(new __1.BN(2));
590
662
  const bidSpread = ((0, __1.convertToNumber)(bestBid, __1.PRICE_PRECISION) /
591
663
  (0, __1.convertToNumber)(oraclePriceData.price, __1.PRICE_PRECISION) -
@@ -602,7 +674,7 @@ class DLOB {
602
674
  }
603
675
  else if ((0, __1.isVariant)(marketType, 'spot')) {
604
676
  const slot = slotSubscriber.getSlot();
605
- const oraclePriceData = clearingHouse.getOracleDataForMarket(marketIndex);
677
+ const oraclePriceData = clearingHouse.getOracleDataForPerpMarket(marketIndex);
606
678
  const bestAsk = this.getBestAsk(marketIndex, undefined, slot, marketType, oraclePriceData);
607
679
  const bestBid = this.getBestBid(marketIndex, undefined, slot, marketType, oraclePriceData);
608
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;
@@ -64,7 +64,7 @@ class NodeList {
64
64
  const currentOrderSortPrice = currentNode.sortValue;
65
65
  const newOrderSortPrice = newNode.sortValue;
66
66
  if (newOrderSortPrice.eq(currentOrderSortPrice)) {
67
- return newOrder.ts.lt(currentOrder.ts);
67
+ return newOrder.slot.lt(currentOrder.slot);
68
68
  }
69
69
  if (this.sortDirection === 'asc') {
70
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 {};
@@ -1,6 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.LogParser = exports.fetchLogs = void 0;
4
+ function mapTransactionResponseToLog(transaction) {
5
+ return {
6
+ txSig: transaction.transaction.signatures[0],
7
+ slot: transaction.slot,
8
+ logs: transaction.meta.logMessages,
9
+ };
10
+ }
4
11
  async function fetchLogs(connection, programId, finality, beforeTx, untilTx, limit) {
5
12
  const signatures = await connection.getSignaturesForAddress(programId, {
6
13
  before: beforeTx,
@@ -16,11 +23,7 @@ async function fetchLogs(connection, programId, finality, beforeTx, untilTx, lim
16
23
  const transactionLogs = (await Promise.all(chunkedSignatures.map(async (chunk) => {
17
24
  const transactions = await connection.getTransactions(chunk.map((confirmedSignature) => confirmedSignature.signature), finality);
18
25
  return transactions.map((transaction) => {
19
- return {
20
- txSig: transaction.transaction.signatures[0],
21
- slot: transaction.slot,
22
- logs: transaction.meta.logMessages,
23
- };
26
+ return mapTransactionResponseToLog(transaction);
24
27
  });
25
28
  }))).flat();
26
29
  const earliest = filteredSignatures[0];
@@ -31,6 +34,7 @@ async function fetchLogs(connection, programId, finality, beforeTx, untilTx, lim
31
34
  mostRecentTx: mostRecent.signature,
32
35
  earliestSlot: earliest.slot,
33
36
  mostRecentSlot: mostRecent.slot,
37
+ mostRecentBlockTime: mostRecent.blockTime,
34
38
  };
35
39
  }
36
40
  exports.fetchLogs = fetchLogs;
@@ -44,6 +48,10 @@ class LogParser {
44
48
  constructor(program) {
45
49
  this.program = program;
46
50
  }
51
+ parseEventsFromTransaction(transaction) {
52
+ const transactionLogObject = mapTransactionResponseToLog(transaction);
53
+ return this.parseEventsFromLogs(transactionLogObject);
54
+ }
47
55
  parseEventsFromLogs(event) {
48
56
  const records = [];
49
57
  // @ts-ignore
@@ -29,7 +29,7 @@ class PollingLogProvider {
29
29
  }
30
30
  const { mostRecentTx, transactionLogs } = response;
31
31
  for (const { txSig, slot, logs } of transactionLogs) {
32
- callback(txSig, slot, logs);
32
+ callback(txSig, slot, logs, response.mostRecentBlockTime);
33
33
  }
34
34
  this.mostRecentSeenTx = mostRecentTx;
35
35
  }
@@ -39,7 +39,7 @@ export interface EventSubscriberEvents {
39
39
  newEvent: (event: WrappedEvent<EventType>) => void;
40
40
  }
41
41
  export declare type SortFn = (currentRecord: EventMap[EventType], newRecord: EventMap[EventType]) => 'less than' | 'greater than';
42
- export declare type logProviderCallback = (txSig: TransactionSignature, slot: number, logs: string[]) => void;
42
+ export declare type logProviderCallback = (txSig: TransactionSignature, slot: number, logs: string[], mostRecentBlockTime: number | undefined) => void;
43
43
  export interface LogProvider {
44
44
  isSubscribed(): boolean;
45
45
  subscribe(callback: logProviderCallback, skipHistory?: boolean): boolean;
@@ -12,7 +12,7 @@ class WebSocketLogProvider {
12
12
  return true;
13
13
  }
14
14
  this.subscriptionId = this.connection.onLogs(this.programId, (logs, ctx) => {
15
- callback(logs.signature, ctx.slot, logs.logs);
15
+ callback(logs.signature, ctx.slot, logs.logs, undefined);
16
16
  }, this.commitment);
17
17
  return true;
18
18
  }