@drift-labs/sdk 2.17.0 → 2.18.0-beta.1

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
@@ -15,6 +15,7 @@ class DLOB {
15
15
  constructor() {
16
16
  this.openOrders = new Map();
17
17
  this.orderLists = new Map();
18
+ this.maxSlotForRestingLimitOrders = 0;
18
19
  this.initialized = false;
19
20
  this.init();
20
21
  }
@@ -42,6 +43,7 @@ class DLOB {
42
43
  }
43
44
  }
44
45
  this.orderLists.clear();
46
+ this.maxSlotForRestingLimitOrders = 0;
45
47
  this.init();
46
48
  }
47
49
  /**
@@ -49,7 +51,7 @@ class DLOB {
49
51
  *
50
52
  * @returns a promise that resolves when the DLOB is initialized
51
53
  */
52
- async initFromUserMap(userMap) {
54
+ async initFromUserMap(userMap, slot) {
53
55
  if (this.initialized) {
54
56
  return false;
55
57
  }
@@ -58,26 +60,26 @@ class DLOB {
58
60
  const userAccount = user.getUserAccount();
59
61
  const userAccountPubkey = user.getUserAccountPublicKey();
60
62
  for (const order of userAccount.orders) {
61
- this.insertOrder(order, userAccountPubkey);
63
+ this.insertOrder(order, userAccountPubkey, slot);
62
64
  }
63
65
  }
64
66
  this.initialized = true;
65
67
  return true;
66
68
  }
67
- initFromOrders(dlobOrders) {
69
+ initFromOrders(dlobOrders, slot) {
68
70
  if (this.initialized) {
69
71
  return false;
70
72
  }
71
73
  for (const { user, order } of dlobOrders) {
72
- this.insertOrder(order, user);
74
+ this.insertOrder(order, user, slot);
73
75
  }
74
76
  this.initialized = true;
75
77
  return true;
76
78
  }
77
- handleOrderRecord(record) {
78
- this.insertOrder(record.order, record.user);
79
+ handleOrderRecord(record, slot) {
80
+ this.insertOrder(record.order, record.user, slot);
79
81
  }
80
- handleOrderActionRecord(record) {
82
+ handleOrderActionRecord(record, slot) {
81
83
  if (__1.isOneOfVariant(record.action, ['place', 'expire'])) {
82
84
  return;
83
85
  }
@@ -85,13 +87,13 @@ class DLOB {
85
87
  if (record.taker !== null) {
86
88
  const takerOrder = this.getOrder(record.takerOrderId, record.taker);
87
89
  if (takerOrder) {
88
- this.trigger(takerOrder, record.taker);
90
+ this.trigger(takerOrder, record.taker, slot);
89
91
  }
90
92
  }
91
93
  if (record.maker !== null) {
92
94
  const makerOrder = this.getOrder(record.makerOrderId, record.maker);
93
95
  if (makerOrder) {
94
- this.trigger(makerOrder, record.maker);
96
+ this.trigger(makerOrder, record.maker, slot);
95
97
  }
96
98
  }
97
99
  }
@@ -99,13 +101,13 @@ class DLOB {
99
101
  if (record.taker !== null) {
100
102
  const takerOrder = this.getOrder(record.takerOrderId, record.taker);
101
103
  if (takerOrder) {
102
- this.updateOrder(takerOrder, record.taker, record.takerOrderCumulativeBaseAssetAmountFilled);
104
+ this.updateOrder(takerOrder, record.taker, slot, record.takerOrderCumulativeBaseAssetAmountFilled);
103
105
  }
104
106
  }
105
107
  if (record.maker !== null) {
106
108
  const makerOrder = this.getOrder(record.makerOrderId, record.maker);
107
109
  if (makerOrder) {
108
- this.updateOrder(makerOrder, record.maker, record.makerOrderCumulativeBaseAssetAmountFilled);
110
+ this.updateOrder(makerOrder, record.maker, slot, record.makerOrderCumulativeBaseAssetAmountFilled);
109
111
  }
110
112
  }
111
113
  }
@@ -113,18 +115,18 @@ class DLOB {
113
115
  if (record.taker !== null) {
114
116
  const takerOrder = this.getOrder(record.takerOrderId, record.taker);
115
117
  if (takerOrder) {
116
- this.delete(takerOrder, record.taker);
118
+ this.delete(takerOrder, record.taker, slot);
117
119
  }
118
120
  }
119
121
  if (record.maker !== null) {
120
122
  const makerOrder = this.getOrder(record.makerOrderId, record.maker);
121
123
  if (makerOrder) {
122
- this.delete(makerOrder, record.maker);
124
+ this.delete(makerOrder, record.maker, slot);
123
125
  }
124
126
  }
125
127
  }
126
128
  }
127
- insertOrder(order, userAccount, onInsert) {
129
+ insertOrder(order, userAccount, slot, onInsert) {
128
130
  var _a;
129
131
  if (__1.isVariant(order.status, 'init')) {
130
132
  return;
@@ -141,21 +143,25 @@ class DLOB {
141
143
  .get(marketType)
142
144
  .add(NodeList_1.getOrderSignature(order.orderId, userAccount));
143
145
  }
144
- (_a = this.getListForOrder(order)) === null || _a === void 0 ? void 0 : _a.insert(order, marketType, userAccount);
146
+ (_a = this.getListForOrder(order, slot)) === null || _a === void 0 ? void 0 : _a.insert(order, marketType, userAccount);
145
147
  if (onInsert) {
146
148
  onInsert();
147
149
  }
148
150
  }
149
151
  addOrderList(marketType, marketIndex) {
150
152
  this.orderLists.get(marketType).set(marketIndex, {
151
- limit: {
152
- ask: new NodeList_1.NodeList('limit', 'asc'),
153
- bid: new NodeList_1.NodeList('limit', 'desc'),
153
+ restingLimit: {
154
+ ask: new NodeList_1.NodeList('restingLimit', 'asc'),
155
+ bid: new NodeList_1.NodeList('restingLimit', 'desc'),
154
156
  },
155
157
  floatingLimit: {
156
158
  ask: new NodeList_1.NodeList('floatingLimit', 'asc'),
157
159
  bid: new NodeList_1.NodeList('floatingLimit', 'desc'),
158
160
  },
161
+ takingLimit: {
162
+ ask: new NodeList_1.NodeList('takingLimit', 'asc'),
163
+ bid: new NodeList_1.NodeList('takingLimit', 'asc'), // always sort ascending for market orders
164
+ },
159
165
  market: {
160
166
  ask: new NodeList_1.NodeList('market', 'asc'),
161
167
  bid: new NodeList_1.NodeList('market', 'asc'), // always sort ascending for market orders
@@ -166,10 +172,11 @@ class DLOB {
166
172
  },
167
173
  });
168
174
  }
169
- updateOrder(order, userAccount, cumulativeBaseAssetAmountFilled, onUpdate) {
175
+ updateOrder(order, userAccount, slot, cumulativeBaseAssetAmountFilled, onUpdate) {
170
176
  var _a;
177
+ this.updateRestingLimitOrders(slot);
171
178
  if (order.baseAssetAmount.eq(cumulativeBaseAssetAmountFilled)) {
172
- this.delete(order, userAccount);
179
+ this.delete(order, userAccount, slot);
173
180
  return;
174
181
  }
175
182
  if (order.baseAssetAmountFilled.eq(cumulativeBaseAssetAmountFilled)) {
@@ -179,16 +186,17 @@ class DLOB {
179
186
  ...order,
180
187
  };
181
188
  newOrder.baseAssetAmountFilled = cumulativeBaseAssetAmountFilled;
182
- (_a = this.getListForOrder(order)) === null || _a === void 0 ? void 0 : _a.update(newOrder, userAccount);
189
+ (_a = this.getListForOrder(order, slot)) === null || _a === void 0 ? void 0 : _a.update(newOrder, userAccount);
183
190
  if (onUpdate) {
184
191
  onUpdate();
185
192
  }
186
193
  }
187
- trigger(order, userAccount, onTrigger) {
194
+ trigger(order, userAccount, slot, onTrigger) {
188
195
  var _a;
189
196
  if (__1.isVariant(order.status, 'init')) {
190
197
  return;
191
198
  }
199
+ this.updateRestingLimitOrders(slot);
192
200
  if (__1.isTriggered(order)) {
193
201
  return;
194
202
  }
@@ -196,22 +204,23 @@ class DLOB {
196
204
  const triggerList = this.orderLists.get(marketType).get(order.marketIndex)
197
205
  .trigger[__1.isVariant(order.triggerCondition, 'above') ? 'above' : 'below'];
198
206
  triggerList.remove(order, userAccount);
199
- (_a = this.getListForOrder(order)) === null || _a === void 0 ? void 0 : _a.insert(order, marketType, userAccount);
207
+ (_a = this.getListForOrder(order, slot)) === null || _a === void 0 ? void 0 : _a.insert(order, marketType, userAccount);
200
208
  if (onTrigger) {
201
209
  onTrigger();
202
210
  }
203
211
  }
204
- delete(order, userAccount, onDelete) {
212
+ delete(order, userAccount, slot, onDelete) {
205
213
  var _a;
206
214
  if (__1.isVariant(order.status, 'init')) {
207
215
  return;
208
216
  }
209
- (_a = this.getListForOrder(order)) === null || _a === void 0 ? void 0 : _a.remove(order, userAccount);
217
+ this.updateRestingLimitOrders(slot);
218
+ (_a = this.getListForOrder(order, slot)) === null || _a === void 0 ? void 0 : _a.remove(order, userAccount);
210
219
  if (onDelete) {
211
220
  onDelete();
212
221
  }
213
222
  }
214
- getListForOrder(order) {
223
+ getListForOrder(order, slot) {
215
224
  const isInactiveTriggerOrder = __1.mustBeTriggered(order) && !__1.isTriggered(order);
216
225
  let type;
217
226
  if (isInactiveTriggerOrder) {
@@ -224,7 +233,8 @@ class DLOB {
224
233
  type = 'floatingLimit';
225
234
  }
226
235
  else {
227
- type = 'limit';
236
+ const isResting = __1.isRestingLimitOrder(order, slot);
237
+ type = isResting ? 'restingLimit' : 'takingLimit';
228
238
  }
229
239
  let subType;
230
240
  if (isInactiveTriggerOrder) {
@@ -239,6 +249,42 @@ class DLOB {
239
249
  }
240
250
  return this.orderLists.get(marketType).get(order.marketIndex)[type][subType];
241
251
  }
252
+ updateRestingLimitOrders(slot) {
253
+ if (slot <= this.maxSlotForRestingLimitOrders) {
254
+ return;
255
+ }
256
+ this.maxSlotForRestingLimitOrders = slot;
257
+ this.updateRestingLimitOrdersForMarketType(slot, 'perp');
258
+ this.updateRestingLimitOrdersForMarketType(slot, 'spot');
259
+ }
260
+ updateRestingLimitOrdersForMarketType(slot, marketTypeStr) {
261
+ for (const [_, nodeLists] of this.orderLists.get(marketTypeStr)) {
262
+ const nodesToUpdate = [];
263
+ for (const node of nodeLists.takingLimit.ask.getGenerator()) {
264
+ if (!__1.isRestingLimitOrder(node.order, slot)) {
265
+ continue;
266
+ }
267
+ nodesToUpdate.push({
268
+ side: 'ask',
269
+ node,
270
+ });
271
+ }
272
+ for (const node of nodeLists.takingLimit.bid.getGenerator()) {
273
+ if (!__1.isRestingLimitOrder(node.order, slot)) {
274
+ continue;
275
+ }
276
+ nodesToUpdate.push({
277
+ side: 'bid',
278
+ node,
279
+ });
280
+ }
281
+ for (const nodeToUpdate of nodesToUpdate) {
282
+ const { side, node } = nodeToUpdate;
283
+ nodeLists.takingLimit[side].remove(node.order, node.userAccount);
284
+ nodeLists.restingLimit[side].insert(node.order, marketTypeStr, node.userAccount);
285
+ }
286
+ }
287
+ }
242
288
  getOrder(orderId, userAccount) {
243
289
  for (const nodeList of this.getNodeLists()) {
244
290
  const node = nodeList.get(orderId, userAccount);
@@ -253,42 +299,45 @@ class DLOB {
253
299
  return [];
254
300
  }
255
301
  const isAmmPaused = exchangeStatus_1.ammPaused(stateAccount, marketAccount);
256
- const marketOrderNodesToFill = this.findMarketNodesToFill(marketIndex, slot, marketType, oraclePriceData, isAmmPaused, fallbackAsk, fallbackBid);
257
- const limitOrderNodesToFill = this.findLimitOrderNodesToFill(marketIndex, slot, marketType, oraclePriceData, isAmmPaused, fallbackAsk, fallbackBid);
302
+ const minAuctionDuration = __1.isVariant(marketType, 'perp')
303
+ ? stateAccount.minPerpAuctionDuration
304
+ : 0;
305
+ const restingLimitOrderNodesToFill = this.findRestingLimitOrderNodesToFill(marketIndex, slot, marketType, oraclePriceData, isAmmPaused, minAuctionDuration, fallbackAsk, fallbackBid);
306
+ const takingOrderNodesToFill = this.findTakingNodesToFill(marketIndex, slot, marketType, oraclePriceData, isAmmPaused, minAuctionDuration, fallbackAsk, fallbackBid);
258
307
  // get expired market nodes
259
308
  const expiredNodesToFill = this.findExpiredNodesToFill(marketIndex, ts, marketType);
260
- return marketOrderNodesToFill.concat(limitOrderNodesToFill, expiredNodesToFill);
309
+ return restingLimitOrderNodesToFill.concat(takingOrderNodesToFill, expiredNodesToFill);
261
310
  }
262
- findLimitOrderNodesToFill(marketIndex, slot, marketType, oraclePriceData, isAmmPaused, fallbackAsk, fallbackBid) {
311
+ findRestingLimitOrderNodesToFill(marketIndex, slot, marketType, oraclePriceData, isAmmPaused, minAuctionDuration, fallbackAsk, fallbackBid) {
263
312
  const nodesToFill = new Array();
264
- const crossingNodes = this.findCrossingLimitOrders(marketIndex, slot, marketType, oraclePriceData, fallbackAsk, fallbackBid);
313
+ const crossingNodes = this.findCrossingRestingLimitOrders(marketIndex, slot, marketType, oraclePriceData, minAuctionDuration, fallbackAsk, fallbackBid);
265
314
  for (const crossingNode of crossingNodes) {
266
315
  nodesToFill.push(crossingNode);
267
316
  }
268
317
  if (fallbackBid && !isAmmPaused) {
269
- const askGenerator = this.getLimitAsks(marketIndex, slot, marketType, oraclePriceData);
318
+ const askGenerator = this.getRestingLimitAsks(marketIndex, slot, marketType, oraclePriceData);
270
319
  const asksCrossingFallback = this.findNodesCrossingFallbackLiquidity(marketType, slot, oraclePriceData, askGenerator, fallbackBid, (askPrice, fallbackPrice) => {
271
320
  return askPrice.lte(fallbackPrice);
272
- });
321
+ }, minAuctionDuration);
273
322
  for (const askCrossingFallback of asksCrossingFallback) {
274
323
  nodesToFill.push(askCrossingFallback);
275
324
  }
276
325
  }
277
326
  if (fallbackAsk && !isAmmPaused) {
278
- const bidGenerator = this.getLimitBids(marketIndex, slot, marketType, oraclePriceData);
327
+ const bidGenerator = this.getRestingLimitBids(marketIndex, slot, marketType, oraclePriceData);
279
328
  const bidsCrossingFallback = this.findNodesCrossingFallbackLiquidity(marketType, slot, oraclePriceData, bidGenerator, fallbackAsk, (bidPrice, fallbackPrice) => {
280
329
  return bidPrice.gte(fallbackPrice);
281
- });
330
+ }, minAuctionDuration);
282
331
  for (const bidCrossingFallback of bidsCrossingFallback) {
283
332
  nodesToFill.push(bidCrossingFallback);
284
333
  }
285
334
  }
286
335
  return nodesToFill;
287
336
  }
288
- findMarketNodesToFill(marketIndex, slot, marketType, oraclePriceData, isAmmPaused, fallbackAsk, fallbackBid) {
337
+ findTakingNodesToFill(marketIndex, slot, marketType, oraclePriceData, isAmmPaused, minAuctionDuration, fallbackAsk, fallbackBid) {
289
338
  const nodesToFill = new Array();
290
- let marketOrderGenerator = this.getMarketAsks(marketIndex, marketType);
291
- const marketAsksCrossingBids = this.findMarketNodesCrossingLimitNodes(marketIndex, slot, marketType, oraclePriceData, marketOrderGenerator, this.getMakerLimitBids.bind(this), (takerPrice, makerPrice) => {
339
+ let takingOrderGenerator = this.getTakingAsks(marketIndex, marketType, slot, oraclePriceData);
340
+ const takingAsksCrossingBids = this.findTakingNodesCrossingMakerNodes(marketIndex, slot, marketType, oraclePriceData, takingOrderGenerator, this.getMakerLimitBids.bind(this), (takerPrice, makerPrice) => {
292
341
  if (__1.isVariant(marketType, 'spot')) {
293
342
  if (takerPrice === undefined) {
294
343
  return false;
@@ -299,20 +348,20 @@ class DLOB {
299
348
  }
300
349
  return takerPrice === undefined || takerPrice.lte(makerPrice);
301
350
  }, fallbackAsk);
302
- for (const marketAskCrossingBid of marketAsksCrossingBids) {
303
- nodesToFill.push(marketAskCrossingBid);
351
+ for (const takingAskCrossingBid of takingAsksCrossingBids) {
352
+ nodesToFill.push(takingAskCrossingBid);
304
353
  }
305
354
  if (fallbackBid && !isAmmPaused) {
306
- marketOrderGenerator = this.getMarketAsks(marketIndex, marketType);
307
- const marketAsksCrossingFallback = this.findNodesCrossingFallbackLiquidity(marketType, slot, oraclePriceData, marketOrderGenerator, fallbackBid, (takerPrice, fallbackPrice) => {
355
+ takingOrderGenerator = this.getTakingAsks(marketIndex, marketType, slot, oraclePriceData);
356
+ const takingAsksCrossingFallback = this.findNodesCrossingFallbackLiquidity(marketType, slot, oraclePriceData, takingOrderGenerator, fallbackBid, (takerPrice, fallbackPrice) => {
308
357
  return takerPrice === undefined || takerPrice.lte(fallbackPrice);
309
- });
310
- for (const marketAskCrossingFallback of marketAsksCrossingFallback) {
311
- nodesToFill.push(marketAskCrossingFallback);
358
+ }, minAuctionDuration);
359
+ for (const takingAskCrossingFallback of takingAsksCrossingFallback) {
360
+ nodesToFill.push(takingAskCrossingFallback);
312
361
  }
313
362
  }
314
- marketOrderGenerator = this.getMarketBids(marketIndex, marketType);
315
- const marketBidsToFill = this.findMarketNodesCrossingLimitNodes(marketIndex, slot, marketType, oraclePriceData, marketOrderGenerator, this.getMakerLimitAsks.bind(this), (takerPrice, makerPrice) => {
363
+ takingOrderGenerator = this.getTakingBids(marketIndex, marketType, slot, oraclePriceData);
364
+ const takingBidsToFill = this.findTakingNodesCrossingMakerNodes(marketIndex, slot, marketType, oraclePriceData, takingOrderGenerator, this.getMakerLimitAsks.bind(this), (takerPrice, makerPrice) => {
316
365
  if (__1.isVariant(marketType, 'spot')) {
317
366
  if (takerPrice === undefined) {
318
367
  return false;
@@ -323,21 +372,21 @@ class DLOB {
323
372
  }
324
373
  return takerPrice === undefined || takerPrice.gte(makerPrice);
325
374
  }, fallbackBid);
326
- for (const marketBidToFill of marketBidsToFill) {
327
- nodesToFill.push(marketBidToFill);
375
+ for (const takingBidToFill of takingBidsToFill) {
376
+ nodesToFill.push(takingBidToFill);
328
377
  }
329
378
  if (fallbackAsk && !isAmmPaused) {
330
- marketOrderGenerator = this.getMarketBids(marketIndex, marketType);
331
- const marketBidsCrossingFallback = this.findNodesCrossingFallbackLiquidity(marketType, slot, oraclePriceData, marketOrderGenerator, fallbackAsk, (takerPrice, fallbackPrice) => {
379
+ takingOrderGenerator = this.getTakingBids(marketIndex, marketType, slot, oraclePriceData);
380
+ const takingBidsCrossingFallback = this.findNodesCrossingFallbackLiquidity(marketType, slot, oraclePriceData, takingOrderGenerator, fallbackAsk, (takerPrice, fallbackPrice) => {
332
381
  return takerPrice === undefined || takerPrice.gte(fallbackPrice);
333
- });
334
- for (const marketBidCrossingFallback of marketBidsCrossingFallback) {
382
+ }, minAuctionDuration);
383
+ for (const marketBidCrossingFallback of takingBidsCrossingFallback) {
335
384
  nodesToFill.push(marketBidCrossingFallback);
336
385
  }
337
386
  }
338
387
  return nodesToFill;
339
388
  }
340
- findMarketNodesCrossingLimitNodes(marketIndex, slot, marketType, oraclePriceData, takerNodeGenerator, makerNodeGeneratorFn, doesCross, fallbackPrice) {
389
+ findTakingNodesCrossingMakerNodes(marketIndex, slot, marketType, oraclePriceData, takerNodeGenerator, makerNodeGeneratorFn, doesCross, fallbackPrice) {
341
390
  const nodesToFill = new Array();
342
391
  for (const takerNode of takerNodeGenerator) {
343
392
  const makerNodeGenerator = makerNodeGeneratorFn(marketIndex, slot, marketType, oraclePriceData, fallbackPrice);
@@ -367,11 +416,11 @@ class DLOB {
367
416
  const newMakerOrder = { ...makerOrder };
368
417
  newMakerOrder.baseAssetAmountFilled =
369
418
  makerOrder.baseAssetAmountFilled.add(baseFilled);
370
- this.getListForOrder(newMakerOrder).update(newMakerOrder, makerNode.userAccount);
419
+ this.getListForOrder(newMakerOrder, slot).update(newMakerOrder, makerNode.userAccount);
371
420
  const newTakerOrder = { ...takerOrder };
372
421
  newTakerOrder.baseAssetAmountFilled =
373
422
  takerOrder.baseAssetAmountFilled.add(baseFilled);
374
- this.getListForOrder(newTakerOrder).update(newTakerOrder, takerNode.userAccount);
423
+ this.getListForOrder(newTakerOrder, slot).update(newTakerOrder, takerNode.userAccount);
375
424
  if (newTakerOrder.baseAssetAmountFilled.eq(takerOrder.baseAssetAmount)) {
376
425
  break;
377
426
  }
@@ -379,7 +428,7 @@ class DLOB {
379
428
  }
380
429
  return nodesToFill;
381
430
  }
382
- findNodesCrossingFallbackLiquidity(marketType, slot, oraclePriceData, nodeGenerator, fallbackPrice, doesCross) {
431
+ findNodesCrossingFallbackLiquidity(marketType, slot, oraclePriceData, nodeGenerator, fallbackPrice, doesCross, minAuctionDuration) {
383
432
  var _a;
384
433
  const nodesToFill = new Array();
385
434
  let nextNode = nodeGenerator.next();
@@ -393,7 +442,8 @@ class DLOB {
393
442
  // order crosses if there is no limit price or it crosses fallback price
394
443
  const crosses = doesCross(nodePrice, fallbackPrice);
395
444
  // fallback is available if auction is complete or it's a spot order
396
- const fallbackAvailable = __1.isVariant(marketType, 'spot') || __1.isAuctionComplete(node.order, slot);
445
+ const fallbackAvailable = __1.isVariant(marketType, 'spot') ||
446
+ __1.isFallbackAvailableLiquiditySource(node.order, minAuctionDuration, slot);
397
447
  if (crosses && fallbackAvailable) {
398
448
  nodesToFill.push({
399
449
  node: node,
@@ -413,12 +463,14 @@ class DLOB {
413
463
  }
414
464
  // All bids/asks that can expire
415
465
  const bidGenerators = [
416
- nodeLists.limit.bid.getGenerator(),
466
+ nodeLists.takingLimit.bid.getGenerator(),
467
+ nodeLists.restingLimit.bid.getGenerator(),
417
468
  nodeLists.floatingLimit.bid.getGenerator(),
418
469
  nodeLists.market.bid.getGenerator(),
419
470
  ];
420
471
  const askGenerators = [
421
- nodeLists.limit.ask.getGenerator(),
472
+ nodeLists.takingLimit.ask.getGenerator(),
473
+ nodeLists.restingLimit.ask.getGenerator(),
422
474
  nodeLists.floatingLimit.ask.getGenerator(),
423
475
  nodeLists.market.ask.getGenerator(),
424
476
  ];
@@ -442,52 +494,50 @@ class DLOB {
442
494
  }
443
495
  return nodesToFill;
444
496
  }
445
- findJitAuctionNodesToFill(marketIndex, slot, marketType) {
497
+ findJitAuctionNodesToFill(marketIndex, slot, oraclePriceData, marketType) {
446
498
  const nodesToFill = new Array();
447
499
  // Then see if there are orders still in JIT auction
448
- for (const marketBid of this.getMarketBids(marketIndex, marketType)) {
449
- if (!__1.isAuctionComplete(marketBid.order, slot)) {
450
- nodesToFill.push({
451
- node: marketBid,
452
- });
453
- }
500
+ for (const marketBid of this.getTakingBids(marketIndex, marketType, slot, oraclePriceData)) {
501
+ nodesToFill.push({
502
+ node: marketBid,
503
+ });
454
504
  }
455
- for (const marketAsk of this.getMarketAsks(marketIndex, marketType)) {
456
- if (!__1.isAuctionComplete(marketAsk.order, slot)) {
457
- nodesToFill.push({
458
- node: marketAsk,
459
- });
460
- }
505
+ for (const marketAsk of this.getTakingAsks(marketIndex, marketType, slot, oraclePriceData)) {
506
+ nodesToFill.push({
507
+ node: marketAsk,
508
+ });
461
509
  }
462
510
  return nodesToFill;
463
511
  }
464
- *getMarketBids(marketIndex, marketType) {
512
+ *getTakingBids(marketIndex, marketType, slot, oraclePriceData) {
465
513
  const marketTypeStr = __1.getVariant(marketType);
466
514
  const orderLists = this.orderLists.get(marketTypeStr).get(marketIndex);
467
515
  if (!orderLists) {
468
516
  return;
469
517
  }
470
- const generator = orderLists.market.bid.getGenerator();
471
- for (const marketBidNode of generator) {
472
- if (marketBidNode.isBaseFilled()) {
473
- continue;
474
- }
475
- yield marketBidNode;
476
- }
518
+ this.updateRestingLimitOrders(slot);
519
+ const generatorList = [
520
+ orderLists.market.bid.getGenerator(),
521
+ orderLists.takingLimit.bid.getGenerator(),
522
+ ];
523
+ yield* this.getBestNode(generatorList, oraclePriceData, slot, (bestNode, currentNode) => {
524
+ return bestNode.order.slot.lt(currentNode.order.slot);
525
+ });
477
526
  }
478
- *getMarketAsks(marketIndex, marketType) {
527
+ *getTakingAsks(marketIndex, marketType, slot, oraclePriceData) {
479
528
  const marketTypeStr = __1.getVariant(marketType);
480
529
  const orderLists = this.orderLists.get(marketTypeStr).get(marketIndex);
481
530
  if (!orderLists) {
482
531
  return;
483
532
  }
484
- const generator = orderLists.market.ask.getGenerator();
485
- for (const marketAskNode of generator) {
486
- if (marketAskNode.isBaseFilled()) {
487
- continue;
488
- }
489
- yield marketAskNode;
490
- }
533
+ this.updateRestingLimitOrders(slot);
534
+ const generatorList = [
535
+ orderLists.market.ask.getGenerator(),
536
+ orderLists.takingLimit.ask.getGenerator(),
537
+ ];
538
+ yield* this.getBestNode(generatorList, oraclePriceData, slot, (bestNode, currentNode) => {
539
+ return bestNode.order.slot.lt(currentNode.order.slot);
540
+ });
491
541
  }
492
542
  *getBestNode(generatorList, oraclePriceData, slot, compareFcn) {
493
543
  const generators = generatorList.map((generator) => {
@@ -507,16 +557,7 @@ class DLOB {
507
557
  }
508
558
  const bestValue = bestGenerator.next.value;
509
559
  const currentValue = currentGenerator.next.value;
510
- // always return the market orders first
511
- if (bestValue.order && __1.isMarketOrder(bestValue.order)) {
512
- return bestGenerator;
513
- }
514
- if (currentValue.order && __1.isMarketOrder(currentValue.order)) {
515
- return currentGenerator;
516
- }
517
- const bestPrice = bestValue.getPrice(oraclePriceData, slot);
518
- const currentPrice = currentValue.getPrice(oraclePriceData, slot);
519
- return compareFcn(bestPrice, currentPrice)
560
+ return compareFcn(bestValue, currentValue, slot, oraclePriceData)
520
561
  ? bestGenerator
521
562
  : currentGenerator;
522
563
  });
@@ -534,58 +575,59 @@ class DLOB {
534
575
  }
535
576
  }
536
577
  }
537
- *getLimitAsks(marketIndex, slot, marketType, oraclePriceData) {
578
+ *getRestingLimitAsks(marketIndex, slot, marketType, oraclePriceData) {
538
579
  if (__1.isVariant(marketType, 'spot') && !oraclePriceData) {
539
580
  throw new Error('Must provide OraclePriceData to get spot asks');
540
581
  }
582
+ this.updateRestingLimitOrders(slot);
541
583
  const marketTypeStr = __1.getVariant(marketType);
542
584
  const nodeLists = this.orderLists.get(marketTypeStr).get(marketIndex);
543
585
  if (!nodeLists) {
544
586
  return;
545
587
  }
546
588
  const generatorList = [
547
- nodeLists.limit.ask.getGenerator(),
589
+ nodeLists.restingLimit.ask.getGenerator(),
548
590
  nodeLists.floatingLimit.ask.getGenerator(),
549
591
  ];
550
- yield* this.getBestNode(generatorList, oraclePriceData, slot, (bestPrice, currentPrice) => {
551
- return bestPrice.lt(currentPrice);
592
+ yield* this.getBestNode(generatorList, oraclePriceData, slot, (bestNode, currentNode, slot, oraclePriceData) => {
593
+ return bestNode
594
+ .getPrice(oraclePriceData, slot)
595
+ .lt(currentNode.getPrice(oraclePriceData, slot));
552
596
  });
553
597
  }
554
598
  /**
555
- * Filters the limit asks that are post only, have been place for sufficiently long or are above the fallback bid
556
- * Market orders can only fill against orders that meet this criteria
599
+ * Filters the limit asks that are resting and do not cross fallback bid
600
+ * Taking orders can only fill against orders that meet this criteria
557
601
  *
558
602
  * @returns
559
603
  */
560
604
  *getMakerLimitAsks(marketIndex, slot, marketType, oraclePriceData, fallbackBid) {
561
- for (const node of this.getLimitAsks(marketIndex, slot, marketType, oraclePriceData)) {
562
- if (this.isRestingLimitOrder(node.order, slot)) {
563
- yield node;
564
- }
565
- else if (fallbackBid &&
566
- node.getPrice(oraclePriceData, slot).gt(fallbackBid)) {
567
- yield node;
605
+ for (const node of this.getRestingLimitAsks(marketIndex, slot, marketType, oraclePriceData)) {
606
+ if (fallbackBid &&
607
+ node.getPrice(oraclePriceData, slot).lte(fallbackBid)) {
608
+ continue;
568
609
  }
610
+ yield node;
569
611
  }
570
612
  }
571
- isRestingLimitOrder(order, slot) {
572
- return order.postOnly || new __1.BN(slot).sub(order.slot).gte(new __1.BN(45));
573
- }
574
- *getLimitBids(marketIndex, slot, marketType, oraclePriceData) {
613
+ *getRestingLimitBids(marketIndex, slot, marketType, oraclePriceData) {
575
614
  if (__1.isVariant(marketType, 'spot') && !oraclePriceData) {
576
615
  throw new Error('Must provide OraclePriceData to get spot bids');
577
616
  }
617
+ this.updateRestingLimitOrders(slot);
578
618
  const marketTypeStr = __1.getVariant(marketType);
579
619
  const nodeLists = this.orderLists.get(marketTypeStr).get(marketIndex);
580
620
  if (!nodeLists) {
581
621
  return;
582
622
  }
583
623
  const generatorList = [
584
- nodeLists.limit.bid.getGenerator(),
624
+ nodeLists.restingLimit.bid.getGenerator(),
585
625
  nodeLists.floatingLimit.bid.getGenerator(),
586
626
  ];
587
- yield* this.getBestNode(generatorList, oraclePriceData, slot, (bestPrice, currentPrice) => {
588
- return bestPrice.gt(currentPrice);
627
+ yield* this.getBestNode(generatorList, oraclePriceData, slot, (bestNode, currentNode, slot, oraclePriceData) => {
628
+ return bestNode
629
+ .getPrice(oraclePriceData, slot)
630
+ .gt(currentNode.getPrice(oraclePriceData, slot));
589
631
  });
590
632
  }
591
633
  /**
@@ -595,14 +637,12 @@ class DLOB {
595
637
  * @returns
596
638
  */
597
639
  *getMakerLimitBids(marketIndex, slot, marketType, oraclePriceData, fallbackAsk) {
598
- for (const node of this.getLimitBids(marketIndex, slot, marketType, oraclePriceData)) {
599
- if (this.isRestingLimitOrder(node.order, slot)) {
600
- yield node;
601
- }
602
- else if (fallbackAsk &&
603
- node.getPrice(oraclePriceData, slot).lt(fallbackAsk)) {
604
- yield node;
640
+ for (const node of this.getRestingLimitBids(marketIndex, slot, marketType, oraclePriceData)) {
641
+ if (fallbackAsk &&
642
+ node.getPrice(oraclePriceData, slot).gte(fallbackAsk)) {
643
+ continue;
605
644
  }
645
+ yield node;
606
646
  }
607
647
  }
608
648
  *getAsks(marketIndex, fallbackAsk, slot, marketType, oraclePriceData) {
@@ -610,15 +650,32 @@ class DLOB {
610
650
  throw new Error('Must provide OraclePriceData to get spot asks');
611
651
  }
612
652
  const generatorList = [
613
- this.getMarketAsks(marketIndex, marketType),
614
- this.getLimitAsks(marketIndex, slot, marketType, oraclePriceData),
653
+ this.getTakingAsks(marketIndex, marketType, slot, oraclePriceData),
654
+ this.getRestingLimitAsks(marketIndex, slot, marketType, oraclePriceData),
615
655
  ];
616
656
  const marketTypeStr = __1.getVariant(marketType);
617
657
  if (marketTypeStr === 'perp' && fallbackAsk) {
618
658
  generatorList.push(NodeList_1.getVammNodeGenerator(fallbackAsk));
619
659
  }
620
- yield* this.getBestNode(generatorList, oraclePriceData, slot, (bestPrice, currentPrice) => {
621
- return bestPrice.lt(currentPrice);
660
+ yield* this.getBestNode(generatorList, oraclePriceData, slot, (bestNode, currentNode, slot, oraclePriceData) => {
661
+ const bestNodeTaking = bestNode.order
662
+ ? __1.isTakingOrder(bestNode.order, slot)
663
+ : false;
664
+ const currentNodeTaking = currentNode.order
665
+ ? __1.isTakingOrder(currentNode.order, slot)
666
+ : false;
667
+ if (bestNodeTaking && currentNodeTaking) {
668
+ return bestNode.order.slot.lt(currentNode.order.slot);
669
+ }
670
+ if (bestNodeTaking) {
671
+ return true;
672
+ }
673
+ if (currentNodeTaking) {
674
+ return false;
675
+ }
676
+ return bestNode
677
+ .getPrice(oraclePriceData, slot)
678
+ .lt(currentNode.getPrice(oraclePriceData, slot));
622
679
  });
623
680
  }
624
681
  *getBids(marketIndex, fallbackBid, slot, marketType, oraclePriceData) {
@@ -626,37 +683,60 @@ class DLOB {
626
683
  throw new Error('Must provide OraclePriceData to get spot bids');
627
684
  }
628
685
  const generatorList = [
629
- this.getMarketBids(marketIndex, marketType),
630
- this.getLimitBids(marketIndex, slot, marketType, oraclePriceData),
686
+ this.getTakingBids(marketIndex, marketType, slot, oraclePriceData),
687
+ this.getRestingLimitBids(marketIndex, slot, marketType, oraclePriceData),
631
688
  ];
632
689
  const marketTypeStr = __1.getVariant(marketType);
633
690
  if (marketTypeStr === 'perp' && fallbackBid) {
634
691
  generatorList.push(NodeList_1.getVammNodeGenerator(fallbackBid));
635
692
  }
636
- yield* this.getBestNode(generatorList, oraclePriceData, slot, (bestPrice, currentPrice) => {
637
- return bestPrice.gt(currentPrice);
693
+ yield* this.getBestNode(generatorList, oraclePriceData, slot, (bestNode, currentNode, slot, oraclePriceData) => {
694
+ const bestNodeTaking = bestNode.order
695
+ ? __1.isTakingOrder(bestNode.order, slot)
696
+ : false;
697
+ const currentNodeTaking = currentNode.order
698
+ ? __1.isTakingOrder(currentNode.order, slot)
699
+ : false;
700
+ if (bestNodeTaking && currentNodeTaking) {
701
+ return bestNode.order.slot.lt(currentNode.order.slot);
702
+ }
703
+ if (bestNodeTaking) {
704
+ return true;
705
+ }
706
+ if (currentNodeTaking) {
707
+ return false;
708
+ }
709
+ return bestNode
710
+ .getPrice(oraclePriceData, slot)
711
+ .gt(currentNode.getPrice(oraclePriceData, slot));
638
712
  });
639
713
  }
640
- findCrossingLimitOrders(marketIndex, slot, marketType, oraclePriceData, fallbackAsk, fallbackBid) {
714
+ findCrossingRestingLimitOrders(marketIndex, slot, marketType, oraclePriceData, minAuctionDuration, fallbackAsk, fallbackBid) {
641
715
  const nodesToFill = new Array();
642
- for (const askNode of this.getLimitAsks(marketIndex, slot, marketType, oraclePriceData)) {
643
- for (const bidNode of this.getLimitBids(marketIndex, slot, marketType, oraclePriceData)) {
716
+ for (const askNode of this.getRestingLimitAsks(marketIndex, slot, marketType, oraclePriceData)) {
717
+ const bidGenerator = this.getRestingLimitBids(marketIndex, slot, marketType, oraclePriceData);
718
+ for (const bidNode of bidGenerator) {
644
719
  const bidPrice = bidNode.getPrice(oraclePriceData, slot);
645
720
  const askPrice = askNode.getPrice(oraclePriceData, slot);
646
- // orders don't cross - we're done walking the book
721
+ // orders don't cross
647
722
  if (bidPrice.lt(askPrice)) {
648
- return nodesToFill;
723
+ break;
649
724
  }
650
725
  const bidOrder = bidNode.order;
651
726
  const askOrder = askNode.order;
652
727
  // Can't match orders from the same user
653
728
  const sameUser = bidNode.userAccount.equals(askNode.userAccount);
654
- if (sameUser || (bidOrder.postOnly && askOrder.postOnly)) {
729
+ if (sameUser) {
730
+ continue;
731
+ }
732
+ const makerAndTaker = this.determineMakerAndTaker(askNode, bidNode);
733
+ // unable to match maker and taker due to post only or slot
734
+ if (!makerAndTaker) {
655
735
  continue;
656
736
  }
657
- const { takerNode, makerNode } = this.determineMakerAndTaker(askNode, bidNode);
737
+ const { takerNode, makerNode } = makerAndTaker;
658
738
  // extra guard against bad fills for limit orders where auction is incomplete
659
- if (!__1.isAuctionComplete(takerNode.order, slot)) {
739
+ if (!__1.isFallbackAvailableLiquiditySource(takerNode.order, minAuctionDuration, slot)) {
660
740
  let bidPrice;
661
741
  let askPrice;
662
742
  if (__1.isVariant(takerNode.order.direction, 'long')) {
@@ -677,12 +757,12 @@ class DLOB {
677
757
  const newBidOrder = { ...bidOrder };
678
758
  newBidOrder.baseAssetAmountFilled =
679
759
  bidOrder.baseAssetAmountFilled.add(baseFilled);
680
- this.getListForOrder(newBidOrder).update(newBidOrder, bidNode.userAccount);
760
+ this.getListForOrder(newBidOrder, slot).update(newBidOrder, bidNode.userAccount);
681
761
  // ask completely filled
682
762
  const newAskOrder = { ...askOrder };
683
763
  newAskOrder.baseAssetAmountFilled =
684
764
  askOrder.baseAssetAmountFilled.add(baseFilled);
685
- this.getListForOrder(newAskOrder).update(newAskOrder, askNode.userAccount);
765
+ this.getListForOrder(newAskOrder, slot).update(newAskOrder, askNode.userAccount);
686
766
  nodesToFill.push({
687
767
  node: takerNode,
688
768
  makerNode: makerNode,
@@ -695,30 +775,23 @@ class DLOB {
695
775
  return nodesToFill;
696
776
  }
697
777
  determineMakerAndTaker(askNode, bidNode) {
698
- if (bidNode.order.postOnly) {
699
- return {
700
- takerNode: askNode,
701
- makerNode: bidNode,
702
- };
703
- }
704
- else if (askNode.order.postOnly) {
778
+ const askSlot = askNode.order.slot.add(new __1.BN(askNode.order.auctionDuration));
779
+ const bidSlot = bidNode.order.slot.add(new __1.BN(bidNode.order.auctionDuration));
780
+ if (askSlot.lte(bidSlot) && !bidNode.order.postOnly) {
705
781
  return {
706
782
  takerNode: bidNode,
707
783
  makerNode: askNode,
708
784
  };
709
785
  }
710
- else if (askNode.order.slot.lt(bidNode.order.slot)) {
711
- return {
712
- takerNode: bidNode,
713
- makerNode: askNode,
714
- };
715
- }
716
- else {
786
+ else if (bidSlot.lte(askSlot) && !askNode.order.postOnly) {
717
787
  return {
718
788
  takerNode: askNode,
719
789
  makerNode: bidNode,
720
790
  };
721
791
  }
792
+ else {
793
+ return undefined;
794
+ }
722
795
  }
723
796
  getBestAsk(marketIndex, fallbackAsk, slot, marketType, oraclePriceData) {
724
797
  return this.getAsks(marketIndex, fallbackAsk, slot, marketType, oraclePriceData)
@@ -743,11 +816,9 @@ class DLOB {
743
816
  if (triggerAboveList) {
744
817
  for (const node of triggerAboveList.getGenerator()) {
745
818
  if (oraclePrice.gt(node.order.triggerPrice)) {
746
- if (__1.isAuctionComplete(node.order, slot)) {
747
- nodesToTrigger.push({
748
- node: node,
749
- });
750
- }
819
+ nodesToTrigger.push({
820
+ node: node,
821
+ });
751
822
  }
752
823
  else {
753
824
  break;
@@ -760,11 +831,9 @@ class DLOB {
760
831
  if (triggerBelowList) {
761
832
  for (const node of triggerBelowList.getGenerator()) {
762
833
  if (oraclePrice.lt(node.order.triggerPrice)) {
763
- if (__1.isAuctionComplete(node.order, slot)) {
764
- nodesToTrigger.push({
765
- node: node,
766
- });
767
- }
834
+ nodesToTrigger.push({
835
+ node: node,
836
+ });
768
837
  }
769
838
  else {
770
839
  break;
@@ -830,8 +899,10 @@ class DLOB {
830
899
  }
831
900
  *getNodeLists() {
832
901
  for (const [_, nodeLists] of this.orderLists.get('perp')) {
833
- yield nodeLists.limit.bid;
834
- yield nodeLists.limit.ask;
902
+ yield nodeLists.restingLimit.bid;
903
+ yield nodeLists.restingLimit.ask;
904
+ yield nodeLists.takingLimit.bid;
905
+ yield nodeLists.takingLimit.ask;
835
906
  yield nodeLists.market.bid;
836
907
  yield nodeLists.market.ask;
837
908
  yield nodeLists.floatingLimit.bid;
@@ -840,8 +911,10 @@ class DLOB {
840
911
  yield nodeLists.trigger.below;
841
912
  }
842
913
  for (const [_, nodeLists] of this.orderLists.get('spot')) {
843
- yield nodeLists.limit.bid;
844
- yield nodeLists.limit.ask;
914
+ yield nodeLists.restingLimit.bid;
915
+ yield nodeLists.restingLimit.ask;
916
+ yield nodeLists.takingLimit.bid;
917
+ yield nodeLists.takingLimit.ask;
845
918
  yield nodeLists.market.bid;
846
919
  yield nodeLists.market.ask;
847
920
  yield nodeLists.floatingLimit.bid;