@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/README.md +3 -3
- package/lib/accounts/pollingDriftClientAccountSubscriber.js +0 -3
- package/lib/accounts/pollingUserStatsAccountSubscriber.js +0 -3
- package/lib/adminClient.d.ts +2 -0
- package/lib/adminClient.js +16 -0
- package/lib/constants/numericConstants.d.ts +2 -0
- package/lib/constants/numericConstants.js +3 -1
- package/lib/dlob/DLOB.d.ts +8 -8
- package/lib/dlob/DLOB.js +227 -246
- package/lib/driftClient.d.ts +19 -0
- package/lib/driftClient.js +32 -0
- package/lib/idl/drift.json +65 -7
- package/lib/math/amm.d.ts +3 -2
- package/lib/math/amm.js +61 -27
- package/lib/math/oracles.d.ts +2 -0
- package/lib/math/oracles.js +23 -1
- package/lib/math/orders.d.ts +1 -2
- package/lib/math/orders.js +4 -20
- package/lib/math/utils.d.ts +1 -0
- package/lib/math/utils.js +5 -1
- package/lib/types.d.ts +6 -2
- package/lib/user.js +10 -6
- package/package.json +1 -1
- package/src/accounts/pollingDriftClientAccountSubscriber.ts +0 -4
- package/src/accounts/pollingUserStatsAccountSubscriber.ts +0 -4
- package/src/adminClient.ts +28 -0
- package/src/constants/numericConstants.ts +3 -0
- package/src/dlob/DLOB.ts +444 -345
- package/src/driftClient.ts +41 -0
- package/src/idl/drift.json +65 -7
- package/src/math/amm.ts +126 -42
- package/src/math/oracles.ts +52 -0
- package/src/math/orders.ts +5 -22
- package/src/math/utils.ts +4 -0
- package/src/types.ts +6 -2
- package/src/user.ts +17 -6
- package/tests/dlob/helpers.ts +4 -2
- package/tests/dlob/test.ts +547 -63
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
|
-
|
|
184
|
-
const
|
|
185
|
-
|
|
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
|
|
188
|
+
return marketOrderNodesToFill.concat(limitOrderNodesToFill, expiredNodesToFill);
|
|
192
189
|
}
|
|
193
|
-
|
|
190
|
+
findLimitOrderNodesToFill(marketIndex, slot, marketType, oraclePriceData, isAmmPaused, fallbackAsk, fallbackBid) {
|
|
194
191
|
const nodesToFill = new Array();
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
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
|
-
|
|
215
|
-
|
|
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
|
-
|
|
218
|
-
|
|
219
|
-
|
|
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
|
-
|
|
222
|
-
|
|
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
|
-
|
|
228
|
-
var _a, _b;
|
|
252
|
+
findMarketNodesCrossingLimitNodes(marketIndex, slot, marketType, oraclePriceData, takerNodeGenerator, makerNodeGeneratorFn, doesCross) {
|
|
229
253
|
const nodesToFill = new Array();
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
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:
|
|
251
|
-
makerNode:
|
|
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
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
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
|
|
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 =
|
|
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)(
|
|
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:
|
|
313
|
+
node: node,
|
|
271
314
|
makerNode: undefined, // filled by fallback
|
|
272
315
|
});
|
|
273
316
|
}
|
|
274
|
-
|
|
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
|
-
|
|
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
|
-
|
|
390
|
+
const generator = this.orderLists
|
|
342
391
|
.get(marketTypeStr)
|
|
343
392
|
.get(marketIndex)
|
|
344
393
|
.market.ask.getGenerator();
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
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
|
-
|
|
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
|
|
370
|
-
while (!
|
|
371
|
-
const bestGenerator =
|
|
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
|
|
379
|
-
const
|
|
380
|
-
return
|
|
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
|
-
|
|
450
|
+
sideExhausted = true;
|
|
403
451
|
}
|
|
404
452
|
}
|
|
405
453
|
}
|
|
406
|
-
*
|
|
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
|
-
|
|
421
|
-
|
|
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
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
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
|
|
439
|
-
const
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
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
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
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
|
}
|
package/lib/driftClient.d.ts
CHANGED
|
@@ -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;
|