@drift-labs/sdk 2.8.0-beta.2 → 2.9.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.
@@ -67,7 +67,7 @@ export declare class DLOB {
67
67
  getLimitBids(marketIndex: number, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData): Generator<DLOBNode>;
68
68
  getAsks(marketIndex: number, fallbackAsk: BN | undefined, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData): Generator<DLOBNode>;
69
69
  getBids(marketIndex: number, fallbackBid: BN | undefined, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData): Generator<DLOBNode>;
70
- findCrossingLimitOrders(marketIndex: number, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData): NodeToFill[];
70
+ findCrossingLimitOrders(marketIndex: number, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData, fallbackAsk: BN | undefined, fallbackBid: BN | undefined): NodeToFill[];
71
71
  determineMakerAndTaker(askNode: DLOBNode, bidNode: DLOBNode): {
72
72
  takerNode: DLOBNode;
73
73
  makerNode: DLOBNode;
package/lib/dlob/DLOB.js CHANGED
@@ -261,7 +261,7 @@ class DLOB {
261
261
  }
262
262
  findLimitOrderNodesToFill(marketIndex, slot, marketType, oraclePriceData, isAmmPaused, fallbackAsk, fallbackBid) {
263
263
  const nodesToFill = new Array();
264
- const crossingNodes = this.findCrossingLimitOrders(marketIndex, slot, marketType, oraclePriceData);
264
+ const crossingNodes = this.findCrossingLimitOrders(marketIndex, slot, marketType, oraclePriceData, fallbackAsk, fallbackBid);
265
265
  for (const crossingNode of crossingNodes) {
266
266
  nodesToFill.push(crossingNode);
267
267
  }
@@ -584,7 +584,7 @@ class DLOB {
584
584
  return bestPrice.gt(currentPrice);
585
585
  });
586
586
  }
587
- findCrossingLimitOrders(marketIndex, slot, marketType, oraclePriceData) {
587
+ findCrossingLimitOrders(marketIndex, slot, marketType, oraclePriceData, fallbackAsk, fallbackBid) {
588
588
  const nodesToFill = new Array();
589
589
  for (const askNode of this.getLimitAsks(marketIndex, slot, marketType, oraclePriceData)) {
590
590
  for (const bidNode of this.getLimitBids(marketIndex, slot, marketType, oraclePriceData)) {
@@ -602,6 +602,22 @@ class DLOB {
602
602
  continue;
603
603
  }
604
604
  const { takerNode, makerNode } = this.determineMakerAndTaker(askNode, bidNode);
605
+ // extra guard against bad fills for limit orders where auction is incomplete
606
+ if (!(0, __1.isAuctionComplete)(takerNode.order, slot)) {
607
+ let bidPrice;
608
+ let askPrice;
609
+ if ((0, __1.isVariant)(takerNode.order.direction, 'long')) {
610
+ bidPrice = __1.BN.min(takerNode.getPrice(oraclePriceData, slot), fallbackAsk || __1.BN_MAX);
611
+ askPrice = makerNode.getPrice(oraclePriceData, slot);
612
+ }
613
+ else {
614
+ bidPrice = makerNode.getPrice(oraclePriceData, slot);
615
+ askPrice = __1.BN.max(takerNode.getPrice(oraclePriceData, slot), fallbackBid || __1.ZERO);
616
+ }
617
+ if (bidPrice.lt(askPrice)) {
618
+ continue;
619
+ }
620
+ }
605
621
  const bidBaseRemaining = bidOrder.baseAssetAmount.sub(bidOrder.baseAssetAmountFilled);
606
622
  const askBaseRemaining = askOrder.baseAssetAmount.sub(askOrder.baseAssetAmountFilled);
607
623
  const baseFilled = __1.BN.min(bidBaseRemaining, askBaseRemaining);
@@ -194,6 +194,8 @@ export declare class DriftClient {
194
194
  addSerumRemainingAccounts(marketIndex: number, remainingAccounts: AccountMeta[], fulfillmentConfig: SerumV3FulfillmentConfigAccount): void;
195
195
  triggerOrder(userAccountPublicKey: PublicKey, user: UserAccount, order: Order): Promise<TransactionSignature>;
196
196
  getTriggerOrderIx(userAccountPublicKey: PublicKey, userAccount: UserAccount, order: Order): Promise<TransactionInstruction>;
197
+ forceCancelOrders(userAccountPublicKey: PublicKey, user: UserAccount): Promise<TransactionSignature>;
198
+ getForceCancelOrdersIx(userAccountPublicKey: PublicKey, userAccount: UserAccount): Promise<TransactionInstruction>;
197
199
  placeAndTakePerpOrder(orderParams: OptionalOrderParams, makerInfo?: MakerInfo, referrerInfo?: ReferrerInfo): Promise<TransactionSignature>;
198
200
  getPlaceAndTakePerpOrderIx(orderParams: OptionalOrderParams, makerInfo?: MakerInfo, referrerInfo?: ReferrerInfo): Promise<TransactionInstruction>;
199
201
  placeAndMakePerpOrder(orderParams: OptionalOrderParams, takerInfo: TakerInfo, referrerInfo?: ReferrerInfo): Promise<TransactionSignature>;
@@ -1484,6 +1484,26 @@ class DriftClient {
1484
1484
  remainingAccounts,
1485
1485
  });
1486
1486
  }
1487
+ async forceCancelOrders(userAccountPublicKey, user) {
1488
+ const { txSig } = await this.txSender.send((0, utils_1.wrapInTx)(await this.getForceCancelOrdersIx(userAccountPublicKey, user)), [], this.opts);
1489
+ return txSig;
1490
+ }
1491
+ async getForceCancelOrdersIx(userAccountPublicKey, userAccount) {
1492
+ const fillerPublicKey = await this.getUserAccountPublicKey();
1493
+ const remainingAccounts = this.getRemainingAccounts({
1494
+ userAccounts: [userAccount],
1495
+ writableSpotMarketIndexes: [numericConstants_1.QUOTE_SPOT_MARKET_INDEX],
1496
+ });
1497
+ return await this.program.instruction.forceCancelOrders({
1498
+ accounts: {
1499
+ state: await this.getStatePublicKey(),
1500
+ filler: fillerPublicKey,
1501
+ user: userAccountPublicKey,
1502
+ authority: this.wallet.publicKey,
1503
+ },
1504
+ remainingAccounts,
1505
+ });
1506
+ }
1487
1507
  async placeAndTakePerpOrder(orderParams, makerInfo, referrerInfo) {
1488
1508
  const { txSig, slot } = await this.txSender.send((0, utils_1.wrapInTx)(await this.getPlaceAndTakePerpOrderIx(orderParams, makerInfo, referrerInfo)), [], this.opts);
1489
1509
  this.perpMarketLastSlotCache.set(orderParams.marketIndex, slot);
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.8.0-beta.2",
2
+ "version": "2.9.0-beta.0",
3
3
  "name": "drift",
4
4
  "instructions": [
5
5
  {
@@ -946,6 +946,32 @@
946
946
  }
947
947
  ]
948
948
  },
949
+ {
950
+ "name": "forceCancelOrders",
951
+ "accounts": [
952
+ {
953
+ "name": "state",
954
+ "isMut": false,
955
+ "isSigner": false
956
+ },
957
+ {
958
+ "name": "authority",
959
+ "isMut": false,
960
+ "isSigner": true
961
+ },
962
+ {
963
+ "name": "filler",
964
+ "isMut": true,
965
+ "isSigner": false
966
+ },
967
+ {
968
+ "name": "user",
969
+ "isMut": true,
970
+ "isSigner": false
971
+ }
972
+ ],
973
+ "args": []
974
+ },
949
975
  {
950
976
  "name": "settlePnl",
951
977
  "accounts": [
@@ -22,10 +22,10 @@ class RetryTxSender {
22
22
  if (opts === undefined) {
23
23
  opts = this.provider.opts;
24
24
  }
25
- if (!preSigned) {
26
- await this.prepareTx(tx, additionalSigners, opts);
27
- }
28
- const rawTransaction = tx.serialize();
25
+ const signedTx = preSigned
26
+ ? tx
27
+ : await this.prepareTx(tx, additionalSigners, opts);
28
+ const rawTransaction = signedTx.serialize();
29
29
  const startTime = this.getTimestamp();
30
30
  let txid;
31
31
  try {
@@ -77,13 +77,13 @@ class RetryTxSender {
77
77
  async prepareTx(tx, additionalSigners, opts) {
78
78
  tx.feePayer = this.provider.wallet.publicKey;
79
79
  tx.recentBlockhash = (await this.provider.connection.getRecentBlockhash(opts.preflightCommitment)).blockhash;
80
- await this.provider.wallet.signTransaction(tx);
80
+ const signedTx = await this.provider.wallet.signTransaction(tx);
81
81
  additionalSigners
82
82
  .filter((s) => s !== undefined)
83
83
  .forEach((kp) => {
84
- tx.partialSign(kp);
84
+ signedTx.partialSign(kp);
85
85
  });
86
- return tx;
86
+ return signedTx;
87
87
  }
88
88
  async confirmTransaction(signature, commitment) {
89
89
  let decodedSignature;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drift-labs/sdk",
3
- "version": "2.8.0-beta.2",
3
+ "version": "2.9.0-beta.0",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "author": "crispheaney",
package/src/dlob/DLOB.ts CHANGED
@@ -26,6 +26,8 @@ import {
26
26
  UserMap,
27
27
  OrderRecord,
28
28
  OrderActionRecord,
29
+ ZERO,
30
+ BN_MAX,
29
31
  } from '..';
30
32
  import { PublicKey } from '@solana/web3.js';
31
33
  import { DLOBNode, DLOBNodeType, TriggerOrderNode } from '..';
@@ -440,7 +442,9 @@ export class DLOB {
440
442
  marketIndex,
441
443
  slot,
442
444
  marketType,
443
- oraclePriceData
445
+ oraclePriceData,
446
+ fallbackAsk,
447
+ fallbackBid
444
448
  );
445
449
 
446
450
  for (const crossingNode of crossingNodes) {
@@ -1009,7 +1013,9 @@ export class DLOB {
1009
1013
  marketIndex: number,
1010
1014
  slot: number,
1011
1015
  marketType: MarketType,
1012
- oraclePriceData: OraclePriceData
1016
+ oraclePriceData: OraclePriceData,
1017
+ fallbackAsk: BN | undefined,
1018
+ fallbackBid: BN | undefined
1013
1019
  ): NodeToFill[] {
1014
1020
  const nodesToFill = new Array<NodeToFill>();
1015
1021
 
@@ -1047,6 +1053,29 @@ export class DLOB {
1047
1053
  bidNode
1048
1054
  );
1049
1055
 
1056
+ // extra guard against bad fills for limit orders where auction is incomplete
1057
+ if (!isAuctionComplete(takerNode.order, slot)) {
1058
+ let bidPrice: BN;
1059
+ let askPrice: BN;
1060
+ if (isVariant(takerNode.order.direction, 'long')) {
1061
+ bidPrice = BN.min(
1062
+ takerNode.getPrice(oraclePriceData, slot),
1063
+ fallbackAsk || BN_MAX
1064
+ );
1065
+ askPrice = makerNode.getPrice(oraclePriceData, slot);
1066
+ } else {
1067
+ bidPrice = makerNode.getPrice(oraclePriceData, slot);
1068
+ askPrice = BN.max(
1069
+ takerNode.getPrice(oraclePriceData, slot),
1070
+ fallbackBid || ZERO
1071
+ );
1072
+ }
1073
+
1074
+ if (bidPrice.lt(askPrice)) {
1075
+ continue;
1076
+ }
1077
+ }
1078
+
1050
1079
  const bidBaseRemaining = bidOrder.baseAssetAmount.sub(
1051
1080
  bidOrder.baseAssetAmountFilled
1052
1081
  );
@@ -2470,6 +2470,40 @@ export class DriftClient {
2470
2470
  });
2471
2471
  }
2472
2472
 
2473
+ public async forceCancelOrders(
2474
+ userAccountPublicKey: PublicKey,
2475
+ user: UserAccount
2476
+ ): Promise<TransactionSignature> {
2477
+ const { txSig } = await this.txSender.send(
2478
+ wrapInTx(await this.getForceCancelOrdersIx(userAccountPublicKey, user)),
2479
+ [],
2480
+ this.opts
2481
+ );
2482
+ return txSig;
2483
+ }
2484
+
2485
+ public async getForceCancelOrdersIx(
2486
+ userAccountPublicKey: PublicKey,
2487
+ userAccount: UserAccount
2488
+ ): Promise<TransactionInstruction> {
2489
+ const fillerPublicKey = await this.getUserAccountPublicKey();
2490
+
2491
+ const remainingAccounts = this.getRemainingAccounts({
2492
+ userAccounts: [userAccount],
2493
+ writableSpotMarketIndexes: [QUOTE_SPOT_MARKET_INDEX],
2494
+ });
2495
+
2496
+ return await this.program.instruction.forceCancelOrders({
2497
+ accounts: {
2498
+ state: await this.getStatePublicKey(),
2499
+ filler: fillerPublicKey,
2500
+ user: userAccountPublicKey,
2501
+ authority: this.wallet.publicKey,
2502
+ },
2503
+ remainingAccounts,
2504
+ });
2505
+ }
2506
+
2473
2507
  public async placeAndTakePerpOrder(
2474
2508
  orderParams: OptionalOrderParams,
2475
2509
  makerInfo?: MakerInfo,
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.8.0-beta.2",
2
+ "version": "2.9.0-beta.0",
3
3
  "name": "drift",
4
4
  "instructions": [
5
5
  {
@@ -946,6 +946,32 @@
946
946
  }
947
947
  ]
948
948
  },
949
+ {
950
+ "name": "forceCancelOrders",
951
+ "accounts": [
952
+ {
953
+ "name": "state",
954
+ "isMut": false,
955
+ "isSigner": false
956
+ },
957
+ {
958
+ "name": "authority",
959
+ "isMut": false,
960
+ "isSigner": true
961
+ },
962
+ {
963
+ "name": "filler",
964
+ "isMut": true,
965
+ "isSigner": false
966
+ },
967
+ {
968
+ "name": "user",
969
+ "isMut": true,
970
+ "isSigner": false
971
+ }
972
+ ],
973
+ "args": []
974
+ },
949
975
  {
950
976
  "name": "settlePnl",
951
977
  "accounts": [
@@ -52,11 +52,11 @@ export class RetryTxSender implements TxSender {
52
52
  opts = this.provider.opts;
53
53
  }
54
54
 
55
- if (!preSigned) {
56
- await this.prepareTx(tx, additionalSigners, opts);
57
- }
55
+ const signedTx = preSigned
56
+ ? tx
57
+ : await this.prepareTx(tx, additionalSigners, opts);
58
58
 
59
- const rawTransaction = tx.serialize();
59
+ const rawTransaction = signedTx.serialize();
60
60
  const startTime = this.getTimestamp();
61
61
 
62
62
  let txid: TransactionSignature;
@@ -123,14 +123,14 @@ export class RetryTxSender implements TxSender {
123
123
  )
124
124
  ).blockhash;
125
125
 
126
- await this.provider.wallet.signTransaction(tx);
126
+ const signedTx = await this.provider.wallet.signTransaction(tx);
127
127
  additionalSigners
128
128
  .filter((s): s is Signer => s !== undefined)
129
129
  .forEach((kp) => {
130
- tx.partialSign(kp);
130
+ signedTx.partialSign(kp);
131
131
  });
132
132
 
133
- return tx;
133
+ return signedTx;
134
134
  }
135
135
 
136
136
  async confirmTransaction(
@@ -369,14 +369,16 @@ describe('DLOB Tests', () => {
369
369
  expect(b).to.equal(3);
370
370
 
371
371
  dlob.clear();
372
- let thrown = false;
373
- try {
374
- const bids1 = dlob.getBids(marketIndex, vBid, 0, MarketType.PERP, oracle);
375
- bids1.next();
376
- } catch (e) {
377
- thrown = true;
378
- }
379
- expect(thrown, 'should throw after clearing').to.equal(true);
372
+
373
+ const bids1 = dlob.getBids(
374
+ marketIndex,
375
+ undefined,
376
+ 0,
377
+ MarketType.PERP,
378
+ oracle
379
+ );
380
+ bids1.next();
381
+ expect(bids1.next().done, 'bid generator should be done').to.equal(true);
380
382
  });
381
383
 
382
384
  it('DLOB orders', () => {