@drift-labs/sdk 2.0.13 → 2.0.14

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.
package/src/dlob/DLOB.ts CHANGED
@@ -21,10 +21,9 @@ import {
21
21
  MarketTypeStr,
22
22
  StateAccount,
23
23
  isMarketOrder,
24
- isLimitOrder,
25
- getOptionalLimitPrice,
26
24
  mustBeTriggered,
27
25
  isTriggered,
26
+ getLimitPrice,
28
27
  } from '..';
29
28
  import { PublicKey } from '@solana/web3.js';
30
29
  import { DLOBNode, DLOBNodeType, TriggerOrderNode } from '..';
@@ -302,25 +301,29 @@ export class DLOB {
302
301
  return [];
303
302
  }
304
303
 
305
- // Find all the crossing nodes
306
- const crossingNodesToFill: Array<NodeToFill> = this.findCrossingNodesToFill(
307
- marketIndex,
308
- slot,
309
- marketType,
310
- oraclePriceData
311
- );
304
+ const isAmmPaused = ammPaused(this.stateAccount, marketAccount);
312
305
 
313
- let fallbackCrossingNodesToFill = new Array<NodeToFill>();
314
- if (!ammPaused(this.stateAccount, marketAccount)) {
315
- fallbackCrossingNodesToFill = this.findFallbackCrossingNodesToFill(
306
+ const marketOrderNodesToFill: Array<NodeToFill> =
307
+ this.findMarketNodesToFill(
316
308
  marketIndex,
317
- fallbackBid,
309
+ slot,
310
+ marketType,
311
+ oraclePriceData,
312
+ isAmmPaused,
318
313
  fallbackAsk,
314
+ fallbackBid
315
+ );
316
+
317
+ const limitOrderNodesToFill: Array<NodeToFill> =
318
+ this.findLimitOrderNodesToFill(
319
+ marketIndex,
319
320
  slot,
320
321
  marketType,
321
- oraclePriceData
322
+ oraclePriceData,
323
+ isAmmPaused,
324
+ fallbackAsk,
325
+ fallbackBid
322
326
  );
323
- }
324
327
 
325
328
  // get expired market nodes
326
329
  const expiredNodesToFill = this.findExpiredNodesToFill(
@@ -328,30 +331,32 @@ export class DLOB {
328
331
  ts,
329
332
  marketType
330
333
  );
331
- return crossingNodesToFill.concat(
332
- fallbackCrossingNodesToFill,
334
+ return marketOrderNodesToFill.concat(
335
+ limitOrderNodesToFill,
333
336
  expiredNodesToFill
334
337
  );
335
338
  }
336
339
 
337
- public findCrossingNodesToFill(
340
+ public findLimitOrderNodesToFill(
338
341
  marketIndex: number,
339
342
  slot: number,
340
343
  marketType: MarketType,
341
- oraclePriceData: OraclePriceData
344
+ oraclePriceData: OraclePriceData,
345
+ isAmmPaused: boolean,
346
+ fallbackAsk: BN | undefined,
347
+ fallbackBid: BN | undefined
342
348
  ): NodeToFill[] {
343
349
  const nodesToFill = new Array<NodeToFill>();
344
350
 
345
- const askGenerator = this.getAsks(
351
+ let askGenerator = this.getLimitAsks(
346
352
  marketIndex,
347
- undefined, // dont include vask
348
353
  slot,
349
354
  marketType,
350
355
  oraclePriceData
351
356
  );
352
- const bidGenerator = this.getBids(
357
+
358
+ let bidGenerator = this.getLimitBids(
353
359
  marketIndex,
354
- undefined, // dont include vbid
355
360
  slot,
356
361
  marketType,
357
362
  oraclePriceData
@@ -362,7 +367,7 @@ export class DLOB {
362
367
 
363
368
  // First try to find orders that cross
364
369
  while (!nextAsk.done && !nextBid.done) {
365
- const { crossingNodes, exhaustedSide } = this.findCrossingOrders(
370
+ const { crossingNodes, exhaustedSide } = this.findCrossingLimitOrders(
366
371
  nextAsk.value,
367
372
  nextBid.value,
368
373
  oraclePriceData,
@@ -387,101 +392,275 @@ export class DLOB {
387
392
  nodesToFill.push(crossingNode);
388
393
  }
389
394
  }
395
+
396
+ if (fallbackBid && !isAmmPaused) {
397
+ askGenerator = this.getLimitAsks(
398
+ marketIndex,
399
+ slot,
400
+ marketType,
401
+ oraclePriceData
402
+ );
403
+ const asksCrossingFallback = this.findNodesCrossingFallbackLiquidity(
404
+ marketType,
405
+ slot,
406
+ oraclePriceData,
407
+ askGenerator,
408
+ fallbackBid,
409
+ (askPrice, fallbackPrice) => {
410
+ return askPrice.lte(fallbackPrice);
411
+ }
412
+ );
413
+
414
+ for (const askCrossingFallback of asksCrossingFallback) {
415
+ nodesToFill.push(askCrossingFallback);
416
+ }
417
+ }
418
+
419
+ if (fallbackAsk && !isAmmPaused) {
420
+ bidGenerator = this.getLimitBids(
421
+ marketIndex,
422
+ slot,
423
+ marketType,
424
+ oraclePriceData
425
+ );
426
+ const bidsCrossingFallback = this.findNodesCrossingFallbackLiquidity(
427
+ marketType,
428
+ slot,
429
+ oraclePriceData,
430
+ bidGenerator,
431
+ fallbackAsk,
432
+ (bidPrice, fallbackPrice) => {
433
+ return bidPrice.gte(fallbackPrice);
434
+ }
435
+ );
436
+
437
+ for (const bidCrossingFallback of bidsCrossingFallback) {
438
+ nodesToFill.push(bidCrossingFallback);
439
+ }
440
+ }
441
+
390
442
  return nodesToFill;
391
443
  }
392
444
 
393
- public findFallbackCrossingNodesToFill(
445
+ public findMarketNodesToFill(
394
446
  marketIndex: number,
395
- fallbackBid: BN,
396
- fallbackAsk: BN,
397
447
  slot: number,
398
448
  marketType: MarketType,
399
- oraclePriceData: OraclePriceData
449
+ oraclePriceData: OraclePriceData,
450
+ isAmmPaused: boolean,
451
+ fallbackAsk: BN | undefined,
452
+ fallbackBid?: BN | undefined
400
453
  ): NodeToFill[] {
401
454
  const nodesToFill = new Array<NodeToFill>();
402
455
 
403
- const askGenerator = this.getAsks(
456
+ let marketOrderGenerator = this.getMarketAsks(marketIndex, marketType);
457
+ let limitOrderGenerator = this.getLimitBids(
404
458
  marketIndex,
405
- undefined, // dont include vask
406
459
  slot,
407
460
  marketType,
408
461
  oraclePriceData
409
462
  );
410
- const bidGenerator = this.getBids(
463
+
464
+ const marketAsksCrossingBids = this.findMarketNodesCrossingLimitNodes(
465
+ slot,
466
+ oraclePriceData,
467
+ marketOrderGenerator,
468
+ limitOrderGenerator,
469
+ (takerPrice, makerPrice) => {
470
+ return takerPrice === undefined || takerPrice.lte(makerPrice);
471
+ }
472
+ );
473
+ for (const marketAskCrossingBid of marketAsksCrossingBids) {
474
+ nodesToFill.push(marketAskCrossingBid);
475
+ }
476
+
477
+ if (fallbackBid && !isAmmPaused) {
478
+ marketOrderGenerator = this.getMarketAsks(marketIndex, marketType);
479
+ const marketAsksCrossingFallback =
480
+ this.findNodesCrossingFallbackLiquidity(
481
+ marketType,
482
+ slot,
483
+ oraclePriceData,
484
+ marketOrderGenerator,
485
+ fallbackBid,
486
+ (takerPrice, fallbackPrice) => {
487
+ return takerPrice === undefined || takerPrice.lte(fallbackPrice);
488
+ }
489
+ );
490
+
491
+ for (const marketAskCrossingFallback of marketAsksCrossingFallback) {
492
+ nodesToFill.push(marketAskCrossingFallback);
493
+ }
494
+ }
495
+
496
+ marketOrderGenerator = this.getMarketBids(marketIndex, marketType);
497
+ limitOrderGenerator = this.getLimitAsks(
411
498
  marketIndex,
412
- undefined, // dont include vbid
413
499
  slot,
414
500
  marketType,
415
501
  oraclePriceData
416
502
  );
417
503
 
418
- let nextAsk = askGenerator.next();
419
- let nextBid = bidGenerator.next();
504
+ const marketBidsToFill = this.findMarketNodesCrossingLimitNodes(
505
+ slot,
506
+ oraclePriceData,
507
+ marketOrderGenerator,
508
+ limitOrderGenerator,
509
+ (takerPrice, fallbackPrice) => {
510
+ return takerPrice === undefined || takerPrice.gte(fallbackPrice);
511
+ }
512
+ );
420
513
 
421
- // check for asks that cross fallbackBid
422
- while (!nextAsk.done && fallbackBid !== undefined) {
423
- const askNode = nextAsk.value;
514
+ for (const marketBidToFill of marketBidsToFill) {
515
+ nodesToFill.push(marketBidToFill);
516
+ }
424
517
 
425
- if (isVariant(marketType, 'spot') && askNode.order?.postOnly) {
426
- nextAsk = askGenerator.next();
427
- continue;
518
+ if (fallbackAsk && !isAmmPaused) {
519
+ marketOrderGenerator = this.getMarketBids(marketIndex, marketType);
520
+ const marketBidsCrossingFallback =
521
+ this.findNodesCrossingFallbackLiquidity(
522
+ marketType,
523
+ slot,
524
+ oraclePriceData,
525
+ marketOrderGenerator,
526
+ fallbackAsk,
527
+ (takerPrice, fallbackPrice) => {
528
+ return takerPrice === undefined || takerPrice.gte(fallbackPrice);
529
+ }
530
+ );
531
+ for (const marketBidCrossingFallback of marketBidsCrossingFallback) {
532
+ nodesToFill.push(marketBidCrossingFallback);
428
533
  }
534
+ }
429
535
 
430
- const askLimitPrice = getOptionalLimitPrice(
431
- askNode.order,
432
- oraclePriceData,
433
- slot
536
+ return nodesToFill;
537
+ }
538
+
539
+ public findMarketNodesCrossingLimitNodes(
540
+ slot: number,
541
+ oraclePriceData: OraclePriceData,
542
+ takerNodeGenerator: Generator<DLOBNode>,
543
+ makerNodeGenerator: Generator<DLOBNode>,
544
+ doesCross: (takerPrice: BN | undefined, makerPrice: BN) => boolean
545
+ ): NodeToFill[] {
546
+ const nodesToFill = new Array<NodeToFill>();
547
+
548
+ let nextTakerNode = takerNodeGenerator.next();
549
+ let nextMakerNode = makerNodeGenerator.next();
550
+
551
+ while (!nextTakerNode.done && !nextMakerNode.done) {
552
+ const takerNode: DLOBNode = nextTakerNode.value;
553
+ const makerNode: DLOBNode = nextMakerNode.value;
554
+
555
+ const bidUserAuthority = this.userMap.getUserAuthority(
556
+ makerNode.userAccount.toString()
557
+ );
558
+ const askUserAuthority = this.userMap.getUserAuthority(
559
+ takerNode.userAccount.toString()
434
560
  );
435
561
 
436
- // order crosses if there is no limit price or it crosses fallback price
437
- const crosses =
438
- askLimitPrice === undefined || askLimitPrice.lte(fallbackBid);
562
+ // Can't match orders from the same authority
563
+ const sameAuthority = bidUserAuthority.equals(askUserAuthority);
439
564
 
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);
565
+ if (sameAuthority) {
566
+ if (Math.random() < 0.5) {
567
+ nextTakerNode = takerNodeGenerator.next();
568
+ } else {
569
+ nextMakerNode = makerNodeGenerator.next();
570
+ }
571
+ continue;
572
+ }
443
573
 
444
- if (crosses && fallbackAvailable) {
445
- nodesToFill.push({
446
- node: askNode,
447
- makerNode: undefined, // filled by fallback
448
- });
574
+ const makerPrice = makerNode.getPrice(oraclePriceData, slot);
575
+ const takerPrice = takerNode.getPrice(oraclePriceData, slot);
576
+
577
+ const ordersCross = doesCross(takerPrice, makerPrice);
578
+ if (!ordersCross) {
579
+ // market orders aren't sorted by price, they are sorted by time, so we need to traverse
580
+ // through all of em
581
+ nextTakerNode = takerNodeGenerator.next();
582
+ continue;
449
583
  }
450
584
 
451
- nextAsk = askGenerator.next();
585
+ nodesToFill.push({
586
+ node: takerNode,
587
+ makerNode: makerNode,
588
+ });
589
+
590
+ const makerOrder = makerNode.order;
591
+ const takerOrder = takerNode.order;
592
+
593
+ const makerBaseRemaining = makerOrder.baseAssetAmount.sub(
594
+ makerOrder.baseAssetAmountFilled
595
+ );
596
+ const takerBaseRemaining = takerOrder.baseAssetAmount.sub(
597
+ takerOrder.baseAssetAmountFilled
598
+ );
599
+
600
+ const baseFilled = BN.min(makerBaseRemaining, takerBaseRemaining);
601
+
602
+ const newMakerOrder = { ...makerOrder };
603
+ newMakerOrder.baseAssetAmountFilled =
604
+ makerOrder.baseAssetAmountFilled.add(baseFilled);
605
+ this.getListForOrder(newMakerOrder).update(
606
+ newMakerOrder,
607
+ makerNode.userAccount
608
+ );
609
+ if (newMakerOrder.baseAssetAmountFilled.eq(makerOrder.baseAssetAmount)) {
610
+ nextMakerNode = makerNodeGenerator.next();
611
+ }
612
+
613
+ const newTakerOrder = { ...takerOrder };
614
+ newTakerOrder.baseAssetAmountFilled =
615
+ takerOrder.baseAssetAmountFilled.add(baseFilled);
616
+ this.getListForOrder(newTakerOrder).update(
617
+ newTakerOrder,
618
+ takerNode.userAccount
619
+ );
620
+ if (newTakerOrder.baseAssetAmountFilled.eq(takerOrder.baseAssetAmount)) {
621
+ nextTakerNode = takerNodeGenerator.next();
622
+ }
452
623
  }
453
624
 
454
- // check for bids that cross fallbackAsk
455
- while (!nextBid.done && fallbackAsk !== undefined) {
456
- const bidNode = nextBid.value;
625
+ return nodesToFill;
626
+ }
457
627
 
458
- if (isVariant(marketType, 'spot') && bidNode.order?.postOnly) {
459
- nextBid = bidGenerator.next();
628
+ public findNodesCrossingFallbackLiquidity(
629
+ marketType: MarketType,
630
+ slot: number,
631
+ oraclePriceData: OraclePriceData,
632
+ nodeGenerator: Generator<DLOBNode>,
633
+ fallbackPrice: BN,
634
+ doesCross: (nodePrice: BN | undefined, fallbackPrice: BN) => boolean
635
+ ): NodeToFill[] {
636
+ const nodesToFill = new Array<NodeToFill>();
637
+
638
+ let nextNode = nodeGenerator.next();
639
+ while (!nextNode.done) {
640
+ const node = nextNode.value;
641
+
642
+ if (isVariant(marketType, 'spot') && node.order?.postOnly) {
643
+ nextNode = nodeGenerator.next();
460
644
  continue;
461
645
  }
462
646
 
463
- const bidLimitPrice = getOptionalLimitPrice(
464
- bidNode.order,
465
- oraclePriceData,
466
- slot
467
- );
647
+ const nodePrice = getLimitPrice(node.order, oraclePriceData, slot);
468
648
 
469
649
  // order crosses if there is no limit price or it crosses fallback price
470
- const crosses =
471
- bidLimitPrice === undefined || bidLimitPrice.gte(fallbackAsk);
650
+ const crosses = doesCross(nodePrice, fallbackPrice);
472
651
 
473
652
  // fallback is available if auction is complete or it's a spot order
474
653
  const fallbackAvailable =
475
- isVariant(marketType, 'spot') || isAuctionComplete(bidNode.order, slot);
654
+ isVariant(marketType, 'spot') || isAuctionComplete(node.order, slot);
476
655
 
477
656
  if (crosses && fallbackAvailable) {
478
657
  nodesToFill.push({
479
- node: bidNode,
658
+ node: node,
480
659
  makerNode: undefined, // filled by fallback
481
660
  });
482
661
  }
483
662
 
484
- nextBid = bidGenerator.next();
663
+ nextNode = nodeGenerator.next();
485
664
  }
486
665
 
487
666
  return nodesToFill;
@@ -557,64 +736,56 @@ export class DLOB {
557
736
  return nodesToFill;
558
737
  }
559
738
 
560
- public getMarketBids(
739
+ *getMarketBids(
561
740
  marketIndex: number,
562
741
  marketType: MarketType
563
742
  ): Generator<DLOBNode> {
564
743
  const marketTypeStr = getVariant(marketType) as MarketTypeStr;
565
- return this.orderLists
744
+ const generator = this.orderLists
566
745
  .get(marketTypeStr)
567
746
  .get(marketIndex)
568
747
  .market.bid.getGenerator();
748
+ for (const marketBidNode of generator) {
749
+ if (marketBidNode.isBaseFilled()) {
750
+ continue;
751
+ }
752
+ yield marketBidNode;
753
+ }
569
754
  }
570
755
 
571
- public getMarketAsks(
756
+ *getMarketAsks(
572
757
  marketIndex: number,
573
758
  marketType: MarketType
574
759
  ): Generator<DLOBNode> {
575
760
  const marketTypeStr = getVariant(marketType) as MarketTypeStr;
576
- return this.orderLists
761
+ const generator = this.orderLists
577
762
  .get(marketTypeStr)
578
763
  .get(marketIndex)
579
764
  .market.ask.getGenerator();
765
+ for (const marketAskNode of generator) {
766
+ if (marketAskNode.isBaseFilled()) {
767
+ continue;
768
+ }
769
+ yield marketAskNode;
770
+ }
580
771
  }
581
772
 
582
- *getAsks(
583
- marketIndex: number,
584
- fallbackAsk: BN | undefined,
773
+ private *getBestNode(
774
+ generatorList: Array<Generator<DLOBNode>>,
775
+ oraclePriceData: OraclePriceData,
585
776
  slot: number,
586
- marketType: MarketType,
587
- oraclePriceData: OraclePriceData
777
+ compareFcn: (bestPrice: BN, currentPrice: BN) => boolean
588
778
  ): Generator<DLOBNode> {
589
- if (isVariant(marketType, 'spot') && !oraclePriceData) {
590
- throw new Error('Must provide OraclePriceData to get spot asks');
591
- }
592
- const marketTypeStr = getVariant(marketType) as MarketTypeStr;
593
- const nodeLists = this.orderLists.get(marketTypeStr).get(marketIndex);
594
-
595
- const generatorList = [
596
- nodeLists.limit.ask.getGenerator(),
597
- nodeLists.floatingLimit.ask.getGenerator(),
598
- nodeLists.market.ask.getGenerator(),
599
- ];
600
-
601
- if (marketTypeStr === 'perp' && fallbackAsk) {
602
- generatorList.push(getVammNodeGenerator(fallbackAsk));
603
- }
604
- if (generatorList.length === 0) {
605
- throw new Error('No ask generators found');
606
- }
607
-
608
- const askGenerators = generatorList.map((generator) => {
779
+ const generators = generatorList.map((generator) => {
609
780
  return {
610
781
  next: generator.next(),
611
782
  generator,
612
783
  };
613
784
  });
614
785
 
615
- let asksExhausted = false;
616
- while (!asksExhausted) {
617
- const bestGenerator = askGenerators.reduce(
786
+ let sideExhausted = false;
787
+ while (!sideExhausted) {
788
+ const bestGenerator = generators.reduce(
618
789
  (bestGenerator, currentGenerator) => {
619
790
  if (currentGenerator.next.done) {
620
791
  return bestGenerator;
@@ -624,16 +795,21 @@ export class DLOB {
624
795
  return currentGenerator;
625
796
  }
626
797
 
627
- const bestAskPrice = bestGenerator.next.value.getPrice(
628
- oraclePriceData,
629
- slot
630
- );
631
- const currentAskPrice = currentGenerator.next.value.getPrice(
632
- oraclePriceData,
633
- slot
634
- );
798
+ const bestValue = bestGenerator.next.value as DLOBNode;
799
+ const currentValue = currentGenerator.next.value as DLOBNode;
800
+
801
+ // always return the market orders first
802
+ if (bestValue.order && isMarketOrder(bestValue.order)) {
803
+ return bestGenerator;
804
+ }
805
+ if (currentValue.order && isMarketOrder(currentValue.order)) {
806
+ return currentGenerator;
807
+ }
635
808
 
636
- return bestAskPrice.lt(currentAskPrice)
809
+ const bestPrice = bestValue.getPrice(oraclePriceData, slot);
810
+ const currentPrice = currentValue.getPrice(oraclePriceData, slot);
811
+
812
+ return compareFcn(bestPrice, currentPrice)
637
813
  ? bestGenerator
638
814
  : currentGenerator;
639
815
  }
@@ -660,14 +836,40 @@ export class DLOB {
660
836
  yield bestGenerator.next.value;
661
837
  bestGenerator.next = bestGenerator.generator.next();
662
838
  } else {
663
- asksExhausted = true;
839
+ sideExhausted = true;
664
840
  }
665
841
  }
666
842
  }
667
843
 
668
- *getBids(
844
+ *getLimitAsks(
845
+ marketIndex: number,
846
+ slot: number,
847
+ marketType: MarketType,
848
+ oraclePriceData: OraclePriceData
849
+ ): Generator<DLOBNode> {
850
+ if (isVariant(marketType, 'spot') && !oraclePriceData) {
851
+ throw new Error('Must provide OraclePriceData to get spot asks');
852
+ }
853
+ const marketTypeStr = getVariant(marketType) as MarketTypeStr;
854
+ const nodeLists = this.orderLists.get(marketTypeStr).get(marketIndex);
855
+
856
+ const generatorList = [
857
+ nodeLists.limit.ask.getGenerator(),
858
+ nodeLists.floatingLimit.ask.getGenerator(),
859
+ ];
860
+
861
+ yield* this.getBestNode(
862
+ generatorList,
863
+ oraclePriceData,
864
+ slot,
865
+ (bestPrice, currentPrice) => {
866
+ return bestPrice.lt(currentPrice);
867
+ }
868
+ );
869
+ }
870
+
871
+ *getLimitBids(
669
872
  marketIndex: number,
670
- fallbackBid: BN | undefined,
671
873
  slot: number,
672
874
  marketType: MarketType,
673
875
  oraclePriceData: OraclePriceData
@@ -682,76 +884,81 @@ export class DLOB {
682
884
  const generatorList = [
683
885
  nodeLists.limit.bid.getGenerator(),
684
886
  nodeLists.floatingLimit.bid.getGenerator(),
685
- nodeLists.market.bid.getGenerator(),
686
887
  ];
687
- if (marketTypeStr === 'perp' && fallbackBid) {
688
- generatorList.push(getVammNodeGenerator(fallbackBid));
689
- }
690
- if (generatorList.length === 0) {
691
- throw new Error('No bid generators found');
692
- }
693
888
 
694
- const bidGenerators = generatorList.map((generator) => {
695
- return {
696
- next: generator.next(),
697
- generator,
698
- };
699
- });
889
+ yield* this.getBestNode(
890
+ generatorList,
891
+ oraclePriceData,
892
+ slot,
893
+ (bestPrice, currentPrice) => {
894
+ return bestPrice.gt(currentPrice);
895
+ }
896
+ );
897
+ }
700
898
 
701
- let bidsExhausted = false; // there will always be the fallbackBid
702
- while (!bidsExhausted) {
703
- const bestGenerator = bidGenerators.reduce(
704
- (bestGenerator, currentGenerator) => {
705
- if (currentGenerator.next.done) {
706
- return bestGenerator;
707
- }
899
+ *getAsks(
900
+ marketIndex: number,
901
+ fallbackAsk: BN | undefined,
902
+ slot: number,
903
+ marketType: MarketType,
904
+ oraclePriceData: OraclePriceData
905
+ ): Generator<DLOBNode> {
906
+ if (isVariant(marketType, 'spot') && !oraclePriceData) {
907
+ throw new Error('Must provide OraclePriceData to get spot asks');
908
+ }
708
909
 
709
- if (bestGenerator.next.done) {
710
- return currentGenerator;
711
- }
910
+ const generatorList = [
911
+ this.getMarketAsks(marketIndex, marketType),
912
+ this.getLimitAsks(marketIndex, slot, marketType, oraclePriceData),
913
+ ];
712
914
 
713
- const bestBidPrice = bestGenerator.next.value.getPrice(
714
- oraclePriceData,
715
- slot
716
- );
717
- const currentBidPrice = currentGenerator.next.value.getPrice(
718
- oraclePriceData,
719
- slot
720
- );
915
+ const marketTypeStr = getVariant(marketType) as MarketTypeStr;
916
+ if (marketTypeStr === 'perp' && fallbackAsk) {
917
+ generatorList.push(getVammNodeGenerator(fallbackAsk));
918
+ }
721
919
 
722
- return bestBidPrice.gt(currentBidPrice)
723
- ? bestGenerator
724
- : currentGenerator;
725
- }
726
- );
920
+ yield* this.getBestNode(
921
+ generatorList,
922
+ oraclePriceData,
923
+ slot,
924
+ (bestPrice, currentPrice) => {
925
+ return bestPrice.lt(currentPrice);
926
+ }
927
+ );
928
+ }
727
929
 
728
- if (!bestGenerator.next.done) {
729
- // skip this node if it's already completely filled
730
- if (bestGenerator.next.value.isBaseFilled()) {
731
- bestGenerator.next = bestGenerator.generator.next();
732
- continue;
733
- }
930
+ *getBids(
931
+ marketIndex: number,
932
+ fallbackBid: BN | undefined,
933
+ slot: number,
934
+ marketType: MarketType,
935
+ oraclePriceData: OraclePriceData
936
+ ): Generator<DLOBNode> {
937
+ if (isVariant(marketType, 'spot') && !oraclePriceData) {
938
+ throw new Error('Must provide OraclePriceData to get spot bids');
939
+ }
734
940
 
735
- // skip order if user is being liquidated/bankrupt
736
- if (bestGenerator.next.value.userAccount !== undefined) {
737
- const user = this.userMap.get(
738
- bestGenerator.next.value.userAccount.toString()
739
- );
740
- if (user?.isBeingLiquidated()) {
741
- bestGenerator.next = bestGenerator.generator.next();
742
- continue;
743
- }
744
- }
941
+ const generatorList = [
942
+ this.getMarketBids(marketIndex, marketType),
943
+ this.getLimitBids(marketIndex, slot, marketType, oraclePriceData),
944
+ ];
745
945
 
746
- yield bestGenerator.next.value;
747
- bestGenerator.next = bestGenerator.generator.next();
748
- } else {
749
- bidsExhausted = true;
750
- }
946
+ const marketTypeStr = getVariant(marketType) as MarketTypeStr;
947
+ if (marketTypeStr === 'perp' && fallbackBid) {
948
+ generatorList.push(getVammNodeGenerator(fallbackBid));
751
949
  }
950
+
951
+ yield* this.getBestNode(
952
+ generatorList,
953
+ oraclePriceData,
954
+ slot,
955
+ (bestPrice, currentPrice) => {
956
+ return bestPrice.gt(currentPrice);
957
+ }
958
+ );
752
959
  }
753
960
 
754
- findCrossingOrders(
961
+ findCrossingLimitOrders(
755
962
  askNode: DLOBNode,
756
963
  bidNode: DLOBNode,
757
964
  oraclePriceData: OraclePriceData,
@@ -793,22 +1000,11 @@ export class DLOB {
793
1000
  };
794
1001
  }
795
1002
 
796
- const { takerNode, makerNode, makerSide } = this.determineMakerAndTaker(
1003
+ const { takerNode, makerNode } = this.determineMakerAndTaker(
797
1004
  askNode,
798
1005
  bidNode
799
1006
  );
800
1007
 
801
- // If maker is market order and auction is complete, cant be maker
802
- if (
803
- isMarketOrder(makerNode.order) &&
804
- isAuctionComplete(makerNode.order, slot)
805
- ) {
806
- return {
807
- crossingNodes: [],
808
- exhaustedSide: makerSide,
809
- };
810
- }
811
-
812
1008
  const bidBaseRemaining = bidOrder.baseAssetAmount.sub(
813
1009
  bidOrder.baseAssetAmountFilled
814
1010
  );
@@ -908,18 +1104,6 @@ export class DLOB {
908
1104
  makerNode: askNode,
909
1105
  makerSide: 'ask',
910
1106
  };
911
- } else if (isMarketOrder(bidNode.order) && isLimitOrder(askNode.order)) {
912
- return {
913
- takerNode: bidNode,
914
- makerNode: askNode,
915
- makerSide: 'ask',
916
- };
917
- } else if (isMarketOrder(askNode.order) && isLimitOrder(bidNode.order)) {
918
- return {
919
- takerNode: askNode,
920
- makerNode: bidNode,
921
- makerSide: 'bid',
922
- };
923
1107
  } else if (askNode.order.slot.lt(bidNode.order.slot)) {
924
1108
  return {
925
1109
  takerNode: bidNode,