@drift-labs/sdk 2.49.0-beta.1 → 2.49.0-beta.11

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.
Files changed (54) hide show
  1. package/VERSION +1 -1
  2. package/lib/accounts/{mockUserAccountSubscriber.d.ts → basicUserAccountSubscriber.d.ts} +2 -2
  3. package/lib/accounts/{mockUserAccountSubscriber.js → basicUserAccountSubscriber.js} +9 -6
  4. package/lib/accounts/pollingInsuranceFundStakeAccountSubscriber.d.ts +29 -0
  5. package/lib/accounts/pollingInsuranceFundStakeAccountSubscriber.js +110 -0
  6. package/lib/accounts/types.d.ts +14 -1
  7. package/lib/accounts/webSocketInsuranceFundStakeAccountSubscriber.d.ts +23 -0
  8. package/lib/accounts/webSocketInsuranceFundStakeAccountSubscriber.js +65 -0
  9. package/lib/dlob/DLOB.d.ts +6 -2
  10. package/lib/dlob/DLOB.js +37 -12
  11. package/lib/driftClient.d.ts +66 -66
  12. package/lib/driftClient.js +208 -194
  13. package/lib/events/eventSubscriber.js +2 -1
  14. package/lib/events/sort.d.ts +2 -2
  15. package/lib/events/sort.js +6 -23
  16. package/lib/examples/loadDlob.js +10 -5
  17. package/lib/index.d.ts +3 -1
  18. package/lib/index.js +3 -1
  19. package/lib/math/superStake.d.ts +43 -0
  20. package/lib/math/superStake.js +64 -22
  21. package/lib/orderSubscriber/OrderSubscriber.js +4 -0
  22. package/lib/orderSubscriber/WebsocketSubscription.d.ts +1 -1
  23. package/lib/orderSubscriber/WebsocketSubscription.js +8 -6
  24. package/lib/types.d.ts +0 -2
  25. package/lib/userMap/PollingSubscription.d.ts +15 -0
  26. package/lib/userMap/PollingSubscription.js +26 -0
  27. package/lib/userMap/WebsocketSubscription.d.ts +19 -0
  28. package/lib/userMap/WebsocketSubscription.js +40 -0
  29. package/lib/userMap/userMap.d.ts +15 -18
  30. package/lib/userMap/userMap.js +62 -31
  31. package/lib/userMap/userMapConfig.d.ts +20 -0
  32. package/lib/userMap/userMapConfig.js +2 -0
  33. package/package.json +1 -1
  34. package/src/accounts/{mockUserAccountSubscriber.ts → basicUserAccountSubscriber.ts} +8 -6
  35. package/src/accounts/pollingInsuranceFundStakeAccountSubscriber.ts +185 -0
  36. package/src/accounts/types.ts +21 -0
  37. package/src/accounts/webSocketInsuranceFundStakeAccountSubscriber.ts +127 -0
  38. package/src/dlob/DLOB.ts +55 -15
  39. package/src/driftClient.ts +429 -272
  40. package/src/events/eventSubscriber.ts +2 -1
  41. package/src/events/sort.ts +7 -29
  42. package/src/examples/loadDlob.ts +11 -6
  43. package/src/index.ts +3 -1
  44. package/src/math/superStake.ts +108 -20
  45. package/src/orderSubscriber/OrderSubscriber.ts +4 -0
  46. package/src/orderSubscriber/WebsocketSubscription.ts +19 -16
  47. package/src/types.ts +0 -2
  48. package/src/userMap/PollingSubscription.ts +46 -0
  49. package/src/userMap/WebsocketSubscription.ts +74 -0
  50. package/src/userMap/userMap.ts +88 -60
  51. package/src/userMap/userMapConfig.ts +31 -0
  52. package/tests/amm/test.ts +6 -3
  53. package/tests/dlob/helpers.ts +2 -6
  54. package/tests/dlob/test.ts +194 -0
