@drift-labs/sdk 2.65.0-beta.3 → 2.65.0-beta.5
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 +1 -1
- package/lib/dlob/orderBookLevels.js +34 -6
- package/lib/userMap/userMap.d.ts +3 -1
- package/lib/userMap/userMap.js +8 -1
- package/package.json +1 -1
- package/src/dlob/orderBookLevels.ts +42 -6
- package/src/userMap/userMap.ts +23 -2
- package/tests/dlob/test.ts +63 -0
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.65.0-beta.
|
|
1
|
+
2.65.0-beta.5
|
|
@@ -311,6 +311,18 @@ function uncrossL2(bids, asks, oraclePrice, oracleTwap5Min, markTwap5Min, groupi
|
|
|
311
311
|
const referencePrice = oraclePrice.add(markTwap5Min.sub(oracleTwap5Min));
|
|
312
312
|
let bidIndex = 0;
|
|
313
313
|
let askIndex = 0;
|
|
314
|
+
let maxBid;
|
|
315
|
+
let minAsk;
|
|
316
|
+
const getPriceAndSetBound = (newPrice, direction) => {
|
|
317
|
+
if ((0, __1.isVariant)(direction, 'long')) {
|
|
318
|
+
maxBid = maxBid ? __1.BN.min(maxBid, newPrice) : newPrice;
|
|
319
|
+
return maxBid;
|
|
320
|
+
}
|
|
321
|
+
else {
|
|
322
|
+
minAsk = minAsk ? __1.BN.max(minAsk, newPrice) : newPrice;
|
|
323
|
+
return minAsk;
|
|
324
|
+
}
|
|
325
|
+
};
|
|
314
326
|
while (bidIndex < bids.length || askIndex < asks.length) {
|
|
315
327
|
const nextBid = cloneL2Level(bids[bidIndex]);
|
|
316
328
|
const nextAsk = cloneL2Level(asks[askIndex]);
|
|
@@ -337,19 +349,23 @@ function uncrossL2(bids, asks, oraclePrice, oracleTwap5Min, markTwap5Min, groupi
|
|
|
337
349
|
}
|
|
338
350
|
if (nextBid.price.gt(referencePrice) &&
|
|
339
351
|
nextAsk.price.gt(referencePrice)) {
|
|
340
|
-
|
|
352
|
+
let newBidPrice = nextAsk.price.sub(grouping);
|
|
353
|
+
newBidPrice = getPriceAndSetBound(newBidPrice, __1.PositionDirection.LONG);
|
|
341
354
|
updateLevels(newBidPrice, nextBid, newBids);
|
|
342
355
|
bidIndex++;
|
|
343
356
|
}
|
|
344
357
|
else if (nextAsk.price.lt(referencePrice) &&
|
|
345
358
|
nextBid.price.lt(referencePrice)) {
|
|
346
|
-
|
|
359
|
+
let newAskPrice = nextBid.price.add(grouping);
|
|
360
|
+
newAskPrice = getPriceAndSetBound(newAskPrice, __1.PositionDirection.SHORT);
|
|
347
361
|
updateLevels(newAskPrice, nextAsk, newAsks);
|
|
348
362
|
askIndex++;
|
|
349
363
|
}
|
|
350
364
|
else {
|
|
351
|
-
|
|
352
|
-
|
|
365
|
+
let newBidPrice = referencePrice.sub(grouping);
|
|
366
|
+
let newAskPrice = referencePrice.add(grouping);
|
|
367
|
+
newBidPrice = getPriceAndSetBound(newBidPrice, __1.PositionDirection.LONG);
|
|
368
|
+
newAskPrice = getPriceAndSetBound(newAskPrice, __1.PositionDirection.SHORT);
|
|
353
369
|
updateLevels(newBidPrice, nextBid, newBids);
|
|
354
370
|
updateLevels(newAskPrice, nextAsk, newAsks);
|
|
355
371
|
bidIndex++;
|
|
@@ -357,9 +373,21 @@ function uncrossL2(bids, asks, oraclePrice, oracleTwap5Min, markTwap5Min, groupi
|
|
|
357
373
|
}
|
|
358
374
|
}
|
|
359
375
|
else {
|
|
360
|
-
|
|
376
|
+
if (minAsk && nextAsk.price.lte(minAsk)) {
|
|
377
|
+
const newAskPrice = getPriceAndSetBound(nextAsk.price, __1.PositionDirection.SHORT);
|
|
378
|
+
updateLevels(newAskPrice, nextAsk, newAsks);
|
|
379
|
+
}
|
|
380
|
+
else {
|
|
381
|
+
newAsks.push(nextAsk);
|
|
382
|
+
}
|
|
361
383
|
askIndex++;
|
|
362
|
-
|
|
384
|
+
if (maxBid && nextBid.price.gte(maxBid)) {
|
|
385
|
+
const newBidPrice = getPriceAndSetBound(nextBid.price, __1.PositionDirection.LONG);
|
|
386
|
+
updateLevels(newBidPrice, nextBid, newBids);
|
|
387
|
+
}
|
|
388
|
+
else {
|
|
389
|
+
newBids.push(nextBid);
|
|
390
|
+
}
|
|
363
391
|
bidIndex++;
|
|
364
392
|
}
|
|
365
393
|
}
|
package/lib/userMap/userMap.d.ts
CHANGED
|
@@ -8,7 +8,8 @@ export interface UserMapInterface {
|
|
|
8
8
|
has(key: string): boolean;
|
|
9
9
|
get(key: string): User | undefined;
|
|
10
10
|
getWithSlot(key: string): DataAndSlot<User> | undefined;
|
|
11
|
-
mustGet(key: string): Promise<User>;
|
|
11
|
+
mustGet(key: string, accountSubscription?: UserSubscriptionConfig): Promise<User>;
|
|
12
|
+
mustGetWithSlot(key: string, accountSubscription?: UserSubscriptionConfig): Promise<DataAndSlot<User>>;
|
|
12
13
|
getUserAuthority(key: string): PublicKey | undefined;
|
|
13
14
|
updateWithOrderRecord(record: OrderRecord): Promise<void>;
|
|
14
15
|
values(): IterableIterator<User>;
|
|
@@ -50,6 +51,7 @@ export declare class UserMap implements UserMapInterface {
|
|
|
50
51
|
* @returns User
|
|
51
52
|
*/
|
|
52
53
|
mustGet(key: string, accountSubscription?: UserSubscriptionConfig): Promise<User>;
|
|
54
|
+
mustGetWithSlot(key: string, accountSubscription?: UserSubscriptionConfig): Promise<DataAndSlot<User>>;
|
|
53
55
|
/**
|
|
54
56
|
* gets the Authority for a particular userAccountPublicKey, if no User exists, undefined is returned
|
|
55
57
|
* @param key userAccountPublicKey to get User for
|
package/lib/userMap/userMap.js
CHANGED
|
@@ -73,6 +73,7 @@ class UserMap {
|
|
|
73
73
|
await this.subscription.subscribe();
|
|
74
74
|
}
|
|
75
75
|
async addPubkey(userAccountPublicKey, userAccount, slot, accountSubscription) {
|
|
76
|
+
var _a;
|
|
76
77
|
const user = new __1.User({
|
|
77
78
|
driftClient: this.driftClient,
|
|
78
79
|
userAccountPublicKey,
|
|
@@ -84,7 +85,7 @@ class UserMap {
|
|
|
84
85
|
await user.subscribe(userAccount);
|
|
85
86
|
this.userMap.set(userAccountPublicKey.toString(), {
|
|
86
87
|
data: user,
|
|
87
|
-
slot: slot !== null &&
|
|
88
|
+
slot: (_a = user.getUserAccountAndSlot().slot) !== null && _a !== void 0 ? _a : -1,
|
|
88
89
|
});
|
|
89
90
|
}
|
|
90
91
|
has(key) {
|
|
@@ -113,6 +114,12 @@ class UserMap {
|
|
|
113
114
|
}
|
|
114
115
|
return this.userMap.get(key).data;
|
|
115
116
|
}
|
|
117
|
+
async mustGetWithSlot(key, accountSubscription) {
|
|
118
|
+
if (!this.has(key)) {
|
|
119
|
+
await this.addPubkey(new web3_js_1.PublicKey(key), undefined, undefined, accountSubscription);
|
|
120
|
+
}
|
|
121
|
+
return this.userMap.get(key);
|
|
122
|
+
}
|
|
116
123
|
/**
|
|
117
124
|
* gets the Authority for a particular userAccountPublicKey, if no User exists, undefined is returned
|
|
118
125
|
* @param key userAccountPublicKey to get User for
|
package/package.json
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
calculateSpreadReserves,
|
|
8
8
|
calculateUpdatedAMM,
|
|
9
9
|
DLOBNode,
|
|
10
|
+
isVariant,
|
|
10
11
|
OraclePriceData,
|
|
11
12
|
PerpMarketAccount,
|
|
12
13
|
PositionDirection,
|
|
@@ -492,6 +493,19 @@ export function uncrossL2(
|
|
|
492
493
|
|
|
493
494
|
let bidIndex = 0;
|
|
494
495
|
let askIndex = 0;
|
|
496
|
+
let maxBid: BN;
|
|
497
|
+
let minAsk: BN;
|
|
498
|
+
|
|
499
|
+
const getPriceAndSetBound = (newPrice: BN, direction: PositionDirection) => {
|
|
500
|
+
if (isVariant(direction, 'long')) {
|
|
501
|
+
maxBid = maxBid ? BN.min(maxBid, newPrice) : newPrice;
|
|
502
|
+
return maxBid;
|
|
503
|
+
} else {
|
|
504
|
+
minAsk = minAsk ? BN.max(minAsk, newPrice) : newPrice;
|
|
505
|
+
return minAsk;
|
|
506
|
+
}
|
|
507
|
+
};
|
|
508
|
+
|
|
495
509
|
while (bidIndex < bids.length || askIndex < asks.length) {
|
|
496
510
|
const nextBid = cloneL2Level(bids[bidIndex]);
|
|
497
511
|
const nextAsk = cloneL2Level(asks[askIndex]);
|
|
@@ -525,29 +539,51 @@ export function uncrossL2(
|
|
|
525
539
|
nextBid.price.gt(referencePrice) &&
|
|
526
540
|
nextAsk.price.gt(referencePrice)
|
|
527
541
|
) {
|
|
528
|
-
|
|
542
|
+
let newBidPrice = nextAsk.price.sub(grouping);
|
|
543
|
+
newBidPrice = getPriceAndSetBound(newBidPrice, PositionDirection.LONG);
|
|
529
544
|
updateLevels(newBidPrice, nextBid, newBids);
|
|
530
545
|
bidIndex++;
|
|
531
546
|
} else if (
|
|
532
547
|
nextAsk.price.lt(referencePrice) &&
|
|
533
548
|
nextBid.price.lt(referencePrice)
|
|
534
549
|
) {
|
|
535
|
-
|
|
550
|
+
let newAskPrice = nextBid.price.add(grouping);
|
|
551
|
+
newAskPrice = getPriceAndSetBound(newAskPrice, PositionDirection.SHORT);
|
|
536
552
|
updateLevels(newAskPrice, nextAsk, newAsks);
|
|
537
553
|
askIndex++;
|
|
538
554
|
} else {
|
|
539
|
-
|
|
540
|
-
|
|
555
|
+
let newBidPrice = referencePrice.sub(grouping);
|
|
556
|
+
let newAskPrice = referencePrice.add(grouping);
|
|
557
|
+
|
|
558
|
+
newBidPrice = getPriceAndSetBound(newBidPrice, PositionDirection.LONG);
|
|
559
|
+
newAskPrice = getPriceAndSetBound(newAskPrice, PositionDirection.SHORT);
|
|
560
|
+
|
|
541
561
|
updateLevels(newBidPrice, nextBid, newBids);
|
|
542
562
|
updateLevels(newAskPrice, nextAsk, newAsks);
|
|
543
563
|
bidIndex++;
|
|
544
564
|
askIndex++;
|
|
545
565
|
}
|
|
546
566
|
} else {
|
|
547
|
-
|
|
567
|
+
if (minAsk && nextAsk.price.lte(minAsk)) {
|
|
568
|
+
const newAskPrice = getPriceAndSetBound(
|
|
569
|
+
nextAsk.price,
|
|
570
|
+
PositionDirection.SHORT
|
|
571
|
+
);
|
|
572
|
+
updateLevels(newAskPrice, nextAsk, newAsks);
|
|
573
|
+
} else {
|
|
574
|
+
newAsks.push(nextAsk);
|
|
575
|
+
}
|
|
548
576
|
askIndex++;
|
|
549
577
|
|
|
550
|
-
|
|
578
|
+
if (maxBid && nextBid.price.gte(maxBid)) {
|
|
579
|
+
const newBidPrice = getPriceAndSetBound(
|
|
580
|
+
nextBid.price,
|
|
581
|
+
PositionDirection.LONG
|
|
582
|
+
);
|
|
583
|
+
updateLevels(newBidPrice, nextBid, newBids);
|
|
584
|
+
} else {
|
|
585
|
+
newBids.push(nextBid);
|
|
586
|
+
}
|
|
551
587
|
bidIndex++;
|
|
552
588
|
}
|
|
553
589
|
}
|
package/src/userMap/userMap.ts
CHANGED
|
@@ -42,7 +42,14 @@ export interface UserMapInterface {
|
|
|
42
42
|
has(key: string): boolean;
|
|
43
43
|
get(key: string): User | undefined;
|
|
44
44
|
getWithSlot(key: string): DataAndSlot<User> | undefined;
|
|
45
|
-
mustGet(
|
|
45
|
+
mustGet(
|
|
46
|
+
key: string,
|
|
47
|
+
accountSubscription?: UserSubscriptionConfig
|
|
48
|
+
): Promise<User>;
|
|
49
|
+
mustGetWithSlot(
|
|
50
|
+
key: string,
|
|
51
|
+
accountSubscription?: UserSubscriptionConfig
|
|
52
|
+
): Promise<DataAndSlot<User>>;
|
|
46
53
|
getUserAuthority(key: string): PublicKey | undefined;
|
|
47
54
|
updateWithOrderRecord(record: OrderRecord): Promise<void>;
|
|
48
55
|
values(): IterableIterator<User>;
|
|
@@ -157,7 +164,7 @@ export class UserMap implements UserMapInterface {
|
|
|
157
164
|
await user.subscribe(userAccount);
|
|
158
165
|
this.userMap.set(userAccountPublicKey.toString(), {
|
|
159
166
|
data: user,
|
|
160
|
-
slot: slot ?? -1,
|
|
167
|
+
slot: user.getUserAccountAndSlot().slot ?? -1,
|
|
161
168
|
});
|
|
162
169
|
}
|
|
163
170
|
|
|
@@ -196,6 +203,20 @@ export class UserMap implements UserMapInterface {
|
|
|
196
203
|
}
|
|
197
204
|
return this.userMap.get(key).data;
|
|
198
205
|
}
|
|
206
|
+
public async mustGetWithSlot(
|
|
207
|
+
key: string,
|
|
208
|
+
accountSubscription?: UserSubscriptionConfig
|
|
209
|
+
): Promise<DataAndSlot<User>> {
|
|
210
|
+
if (!this.has(key)) {
|
|
211
|
+
await this.addPubkey(
|
|
212
|
+
new PublicKey(key),
|
|
213
|
+
undefined,
|
|
214
|
+
undefined,
|
|
215
|
+
accountSubscription
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
return this.userMap.get(key);
|
|
219
|
+
}
|
|
199
220
|
|
|
200
221
|
/**
|
|
201
222
|
* gets the Authority for a particular userAccountPublicKey, if no User exists, undefined is returned
|
package/tests/dlob/test.ts
CHANGED
|
@@ -6677,4 +6677,67 @@ describe('Uncross L2', () => {
|
|
|
6677
6677
|
expect(asksAreSortedAsc(newAsks), "Uncrossed asks are ascending").to.be.true;
|
|
6678
6678
|
expect(bidsAreSortedDesc(newBids), "Uncrossed bids are descending").to.be.true;
|
|
6679
6679
|
});
|
|
6680
|
+
|
|
6681
|
+
it('Crossing edge case : top bid and ask have a big cross, following ones dont - shouldnt get uncrossed out of order', () => {
|
|
6682
|
+
const bids = [
|
|
6683
|
+
"101825900",
|
|
6684
|
+
"101783900",
|
|
6685
|
+
"101783000",
|
|
6686
|
+
"101782600",
|
|
6687
|
+
"101770700",
|
|
6688
|
+
"101770200",
|
|
6689
|
+
"101749857",
|
|
6690
|
+
"101735900",
|
|
6691
|
+
"101729994",
|
|
6692
|
+
"101726900",
|
|
6693
|
+
].map(priceStr => (
|
|
6694
|
+
{
|
|
6695
|
+
price: new BN(priceStr),
|
|
6696
|
+
size: new BN(1).mul(BASE_PRECISION),
|
|
6697
|
+
sources: { vamm: new BN(1).mul(BASE_PRECISION) },
|
|
6698
|
+
}
|
|
6699
|
+
));
|
|
6700
|
+
|
|
6701
|
+
const asks = [
|
|
6702
|
+
"101750700",
|
|
6703
|
+
"101790467",
|
|
6704
|
+
"101793400",
|
|
6705
|
+
"101794116",
|
|
6706
|
+
"101798548",
|
|
6707
|
+
"101799532",
|
|
6708
|
+
"101803500",
|
|
6709
|
+
"101820927",
|
|
6710
|
+
"101823900",
|
|
6711
|
+
"101827638",
|
|
6712
|
+
].map(priceStr => ({
|
|
6713
|
+
price: new BN(priceStr),
|
|
6714
|
+
size: new BN(1).mul(BASE_PRECISION),
|
|
6715
|
+
sources: { vamm: new BN(1).mul(BASE_PRECISION) },
|
|
6716
|
+
}));
|
|
6717
|
+
|
|
6718
|
+
expect(asksAreSortedAsc(asks), "Input asks are ascending").to.be.true;
|
|
6719
|
+
expect(bidsAreSortedDesc(bids), "Input bids are descending").to.be.true;
|
|
6720
|
+
|
|
6721
|
+
const oraclePrice = new BN("101711384");
|
|
6722
|
+
const oraclePrice5Min = new BN("101805000");
|
|
6723
|
+
const markPrice5Min = new BN("101867000");
|
|
6724
|
+
|
|
6725
|
+
const groupingSize = new BN("100");
|
|
6726
|
+
|
|
6727
|
+
const userAsks = new Set<string>();
|
|
6728
|
+
|
|
6729
|
+
const { bids: newBids, asks: newAsks } = uncrossL2(
|
|
6730
|
+
bids,
|
|
6731
|
+
asks,
|
|
6732
|
+
oraclePrice,
|
|
6733
|
+
oraclePrice5Min,
|
|
6734
|
+
markPrice5Min,
|
|
6735
|
+
groupingSize,
|
|
6736
|
+
new Set<string>(),
|
|
6737
|
+
userAsks
|
|
6738
|
+
);
|
|
6739
|
+
|
|
6740
|
+
expect(asksAreSortedAsc(newAsks), "Uncrossed asks are ascending").to.be.true;
|
|
6741
|
+
expect(bidsAreSortedDesc(newBids), "Uncrossed bids are descending").to.be.true;
|
|
6742
|
+
});
|
|
6680
6743
|
});
|