@drift-labs/sdk 2.74.0-beta.7 → 2.74.0-beta.8

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/VERSION CHANGED
@@ -1 +1 @@
1
- 2.74.0-beta.7
1
+ 2.74.0-beta.8
@@ -261,6 +261,31 @@ function groupL2Levels(levels, grouping, direction, depth) {
261
261
  }
262
262
  return groupedLevels;
263
263
  }
264
+ /**
265
+ * Method to merge bids or asks by price
266
+ */
267
+ const mergeByPrice = (bidsOrAsks) => {
268
+ const merged = new Map();
269
+ for (const level of bidsOrAsks) {
270
+ const key = level.price.toString();
271
+ if (merged.has(key)) {
272
+ const existing = merged.get(key);
273
+ existing.size = existing.size.add(level.size);
274
+ for (const [source, size] of Object.entries(level.sources)) {
275
+ if (existing.sources[source]) {
276
+ existing.sources[source] = existing.sources[source].add(size);
277
+ }
278
+ else {
279
+ existing.sources[source] = size;
280
+ }
281
+ }
282
+ }
283
+ else {
284
+ merged.set(key, cloneL2Level(level));
285
+ }
286
+ }
287
+ return Array.from(merged.values());
288
+ };
264
289
  /**
265
290
  * The purpose of this function is uncross the L2 orderbook by modifying the bid/ask price at the top of the book
266
291
  * This will make the liquidity look worse but more intuitive (users familiar with clob get confused w temporarily
@@ -342,17 +367,17 @@ function uncrossL2(bids, asks, oraclePrice, oracleTwap5Min, markTwap5Min, groupi
342
367
  bidIndex++;
343
368
  continue;
344
369
  }
370
+ if (userBids.has(nextBid.price.toString())) {
371
+ newBids.push(nextBid);
372
+ bidIndex++;
373
+ continue;
374
+ }
375
+ if (userAsks.has(nextAsk.price.toString())) {
376
+ newAsks.push(nextAsk);
377
+ askIndex++;
378
+ continue;
379
+ }
345
380
  if (nextBid.price.gte(nextAsk.price)) {
346
- if (userBids.has(nextBid.price.toString())) {
347
- newBids.push(nextBid);
348
- bidIndex++;
349
- continue;
350
- }
351
- if (userAsks.has(nextAsk.price.toString())) {
352
- newAsks.push(nextAsk);
353
- askIndex++;
354
- continue;
355
- }
356
381
  if (nextBid.price.gt(referencePrice) &&
357
382
  nextAsk.price.gt(referencePrice)) {
358
383
  let newBidPrice = nextAsk.price.sub(grouping);
@@ -397,9 +422,13 @@ function uncrossL2(bids, asks, oraclePrice, oracleTwap5Min, markTwap5Min, groupi
397
422
  bidIndex++;
398
423
  }
399
424
  }
425
+ newBids.sort((a, b) => b.price.cmp(a.price));
426
+ newAsks.sort((a, b) => a.price.cmp(b.price));
427
+ const finalNewBids = mergeByPrice(newBids);
428
+ const finalNewAsks = mergeByPrice(newAsks);
400
429
  return {
401
- bids: newBids,
402
- asks: newAsks,
430
+ bids: finalNewBids,
431
+ asks: finalNewAsks,
403
432
  };
404
433
  }
405
434
  exports.uncrossL2 = uncrossL2;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drift-labs/sdk",
3
- "version": "2.74.0-beta.7",
3
+ "version": "2.74.0-beta.8",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "author": "crispheaney",
@@ -429,6 +429,30 @@ function groupL2Levels(
429
429
  return groupedLevels;
430
430
  }
431
431
 
432
+ /**
433
+ * Method to merge bids or asks by price
434
+ */
435
+ const mergeByPrice = (bidsOrAsks: L2Level[]) => {
436
+ const merged = new Map<string, L2Level>();
437
+ for (const level of bidsOrAsks) {
438
+ const key = level.price.toString();
439
+ if (merged.has(key)) {
440
+ const existing = merged.get(key);
441
+ existing.size = existing.size.add(level.size);
442
+ for (const [source, size] of Object.entries(level.sources)) {
443
+ if (existing.sources[source]) {
444
+ existing.sources[source] = existing.sources[source].add(size);
445
+ } else {
446
+ existing.sources[source] = size;
447
+ }
448
+ }
449
+ } else {
450
+ merged.set(key, cloneL2Level(level));
451
+ }
452
+ }
453
+ return Array.from(merged.values());
454
+ };
455
+
432
456
  /**
433
457
  * The purpose of this function is uncross the L2 orderbook by modifying the bid/ask price at the top of the book
434
458
  * This will make the liquidity look worse but more intuitive (users familiar with clob get confused w temporarily
@@ -467,8 +491,8 @@ export function uncrossL2(
467
491
  return { bids, asks };
468
492
  }
469
493
 
470
- const newBids = [];
471
- const newAsks = [];
494
+ const newBids: L2Level[] = [];
495
+ const newAsks: L2Level[] = [];
472
496
 
473
497
  const updateLevels = (newPrice: BN, oldLevel: L2Level, levels: L2Level[]) => {
474
498
  if (levels.length > 0 && levels[levels.length - 1].price.eq(newPrice)) {
@@ -528,19 +552,19 @@ export function uncrossL2(
528
552
  continue;
529
553
  }
530
554
 
531
- if (nextBid.price.gte(nextAsk.price)) {
532
- if (userBids.has(nextBid.price.toString())) {
533
- newBids.push(nextBid);
534
- bidIndex++;
535
- continue;
536
- }
555
+ if (userBids.has(nextBid.price.toString())) {
556
+ newBids.push(nextBid);
557
+ bidIndex++;
558
+ continue;
559
+ }
537
560
 
538
- if (userAsks.has(nextAsk.price.toString())) {
539
- newAsks.push(nextAsk);
540
- askIndex++;
541
- continue;
542
- }
561
+ if (userAsks.has(nextAsk.price.toString())) {
562
+ newAsks.push(nextAsk);
563
+ askIndex++;
564
+ continue;
565
+ }
543
566
 
567
+ if (nextBid.price.gte(nextAsk.price)) {
544
568
  if (
545
569
  nextBid.price.gt(referencePrice) &&
546
570
  nextAsk.price.gt(referencePrice)
@@ -594,8 +618,14 @@ export function uncrossL2(
594
618
  }
595
619
  }
596
620
 
621
+ newBids.sort((a, b) => b.price.cmp(a.price));
622
+ newAsks.sort((a, b) => a.price.cmp(b.price));
623
+
624
+ const finalNewBids = mergeByPrice(newBids);
625
+ const finalNewAsks = mergeByPrice(newAsks);
626
+
597
627
  return {
598
- bids: newBids,
599
- asks: newAsks,
628
+ bids: finalNewBids,
629
+ asks: finalNewAsks,
600
630
  };
601
631
  }
@@ -6614,6 +6614,63 @@ describe('Uncross L2', () => {
6614
6614
  );
6615
6615
  });
6616
6616
 
6617
+ it('Handles user crossing bid in second level', () => {
6618
+ const oraclePrice = new BN(190.3843 * PRICE_PRECISION.toNumber());
6619
+ const bids = [
6620
+ [190.59, 2],
6621
+ [190.588, 58.3],
6622
+ [190.5557, 5],
6623
+ [190.5547, 5],
6624
+ [190.5508, 5],
6625
+ [190.541, 2],
6626
+ [190.5099, 49.1],
6627
+ [190.5, 60],
6628
+ ].map(([price, size]) => ({
6629
+ price: new BN(price * PRICE_PRECISION.toNumber()),
6630
+ size: new BN(size * BASE_PRECISION.toNumber()),
6631
+ sources: { vamm: new BN(size * BASE_PRECISION.toNumber()) },
6632
+ }));
6633
+
6634
+ const asks = [
6635
+ [190.5, 86.5],
6636
+ [190.6159, 1],
6637
+ [190.656, 10.5],
6638
+ [190.6561, 1],
6639
+ [190.6585, 5],
6640
+ [190.6595, 5],
6641
+ [190.6596, 5],
6642
+ ].map(([price, size]) => ({
6643
+ price: new BN(price * PRICE_PRECISION.toNumber()),
6644
+ size: new BN(size * BASE_PRECISION.toNumber()),
6645
+ sources: { vamm: new BN(size * BASE_PRECISION.toNumber()) },
6646
+ }));
6647
+
6648
+ expect(asksAreSortedAsc(asks), 'Input asks are ascending').to.be.true;
6649
+ expect(bidsAreSortedDesc(bids), 'Input bids are descending').to.be.true;
6650
+
6651
+ const groupingSize = new BN('100');
6652
+
6653
+ const userBidPrice = new BN(190.588 * PRICE_PRECISION.toNumber());
6654
+ const userBids = new Set<string>([userBidPrice.toString()]);
6655
+
6656
+ const { bids: newBids, asks: newAsks } = uncrossL2(
6657
+ bids,
6658
+ asks,
6659
+ oraclePrice,
6660
+ oraclePrice,
6661
+ oraclePrice,
6662
+ groupingSize,
6663
+ userBids,
6664
+ new Set<string>()
6665
+ );
6666
+
6667
+ expect(asksAreSortedAsc(newAsks), 'Uncrossed asks are ascending').to.be
6668
+ .true;
6669
+ expect(bidsAreSortedDesc(newBids), 'Uncrossed bids are descending').to.be
6670
+ .true;
6671
+ expect(newBids[0].price.toString()).to.equal(userBidPrice.toString());
6672
+ });
6673
+
6617
6674
  it('Handles edge case bide and asks with large cross and an overlapping level', () => {
6618
6675
  const bids = [
6619
6676
  '104411000',