@@ -3,7 +3,6 @@ import {
3
3
  DriftClient,
4
4
  UserAccount,
5
5
  OrderRecord,
6
- UserSubscriptionConfig,
7
6
  WrappedEvent,
8
7
  DepositRecord,
9
8
  FundingPaymentRecord,
@@ -14,11 +13,24 @@ import {
14
13
  LPRecord,
15
14
  StateAccount,
16
15
  DLOB,
16
+ BasicUserAccountSubscriber,
17
+ BN,
17
18
  } from '..';
18
19
 
19
- import { PublicKey, RpcResponseAndContext } from '@solana/web3.js';
20
+ import {
21
+ Commitment,
22
+ Connection,
23
+ PublicKey,
24
+ RpcResponseAndContext,
25
+ } from '@solana/web3.js';
20
26
  import { Buffer } from 'buffer';
21
27
  import { getNonIdleUserFilter, getUserFilter } from '../memcmp';
28
+ import {
29
+ UserAccountFilterCriteria as UserFilterCriteria,
30
+ UserMapConfig,
31
+ } from './userMapConfig';
32
+ import { WebsocketSubscription } from './WebsocketSubscription';
33
+ import { PollingSubscription } from './PollingSubscription';
22
34
 
23
35
  export interface UserMapInterface {
24
36
  subscribe(): Promise<void>;
@@ -32,59 +44,51 @@ export interface UserMapInterface {
32
44
  values(): IterableIterator<User>;
33
45
  }
34
46
 
35
- // filter users that meet these criteria when passing into syncCallback
36
- export type SyncCallbackCriteria = {
37
- // only sync users that have open orders
38
- hasOpenOrders: boolean;
39
- };
40
-
41
47
  export class UserMap implements UserMapInterface {
42
48
  private userMap = new Map<string, User>();
43
- private driftClient: DriftClient;
44
- private accountSubscription: UserSubscriptionConfig;
49
+ driftClient: DriftClient;
50
+ private connection: Connection;
51
+ private commitment: Commitment;
45
52
  private includeIdle: boolean;
46
- private lastNumberOfSubAccounts;
53
+ private lastNumberOfSubAccounts: BN;
54
+ private subscription: PollingSubscription | WebsocketSubscription;
47
55
  private stateAccountUpdateCallback = async (state: StateAccount) => {
48
- if (state.numberOfSubAccounts !== this.lastNumberOfSubAccounts) {
56
+ if (!state.numberOfSubAccounts.eq(this.lastNumberOfSubAccounts)) {
49
57
  await this.sync();
50
58
  this.lastNumberOfSubAccounts = state.numberOfSubAccounts;
51
59
  }
52
60
  };
53
- private syncCallback: (authorities: PublicKey[]) => Promise<void>;
54
- private syncCallbackCriteria: SyncCallbackCriteria;
55
61
 
56
62
  private syncPromise?: Promise<void>;
57
63
  private syncPromiseResolver: () => void;
58
64
 
59
65
  /**
60
66
  * Constructs a new UserMap instance.
61
- *
62
- * @param {DriftClient} driftClient - The DriftClient instance.
63
- * @param {UserSubscriptionConfig} accountSubscription - The UserSubscriptionConfig instance.
64
- * @param {boolean} includeIdle - Whether idle users are subscribed to. Defaults to false to decrease # of user subscriptions.
65
- * @param {(authorities: PublicKey[]) => Promise<void>} syncCallback - Called after `sync` completes, will pas in unique list of authorities. Useful for using it to sync UserStatsMap.
66
- * @param {SyncCallbackCriteria} syncCallbackCriteria - The criteria for the sync callback. Defaults to having no filters
67
67
  */
68
- constructor(
69
- driftClient: DriftClient,
70
- accountSubscription: UserSubscriptionConfig,
71
- includeIdle = false,
72
- syncCallback?: (authorities: PublicKey[]) => Promise<void>,
73
- syncCallbackCriteria: SyncCallbackCriteria = { hasOpenOrders: false }
74
- ) {
75
- this.driftClient = driftClient;
76
- this.accountSubscription = accountSubscription;
77
- this.includeIdle = includeIdle;
78
- this.syncCallback = syncCallback;
79
- this.syncCallbackCriteria = syncCallbackCriteria;
80
- }
81
-
82
- public addSyncCallback(
83
- syncCallback?: (authorities: PublicKey[]) => Promise<void>,
84
- syncCallbackCriteria: SyncCallbackCriteria = { hasOpenOrders: false }
85
- ) {
86
- this.syncCallback = syncCallback;
87
- this.syncCallbackCriteria = syncCallbackCriteria;
68
+ constructor(config: UserMapConfig) {
69
+ this.driftClient = config.driftClient;
70
+ if (config.connection) {
71
+ this.connection = config.connection;
72
+ } else {
73
+ this.connection = this.driftClient.connection;
74
+ }
75
+ this.commitment =
76
+ config.subscriptionConfig.commitment ?? this.driftClient.opts.commitment;
77
+ this.includeIdle = config.includeIdle ?? false;
78
+ if (config.subscriptionConfig.type === 'polling') {
79
+ this.subscription = new PollingSubscription({
80
+ userMap: this,
81
+ frequency: config.subscriptionConfig.frequency,
82
+ skipInitialLoad: config.skipInitialLoad,
83
+ });
84
+ } else {
85
+ this.subscription = new WebsocketSubscription({
86
+ userMap: this,
87
+ commitment: this.commitment,
88
+ resubTimeoutMs: config.subscriptionConfig.resubTimeoutMs,
89
+ skipInitialLoad: config.skipInitialLoad,
90
+ });
91
+ }
88
92
  }
89
93
 
90
94
  public async subscribe() {
@@ -100,17 +104,25 @@ export class UserMap implements UserMapInterface {
100
104
  this.stateAccountUpdateCallback
101
105
  );
102
106
 
103
- await this.sync();
107
+ await this.subscription.subscribe();
104
108
  }
105
109
 
106
110
  public async addPubkey(
107
111
  userAccountPublicKey: PublicKey,
108
- userAccount?: UserAccount
112
+ userAccount?: UserAccount,
113
+ slot?: number
109
114
  ) {
110
115
  const user = new User({
111
116
  driftClient: this.driftClient,
112
117
  userAccountPublicKey,
113
- accountSubscription: this.accountSubscription,
118
+ accountSubscription: {
119
+ type: 'custom',
120
+ userAccountSubscriber: new BasicUserAccountSubscriber(
121
+ userAccountPublicKey,
122
+ userAccount,
123
+ slot
124
+ ),
125
+ },
114
126
  });
115
127
  await user.subscribe(userAccount);
116
128
  this.userMap.set(userAccountPublicKey.toString(), user);
@@ -216,14 +228,18 @@ export class UserMap implements UserMapInterface {
216
228
  return this.userMap.size;
217
229
  }
218
230
 
219
- public getUniqueAuthorities(useSyncCallbackCriteria = true): PublicKey[] {
231
+ /**
232
+ * Returns a unique list of authorities for all users in the UserMap that meet the filter criteria
233
+ * @param filterCriteria: Users must meet these criteria to be included
234
+ * @returns
235
+ */
236
+ public getUniqueAuthorities(
237
+ filterCriteria?: UserFilterCriteria
238
+ ): PublicKey[] {
220
239
  const usersMeetingCriteria = Array.from(this.userMap.values()).filter(
221
240
  (user) => {
222
241
  let pass = true;
223
- if (
224
- useSyncCallbackCriteria &&
225
- this.syncCallbackCriteria.hasOpenOrders
226
- ) {
242
+ if (filterCriteria && filterCriteria.hasOpenOrders) {
227
243
  pass = pass && user.getUserAccount().hasOpenOrder;
228
244
  }
229
245
  return pass;
@@ -257,7 +273,7 @@ export class UserMap implements UserMapInterface {
257
273
  const rpcRequestArgs = [
258
274
  this.driftClient.program.programId.toBase58(),
259
275
  {
260
- commitment: this.driftClient.connection.commitment,
276
+ commitment: this.commitment,
261
277
  filters,
262
278
  encoding: 'base64',
263
279
  withContext: true,
@@ -266,10 +282,7 @@ export class UserMap implements UserMapInterface {
266
282
 
267
283
  const rpcJSONResponse: any =
268
284
  // @ts-ignore
269
- await this.driftClient.connection._rpcRequest(
270
- 'getProgramAccounts',
271
- rpcRequestArgs
272
- );
285
+ await this.connection._rpcRequest('getProgramAccounts', rpcRequestArgs);
273
286
 
274
287
  const rpcResponseAndContext: RpcResponseAndContext<
275
288
  Array<{
@@ -297,12 +310,14 @@ export class UserMap implements UserMapInterface {
297
310
  for (const [key, buffer] of programAccountBufferMap.entries()) {
298
311
  if (!this.has(key)) {
299
312
  const userAccount =
300
- this.driftClient.program.account.user.coder.accounts.decode(
313
+ this.driftClient.program.account.user.coder.accounts.decodeUnchecked(
301
314
  'User',
302
315
  buffer
303
316
  );
304
317
  await this.addPubkey(new PublicKey(key), userAccount);
305
318
  }
319
+ // give event loop a chance to breathe
320
+ await new Promise((resolve) => setTimeout(resolve, 0));
306
321
  }
307
322
 
308
323
  for (const [key, user] of this.userMap.entries()) {
@@ -311,19 +326,17 @@ export class UserMap implements UserMapInterface {
311
326
  this.userMap.delete(key);
312
327
  } else {
313
328
  const userAccount =
314
- this.driftClient.program.account.user.coder.accounts.decode(
329
+ this.driftClient.program.account.user.coder.accounts.decodeUnchecked(
315
330
  'User',
316
331
  programAccountBufferMap.get(key)
317
332
  );
318
333
  user.accountSubscriber.updateData(userAccount, slot);
319
334
  }
320
- }
321
-
322
- if (this.syncCallback) {
323
- await this.syncCallback(this.getUniqueAuthorities());
335
+ // give event loop a chance to breathe
336
+ await new Promise((resolve) => setTimeout(resolve, 0));
324
337
  }
325
338
  } catch (e) {
326
- console.error(`Error in UserMap.sync()`);
339
+ console.error(`Error in UserMap.sync():`);
327
340
  console.error(e);
328
341
  } finally {
329
342
  this.syncPromiseResolver();
@@ -332,6 +345,8 @@ export class UserMap implements UserMapInterface {
332
345
  }
333
346
 
334
347
  public async unsubscribe() {
348
+ await this.subscription.unsubscribe();
349
+
335
350
  for (const [key, user] of this.userMap.entries()) {
336
351
  await user.unsubscribe();
337
352
  this.userMap.delete(key);
@@ -345,4 +360,17 @@ export class UserMap implements UserMapInterface {
345
360
  this.lastNumberOfSubAccounts = undefined;
346
361
  }
347
362
  }
363
+
364
+ public async updateUserAccount(
365
+ key: string,
366
+ userAccount: UserAccount,
367
+ slot: number
368
+ ) {
369
+ if (!this.userMap.has(key)) {
370
+ this.addPubkey(new PublicKey(key), userAccount, slot);
371
+ } else {
372
+ const user = this.userMap.get(key);
373
+ user.accountSubscriber.updateData(userAccount, slot);
374
+ }
375
+ }
348
376
  }
@@ -0,0 +1,31 @@
1
+ import { Commitment, Connection } from '@solana/web3.js';
2
+ import { DriftClient } from '../driftClient';
3
+
4
+ // passed into UserMap.getUniqueAuthorities to filter users
5
+ export type UserAccountFilterCriteria = {
6
+ // only return users that have open orders
7
+ hasOpenOrders: boolean;
8
+ };
9
+
10
+ export type UserMapConfig = {
11
+ driftClient: DriftClient;
12
+ // connection object to use specifically for the UserMap. If undefined, will use the driftClient's connection
13
+ connection?: Connection;
14
+ subscriptionConfig:
15
+ | {
16
+ type: 'polling';
17
+ frequency: number;
18
+ commitment?: Commitment;
19
+ }
20
+ | {
21
+ type: 'websocket';
22
+ resubTimeoutMs?: number;
23
+ commitment?: Commitment;
24
+ };
25
+
26
+ // True to skip the initial load of userAccounts via getProgramAccounts
27
+ skipInitialLoad?: boolean;
28
+
29
+ // True to include idle users when loading. Defaults to false to decrease # of accounts subscribed to.
30
+ includeIdle?: boolean;
31
+ };
package/tests/amm/test.ts CHANGED
@@ -361,7 +361,10 @@ describe('AMM Tests', () => {
361
361
 
362
362
  console.log(terms2);
363
363
  assert(terms2.effectiveLeverageCapped <= 1.000001);
364
- assert(terms2.inventorySpreadScale == 1.013527);
364
+ assert(
365
+ terms2.inventorySpreadScale == 1.013527,
366
+ `got: ${terms2.inventorySpreadScale}`
367
+ );
365
368
  assert(terms2.longSpread == 1146);
366
369
  assert(terms2.shortSpread == 6686);
367
370
  });
@@ -466,7 +469,7 @@ describe('AMM Tests', () => {
466
469
 
467
470
  assert(markTwapLive.eq(new BN('1949826')));
468
471
  assert(oracleTwapLive.eq(new BN('1942510')));
469
- assert(est1.eq(new BN('15692')));
472
+ assert(est1.eq(new BN('15692')), `got: ${est1}`);
470
473
  assert(est2.eq(new BN('15692')));
471
474
  });
472
475
 
@@ -556,7 +559,7 @@ describe('AMM Tests', () => {
556
559
  assert(markTwapLive.eq(new BN('1222131')));
557
560
  assert(oracleTwapLive.eq(new BN('1222586')));
558
561
  assert(est1.eq(est2));
559
- assert(est2.eq(new BN('-1550')));
562
+ assert(est2.eq(new BN('-1550')), `got: ${est2}`);
560
563
  });
561
564
 
562
565
  it('orderbook L2 gen (no topOfBookQuoteAmounts, 10 numOrders, low liquidity)', async () => {
@@ -539,15 +539,13 @@ export const mockStateAccount: StateAccount = {
539
539
  feeNumerator: 0,
540
540
  feeDenominator: 0,
541
541
  makerRebateNumerator: 0,
542
- makerRebateDenominator: 0,
542
+ makerRebateDenominator: 1,
543
543
  referrerRewardNumerator: 0,
544
544
  referrerRewardDenominator: 0,
545
545
  refereeFeeNumerator: 0,
546
546
  refereeFeeDenominator: 0,
547
547
  },
548
548
  ],
549
- makerRebateNumerator: new BN(0),
550
- makerRebateDenominator: new BN(0),
551
549
  fillerRewardStructure: {
552
550
  rewardNumerator: new BN(0),
553
551
  rewardDenominator: new BN(0),
@@ -565,15 +563,13 @@ export const mockStateAccount: StateAccount = {
565
563
  feeNumerator: 0,
566
564
  feeDenominator: 0,
567
565
  makerRebateNumerator: 0,
568
- makerRebateDenominator: 0,
566
+ makerRebateDenominator: 1,
569
567
  referrerRewardNumerator: 0,
570
568
  referrerRewardDenominator: 0,
571
569
  refereeFeeNumerator: 0,
572
570
  refereeFeeDenominator: 0,
573
571
  },
574
572
  ],
575
- makerRebateNumerator: new BN(0),
576
- makerRebateDenominator: new BN(0),
577
573
  fillerRewardStructure: {
578
574
  rewardNumerator: new BN(0),
579
575
  rewardDenominator: new BN(0),
@@ -1914,6 +1914,8 @@ describe('DLOB Perp Tests', () => {
1914
1914
  },
1915
1915
  false,
1916
1916
  10,
1917
+ 0,
1918
+ 1,
1917
1919
  undefined,
1918
1920
  undefined
1919
1921
  );
@@ -2059,6 +2061,8 @@ describe('DLOB Perp Tests', () => {
2059
2061
  },
2060
2062
  false,
2061
2063
  10,
2064
+ 0,
2065
+ 1,
2062
2066
  undefined,
2063
2067
  undefined
2064
2068
  );
@@ -2267,6 +2271,194 @@ describe('DLOB Perp Tests', () => {
2267
2271
  expect(nodesToFillAfter[1].makerNodes[0]?.order?.orderId).to.equal(3);
2268
2272
  });
2269
2273
 
2274
+ it('Test post only bid fills against fallback', async () => {
2275
+ const vAsk = new BN(150);
2276
+ const vBid = new BN(100);
2277
+
2278
+ const user0 = Keypair.generate();
2279
+
2280
+ const dlob = new DLOB();
2281
+ const marketIndex = 0;
2282
+
2283
+ const makerRebateNumerator = 1;
2284
+ const makerRebateDenominator = 10;
2285
+
2286
+ // post only bid same as ask
2287
+ insertOrderToDLOB(
2288
+ dlob,
2289
+ user0.publicKey,
2290
+ OrderType.LIMIT,
2291
+ MarketType.PERP,
2292
+ 1, // orderId
2293
+ marketIndex,
2294
+ vAsk, // same price as vAsk
2295
+ BASE_PRECISION, // quantity
2296
+ PositionDirection.LONG,
2297
+ vBid,
2298
+ vAsk,
2299
+ undefined,
2300
+ undefined,
2301
+ undefined,
2302
+ true
2303
+ );
2304
+
2305
+ // should have no crossing orders
2306
+ const nodesToFillBefore = dlob.findRestingLimitOrderNodesToFill(
2307
+ marketIndex,
2308
+ 12, // auction over
2309
+ MarketType.PERP,
2310
+ {
2311
+ price: vBid.add(vAsk).div(new BN(2)),
2312
+ slot: new BN(12),
2313
+ confidence: new BN(1),
2314
+ hasSufficientNumberOfDataPoints: true,
2315
+ },
2316
+ false,
2317
+ 10,
2318
+ makerRebateNumerator,
2319
+ makerRebateDenominator,
2320
+ vAsk,
2321
+ vBid
2322
+ );
2323
+ expect(nodesToFillBefore.length).to.equal(0);
2324
+
2325
+ // post only bid crosses ask
2326
+ const price = vAsk.add(
2327
+ vAsk.muln(makerRebateNumerator).divn(makerRebateDenominator)
2328
+ );
2329
+ insertOrderToDLOB(
2330
+ dlob,
2331
+ user0.publicKey,
2332
+ OrderType.LIMIT,
2333
+ MarketType.PERP,
2334
+ 2, // orderId
2335
+ marketIndex,
2336
+ price, // crosses vask
2337
+ BASE_PRECISION, // quantity
2338
+ PositionDirection.LONG,
2339
+ vBid,
2340
+ vAsk,
2341
+ undefined,
2342
+ undefined,
2343
+ undefined,
2344
+ true
2345
+ );
2346
+
2347
+ // should have no crossing orders
2348
+ const nodesToFillAfter = dlob.findRestingLimitOrderNodesToFill(
2349
+ marketIndex,
2350
+ 12, // auction over
2351
+ MarketType.PERP,
2352
+ {
2353
+ price: vBid.add(vAsk).div(new BN(2)),
2354
+ slot: new BN(12),
2355
+ confidence: new BN(1),
2356
+ hasSufficientNumberOfDataPoints: true,
2357
+ },
2358
+ false,
2359
+ 10,
2360
+ makerRebateNumerator,
2361
+ makerRebateDenominator,
2362
+ vAsk,
2363
+ vBid
2364
+ );
2365
+ expect(nodesToFillAfter.length).to.equal(1);
2366
+ });
2367
+
2368
+ it('Test post only ask fills against fallback', async () => {
2369
+ const vAsk = new BN(150);
2370
+ const vBid = new BN(100);
2371
+
2372
+ const user0 = Keypair.generate();
2373
+
2374
+ const dlob = new DLOB();
2375
+ const marketIndex = 0;
2376
+
2377
+ const makerRebateNumerator = 1;
2378
+ const makerRebateDenominator = 10;
2379
+
2380
+ // post only bid same as ask
2381
+ insertOrderToDLOB(
2382
+ dlob,
2383
+ user0.publicKey,
2384
+ OrderType.LIMIT,
2385
+ MarketType.PERP,
2386
+ 1, // orderId
2387
+ marketIndex,
2388
+ vBid, // same price as vAsk
2389
+ BASE_PRECISION, // quantity
2390
+ PositionDirection.SHORT,
2391
+ vBid,
2392
+ vAsk,
2393
+ undefined,
2394
+ undefined,
2395
+ undefined,
2396
+ true
2397
+ );
2398
+
2399
+ // should have no crossing orders
2400
+ const nodesToFillBefore = dlob.findRestingLimitOrderNodesToFill(
2401
+ marketIndex,
2402
+ 12, // auction over
2403
+ MarketType.PERP,
2404
+ {
2405
+ price: vBid.add(vAsk).div(new BN(2)),
2406
+ slot: new BN(12),
2407
+ confidence: new BN(1),
2408
+ hasSufficientNumberOfDataPoints: true,
2409
+ },
2410
+ false,
2411
+ 10,
2412
+ makerRebateNumerator,
2413
+ makerRebateDenominator,
2414
+ vAsk,
2415
+ vBid
2416
+ );
2417
+ expect(nodesToFillBefore.length).to.equal(0);
2418
+
2419
+ // post only bid crosses ask
2420
+ const price = vBid.sub(
2421
+ vAsk.muln(makerRebateNumerator).divn(makerRebateDenominator)
2422
+ );
2423
+ insertOrderToDLOB(
2424
+ dlob,
2425
+ user0.publicKey,
2426
+ OrderType.LIMIT,
2427
+ MarketType.PERP,
2428
+ 2, // orderId
2429
+ marketIndex,
2430
+ price, // crosses vask
2431
+ BASE_PRECISION, // quantity
2432
+ PositionDirection.SHORT,
2433
+ vBid,
2434
+ vAsk,
2435
+ undefined,
2436
+ undefined,
2437
+ undefined,
2438
+ true
2439
+ );
2440
+
2441
+ // should have no crossing orders
2442
+ const nodesToFillAfter = dlob.findRestingLimitOrderNodesToFill(
2443
+ marketIndex,
2444
+ 12, // auction over
2445
+ MarketType.PERP,
2446
+ {
2447
+ price: vBid.add(vAsk).div(new BN(2)),
2448
+ slot: new BN(12),
2449
+ confidence: new BN(1),
2450
+ hasSufficientNumberOfDataPoints: true,
2451
+ },
2452
+ false,
2453
+ 10,
2454
+ makerRebateNumerator,
2455
+ makerRebateDenominator,
2456
+ vAsk,
2457
+ vBid
2458
+ );
2459
+ expect(nodesToFillAfter.length).to.equal(1);
2460
+ });
2461
+
2270
2462
  it('Test trigger orders', () => {
2271
2463
  const vAsk = new BN(15);
2272
2464
  const vBid = new BN(8);
@@ -2901,6 +3093,8 @@ describe('DLOB Perp Tests', () => {
2901
3093
  oracle,
2902
3094
  false,
2903
3095
  10,
3096
+ 0,
3097
+ 1,
2904
3098
  undefined,
2905
3099
  undefined
2906
3100
  );