@drift-labs/sdk 2.0.12 → 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/lib/dlob/DLOB.d.ts +8 -3
- package/lib/dlob/DLOB.js +209 -146
- package/lib/driftClient.d.ts +19 -0
- package/lib/driftClient.js +32 -0
- package/lib/math/orders.d.ts +1 -2
- package/lib/math/orders.js +4 -20
- package/lib/userMap/userMap.d.ts +2 -1
- package/lib/userMap/userMap.js +40 -0
- package/lib/userMap/userStatsMap.d.ts +2 -1
- package/lib/userMap/userStatsMap.js +65 -0
- package/package.json +1 -1
- package/src/dlob/DLOB.ts +381 -197
- package/src/driftClient.ts +41 -0
- package/src/math/orders.ts +5 -22
- package/src/userMap/userMap.ts +44 -0
- package/src/userMap/userStatsMap.ts +76 -0
- package/tests/dlob/test.ts +295 -63
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
|
-
|
|
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
|
-
|
|
314
|
-
|
|
315
|
-
fallbackCrossingNodesToFill = this.findFallbackCrossingNodesToFill(
|
|
306
|
+
const marketOrderNodesToFill: Array<NodeToFill> =
|
|
307
|
+
this.findMarketNodesToFill(
|
|
316
308
|
marketIndex,
|
|
317
|
-
|
|
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
|
|
332
|
-
|
|
334
|
+
return marketOrderNodesToFill.concat(
|
|
335
|
+
limitOrderNodesToFill,
|
|
333
336
|
expiredNodesToFill
|
|
334
337
|
);
|
|
335
338
|
}
|
|
336
339
|
|
|
337
|
-
public
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
419
|
-
|
|
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
|
-
|
|
422
|
-
|
|
423
|
-
|
|
514
|
+
for (const marketBidToFill of marketBidsToFill) {
|
|
515
|
+
nodesToFill.push(marketBidToFill);
|
|
516
|
+
}
|
|
424
517
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
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
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
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
|
-
//
|
|
437
|
-
const
|
|
438
|
-
askLimitPrice === undefined || askLimitPrice.lte(fallbackBid);
|
|
562
|
+
// Can't match orders from the same authority
|
|
563
|
+
const sameAuthority = bidUserAuthority.equals(askUserAuthority);
|
|
439
564
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
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
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
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
|
-
|
|
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
|
-
|
|
455
|
-
|
|
456
|
-
const bidNode = nextBid.value;
|
|
625
|
+
return nodesToFill;
|
|
626
|
+
}
|
|
457
627
|
|
|
458
|
-
|
|
459
|
-
|
|
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
|
|
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(
|
|
654
|
+
isVariant(marketType, 'spot') || isAuctionComplete(node.order, slot);
|
|
476
655
|
|
|
477
656
|
if (crosses && fallbackAvailable) {
|
|
478
657
|
nodesToFill.push({
|
|
479
|
-
node:
|
|
658
|
+
node: node,
|
|
480
659
|
makerNode: undefined, // filled by fallback
|
|
481
660
|
});
|
|
482
661
|
}
|
|
483
662
|
|
|
484
|
-
|
|
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
|
-
|
|
739
|
+
*getMarketBids(
|
|
561
740
|
marketIndex: number,
|
|
562
741
|
marketType: MarketType
|
|
563
742
|
): Generator<DLOBNode> {
|
|
564
743
|
const marketTypeStr = getVariant(marketType) as MarketTypeStr;
|
|
565
|
-
|
|
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
|
-
|
|
756
|
+
*getMarketAsks(
|
|
572
757
|
marketIndex: number,
|
|
573
758
|
marketType: MarketType
|
|
574
759
|
): Generator<DLOBNode> {
|
|
575
760
|
const marketTypeStr = getVariant(marketType) as MarketTypeStr;
|
|
576
|
-
|
|
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
|
-
*
|
|
583
|
-
|
|
584
|
-
|
|
773
|
+
private *getBestNode(
|
|
774
|
+
generatorList: Array<Generator<DLOBNode>>,
|
|
775
|
+
oraclePriceData: OraclePriceData,
|
|
585
776
|
slot: number,
|
|
586
|
-
|
|
587
|
-
oraclePriceData: OraclePriceData
|
|
777
|
+
compareFcn: (bestPrice: BN, currentPrice: BN) => boolean
|
|
588
778
|
): Generator<DLOBNode> {
|
|
589
|
-
|
|
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
|
|
616
|
-
while (!
|
|
617
|
-
const bestGenerator =
|
|
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
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
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
|
-
|
|
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
|
-
|
|
839
|
+
sideExhausted = true;
|
|
664
840
|
}
|
|
665
841
|
}
|
|
666
842
|
}
|
|
667
843
|
|
|
668
|
-
*
|
|
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
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
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
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
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
|
-
|
|
710
|
-
|
|
711
|
-
|
|
910
|
+
const generatorList = [
|
|
911
|
+
this.getMarketAsks(marketIndex, marketType),
|
|
912
|
+
this.getLimitAsks(marketIndex, slot, marketType, oraclePriceData),
|
|
913
|
+
];
|
|
712
914
|
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
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
|
-
|
|
723
|
-
|
|
724
|
-
|
|
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
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
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
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
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
|
-
|
|
747
|
-
|
|
748
|
-
|
|
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
|
-
|
|
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
|
|
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,
|