@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/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>());
@@ -164,36 +176,20 @@ export class DLOB {
164
176
  /**
165
177
  * initializes a new DLOB instance
166
178
  *
167
- * @param clearingHouse The ClearingHouse instance to use for price data
168
179
  * @returns a promise that resolves when the DLOB is initialized
169
180
  */
170
- public async init(
171
- clearingHouse: ClearingHouse,
172
- userMap?: UserMap
173
- ): Promise<boolean> {
181
+ public async init(): Promise<boolean> {
174
182
  if (this.initialized) {
175
183
  return false;
176
184
  }
177
- if (userMap) {
178
- // initialize the dlob with the user map (prevents hitting getProgramAccounts)
179
- for (const user of userMap.values()) {
180
- const userAccount = user.getUserAccount();
181
- const userAccountPubkey = user.getUserAccountPublicKey();
182
185
 
183
- for (const order of userAccount.orders) {
184
- this.insertOrder(order, userAccountPubkey);
185
- }
186
- }
187
- } else {
188
- const programAccounts = await clearingHouse.program.account.user.all();
189
- for (const programAccount of programAccounts) {
190
- // @ts-ignore
191
- const userAccount: UserAccount = programAccount.account;
192
- const userAccountPublicKey = programAccount.publicKey;
193
-
194
- for (const order of userAccount.orders) {
195
- this.insertOrder(order, userAccountPublicKey);
196
- }
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);
197
193
  }
198
194
  }
199
195
 
@@ -265,7 +261,7 @@ export class DLOB {
265
261
  type = 'trigger';
266
262
  } else if (isOneOfVariant(order.orderType, ['market', 'triggerMarket'])) {
267
263
  type = 'market';
268
- } else if (!order.oraclePriceOffset.eq(ZERO)) {
264
+ } else if (order.oraclePriceOffset !== 0) {
269
265
  type = 'floatingLimit';
270
266
  } else {
271
267
  type = 'limit';
@@ -291,12 +287,21 @@ export class DLOB {
291
287
 
292
288
  public findNodesToFill(
293
289
  marketIndex: number,
294
- vBid: BN | undefined,
295
- vAsk: BN | undefined,
290
+ fallbackBid: BN | undefined,
291
+ fallbackAsk: BN | undefined,
296
292
  slot: number,
293
+ ts: number,
297
294
  marketType: MarketType,
298
295
  oraclePriceData: OraclePriceData
299
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
+
300
305
  // Find all the crossing nodes
301
306
  const crossingNodesToFill: Array<NodeToFill> = this.findCrossingNodesToFill(
302
307
  marketIndex,
@@ -305,25 +310,27 @@ export class DLOB {
305
310
  oraclePriceData
306
311
  );
307
312
 
308
- const vAMMCrossingNodesToFill: Array<NodeToFill> =
309
- this.findvAMMCrossingNodesToFill(
313
+ let fallbackCrossingNodesToFill = new Array<NodeToFill>();
314
+ if (!ammPaused(this.stateAccount, marketAccount)) {
315
+ fallbackCrossingNodesToFill = this.findFallbackCrossingNodesToFill(
310
316
  marketIndex,
311
- vBid,
312
- vAsk,
317
+ fallbackBid,
318
+ fallbackAsk,
313
319
  slot,
314
320
  marketType,
315
321
  oraclePriceData
316
322
  );
323
+ }
317
324
 
318
325
  // get expired market nodes
319
- const marketNodesToFill = this.findExpiredMarketNodesToFill(
326
+ const expiredNodesToFill = this.findExpiredNodesToFill(
320
327
  marketIndex,
321
- slot,
328
+ ts,
322
329
  marketType
323
330
  );
324
331
  return crossingNodesToFill.concat(
325
- vAMMCrossingNodesToFill,
326
- marketNodesToFill
332
+ fallbackCrossingNodesToFill,
333
+ expiredNodesToFill
327
334
  );
328
335
  }
329
336
 
@@ -383,10 +390,10 @@ export class DLOB {
383
390
  return nodesToFill;
384
391
  }
385
392
 
386
- public findvAMMCrossingNodesToFill(
393
+ public findFallbackCrossingNodesToFill(
387
394
  marketIndex: number,
388
- vBid: BN,
389
- vAsk: BN,
395
+ fallbackBid: BN,
396
+ fallbackAsk: BN,
390
397
  slot: number,
391
398
  marketType: MarketType,
392
399
  oraclePriceData: OraclePriceData
@@ -411,35 +418,67 @@ export class DLOB {
411
418
  let nextAsk = askGenerator.next();
412
419
  let nextBid = bidGenerator.next();
413
420
 
414
- // check for asks that cross vBid
415
- while (!nextAsk.done) {
421
+ // check for asks that cross fallbackBid
422
+ while (!nextAsk.done && fallbackBid !== undefined) {
416
423
  const askNode = nextAsk.value;
417
- const askPrice = askNode.getPrice(oraclePriceData, slot);
418
424
 
419
- 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) {
420
445
  nodesToFill.push({
421
446
  node: askNode,
422
- makerNode: undefined, // filled by vAMM
447
+ makerNode: undefined, // filled by fallback
423
448
  });
424
- } else {
425
- break;
426
449
  }
427
450
 
428
451
  nextAsk = askGenerator.next();
429
452
  }
430
453
 
431
- // check for bids that cross vAsk
432
- while (!nextBid.done) {
454
+ // check for bids that cross fallbackAsk
455
+ while (!nextBid.done && fallbackAsk !== undefined) {
433
456
  const bidNode = nextBid.value;
434
- const bidPrice = bidNode.getPrice(oraclePriceData, slot);
435
457
 
436
- 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) {
437
478
  nodesToFill.push({
438
479
  node: bidNode,
439
- makerNode: undefined, // filled by vAMM
480
+ makerNode: undefined, // filled by fallback
440
481
  });
441
- } else {
442
- break;
443
482
  }
444
483
 
445
484
  nextBid = bidGenerator.next();
@@ -448,28 +487,48 @@ export class DLOB {
448
487
  return nodesToFill;
449
488
  }
450
489
 
451
- public findExpiredMarketNodesToFill(
490
+ public findExpiredNodesToFill(
452
491
  marketIndex: number,
453
- slot: number,
492
+ ts: number,
454
493
  marketType: MarketType
455
494
  ): NodeToFill[] {
456
495
  const nodesToFill = new Array<NodeToFill>();
457
- // Then see if there are orders to fill against vamm
458
- for (const marketBid of this.getMarketBids(marketIndex, marketType)) {
459
- if (isOrderExpired(marketBid.order, slot)) {
460
- nodesToFill.push({
461
- node: marketBid,
462
- });
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
+ }
463
519
  }
464
520
  }
465
521
 
466
- for (const marketAsk of this.getMarketAsks(marketIndex, marketType)) {
467
- if (isOrderExpired(marketAsk.order, slot)) {
468
- nodesToFill.push({
469
- node: marketAsk,
470
- });
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
+ }
471
529
  }
472
530
  }
531
+
473
532
  return nodesToFill;
474
533
  }
475
534
 
@@ -522,7 +581,7 @@ export class DLOB {
522
581
 
523
582
  *getAsks(
524
583
  marketIndex: number,
525
- vAsk: BN | undefined,
584
+ fallbackAsk: BN | undefined,
526
585
  slot: number,
527
586
  marketType: MarketType,
528
587
  oraclePriceData: OraclePriceData
@@ -539,8 +598,8 @@ export class DLOB {
539
598
  nodeLists.market.ask.getGenerator(),
540
599
  ];
541
600
 
542
- if (marketTypeStr === 'perp' && vAsk) {
543
- generatorList.push(getVammNodeGenerator(vAsk));
601
+ if (marketTypeStr === 'perp' && fallbackAsk) {
602
+ generatorList.push(getVammNodeGenerator(fallbackAsk));
544
603
  }
545
604
  if (generatorList.length === 0) {
546
605
  throw new Error('No ask generators found');
@@ -587,6 +646,20 @@ export class DLOB {
587
646
  continue;
588
647
  }
589
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
+
590
663
  yield bestGenerator.next.value;
591
664
  bestGenerator.next = bestGenerator.generator.next();
592
665
  } else {
@@ -597,7 +670,7 @@ export class DLOB {
597
670
 
598
671
  *getBids(
599
672
  marketIndex: number,
600
- vBid: BN | undefined,
673
+ fallbackBid: BN | undefined,
601
674
  slot: number,
602
675
  marketType: MarketType,
603
676
  oraclePriceData: OraclePriceData
@@ -614,8 +687,8 @@ export class DLOB {
614
687
  nodeLists.floatingLimit.bid.getGenerator(),
615
688
  nodeLists.market.bid.getGenerator(),
616
689
  ];
617
- if (marketTypeStr === 'perp' && vBid) {
618
- generatorList.push(getVammNodeGenerator(vBid));
690
+ if (marketTypeStr === 'perp' && fallbackBid) {
691
+ generatorList.push(getVammNodeGenerator(fallbackBid));
619
692
  }
620
693
  if (generatorList.length === 0) {
621
694
  throw new Error('No bid generators found');
@@ -628,7 +701,7 @@ export class DLOB {
628
701
  };
629
702
  });
630
703
 
631
- let bidsExhausted = false; // there will always be the vBid
704
+ let bidsExhausted = false; // there will always be the fallbackBid
632
705
  while (!bidsExhausted) {
633
706
  const bestGenerator = bidGenerators.reduce(
634
707
  (bestGenerator, currentGenerator) => {
@@ -662,6 +735,20 @@ export class DLOB {
662
735
  continue;
663
736
  }
664
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
+
665
752
  yield bestGenerator.next.value;
666
753
  bestGenerator.next = bestGenerator.generator.next();
667
754
  } else {
@@ -693,9 +780,16 @@ export class DLOB {
693
780
  const bidOrder = bidNode.order;
694
781
  const askOrder = askNode.order;
695
782
 
696
- // Can't match two maker orders or if maker and taker are the same
697
- const makerIsTaker = bidNode.userAccount.equals(askNode.userAccount);
698
- 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)) {
699
793
  // don't have a principle way to pick which one to exhaust,
700
794
  // exhaust each one 50% of the time so we can try each one against other orders
701
795
  const exhaustedSide = Math.random() < 0.5 ? 'bid' : 'ask';
@@ -705,6 +799,19 @@ export class DLOB {
705
799
  };
706
800
  }
707
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
+
708
815
  const bidBaseRemaining = bidOrder.baseAssetAmount.sub(
709
816
  bidOrder.baseAssetAmountFilled
710
817
  );
@@ -777,68 +884,92 @@ export class DLOB {
777
884
  );
778
885
  }
779
886
 
780
- // Bid is maker
781
- if (bidOrder.postOnly) {
782
- return {
783
- crossingNodes: [
784
- {
785
- node: askNode,
786
- makerNode: bidNode,
787
- },
788
- ],
789
- exhaustedSide,
790
- };
791
- }
792
-
793
- // Ask is maker
794
- if (askOrder.postOnly) {
795
- return {
796
- crossingNodes: [
797
- {
798
- node: bidNode,
799
- makerNode: askNode,
800
- },
801
- ],
802
- exhaustedSide,
803
- };
804
- }
805
-
806
- // Both are takers
807
- // older order is maker
808
- const [olderNode, newerNode] = askOrder.ts.lt(bidOrder.ts)
809
- ? [askNode, bidNode]
810
- : [bidNode, askNode];
811
887
  return {
812
888
  crossingNodes: [
813
889
  {
814
- node: newerNode,
815
- makerNode: olderNode,
890
+ node: takerNode,
891
+ makerNode: makerNode,
816
892
  },
817
893
  ],
818
894
  exhaustedSide,
819
895
  };
820
896
  }
821
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
+
822
941
  public getBestAsk(
823
942
  marketIndex: number,
824
- vAsk: BN | undefined,
943
+ fallbackAsk: BN | undefined,
825
944
  slot: number,
826
945
  marketType: MarketType,
827
946
  oraclePriceData: OraclePriceData
828
947
  ): BN {
829
- return this.getAsks(marketIndex, vAsk, slot, marketType, oraclePriceData)
948
+ return this.getAsks(
949
+ marketIndex,
950
+ fallbackAsk,
951
+ slot,
952
+ marketType,
953
+ oraclePriceData
954
+ )
830
955
  .next()
831
956
  .value.getPrice(oraclePriceData, slot);
832
957
  }
833
958
 
834
959
  public getBestBid(
835
960
  marketIndex: number,
836
- vBid: BN | undefined,
961
+ fallbackBid: BN | undefined,
837
962
  slot: number,
838
963
  marketType: MarketType,
839
964
  oraclePriceData: OraclePriceData
840
965
  ): BN {
841
- return this.getBids(marketIndex, vBid, slot, marketType, oraclePriceData)
966
+ return this.getBids(
967
+ marketIndex,
968
+ fallbackBid,
969
+ slot,
970
+ marketType,
971
+ oraclePriceData
972
+ )
842
973
  .next()
843
974
  .value.getPrice(oraclePriceData, slot);
844
975
  }
@@ -849,6 +980,10 @@ export class DLOB {
849
980
  oraclePrice: BN,
850
981
  marketType: MarketType
851
982
  ): NodeToTrigger[] {
983
+ if (exchangePaused(this.stateAccount)) {
984
+ return [];
985
+ }
986
+
852
987
  const nodesToTrigger = [];
853
988
  const marketTypeStr = getVariant(marketType) as MarketTypeStr;
854
989
  for (const node of this.orderLists
@@ -895,20 +1030,21 @@ export class DLOB {
895
1030
  const market = clearingHouse.getPerpMarketAccount(marketIndex);
896
1031
 
897
1032
  const slot = slotSubscriber.getSlot();
898
- const oraclePriceData = clearingHouse.getOracleDataForMarket(marketIndex);
899
- const vAsk = calculateAskPrice(market, oraclePriceData);
900
- const vBid = calculateBidPrice(market, oraclePriceData);
1033
+ const oraclePriceData =
1034
+ clearingHouse.getOracleDataForPerpMarket(marketIndex);
1035
+ const fallbackAsk = calculateAskPrice(market, oraclePriceData);
1036
+ const fallbackBid = calculateBidPrice(market, oraclePriceData);
901
1037
 
902
1038
  const bestAsk = this.getBestAsk(
903
1039
  marketIndex,
904
- vAsk,
1040
+ fallbackAsk,
905
1041
  slot,
906
1042
  marketType,
907
1043
  oraclePriceData
908
1044
  );
909
1045
  const bestBid = this.getBestBid(
910
1046
  marketIndex,
911
- vBid,
1047
+ fallbackBid,
912
1048
  slot,
913
1049
  marketType,
914
1050
  oraclePriceData
@@ -940,7 +1076,8 @@ export class DLOB {
940
1076
  );
941
1077
  } else if (isVariant(marketType, 'spot')) {
942
1078
  const slot = slotSubscriber.getSlot();
943
- const oraclePriceData = clearingHouse.getOracleDataForMarket(marketIndex);
1079
+ const oraclePriceData =
1080
+ clearingHouse.getOracleDataForPerpMarket(marketIndex);
944
1081
 
945
1082
  const bestAsk = this.getBestAsk(
946
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
 
@@ -105,7 +105,7 @@ export class NodeList<NodeType extends keyof DLOBNodeMap>
105
105
  const newOrderSortPrice = newNode.sortValue;
106
106
 
107
107
  if (newOrderSortPrice.eq(currentOrderSortPrice)) {
108
- return newOrder.ts.lt(currentOrder.ts);
108
+ return newOrder.slot.lt(currentOrder.slot);
109
109
  }
110
110
 
111
111
  if (this.sortDirection === 'asc') {