@drift-labs/sdk 2.65.0-beta.0 → 2.65.0-beta.2

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.65.0-beta.0
1
+ 2.65.0-beta.2
@@ -324,7 +324,7 @@ function uncrossL2(bids, asks, oraclePrice, oracleTwap5Min, markTwap5Min, groupi
324
324
  bidIndex++;
325
325
  continue;
326
326
  }
327
- if (nextBid.price.gt(nextAsk.price)) {
327
+ if (nextBid.price.gte(nextAsk.price)) {
328
328
  if (userBids.has(nextBid.price.toString())) {
329
329
  newBids.push(nextBid);
330
330
  bidIndex++;
@@ -1,4 +1,4 @@
1
- import { User, DriftClient, UserAccount, OrderRecord, WrappedEvent, DLOB, UserSubscriptionConfig } from '..';
1
+ import { User, DriftClient, UserAccount, OrderRecord, WrappedEvent, DLOB, UserSubscriptionConfig, DataAndSlot } from '..';
2
2
  import { PublicKey } from '@solana/web3.js';
3
3
  import { UserAccountFilterCriteria as UserFilterCriteria, UserMapConfig } from './userMapConfig';
4
4
  export interface UserMapInterface {
@@ -7,10 +7,14 @@ export interface UserMapInterface {
7
7
  addPubkey(userAccountPublicKey: PublicKey): Promise<void>;
8
8
  has(key: string): boolean;
9
9
  get(key: string): User | undefined;
10
+ getWithSlot(key: string): DataAndSlot<User> | undefined;
10
11
  mustGet(key: string): Promise<User>;
11
12
  getUserAuthority(key: string): PublicKey | undefined;
12
13
  updateWithOrderRecord(record: OrderRecord): Promise<void>;
13
14
  values(): IterableIterator<User>;
15
+ valuesWithSlot(): IterableIterator<DataAndSlot<User>>;
16
+ entries(): IterableIterator<[string, User]>;
17
+ entriesWithSlot(): IterableIterator<[string, DataAndSlot<User>]>;
14
18
  }
15
19
  export declare class UserMap implements UserMapInterface {
16
20
  private userMap;
@@ -39,6 +43,7 @@ export declare class UserMap implements UserMapInterface {
39
43
  * @returns user User | undefined
40
44
  */
41
45
  get(key: string): User | undefined;
46
+ getWithSlot(key: string): DataAndSlot<User> | undefined;
42
47
  /**
43
48
  * gets the User for a particular userAccountPublicKey, if no User exists, new one is created
44
49
  * @param key userAccountPublicKey to get User for
@@ -60,6 +65,9 @@ export declare class UserMap implements UserMapInterface {
60
65
  updateWithOrderRecord(record: OrderRecord): Promise<void>;
61
66
  updateWithEventRecord(record: WrappedEvent<any>): Promise<void>;
62
67
  values(): IterableIterator<User>;
68
+ valuesWithSlot(): IterableIterator<DataAndSlot<User>>;
69
+ entries(): IterableIterator<[string, User]>;
70
+ entriesWithSlot(): IterableIterator<[string, DataAndSlot<User>]>;
63
71
  size(): number;
64
72
  /**
65
73
  * Returns a unique list of authorities for all users in the UserMap that meet the filter criteria
@@ -82,7 +82,10 @@ class UserMap {
82
82
  },
83
83
  });
84
84
  await user.subscribe(userAccount);
85
- this.userMap.set(userAccountPublicKey.toString(), user);
85
+ this.userMap.set(userAccountPublicKey.toString(), {
86
+ data: user,
87
+ slot: slot !== null && slot !== void 0 ? slot : -1,
88
+ });
86
89
  }
87
90
  has(key) {
88
91
  return this.userMap.has(key);
@@ -93,6 +96,10 @@ class UserMap {
93
96
  * @returns user User | undefined
94
97
  */
95
98
  get(key) {
99
+ var _a;
100
+ return (_a = this.userMap.get(key)) === null || _a === void 0 ? void 0 : _a.data;
101
+ }
102
+ getWithSlot(key) {
96
103
  return this.userMap.get(key);
97
104
  }
98
105
  /**
@@ -104,8 +111,7 @@ class UserMap {
104
111
  if (!this.has(key)) {
105
112
  await this.addPubkey(new web3_js_1.PublicKey(key), undefined, undefined, accountSubscription);
106
113
  }
107
- const user = this.userMap.get(key);
108
- return user;
114
+ return this.userMap.get(key).data;
109
115
  }
110
116
  /**
111
117
  * gets the Authority for a particular userAccountPublicKey, if no User exists, undefined is returned
@@ -113,11 +119,11 @@ class UserMap {
113
119
  * @returns authority PublicKey | undefined
114
120
  */
115
121
  getUserAuthority(key) {
116
- const chUser = this.userMap.get(key);
117
- if (!chUser) {
122
+ const user = this.userMap.get(key);
123
+ if (!user) {
118
124
  return undefined;
119
125
  }
120
- return chUser.getUserAccount().authority;
126
+ return user.data.getUserAccount().authority;
121
127
  }
122
128
  /**
123
129
  * implements the {@link DLOBSource} interface
@@ -174,9 +180,22 @@ class UserMap {
174
180
  await this.mustGet(lpRecord.user.toString());
175
181
  }
176
182
  }
177
- values() {
183
+ *values() {
184
+ for (const dataAndSlot of this.userMap.values()) {
185
+ yield dataAndSlot.data;
186
+ }
187
+ }
188
+ valuesWithSlot() {
178
189
  return this.userMap.values();
179
190
  }
191
+ *entries() {
192
+ for (const [key, dataAndSlot] of this.userMap.entries()) {
193
+ yield [key, dataAndSlot.data];
194
+ }
195
+ }
196
+ entriesWithSlot() {
197
+ return this.userMap.entries();
198
+ }
180
199
  size() {
181
200
  return this.userMap.size;
182
201
  }
@@ -186,7 +205,7 @@ class UserMap {
186
205
  * @returns
187
206
  */
188
207
  getUniqueAuthorities(filterCriteria) {
189
- const usersMeetingCriteria = Array.from(this.userMap.values()).filter((user) => {
208
+ const usersMeetingCriteria = Array.from(this.values()).filter((user) => {
190
209
  let pass = true;
191
210
  if (filterCriteria && filterCriteria.hasOpenOrders) {
192
211
  pass = pass && user.getUserAccount().hasOpenOrder;
@@ -233,17 +252,17 @@ class UserMap {
233
252
  for (const [key, buffer] of programAccountBufferMap.entries()) {
234
253
  if (!this.has(key)) {
235
254
  const userAccount = this.decode('User', buffer);
236
- await this.addPubkey(new web3_js_1.PublicKey(key), userAccount);
237
- this.userMap.get(key).accountSubscriber.updateData(userAccount, slot);
255
+ await this.addPubkey(new web3_js_1.PublicKey(key), userAccount, slot);
256
+ this.get(key).accountSubscriber.updateData(userAccount, slot);
238
257
  }
239
258
  else {
240
259
  const userAccount = this.decode('User', buffer);
241
- this.userMap.get(key).accountSubscriber.updateData(userAccount, slot);
260
+ this.get(key).accountSubscriber.updateData(userAccount, slot);
242
261
  }
243
262
  // give event loop a chance to breathe
244
263
  await new Promise((resolve) => setTimeout(resolve, 0));
245
264
  }
246
- for (const [key, user] of this.userMap.entries()) {
265
+ for (const [key, user] of this.entries()) {
247
266
  if (!programAccountBufferMap.has(key)) {
248
267
  await user.unsubscribe();
249
268
  this.userMap.delete(key);
@@ -263,7 +282,7 @@ class UserMap {
263
282
  }
264
283
  async unsubscribe() {
265
284
  await this.subscription.unsubscribe();
266
- for (const [key, user] of this.userMap.entries()) {
285
+ for (const [key, user] of this.entries()) {
267
286
  await user.unsubscribe();
268
287
  this.userMap.delete(key);
269
288
  }
@@ -276,12 +295,16 @@ class UserMap {
276
295
  }
277
296
  async updateUserAccount(key, userAccount, slot) {
278
297
  this.updateLatestSlot(slot);
279
- if (!this.userMap.has(key)) {
298
+ if (!this.has(key)) {
280
299
  this.addPubkey(new web3_js_1.PublicKey(key), userAccount, slot);
281
300
  }
282
301
  else {
283
- const user = this.userMap.get(key);
302
+ const user = this.get(key);
284
303
  user.accountSubscriber.updateData(userAccount, slot);
304
+ this.userMap.set(key, {
305
+ data: user,
306
+ slot,
307
+ });
285
308
  }
286
309
  }
287
310
  updateLatestSlot(slot) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drift-labs/sdk",
3
- "version": "2.65.0-beta.0",
3
+ "version": "2.65.0-beta.2",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "author": "crispheaney",
@@ -508,7 +508,7 @@ export function uncrossL2(
508
508
  continue;
509
509
  }
510
510
 
511
- if (nextBid.price.gt(nextAsk.price)) {
511
+ if (nextBid.price.gte(nextAsk.price)) {
512
512
  if (userBids.has(nextBid.price.toString())) {
513
513
  newBids.push(nextBid);
514
514
  bidIndex++;
@@ -16,6 +16,7 @@ import {
16
16
  OneShotUserAccountSubscriber,
17
17
  BN,
18
18
  UserSubscriptionConfig,
19
+ DataAndSlot,
19
20
  } from '..';
20
21
 
21
22
  import {
@@ -40,14 +41,18 @@ export interface UserMapInterface {
40
41
  addPubkey(userAccountPublicKey: PublicKey): Promise<void>;
41
42
  has(key: string): boolean;
42
43
  get(key: string): User | undefined;
44
+ getWithSlot(key: string): DataAndSlot<User> | undefined;
43
45
  mustGet(key: string): Promise<User>;
44
46
  getUserAuthority(key: string): PublicKey | undefined;
45
47
  updateWithOrderRecord(record: OrderRecord): Promise<void>;
46
48
  values(): IterableIterator<User>;
49
+ valuesWithSlot(): IterableIterator<DataAndSlot<User>>;
50
+ entries(): IterableIterator<[string, User]>;
51
+ entriesWithSlot(): IterableIterator<[string, DataAndSlot<User>]>;
47
52
  }
48
53
 
49
54
  export class UserMap implements UserMapInterface {
50
- private userMap = new Map<string, User>();
55
+ private userMap = new Map<string, DataAndSlot<User>>();
51
56
  driftClient: DriftClient;
52
57
  private connection: Connection;
53
58
  private commitment: Commitment;
@@ -150,7 +155,10 @@ export class UserMap implements UserMapInterface {
150
155
  },
151
156
  });
152
157
  await user.subscribe(userAccount);
153
- this.userMap.set(userAccountPublicKey.toString(), user);
158
+ this.userMap.set(userAccountPublicKey.toString(), {
159
+ data: user,
160
+ slot: slot ?? -1,
161
+ });
154
162
  }
155
163
 
156
164
  public has(key: string): boolean {
@@ -163,6 +171,9 @@ export class UserMap implements UserMapInterface {
163
171
  * @returns user User | undefined
164
172
  */
165
173
  public get(key: string): User | undefined {
174
+ return this.userMap.get(key)?.data;
175
+ }
176
+ public getWithSlot(key: string): DataAndSlot<User> | undefined {
166
177
  return this.userMap.get(key);
167
178
  }
168
179
 
@@ -183,8 +194,7 @@ export class UserMap implements UserMapInterface {
183
194
  accountSubscription
184
195
  );
185
196
  }
186
- const user = this.userMap.get(key);
187
- return user;
197
+ return this.userMap.get(key).data;
188
198
  }
189
199
 
190
200
  /**
@@ -193,11 +203,11 @@ export class UserMap implements UserMapInterface {
193
203
  * @returns authority PublicKey | undefined
194
204
  */
195
205
  public getUserAuthority(key: string): PublicKey | undefined {
196
- const chUser = this.userMap.get(key);
197
- if (!chUser) {
206
+ const user = this.userMap.get(key);
207
+ if (!user) {
198
208
  return undefined;
199
209
  }
200
- return chUser.getUserAccount().authority;
210
+ return user.data.getUserAccount().authority;
201
211
  }
202
212
 
203
213
  /**
@@ -253,10 +263,24 @@ export class UserMap implements UserMapInterface {
253
263
  }
254
264
  }
255
265
 
256
- public values(): IterableIterator<User> {
266
+ public *values(): IterableIterator<User> {
267
+ for (const dataAndSlot of this.userMap.values()) {
268
+ yield dataAndSlot.data;
269
+ }
270
+ }
271
+ public valuesWithSlot(): IterableIterator<DataAndSlot<User>> {
257
272
  return this.userMap.values();
258
273
  }
259
274
 
275
+ public *entries(): IterableIterator<[string, User]> {
276
+ for (const [key, dataAndSlot] of this.userMap.entries()) {
277
+ yield [key, dataAndSlot.data];
278
+ }
279
+ }
280
+ public entriesWithSlot(): IterableIterator<[string, DataAndSlot<User>]> {
281
+ return this.userMap.entries();
282
+ }
283
+
260
284
  public size(): number {
261
285
  return this.userMap.size;
262
286
  }
@@ -269,15 +293,13 @@ export class UserMap implements UserMapInterface {
269
293
  public getUniqueAuthorities(
270
294
  filterCriteria?: UserFilterCriteria
271
295
  ): PublicKey[] {
272
- const usersMeetingCriteria = Array.from(this.userMap.values()).filter(
273
- (user) => {
274
- let pass = true;
275
- if (filterCriteria && filterCriteria.hasOpenOrders) {
276
- pass = pass && user.getUserAccount().hasOpenOrder;
277
- }
278
- return pass;
296
+ const usersMeetingCriteria = Array.from(this.values()).filter((user) => {
297
+ let pass = true;
298
+ if (filterCriteria && filterCriteria.hasOpenOrders) {
299
+ pass = pass && user.getUserAccount().hasOpenOrder;
279
300
  }
280
- );
301
+ return pass;
302
+ });
281
303
  const userAuths = new Set(
282
304
  usersMeetingCriteria.map((user) =>
283
305
  user.getUserAccount().authority.toBase58()
@@ -345,17 +367,17 @@ export class UserMap implements UserMapInterface {
345
367
  for (const [key, buffer] of programAccountBufferMap.entries()) {
346
368
  if (!this.has(key)) {
347
369
  const userAccount = this.decode('User', buffer);
348
- await this.addPubkey(new PublicKey(key), userAccount);
349
- this.userMap.get(key).accountSubscriber.updateData(userAccount, slot);
370
+ await this.addPubkey(new PublicKey(key), userAccount, slot);
371
+ this.get(key).accountSubscriber.updateData(userAccount, slot);
350
372
  } else {
351
373
  const userAccount = this.decode('User', buffer);
352
- this.userMap.get(key).accountSubscriber.updateData(userAccount, slot);
374
+ this.get(key).accountSubscriber.updateData(userAccount, slot);
353
375
  }
354
376
  // give event loop a chance to breathe
355
377
  await new Promise((resolve) => setTimeout(resolve, 0));
356
378
  }
357
379
 
358
- for (const [key, user] of this.userMap.entries()) {
380
+ for (const [key, user] of this.entries()) {
359
381
  if (!programAccountBufferMap.has(key)) {
360
382
  await user.unsubscribe();
361
383
  this.userMap.delete(key);
@@ -375,7 +397,7 @@ export class UserMap implements UserMapInterface {
375
397
  public async unsubscribe() {
376
398
  await this.subscription.unsubscribe();
377
399
 
378
- for (const [key, user] of this.userMap.entries()) {
400
+ for (const [key, user] of this.entries()) {
379
401
  await user.unsubscribe();
380
402
  this.userMap.delete(key);
381
403
  }
@@ -398,11 +420,15 @@ export class UserMap implements UserMapInterface {
398
420
  slot: number
399
421
  ) {
400
422
  this.updateLatestSlot(slot);
401
- if (!this.userMap.has(key)) {
423
+ if (!this.has(key)) {
402
424
  this.addPubkey(new PublicKey(key), userAccount, slot);
403
425
  } else {
404
- const user = this.userMap.get(key);
426
+ const user = this.get(key);
405
427
  user.accountSubscriber.updateData(userAccount, slot);
428
+ this.userMap.set(key, {
429
+ data: user,
430
+ slot,
431
+ });
406
432
  }
407
433
  }
408
434
 
@@ -24,11 +24,32 @@ import {
24
24
  QUOTE_PRECISION,
25
25
  isVariant,
26
26
  uncrossL2,
27
+ L2Level,
27
28
  } from '../../src';
28
29
 
29
30
  import { mockPerpMarkets, mockSpotMarkets, mockStateAccount } from './helpers';
30
31
  import { DLOBOrdersCoder } from '../../src/dlob/DLOBOrders';
31
32
 
33
+ // Returns true if asks are sorted ascending
34
+ const asksAreSortedAsc = (asks: L2Level[]) => {
35
+ return asks.every((ask, i) => {
36
+ if (i === 0) {
37
+ return true;
38
+ }
39
+ return ask.price.gt(asks[i - 1].price);
40
+ });
41
+ };
42
+
43
+ // Returns true if asks are sorted descending
44
+ const bidsAreSortedDesc = (bids: L2Level[]) => {
45
+ return bids.every((bid, i) => {
46
+ if (i === 0) {
47
+ return true;
48
+ }
49
+ return bid.price.lt(bids[i - 1].price);
50
+ });
51
+ };
52
+
32
53
  function insertOrderToDLOB(
33
54
  dlob: DLOB,
34
55
  userAccount: PublicKey,
@@ -6592,4 +6613,68 @@ describe('Uncross L2', () => {
6592
6613
  new BN(1).mul(BASE_PRECISION).toString()
6593
6614
  );
6594
6615
  });
6616
+
6617
+ it('Handles edge case bide and asks with large cross and an overlapping level', () => {
6618
+
6619
+ const bids = [
6620
+ "104411000",
6621
+ "103835800",
6622
+ "103826259",
6623
+ "103825000",
6624
+ "103822000",
6625
+ "103821500",
6626
+ "103820283",
6627
+ "103816900",
6628
+ "103816000",
6629
+ "103815121",
6630
+ ].map(priceStr => (
6631
+ {
6632
+ price: new BN(priceStr),
6633
+ size: new BN(1).mul(BASE_PRECISION),
6634
+ sources: { vamm: new BN(1).mul(BASE_PRECISION) },
6635
+ }
6636
+ ));
6637
+
6638
+ const asks = [
6639
+ "103822000",
6640
+ "103838354",
6641
+ "103843087",
6642
+ "103843351",
6643
+ "103843880",
6644
+ "103845114",
6645
+ "103846148",
6646
+ "103850100",
6647
+ "103851300",
6648
+ "103854304",
6649
+ ].map(priceStr => ({
6650
+ price: new BN(priceStr),
6651
+ size: new BN(1).mul(BASE_PRECISION),
6652
+ sources: { vamm: new BN(1).mul(BASE_PRECISION) },
6653
+ }));
6654
+
6655
+ expect(asksAreSortedAsc(asks), "Input asks are ascending").to.be.true;
6656
+ expect(bidsAreSortedDesc(bids), "Input bids are descending").to.be.true;
6657
+
6658
+ const oraclePrice = new BN("103649895");
6659
+ const oraclePrice5Min = new BN("103285000");
6660
+ const markPrice5Min = new BN("103371000");
6661
+
6662
+ const groupingSize = new BN("100");
6663
+
6664
+ const userAsks = new Set<string>();
6665
+
6666
+ const { bids: newBids, asks: newAsks } = uncrossL2(
6667
+ bids,
6668
+ asks,
6669
+ oraclePrice,
6670
+ oraclePrice5Min,
6671
+ markPrice5Min,
6672
+ groupingSize,
6673
+ new Set<string>(),
6674
+ userAsks
6675
+ );
6676
+
6677
+ expect(asksAreSortedAsc(newAsks), "Uncrossed asks are ascending").to.be.true;
6678
+ expect(bidsAreSortedDesc(newBids), "Uncrossed bids are descending").to.be.true;
6679
+ });
6595
6680
  });