@drift-labs/sdk 2.16.0-beta.2 → 2.16.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/config.d.ts CHANGED
@@ -10,6 +10,7 @@ declare type DriftConfig = {
10
10
  V2_ALPHA_TICKET_MINT_ADDRESS: string;
11
11
  PERP_MARKETS: PerpMarketConfig[];
12
12
  SPOT_MARKETS: SpotMarketConfig[];
13
+ MARKET_LOOKUP_TABLE: string;
13
14
  };
14
15
  export declare type DriftEnv = 'devnet' | 'mainnet-beta';
15
16
  export declare const configs: {
package/lib/config.js CHANGED
@@ -13,6 +13,7 @@ exports.configs = {
13
13
  V2_ALPHA_TICKET_MINT_ADDRESS: 'DeEiGWfCMP9psnLGkxGrBBMEAW5Jv8bBGMN8DCtFRCyB',
14
14
  PERP_MARKETS: perpMarkets_1.DevnetPerpMarkets,
15
15
  SPOT_MARKETS: spotMarkets_1.DevnetSpotMarkets,
16
+ MARKET_LOOKUP_TABLE: 'FaMS3U4uBojvGn5FSDEPimddcXsCfwkKsFgMVVnDdxGb',
16
17
  },
17
18
  'mainnet-beta': {
18
19
  ENV: 'mainnet-beta',
@@ -23,6 +24,7 @@ exports.configs = {
23
24
  V2_ALPHA_TICKET_MINT_ADDRESS: 'Cmvhycb6LQvvzaShGw4iDHRLzeSSryioAsU98DSSkMNa',
24
25
  PERP_MARKETS: perpMarkets_1.MainnetPerpMarkets,
25
26
  SPOT_MARKETS: spotMarkets_1.MainnetSpotMarkets,
27
+ MARKET_LOOKUP_TABLE: 'D9cnvzswDikQDf53k4HpQ3KJ9y1Fv3HGGDFYMXnK5T6c',
26
28
  },
27
29
  };
28
30
  let currentConfig = exports.configs.devnet;
package/lib/dlob/DLOB.js CHANGED
@@ -289,6 +289,14 @@ class DLOB {
289
289
  const nodesToFill = new Array();
290
290
  let marketOrderGenerator = this.getMarketAsks(marketIndex, marketType);
291
291
  const marketAsksCrossingBids = this.findMarketNodesCrossingLimitNodes(marketIndex, slot, marketType, oraclePriceData, marketOrderGenerator, this.getMakerLimitBids.bind(this), (takerPrice, makerPrice) => {
292
+ if (__1.isVariant(marketType, 'spot')) {
293
+ if (takerPrice === undefined) {
294
+ return false;
295
+ }
296
+ if (fallbackBid && makerPrice.lt(fallbackBid)) {
297
+ return false;
298
+ }
299
+ }
292
300
  return takerPrice === undefined || takerPrice.lte(makerPrice);
293
301
  }, fallbackAsk);
294
302
  for (const marketAskCrossingBid of marketAsksCrossingBids) {
@@ -304,8 +312,16 @@ class DLOB {
304
312
  }
305
313
  }
306
314
  marketOrderGenerator = this.getMarketBids(marketIndex, marketType);
307
- const marketBidsToFill = this.findMarketNodesCrossingLimitNodes(marketIndex, slot, marketType, oraclePriceData, marketOrderGenerator, this.getMakerLimitAsks.bind(this), (takerPrice, fallbackPrice) => {
308
- return takerPrice === undefined || takerPrice.gte(fallbackPrice);
315
+ const marketBidsToFill = this.findMarketNodesCrossingLimitNodes(marketIndex, slot, marketType, oraclePriceData, marketOrderGenerator, this.getMakerLimitAsks.bind(this), (takerPrice, makerPrice) => {
316
+ if (__1.isVariant(marketType, 'spot')) {
317
+ if (takerPrice === undefined) {
318
+ return false;
319
+ }
320
+ if (fallbackAsk && makerPrice.gt(fallbackAsk)) {
321
+ return false;
322
+ }
323
+ }
324
+ return takerPrice === undefined || takerPrice.gte(makerPrice);
309
325
  }, fallbackBid);
310
326
  for (const marketBidToFill of marketBidsToFill) {
311
327
  nodesToFill.push(marketBidToFill);
@@ -1,9 +1,9 @@
1
1
  /// <reference types="node" />
2
2
  /// <reference types="bn.js" />
3
3
  import { AnchorProvider, BN, Program } from '@project-serum/anchor';
4
- import { StateAccount, IWallet, PositionDirection, UserAccount, PerpMarketAccount, OrderParams, Order, SpotMarketAccount, SpotPosition, MakerInfo, TakerInfo, OptionalOrderParams, ReferrerInfo, MarketType, TxParams, SerumV3FulfillmentConfigAccount } from './types';
4
+ import { StateAccount, IWallet, PositionDirection, UserAccount, PerpMarketAccount, OrderParams, Order, SpotMarketAccount, SpotPosition, MakerInfo, TakerInfo, OptionalOrderParams, OrderType, ReferrerInfo, MarketType, TxParams, SerumV3FulfillmentConfigAccount } from './types';
5
5
  import * as anchor from '@project-serum/anchor';
6
- import { Connection, PublicKey, TransactionSignature, ConfirmOptions, Transaction, TransactionInstruction, AccountMeta, Signer } from '@solana/web3.js';
6
+ import { Connection, PublicKey, TransactionSignature, ConfirmOptions, Transaction, TransactionInstruction, AccountMeta, Signer, AddressLookupTableAccount } from '@solana/web3.js';
7
7
  import { TokenFaucet } from './tokenFaucet';
8
8
  import { EventEmitter } from 'events';
9
9
  import StrictEventEmitter from 'strict-event-emitter-types';
@@ -43,6 +43,7 @@ export declare class DriftClient {
43
43
  perpMarketLastSlotCache: Map<number, number>;
44
44
  spotMarketLastSlotCache: Map<number, number>;
45
45
  authority: PublicKey;
46
+ marketLookupTable: PublicKey;
46
47
  get isSubscribed(): boolean;
47
48
  set isSubscribed(val: boolean);
48
49
  constructor(config: DriftClientConfig);
@@ -82,6 +83,7 @@ export declare class DriftClient {
82
83
  getQuoteSpotMarketAccount(): SpotMarketAccount;
83
84
  getOraclePriceDataAndSlot(oraclePublicKey: PublicKey): DataAndSlot<OraclePriceData> | undefined;
84
85
  getSerumV3FulfillmentConfig(serumMarket: PublicKey): Promise<SerumV3FulfillmentConfigAccount>;
86
+ fetchMarketLookupTableAccount(): Promise<AddressLookupTableAccount>;
85
87
  /**
86
88
  * Update the wallet to use for drift transactions and linked user account
87
89
  * @param newWallet
@@ -212,6 +214,7 @@ export declare class DriftClient {
212
214
  closePosition(marketIndex: number, limitPrice?: BN): Promise<TransactionSignature>;
213
215
  /**
214
216
  * Modifies an open order by closing it and replacing it with a new order.
217
+ * @deprecated use modifyOrder instead
215
218
  * @param orderId: The open order to modify
216
219
  * @param newBaseAmount: The new base amount for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
217
220
  * @param newLimitPice: The new limit price for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
@@ -219,8 +222,29 @@ export declare class DriftClient {
219
222
  * @returns
220
223
  */
221
224
  modifyPerpOrder(orderId: number, newBaseAmount?: BN, newLimitPrice?: BN, newOraclePriceOffset?: number): Promise<TransactionSignature>;
225
+ /**
226
+ * Modifies an open order (spot or perp) by closing it and replacing it with a new order.
227
+ * @param orderId: The open order to modify
228
+ * @param newBaseAmount: The new base amount for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
229
+ * @param newLimitPice: The new limit price for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
230
+ * @param newOraclePriceOffset: The new oracle price offset for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
231
+ * @param newOrderType: Optional - New order type for the order.
232
+ * @param newTriggerPrice: Optional - Thew new trigger price for the order.
233
+ * @param isSpot: Optional - Set to true if the order is a spot order
234
+ * @returns
235
+ */
236
+ modifyOrder({ orderId, newBaseAmount, newLimitPrice, newOraclePriceOffset, newOrderType, newTriggerPrice, isSpot, }: {
237
+ orderId: number;
238
+ newBaseAmount?: BN;
239
+ newLimitPrice?: BN;
240
+ newOraclePriceOffset?: number;
241
+ newOrderType?: OrderType;
242
+ newTriggerPrice?: BN;
243
+ isSpot?: boolean;
244
+ }): Promise<TransactionSignature>;
222
245
  /**
223
246
  * Modifies an open order by closing it and replacing it with a new order.
247
+ * @deprecated use modifyOrderByUserOrderId instead
224
248
  * @param userOrderId: The open order to modify
225
249
  * @param newBaseAmount: The new base amount for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
226
250
  * @param newLimitPice: The new limit price for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
@@ -228,6 +252,26 @@ export declare class DriftClient {
228
252
  * @returns
229
253
  */
230
254
  modifyPerpOrderByUserOrderId(userOrderId: number, newBaseAmount?: BN, newLimitPrice?: BN, newOraclePriceOffset?: number): Promise<TransactionSignature>;
255
+ /**
256
+ * Modifies an open order by closing it and replacing it with a new order.
257
+ * @param userOrderId: The open order to modify
258
+ * @param newBaseAmount: The new base amount for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
259
+ * @param newLimitPice: The new limit price for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
260
+ * @param newOraclePriceOffset: The new oracle price offset for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
261
+ * @param newOrderType: Optional - New order type for the order.
262
+ * @param newTriggerPrice: Optional - Thew new trigger price for the order.
263
+ * @param isSpot: Set to true if the order is a spot order
264
+ * @returns
265
+ */
266
+ modifyOrderByUserOrderId({ userOrderId, newBaseAmount, newLimitPrice, newOraclePriceOffset, newOrderType, newTriggerPrice, isSpot, }: {
267
+ userOrderId: number;
268
+ newBaseAmount?: BN;
269
+ newLimitPrice?: BN;
270
+ newOraclePriceOffset?: number;
271
+ newOrderType?: OrderType;
272
+ newTriggerPrice?: BN;
273
+ isSpot?: boolean;
274
+ }): Promise<TransactionSignature>;
231
275
  settlePNLs(users: {
232
276
  settleeUserAccountPublicKey: PublicKey;
233
277
  settleeUserAccount: UserAccount;
@@ -93,6 +93,10 @@ class DriftClient {
93
93
  : envSpotMarketIndexes;
94
94
  oracleInfos = oracleInfos ? oracleInfos : envOracleInfos;
95
95
  }
96
+ this.marketLookupTable = config.marketLookupTable;
97
+ if (config.env && !this.marketLookupTable) {
98
+ this.marketLookupTable = new web3_js_1.PublicKey(config_1.configs[config.env].MARKET_LOOKUP_TABLE);
99
+ }
96
100
  if (((_e = config.accountSubscription) === null || _e === void 0 ? void 0 : _e.type) === 'polling') {
97
101
  this.accountSubscriber = new pollingDriftClientAccountSubscriber_1.PollingDriftClientAccountSubscriber(this.program, config.accountSubscription.accountLoader, perpMarketIndexes !== null && perpMarketIndexes !== void 0 ? perpMarketIndexes : [], spotMarketIndexes !== null && spotMarketIndexes !== void 0 ? spotMarketIndexes : [], oracleInfos !== null && oracleInfos !== void 0 ? oracleInfos : []);
98
102
  }
@@ -231,6 +235,13 @@ class DriftClient {
231
235
  const address = await pda_1.getSerumFulfillmentConfigPublicKey(this.program.programId, serumMarket);
232
236
  return (await this.program.account.serumV3FulfillmentConfig.fetch(address));
233
237
  }
238
+ async fetchMarketLookupTableAccount() {
239
+ if (!this.marketLookupTable) {
240
+ throw Error('Market lookup table address not set');
241
+ }
242
+ return (await this.connection.getAddressLookupTable(this.marketLookupTable))
243
+ .value;
244
+ }
234
245
  /**
235
246
  * Update the wallet to use for drift transactions and linked user account
236
247
  * @param newWallet
@@ -1750,6 +1761,7 @@ class DriftClient {
1750
1761
  }
1751
1762
  /**
1752
1763
  * Modifies an open order by closing it and replacing it with a new order.
1764
+ * @deprecated use modifyOrder instead
1753
1765
  * @param orderId: The open order to modify
1754
1766
  * @param newBaseAmount: The new base amount for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
1755
1767
  * @param newLimitPice: The new limit price for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
@@ -1796,8 +1808,68 @@ class DriftClient {
1796
1808
  this.perpMarketLastSlotCache.set(newOrderParams.marketIndex, slot);
1797
1809
  return txSig;
1798
1810
  }
1811
+ /**
1812
+ * Modifies an open order (spot or perp) by closing it and replacing it with a new order.
1813
+ * @param orderId: The open order to modify
1814
+ * @param newBaseAmount: The new base amount for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
1815
+ * @param newLimitPice: The new limit price for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
1816
+ * @param newOraclePriceOffset: The new oracle price offset for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
1817
+ * @param newOrderType: Optional - New order type for the order.
1818
+ * @param newTriggerPrice: Optional - Thew new trigger price for the order.
1819
+ * @param isSpot: Optional - Set to true if the order is a spot order
1820
+ * @returns
1821
+ */
1822
+ async modifyOrder({ orderId, newBaseAmount, newLimitPrice, newOraclePriceOffset, newOrderType, newTriggerPrice, isSpot, }) {
1823
+ if (!newBaseAmount && !newLimitPrice && !newOraclePriceOffset) {
1824
+ throw new Error(`Must provide newBaseAmount or newLimitPrice or newOraclePriceOffset to modify order`);
1825
+ }
1826
+ const openOrder = this.getUser().getOrder(orderId);
1827
+ if (!openOrder) {
1828
+ throw new Error(`No open order with id ${orderId.toString()}`);
1829
+ }
1830
+ const cancelOrderIx = await this.getCancelOrderIx(orderId);
1831
+ const newOrderParams = {
1832
+ orderType: newOrderType || openOrder.orderType,
1833
+ marketType: openOrder.marketType,
1834
+ direction: openOrder.direction,
1835
+ baseAssetAmount: newBaseAmount || openOrder.baseAssetAmount,
1836
+ price: newLimitPrice || openOrder.price,
1837
+ marketIndex: openOrder.marketIndex,
1838
+ reduceOnly: openOrder.reduceOnly,
1839
+ postOnly: openOrder.postOnly,
1840
+ immediateOrCancel: openOrder.immediateOrCancel,
1841
+ triggerPrice: newTriggerPrice || openOrder.triggerPrice,
1842
+ triggerCondition: openOrder.triggerCondition,
1843
+ oraclePriceOffset: newOraclePriceOffset || openOrder.oraclePriceOffset,
1844
+ auctionDuration: openOrder.auctionDuration,
1845
+ maxTs: openOrder.maxTs,
1846
+ auctionStartPrice: openOrder.auctionStartPrice,
1847
+ auctionEndPrice: openOrder.auctionEndPrice,
1848
+ userOrderId: openOrder.userOrderId,
1849
+ };
1850
+ const placeOrderIx = isSpot
1851
+ ? await this.getPlaceSpotOrderIx(newOrderParams)
1852
+ : await this.getPlacePerpOrderIx(newOrderParams);
1853
+ const tx = new web3_js_1.Transaction();
1854
+ tx.add(web3_js_1.ComputeBudgetProgram.requestUnits({
1855
+ units: 1000000,
1856
+ additionalFee: 0,
1857
+ }));
1858
+ tx.add(cancelOrderIx);
1859
+ tx.add(placeOrderIx);
1860
+ const { txSig, slot } = await this.sendTransaction(tx, [], this.opts);
1861
+ if (isSpot) {
1862
+ this.spotMarketLastSlotCache.set(newOrderParams.marketIndex, slot);
1863
+ this.spotMarketLastSlotCache.set(numericConstants_1.QUOTE_SPOT_MARKET_INDEX, slot);
1864
+ }
1865
+ else {
1866
+ this.perpMarketLastSlotCache.set(newOrderParams.marketIndex, slot);
1867
+ }
1868
+ return txSig;
1869
+ }
1799
1870
  /**
1800
1871
  * Modifies an open order by closing it and replacing it with a new order.
1872
+ * @deprecated use modifyOrderByUserOrderId instead
1801
1873
  * @param userOrderId: The open order to modify
1802
1874
  * @param newBaseAmount: The new base amount for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
1803
1875
  * @param newLimitPice: The new limit price for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
@@ -1844,6 +1916,65 @@ class DriftClient {
1844
1916
  this.perpMarketLastSlotCache.set(newOrderParams.marketIndex, slot);
1845
1917
  return txSig;
1846
1918
  }
1919
+ /**
1920
+ * Modifies an open order by closing it and replacing it with a new order.
1921
+ * @param userOrderId: The open order to modify
1922
+ * @param newBaseAmount: The new base amount for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
1923
+ * @param newLimitPice: The new limit price for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
1924
+ * @param newOraclePriceOffset: The new oracle price offset for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
1925
+ * @param newOrderType: Optional - New order type for the order.
1926
+ * @param newTriggerPrice: Optional - Thew new trigger price for the order.
1927
+ * @param isSpot: Set to true if the order is a spot order
1928
+ * @returns
1929
+ */
1930
+ async modifyOrderByUserOrderId({ userOrderId, newBaseAmount, newLimitPrice, newOraclePriceOffset, newOrderType, newTriggerPrice, isSpot, }) {
1931
+ if (!newBaseAmount && !newLimitPrice && !newOraclePriceOffset) {
1932
+ throw new Error(`Must provide newBaseAmount or newLimitPrice or newOraclePriceOffset to modify order`);
1933
+ }
1934
+ const openOrder = this.getUser().getOrderByUserOrderId(userOrderId);
1935
+ if (!openOrder) {
1936
+ throw new Error(`No open order with user order id ${userOrderId.toString()}`);
1937
+ }
1938
+ const cancelOrderIx = await this.getCancelOrderByUserIdIx(userOrderId);
1939
+ const newOrderParams = {
1940
+ orderType: newOrderType || openOrder.orderType,
1941
+ marketType: openOrder.marketType,
1942
+ direction: openOrder.direction,
1943
+ baseAssetAmount: newBaseAmount || openOrder.baseAssetAmount,
1944
+ price: newLimitPrice || openOrder.price,
1945
+ marketIndex: openOrder.marketIndex,
1946
+ reduceOnly: openOrder.reduceOnly,
1947
+ postOnly: openOrder.postOnly,
1948
+ immediateOrCancel: openOrder.immediateOrCancel,
1949
+ triggerPrice: newTriggerPrice || openOrder.triggerPrice,
1950
+ triggerCondition: openOrder.triggerCondition,
1951
+ oraclePriceOffset: newOraclePriceOffset || openOrder.oraclePriceOffset,
1952
+ auctionDuration: openOrder.auctionDuration,
1953
+ maxTs: openOrder.maxTs,
1954
+ auctionStartPrice: openOrder.auctionStartPrice,
1955
+ auctionEndPrice: openOrder.auctionEndPrice,
1956
+ userOrderId: openOrder.userOrderId,
1957
+ };
1958
+ const placeOrderIx = isSpot
1959
+ ? await this.getPlaceSpotOrderIx(newOrderParams)
1960
+ : await this.getPlacePerpOrderIx(newOrderParams);
1961
+ const tx = new web3_js_1.Transaction();
1962
+ tx.add(web3_js_1.ComputeBudgetProgram.requestUnits({
1963
+ units: 1000000,
1964
+ additionalFee: 0,
1965
+ }));
1966
+ tx.add(cancelOrderIx);
1967
+ tx.add(placeOrderIx);
1968
+ const { txSig, slot } = await this.sendTransaction(tx, [], this.opts);
1969
+ if (isSpot) {
1970
+ this.spotMarketLastSlotCache.set(newOrderParams.marketIndex, slot);
1971
+ this.spotMarketLastSlotCache.set(numericConstants_1.QUOTE_SPOT_MARKET_INDEX, slot);
1972
+ }
1973
+ else {
1974
+ this.perpMarketLastSlotCache.set(newOrderParams.marketIndex, slot);
1975
+ }
1976
+ return txSig;
1977
+ }
1847
1978
  async settlePNLs(users, marketIndex) {
1848
1979
  const ixs = [];
1849
1980
  for (const { settleeUserAccountPublicKey, settleeUserAccount } of users) {
@@ -14,6 +14,7 @@ export declare type DriftClientConfig = {
14
14
  activeSubAccountId?: number;
15
15
  perpMarketIndexes?: number[];
16
16
  spotMarketIndexes?: number[];
17
+ marketLookupTable?: PublicKey;
17
18
  oracleInfos?: OracleInfo[];
18
19
  env?: DriftEnv;
19
20
  userStats?: boolean;
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.16.0-beta.2",
2
+ "version": "2.16.0",
3
3
  "name": "drift",
4
4
  "instructions": [
5
5
  {
@@ -201,9 +201,11 @@ function calculateWithdrawLimit(spotMarket, now) {
201
201
  .add(marketDepositTokenAmount.mul(sinceLast))
202
202
  .div(sinceLast.add(sinceStart));
203
203
  const maxBorrowTokens = anchor_1.BN.max(spotMarket.withdrawGuardThreshold, anchor_1.BN.min(anchor_1.BN.max(marketDepositTokenAmount.div(new anchor_1.BN(6)), borrowTokenTwapLive.add(borrowTokenTwapLive.div(new anchor_1.BN(5)))), marketDepositTokenAmount.sub(marketDepositTokenAmount.div(new anchor_1.BN(5))))); // between ~15-80% utilization with friction on twap
204
- const minDepositTokens = depositTokenTwapLive.sub(anchor_1.BN.min(anchor_1.BN.max(depositTokenTwapLive.div(new anchor_1.BN(5)), spotMarket.withdrawGuardThreshold), depositTokenTwapLive));
204
+ const minDepositTokens = depositTokenTwapLive.sub(anchor_1.BN.max(depositTokenTwapLive.div(new anchor_1.BN(5)), anchor_1.BN.min(spotMarket.withdrawGuardThreshold, depositTokenTwapLive)));
205
205
  let withdrawLimit = anchor_1.BN.max(marketDepositTokenAmount.sub(minDepositTokens), numericConstants_1.ZERO);
206
- let borrowLimit = anchor_1.BN.max(maxBorrowTokens.sub(marketBorrowTokenAmount), numericConstants_1.ZERO);
206
+ let borrowLimit = maxBorrowTokens.sub(marketBorrowTokenAmount);
207
+ borrowLimit = anchor_1.BN.min(borrowLimit, marketDepositTokenAmount.sub(minDepositTokens));
208
+ borrowLimit = anchor_1.BN.min(borrowLimit, marketDepositTokenAmount.sub(marketBorrowTokenAmount));
207
209
  if (borrowLimit.eq(numericConstants_1.ZERO)) {
208
210
  withdrawLimit = numericConstants_1.ZERO;
209
211
  }
@@ -1,4 +1,4 @@
1
1
  /// <reference types="bn.js" />
2
2
  import { BN } from '../';
3
3
  export declare function clampBN(x: BN, min: BN, max: BN): BN;
4
- export declare const squareRootBN: (n: any, closeness?: BN) => BN;
4
+ export declare const squareRootBN: (n: BN) => BN;
package/lib/math/utils.js CHANGED
@@ -6,28 +6,20 @@ function clampBN(x, min, max) {
6
6
  return __1.BN.max(min, __1.BN.min(x, max));
7
7
  }
8
8
  exports.clampBN = clampBN;
9
- const squareRootBN = (n, closeness = new __1.BN(1)) => {
10
- if (n.lt(__1.ZERO)) {
11
- throw new Error('square root of negative number');
9
+ const squareRootBN = (n) => {
10
+ if (n.lt(new __1.BN(0))) {
11
+ throw new Error('Sqrt only works on non-negtiave inputs');
12
12
  }
13
- // Assuming the sqrt of n as n only
14
- let x = n;
15
- // The closed guess will be stored in the root
16
- let root;
17
- // To count the number of iterations
18
- let count = 0;
19
- const TWO = new __1.BN(2);
20
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
21
- while (count < Number.MAX_SAFE_INTEGER) {
22
- count++;
23
- // Calculate more closed x
24
- root = x.add(n.div(x)).div(TWO);
25
- // Check for closeness
26
- if (x.sub(root).abs().lte(closeness))
27
- break;
28
- // Update root
29
- x = root;
13
+ if (n.lt(new __1.BN(2))) {
14
+ return n;
15
+ }
16
+ const smallCand = exports.squareRootBN(n.shrn(2)).shln(1);
17
+ const largeCand = smallCand.add(new __1.BN(1));
18
+ if (largeCand.mul(largeCand).gt(n)) {
19
+ return smallCand;
20
+ }
21
+ else {
22
+ return largeCand;
30
23
  }
31
- return root;
32
24
  };
33
25
  exports.squareRootBN = squareRootBN;
@@ -1,6 +1,6 @@
1
1
  /// <reference types="node" />
2
2
  import { TxSender, TxSigAndSlot } from './types';
3
- import { Commitment, ConfirmOptions, RpcResponseAndContext, Signer, SignatureResult, Transaction, TransactionSignature, Connection } from '@solana/web3.js';
3
+ import { Commitment, ConfirmOptions, RpcResponseAndContext, Signer, SignatureResult, Transaction, TransactionSignature, Connection, TransactionInstruction, AddressLookupTableAccount } from '@solana/web3.js';
4
4
  import { AnchorProvider } from '@project-serum/anchor';
5
5
  declare type ResolveReference = {
6
6
  resolve?: () => void;
@@ -13,11 +13,13 @@ export declare class RetryTxSender implements TxSender {
13
13
  constructor(provider: AnchorProvider, timeout?: number, retrySleep?: number, additionalConnections?: Connection[]);
14
14
  send(tx: Transaction, additionalSigners?: Array<Signer>, opts?: ConfirmOptions, preSigned?: boolean): Promise<TxSigAndSlot>;
15
15
  prepareTx(tx: Transaction, additionalSigners: Array<Signer>, opts: ConfirmOptions): Promise<Transaction>;
16
+ sendVersionedTransaction(ixs: TransactionInstruction[], lookupTableAccounts: AddressLookupTableAccount[], additionalSigners?: Array<Signer>, opts?: ConfirmOptions): Promise<TxSigAndSlot>;
17
+ sendRawTransaction(rawTransaction: Buffer | Uint8Array, opts: ConfirmOptions): Promise<TxSigAndSlot>;
16
18
  confirmTransaction(signature: TransactionSignature, commitment?: Commitment): Promise<RpcResponseAndContext<SignatureResult>>;
17
19
  getTimestamp(): number;
18
20
  sleep(reference: ResolveReference): Promise<void>;
19
21
  promiseTimeout<T>(promises: Promise<T>[], timeoutMs: number): Promise<T | null>;
20
- sendToAdditionalConnections(rawTx: Buffer, opts: ConfirmOptions): void;
22
+ sendToAdditionalConnections(rawTx: Buffer | Uint8Array, opts: ConfirmOptions): void;
21
23
  addAdditionalConnection(newConnection: Connection): void;
22
24
  }
23
25
  export {};
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.RetryTxSender = void 0;
7
+ const web3_js_1 = require("@solana/web3.js");
7
8
  const assert_1 = __importDefault(require("assert"));
8
9
  const bs58_1 = __importDefault(require("bs58"));
9
10
  const DEFAULT_TIMEOUT = 35000;
@@ -25,7 +26,37 @@ class RetryTxSender {
25
26
  const signedTx = preSigned
26
27
  ? tx
27
28
  : await this.prepareTx(tx, additionalSigners, opts);
28
- const rawTransaction = signedTx.serialize();
29
+ return this.sendRawTransaction(signedTx.serialize(), opts);
30
+ }
31
+ async prepareTx(tx, additionalSigners, opts) {
32
+ tx.feePayer = this.provider.wallet.publicKey;
33
+ tx.recentBlockhash = (await this.provider.connection.getRecentBlockhash(opts.preflightCommitment)).blockhash;
34
+ const signedTx = await this.provider.wallet.signTransaction(tx);
35
+ additionalSigners
36
+ .filter((s) => s !== undefined)
37
+ .forEach((kp) => {
38
+ signedTx.partialSign(kp);
39
+ });
40
+ return signedTx;
41
+ }
42
+ async sendVersionedTransaction(ixs, lookupTableAccounts, additionalSigners, opts) {
43
+ if (additionalSigners === undefined) {
44
+ additionalSigners = [];
45
+ }
46
+ if (opts === undefined) {
47
+ opts = this.provider.opts;
48
+ }
49
+ const message = new web3_js_1.TransactionMessage({
50
+ payerKey: this.provider.wallet.publicKey,
51
+ recentBlockhash: (await this.provider.connection.getRecentBlockhash(opts.preflightCommitment)).blockhash,
52
+ instructions: ixs,
53
+ }).compileToV0Message(lookupTableAccounts);
54
+ const tx = new web3_js_1.VersionedTransaction(message);
55
+ // @ts-ignore
56
+ tx.sign(additionalSigners.concat(this.provider.wallet.payer));
57
+ return this.sendRawTransaction(tx.serialize(), opts);
58
+ }
59
+ async sendRawTransaction(rawTransaction, opts) {
29
60
  const startTime = this.getTimestamp();
30
61
  let txid;
31
62
  try {
@@ -74,17 +105,6 @@ class RetryTxSender {
74
105
  }
75
106
  return { txSig: txid, slot };
76
107
  }
77
- async prepareTx(tx, additionalSigners, opts) {
78
- tx.feePayer = this.provider.wallet.publicKey;
79
- tx.recentBlockhash = (await this.provider.connection.getRecentBlockhash(opts.preflightCommitment)).blockhash;
80
- const signedTx = await this.provider.wallet.signTransaction(tx);
81
- additionalSigners
82
- .filter((s) => s !== undefined)
83
- .forEach((kp) => {
84
- signedTx.partialSign(kp);
85
- });
86
- return signedTx;
87
- }
88
108
  async confirmTransaction(signature, commitment) {
89
109
  let decodedSignature;
90
110
  try {
package/lib/tx/types.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Provider } from '@project-serum/anchor';
2
- import { ConfirmOptions, Signer, Transaction, TransactionSignature } from '@solana/web3.js';
2
+ import { AddressLookupTableAccount, ConfirmOptions, Signer, Transaction, TransactionInstruction, TransactionSignature } from '@solana/web3.js';
3
3
  export declare type TxSigAndSlot = {
4
4
  txSig: TransactionSignature;
5
5
  slot: number;
@@ -7,4 +7,5 @@ export declare type TxSigAndSlot = {
7
7
  export interface TxSender {
8
8
  provider: Provider;
9
9
  send(tx: Transaction, additionalSigners?: Array<Signer>, opts?: ConfirmOptions, preSigned?: boolean): Promise<TxSigAndSlot>;
10
+ sendVersionedTransaction(ixs: TransactionInstruction[], lookupTableAccounts: AddressLookupTableAccount[], additionalSigners?: Array<Signer>, opts?: ConfirmOptions): Promise<TxSigAndSlot>;
10
11
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drift-labs/sdk",
3
- "version": "2.16.0-beta.2",
3
+ "version": "2.16.0",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "author": "crispheaney",
package/src/config.ts CHANGED
@@ -21,6 +21,7 @@ type DriftConfig = {
21
21
  V2_ALPHA_TICKET_MINT_ADDRESS: string;
22
22
  PERP_MARKETS: PerpMarketConfig[];
23
23
  SPOT_MARKETS: SpotMarketConfig[];
24
+ MARKET_LOOKUP_TABLE: string;
24
25
  };
25
26
 
26
27
  export type DriftEnv = 'devnet' | 'mainnet-beta';
@@ -36,6 +37,7 @@ export const configs: { [key in DriftEnv]: DriftConfig } = {
36
37
  'DeEiGWfCMP9psnLGkxGrBBMEAW5Jv8bBGMN8DCtFRCyB',
37
38
  PERP_MARKETS: DevnetPerpMarkets,
38
39
  SPOT_MARKETS: DevnetSpotMarkets,
40
+ MARKET_LOOKUP_TABLE: 'FaMS3U4uBojvGn5FSDEPimddcXsCfwkKsFgMVVnDdxGb',
39
41
  },
40
42
  'mainnet-beta': {
41
43
  ENV: 'mainnet-beta',
@@ -47,6 +49,7 @@ export const configs: { [key in DriftEnv]: DriftConfig } = {
47
49
  'Cmvhycb6LQvvzaShGw4iDHRLzeSSryioAsU98DSSkMNa',
48
50
  PERP_MARKETS: MainnetPerpMarkets,
49
51
  SPOT_MARKETS: MainnetSpotMarkets,
52
+ MARKET_LOOKUP_TABLE: 'D9cnvzswDikQDf53k4HpQ3KJ9y1Fv3HGGDFYMXnK5T6c',
50
53
  },
51
54
  };
52
55
 
package/src/dlob/DLOB.ts CHANGED
@@ -521,6 +521,15 @@ export class DLOB {
521
521
  marketOrderGenerator,
522
522
  this.getMakerLimitBids.bind(this),
523
523
  (takerPrice, makerPrice) => {
524
+ if (isVariant(marketType, 'spot')) {
525
+ if (takerPrice === undefined) {
526
+ return false;
527
+ }
528
+
529
+ if (fallbackBid && makerPrice.lt(fallbackBid)) {
530
+ return false;
531
+ }
532
+ }
524
533
  return takerPrice === undefined || takerPrice.lte(makerPrice);
525
534
  },
526
535
  fallbackAsk
@@ -557,8 +566,18 @@ export class DLOB {
557
566
  oraclePriceData,
558
567
  marketOrderGenerator,
559
568
  this.getMakerLimitAsks.bind(this),
560
- (takerPrice, fallbackPrice) => {
561
- return takerPrice === undefined || takerPrice.gte(fallbackPrice);
569
+ (takerPrice, makerPrice) => {
570
+ if (isVariant(marketType, 'spot')) {
571
+ if (takerPrice === undefined) {
572
+ return false;
573
+ }
574
+
575
+ if (fallbackAsk && makerPrice.gt(fallbackAsk)) {
576
+ return false;
577
+ }
578
+ }
579
+
580
+ return takerPrice === undefined || takerPrice.gte(makerPrice);
562
581
  },
563
582
  fallbackBid
564
583
  );
@@ -42,6 +42,7 @@ import {
42
42
  Signer,
43
43
  SystemProgram,
44
44
  ComputeBudgetProgram,
45
+ AddressLookupTableAccount,
45
46
  } from '@solana/web3.js';
46
47
 
47
48
  import { TokenFaucet } from './tokenFaucet';
@@ -77,7 +78,7 @@ import { WebSocketDriftClientAccountSubscriber } from './accounts/webSocketDrift
77
78
  import { RetryTxSender } from './tx/retryTxSender';
78
79
  import { User } from './user';
79
80
  import { UserSubscriptionConfig } from './userConfig';
80
- import { getMarketsAndOraclesForSubscription } from './config';
81
+ import { configs, getMarketsAndOraclesForSubscription } from './config';
81
82
  import { WRAPPED_SOL_MINT } from './constants/spotMarkets';
82
83
  import { UserStats } from './userStats';
83
84
  import { isSpotPositionAvailable } from './math/spotPosition';
@@ -112,6 +113,7 @@ export class DriftClient {
112
113
  perpMarketLastSlotCache = new Map<number, number>();
113
114
  spotMarketLastSlotCache = new Map<number, number>();
114
115
  authority: PublicKey;
116
+ marketLookupTable: PublicKey;
115
117
 
116
118
  public get isSubscribed() {
117
119
  return this._isSubscribed && this.accountSubscriber.isSubscribed;
@@ -178,6 +180,13 @@ export class DriftClient {
178
180
  oracleInfos = oracleInfos ? oracleInfos : envOracleInfos;
179
181
  }
180
182
 
183
+ this.marketLookupTable = config.marketLookupTable;
184
+ if (config.env && !this.marketLookupTable) {
185
+ this.marketLookupTable = new PublicKey(
186
+ configs[config.env].MARKET_LOOKUP_TABLE
187
+ );
188
+ }
189
+
181
190
  if (config.accountSubscription?.type === 'polling') {
182
191
  this.accountSubscriber = new PollingDriftClientAccountSubscriber(
183
192
  this.program,
@@ -387,6 +396,15 @@ export class DriftClient {
387
396
  )) as SerumV3FulfillmentConfigAccount;
388
397
  }
389
398
 
399
+ public async fetchMarketLookupTableAccount(): Promise<AddressLookupTableAccount> {
400
+ if (!this.marketLookupTable) {
401
+ throw Error('Market lookup table address not set');
402
+ }
403
+
404
+ return (await this.connection.getAddressLookupTable(this.marketLookupTable))
405
+ .value;
406
+ }
407
+
390
408
  /**
391
409
  * Update the wallet to use for drift transactions and linked user account
392
410
  * @param newWallet
@@ -3018,6 +3036,7 @@ export class DriftClient {
3018
3036
 
3019
3037
  /**
3020
3038
  * Modifies an open order by closing it and replacing it with a new order.
3039
+ * @deprecated use modifyOrder instead
3021
3040
  * @param orderId: The open order to modify
3022
3041
  * @param newBaseAmount: The new base amount for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
3023
3042
  * @param newLimitPice: The new limit price for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
@@ -3077,8 +3096,93 @@ export class DriftClient {
3077
3096
  return txSig;
3078
3097
  }
3079
3098
 
3099
+ /**
3100
+ * Modifies an open order (spot or perp) by closing it and replacing it with a new order.
3101
+ * @param orderId: The open order to modify
3102
+ * @param newBaseAmount: The new base amount for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
3103
+ * @param newLimitPice: The new limit price for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
3104
+ * @param newOraclePriceOffset: The new oracle price offset for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
3105
+ * @param newOrderType: Optional - New order type for the order.
3106
+ * @param newTriggerPrice: Optional - Thew new trigger price for the order.
3107
+ * @param isSpot: Optional - Set to true if the order is a spot order
3108
+ * @returns
3109
+ */
3110
+ public async modifyOrder({
3111
+ orderId,
3112
+ newBaseAmount,
3113
+ newLimitPrice,
3114
+ newOraclePriceOffset,
3115
+ newOrderType,
3116
+ newTriggerPrice,
3117
+ isSpot,
3118
+ }: {
3119
+ orderId: number;
3120
+ newBaseAmount?: BN;
3121
+ newLimitPrice?: BN;
3122
+ newOraclePriceOffset?: number;
3123
+ newOrderType?: OrderType;
3124
+ newTriggerPrice?: BN;
3125
+ isSpot?: boolean;
3126
+ }): Promise<TransactionSignature> {
3127
+ if (!newBaseAmount && !newLimitPrice && !newOraclePriceOffset) {
3128
+ throw new Error(
3129
+ `Must provide newBaseAmount or newLimitPrice or newOraclePriceOffset to modify order`
3130
+ );
3131
+ }
3132
+
3133
+ const openOrder = this.getUser().getOrder(orderId);
3134
+ if (!openOrder) {
3135
+ throw new Error(`No open order with id ${orderId.toString()}`);
3136
+ }
3137
+ const cancelOrderIx = await this.getCancelOrderIx(orderId);
3138
+
3139
+ const newOrderParams: OptionalOrderParams = {
3140
+ orderType: newOrderType || openOrder.orderType,
3141
+ marketType: openOrder.marketType,
3142
+ direction: openOrder.direction,
3143
+ baseAssetAmount: newBaseAmount || openOrder.baseAssetAmount,
3144
+ price: newLimitPrice || openOrder.price,
3145
+ marketIndex: openOrder.marketIndex,
3146
+ reduceOnly: openOrder.reduceOnly,
3147
+ postOnly: openOrder.postOnly,
3148
+ immediateOrCancel: openOrder.immediateOrCancel,
3149
+ triggerPrice: newTriggerPrice || openOrder.triggerPrice,
3150
+ triggerCondition: openOrder.triggerCondition,
3151
+ oraclePriceOffset: newOraclePriceOffset || openOrder.oraclePriceOffset,
3152
+ auctionDuration: openOrder.auctionDuration,
3153
+ maxTs: openOrder.maxTs,
3154
+ auctionStartPrice: openOrder.auctionStartPrice,
3155
+ auctionEndPrice: openOrder.auctionEndPrice,
3156
+ userOrderId: openOrder.userOrderId,
3157
+ };
3158
+ const placeOrderIx = isSpot
3159
+ ? await this.getPlaceSpotOrderIx(newOrderParams)
3160
+ : await this.getPlacePerpOrderIx(newOrderParams);
3161
+
3162
+ const tx = new Transaction();
3163
+ tx.add(
3164
+ ComputeBudgetProgram.requestUnits({
3165
+ units: 1_000_000,
3166
+ additionalFee: 0,
3167
+ })
3168
+ );
3169
+ tx.add(cancelOrderIx);
3170
+ tx.add(placeOrderIx);
3171
+ const { txSig, slot } = await this.sendTransaction(tx, [], this.opts);
3172
+
3173
+ if (isSpot) {
3174
+ this.spotMarketLastSlotCache.set(newOrderParams.marketIndex, slot);
3175
+ this.spotMarketLastSlotCache.set(QUOTE_SPOT_MARKET_INDEX, slot);
3176
+ } else {
3177
+ this.perpMarketLastSlotCache.set(newOrderParams.marketIndex, slot);
3178
+ }
3179
+
3180
+ return txSig;
3181
+ }
3182
+
3080
3183
  /**
3081
3184
  * Modifies an open order by closing it and replacing it with a new order.
3185
+ * @deprecated use modifyOrderByUserOrderId instead
3082
3186
  * @param userOrderId: The open order to modify
3083
3187
  * @param newBaseAmount: The new base amount for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
3084
3188
  * @param newLimitPice: The new limit price for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
@@ -3140,6 +3244,92 @@ export class DriftClient {
3140
3244
  return txSig;
3141
3245
  }
3142
3246
 
3247
+ /**
3248
+ * Modifies an open order by closing it and replacing it with a new order.
3249
+ * @param userOrderId: The open order to modify
3250
+ * @param newBaseAmount: The new base amount for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
3251
+ * @param newLimitPice: The new limit price for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
3252
+ * @param newOraclePriceOffset: The new oracle price offset for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
3253
+ * @param newOrderType: Optional - New order type for the order.
3254
+ * @param newTriggerPrice: Optional - Thew new trigger price for the order.
3255
+ * @param isSpot: Set to true if the order is a spot order
3256
+ * @returns
3257
+ */
3258
+ public async modifyOrderByUserOrderId({
3259
+ userOrderId,
3260
+ newBaseAmount,
3261
+ newLimitPrice,
3262
+ newOraclePriceOffset,
3263
+ newOrderType,
3264
+ newTriggerPrice,
3265
+ isSpot,
3266
+ }: {
3267
+ userOrderId: number;
3268
+ newBaseAmount?: BN;
3269
+ newLimitPrice?: BN;
3270
+ newOraclePriceOffset?: number;
3271
+ newOrderType?: OrderType;
3272
+ newTriggerPrice?: BN;
3273
+ isSpot?: boolean;
3274
+ }): Promise<TransactionSignature> {
3275
+ if (!newBaseAmount && !newLimitPrice && !newOraclePriceOffset) {
3276
+ throw new Error(
3277
+ `Must provide newBaseAmount or newLimitPrice or newOraclePriceOffset to modify order`
3278
+ );
3279
+ }
3280
+
3281
+ const openOrder = this.getUser().getOrderByUserOrderId(userOrderId);
3282
+ if (!openOrder) {
3283
+ throw new Error(
3284
+ `No open order with user order id ${userOrderId.toString()}`
3285
+ );
3286
+ }
3287
+ const cancelOrderIx = await this.getCancelOrderByUserIdIx(userOrderId);
3288
+
3289
+ const newOrderParams: OptionalOrderParams = {
3290
+ orderType: newOrderType || openOrder.orderType,
3291
+ marketType: openOrder.marketType,
3292
+ direction: openOrder.direction,
3293
+ baseAssetAmount: newBaseAmount || openOrder.baseAssetAmount,
3294
+ price: newLimitPrice || openOrder.price,
3295
+ marketIndex: openOrder.marketIndex,
3296
+ reduceOnly: openOrder.reduceOnly,
3297
+ postOnly: openOrder.postOnly,
3298
+ immediateOrCancel: openOrder.immediateOrCancel,
3299
+ triggerPrice: newTriggerPrice || openOrder.triggerPrice,
3300
+ triggerCondition: openOrder.triggerCondition,
3301
+ oraclePriceOffset: newOraclePriceOffset || openOrder.oraclePriceOffset,
3302
+ auctionDuration: openOrder.auctionDuration,
3303
+ maxTs: openOrder.maxTs,
3304
+ auctionStartPrice: openOrder.auctionStartPrice,
3305
+ auctionEndPrice: openOrder.auctionEndPrice,
3306
+ userOrderId: openOrder.userOrderId,
3307
+ };
3308
+ const placeOrderIx = isSpot
3309
+ ? await this.getPlaceSpotOrderIx(newOrderParams)
3310
+ : await this.getPlacePerpOrderIx(newOrderParams);
3311
+
3312
+ const tx = new Transaction();
3313
+ tx.add(
3314
+ ComputeBudgetProgram.requestUnits({
3315
+ units: 1_000_000,
3316
+ additionalFee: 0,
3317
+ })
3318
+ );
3319
+ tx.add(cancelOrderIx);
3320
+ tx.add(placeOrderIx);
3321
+ const { txSig, slot } = await this.sendTransaction(tx, [], this.opts);
3322
+
3323
+ if (isSpot) {
3324
+ this.spotMarketLastSlotCache.set(newOrderParams.marketIndex, slot);
3325
+ this.spotMarketLastSlotCache.set(QUOTE_SPOT_MARKET_INDEX, slot);
3326
+ } else {
3327
+ this.perpMarketLastSlotCache.set(newOrderParams.marketIndex, slot);
3328
+ }
3329
+
3330
+ return txSig;
3331
+ }
3332
+
3143
3333
  public async settlePNLs(
3144
3334
  users: {
3145
3335
  settleeUserAccountPublicKey: PublicKey;
@@ -15,6 +15,7 @@ export type DriftClientConfig = {
15
15
  activeSubAccountId?: number;
16
16
  perpMarketIndexes?: number[];
17
17
  spotMarketIndexes?: number[];
18
+ marketLookupTable?: PublicKey;
18
19
  oracleInfos?: OracleInfo[];
19
20
  env?: DriftEnv;
20
21
  userStats?: boolean;
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.16.0-beta.2",
2
+ "version": "2.16.0",
3
3
  "name": "drift",
4
4
  "instructions": [
5
5
  {
@@ -338,12 +338,9 @@ export function calculateWithdrawLimit(
338
338
  ); // between ~15-80% utilization with friction on twap
339
339
 
340
340
  const minDepositTokens = depositTokenTwapLive.sub(
341
- BN.min(
342
- BN.max(
343
- depositTokenTwapLive.div(new BN(5)),
344
- spotMarket.withdrawGuardThreshold
345
- ),
346
- depositTokenTwapLive
341
+ BN.max(
342
+ depositTokenTwapLive.div(new BN(5)),
343
+ BN.min(spotMarket.withdrawGuardThreshold, depositTokenTwapLive)
347
344
  )
348
345
  );
349
346
 
@@ -352,7 +349,15 @@ export function calculateWithdrawLimit(
352
349
  ZERO
353
350
  );
354
351
 
355
- let borrowLimit = BN.max(maxBorrowTokens.sub(marketBorrowTokenAmount), ZERO);
352
+ let borrowLimit = maxBorrowTokens.sub(marketBorrowTokenAmount);
353
+ borrowLimit = BN.min(
354
+ borrowLimit,
355
+ marketDepositTokenAmount.sub(minDepositTokens)
356
+ );
357
+ borrowLimit = BN.min(
358
+ borrowLimit,
359
+ marketDepositTokenAmount.sub(marketBorrowTokenAmount)
360
+ );
356
361
 
357
362
  if (borrowLimit.eq(ZERO)) {
358
363
  withdrawLimit = ZERO;
package/src/math/utils.ts CHANGED
@@ -1,37 +1,23 @@
1
- import { BN, ZERO } from '../';
1
+ import { BN } from '../';
2
2
 
3
3
  export function clampBN(x: BN, min: BN, max: BN): BN {
4
4
  return BN.max(min, BN.min(x, max));
5
5
  }
6
6
 
7
- export const squareRootBN = (n, closeness = new BN(1)): BN => {
8
- if (n.lt(ZERO)) {
9
- throw new Error('square root of negative number');
7
+ export const squareRootBN = (n: BN): BN => {
8
+ if (n.lt(new BN(0))) {
9
+ throw new Error('Sqrt only works on non-negtiave inputs');
10
+ }
11
+ if (n.lt(new BN(2))) {
12
+ return n;
10
13
  }
11
14
 
12
- // Assuming the sqrt of n as n only
13
- let x = n;
14
-
15
- // The closed guess will be stored in the root
16
- let root;
17
-
18
- // To count the number of iterations
19
- let count = 0;
20
- const TWO = new BN(2);
21
-
22
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
23
- while (count < Number.MAX_SAFE_INTEGER) {
24
- count++;
25
-
26
- // Calculate more closed x
27
- root = x.add(n.div(x)).div(TWO);
28
-
29
- // Check for closeness
30
- if (x.sub(root).abs().lte(closeness)) break;
15
+ const smallCand = squareRootBN(n.shrn(2)).shln(1);
16
+ const largeCand = smallCand.add(new BN(1));
31
17
 
32
- // Update root
33
- x = root;
18
+ if (largeCand.mul(largeCand).gt(n)) {
19
+ return smallCand;
20
+ } else {
21
+ return largeCand;
34
22
  }
35
-
36
- return root;
37
23
  };
@@ -9,6 +9,10 @@ import {
9
9
  Transaction,
10
10
  TransactionSignature,
11
11
  Connection,
12
+ VersionedTransaction,
13
+ TransactionMessage,
14
+ TransactionInstruction,
15
+ AddressLookupTableAccount,
12
16
  } from '@solana/web3.js';
13
17
  import { AnchorProvider } from '@project-serum/anchor';
14
18
  import assert from 'assert';
@@ -56,7 +60,65 @@ export class RetryTxSender implements TxSender {
56
60
  ? tx
57
61
  : await this.prepareTx(tx, additionalSigners, opts);
58
62
 
59
- const rawTransaction = signedTx.serialize();
63
+ return this.sendRawTransaction(signedTx.serialize(), opts);
64
+ }
65
+
66
+ async prepareTx(
67
+ tx: Transaction,
68
+ additionalSigners: Array<Signer>,
69
+ opts: ConfirmOptions
70
+ ): Promise<Transaction> {
71
+ tx.feePayer = this.provider.wallet.publicKey;
72
+ tx.recentBlockhash = (
73
+ await this.provider.connection.getRecentBlockhash(
74
+ opts.preflightCommitment
75
+ )
76
+ ).blockhash;
77
+
78
+ const signedTx = await this.provider.wallet.signTransaction(tx);
79
+ additionalSigners
80
+ .filter((s): s is Signer => s !== undefined)
81
+ .forEach((kp) => {
82
+ signedTx.partialSign(kp);
83
+ });
84
+
85
+ return signedTx;
86
+ }
87
+
88
+ async sendVersionedTransaction(
89
+ ixs: TransactionInstruction[],
90
+ lookupTableAccounts: AddressLookupTableAccount[],
91
+ additionalSigners?: Array<Signer>,
92
+ opts?: ConfirmOptions
93
+ ): Promise<TxSigAndSlot> {
94
+ if (additionalSigners === undefined) {
95
+ additionalSigners = [];
96
+ }
97
+ if (opts === undefined) {
98
+ opts = this.provider.opts;
99
+ }
100
+
101
+ const message = new TransactionMessage({
102
+ payerKey: this.provider.wallet.publicKey,
103
+ recentBlockhash: (
104
+ await this.provider.connection.getRecentBlockhash(
105
+ opts.preflightCommitment
106
+ )
107
+ ).blockhash,
108
+ instructions: ixs,
109
+ }).compileToV0Message(lookupTableAccounts);
110
+
111
+ const tx = new VersionedTransaction(message);
112
+ // @ts-ignore
113
+ tx.sign(additionalSigners.concat(this.provider.wallet.payer));
114
+
115
+ return this.sendRawTransaction(tx.serialize(), opts);
116
+ }
117
+
118
+ async sendRawTransaction(
119
+ rawTransaction: Buffer | Uint8Array,
120
+ opts: ConfirmOptions
121
+ ): Promise<TxSigAndSlot> {
60
122
  const startTime = this.getTimestamp();
61
123
 
62
124
  let txid: TransactionSignature;
@@ -111,28 +173,6 @@ export class RetryTxSender implements TxSender {
111
173
  return { txSig: txid, slot };
112
174
  }
113
175
 
114
- async prepareTx(
115
- tx: Transaction,
116
- additionalSigners: Array<Signer>,
117
- opts: ConfirmOptions
118
- ): Promise<Transaction> {
119
- tx.feePayer = this.provider.wallet.publicKey;
120
- tx.recentBlockhash = (
121
- await this.provider.connection.getRecentBlockhash(
122
- opts.preflightCommitment
123
- )
124
- ).blockhash;
125
-
126
- const signedTx = await this.provider.wallet.signTransaction(tx);
127
- additionalSigners
128
- .filter((s): s is Signer => s !== undefined)
129
- .forEach((kp) => {
130
- signedTx.partialSign(kp);
131
- });
132
-
133
- return signedTx;
134
- }
135
-
136
176
  async confirmTransaction(
137
177
  signature: TransactionSignature,
138
178
  commitment?: Commitment
@@ -229,7 +269,10 @@ export class RetryTxSender implements TxSender {
229
269
  );
230
270
  }
231
271
 
232
- sendToAdditionalConnections(rawTx: Buffer, opts: ConfirmOptions): void {
272
+ sendToAdditionalConnections(
273
+ rawTx: Buffer | Uint8Array,
274
+ opts: ConfirmOptions
275
+ ): void {
233
276
  this.additionalConnections.map((connection) => {
234
277
  connection.sendRawTransaction(rawTx, opts).catch((e) => {
235
278
  console.error(
package/src/tx/types.ts CHANGED
@@ -1,8 +1,10 @@
1
1
  import { Provider } from '@project-serum/anchor';
2
2
  import {
3
+ AddressLookupTableAccount,
3
4
  ConfirmOptions,
4
5
  Signer,
5
6
  Transaction,
7
+ TransactionInstruction,
6
8
  TransactionSignature,
7
9
  } from '@solana/web3.js';
8
10
 
@@ -20,4 +22,11 @@ export interface TxSender {
20
22
  opts?: ConfirmOptions,
21
23
  preSigned?: boolean
22
24
  ): Promise<TxSigAndSlot>;
25
+
26
+ sendVersionedTransaction(
27
+ ixs: TransactionInstruction[],
28
+ lookupTableAccounts: AddressLookupTableAccount[],
29
+ additionalSigners?: Array<Signer>,
30
+ opts?: ConfirmOptions
31
+ ): Promise<TxSigAndSlot>;
23
32
  }
@@ -4266,8 +4266,8 @@ describe('DLOB Spot Tests', () => {
4266
4266
  });
4267
4267
 
4268
4268
  it('Test two market orders to fill one limit order', () => {
4269
- const vAsk = new BN(15);
4270
- const vBid = new BN(8);
4269
+ const fallbackAsk = new BN(15);
4270
+ const fallbackBid = new BN(8);
4271
4271
 
4272
4272
  const user0 = Keypair.generate();
4273
4273
  const user1 = Keypair.generate();
@@ -4280,7 +4280,7 @@ describe('DLOB Spot Tests', () => {
4280
4280
 
4281
4281
  const slot = 12;
4282
4282
  const oracle = {
4283
- price: vBid.add(vAsk).div(new BN(2)),
4283
+ price: fallbackBid.add(fallbackAsk).div(new BN(2)),
4284
4284
  slot: new BN(slot),
4285
4285
  confidence: new BN(1),
4286
4286
  hasSufficientNumberOfDataPoints: true,
@@ -4297,8 +4297,8 @@ describe('DLOB Spot Tests', () => {
4297
4297
  new BN(14), // price
4298
4298
  BASE_PRECISION, // quantity
4299
4299
  PositionDirection.SHORT,
4300
- vBid,
4301
- vAsk,
4300
+ fallbackBid,
4301
+ fallbackAsk,
4302
4302
  undefined,
4303
4303
  undefined,
4304
4304
  undefined,
@@ -4314,8 +4314,8 @@ describe('DLOB Spot Tests', () => {
4314
4314
  new BN(13), // price
4315
4315
  BASE_PRECISION, // quantity
4316
4316
  PositionDirection.SHORT,
4317
- vBid,
4318
- vAsk,
4317
+ fallbackBid,
4318
+ fallbackAsk,
4319
4319
  undefined,
4320
4320
  undefined,
4321
4321
  undefined,
@@ -4331,8 +4331,8 @@ describe('DLOB Spot Tests', () => {
4331
4331
  new BN(8), // price <-- best price
4332
4332
  new BN(3).mul(BASE_PRECISION), // quantity
4333
4333
  PositionDirection.SHORT,
4334
- vBid,
4335
- vAsk,
4334
+ fallbackBid,
4335
+ fallbackAsk,
4336
4336
  undefined,
4337
4337
  undefined,
4338
4338
  undefined,
@@ -4361,11 +4361,11 @@ describe('DLOB Spot Tests', () => {
4361
4361
  MarketType.SPOT,
4362
4362
  4, // orderId
4363
4363
  marketIndex,
4364
- new BN(0), // price
4364
+ fallbackAsk, // price
4365
4365
  new BN(1).mul(BASE_PRECISION), // quantity
4366
4366
  PositionDirection.LONG,
4367
- vBid,
4368
- vAsk
4367
+ fallbackBid,
4368
+ fallbackAsk
4369
4369
  );
4370
4370
  insertOrderToDLOB(
4371
4371
  dlob,
@@ -4374,11 +4374,11 @@ describe('DLOB Spot Tests', () => {
4374
4374
  MarketType.SPOT,
4375
4375
  5, // orderId
4376
4376
  marketIndex,
4377
- new BN(0), // price
4377
+ fallbackAsk, // price
4378
4378
  new BN(2).mul(BASE_PRECISION), // quantity
4379
4379
  PositionDirection.LONG,
4380
- vBid,
4381
- vAsk
4380
+ fallbackBid,
4381
+ fallbackAsk
4382
4382
  );
4383
4383
 
4384
4384
  const nodesToFillAfter = dlob.findNodesToFill(
@@ -4399,7 +4399,7 @@ describe('DLOB Spot Tests', () => {
4399
4399
  );
4400
4400
  console.log(`market nodes: ${mktNodes.length}`);
4401
4401
 
4402
- printBookState(dlob, marketIndex, vBid, vAsk, slot, oracle);
4402
+ printBookState(dlob, marketIndex, fallbackBid, fallbackAsk, slot, oracle);
4403
4403
 
4404
4404
  for (const n of nodesToFillAfter) {
4405
4405
  console.log(
package/src/tx/types.js DELETED
@@ -1,2 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
package/src/tx/utils.js DELETED
@@ -1,17 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.wrapInTx = void 0;
4
- const web3_js_1 = require("@solana/web3.js");
5
- const COMPUTE_UNITS_DEFAULT = 200000;
6
- function wrapInTx(instruction, computeUnits = 600000 // TODO, requires less code change
7
- ) {
8
- const tx = new web3_js_1.Transaction();
9
- if (computeUnits != COMPUTE_UNITS_DEFAULT) {
10
- tx.add(web3_js_1.ComputeBudgetProgram.requestUnits({
11
- units: computeUnits,
12
- additionalFee: 0,
13
- }));
14
- }
15
- return tx.add(instruction);
16
- }
17
- exports.wrapInTx = wrapInTx;