@drift-labs/sdk 2.0.13 → 2.0.15

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.js CHANGED
@@ -180,98 +180,141 @@ class DLOB {
180
180
  if ((0, exchangeStatus_1.fillPaused)(this.stateAccount, marketAccount)) {
181
181
  return [];
182
182
  }
183
- // Find all the crossing nodes
184
- const crossingNodesToFill = this.findCrossingNodesToFill(marketIndex, slot, marketType, oraclePriceData);
185
- let fallbackCrossingNodesToFill = new Array();
186
- if (!(0, exchangeStatus_1.ammPaused)(this.stateAccount, marketAccount)) {
187
- fallbackCrossingNodesToFill = this.findFallbackCrossingNodesToFill(marketIndex, fallbackBid, fallbackAsk, slot, marketType, oraclePriceData);
188
- }
183
+ const isAmmPaused = (0, exchangeStatus_1.ammPaused)(this.stateAccount, marketAccount);
184
+ const marketOrderNodesToFill = this.findMarketNodesToFill(marketIndex, slot, marketType, oraclePriceData, isAmmPaused, fallbackAsk, fallbackBid);
185
+ const limitOrderNodesToFill = this.findLimitOrderNodesToFill(marketIndex, slot, marketType, oraclePriceData, isAmmPaused, fallbackAsk, fallbackBid);
189
186
  // get expired market nodes
190
187
  const expiredNodesToFill = this.findExpiredNodesToFill(marketIndex, ts, marketType);
191
- return crossingNodesToFill.concat(fallbackCrossingNodesToFill, expiredNodesToFill);
188
+ return marketOrderNodesToFill.concat(limitOrderNodesToFill, expiredNodesToFill);
192
189
  }
