@drift-labs/sdk 2.17.0-beta.0 → 2.18.0-beta.0
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.d.ts +31 -25
- package/lib/dlob/DLOB.js +229 -155
- package/lib/dlob/DLOBNode.d.ts +11 -5
- package/lib/dlob/DLOBNode.js +13 -5
- package/lib/driftClient.js +5 -10
- package/lib/examples/loadDlob.js +1 -1
- package/lib/idl/drift.json +1 -1
- package/lib/math/auction.d.ts +1 -0
- package/lib/math/auction.js +9 -2
- package/lib/math/orders.d.ts +2 -0
- package/lib/math/orders.js +11 -2
- package/lib/math/spotBalance.js +1 -1
- package/lib/types.d.ts +1 -1
- package/lib/user.js +1 -4
- package/package.json +1 -1
- package/src/dlob/DLOB.ts +362 -161
- package/src/dlob/DLOBNode.ts +20 -7
- package/src/driftClient.ts +5 -10
- package/src/examples/loadDlob.ts +1 -1
- package/src/idl/drift.json +1 -1
- package/src/math/auction.ts +13 -1
- package/src/math/orders.ts +14 -1
- package/src/math/spotBalance.ts +1 -1
- package/src/types.ts +1 -1
- package/src/user.ts +1 -4
- package/tests/dlob/test.ts +612 -76
package/src/dlob/DLOB.ts
CHANGED
|
@@ -19,7 +19,6 @@ import {
|
|
|
19
19
|
SlotSubscriber,
|
|
20
20
|
MarketTypeStr,
|
|
21
21
|
StateAccount,
|
|
22
|
-
isMarketOrder,
|
|
23
22
|
mustBeTriggered,
|
|
24
23
|
isTriggered,
|
|
25
24
|
getLimitPrice,
|
|
@@ -28,6 +27,9 @@ import {
|
|
|
28
27
|
OrderActionRecord,
|
|
29
28
|
ZERO,
|
|
30
29
|
BN_MAX,
|
|
30
|
+
isRestingLimitOrder,
|
|
31
|
+
isTakingOrder,
|
|
32
|
+
isFallbackAvailableLiquiditySource,
|
|
31
33
|
} from '..';
|
|
32
34
|
import { PublicKey } from '@solana/web3.js';
|
|
33
35
|
import { DLOBNode, DLOBNodeType, TriggerOrderNode } from '..';
|
|
@@ -35,14 +37,18 @@ import { ammPaused, exchangePaused, fillPaused } from '../math/exchangeStatus';
|
|
|
35
37
|
import { DLOBOrders } from './DLOBOrders';
|
|
36
38
|
|
|
37
39
|
export type MarketNodeLists = {
|
|
38
|
-
|
|
39
|
-
ask: NodeList<'
|
|
40
|
-
bid: NodeList<'
|
|
40
|
+
restingLimit: {
|
|
41
|
+
ask: NodeList<'restingLimit'>;
|
|
42
|
+
bid: NodeList<'restingLimit'>;
|
|
41
43
|
};
|
|
42
44
|
floatingLimit: {
|
|
43
45
|
ask: NodeList<'floatingLimit'>;
|
|
44
46
|
bid: NodeList<'floatingLimit'>;
|
|
45
47
|
};
|
|
48
|
+
takingLimit: {
|
|
49
|
+
ask: NodeList<'takingLimit'>;
|
|
50
|
+
bid: NodeList<'takingLimit'>;
|
|
51
|
+
};
|
|
46
52
|
market: {
|
|
47
53
|
ask: NodeList<'market'>;
|
|
48
54
|
bid: NodeList<'market'>;
|
|
@@ -75,6 +81,7 @@ const SUPPORTED_ORDER_TYPES = [
|
|
|
75
81
|
export class DLOB {
|
|
76
82
|
openOrders = new Map<MarketTypeStr, Set<string>>();
|
|
77
83
|
orderLists = new Map<MarketTypeStr, Map<number, MarketNodeLists>>();
|
|
84
|
+
maxSlotForRestingLimitOrders = 0;
|
|
78
85
|
|
|
79
86
|
initialized = false;
|
|
80
87
|
|
|
@@ -109,6 +116,8 @@ export class DLOB {
|
|
|
109
116
|
}
|
|
110
117
|
this.orderLists.clear();
|
|
111
118
|
|
|
119
|
+
this.maxSlotForRestingLimitOrders = 0;
|
|
120
|
+
|
|
112
121
|
this.init();
|
|
113
122
|
}
|
|
114
123
|
|
|
@@ -117,7 +126,10 @@ export class DLOB {
|
|
|
117
126
|
*
|
|
118
127
|
* @returns a promise that resolves when the DLOB is initialized
|
|
119
128
|
*/
|
|
120
|
-
public async initFromUserMap(
|
|
129
|
+
public async initFromUserMap(
|
|
130
|
+
userMap: UserMap,
|
|
131
|
+
slot: number
|
|
132
|
+
): Promise<boolean> {
|
|
121
133
|
if (this.initialized) {
|
|
122
134
|
return false;
|
|
123
135
|
}
|
|
@@ -128,7 +140,7 @@ export class DLOB {
|
|
|
128
140
|
const userAccountPubkey = user.getUserAccountPublicKey();
|
|
129
141
|
|
|
130
142
|
for (const order of userAccount.orders) {
|
|
131
|
-
this.insertOrder(order, userAccountPubkey);
|
|
143
|
+
this.insertOrder(order, userAccountPubkey, slot);
|
|
132
144
|
}
|
|
133
145
|
}
|
|
134
146
|
|
|
@@ -136,24 +148,27 @@ export class DLOB {
|
|
|
136
148
|
return true;
|
|
137
149
|
}
|
|
138
150
|
|
|
139
|
-
public initFromOrders(dlobOrders: DLOBOrders): boolean {
|
|
151
|
+
public initFromOrders(dlobOrders: DLOBOrders, slot: number): boolean {
|
|
140
152
|
if (this.initialized) {
|
|
141
153
|
return false;
|
|
142
154
|
}
|
|
143
155
|
|
|
144
156
|
for (const { user, order } of dlobOrders) {
|
|
145
|
-
this.insertOrder(order, user);
|
|
157
|
+
this.insertOrder(order, user, slot);
|
|
146
158
|
}
|
|
147
159
|
|
|
148
160
|
this.initialized = true;
|
|
149
161
|
return true;
|
|
150
162
|
}
|
|
151
163
|
|
|
152
|
-
public handleOrderRecord(record: OrderRecord): void {
|
|
153
|
-
this.insertOrder(record.order, record.user);
|
|
164
|
+
public handleOrderRecord(record: OrderRecord, slot: number): void {
|
|
165
|
+
this.insertOrder(record.order, record.user, slot);
|
|
154
166
|
}
|
|
155
167
|
|
|
156
|
-
public handleOrderActionRecord(
|
|
168
|
+
public handleOrderActionRecord(
|
|
169
|
+
record: OrderActionRecord,
|
|
170
|
+
slot: number
|
|
171
|
+
): void {
|
|
157
172
|
if (isOneOfVariant(record.action, ['place', 'expire'])) {
|
|
158
173
|
return;
|
|
159
174
|
}
|
|
@@ -162,14 +177,14 @@ export class DLOB {
|
|
|
162
177
|
if (record.taker !== null) {
|
|
163
178
|
const takerOrder = this.getOrder(record.takerOrderId, record.taker);
|
|
164
179
|
if (takerOrder) {
|
|
165
|
-
this.trigger(takerOrder, record.taker);
|
|
180
|
+
this.trigger(takerOrder, record.taker, slot);
|
|
166
181
|
}
|
|
167
182
|
}
|
|
168
183
|
|
|
169
184
|
if (record.maker !== null) {
|
|
170
185
|
const makerOrder = this.getOrder(record.makerOrderId, record.maker);
|
|
171
186
|
if (makerOrder) {
|
|
172
|
-
this.trigger(makerOrder, record.maker);
|
|
187
|
+
this.trigger(makerOrder, record.maker, slot);
|
|
173
188
|
}
|
|
174
189
|
}
|
|
175
190
|
} else if (isVariant(record.action, 'fill')) {
|
|
@@ -179,6 +194,7 @@ export class DLOB {
|
|
|
179
194
|
this.updateOrder(
|
|
180
195
|
takerOrder,
|
|
181
196
|
record.taker,
|
|
197
|
+
slot,
|
|
182
198
|
record.takerOrderCumulativeBaseAssetAmountFilled
|
|
183
199
|
);
|
|
184
200
|
}
|
|
@@ -190,6 +206,7 @@ export class DLOB {
|
|
|
190
206
|
this.updateOrder(
|
|
191
207
|
makerOrder,
|
|
192
208
|
record.maker,
|
|
209
|
+
slot,
|
|
193
210
|
record.makerOrderCumulativeBaseAssetAmountFilled
|
|
194
211
|
);
|
|
195
212
|
}
|
|
@@ -198,14 +215,14 @@ export class DLOB {
|
|
|
198
215
|
if (record.taker !== null) {
|
|
199
216
|
const takerOrder = this.getOrder(record.takerOrderId, record.taker);
|
|
200
217
|
if (takerOrder) {
|
|
201
|
-
this.delete(takerOrder, record.taker);
|
|
218
|
+
this.delete(takerOrder, record.taker, slot);
|
|
202
219
|
}
|
|
203
220
|
}
|
|
204
221
|
|
|
205
222
|
if (record.maker !== null) {
|
|
206
223
|
const makerOrder = this.getOrder(record.makerOrderId, record.maker);
|
|
207
224
|
if (makerOrder) {
|
|
208
|
-
this.delete(makerOrder, record.maker);
|
|
225
|
+
this.delete(makerOrder, record.maker, slot);
|
|
209
226
|
}
|
|
210
227
|
}
|
|
211
228
|
}
|
|
@@ -214,6 +231,7 @@ export class DLOB {
|
|
|
214
231
|
public insertOrder(
|
|
215
232
|
order: Order,
|
|
216
233
|
userAccount: PublicKey,
|
|
234
|
+
slot: number,
|
|
217
235
|
onInsert?: OrderBookCallback
|
|
218
236
|
): void {
|
|
219
237
|
if (isVariant(order.status, 'init')) {
|
|
@@ -235,7 +253,7 @@ export class DLOB {
|
|
|
235
253
|
.get(marketType)
|
|
236
254
|
.add(getOrderSignature(order.orderId, userAccount));
|
|
237
255
|
}
|
|
238
|
-
this.getListForOrder(order)?.insert(order, marketType, userAccount);
|
|
256
|
+
this.getListForOrder(order, slot)?.insert(order, marketType, userAccount);
|
|
239
257
|
|
|
240
258
|
if (onInsert) {
|
|
241
259
|
onInsert();
|
|
@@ -244,14 +262,18 @@ export class DLOB {
|
|
|
244
262
|
|
|
245
263
|
addOrderList(marketType: MarketTypeStr, marketIndex: number): void {
|
|
246
264
|
this.orderLists.get(marketType).set(marketIndex, {
|
|
247
|
-
|
|
248
|
-
ask: new NodeList('
|
|
249
|
-
bid: new NodeList('
|
|
265
|
+
restingLimit: {
|
|
266
|
+
ask: new NodeList('restingLimit', 'asc'),
|
|
267
|
+
bid: new NodeList('restingLimit', 'desc'),
|
|
250
268
|
},
|
|
251
269
|
floatingLimit: {
|
|
252
270
|
ask: new NodeList('floatingLimit', 'asc'),
|
|
253
271
|
bid: new NodeList('floatingLimit', 'desc'),
|
|
254
272
|
},
|
|
273
|
+
takingLimit: {
|
|
274
|
+
ask: new NodeList('takingLimit', 'asc'),
|
|
275
|
+
bid: new NodeList('takingLimit', 'asc'), // always sort ascending for market orders
|
|
276
|
+
},
|
|
255
277
|
market: {
|
|
256
278
|
ask: new NodeList('market', 'asc'),
|
|
257
279
|
bid: new NodeList('market', 'asc'), // always sort ascending for market orders
|
|
@@ -266,11 +288,14 @@ export class DLOB {
|
|
|
266
288
|
public updateOrder(
|
|
267
289
|
order: Order,
|
|
268
290
|
userAccount: PublicKey,
|
|
291
|
+
slot: number,
|
|
269
292
|
cumulativeBaseAssetAmountFilled: BN,
|
|
270
293
|
onUpdate?: OrderBookCallback
|
|
271
294
|
): void {
|
|
295
|
+
this.updateRestingLimitOrders(slot);
|
|
296
|
+
|
|
272
297
|
if (order.baseAssetAmount.eq(cumulativeBaseAssetAmountFilled)) {
|
|
273
|
-
this.delete(order, userAccount);
|
|
298
|
+
this.delete(order, userAccount, slot);
|
|
274
299
|
return;
|
|
275
300
|
}
|
|
276
301
|
|
|
@@ -283,7 +308,7 @@ export class DLOB {
|
|
|
283
308
|
};
|
|
284
309
|
newOrder.baseAssetAmountFilled = cumulativeBaseAssetAmountFilled;
|
|
285
310
|
|
|
286
|
-
this.getListForOrder(order)?.update(newOrder, userAccount);
|
|
311
|
+
this.getListForOrder(order, slot)?.update(newOrder, userAccount);
|
|
287
312
|
|
|
288
313
|
if (onUpdate) {
|
|
289
314
|
onUpdate();
|
|
@@ -293,12 +318,15 @@ export class DLOB {
|
|
|
293
318
|
public trigger(
|
|
294
319
|
order: Order,
|
|
295
320
|
userAccount: PublicKey,
|
|
321
|
+
slot: number,
|
|
296
322
|
onTrigger?: OrderBookCallback
|
|
297
323
|
): void {
|
|
298
324
|
if (isVariant(order.status, 'init')) {
|
|
299
325
|
return;
|
|
300
326
|
}
|
|
301
327
|
|
|
328
|
+
this.updateRestingLimitOrders(slot);
|
|
329
|
+
|
|
302
330
|
if (isTriggered(order)) {
|
|
303
331
|
return;
|
|
304
332
|
}
|
|
@@ -309,7 +337,7 @@ export class DLOB {
|
|
|
309
337
|
.trigger[isVariant(order.triggerCondition, 'above') ? 'above' : 'below'];
|
|
310
338
|
triggerList.remove(order, userAccount);
|
|
311
339
|
|
|
312
|
-
this.getListForOrder(order)?.insert(order, marketType, userAccount);
|
|
340
|
+
this.getListForOrder(order, slot)?.insert(order, marketType, userAccount);
|
|
313
341
|
if (onTrigger) {
|
|
314
342
|
onTrigger();
|
|
315
343
|
}
|
|
@@ -318,19 +346,25 @@ export class DLOB {
|
|
|
318
346
|
public delete(
|
|
319
347
|
order: Order,
|
|
320
348
|
userAccount: PublicKey,
|
|
349
|
+
slot: number,
|
|
321
350
|
onDelete?: OrderBookCallback
|
|
322
351
|
): void {
|
|
323
352
|
if (isVariant(order.status, 'init')) {
|
|
324
353
|
return;
|
|
325
354
|
}
|
|
326
355
|
|
|
327
|
-
this.
|
|
356
|
+
this.updateRestingLimitOrders(slot);
|
|
357
|
+
|
|
358
|
+
this.getListForOrder(order, slot)?.remove(order, userAccount);
|
|
328
359
|
if (onDelete) {
|
|
329
360
|
onDelete();
|
|
330
361
|
}
|
|
331
362
|
}
|
|
332
363
|
|
|
333
|
-
public getListForOrder(
|
|
364
|
+
public getListForOrder(
|
|
365
|
+
order: Order,
|
|
366
|
+
slot: number
|
|
367
|
+
): NodeList<any> | undefined {
|
|
334
368
|
const isInactiveTriggerOrder =
|
|
335
369
|
mustBeTriggered(order) && !isTriggered(order);
|
|
336
370
|
|
|
@@ -344,7 +378,8 @@ export class DLOB {
|
|
|
344
378
|
} else if (order.oraclePriceOffset !== 0) {
|
|
345
379
|
type = 'floatingLimit';
|
|
346
380
|
} else {
|
|
347
|
-
|
|
381
|
+
const isResting = isRestingLimitOrder(order, slot);
|
|
382
|
+
type = isResting ? 'restingLimit' : 'takingLimit';
|
|
348
383
|
}
|
|
349
384
|
|
|
350
385
|
let subType: string;
|
|
@@ -365,6 +400,58 @@ export class DLOB {
|
|
|
365
400
|
];
|
|
366
401
|
}
|
|
367
402
|
|
|
403
|
+
public updateRestingLimitOrders(slot: number): void {
|
|
404
|
+
if (slot <= this.maxSlotForRestingLimitOrders) {
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
this.maxSlotForRestingLimitOrders = slot;
|
|
409
|
+
|
|
410
|
+
this.updateRestingLimitOrdersForMarketType(slot, 'perp');
|
|
411
|
+
|
|
412
|
+
this.updateRestingLimitOrdersForMarketType(slot, 'spot');
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
updateRestingLimitOrdersForMarketType(
|
|
416
|
+
slot: number,
|
|
417
|
+
marketTypeStr: MarketTypeStr
|
|
418
|
+
): void {
|
|
419
|
+
for (const [_, nodeLists] of this.orderLists.get(marketTypeStr)) {
|
|
420
|
+
const nodesToUpdate = [];
|
|
421
|
+
for (const node of nodeLists.takingLimit.ask.getGenerator()) {
|
|
422
|
+
if (!isRestingLimitOrder(node.order, slot)) {
|
|
423
|
+
continue;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
nodesToUpdate.push({
|
|
427
|
+
side: 'ask',
|
|
428
|
+
node,
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
for (const node of nodeLists.takingLimit.bid.getGenerator()) {
|
|
433
|
+
if (!isRestingLimitOrder(node.order, slot)) {
|
|
434
|
+
continue;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
nodesToUpdate.push({
|
|
438
|
+
side: 'bid',
|
|
439
|
+
node,
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
for (const nodeToUpdate of nodesToUpdate) {
|
|
444
|
+
const { side, node } = nodeToUpdate;
|
|
445
|
+
nodeLists.takingLimit[side].remove(node.order, node.userAccount);
|
|
446
|
+
nodeLists.restingLimit[side].insert(
|
|
447
|
+
node.order,
|
|
448
|
+
marketTypeStr,
|
|
449
|
+
node.userAccount
|
|
450
|
+
);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
368
455
|
public getOrder(orderId: number, userAccount: PublicKey): Order | undefined {
|
|
369
456
|
for (const nodeList of this.getNodeLists()) {
|
|
370
457
|
const node = nodeList.get(orderId, userAccount);
|
|
@@ -393,24 +480,30 @@ export class DLOB {
|
|
|
393
480
|
|
|
394
481
|
const isAmmPaused = ammPaused(stateAccount, marketAccount);
|
|
395
482
|
|
|
396
|
-
const
|
|
397
|
-
|
|
483
|
+
const minAuctionDuration = isVariant(marketType, 'perp')
|
|
484
|
+
? stateAccount.minPerpAuctionDuration
|
|
485
|
+
: 0;
|
|
486
|
+
|
|
487
|
+
const restingLimitOrderNodesToFill: Array<NodeToFill> =
|
|
488
|
+
this.findRestingLimitOrderNodesToFill(
|
|
398
489
|
marketIndex,
|
|
399
490
|
slot,
|
|
400
491
|
marketType,
|
|
401
492
|
oraclePriceData,
|
|
402
493
|
isAmmPaused,
|
|
494
|
+
minAuctionDuration,
|
|
403
495
|
fallbackAsk,
|
|
404
496
|
fallbackBid
|
|
405
497
|
);
|
|
406
498
|
|
|
407
|
-
const
|
|
408
|
-
this.
|
|
499
|
+
const takingOrderNodesToFill: Array<NodeToFill> =
|
|
500
|
+
this.findTakingNodesToFill(
|
|
409
501
|
marketIndex,
|
|
410
502
|
slot,
|
|
411
503
|
marketType,
|
|
412
504
|
oraclePriceData,
|
|
413
505
|
isAmmPaused,
|
|
506
|
+
minAuctionDuration,
|
|
414
507
|
fallbackAsk,
|
|
415
508
|
fallbackBid
|
|
416
509
|
);
|
|
@@ -421,28 +514,30 @@ export class DLOB {
|
|
|
421
514
|
ts,
|
|
422
515
|
marketType
|
|
423
516
|
);
|
|
424
|
-
return
|
|
425
|
-
|
|
517
|
+
return restingLimitOrderNodesToFill.concat(
|
|
518
|
+
takingOrderNodesToFill,
|
|
426
519
|
expiredNodesToFill
|
|
427
520
|
);
|
|
428
521
|
}
|
|
429
522
|
|
|
430
|
-
public
|
|
523
|
+
public findRestingLimitOrderNodesToFill(
|
|
431
524
|
marketIndex: number,
|
|
432
525
|
slot: number,
|
|
433
526
|
marketType: MarketType,
|
|
434
527
|
oraclePriceData: OraclePriceData,
|
|
435
528
|
isAmmPaused: boolean,
|
|
529
|
+
minAuctionDuration: number,
|
|
436
530
|
fallbackAsk: BN | undefined,
|
|
437
531
|
fallbackBid: BN | undefined
|
|
438
532
|
): NodeToFill[] {
|
|
439
533
|
const nodesToFill = new Array<NodeToFill>();
|
|
440
534
|
|
|
441
|
-
const crossingNodes = this.
|
|
535
|
+
const crossingNodes = this.findCrossingRestingLimitOrders(
|
|
442
536
|
marketIndex,
|
|
443
537
|
slot,
|
|
444
538
|
marketType,
|
|
445
539
|
oraclePriceData,
|
|
540
|
+
minAuctionDuration,
|
|
446
541
|
fallbackAsk,
|
|
447
542
|
fallbackBid
|
|
448
543
|
);
|
|
@@ -452,7 +547,7 @@ export class DLOB {
|
|
|
452
547
|
}
|
|
453
548
|
|
|
454
549
|
if (fallbackBid && !isAmmPaused) {
|
|
455
|
-
const askGenerator = this.
|
|
550
|
+
const askGenerator = this.getRestingLimitAsks(
|
|
456
551
|
marketIndex,
|
|
457
552
|
slot,
|
|
458
553
|
marketType,
|
|
@@ -466,7 +561,8 @@ export class DLOB {
|
|
|
466
561
|
fallbackBid,
|
|
467
562
|
(askPrice, fallbackPrice) => {
|
|
468
563
|
return askPrice.lte(fallbackPrice);
|
|
469
|
-
}
|
|
564
|
+
},
|
|
565
|
+
minAuctionDuration
|
|
470
566
|
);
|
|
471
567
|
|
|
472
568
|
for (const askCrossingFallback of asksCrossingFallback) {
|
|
@@ -475,7 +571,7 @@ export class DLOB {
|
|
|
475
571
|
}
|
|
476
572
|
|
|
477
573
|
if (fallbackAsk && !isAmmPaused) {
|
|
478
|
-
const bidGenerator = this.
|
|
574
|
+
const bidGenerator = this.getRestingLimitBids(
|
|
479
575
|
marketIndex,
|
|
480
576
|
slot,
|
|
481
577
|
marketType,
|
|
@@ -489,7 +585,8 @@ export class DLOB {
|
|
|
489
585
|
fallbackAsk,
|
|
490
586
|
(bidPrice, fallbackPrice) => {
|
|
491
587
|
return bidPrice.gte(fallbackPrice);
|
|
492
|
-
}
|
|
588
|
+
},
|
|
589
|
+
minAuctionDuration
|
|
493
590
|
);
|
|
494
591
|
|
|
495
592
|
for (const bidCrossingFallback of bidsCrossingFallback) {
|
|
@@ -500,25 +597,31 @@ export class DLOB {
|
|
|
500
597
|
return nodesToFill;
|
|
501
598
|
}
|
|
502
599
|
|
|
503
|
-
public
|
|
600
|
+
public findTakingNodesToFill(
|
|
504
601
|
marketIndex: number,
|
|
505
602
|
slot: number,
|
|
506
603
|
marketType: MarketType,
|
|
507
604
|
oraclePriceData: OraclePriceData,
|
|
508
605
|
isAmmPaused: boolean,
|
|
606
|
+
minAuctionDuration: number,
|
|
509
607
|
fallbackAsk: BN | undefined,
|
|
510
608
|
fallbackBid?: BN | undefined
|
|
511
609
|
): NodeToFill[] {
|
|
512
610
|
const nodesToFill = new Array<NodeToFill>();
|
|
513
611
|
|
|
514
|
-
let
|
|
612
|
+
let takingOrderGenerator = this.getTakingAsks(
|
|
613
|
+
marketIndex,
|
|
614
|
+
marketType,
|
|
615
|
+
slot,
|
|
616
|
+
oraclePriceData
|
|
617
|
+
);
|
|
515
618
|
|
|
516
|
-
const
|
|
619
|
+
const takingAsksCrossingBids = this.findTakingNodesCrossingMakerNodes(
|
|
517
620
|
marketIndex,
|
|
518
621
|
slot,
|
|
519
622
|
marketType,
|
|
520
623
|
oraclePriceData,
|
|
521
|
-
|
|
624
|
+
takingOrderGenerator,
|
|
522
625
|
this.getMakerLimitBids.bind(this),
|
|
523
626
|
(takerPrice, makerPrice) => {
|
|
524
627
|
if (isVariant(marketType, 'spot')) {
|
|
@@ -534,37 +637,48 @@ export class DLOB {
|
|
|
534
637
|
},
|
|
535
638
|
fallbackAsk
|
|
536
639
|
);
|
|
537
|
-
for (const
|
|
538
|
-
nodesToFill.push(
|
|
640
|
+
for (const takingAskCrossingBid of takingAsksCrossingBids) {
|
|
641
|
+
nodesToFill.push(takingAskCrossingBid);
|
|
539
642
|
}
|
|
540
643
|
|
|
541
644
|
if (fallbackBid && !isAmmPaused) {
|
|
542
|
-
|
|
543
|
-
|
|
645
|
+
takingOrderGenerator = this.getTakingAsks(
|
|
646
|
+
marketIndex,
|
|
647
|
+
marketType,
|
|
648
|
+
slot,
|
|
649
|
+
oraclePriceData
|
|
650
|
+
);
|
|
651
|
+
const takingAsksCrossingFallback =
|
|
544
652
|
this.findNodesCrossingFallbackLiquidity(
|
|
545
653
|
marketType,
|
|
546
654
|
slot,
|
|
547
655
|
oraclePriceData,
|
|
548
|
-
|
|
656
|
+
takingOrderGenerator,
|
|
549
657
|
fallbackBid,
|
|
550
658
|
(takerPrice, fallbackPrice) => {
|
|
551
659
|
return takerPrice === undefined || takerPrice.lte(fallbackPrice);
|
|
552
|
-
}
|
|
660
|
+
},
|
|
661
|
+
minAuctionDuration
|
|
553
662
|
);
|
|
554
663
|
|
|
555
|
-
for (const
|
|
556
|
-
nodesToFill.push(
|
|
664
|
+
for (const takingAskCrossingFallback of takingAsksCrossingFallback) {
|
|
665
|
+
nodesToFill.push(takingAskCrossingFallback);
|
|
557
666
|
}
|
|
558
667
|
}
|
|
559
668
|
|
|
560
|
-
|
|
669
|
+
takingOrderGenerator = this.getTakingBids(
|
|
670
|
+
marketIndex,
|
|
671
|
+
marketType,
|
|
672
|
+
slot,
|
|
673
|
+
oraclePriceData
|
|
674
|
+
);
|
|
561
675
|
|
|
562
|
-
const
|
|
676
|
+
const takingBidsToFill = this.findTakingNodesCrossingMakerNodes(
|
|
563
677
|
marketIndex,
|
|
564
678
|
slot,
|
|
565
679
|
marketType,
|
|
566
680
|
oraclePriceData,
|
|
567
|
-
|
|
681
|
+
takingOrderGenerator,
|
|
568
682
|
this.getMakerLimitAsks.bind(this),
|
|
569
683
|
(takerPrice, makerPrice) => {
|
|
570
684
|
if (isVariant(marketType, 'spot')) {
|
|
@@ -582,24 +696,30 @@ export class DLOB {
|
|
|
582
696
|
fallbackBid
|
|
583
697
|
);
|
|
584
698
|
|
|
585
|
-
for (const
|
|
586
|
-
nodesToFill.push(
|
|
699
|
+
for (const takingBidToFill of takingBidsToFill) {
|
|
700
|
+
nodesToFill.push(takingBidToFill);
|
|
587
701
|
}
|
|
588
702
|
|
|
589
703
|
if (fallbackAsk && !isAmmPaused) {
|
|
590
|
-
|
|
591
|
-
|
|
704
|
+
takingOrderGenerator = this.getTakingBids(
|
|
705
|
+
marketIndex,
|
|
706
|
+
marketType,
|
|
707
|
+
slot,
|
|
708
|
+
oraclePriceData
|
|
709
|
+
);
|
|
710
|
+
const takingBidsCrossingFallback =
|
|
592
711
|
this.findNodesCrossingFallbackLiquidity(
|
|
593
712
|
marketType,
|
|
594
713
|
slot,
|
|
595
714
|
oraclePriceData,
|
|
596
|
-
|
|
715
|
+
takingOrderGenerator,
|
|
597
716
|
fallbackAsk,
|
|
598
717
|
(takerPrice, fallbackPrice) => {
|
|
599
718
|
return takerPrice === undefined || takerPrice.gte(fallbackPrice);
|
|
600
|
-
}
|
|
719
|
+
},
|
|
720
|
+
minAuctionDuration
|
|
601
721
|
);
|
|
602
|
-
for (const marketBidCrossingFallback of
|
|
722
|
+
for (const marketBidCrossingFallback of takingBidsCrossingFallback) {
|
|
603
723
|
nodesToFill.push(marketBidCrossingFallback);
|
|
604
724
|
}
|
|
605
725
|
}
|
|
@@ -607,7 +727,7 @@ export class DLOB {
|
|
|
607
727
|
return nodesToFill;
|
|
608
728
|
}
|
|
609
729
|
|
|
610
|
-
public
|
|
730
|
+
public findTakingNodesCrossingMakerNodes(
|
|
611
731
|
marketIndex: number,
|
|
612
732
|
slot: number,
|
|
613
733
|
marketType: MarketType,
|
|
@@ -671,7 +791,7 @@ export class DLOB {
|
|
|
671
791
|
const newMakerOrder = { ...makerOrder };
|
|
672
792
|
newMakerOrder.baseAssetAmountFilled =
|
|
673
793
|
makerOrder.baseAssetAmountFilled.add(baseFilled);
|
|
674
|
-
this.getListForOrder(newMakerOrder).update(
|
|
794
|
+
this.getListForOrder(newMakerOrder, slot).update(
|
|
675
795
|
newMakerOrder,
|
|
676
796
|
makerNode.userAccount
|
|
677
797
|
);
|
|
@@ -679,7 +799,7 @@ export class DLOB {
|
|
|
679
799
|
const newTakerOrder = { ...takerOrder };
|
|
680
800
|
newTakerOrder.baseAssetAmountFilled =
|
|
681
801
|
takerOrder.baseAssetAmountFilled.add(baseFilled);
|
|
682
|
-
this.getListForOrder(newTakerOrder).update(
|
|
802
|
+
this.getListForOrder(newTakerOrder, slot).update(
|
|
683
803
|
newTakerOrder,
|
|
684
804
|
takerNode.userAccount
|
|
685
805
|
);
|
|
@@ -701,7 +821,8 @@ export class DLOB {
|
|
|
701
821
|
oraclePriceData: OraclePriceData,
|
|
702
822
|
nodeGenerator: Generator<DLOBNode>,
|
|
703
823
|
fallbackPrice: BN,
|
|
704
|
-
doesCross: (nodePrice: BN | undefined, fallbackPrice: BN) => boolean
|
|
824
|
+
doesCross: (nodePrice: BN | undefined, fallbackPrice: BN) => boolean,
|
|
825
|
+
minAuctionDuration: number
|
|
705
826
|
): NodeToFill[] {
|
|
706
827
|
const nodesToFill = new Array<NodeToFill>();
|
|
707
828
|
|
|
@@ -721,7 +842,12 @@ export class DLOB {
|
|
|
721
842
|
|
|
722
843
|
// fallback is available if auction is complete or it's a spot order
|
|
723
844
|
const fallbackAvailable =
|
|
724
|
-
isVariant(marketType, 'spot') ||
|
|
845
|
+
isVariant(marketType, 'spot') ||
|
|
846
|
+
isFallbackAvailableLiquiditySource(
|
|
847
|
+
node.order,
|
|
848
|
+
minAuctionDuration,
|
|
849
|
+
slot
|
|
850
|
+
);
|
|
725
851
|
|
|
726
852
|
if (crosses && fallbackAvailable) {
|
|
727
853
|
nodesToFill.push({
|
|
@@ -752,12 +878,14 @@ export class DLOB {
|
|
|
752
878
|
|
|
753
879
|
// All bids/asks that can expire
|
|
754
880
|
const bidGenerators = [
|
|
755
|
-
nodeLists.
|
|
881
|
+
nodeLists.takingLimit.bid.getGenerator(),
|
|
882
|
+
nodeLists.restingLimit.bid.getGenerator(),
|
|
756
883
|
nodeLists.floatingLimit.bid.getGenerator(),
|
|
757
884
|
nodeLists.market.bid.getGenerator(),
|
|
758
885
|
];
|
|
759
886
|
const askGenerators = [
|
|
760
|
-
nodeLists.
|
|
887
|
+
nodeLists.takingLimit.ask.getGenerator(),
|
|
888
|
+
nodeLists.restingLimit.ask.getGenerator(),
|
|
761
889
|
nodeLists.floatingLimit.ask.getGenerator(),
|
|
762
890
|
nodeLists.market.ask.getGenerator(),
|
|
763
891
|
];
|
|
@@ -788,31 +916,40 @@ export class DLOB {
|
|
|
788
916
|
public findJitAuctionNodesToFill(
|
|
789
917
|
marketIndex: number,
|
|
790
918
|
slot: number,
|
|
919
|
+
oraclePriceData: OraclePriceData,
|
|
791
920
|
marketType: MarketType
|
|
792
921
|
): NodeToFill[] {
|
|
793
922
|
const nodesToFill = new Array<NodeToFill>();
|
|
794
923
|
// Then see if there are orders still in JIT auction
|
|
795
|
-
for (const marketBid of this.
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
924
|
+
for (const marketBid of this.getTakingBids(
|
|
925
|
+
marketIndex,
|
|
926
|
+
marketType,
|
|
927
|
+
slot,
|
|
928
|
+
oraclePriceData
|
|
929
|
+
)) {
|
|
930
|
+
nodesToFill.push({
|
|
931
|
+
node: marketBid,
|
|
932
|
+
});
|
|
801
933
|
}
|
|
802
934
|
|
|
803
|
-
for (const marketAsk of this.
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
935
|
+
for (const marketAsk of this.getTakingAsks(
|
|
936
|
+
marketIndex,
|
|
937
|
+
marketType,
|
|
938
|
+
slot,
|
|
939
|
+
oraclePriceData
|
|
940
|
+
)) {
|
|
941
|
+
nodesToFill.push({
|
|
942
|
+
node: marketAsk,
|
|
943
|
+
});
|
|
809
944
|
}
|
|
810
945
|
return nodesToFill;
|
|
811
946
|
}
|
|
812
947
|
|
|
813
|
-
*
|
|
948
|
+
*getTakingBids(
|
|
814
949
|
marketIndex: number,
|
|
815
|
-
marketType: MarketType
|
|
950
|
+
marketType: MarketType,
|
|
951
|
+
slot: number,
|
|
952
|
+
oraclePriceData: OraclePriceData
|
|
816
953
|
): Generator<DLOBNode> {
|
|
817
954
|
const marketTypeStr = getVariant(marketType) as MarketTypeStr;
|
|
818
955
|
const orderLists = this.orderLists.get(marketTypeStr).get(marketIndex);
|
|
@@ -820,18 +957,28 @@ export class DLOB {
|
|
|
820
957
|
return;
|
|
821
958
|
}
|
|
822
959
|
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
960
|
+
this.updateRestingLimitOrders(slot);
|
|
961
|
+
|
|
962
|
+
const generatorList = [
|
|
963
|
+
orderLists.market.bid.getGenerator(),
|
|
964
|
+
orderLists.takingLimit.bid.getGenerator(),
|
|
965
|
+
];
|
|
966
|
+
|
|
967
|
+
yield* this.getBestNode(
|
|
968
|
+
generatorList,
|
|
969
|
+
oraclePriceData,
|
|
970
|
+
slot,
|
|
971
|
+
(bestNode, currentNode) => {
|
|
972
|
+
return bestNode.order.slot.lt(currentNode.order.slot);
|
|
827
973
|
}
|
|
828
|
-
|
|
829
|
-
}
|
|
974
|
+
);
|
|
830
975
|
}
|
|
831
976
|
|
|
832
|
-
*
|
|
977
|
+
*getTakingAsks(
|
|
833
978
|
marketIndex: number,
|
|
834
|
-
marketType: MarketType
|
|
979
|
+
marketType: MarketType,
|
|
980
|
+
slot: number,
|
|
981
|
+
oraclePriceData: OraclePriceData
|
|
835
982
|
): Generator<DLOBNode> {
|
|
836
983
|
const marketTypeStr = getVariant(marketType) as MarketTypeStr;
|
|
837
984
|
const orderLists = this.orderLists.get(marketTypeStr).get(marketIndex);
|
|
@@ -839,20 +986,33 @@ export class DLOB {
|
|
|
839
986
|
return;
|
|
840
987
|
}
|
|
841
988
|
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
989
|
+
this.updateRestingLimitOrders(slot);
|
|
990
|
+
|
|
991
|
+
const generatorList = [
|
|
992
|
+
orderLists.market.ask.getGenerator(),
|
|
993
|
+
orderLists.takingLimit.ask.getGenerator(),
|
|
994
|
+
];
|
|
995
|
+
|
|
996
|
+
yield* this.getBestNode(
|
|
997
|
+
generatorList,
|
|
998
|
+
oraclePriceData,
|
|
999
|
+
slot,
|
|
1000
|
+
(bestNode, currentNode) => {
|
|
1001
|
+
return bestNode.order.slot.lt(currentNode.order.slot);
|
|
846
1002
|
}
|
|
847
|
-
|
|
848
|
-
}
|
|
1003
|
+
);
|
|
849
1004
|
}
|
|
850
1005
|
|
|
851
1006
|
private *getBestNode(
|
|
852
1007
|
generatorList: Array<Generator<DLOBNode>>,
|
|
853
1008
|
oraclePriceData: OraclePriceData,
|
|
854
1009
|
slot: number,
|
|
855
|
-
compareFcn: (
|
|
1010
|
+
compareFcn: (
|
|
1011
|
+
bestDLOBNode: DLOBNode,
|
|
1012
|
+
currentDLOBNode: DLOBNode,
|
|
1013
|
+
slot: number,
|
|
1014
|
+
oraclePriceData: OraclePriceData
|
|
1015
|
+
) => boolean
|
|
856
1016
|
): Generator<DLOBNode> {
|
|
857
1017
|
const generators = generatorList.map((generator) => {
|
|
858
1018
|
return {
|
|
@@ -876,18 +1036,7 @@ export class DLOB {
|
|
|
876
1036
|
const bestValue = bestGenerator.next.value as DLOBNode;
|
|
877
1037
|
const currentValue = currentGenerator.next.value as DLOBNode;
|
|
878
1038
|
|
|
879
|
-
|
|
880
|
-
if (bestValue.order && isMarketOrder(bestValue.order)) {
|
|
881
|
-
return bestGenerator;
|
|
882
|
-
}
|
|
883
|
-
if (currentValue.order && isMarketOrder(currentValue.order)) {
|
|
884
|
-
return currentGenerator;
|
|
885
|
-
}
|
|
886
|
-
|
|
887
|
-
const bestPrice = bestValue.getPrice(oraclePriceData, slot);
|
|
888
|
-
const currentPrice = currentValue.getPrice(oraclePriceData, slot);
|
|
889
|
-
|
|
890
|
-
return compareFcn(bestPrice, currentPrice)
|
|
1039
|
+
return compareFcn(bestValue, currentValue, slot, oraclePriceData)
|
|
891
1040
|
? bestGenerator
|
|
892
1041
|
: currentGenerator;
|
|
893
1042
|
}
|
|
@@ -908,7 +1057,7 @@ export class DLOB {
|
|
|
908
1057
|
}
|
|
909
1058
|
}
|
|
910
1059
|
|
|
911
|
-
*
|
|
1060
|
+
*getRestingLimitAsks(
|
|
912
1061
|
marketIndex: number,
|
|
913
1062
|
slot: number,
|
|
914
1063
|
marketType: MarketType,
|
|
@@ -917,6 +1066,9 @@ export class DLOB {
|
|
|
917
1066
|
if (isVariant(marketType, 'spot') && !oraclePriceData) {
|
|
918
1067
|
throw new Error('Must provide OraclePriceData to get spot asks');
|
|
919
1068
|
}
|
|
1069
|
+
|
|
1070
|
+
this.updateRestingLimitOrders(slot);
|
|
1071
|
+
|
|
920
1072
|
const marketTypeStr = getVariant(marketType) as MarketTypeStr;
|
|
921
1073
|
const nodeLists = this.orderLists.get(marketTypeStr).get(marketIndex);
|
|
922
1074
|
|
|
@@ -925,7 +1077,7 @@ export class DLOB {
|
|
|
925
1077
|
}
|
|
926
1078
|
|
|
927
1079
|
const generatorList = [
|
|
928
|
-
nodeLists.
|
|
1080
|
+
nodeLists.restingLimit.ask.getGenerator(),
|
|
929
1081
|
nodeLists.floatingLimit.ask.getGenerator(),
|
|
930
1082
|
];
|
|
931
1083
|
|
|
@@ -933,15 +1085,17 @@ export class DLOB {
|
|
|
933
1085
|
generatorList,
|
|
934
1086
|
oraclePriceData,
|
|
935
1087
|
slot,
|
|
936
|
-
(
|
|
937
|
-
return
|
|
1088
|
+
(bestNode, currentNode, slot, oraclePriceData) => {
|
|
1089
|
+
return bestNode
|
|
1090
|
+
.getPrice(oraclePriceData, slot)
|
|
1091
|
+
.lt(currentNode.getPrice(oraclePriceData, slot));
|
|
938
1092
|
}
|
|
939
1093
|
);
|
|
940
1094
|
}
|
|
941
1095
|
|
|
942
1096
|
/**
|
|
943
|
-
* Filters the limit asks that are
|
|
944
|
-
*
|
|
1097
|
+
* Filters the limit asks that are resting and do not cross fallback bid
|
|
1098
|
+
* Taking orders can only fill against orders that meet this criteria
|
|
945
1099
|
*
|
|
946
1100
|
* @returns
|
|
947
1101
|
*/
|
|
@@ -952,28 +1106,23 @@ export class DLOB {
|
|
|
952
1106
|
oraclePriceData: OraclePriceData,
|
|
953
1107
|
fallbackBid?: BN
|
|
954
1108
|
): Generator<DLOBNode> {
|
|
955
|
-
for (const node of this.
|
|
1109
|
+
for (const node of this.getRestingLimitAsks(
|
|
956
1110
|
marketIndex,
|
|
957
1111
|
slot,
|
|
958
1112
|
marketType,
|
|
959
1113
|
oraclePriceData
|
|
960
1114
|
)) {
|
|
961
|
-
if (
|
|
962
|
-
yield node;
|
|
963
|
-
} else if (
|
|
1115
|
+
if (
|
|
964
1116
|
fallbackBid &&
|
|
965
|
-
node.getPrice(oraclePriceData, slot).
|
|
1117
|
+
node.getPrice(oraclePriceData, slot).lte(fallbackBid)
|
|
966
1118
|
) {
|
|
967
|
-
|
|
1119
|
+
continue;
|
|
968
1120
|
}
|
|
1121
|
+
yield node;
|
|
969
1122
|
}
|
|
970
1123
|
}
|
|
971
1124
|
|
|
972
|
-
|
|
973
|
-
return order.postOnly || new BN(slot).sub(order.slot).gte(new BN(45));
|
|
974
|
-
}
|
|
975
|
-
|
|
976
|
-
*getLimitBids(
|
|
1125
|
+
*getRestingLimitBids(
|
|
977
1126
|
marketIndex: number,
|
|
978
1127
|
slot: number,
|
|
979
1128
|
marketType: MarketType,
|
|
@@ -983,6 +1132,8 @@ export class DLOB {
|
|
|
983
1132
|
throw new Error('Must provide OraclePriceData to get spot bids');
|
|
984
1133
|
}
|
|
985
1134
|
|
|
1135
|
+
this.updateRestingLimitOrders(slot);
|
|
1136
|
+
|
|
986
1137
|
const marketTypeStr = getVariant(marketType) as MarketTypeStr;
|
|
987
1138
|
const nodeLists = this.orderLists.get(marketTypeStr).get(marketIndex);
|
|
988
1139
|
|
|
@@ -991,7 +1142,7 @@ export class DLOB {
|
|
|
991
1142
|
}
|
|
992
1143
|
|
|
993
1144
|
const generatorList = [
|
|
994
|
-
nodeLists.
|
|
1145
|
+
nodeLists.restingLimit.bid.getGenerator(),
|
|
995
1146
|
nodeLists.floatingLimit.bid.getGenerator(),
|
|
996
1147
|
];
|
|
997
1148
|
|
|
@@ -999,8 +1150,10 @@ export class DLOB {
|
|
|
999
1150
|
generatorList,
|
|
1000
1151
|
oraclePriceData,
|
|
1001
1152
|
slot,
|
|
1002
|
-
(
|
|
1003
|
-
return
|
|
1153
|
+
(bestNode, currentNode, slot, oraclePriceData) => {
|
|
1154
|
+
return bestNode
|
|
1155
|
+
.getPrice(oraclePriceData, slot)
|
|
1156
|
+
.gt(currentNode.getPrice(oraclePriceData, slot));
|
|
1004
1157
|
}
|
|
1005
1158
|
);
|
|
1006
1159
|
}
|
|
@@ -1018,20 +1171,19 @@ export class DLOB {
|
|
|
1018
1171
|
oraclePriceData: OraclePriceData,
|
|
1019
1172
|
fallbackAsk?: BN
|
|
1020
1173
|
): Generator<DLOBNode> {
|
|
1021
|
-
for (const node of this.
|
|
1174
|
+
for (const node of this.getRestingLimitBids(
|
|
1022
1175
|
marketIndex,
|
|
1023
1176
|
slot,
|
|
1024
1177
|
marketType,
|
|
1025
1178
|
oraclePriceData
|
|
1026
1179
|
)) {
|
|
1027
|
-
if (
|
|
1028
|
-
yield node;
|
|
1029
|
-
} else if (
|
|
1180
|
+
if (
|
|
1030
1181
|
fallbackAsk &&
|
|
1031
|
-
node.getPrice(oraclePriceData, slot).
|
|
1182
|
+
node.getPrice(oraclePriceData, slot).gte(fallbackAsk)
|
|
1032
1183
|
) {
|
|
1033
|
-
|
|
1184
|
+
continue;
|
|
1034
1185
|
}
|
|
1186
|
+
yield node;
|
|
1035
1187
|
}
|
|
1036
1188
|
}
|
|
1037
1189
|
|
|
@@ -1047,8 +1199,8 @@ export class DLOB {
|
|
|
1047
1199
|
}
|
|
1048
1200
|
|
|
1049
1201
|
const generatorList = [
|
|
1050
|
-
this.
|
|
1051
|
-
this.
|
|
1202
|
+
this.getTakingAsks(marketIndex, marketType, slot, oraclePriceData),
|
|
1203
|
+
this.getRestingLimitAsks(marketIndex, slot, marketType, oraclePriceData),
|
|
1052
1204
|
];
|
|
1053
1205
|
|
|
1054
1206
|
const marketTypeStr = getVariant(marketType) as MarketTypeStr;
|
|
@@ -1060,8 +1212,29 @@ export class DLOB {
|
|
|
1060
1212
|
generatorList,
|
|
1061
1213
|
oraclePriceData,
|
|
1062
1214
|
slot,
|
|
1063
|
-
(
|
|
1064
|
-
|
|
1215
|
+
(bestNode, currentNode, slot, oraclePriceData) => {
|
|
1216
|
+
const bestNodeTaking = bestNode.order
|
|
1217
|
+
? isTakingOrder(bestNode.order, slot)
|
|
1218
|
+
: false;
|
|
1219
|
+
const currentNodeTaking = currentNode.order
|
|
1220
|
+
? isTakingOrder(currentNode.order, slot)
|
|
1221
|
+
: false;
|
|
1222
|
+
|
|
1223
|
+
if (bestNodeTaking && currentNodeTaking) {
|
|
1224
|
+
return bestNode.order.slot.lt(currentNode.order.slot);
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
if (bestNodeTaking) {
|
|
1228
|
+
return true;
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
if (currentNodeTaking) {
|
|
1232
|
+
return false;
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
return bestNode
|
|
1236
|
+
.getPrice(oraclePriceData, slot)
|
|
1237
|
+
.lt(currentNode.getPrice(oraclePriceData, slot));
|
|
1065
1238
|
}
|
|
1066
1239
|
);
|
|
1067
1240
|
}
|
|
@@ -1078,8 +1251,8 @@ export class DLOB {
|
|
|
1078
1251
|
}
|
|
1079
1252
|
|
|
1080
1253
|
const generatorList = [
|
|
1081
|
-
this.
|
|
1082
|
-
this.
|
|
1254
|
+
this.getTakingBids(marketIndex, marketType, slot, oraclePriceData),
|
|
1255
|
+
this.getRestingLimitBids(marketIndex, slot, marketType, oraclePriceData),
|
|
1083
1256
|
];
|
|
1084
1257
|
|
|
1085
1258
|
const marketTypeStr = getVariant(marketType) as MarketTypeStr;
|
|
@@ -1091,29 +1264,51 @@ export class DLOB {
|
|
|
1091
1264
|
generatorList,
|
|
1092
1265
|
oraclePriceData,
|
|
1093
1266
|
slot,
|
|
1094
|
-
(
|
|
1095
|
-
|
|
1267
|
+
(bestNode, currentNode, slot, oraclePriceData) => {
|
|
1268
|
+
const bestNodeTaking = bestNode.order
|
|
1269
|
+
? isTakingOrder(bestNode.order, slot)
|
|
1270
|
+
: false;
|
|
1271
|
+
const currentNodeTaking = currentNode.order
|
|
1272
|
+
? isTakingOrder(currentNode.order, slot)
|
|
1273
|
+
: false;
|
|
1274
|
+
|
|
1275
|
+
if (bestNodeTaking && currentNodeTaking) {
|
|
1276
|
+
return bestNode.order.slot.lt(currentNode.order.slot);
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
if (bestNodeTaking) {
|
|
1280
|
+
return true;
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
if (currentNodeTaking) {
|
|
1284
|
+
return false;
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
return bestNode
|
|
1288
|
+
.getPrice(oraclePriceData, slot)
|
|
1289
|
+
.gt(currentNode.getPrice(oraclePriceData, slot));
|
|
1096
1290
|
}
|
|
1097
1291
|
);
|
|
1098
1292
|
}
|
|
1099
1293
|
|
|
1100
|
-
|
|
1294
|
+
findCrossingRestingLimitOrders(
|
|
1101
1295
|
marketIndex: number,
|
|
1102
1296
|
slot: number,
|
|
1103
1297
|
marketType: MarketType,
|
|
1104
1298
|
oraclePriceData: OraclePriceData,
|
|
1299
|
+
minAuctionDuration: number,
|
|
1105
1300
|
fallbackAsk: BN | undefined,
|
|
1106
1301
|
fallbackBid: BN | undefined
|
|
1107
1302
|
): NodeToFill[] {
|
|
1108
1303
|
const nodesToFill = new Array<NodeToFill>();
|
|
1109
1304
|
|
|
1110
|
-
for (const askNode of this.
|
|
1305
|
+
for (const askNode of this.getRestingLimitAsks(
|
|
1111
1306
|
marketIndex,
|
|
1112
1307
|
slot,
|
|
1113
1308
|
marketType,
|
|
1114
1309
|
oraclePriceData
|
|
1115
1310
|
)) {
|
|
1116
|
-
for (const bidNode of this.
|
|
1311
|
+
for (const bidNode of this.getRestingLimitBids(
|
|
1117
1312
|
marketIndex,
|
|
1118
1313
|
slot,
|
|
1119
1314
|
marketType,
|
|
@@ -1142,7 +1337,13 @@ export class DLOB {
|
|
|
1142
1337
|
);
|
|
1143
1338
|
|
|
1144
1339
|
// extra guard against bad fills for limit orders where auction is incomplete
|
|
1145
|
-
if (
|
|
1340
|
+
if (
|
|
1341
|
+
!isFallbackAvailableLiquiditySource(
|
|
1342
|
+
takerNode.order,
|
|
1343
|
+
minAuctionDuration,
|
|
1344
|
+
slot
|
|
1345
|
+
)
|
|
1346
|
+
) {
|
|
1146
1347
|
let bidPrice: BN;
|
|
1147
1348
|
let askPrice: BN;
|
|
1148
1349
|
if (isVariant(takerNode.order.direction, 'long')) {
|
|
@@ -1176,7 +1377,7 @@ export class DLOB {
|
|
|
1176
1377
|
const newBidOrder = { ...bidOrder };
|
|
1177
1378
|
newBidOrder.baseAssetAmountFilled =
|
|
1178
1379
|
bidOrder.baseAssetAmountFilled.add(baseFilled);
|
|
1179
|
-
this.getListForOrder(newBidOrder).update(
|
|
1380
|
+
this.getListForOrder(newBidOrder, slot).update(
|
|
1180
1381
|
newBidOrder,
|
|
1181
1382
|
bidNode.userAccount
|
|
1182
1383
|
);
|
|
@@ -1185,7 +1386,7 @@ export class DLOB {
|
|
|
1185
1386
|
const newAskOrder = { ...askOrder };
|
|
1186
1387
|
newAskOrder.baseAssetAmountFilled =
|
|
1187
1388
|
askOrder.baseAssetAmountFilled.add(baseFilled);
|
|
1188
|
-
this.getListForOrder(newAskOrder).update(
|
|
1389
|
+
this.getListForOrder(newAskOrder, slot).update(
|
|
1189
1390
|
newAskOrder,
|
|
1190
1391
|
askNode.userAccount
|
|
1191
1392
|
);
|
|
@@ -1288,11 +1489,9 @@ export class DLOB {
|
|
|
1288
1489
|
if (triggerAboveList) {
|
|
1289
1490
|
for (const node of triggerAboveList.getGenerator()) {
|
|
1290
1491
|
if (oraclePrice.gt(node.order.triggerPrice)) {
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
});
|
|
1295
|
-
}
|
|
1492
|
+
nodesToTrigger.push({
|
|
1493
|
+
node: node,
|
|
1494
|
+
});
|
|
1296
1495
|
} else {
|
|
1297
1496
|
break;
|
|
1298
1497
|
}
|
|
@@ -1305,11 +1504,9 @@ export class DLOB {
|
|
|
1305
1504
|
if (triggerBelowList) {
|
|
1306
1505
|
for (const node of triggerBelowList.getGenerator()) {
|
|
1307
1506
|
if (oraclePrice.lt(node.order.triggerPrice)) {
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
});
|
|
1312
|
-
}
|
|
1507
|
+
nodesToTrigger.push({
|
|
1508
|
+
node: node,
|
|
1509
|
+
});
|
|
1313
1510
|
} else {
|
|
1314
1511
|
break;
|
|
1315
1512
|
}
|
|
@@ -1438,8 +1635,10 @@ export class DLOB {
|
|
|
1438
1635
|
|
|
1439
1636
|
*getNodeLists(): Generator<NodeList<DLOBNodeType>> {
|
|
1440
1637
|
for (const [_, nodeLists] of this.orderLists.get('perp')) {
|
|
1441
|
-
yield nodeLists.
|
|
1442
|
-
yield nodeLists.
|
|
1638
|
+
yield nodeLists.restingLimit.bid;
|
|
1639
|
+
yield nodeLists.restingLimit.ask;
|
|
1640
|
+
yield nodeLists.takingLimit.bid;
|
|
1641
|
+
yield nodeLists.takingLimit.ask;
|
|
1443
1642
|
yield nodeLists.market.bid;
|
|
1444
1643
|
yield nodeLists.market.ask;
|
|
1445
1644
|
yield nodeLists.floatingLimit.bid;
|
|
@@ -1449,8 +1648,10 @@ export class DLOB {
|
|
|
1449
1648
|
}
|
|
1450
1649
|
|
|
1451
1650
|
for (const [_, nodeLists] of this.orderLists.get('spot')) {
|
|
1452
|
-
yield nodeLists.
|
|
1453
|
-
yield nodeLists.
|
|
1651
|
+
yield nodeLists.restingLimit.bid;
|
|
1652
|
+
yield nodeLists.restingLimit.ask;
|
|
1653
|
+
yield nodeLists.takingLimit.bid;
|
|
1654
|
+
yield nodeLists.takingLimit.ask;
|
|
1454
1655
|
yield nodeLists.market.bid;
|
|
1455
1656
|
yield nodeLists.market.ask;
|
|
1456
1657
|
yield nodeLists.floatingLimit.bid;
|