@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/src/dlob/DLOB.ts CHANGED
@@ -12,17 +12,22 @@ import {
12
12
  isVariant,
13
13
  getVariant,
14
14
  Order,
15
- ZERO,
16
15
  PRICE_PRECISION,
17
16
  SpotMarketAccount,
18
17
  PerpMarketAccount,
19
18
  OraclePriceData,
20
19
  SlotSubscriber,
21
- UserMap,
20
+ UserMapInterface,
22
21
  MarketTypeStr,
22
+ StateAccount,
23
+ isMarketOrder,
24
+ isLimitOrder,
25
+ hasLimitPrice,
26
+ getOptionalLimitPrice,
23
27
  } from '..';
24
28
  import { PublicKey } from '@solana/web3.js';
25
29
  import { DLOBNode, DLOBNodeType, TriggerOrderNode } from '..';
30
+ import { ammPaused, exchangePaused, fillPaused } from '../math/exchangeStatus';
26
31
 
27
32
  export type MarketNodeLists = {
28
33
  limit: {
@@ -59,11 +64,13 @@ type Side = 'ask' | 'bid' | 'both' | 'nocross';
59
64
  export class DLOB {
60
65
  openOrders = new Map<MarketTypeStr, Set<string>>();
61
66
  orderLists = new Map<MarketTypeStr, Map<number, MarketNodeLists>>();
67
+ stateAccount: StateAccount;
62
68
  marketIndexToAccount = new Map<
63
69
  MarketTypeStr,
64
70
  Map<number, PerpMarketAccount | SpotMarketAccount>
65
71
  >();
66
72
 
73
+ userMap: UserMapInterface;
67
74
  silent = false;
68
75
  initialized = false;
69
76
 
@@ -71,13 +78,18 @@ export class DLOB {
71
78
  *
72
79
  * @param perpMarkets The perp markets to maintain a DLOB for
73
80
  * @param spotMarkets The spot markets to maintain a DLOB for
81
+ * @param userMap map of all users
74
82
  * @param silent set to true to prevent logging on inserts and removals
75
83
  */
76
84
  public constructor(
77
85
  perpMarkets: PerpMarketAccount[],
78
86
  spotMarkets: SpotMarketAccount[],
87
+ stateAccount: StateAccount,
88
+ userMap: UserMapInterface,
79
89
  silent?: boolean
80
90
  ) {
91
+ this.stateAccount = stateAccount;
92
+ this.userMap = userMap;
81
93
  this.silent = silent;
82
94
 
83
95
  this.openOrders.set('perp', new Set<string>());
@@ -135,39 +147,49 @@ export class DLOB {
135
147
  }
136
148
  }
137
149
 
150
+ public clear() {
151
+ for (const marketType of this.openOrders.keys()) {
152
+ this.openOrders.get(marketType).clear();
153
+ }
154
+ this.openOrders.clear();
155
+
156
+ for (const marketType of this.orderLists.keys()) {
157
+ for (const marketIndex of this.orderLists.get(marketType).keys()) {
158
+ const marketNodeLists = this.orderLists
159
+ .get(marketType)
160
+ .get(marketIndex);
161
+ for (const side of Object.keys(marketNodeLists)) {
162
+ for (const orderType of Object.keys(marketNodeLists[side])) {
163
+ marketNodeLists[side][orderType].clear();
164
+ }
165
+ }
166
+ }
167
+ }
168
+ this.orderLists.clear();
169
+
170
+ for (const marketType of this.marketIndexToAccount.keys()) {
171
+ this.marketIndexToAccount.get(marketType).clear();
172
+ }
173
+ this.marketIndexToAccount.clear();
174
+ }
175
+
138
176
  /**
139
177
  * initializes a new DLOB instance
140
178
  *
141
- * @param clearingHouse The ClearingHouse instance to use for price data
142
179
  * @returns a promise that resolves when the DLOB is initialized
143
180
  */
144
- public async init(
145
- clearingHouse: ClearingHouse,
146
- userMap?: UserMap
147
- ): Promise<boolean> {
181
+ public async init(): Promise<boolean> {
148
182
  if (this.initialized) {
149
183
  return false;
150
184
  }
151
- if (userMap) {
152
- // initialize the dlob with the user map (prevents hitting getProgramAccounts)
153
- for (const user of userMap.values()) {
154
- const userAccount = user.getUserAccount();
155
- const userAccountPubkey = user.getUserAccountPublicKey();
156
185
 
157
- for (const order of userAccount.orders) {
158
- this.insertOrder(order, userAccountPubkey);
159
- }
160
- }
161
- } else {
162
- const programAccounts = await clearingHouse.program.account.user.all();
163
- for (const programAccount of programAccounts) {
164
- // @ts-ignore
165
- const userAccount: UserAccount = programAccount.account;
166
- const userAccountPublicKey = programAccount.publicKey;
167
-
168
- for (const order of userAccount.orders) {
169
- this.insertOrder(order, userAccountPublicKey);
170
- }
186
+ // initialize the dlob with the user map (prevents hitting getProgramAccounts)
187
+ for (const user of this.userMap.values()) {
188
+ const userAccount = user.getUserAccount();
189
+ const userAccountPubkey = user.getUserAccountPublicKey();
190
+
191
+ for (const order of userAccount.orders) {
192
+ this.insertOrder(order, userAccountPubkey);
171
193
  }
172
194
  }
173
195
 
@@ -239,7 +261,7 @@ export class DLOB {
239
261
  type = 'trigger';
240
262
  } else if (isOneOfVariant(order.orderType, ['market', 'triggerMarket'])) {
241
263
  type = 'market';
242
- } else if (!order.oraclePriceOffset.eq(ZERO)) {
264
+ } else if (order.oraclePriceOffset !== 0) {
243
265
  type = 'floatingLimit';
244
266
  } else {
245
267
  type = 'limit';
@@ -265,12 +287,21 @@ export class DLOB {
265
287
 
266
288
  public findNodesToFill(
267
289
  marketIndex: number,
268
- vBid: BN | undefined,
269
- vAsk: BN | undefined,
290
+ fallbackBid: BN | undefined,
291
+ fallbackAsk: BN | undefined,
270
292
  slot: number,
293
+ ts: number,
271
294
  marketType: MarketType,
272
295
  oraclePriceData: OraclePriceData
273
296
  ): NodeToFill[] {
297
+ const marketAccount = this.marketIndexToAccount
298
+ .get(getVariant(marketType) as MarketTypeStr)
299
+ .get(marketIndex);
300
+
301
+ if (fillPaused(this.stateAccount, marketAccount)) {
302
+ return [];
303
+ }
304
+
274
305
  // Find all the crossing nodes
275
306
  const crossingNodesToFill: Array<NodeToFill> = this.findCrossingNodesToFill(
276
307
  marketIndex,
@@ -279,25 +310,27 @@ export class DLOB {
279
310
  oraclePriceData
280
311
  );
281
312
 
282
- const vAMMCrossingNodesToFill: Array<NodeToFill> =
283
- this.findvAMMCrossingNodesToFill(
313
+ let fallbackCrossingNodesToFill = new Array<NodeToFill>();
314
+ if (!ammPaused(this.stateAccount, marketAccount)) {
315
+ fallbackCrossingNodesToFill = this.findFallbackCrossingNodesToFill(
284
316
  marketIndex,
285
- vBid,
286
- vAsk,
317
+ fallbackBid,
318
+ fallbackAsk,
287
319
  slot,
288
320
  marketType,
289
321
  oraclePriceData
290
322
  );
323
+ }
291
324
 
292
325
  // get expired market nodes
293
- const marketNodesToFill = this.findExpiredMarketNodesToFill(
326
+ const expiredNodesToFill = this.findExpiredNodesToFill(
294
327
  marketIndex,
295
- slot,
328
+ ts,
296
329
  marketType
297
330
  );
298
331
  return crossingNodesToFill.concat(
299
- vAMMCrossingNodesToFill,
300
- marketNodesToFill
332
+ fallbackCrossingNodesToFill,
333
+ expiredNodesToFill
301
334
  );
302
335
  }
303
336
 
@@ -357,10 +390,10 @@ export class DLOB {
357
390
  return nodesToFill;
358
391
  }
359
392
 
360
- public findvAMMCrossingNodesToFill(
393
+ public findFallbackCrossingNodesToFill(
361
394
  marketIndex: number,
362
- vBid: BN,
363
- vAsk: BN,
395
+ fallbackBid: BN,
396
+ fallbackAsk: BN,
364
397
  slot: number,
365
398
  marketType: MarketType,
366
399
  oraclePriceData: OraclePriceData
@@ -385,35 +418,67 @@ export class DLOB {
385
418
  let nextAsk = askGenerator.next();
386
419
  let nextBid = bidGenerator.next();
387
420
 
388
- // check for asks that cross vBid
389
- while (!nextAsk.done) {
421
+ // check for asks that cross fallbackBid
422
+ while (!nextAsk.done && fallbackBid !== undefined) {
390
423
  const askNode = nextAsk.value;
391
- const askPrice = askNode.getPrice(oraclePriceData, slot);
392
424
 
393
- if (askPrice.lte(vBid) && isAuctionComplete(askNode.order, slot)) {
425
+ if (isVariant(marketType, 'spot') && askNode.order?.postOnly) {
426
+ nextAsk = askGenerator.next();
427
+ continue;
428
+ }
429
+
430
+ const askLimitPrice = getOptionalLimitPrice(
431
+ askNode.order,
432
+ oraclePriceData,
433
+ slot
434
+ );
435
+
436
+ // order crosses if there is no limit price or it crosses fallback price
437
+ const crosses =
438
+ askLimitPrice === undefined || askLimitPrice.lte(fallbackBid);
439
+
440
+ // fallback is available if auction is complete or it's a spot order
441
+ const fallbackAvailable =
442
+ isVariant(marketType, 'spot') || isAuctionComplete(askNode.order, slot);
443
+
444
+ if (crosses && fallbackAvailable) {
394
445
  nodesToFill.push({
395
446
  node: askNode,
396
- makerNode: undefined, // filled by vAMM
447
+ makerNode: undefined, // filled by fallback
397
448
  });
398
- } else {
399
- break;
400
449
  }
401
450
 
402
451
  nextAsk = askGenerator.next();
403
452
  }
404
453
 
405
- // check for bids that cross vAsk
406
- while (!nextBid.done) {
454
+ // check for bids that cross fallbackAsk
455
+ while (!nextBid.done && fallbackAsk !== undefined) {
407
456
  const bidNode = nextBid.value;
408
- const bidPrice = bidNode.getPrice(oraclePriceData, slot);
409
457
 
410
- if (bidPrice.gte(vAsk) && isAuctionComplete(bidNode.order, slot)) {
458
+ if (isVariant(marketType, 'spot') && bidNode.order?.postOnly) {
459
+ nextBid = bidGenerator.next();
460
+ continue;
461
+ }
462
+
463
+ const bidLimitPrice = getOptionalLimitPrice(
464
+ bidNode.order,
465
+ oraclePriceData,
466
+ slot
467
+ );
468
+
469
+ // order crosses if there is no limit price or it crosses fallback price
470
+ const crosses =
471
+ bidLimitPrice === undefined || bidLimitPrice.gte(fallbackAsk);
472
+
473
+ // fallback is available if auction is complete or it's a spot order
474
+ const fallbackAvailable =
475
+ isVariant(marketType, 'spot') || isAuctionComplete(bidNode.order, slot);
476
+
477
+ if (crosses && fallbackAvailable) {
411
478
  nodesToFill.push({
412
479
  node: bidNode,
413
- makerNode: undefined, // filled by vAMM
480
+ makerNode: undefined, // filled by fallback
414
481
  });
415
- } else {
416
- break;
417
482
  }
418
483
 
419
484
  nextBid = bidGenerator.next();
@@ -422,28 +487,48 @@ export class DLOB {
422
487
  return nodesToFill;
423
488
  }
424
489
 
425
- public findExpiredMarketNodesToFill(
490
+ public findExpiredNodesToFill(
426
491
  marketIndex: number,
427
- slot: number,
492
+ ts: number,
428
493
  marketType: MarketType
429
494
  ): NodeToFill[] {
430
495
  const nodesToFill = new Array<NodeToFill>();
431
- // Then see if there are orders to fill against vamm
432
- for (const marketBid of this.getMarketBids(marketIndex, marketType)) {
433
- if (isOrderExpired(marketBid.order, slot)) {
434
- nodesToFill.push({
435
- node: marketBid,
436
- });
496
+
497
+ const marketTypeStr = getVariant(marketType) as MarketTypeStr;
498
+ const nodeLists = this.orderLists.get(marketTypeStr).get(marketIndex);
499
+
500
+ // All bids/asks that can expire
501
+ const bidGenerators = [
502
+ nodeLists.limit.bid.getGenerator(),
503
+ nodeLists.floatingLimit.bid.getGenerator(),
504
+ nodeLists.market.bid.getGenerator(),
505
+ ];
506
+ const askGenerators = [
507
+ nodeLists.limit.ask.getGenerator(),
508
+ nodeLists.floatingLimit.ask.getGenerator(),
509
+ nodeLists.market.ask.getGenerator(),
510
+ ];
511
+
512
+ for (const bidGenerator of bidGenerators) {
513
+ for (const bid of bidGenerator) {
514
+ if (isOrderExpired(bid.order, ts)) {
515
+ nodesToFill.push({
516
+ node: bid,
517
+ });
518
+ }
437
519
  }
438
520
  }
439
521
 
440
- for (const marketAsk of this.getMarketAsks(marketIndex, marketType)) {
441
- if (isOrderExpired(marketAsk.order, slot)) {
442
- nodesToFill.push({
443
- node: marketAsk,
444
- });
522
+ for (const askGenerator of askGenerators) {
523
+ for (const ask of askGenerator) {
524
+ if (isOrderExpired(ask.order, ts)) {
525
+ nodesToFill.push({
526
+ node: ask,
527
+ });
528
+ }
445
529
  }
446
530
  }
531
+
447
532
  return nodesToFill;
448
533
  }
449
534
 
@@ -496,7 +581,7 @@ export class DLOB {
496
581
 
497
582
  *getAsks(
498
583
  marketIndex: number,
499
- vAsk: BN | undefined,
584
+ fallbackAsk: BN | undefined,
500
585
  slot: number,
501
586
  marketType: MarketType,
502
587
  oraclePriceData: OraclePriceData
@@ -513,8 +598,11 @@ export class DLOB {
513
598
  nodeLists.market.ask.getGenerator(),
514
599
  ];
515
600
 
516
- if (marketTypeStr === 'perp' && vAsk) {
517
- generatorList.push(getVammNodeGenerator(vAsk));
601
+ if (marketTypeStr === 'perp' && fallbackAsk) {
602
+ generatorList.push(getVammNodeGenerator(fallbackAsk));
603
+ }
604
+ if (generatorList.length === 0) {
605
+ throw new Error('No ask generators found');
518
606
  }
519
607
 
520
608
  const askGenerators = generatorList.map((generator) => {
@@ -558,6 +646,20 @@ export class DLOB {
558
646
  continue;
559
647
  }
560
648
 
649
+ // skip order if user is being liquidated/bankrupt
650
+ if (bestGenerator.next.value.userAccount !== undefined) {
651
+ const user = this.userMap.get(
652
+ bestGenerator.next.value.userAccount.toString()
653
+ );
654
+ if (
655
+ user?.getUserAccount().isBeingLiquidated ||
656
+ user?.getUserAccount().isBankrupt
657
+ ) {
658
+ bestGenerator.next = bestGenerator.generator.next();
659
+ continue;
660
+ }
661
+ }
662
+
561
663
  yield bestGenerator.next.value;
562
664
  bestGenerator.next = bestGenerator.generator.next();
563
665
  } else {
@@ -568,7 +670,7 @@ export class DLOB {
568
670
 
569
671
  *getBids(
570
672
  marketIndex: number,
571
- vBid: BN | undefined,
673
+ fallbackBid: BN | undefined,
572
674
  slot: number,
573
675
  marketType: MarketType,
574
676
  oraclePriceData: OraclePriceData
@@ -585,9 +687,13 @@ export class DLOB {
585
687
  nodeLists.floatingLimit.bid.getGenerator(),
586
688
  nodeLists.market.bid.getGenerator(),
587
689
  ];
588
- if (marketTypeStr === 'perp' && vBid) {
589
- generatorList.push(getVammNodeGenerator(vBid));
690
+ if (marketTypeStr === 'perp' && fallbackBid) {
691
+ generatorList.push(getVammNodeGenerator(fallbackBid));
590
692
  }
693
+ if (generatorList.length === 0) {
694
+ throw new Error('No bid generators found');
695
+ }
696
+
591
697
  const bidGenerators = generatorList.map((generator) => {
592
698
  return {
593
699
  next: generator.next(),
@@ -595,7 +701,7 @@ export class DLOB {
595
701
  };
596
702
  });
597
703
 
598
- let bidsExhausted = false; // there will always be the vBid
704
+ let bidsExhausted = false; // there will always be the fallbackBid
599
705
  while (!bidsExhausted) {
600
706
  const bestGenerator = bidGenerators.reduce(
601
707
  (bestGenerator, currentGenerator) => {
@@ -629,6 +735,20 @@ export class DLOB {
629
735
  continue;
630
736
  }
631
737
 
738
+ // skip order if user is being liquidated/bankrupt
739
+ if (bestGenerator.next.value.userAccount !== undefined) {
740
+ const user = this.userMap.get(
741
+ bestGenerator.next.value.userAccount.toString()
742
+ );
743
+ if (
744
+ user?.getUserAccount().isBeingLiquidated ||
745
+ user?.getUserAccount().isBankrupt
746
+ ) {
747
+ bestGenerator.next = bestGenerator.generator.next();
748
+ continue;
749
+ }
750
+ }
751
+
632
752
  yield bestGenerator.next.value;
633
753
  bestGenerator.next = bestGenerator.generator.next();
634
754
  } else {
@@ -660,9 +780,16 @@ export class DLOB {
660
780
  const bidOrder = bidNode.order;
661
781
  const askOrder = askNode.order;
662
782
 
663
- // Can't match two maker orders or if maker and taker are the same
664
- const makerIsTaker = bidNode.userAccount.equals(askNode.userAccount);
665
- if (makerIsTaker || (bidOrder.postOnly && askOrder.postOnly)) {
783
+ const bidUserAuthority = this.userMap.getUserAuthority(
784
+ bidNode.userAccount.toString()
785
+ );
786
+ const askUserAuthority = this.userMap.getUserAuthority(
787
+ askNode.userAccount.toString()
788
+ );
789
+
790
+ // Can't match orders from the same authority
791
+ const sameAuthority = bidUserAuthority.equals(askUserAuthority);
792
+ if (sameAuthority || (bidOrder.postOnly && askOrder.postOnly)) {
666
793
  // don't have a principle way to pick which one to exhaust,
667
794
  // exhaust each one 50% of the time so we can try each one against other orders
668
795
  const exhaustedSide = Math.random() < 0.5 ? 'bid' : 'ask';
@@ -672,6 +799,19 @@ export class DLOB {
672
799
  };
673
800
  }
674
801
 
802
+ const { takerNode, makerNode, makerSide } = this.determineMakerAndTaker(
803
+ askNode,
804
+ bidNode
805
+ );
806
+
807
+ // If order doesn't have limit price, cant be maker
808
+ if (!hasLimitPrice(makerNode.order, slot)) {
809
+ return {
810
+ crossingNodes: [],
811
+ exhaustedSide: makerSide,
812
+ };
813
+ }
814
+
675
815
  const bidBaseRemaining = bidOrder.baseAssetAmount.sub(
676
816
  bidOrder.baseAssetAmountFilled
677
817
  );
@@ -744,68 +884,92 @@ export class DLOB {
744
884
  );
745
885
  }
746
886
 
747
- // Bid is maker
748
- if (bidOrder.postOnly) {
749
- return {
750
- crossingNodes: [
751
- {
752
- node: askNode,
753
- makerNode: bidNode,
754
- },
755
- ],
756
- exhaustedSide,
757
- };
758
- }
759
-
760
- // Ask is maker
761
- if (askOrder.postOnly) {
762
- return {
763
- crossingNodes: [
764
- {
765
- node: bidNode,
766
- makerNode: askNode,
767
- },
768
- ],
769
- exhaustedSide,
770
- };
771
- }
772
-
773
- // Both are takers
774
- // older order is maker
775
- const [olderNode, newerNode] = askOrder.ts.lt(bidOrder.ts)
776
- ? [askNode, bidNode]
777
- : [bidNode, askNode];
778
887
  return {
779
888
  crossingNodes: [
780
889
  {
781
- node: newerNode,
782
- makerNode: olderNode,
890
+ node: takerNode,
891
+ makerNode: makerNode,
783
892
  },
784
893
  ],
785
894
  exhaustedSide,
786
895
  };
787
896
  }
788
897
 
898
+ determineMakerAndTaker(
899
+ askNode: DLOBNode,
900
+ bidNode: DLOBNode
901
+ ): { takerNode: DLOBNode; makerNode: DLOBNode; makerSide: Side } {
902
+ if (bidNode.order.postOnly) {
903
+ return {
904
+ takerNode: askNode,
905
+ makerNode: bidNode,
906
+ makerSide: 'bid',
907
+ };
908
+ } else if (askNode.order.postOnly) {
909
+ return {
910
+ takerNode: bidNode,
911
+ makerNode: askNode,
912
+ makerSide: 'ask',
913
+ };
914
+ } else if (isMarketOrder(bidNode.order) && isLimitOrder(askNode.order)) {
915
+ return {
916
+ takerNode: bidNode,
917
+ makerNode: askNode,
918
+ makerSide: 'ask',
919
+ };
920
+ } else if (isMarketOrder(askNode.order) && isLimitOrder(bidNode.order)) {
921
+ return {
922
+ takerNode: askNode,
923
+ makerNode: bidNode,
924
+ makerSide: 'bid',
925
+ };
926
+ } else if (askNode.order.slot.lt(bidNode.order.slot)) {
927
+ return {
928
+ takerNode: bidNode,
929
+ makerNode: askNode,
930
+ makerSide: 'ask',
931
+ };
932
+ } else {
933
+ return {
934
+ takerNode: askNode,
935
+ makerNode: bidNode,
936
+ makerSide: 'bid',
937
+ };
938
+ }
939
+ }
940
+
789
941
  public getBestAsk(
790
942
  marketIndex: number,
791
- vAsk: BN | undefined,
943
+ fallbackAsk: BN | undefined,
792
944
  slot: number,
793
945
  marketType: MarketType,
794
946
  oraclePriceData: OraclePriceData
795
947
  ): BN {
796
- return this.getAsks(marketIndex, vAsk, slot, marketType, oraclePriceData)
948
+ return this.getAsks(
949
+ marketIndex,
950
+ fallbackAsk,
951
+ slot,
952
+ marketType,
953
+ oraclePriceData
954
+ )
797
955
  .next()
798
956
  .value.getPrice(oraclePriceData, slot);
799
957
  }
800
958
 
801
959
  public getBestBid(
802
960
  marketIndex: number,
803
- vBid: BN | undefined,
961
+ fallbackBid: BN | undefined,
804
962
  slot: number,
805
963
  marketType: MarketType,
806
964
  oraclePriceData: OraclePriceData
807
965
  ): BN {
808
- return this.getBids(marketIndex, vBid, slot, marketType, oraclePriceData)
966
+ return this.getBids(
967
+ marketIndex,
968
+ fallbackBid,
969
+ slot,
970
+ marketType,
971
+ oraclePriceData
972
+ )
809
973
  .next()
810
974
  .value.getPrice(oraclePriceData, slot);
811
975
  }
@@ -816,6 +980,10 @@ export class DLOB {
816
980
  oraclePrice: BN,
817
981
  marketType: MarketType
818
982
  ): NodeToTrigger[] {
983
+ if (exchangePaused(this.stateAccount)) {
984
+ return [];
985
+ }
986
+
819
987
  const nodesToTrigger = [];
820
988
  const marketTypeStr = getVariant(marketType) as MarketTypeStr;
821
989
  for (const node of this.orderLists
@@ -862,20 +1030,21 @@ export class DLOB {
862
1030
  const market = clearingHouse.getPerpMarketAccount(marketIndex);
863
1031
 
864
1032
  const slot = slotSubscriber.getSlot();
865
- const oraclePriceData = clearingHouse.getOracleDataForMarket(marketIndex);
866
- const vAsk = calculateAskPrice(market, oraclePriceData);
867
- const vBid = calculateBidPrice(market, oraclePriceData);
1033
+ const oraclePriceData =
1034
+ clearingHouse.getOracleDataForPerpMarket(marketIndex);
1035
+ const fallbackAsk = calculateAskPrice(market, oraclePriceData);
1036
+ const fallbackBid = calculateBidPrice(market, oraclePriceData);
868
1037
 
869
1038
  const bestAsk = this.getBestAsk(
870
1039
  marketIndex,
871
- vAsk,
1040
+ fallbackAsk,
872
1041
  slot,
873
1042
  marketType,
874
1043
  oraclePriceData
875
1044
  );
876
1045
  const bestBid = this.getBestBid(
877
1046
  marketIndex,
878
- vBid,
1047
+ fallbackBid,
879
1048
  slot,
880
1049
  marketType,
881
1050
  oraclePriceData
@@ -907,7 +1076,8 @@ export class DLOB {
907
1076
  );
908
1077
  } else if (isVariant(marketType, 'spot')) {
909
1078
  const slot = slotSubscriber.getSlot();
910
- const oraclePriceData = clearingHouse.getOracleDataForMarket(marketIndex);
1079
+ const oraclePriceData =
1080
+ clearingHouse.getOracleDataForPerpMarket(marketIndex);
911
1081
 
912
1082
  const bestAsk = this.getBestAsk(
913
1083
  marketIndex,
@@ -1,5 +1,4 @@
1
1
  import {
2
- isOneOfVariant,
3
2
  AMM_RESERVE_PRECISION,
4
3
  BN,
5
4
  convertToNumber,
@@ -74,18 +73,7 @@ export abstract class OrderNode implements DLOBNode {
74
73
  }
75
74
 
76
75
  getPrice(oraclePriceData: OraclePriceData, slot: number): BN {
77
- if (isOneOfVariant(this.order.marketType, ['spot'])) {
78
- return getLimitPrice(this.order, oraclePriceData, slot);
79
- } else if (isOneOfVariant(this.order.marketType, ['perp'])) {
80
- return getLimitPrice(
81
- this.order,
82
- oraclePriceData,
83
- slot,
84
- this.market as PerpMarketAccount
85
- );
86
- } else {
87
- console.error(`Unknown market type: ${this.order.marketType}`);
88
- }
76
+ return getLimitPrice(this.order, oraclePriceData, slot);
89
77
  }
90
78
 
91
79
  isBaseFilled(): boolean {
@@ -111,7 +99,7 @@ export class FloatingLimitOrderNode extends OrderNode {
111
99
  previous?: FloatingLimitOrderNode;
112
100
 
113
101
  getSortValue(order: Order): BN {
114
- return order.oraclePriceOffset;
102
+ return new BN(order.oraclePriceOffset);
115
103
  }
116
104
  }
117
105