193
- findCrossingNodesToFill(marketIndex, slot, marketType, oraclePriceData) {
190
+ findLimitOrderNodesToFill(marketIndex, slot, marketType, oraclePriceData, isAmmPaused, fallbackAsk, fallbackBid) {
194
191
  const nodesToFill = new Array();
195
- const askGenerator = this.getAsks(marketIndex, undefined, // dont include vask
196
- slot, marketType, oraclePriceData);
197
- const bidGenerator = this.getBids(marketIndex, undefined, // dont include vbid
198
- slot, marketType, oraclePriceData);
199
- let nextAsk = askGenerator.next();
200
- let nextBid = bidGenerator.next();
201
- // First try to find orders that cross
202
- while (!nextAsk.done && !nextBid.done) {
203
- const { crossingNodes, exhaustedSide } = this.findCrossingOrders(nextAsk.value, nextBid.value, oraclePriceData, slot);
204
- if (exhaustedSide === 'bid') {
205
- nextBid = bidGenerator.next();
206
- }
207
- else if (exhaustedSide === 'ask') {
208
- nextAsk = askGenerator.next();
209
- }
210
- else if (exhaustedSide === 'both') {
211
- nextBid = bidGenerator.next();
212
- nextAsk = askGenerator.next();
192
+ const crossingNodes = this.findCrossingLimitOrders(marketIndex, slot, marketType, oraclePriceData);
193
+ for (const crossingNode of crossingNodes) {
194
+ nodesToFill.push(crossingNode);
195
+ }
196
+ if (fallbackBid && !isAmmPaused) {
197
+ const askGenerator = this.getLimitAsks(marketIndex, slot, marketType, oraclePriceData);
198
+ const asksCrossingFallback = this.findNodesCrossingFallbackLiquidity(marketType, slot, oraclePriceData, askGenerator, fallbackBid, (askPrice, fallbackPrice) => {
199
+ return askPrice.lte(fallbackPrice);
200
+ });
201
+ for (const askCrossingFallback of asksCrossingFallback) {
202
+ nodesToFill.push(askCrossingFallback);
213
203
  }
214
- else if (exhaustedSide === 'nocross') {
215
- break;
204
+ }
205
+ if (fallbackAsk && !isAmmPaused) {
206
+ const bidGenerator = this.getLimitBids(marketIndex, slot, marketType, oraclePriceData);
207
+ const bidsCrossingFallback = this.findNodesCrossingFallbackLiquidity(marketType, slot, oraclePriceData, bidGenerator, fallbackAsk, (bidPrice, fallbackPrice) => {
208
+ return bidPrice.gte(fallbackPrice);
209
+ });
210
+ for (const bidCrossingFallback of bidsCrossingFallback) {
211
+ nodesToFill.push(bidCrossingFallback);
216
212
  }
217
- else {
218
- console.error(`invalid exhaustedSide: ${exhaustedSide}`);
219
- break;
213
+ }
214
+ return nodesToFill;
215
+ }
216
+ findMarketNodesToFill(marketIndex, slot, marketType, oraclePriceData, isAmmPaused, fallbackAsk, fallbackBid) {
217
+ const nodesToFill = new Array();
218
+ let marketOrderGenerator = this.getMarketAsks(marketIndex, marketType);
219
+ const marketAsksCrossingBids = this.findMarketNodesCrossingLimitNodes(marketIndex, slot, marketType, oraclePriceData, marketOrderGenerator, this.getLimitBids.bind(this), (takerPrice, makerPrice) => {
220
+ return takerPrice === undefined || takerPrice.lte(makerPrice);
221
+ });
222
+ for (const marketAskCrossingBid of marketAsksCrossingBids) {
223
+ nodesToFill.push(marketAskCrossingBid);
224
+ }
225
+ if (fallbackBid && !isAmmPaused) {
226
+ marketOrderGenerator = this.getMarketAsks(marketIndex, marketType);
227
+ const marketAsksCrossingFallback = this.findNodesCrossingFallbackLiquidity(marketType, slot, oraclePriceData, marketOrderGenerator, fallbackBid, (takerPrice, fallbackPrice) => {
228
+ return takerPrice === undefined || takerPrice.lte(fallbackPrice);
229
+ });
230
+ for (const marketAskCrossingFallback of marketAsksCrossingFallback) {
231
+ nodesToFill.push(marketAskCrossingFallback);
220
232
  }
221
- for (const crossingNode of crossingNodes) {
222
- nodesToFill.push(crossingNode);
233
+ }
234
+ marketOrderGenerator = this.getMarketBids(marketIndex, marketType);
235
+ const marketBidsToFill = this.findMarketNodesCrossingLimitNodes(marketIndex, slot, marketType, oraclePriceData, marketOrderGenerator, this.getLimitAsks.bind(this), (takerPrice, fallbackPrice) => {
236
+ return takerPrice === undefined || takerPrice.gte(fallbackPrice);
237
+ });
238
+ for (const marketBidToFill of marketBidsToFill) {
239
+ nodesToFill.push(marketBidToFill);
240
+ }
241
+ if (fallbackAsk && !isAmmPaused) {
242
+ marketOrderGenerator = this.getMarketBids(marketIndex, marketType);
243
+ const marketBidsCrossingFallback = this.findNodesCrossingFallbackLiquidity(marketType, slot, oraclePriceData, marketOrderGenerator, fallbackAsk, (takerPrice, fallbackPrice) => {
244
+ return takerPrice === undefined || takerPrice.gte(fallbackPrice);
245
+ });
246
+ for (const marketBidCrossingFallback of marketBidsCrossingFallback) {
247
+ nodesToFill.push(marketBidCrossingFallback);
223
248
  }
224
249
  }
225
250
  return nodesToFill;
226
251
  }
227
- findFallbackCrossingNodesToFill(marketIndex, fallbackBid, fallbackAsk, slot, marketType, oraclePriceData) {
228
- var _a, _b;
252
+ findMarketNodesCrossingLimitNodes(marketIndex, slot, marketType, oraclePriceData, takerNodeGenerator, makerNodeGeneratorFn, doesCross) {
229
253
  const nodesToFill = new Array();
230
- const askGenerator = this.getAsks(marketIndex, undefined, // dont include vask
231
- slot, marketType, oraclePriceData);
232
- const bidGenerator = this.getBids(marketIndex, undefined, // dont include vbid
233
- slot, marketType, oraclePriceData);
234
- let nextAsk = askGenerator.next();
235
- let nextBid = bidGenerator.next();
236
- // check for asks that cross fallbackBid
237
- while (!nextAsk.done && fallbackBid !== undefined) {
238
- const askNode = nextAsk.value;
239
- if ((0, __1.isVariant)(marketType, 'spot') && ((_a = askNode.order) === null || _a === void 0 ? void 0 : _a.postOnly)) {
240
- nextAsk = askGenerator.next();
241
- continue;
242
- }
243
- const askLimitPrice = (0, __1.getOptionalLimitPrice)(askNode.order, oraclePriceData, slot);
244
- // order crosses if there is no limit price or it crosses fallback price
245
- const crosses = askLimitPrice === undefined || askLimitPrice.lte(fallbackBid);
246
- // fallback is available if auction is complete or it's a spot order
247
- const fallbackAvailable = (0, __1.isVariant)(marketType, 'spot') || (0, __1.isAuctionComplete)(askNode.order, slot);
248
- if (crosses && fallbackAvailable) {
254
+ for (const takerNode of takerNodeGenerator) {
255
+ const makerNodeGenerator = makerNodeGeneratorFn(marketIndex, slot, marketType, oraclePriceData);
256
+ for (const makerNode of makerNodeGenerator) {
257
+ const bidUserAuthority = this.userMap.getUserAuthority(makerNode.userAccount.toString());
258
+ const askUserAuthority = this.userMap.getUserAuthority(takerNode.userAccount.toString());
259
+ // Can't match orders from the same authority
260
+ const sameAuthority = bidUserAuthority.equals(askUserAuthority);
261
+ if (sameAuthority) {
262
+ continue;
263
+ }
264
+ const makerPrice = makerNode.getPrice(oraclePriceData, slot);
265
+ const takerPrice = takerNode.getPrice(oraclePriceData, slot);
266
+ const ordersCross = doesCross(takerPrice, makerPrice);
267
+ if (!ordersCross) {
268
+ // market orders aren't sorted by price, they are sorted by time, so we need to traverse
269
+ // through all of em
270
+ break;
271
+ }
249
272
  nodesToFill.push({
250
- node: askNode,
251
- makerNode: undefined, // filled by fallback
273
+ node: takerNode,
274
+ makerNode: makerNode,
252
275
  });
276
+ const makerOrder = makerNode.order;
277
+ const takerOrder = takerNode.order;
278
+ const makerBaseRemaining = makerOrder.baseAssetAmount.sub(makerOrder.baseAssetAmountFilled);
279
+ const takerBaseRemaining = takerOrder.baseAssetAmount.sub(takerOrder.baseAssetAmountFilled);
280
+ const baseFilled = __1.BN.min(makerBaseRemaining, takerBaseRemaining);
281
+ const newMakerOrder = { ...makerOrder };
282
+ newMakerOrder.baseAssetAmountFilled =
283
+ makerOrder.baseAssetAmountFilled.add(baseFilled);
284
+ this.getListForOrder(newMakerOrder).update(newMakerOrder, makerNode.userAccount);
285
+ const newTakerOrder = { ...takerOrder };
286
+ newTakerOrder.baseAssetAmountFilled =
287
+ takerOrder.baseAssetAmountFilled.add(baseFilled);
288
+ this.getListForOrder(newTakerOrder).update(newTakerOrder, takerNode.userAccount);
289
+ if (newTakerOrder.baseAssetAmountFilled.eq(takerOrder.baseAssetAmount)) {
290
+ break;
291
+ }
253
292
  }
254
- nextAsk = askGenerator.next();
255
293
  }
256
- // check for bids that cross fallbackAsk
257
- while (!nextBid.done && fallbackAsk !== undefined) {
258
- const bidNode = nextBid.value;
259
- if ((0, __1.isVariant)(marketType, 'spot') && ((_b = bidNode.order) === null || _b === void 0 ? void 0 : _b.postOnly)) {
260
- nextBid = bidGenerator.next();
294
+ return nodesToFill;
295
+ }
296
+ findNodesCrossingFallbackLiquidity(marketType, slot, oraclePriceData, nodeGenerator, fallbackPrice, doesCross) {
297
+ var _a;
298
+ const nodesToFill = new Array();
299
+ let nextNode = nodeGenerator.next();
300
+ while (!nextNode.done) {
301
+ const node = nextNode.value;
302
+ if ((0, __1.isVariant)(marketType, 'spot') && ((_a = node.order) === null || _a === void 0 ? void 0 : _a.postOnly)) {
303
+ nextNode = nodeGenerator.next();
261
304
  continue;
262
305
  }
263
- const bidLimitPrice = (0, __1.getOptionalLimitPrice)(bidNode.order, oraclePriceData, slot);
306
+ const nodePrice = (0, __1.getLimitPrice)(node.order, oraclePriceData, slot);
264
307
  // order crosses if there is no limit price or it crosses fallback price
265
- const crosses = bidLimitPrice === undefined || bidLimitPrice.gte(fallbackAsk);
308
+ const crosses = doesCross(nodePrice, fallbackPrice);
266
309
  // fallback is available if auction is complete or it's a spot order
267
- const fallbackAvailable = (0, __1.isVariant)(marketType, 'spot') || (0, __1.isAuctionComplete)(bidNode.order, slot);
310
+ const fallbackAvailable = (0, __1.isVariant)(marketType, 'spot') || (0, __1.isAuctionComplete)(node.order, slot);
268
311
  if (crosses && fallbackAvailable) {
269
312
  nodesToFill.push({
270
- node: bidNode,
313
+ node: node,
271
314
  makerNode: undefined, // filled by fallback
272
315
  });
273
316
  }
274
- nextBid = bidGenerator.next();
317
+ nextNode = nodeGenerator.next();
275
318
  }
276
319
  return nodesToFill;
277
320
  }
@@ -329,55 +372,60 @@ class DLOB {
329
372
  }
330
373
  return nodesToFill;
331
374
  }
332
- getMarketBids(marketIndex, marketType) {
375
+ *getMarketBids(marketIndex, marketType) {
333
376
  const marketTypeStr = (0, __1.getVariant)(marketType);
334
- return this.orderLists
377
+ const generator = this.orderLists
335
378
  .get(marketTypeStr)
336
379
  .get(marketIndex)
337
380
  .market.bid.getGenerator();
381
+ for (const marketBidNode of generator) {
382
+ if (marketBidNode.isBaseFilled()) {
383
+ continue;
384
+ }
385
+ yield marketBidNode;
386
+ }
338
387
  }
339
- getMarketAsks(marketIndex, marketType) {
388
+ *getMarketAsks(marketIndex, marketType) {
340
389
  const marketTypeStr = (0, __1.getVariant)(marketType);
341
- return this.orderLists
390
+ const generator = this.orderLists
342
391
  .get(marketTypeStr)
343
392
  .get(marketIndex)
344
393
  .market.ask.getGenerator();
345
- }
346
- *getAsks(marketIndex, fallbackAsk, slot, marketType, oraclePriceData) {
347
- if ((0, __1.isVariant)(marketType, 'spot') && !oraclePriceData) {
348
- throw new Error('Must provide OraclePriceData to get spot asks');
349
- }
350
- const marketTypeStr = (0, __1.getVariant)(marketType);
351
- const nodeLists = this.orderLists.get(marketTypeStr).get(marketIndex);
352
- const generatorList = [
353
- nodeLists.limit.ask.getGenerator(),
354
- nodeLists.floatingLimit.ask.getGenerator(),
355
- nodeLists.market.ask.getGenerator(),
356
- ];
357
- if (marketTypeStr === 'perp' && fallbackAsk) {
358
- generatorList.push((0, NodeList_1.getVammNodeGenerator)(fallbackAsk));
359
- }
360
- if (generatorList.length === 0) {
361
- throw new Error('No ask generators found');
394
+ for (const marketAskNode of generator) {
395
+ if (marketAskNode.isBaseFilled()) {
396
+ continue;
397
+ }
398
+ yield marketAskNode;
362
399
  }
363
- const askGenerators = generatorList.map((generator) => {
400
+ }
401
+ *getBestNode(generatorList, oraclePriceData, slot, compareFcn) {
402
+ const generators = generatorList.map((generator) => {
364
403
  return {
365
404
  next: generator.next(),
366
405
  generator,
367
406
  };
368
407
  });
369
- let asksExhausted = false;
370
- while (!asksExhausted) {
371
- const bestGenerator = askGenerators.reduce((bestGenerator, currentGenerator) => {
408
+ let sideExhausted = false;
409
+ while (!sideExhausted) {
410
+ const bestGenerator = generators.reduce((bestGenerator, currentGenerator) => {
372
411
  if (currentGenerator.next.done) {
373
412
  return bestGenerator;
374
413
  }
375
414
  if (bestGenerator.next.done) {
376
415
  return currentGenerator;
377
416
  }
378
- const bestAskPrice = bestGenerator.next.value.getPrice(oraclePriceData, slot);
379
- const currentAskPrice = currentGenerator.next.value.getPrice(oraclePriceData, slot);
380
- return bestAskPrice.lt(currentAskPrice)
417
+ const bestValue = bestGenerator.next.value;
418
+ const currentValue = currentGenerator.next.value;
419
+ // always return the market orders first
420
+ if (bestValue.order && (0, __1.isMarketOrder)(bestValue.order)) {
421
+ return bestGenerator;
422
+ }
423
+ if (currentValue.order && (0, __1.isMarketOrder)(currentValue.order)) {
424
+ return currentGenerator;
425
+ }
426
+ const bestPrice = bestValue.getPrice(oraclePriceData, slot);
427
+ const currentPrice = currentValue.getPrice(oraclePriceData, slot);
428
+ return compareFcn(bestPrice, currentPrice)
381
429
  ? bestGenerator
382
430
  : currentGenerator;
383
431
  });
@@ -399,11 +447,25 @@ class DLOB {
399
447
  bestGenerator.next = bestGenerator.generator.next();
400
448
  }
401
449
  else {
402
- asksExhausted = true;
450
+ sideExhausted = true;
403
451
  }
404
452
  }
405
453
  }
406
- *getBids(marketIndex, fallbackBid, slot, marketType, oraclePriceData) {
454
+ *getLimitAsks(marketIndex, slot, marketType, oraclePriceData) {
455
+ if ((0, __1.isVariant)(marketType, 'spot') && !oraclePriceData) {
456
+ throw new Error('Must provide OraclePriceData to get spot asks');
457
+ }
458
+ const marketTypeStr = (0, __1.getVariant)(marketType);
459
+ const nodeLists = this.orderLists.get(marketTypeStr).get(marketIndex);
460
+ const generatorList = [
461
+ nodeLists.limit.ask.getGenerator(),
462
+ nodeLists.floatingLimit.ask.getGenerator(),
463
+ ];
464
+ yield* this.getBestNode(generatorList, oraclePriceData, slot, (bestPrice, currentPrice) => {
465
+ return bestPrice.lt(currentPrice);
466
+ });
467
+ }
468
+ *getLimitBids(marketIndex, slot, marketType, oraclePriceData) {
407
469
  if ((0, __1.isVariant)(marketType, 'spot') && !oraclePriceData) {
408
470
  throw new Error('Must provide OraclePriceData to get spot bids');
409
471
  }
@@ -412,190 +474,109 @@ class DLOB {
412
474
  const generatorList = [
413
475
  nodeLists.limit.bid.getGenerator(),
414
476
  nodeLists.floatingLimit.bid.getGenerator(),
415
- nodeLists.market.bid.getGenerator(),
416
477
  ];
478
+ yield* this.getBestNode(generatorList, oraclePriceData, slot, (bestPrice, currentPrice) => {
479
+ return bestPrice.gt(currentPrice);
480
+ });
481
+ }
482
+ *getAsks(marketIndex, fallbackAsk, slot, marketType, oraclePriceData) {
483
+ if ((0, __1.isVariant)(marketType, 'spot') && !oraclePriceData) {
484
+ throw new Error('Must provide OraclePriceData to get spot asks');
485
+ }
486
+ const generatorList = [
487
+ this.getMarketAsks(marketIndex, marketType),
488
+ this.getLimitAsks(marketIndex, slot, marketType, oraclePriceData),
489
+ ];
490
+ const marketTypeStr = (0, __1.getVariant)(marketType);
491
+ if (marketTypeStr === 'perp' && fallbackAsk) {
492
+ generatorList.push((0, NodeList_1.getVammNodeGenerator)(fallbackAsk));
493
+ }
494
+ yield* this.getBestNode(generatorList, oraclePriceData, slot, (bestPrice, currentPrice) => {
495
+ return bestPrice.lt(currentPrice);
496
+ });
497
+ }
498
+ *getBids(marketIndex, fallbackBid, slot, marketType, oraclePriceData) {
499
+ if ((0, __1.isVariant)(marketType, 'spot') && !oraclePriceData) {
500
+ throw new Error('Must provide OraclePriceData to get spot bids');
501
+ }
502
+ const generatorList = [
503
+ this.getMarketBids(marketIndex, marketType),
504
+ this.getLimitBids(marketIndex, slot, marketType, oraclePriceData),
505
+ ];
506
+ const marketTypeStr = (0, __1.getVariant)(marketType);
417
507
  if (marketTypeStr === 'perp' && fallbackBid) {
418
508
  generatorList.push((0, NodeList_1.getVammNodeGenerator)(fallbackBid));
419
509
  }
420
- if (generatorList.length === 0) {
421
- throw new Error('No bid generators found');
422
- }
423
- const bidGenerators = generatorList.map((generator) => {
424
- return {
425
- next: generator.next(),
426
- generator,
427
- };
510
+ yield* this.getBestNode(generatorList, oraclePriceData, slot, (bestPrice, currentPrice) => {
511
+ return bestPrice.gt(currentPrice);
428
512
  });
429
- let bidsExhausted = false; // there will always be the fallbackBid
430
- while (!bidsExhausted) {
431
- const bestGenerator = bidGenerators.reduce((bestGenerator, currentGenerator) => {
432
- if (currentGenerator.next.done) {
433
- return bestGenerator;
434
- }
435
- if (bestGenerator.next.done) {
436
- return currentGenerator;
513
+ }
514
+ findCrossingLimitOrders(marketIndex, slot, marketType, oraclePriceData) {
515
+ const nodesToFill = new Array();
516
+ for (const askNode of this.getLimitAsks(marketIndex, slot, marketType, oraclePriceData)) {
517
+ for (const bidNode of this.getLimitBids(marketIndex, slot, marketType, oraclePriceData)) {
518
+ const bidPrice = bidNode.getPrice(oraclePriceData, slot);
519
+ const askPrice = askNode.getPrice(oraclePriceData, slot);
520
+ // orders don't cross - we're done walking the book
521
+ if (bidPrice.lt(askPrice)) {
522
+ return nodesToFill;
437
523
  }
438
- const bestBidPrice = bestGenerator.next.value.getPrice(oraclePriceData, slot);
439
- const currentBidPrice = currentGenerator.next.value.getPrice(oraclePriceData, slot);
440
- return bestBidPrice.gt(currentBidPrice)
441
- ? bestGenerator
442
- : currentGenerator;
443
- });
444
- if (!bestGenerator.next.done) {
445
- // skip this node if it's already completely filled
446
- if (bestGenerator.next.value.isBaseFilled()) {
447
- bestGenerator.next = bestGenerator.generator.next();
524
+ const bidOrder = bidNode.order;
525
+ const askOrder = askNode.order;
526
+ const bidUserAuthority = this.userMap.getUserAuthority(bidNode.userAccount.toString());
527
+ const askUserAuthority = this.userMap.getUserAuthority(askNode.userAccount.toString());
528
+ // Can't match orders from the same authority
529
+ const sameAuthority = bidUserAuthority.equals(askUserAuthority);
530
+ if (sameAuthority || (bidOrder.postOnly && askOrder.postOnly)) {
448
531
  continue;
449
532
  }
450
- // skip order if user is being liquidated/bankrupt
451
- if (bestGenerator.next.value.userAccount !== undefined) {
452
- const user = this.userMap.get(bestGenerator.next.value.userAccount.toString());
453
- if (user === null || user === void 0 ? void 0 : user.isBeingLiquidated()) {
454
- bestGenerator.next = bestGenerator.generator.next();
455
- continue;
456
- }
533
+ const { takerNode, makerNode } = this.determineMakerAndTaker(askNode, bidNode);
534
+ const bidBaseRemaining = bidOrder.baseAssetAmount.sub(bidOrder.baseAssetAmountFilled);
535
+ const askBaseRemaining = askOrder.baseAssetAmount.sub(askOrder.baseAssetAmountFilled);
536
+ const baseFilled = __1.BN.min(bidBaseRemaining, askBaseRemaining);
537
+ const newBidOrder = { ...bidOrder };
538
+ newBidOrder.baseAssetAmountFilled =
539
+ bidOrder.baseAssetAmountFilled.add(baseFilled);
540
+ this.getListForOrder(newBidOrder).update(newBidOrder, bidNode.userAccount);
541
+ // ask completely filled
542
+ const newAskOrder = { ...askOrder };
543
+ newAskOrder.baseAssetAmountFilled =
544
+ askOrder.baseAssetAmountFilled.add(baseFilled);
545
+ this.getListForOrder(newAskOrder).update(newAskOrder, askNode.userAccount);
546
+ nodesToFill.push({
547
+ node: takerNode,
548
+ makerNode: makerNode,
549
+ });
550
+ if (newAskOrder.baseAssetAmount.eq(newAskOrder.baseAssetAmountFilled)) {
551
+ break;
457
552
  }
458
- yield bestGenerator.next.value;
459
- bestGenerator.next = bestGenerator.generator.next();
460
- }
461
- else {
462
- bidsExhausted = true;
463
553
  }
464
554
  }
465
- }
466
- findCrossingOrders(askNode, bidNode, oraclePriceData, slot) {
467
- const bidPrice = bidNode.getPrice(oraclePriceData, slot);
468
- const askPrice = askNode.getPrice(oraclePriceData, slot);
469
- // orders don't cross - we're done walkin gup the book
470
- if (bidPrice.lt(askPrice)) {
471
- return {
472
- crossingNodes: [],
473
- exhaustedSide: 'nocross',
474
- };
475
- }
476
- const bidOrder = bidNode.order;
477
- const askOrder = askNode.order;
478
- const bidUserAuthority = this.userMap.getUserAuthority(bidNode.userAccount.toString());
479
- const askUserAuthority = this.userMap.getUserAuthority(askNode.userAccount.toString());
480
- // Can't match orders from the same authority
481
- const sameAuthority = bidUserAuthority.equals(askUserAuthority);
482
- if (sameAuthority || (bidOrder.postOnly && askOrder.postOnly)) {
483
- // don't have a principle way to pick which one to exhaust,
484
- // exhaust each one 50% of the time so we can try each one against other orders
485
- const exhaustedSide = Math.random() < 0.5 ? 'bid' : 'ask';
486
- return {
487
- crossingNodes: [],
488
- exhaustedSide,
489
- };
490
- }
491
- const { takerNode, makerNode, makerSide } = this.determineMakerAndTaker(askNode, bidNode);
492
- // If maker is market order and auction is complete, cant be maker
493
- if ((0, __1.isMarketOrder)(makerNode.order) &&
494
- (0, __1.isAuctionComplete)(makerNode.order, slot)) {
495
- return {
496
- crossingNodes: [],
497
- exhaustedSide: makerSide,
498
- };
499
- }
500
- const bidBaseRemaining = bidOrder.baseAssetAmount.sub(bidOrder.baseAssetAmountFilled);
501
- const askBaseRemaining = askOrder.baseAssetAmount.sub(askOrder.baseAssetAmountFilled);
502
- let exhaustedSide;
503
- if (bidBaseRemaining.eq(askBaseRemaining)) {
504
- exhaustedSide = 'both';
505
- }
506
- else if (bidBaseRemaining.gt(askBaseRemaining)) {
507
- exhaustedSide = 'ask';
508
- }
509
- else {
510
- exhaustedSide = 'bid';
511
- }
512
- // update the orders on DLOB as if they were fill - so we don't try to match them on the next iteration
513
- // NOTE: if something goes wrong during the actual fill (transaction fails, i.e. due to orders already being filled)
514
- // then we risk having a mismatch between this local DLOB and the actual DLOB state on the blockchain. This isn't
515
- // a problem in the current implementation because we construct a new DLOB from the blockchain state every time, rather
516
- // than updating the existing DLOB based on events.
517
- if (exhaustedSide === 'ask') {
518
- // bid partially filled
519
- const newBidOrder = { ...bidOrder };
520
- newBidOrder.baseAssetAmountFilled =
521
- bidOrder.baseAssetAmountFilled.add(askBaseRemaining);
522
- this.getListForOrder(newBidOrder).update(newBidOrder, bidNode.userAccount);
523
- // ask completely filled
524
- const newAskOrder = { ...askOrder };
525
- newAskOrder.baseAssetAmountFilled = askOrder.baseAssetAmount;
526
- this.getListForOrder(newAskOrder).update(newAskOrder, askNode.userAccount);
527
- }
528
- else if (exhaustedSide === 'bid') {
529
- // ask partially filled
530
- const newAskOrder = { ...askOrder };
531
- newAskOrder.baseAssetAmountFilled =
532
- askOrder.baseAssetAmountFilled.add(bidBaseRemaining);
533
- this.getListForOrder(newAskOrder).update(newAskOrder, askNode.userAccount);
534
- // bid completely filled
535
- const newBidOrder = { ...bidOrder };
536
- newBidOrder.baseAssetAmountFilled = bidOrder.baseAssetAmount;
537
- this.getListForOrder(newBidOrder).update(newBidOrder, bidNode.userAccount);
538
- }
539
- else {
540
- // both completely filled
541
- const newBidOrder = { ...bidOrder };
542
- newBidOrder.baseAssetAmountFilled = bidOrder.baseAssetAmount;
543
- this.getListForOrder(newBidOrder).update(newBidOrder, bidNode.userAccount);
544
- const newAskOrder = { ...askOrder };
545
- newAskOrder.baseAssetAmountFilled = askOrder.baseAssetAmount;
546
- this.getListForOrder(newAskOrder).update(newAskOrder, askNode.userAccount);
547
- }
548
- return {
549
- crossingNodes: [
550
- {
551
- node: takerNode,
552
- makerNode: makerNode,
553
- },
554
- ],
555
- exhaustedSide,
556
- };
555
+ return nodesToFill;
557
556
  }
558
557
  determineMakerAndTaker(askNode, bidNode) {
559
558
  if (bidNode.order.postOnly) {
560
559
  return {
561
560
  takerNode: askNode,
562
561
  makerNode: bidNode,
563
- makerSide: 'bid',
564
562
  };
565
563
  }
566
564
  else if (askNode.order.postOnly) {
567
565
  return {
568
566
  takerNode: bidNode,
569
567
  makerNode: askNode,
570
- makerSide: 'ask',
571
- };
572
- }
573
- else if ((0, __1.isMarketOrder)(bidNode.order) && (0, __1.isLimitOrder)(askNode.order)) {
574
- return {
575
- takerNode: bidNode,
576
- makerNode: askNode,
577
- makerSide: 'ask',
578
- };
579
- }
580
- else if ((0, __1.isMarketOrder)(askNode.order) && (0, __1.isLimitOrder)(bidNode.order)) {
581
- return {
582
- takerNode: askNode,
583
- makerNode: bidNode,
584
- makerSide: 'bid',
585
568
  };
586
569
  }
587
570
  else if (askNode.order.slot.lt(bidNode.order.slot)) {
588
571
  return {
589
572
  takerNode: bidNode,
590
573
  makerNode: askNode,
591
- makerSide: 'ask',
592
574
  };
593
575
  }
594
576
  else {
595
577
  return {
596
578
  takerNode: askNode,
597
579
  makerNode: bidNode,
598
- makerSide: 'bid',
599
580
  };
600
581
  }
601
582
  }
@@ -61,9 +61,23 @@ export declare class DriftClient {
61
61
  signerPublicKey?: PublicKey;
62
62
  getSignerPublicKey(): PublicKey;
63
63
  getStateAccount(): StateAccount;
64
+ /**
65
+ * Forces a fetch to rpc before returning accounts. Useful for anchor tests.
66
+ */
67
+ forceGetStateAccount(): Promise<StateAccount>;
64
68
  getPerpMarketAccount(marketIndex: number): PerpMarketAccount | undefined;
69
+ /**
70
+ * Forces a fetch to rpc before returning accounts. Useful for anchor tests.
71
+ * @param marketIndex
72
+ */
73
+ forceGetPerpMarketAccount(marketIndex: number): Promise<PerpMarketAccount | undefined>;
65
74
  getPerpMarketAccounts(): PerpMarketAccount[];
66
75
  getSpotMarketAccount(marketIndex: number): SpotMarketAccount | undefined;
76
+ /**
77
+ * Forces a fetch to rpc before returning accounts. Useful for anchor tests.
78
+ * @param marketIndex
79
+ */
80
+ forceGetSpotMarketAccount(marketIndex: number): Promise<SpotMarketAccount | undefined>;
67
81
  getSpotMarketAccounts(): SpotMarketAccount[];
68
82
  getQuoteSpotMarketAccount(): SpotMarketAccount;
69
83
  getOraclePriceDataAndSlot(oraclePublicKey: PublicKey): DataAndSlot<OraclePriceData> | undefined;
@@ -94,6 +108,11 @@ export declare class DriftClient {
94
108
  getUserStatsAccountPublicKey(): PublicKey;
95
109
  getUserAccountPublicKey(): Promise<PublicKey>;
96
110
  getUserAccount(subAccountId?: number): UserAccount | undefined;
111
+ /**
112
+ * Forces a fetch to rpc before returning accounts. Useful for anchor tests.
113
+ * @param subAccountId
114
+ */
115
+ forceGetUserAccount(subAccountId?: number): Promise<UserAccount | undefined>;
97
116
  getUserAccountAndSlot(subAccountId?: number): DataAndSlot<UserAccount> | undefined;
98
117
  getSpotPosition(marketIndex: number): SpotPosition | undefined;
99
118
  getQuoteAssetTokenAmount(): BN;