@drift-labs/sdk 2.16.0-beta.3 → 2.17.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/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;
@@ -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, OrderType, 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, OrderTriggerCondition } 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
@@ -220,6 +222,16 @@ export declare class DriftClient {
220
222
  * @returns
221
223
  */
222
224
  modifyPerpOrder(orderId: number, newBaseAmount?: BN, newLimitPrice?: BN, newOraclePriceOffset?: number): Promise<TransactionSignature>;
225
+ /**
226
+ * Modifies an open order by closing it and replacing it with a new order.
227
+ * @deprecated use modifyOrderByUserOrderId instead
228
+ * @param userOrderId: The open order to modify
229
+ * @param newBaseAmount: The new base amount for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
230
+ * @param newLimitPice: The new limit price for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
231
+ * @param newOraclePriceOffset: The new oracle price offset for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
232
+ * @returns
233
+ */
234
+ modifyPerpOrderByUserOrderId(userOrderId: number, newBaseAmount?: BN, newLimitPrice?: BN, newOraclePriceOffset?: number): Promise<TransactionSignature>;
223
235
  /**
224
236
  * Modifies an open order (spot or perp) by closing it and replacing it with a new order.
225
237
  * @param orderId: The open order to modify
@@ -229,27 +241,24 @@ export declare class DriftClient {
229
241
  * @param newOrderType: Optional - New order type for the order.
230
242
  * @param newTriggerPrice: Optional - Thew new trigger price for the order.
231
243
  * @param isSpot: Optional - Set to true if the order is a spot order
244
+ * @param auctionDuration: Only required if order type changed to market from something else
245
+ * @param auctionStartPrice: Only required if order type changed to market from something else
246
+ * @param auctionEndPrice: Only required if order type changed to market from something else
232
247
  * @returns
233
248
  */
234
- modifyOrder({ orderId, newBaseAmount, newLimitPrice, newOraclePriceOffset, newOrderType, newTriggerPrice, isSpot, }: {
249
+ modifyOrder({ orderId, newBaseAmount, newLimitPrice, newOraclePriceOffset, newOrderType, newTriggerPrice, newTriggerCondition, isSpot, auctionDuration, auctionStartPrice, auctionEndPrice, }: {
235
250
  orderId: number;
236
251
  newBaseAmount?: BN;
237
252
  newLimitPrice?: BN;
238
253
  newOraclePriceOffset?: number;
239
254
  newOrderType?: OrderType;
240
255
  newTriggerPrice?: BN;
256
+ newTriggerCondition?: OrderTriggerCondition;
241
257
  isSpot?: boolean;
258
+ auctionDuration?: number;
259
+ auctionStartPrice?: BN;
260
+ auctionEndPrice?: BN;
242
261
  }): Promise<TransactionSignature>;
243
- /**
244
- * Modifies an open order by closing it and replacing it with a new order.
245
- * @deprecated use modifyOrderByUserOrderId instead
246
- * @param userOrderId: The open order to modify
247
- * @param newBaseAmount: The new base amount for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
248
- * @param newLimitPice: The new limit price for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
249
- * @param newOraclePriceOffset: The new oracle price offset for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
250
- * @returns
251
- */
252
- modifyPerpOrderByUserOrderId(userOrderId: number, newBaseAmount?: BN, newLimitPrice?: BN, newOraclePriceOffset?: number): Promise<TransactionSignature>;
253
262
  /**
254
263
  * Modifies an open order by closing it and replacing it with a new order.
255
264
  * @param userOrderId: The open order to modify
@@ -259,16 +268,23 @@ export declare class DriftClient {
259
268
  * @param newOrderType: Optional - New order type for the order.
260
269
  * @param newTriggerPrice: Optional - Thew new trigger price for the order.
261
270
  * @param isSpot: Set to true if the order is a spot order
271
+ * @param auctionDuration: Only required if order type changed to market from something else
272
+ * @param auctionStartPrice: Only required if order type changed to market from something else
273
+ * @param auctionEndPrice: Only required if order type changed to market from something else
262
274
  * @returns
263
275
  */
264
- modifyOrderByUserOrderId({ userOrderId, newBaseAmount, newLimitPrice, newOraclePriceOffset, newOrderType, newTriggerPrice, isSpot, }: {
276
+ modifyOrderByUserOrderId({ userOrderId, newBaseAmount, newLimitPrice, newOraclePriceOffset, newOrderType, newTriggerPrice, newTriggerCondition, isSpot, auctionDuration, auctionStartPrice, auctionEndPrice, }: {
265
277
  userOrderId: number;
266
278
  newBaseAmount?: BN;
267
279
  newLimitPrice?: BN;
268
280
  newOraclePriceOffset?: number;
269
281
  newOrderType?: OrderType;
270
282
  newTriggerPrice?: BN;
283
+ newTriggerCondition?: OrderTriggerCondition;
271
284
  isSpot?: boolean;
285
+ auctionDuration?: number;
286
+ auctionStartPrice?: BN;
287
+ auctionEndPrice?: BN;
272
288
  }): Promise<TransactionSignature>;
273
289
  settlePNLs(users: {
274
290
  settleeUserAccountPublicKey: PublicKey;
@@ -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
@@ -1758,44 +1769,29 @@ class DriftClient {
1758
1769
  * @returns
1759
1770
  */
1760
1771
  async modifyPerpOrder(orderId, newBaseAmount, newLimitPrice, newOraclePriceOffset) {
1761
- if (!newBaseAmount && !newLimitPrice && !newOraclePriceOffset) {
1762
- throw new Error(`Must provide newBaseAmount or newLimitPrice or newOraclePriceOffset to modify order`);
1763
- }
1764
- const openOrder = this.getUser().getOrder(orderId);
1765
- if (!openOrder) {
1766
- throw new Error(`No open order with id ${orderId.toString()}`);
1767
- }
1768
- const cancelOrderIx = await this.getCancelOrderIx(orderId);
1769
- const newOrderParams = {
1770
- orderType: openOrder.orderType,
1771
- marketType: openOrder.marketType,
1772
- direction: openOrder.direction,
1773
- baseAssetAmount: newBaseAmount || openOrder.baseAssetAmount,
1774
- price: newLimitPrice || openOrder.price,
1775
- marketIndex: openOrder.marketIndex,
1776
- reduceOnly: openOrder.reduceOnly,
1777
- postOnly: openOrder.postOnly,
1778
- immediateOrCancel: openOrder.immediateOrCancel,
1779
- triggerPrice: openOrder.triggerPrice,
1780
- triggerCondition: openOrder.triggerCondition,
1781
- oraclePriceOffset: newOraclePriceOffset || openOrder.oraclePriceOffset,
1782
- auctionDuration: openOrder.auctionDuration,
1783
- maxTs: openOrder.maxTs,
1784
- auctionStartPrice: openOrder.auctionStartPrice,
1785
- auctionEndPrice: openOrder.auctionEndPrice,
1786
- userOrderId: openOrder.userOrderId,
1787
- };
1788
- const placeOrderIx = await this.getPlacePerpOrderIx(newOrderParams);
1789
- const tx = new web3_js_1.Transaction();
1790
- tx.add(web3_js_1.ComputeBudgetProgram.requestUnits({
1791
- units: 1000000,
1792
- additionalFee: 0,
1793
- }));
1794
- tx.add(cancelOrderIx);
1795
- tx.add(placeOrderIx);
1796
- const { txSig, slot } = await this.sendTransaction(tx, [], this.opts);
1797
- this.perpMarketLastSlotCache.set(newOrderParams.marketIndex, slot);
1798
- return txSig;
1772
+ return this.modifyOrder({
1773
+ orderId,
1774
+ newBaseAmount,
1775
+ newLimitPrice,
1776
+ newOraclePriceOffset,
1777
+ });
1778
+ }
1779
+ /**
1780
+ * Modifies an open order by closing it and replacing it with a new order.
1781
+ * @deprecated use modifyOrderByUserOrderId instead
1782
+ * @param userOrderId: The open order to modify
1783
+ * @param newBaseAmount: The new base amount for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
1784
+ * @param newLimitPice: The new limit price for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
1785
+ * @param newOraclePriceOffset: The new oracle price offset for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
1786
+ * @returns
1787
+ */
1788
+ async modifyPerpOrderByUserOrderId(userOrderId, newBaseAmount, newLimitPrice, newOraclePriceOffset) {
1789
+ return this.modifyOrderByUserOrderId({
1790
+ userOrderId,
1791
+ newBaseAmount,
1792
+ newLimitPrice,
1793
+ newOraclePriceOffset,
1794
+ });
1799
1795
  }
1800
1796
  /**
1801
1797
  * Modifies an open order (spot or perp) by closing it and replacing it with a new order.
@@ -1806,9 +1802,12 @@ class DriftClient {
1806
1802
  * @param newOrderType: Optional - New order type for the order.
1807
1803
  * @param newTriggerPrice: Optional - Thew new trigger price for the order.
1808
1804
  * @param isSpot: Optional - Set to true if the order is a spot order
1805
+ * @param auctionDuration: Only required if order type changed to market from something else
1806
+ * @param auctionStartPrice: Only required if order type changed to market from something else
1807
+ * @param auctionEndPrice: Only required if order type changed to market from something else
1809
1808
  * @returns
1810
1809
  */
1811
- async modifyOrder({ orderId, newBaseAmount, newLimitPrice, newOraclePriceOffset, newOrderType, newTriggerPrice, isSpot, }) {
1810
+ async modifyOrder({ orderId, newBaseAmount, newLimitPrice, newOraclePriceOffset, newOrderType, newTriggerPrice, newTriggerCondition, isSpot, auctionDuration, auctionStartPrice, auctionEndPrice, }) {
1812
1811
  if (!newBaseAmount && !newLimitPrice && !newOraclePriceOffset) {
1813
1812
  throw new Error(`Must provide newBaseAmount or newLimitPrice or newOraclePriceOffset to modify order`);
1814
1813
  }
@@ -1817,23 +1816,37 @@ class DriftClient {
1817
1816
  throw new Error(`No open order with id ${orderId.toString()}`);
1818
1817
  }
1819
1818
  const cancelOrderIx = await this.getCancelOrderIx(orderId);
1819
+ const orderTypeHasTrigger = newOrderType
1820
+ ? types_1.isOneOfVariant(newOrderType, ['triggerlimit', 'triggerMarket'])
1821
+ : types_1.isOneOfVariant(openOrder.orderType, ['triggerLimit', 'triggerMarket']);
1822
+ const orderTypeHasLimitPrice = newOrderType
1823
+ ? types_1.isOneOfVariant(newOrderType, ['triggerLimit', 'limit'])
1824
+ : types_1.isOneOfVariant(openOrder.orderType, ['triggerLimit', 'limit']);
1820
1825
  const newOrderParams = {
1821
1826
  orderType: newOrderType || openOrder.orderType,
1822
1827
  marketType: openOrder.marketType,
1823
1828
  direction: openOrder.direction,
1824
1829
  baseAssetAmount: newBaseAmount || openOrder.baseAssetAmount,
1825
- price: newLimitPrice || openOrder.price,
1830
+ price: orderTypeHasLimitPrice
1831
+ ? newLimitPrice || openOrder.price
1832
+ : undefined,
1826
1833
  marketIndex: openOrder.marketIndex,
1827
1834
  reduceOnly: openOrder.reduceOnly,
1828
- postOnly: openOrder.postOnly,
1835
+ postOnly: openOrder.postOnly
1836
+ ? types_1.PostOnlyParams.MUST_POST_ONLY
1837
+ : types_1.PostOnlyParams.NONE,
1829
1838
  immediateOrCancel: openOrder.immediateOrCancel,
1830
- triggerPrice: newTriggerPrice || openOrder.triggerPrice,
1831
- triggerCondition: openOrder.triggerCondition,
1839
+ triggerPrice: orderTypeHasTrigger
1840
+ ? newTriggerPrice || openOrder.triggerPrice
1841
+ : undefined,
1842
+ triggerCondition: orderTypeHasTrigger
1843
+ ? newTriggerCondition || openOrder.triggerCondition
1844
+ : undefined,
1832
1845
  oraclePriceOffset: newOraclePriceOffset || openOrder.oraclePriceOffset,
1833
- auctionDuration: openOrder.auctionDuration,
1846
+ auctionDuration: auctionDuration || openOrder.auctionDuration,
1834
1847
  maxTs: openOrder.maxTs,
1835
- auctionStartPrice: openOrder.auctionStartPrice,
1836
- auctionEndPrice: openOrder.auctionEndPrice,
1848
+ auctionStartPrice: auctionStartPrice || openOrder.auctionStartPrice,
1849
+ auctionEndPrice: auctionEndPrice || openOrder.auctionEndPrice,
1837
1850
  userOrderId: openOrder.userOrderId,
1838
1851
  };
1839
1852
  const placeOrderIx = isSpot
@@ -1856,55 +1869,6 @@ class DriftClient {
1856
1869
  }
1857
1870
  return txSig;
1858
1871
  }
1859
- /**
1860
- * Modifies an open order by closing it and replacing it with a new order.
1861
- * @deprecated use modifyOrderByUserOrderId instead
1862
- * @param userOrderId: The open order to modify
1863
- * @param newBaseAmount: The new base amount for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
1864
- * @param newLimitPice: The new limit price for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
1865
- * @param newOraclePriceOffset: The new oracle price offset for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
1866
- * @returns
1867
- */
1868
- async modifyPerpOrderByUserOrderId(userOrderId, newBaseAmount, newLimitPrice, newOraclePriceOffset) {
1869
- if (!newBaseAmount && !newLimitPrice && !newOraclePriceOffset) {
1870
- throw new Error(`Must provide newBaseAmount or newLimitPrice or newOraclePriceOffset to modify order`);
1871
- }
1872
- const openOrder = this.getUser().getOrderByUserOrderId(userOrderId);
1873
- if (!openOrder) {
1874
- throw new Error(`No open order with user order id ${userOrderId.toString()}`);
1875
- }
1876
- const cancelOrderIx = await this.getCancelOrderByUserIdIx(userOrderId);
1877
- const newOrderParams = {
1878
- orderType: openOrder.orderType,
1879
- marketType: openOrder.marketType,
1880
- direction: openOrder.direction,
1881
- baseAssetAmount: newBaseAmount || openOrder.baseAssetAmount,
1882
- price: newLimitPrice || openOrder.price,
1883
- marketIndex: openOrder.marketIndex,
1884
- reduceOnly: openOrder.reduceOnly,
1885
- postOnly: openOrder.postOnly,
1886
- immediateOrCancel: openOrder.immediateOrCancel,
1887
- triggerPrice: openOrder.triggerPrice,
1888
- triggerCondition: openOrder.triggerCondition,
1889
- oraclePriceOffset: newOraclePriceOffset || openOrder.oraclePriceOffset,
1890
- auctionDuration: openOrder.auctionDuration,
1891
- maxTs: openOrder.maxTs,
1892
- auctionStartPrice: openOrder.auctionStartPrice,
1893
- auctionEndPrice: openOrder.auctionEndPrice,
1894
- userOrderId: openOrder.userOrderId,
1895
- };
1896
- const placeOrderIx = await this.getPlacePerpOrderIx(newOrderParams);
1897
- const tx = new web3_js_1.Transaction();
1898
- tx.add(web3_js_1.ComputeBudgetProgram.requestUnits({
1899
- units: 1000000,
1900
- additionalFee: 0,
1901
- }));
1902
- tx.add(cancelOrderIx);
1903
- tx.add(placeOrderIx);
1904
- const { txSig, slot } = await this.sendTransaction(tx, [], this.opts);
1905
- this.perpMarketLastSlotCache.set(newOrderParams.marketIndex, slot);
1906
- return txSig;
1907
- }
1908
1872
  /**
1909
1873
  * Modifies an open order by closing it and replacing it with a new order.
1910
1874
  * @param userOrderId: The open order to modify
@@ -1914,9 +1878,12 @@ class DriftClient {
1914
1878
  * @param newOrderType: Optional - New order type for the order.
1915
1879
  * @param newTriggerPrice: Optional - Thew new trigger price for the order.
1916
1880
  * @param isSpot: Set to true if the order is a spot order
1881
+ * @param auctionDuration: Only required if order type changed to market from something else
1882
+ * @param auctionStartPrice: Only required if order type changed to market from something else
1883
+ * @param auctionEndPrice: Only required if order type changed to market from something else
1917
1884
  * @returns
1918
1885
  */
1919
- async modifyOrderByUserOrderId({ userOrderId, newBaseAmount, newLimitPrice, newOraclePriceOffset, newOrderType, newTriggerPrice, isSpot, }) {
1886
+ async modifyOrderByUserOrderId({ userOrderId, newBaseAmount, newLimitPrice, newOraclePriceOffset, newOrderType, newTriggerPrice, newTriggerCondition, isSpot, auctionDuration, auctionStartPrice, auctionEndPrice, }) {
1920
1887
  if (!newBaseAmount && !newLimitPrice && !newOraclePriceOffset) {
1921
1888
  throw new Error(`Must provide newBaseAmount or newLimitPrice or newOraclePriceOffset to modify order`);
1922
1889
  }
@@ -1925,23 +1892,37 @@ class DriftClient {
1925
1892
  throw new Error(`No open order with user order id ${userOrderId.toString()}`);
1926
1893
  }
1927
1894
  const cancelOrderIx = await this.getCancelOrderByUserIdIx(userOrderId);
1895
+ const orderTypeHasTrigger = newOrderType
1896
+ ? types_1.isOneOfVariant(newOrderType, ['triggerlimit', 'triggerMarket'])
1897
+ : types_1.isOneOfVariant(openOrder.orderType, ['triggerLimit', 'triggerMarket']);
1898
+ const orderTypeHasLimitPrice = newOrderType
1899
+ ? types_1.isOneOfVariant(newOrderType, ['triggerLimit', 'limit'])
1900
+ : types_1.isOneOfVariant(openOrder.orderType, ['triggerLimit', 'limit']);
1928
1901
  const newOrderParams = {
1929
1902
  orderType: newOrderType || openOrder.orderType,
1930
1903
  marketType: openOrder.marketType,
1931
1904
  direction: openOrder.direction,
1932
1905
  baseAssetAmount: newBaseAmount || openOrder.baseAssetAmount,
1933
- price: newLimitPrice || openOrder.price,
1906
+ price: orderTypeHasLimitPrice
1907
+ ? newLimitPrice || openOrder.price
1908
+ : undefined,
1934
1909
  marketIndex: openOrder.marketIndex,
1935
1910
  reduceOnly: openOrder.reduceOnly,
1936
- postOnly: openOrder.postOnly,
1911
+ postOnly: openOrder.postOnly
1912
+ ? types_1.PostOnlyParams.MUST_POST_ONLY
1913
+ : types_1.PostOnlyParams.NONE,
1937
1914
  immediateOrCancel: openOrder.immediateOrCancel,
1938
- triggerPrice: newTriggerPrice || openOrder.triggerPrice,
1939
- triggerCondition: openOrder.triggerCondition,
1915
+ triggerPrice: orderTypeHasTrigger
1916
+ ? newTriggerPrice || openOrder.triggerPrice
1917
+ : undefined,
1918
+ triggerCondition: orderTypeHasTrigger
1919
+ ? newTriggerCondition || openOrder.triggerCondition
1920
+ : undefined,
1940
1921
  oraclePriceOffset: newOraclePriceOffset || openOrder.oraclePriceOffset,
1941
- auctionDuration: openOrder.auctionDuration,
1922
+ auctionDuration: auctionDuration || openOrder.auctionDuration,
1942
1923
  maxTs: openOrder.maxTs,
1943
- auctionStartPrice: openOrder.auctionStartPrice,
1944
- auctionEndPrice: openOrder.auctionEndPrice,
1924
+ auctionStartPrice: auctionStartPrice || openOrder.auctionStartPrice,
1925
+ auctionEndPrice: auctionEndPrice || openOrder.auctionEndPrice,
1945
1926
  userOrderId: openOrder.userOrderId,
1946
1927
  };
1947
1928
  const placeOrderIx = isSpot
@@ -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.3",
2
+ "version": "2.17.0-beta.0",
3
3
  "name": "drift",
4
4
  "instructions": [
5
5
  {
@@ -4655,7 +4655,9 @@
4655
4655
  },
4656
4656
  {
4657
4657
  "name": "postOnly",
4658
- "type": "bool"
4658
+ "type": {
4659
+ "defined": "PostOnlyParam"
4660
+ }
4659
4661
  },
4660
4662
  {
4661
4663
  "name": "immediateOrCancel",
@@ -5862,6 +5864,23 @@
5862
5864
  ]
5863
5865
  }
5864
5866
  },
5867
+ {
5868
+ "name": "PostOnlyParam",
5869
+ "type": {
5870
+ "kind": "enum",
5871
+ "variants": [
5872
+ {
5873
+ "name": "None"
5874
+ },
5875
+ {
5876
+ "name": "MustPostOnly"
5877
+ },
5878
+ {
5879
+ "name": "TryPostOnly"
5880
+ }
5881
+ ]
5882
+ }
5883
+ },
5865
5884
  {
5866
5885
  "name": "TwapPeriod",
5867
5886
  "type": {
@@ -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/lib/types.d.ts CHANGED
@@ -886,7 +886,7 @@ export declare type OrderParams = {
886
886
  price: BN;
887
887
  marketIndex: number;
888
888
  reduceOnly: boolean;
889
- postOnly: boolean;
889
+ postOnly: PostOnlyParams;
890
890
  immediateOrCancel: boolean;
891
891
  triggerPrice: BN | null;
892
892
  triggerCondition: OrderTriggerCondition;
@@ -897,6 +897,17 @@ export declare type OrderParams = {
897
897
  auctionStartPrice: BN | null;
898
898
  auctionEndPrice: BN | null;
899
899
  };
900
+ export declare class PostOnlyParams {
901
+ static readonly NONE: {
902
+ none: {};
903
+ };
904
+ static readonly MUST_POST_ONLY: {
905
+ mustPostOnly: {};
906
+ };
907
+ static readonly TRY_POST_ONLY: {
908
+ tryPostOnly: {};
909
+ };
910
+ }
900
911
  export declare type NecessaryOrderParams = {
901
912
  orderType: OrderType;
902
913
  marketIndex: number;
package/lib/types.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DefaultOrderParams = exports.LiquidationType = exports.LPAction = exports.TradeSide = exports.getVariant = exports.isOneOfVariant = exports.isVariant = exports.StakeAction = exports.SpotFulfillmentConfigStatus = exports.SettlePnlExplanation = exports.DepositExplanation = exports.SpotFulfillmentStatus = exports.SpotFulfillmentType = exports.OrderTriggerCondition = exports.OrderActionExplanation = exports.OrderAction = exports.OrderStatus = exports.MarketType = exports.OrderType = exports.OracleSource = exports.DepositDirection = exports.PositionDirection = exports.SpotBalanceType = exports.SwapDirection = exports.AssetTier = exports.ContractTier = exports.ContractType = exports.UserStatus = exports.MarketStatus = exports.ExchangeStatus = void 0;
3
+ exports.DefaultOrderParams = exports.PostOnlyParams = exports.LiquidationType = exports.LPAction = exports.TradeSide = exports.getVariant = exports.isOneOfVariant = exports.isVariant = exports.StakeAction = exports.SpotFulfillmentConfigStatus = exports.SettlePnlExplanation = exports.DepositExplanation = exports.SpotFulfillmentStatus = exports.SpotFulfillmentType = exports.OrderTriggerCondition = exports.OrderActionExplanation = exports.OrderAction = exports.OrderStatus = exports.MarketType = exports.OrderType = exports.OracleSource = exports.DepositDirection = exports.PositionDirection = exports.SpotBalanceType = exports.SwapDirection = exports.AssetTier = exports.ContractTier = exports.ContractType = exports.UserStatus = exports.MarketStatus = exports.ExchangeStatus = void 0;
4
4
  const _1 = require(".");
5
5
  // # Utility Types / Enums / Constants
6
6
  var ExchangeStatus;
@@ -232,6 +232,12 @@ LiquidationType.BORROW_BANKRUPTCY = {
232
232
  LiquidationType.LIQUIDATE_SPOT = {
233
233
  liquidateSpot: {},
234
234
  };
235
+ class PostOnlyParams {
236
+ }
237
+ exports.PostOnlyParams = PostOnlyParams;
238
+ PostOnlyParams.NONE = { none: {} };
239
+ PostOnlyParams.MUST_POST_ONLY = { mustPostOnly: {} }; // Tx fails if order can't be post only
240
+ PostOnlyParams.TRY_POST_ONLY = { tryPostOnly: {} }; // Tx succeeds and order not placed if can't be post only
235
241
  exports.DefaultOrderParams = {
236
242
  orderType: OrderType.MARKET,
237
243
  marketType: MarketType.PERP,
@@ -241,7 +247,7 @@ exports.DefaultOrderParams = {
241
247
  price: _1.ZERO,
242
248
  marketIndex: 0,
243
249
  reduceOnly: false,
244
- postOnly: false,
250
+ postOnly: PostOnlyParams.NONE,
245
251
  immediateOrCancel: false,
246
252
  triggerPrice: null,
247
253
  triggerCondition: OrderTriggerCondition.ABOVE,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drift-labs/sdk",
3
- "version": "2.16.0-beta.3",
3
+ "version": "2.17.0-beta.0",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "author": "crispheaney",
@@ -53,10 +53,11 @@
53
53
  "eslint-plugin-prettier": "^3.4.0",
54
54
  "mocha": "^10.0.0",
55
55
  "prettier": "^2.4.1",
56
- "ts-node": "^10.8.0"
56
+ "ts-node": "^10.8.0",
57
+ "typescript": "^4.9.5"
57
58
  },
58
59
  "description": "SDK for Drift Protocol",
59
60
  "engines": {
60
61
  "node": ">=12"
61
62
  }
62
- }
63
+ }
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
 
@@ -25,6 +25,9 @@ import {
25
25
  TxParams,
26
26
  SerumV3FulfillmentConfigAccount,
27
27
  isVariant,
28
+ OrderTriggerCondition,
29
+ isOneOfVariant,
30
+ PostOnlyParams,
28
31
  } from './types';
29
32
  import * as anchor from '@project-serum/anchor';
30
33
  import driftIDL from './idl/drift.json';
@@ -42,6 +45,7 @@ import {
42
45
  Signer,
43
46
  SystemProgram,
44
47
  ComputeBudgetProgram,
48
+ AddressLookupTableAccount,
45
49
  } from '@solana/web3.js';
46
50
 
47
51
  import { TokenFaucet } from './tokenFaucet';
@@ -77,7 +81,7 @@ import { WebSocketDriftClientAccountSubscriber } from './accounts/webSocketDrift
77
81
  import { RetryTxSender } from './tx/retryTxSender';
78
82
  import { User } from './user';
79
83
  import { UserSubscriptionConfig } from './userConfig';
80
- import { getMarketsAndOraclesForSubscription } from './config';
84
+ import { configs, getMarketsAndOraclesForSubscription } from './config';
81
85
  import { WRAPPED_SOL_MINT } from './constants/spotMarkets';
82
86
  import { UserStats } from './userStats';
83
87
  import { isSpotPositionAvailable } from './math/spotPosition';
@@ -112,6 +116,7 @@ export class DriftClient {
112
116
  perpMarketLastSlotCache = new Map<number, number>();
113
117
  spotMarketLastSlotCache = new Map<number, number>();
114
118
  authority: PublicKey;
119
+ marketLookupTable: PublicKey;
115
120
 
116
121
  public get isSubscribed() {
117
122
  return this._isSubscribed && this.accountSubscriber.isSubscribed;
@@ -178,6 +183,13 @@ export class DriftClient {
178
183
  oracleInfos = oracleInfos ? oracleInfos : envOracleInfos;
179
184
  }
180
185
 
186
+ this.marketLookupTable = config.marketLookupTable;
187
+ if (config.env && !this.marketLookupTable) {
188
+ this.marketLookupTable = new PublicKey(
189
+ configs[config.env].MARKET_LOOKUP_TABLE
190
+ );
191
+ }
192
+
181
193
  if (config.accountSubscription?.type === 'polling') {
182
194
  this.accountSubscriber = new PollingDriftClientAccountSubscriber(
183
195
  this.program,
@@ -387,6 +399,15 @@ export class DriftClient {
387
399
  )) as SerumV3FulfillmentConfigAccount;
388
400
  }
389
401
 
402
+ public async fetchMarketLookupTableAccount(): Promise<AddressLookupTableAccount> {
403
+ if (!this.marketLookupTable) {
404
+ throw Error('Market lookup table address not set');
405
+ }
406
+
407
+ return (await this.connection.getAddressLookupTable(this.marketLookupTable))
408
+ .value;
409
+ }
410
+
390
411
  /**
391
412
  * Update the wallet to use for drift transactions and linked user account
392
413
  * @param newWallet
@@ -3031,51 +3052,35 @@ export class DriftClient {
3031
3052
  newLimitPrice?: BN,
3032
3053
  newOraclePriceOffset?: number
3033
3054
  ): Promise<TransactionSignature> {
3034
- if (!newBaseAmount && !newLimitPrice && !newOraclePriceOffset) {
3035
- throw new Error(
3036
- `Must provide newBaseAmount or newLimitPrice or newOraclePriceOffset to modify order`
3037
- );
3038
- }
3039
-
3040
- const openOrder = this.getUser().getOrder(orderId);
3041
- if (!openOrder) {
3042
- throw new Error(`No open order with id ${orderId.toString()}`);
3043
- }
3044
- const cancelOrderIx = await this.getCancelOrderIx(orderId);
3045
-
3046
- const newOrderParams: OptionalOrderParams = {
3047
- orderType: openOrder.orderType,
3048
- marketType: openOrder.marketType,
3049
- direction: openOrder.direction,
3050
- baseAssetAmount: newBaseAmount || openOrder.baseAssetAmount,
3051
- price: newLimitPrice || openOrder.price,
3052
- marketIndex: openOrder.marketIndex,
3053
- reduceOnly: openOrder.reduceOnly,
3054
- postOnly: openOrder.postOnly,
3055
- immediateOrCancel: openOrder.immediateOrCancel,
3056
- triggerPrice: openOrder.triggerPrice,
3057
- triggerCondition: openOrder.triggerCondition,
3058
- oraclePriceOffset: newOraclePriceOffset || openOrder.oraclePriceOffset,
3059
- auctionDuration: openOrder.auctionDuration,
3060
- maxTs: openOrder.maxTs,
3061
- auctionStartPrice: openOrder.auctionStartPrice,
3062
- auctionEndPrice: openOrder.auctionEndPrice,
3063
- userOrderId: openOrder.userOrderId,
3064
- };
3065
- const placeOrderIx = await this.getPlacePerpOrderIx(newOrderParams);
3055
+ return this.modifyOrder({
3056
+ orderId,
3057
+ newBaseAmount,
3058
+ newLimitPrice,
3059
+ newOraclePriceOffset,
3060
+ });
3061
+ }
3066
3062
 
3067
- const tx = new Transaction();
3068
- tx.add(
3069
- ComputeBudgetProgram.requestUnits({
3070
- units: 1_000_000,
3071
- additionalFee: 0,
3072
- })
3073
- );
3074
- tx.add(cancelOrderIx);
3075
- tx.add(placeOrderIx);
3076
- const { txSig, slot } = await this.sendTransaction(tx, [], this.opts);
3077
- this.perpMarketLastSlotCache.set(newOrderParams.marketIndex, slot);
3078
- return txSig;
3063
+ /**
3064
+ * Modifies an open order by closing it and replacing it with a new order.
3065
+ * @deprecated use modifyOrderByUserOrderId instead
3066
+ * @param userOrderId: The open order to modify
3067
+ * @param newBaseAmount: The new base amount for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
3068
+ * @param newLimitPice: The new limit price for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
3069
+ * @param newOraclePriceOffset: The new oracle price offset for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
3070
+ * @returns
3071
+ */
3072
+ public async modifyPerpOrderByUserOrderId(
3073
+ userOrderId: number,
3074
+ newBaseAmount?: BN,
3075
+ newLimitPrice?: BN,
3076
+ newOraclePriceOffset?: number
3077
+ ): Promise<TransactionSignature> {
3078
+ return this.modifyOrderByUserOrderId({
3079
+ userOrderId,
3080
+ newBaseAmount,
3081
+ newLimitPrice,
3082
+ newOraclePriceOffset,
3083
+ });
3079
3084
  }
3080
3085
 
3081
3086
  /**
@@ -3087,6 +3092,9 @@ export class DriftClient {
3087
3092
  * @param newOrderType: Optional - New order type for the order.
3088
3093
  * @param newTriggerPrice: Optional - Thew new trigger price for the order.
3089
3094
  * @param isSpot: Optional - Set to true if the order is a spot order
3095
+ * @param auctionDuration: Only required if order type changed to market from something else
3096
+ * @param auctionStartPrice: Only required if order type changed to market from something else
3097
+ * @param auctionEndPrice: Only required if order type changed to market from something else
3090
3098
  * @returns
3091
3099
  */
3092
3100
  public async modifyOrder({
@@ -3096,7 +3104,11 @@ export class DriftClient {
3096
3104
  newOraclePriceOffset,
3097
3105
  newOrderType,
3098
3106
  newTriggerPrice,
3107
+ newTriggerCondition,
3099
3108
  isSpot,
3109
+ auctionDuration,
3110
+ auctionStartPrice,
3111
+ auctionEndPrice,
3100
3112
  }: {
3101
3113
  orderId: number;
3102
3114
  newBaseAmount?: BN;
@@ -3104,7 +3116,11 @@ export class DriftClient {
3104
3116
  newOraclePriceOffset?: number;
3105
3117
  newOrderType?: OrderType;
3106
3118
  newTriggerPrice?: BN;
3119
+ newTriggerCondition?: OrderTriggerCondition;
3107
3120
  isSpot?: boolean;
3121
+ auctionDuration?: number;
3122
+ auctionStartPrice?: BN;
3123
+ auctionEndPrice?: BN;
3108
3124
  }): Promise<TransactionSignature> {
3109
3125
  if (!newBaseAmount && !newLimitPrice && !newOraclePriceOffset) {
3110
3126
  throw new Error(
@@ -3118,23 +3134,38 @@ export class DriftClient {
3118
3134
  }
3119
3135
  const cancelOrderIx = await this.getCancelOrderIx(orderId);
3120
3136
 
3137
+ const orderTypeHasTrigger = newOrderType
3138
+ ? isOneOfVariant(newOrderType, ['triggerlimit', 'triggerMarket'])
3139
+ : isOneOfVariant(openOrder.orderType, ['triggerLimit', 'triggerMarket']);
3140
+ const orderTypeHasLimitPrice = newOrderType
3141
+ ? isOneOfVariant(newOrderType, ['triggerLimit', 'limit'])
3142
+ : isOneOfVariant(openOrder.orderType, ['triggerLimit', 'limit']);
3143
+
3121
3144
  const newOrderParams: OptionalOrderParams = {
3122
3145
  orderType: newOrderType || openOrder.orderType,
3123
3146
  marketType: openOrder.marketType,
3124
3147
  direction: openOrder.direction,
3125
3148
  baseAssetAmount: newBaseAmount || openOrder.baseAssetAmount,
3126
- price: newLimitPrice || openOrder.price,
3149
+ price: orderTypeHasLimitPrice
3150
+ ? newLimitPrice || openOrder.price
3151
+ : undefined,
3127
3152
  marketIndex: openOrder.marketIndex,
3128
3153
  reduceOnly: openOrder.reduceOnly,
3129
- postOnly: openOrder.postOnly,
3154
+ postOnly: openOrder.postOnly
3155
+ ? PostOnlyParams.MUST_POST_ONLY
3156
+ : PostOnlyParams.NONE,
3130
3157
  immediateOrCancel: openOrder.immediateOrCancel,
3131
- triggerPrice: newTriggerPrice || openOrder.triggerPrice,
3132
- triggerCondition: openOrder.triggerCondition,
3158
+ triggerPrice: orderTypeHasTrigger
3159
+ ? newTriggerPrice || openOrder.triggerPrice
3160
+ : undefined,
3161
+ triggerCondition: orderTypeHasTrigger
3162
+ ? newTriggerCondition || openOrder.triggerCondition
3163
+ : undefined,
3133
3164
  oraclePriceOffset: newOraclePriceOffset || openOrder.oraclePriceOffset,
3134
- auctionDuration: openOrder.auctionDuration,
3165
+ auctionDuration: auctionDuration || openOrder.auctionDuration,
3135
3166
  maxTs: openOrder.maxTs,
3136
- auctionStartPrice: openOrder.auctionStartPrice,
3137
- auctionEndPrice: openOrder.auctionEndPrice,
3167
+ auctionStartPrice: auctionStartPrice || openOrder.auctionStartPrice,
3168
+ auctionEndPrice: auctionEndPrice || openOrder.auctionEndPrice,
3138
3169
  userOrderId: openOrder.userOrderId,
3139
3170
  };
3140
3171
  const placeOrderIx = isSpot
@@ -3162,70 +3193,6 @@ export class DriftClient {
3162
3193
  return txSig;
3163
3194
  }
3164
3195
 
3165
- /**
3166
- * Modifies an open order by closing it and replacing it with a new order.
3167
- * @deprecated use modifyOrderByUserOrderId instead
3168
- * @param userOrderId: The open order to modify
3169
- * @param newBaseAmount: The new base amount for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
3170
- * @param newLimitPice: The new limit price for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
3171
- * @param newOraclePriceOffset: The new oracle price offset for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
3172
- * @returns
3173
- */
3174
- public async modifyPerpOrderByUserOrderId(
3175
- userOrderId: number,
3176
- newBaseAmount?: BN,
3177
- newLimitPrice?: BN,
3178
- newOraclePriceOffset?: number
3179
- ): Promise<TransactionSignature> {
3180
- if (!newBaseAmount && !newLimitPrice && !newOraclePriceOffset) {
3181
- throw new Error(
3182
- `Must provide newBaseAmount or newLimitPrice or newOraclePriceOffset to modify order`
3183
- );
3184
- }
3185
-
3186
- const openOrder = this.getUser().getOrderByUserOrderId(userOrderId);
3187
- if (!openOrder) {
3188
- throw new Error(
3189
- `No open order with user order id ${userOrderId.toString()}`
3190
- );
3191
- }
3192
- const cancelOrderIx = await this.getCancelOrderByUserIdIx(userOrderId);
3193
-
3194
- const newOrderParams: OptionalOrderParams = {
3195
- orderType: openOrder.orderType,
3196
- marketType: openOrder.marketType,
3197
- direction: openOrder.direction,
3198
- baseAssetAmount: newBaseAmount || openOrder.baseAssetAmount,
3199
- price: newLimitPrice || openOrder.price,
3200
- marketIndex: openOrder.marketIndex,
3201
- reduceOnly: openOrder.reduceOnly,
3202
- postOnly: openOrder.postOnly,
3203
- immediateOrCancel: openOrder.immediateOrCancel,
3204
- triggerPrice: openOrder.triggerPrice,
3205
- triggerCondition: openOrder.triggerCondition,
3206
- oraclePriceOffset: newOraclePriceOffset || openOrder.oraclePriceOffset,
3207
- auctionDuration: openOrder.auctionDuration,
3208
- maxTs: openOrder.maxTs,
3209
- auctionStartPrice: openOrder.auctionStartPrice,
3210
- auctionEndPrice: openOrder.auctionEndPrice,
3211
- userOrderId: openOrder.userOrderId,
3212
- };
3213
- const placeOrderIx = await this.getPlacePerpOrderIx(newOrderParams);
3214
-
3215
- const tx = new Transaction();
3216
- tx.add(
3217
- ComputeBudgetProgram.requestUnits({
3218
- units: 1_000_000,
3219
- additionalFee: 0,
3220
- })
3221
- );
3222
- tx.add(cancelOrderIx);
3223
- tx.add(placeOrderIx);
3224
- const { txSig, slot } = await this.sendTransaction(tx, [], this.opts);
3225
- this.perpMarketLastSlotCache.set(newOrderParams.marketIndex, slot);
3226
- return txSig;
3227
- }
3228
-
3229
3196
  /**
3230
3197
  * Modifies an open order by closing it and replacing it with a new order.
3231
3198
  * @param userOrderId: The open order to modify
@@ -3235,6 +3202,9 @@ export class DriftClient {
3235
3202
  * @param newOrderType: Optional - New order type for the order.
3236
3203
  * @param newTriggerPrice: Optional - Thew new trigger price for the order.
3237
3204
  * @param isSpot: Set to true if the order is a spot order
3205
+ * @param auctionDuration: Only required if order type changed to market from something else
3206
+ * @param auctionStartPrice: Only required if order type changed to market from something else
3207
+ * @param auctionEndPrice: Only required if order type changed to market from something else
3238
3208
  * @returns
3239
3209
  */
3240
3210
  public async modifyOrderByUserOrderId({
@@ -3244,7 +3214,11 @@ export class DriftClient {
3244
3214
  newOraclePriceOffset,
3245
3215
  newOrderType,
3246
3216
  newTriggerPrice,
3217
+ newTriggerCondition,
3247
3218
  isSpot,
3219
+ auctionDuration,
3220
+ auctionStartPrice,
3221
+ auctionEndPrice,
3248
3222
  }: {
3249
3223
  userOrderId: number;
3250
3224
  newBaseAmount?: BN;
@@ -3252,7 +3226,11 @@ export class DriftClient {
3252
3226
  newOraclePriceOffset?: number;
3253
3227
  newOrderType?: OrderType;
3254
3228
  newTriggerPrice?: BN;
3229
+ newTriggerCondition?: OrderTriggerCondition;
3255
3230
  isSpot?: boolean;
3231
+ auctionDuration?: number;
3232
+ auctionStartPrice?: BN;
3233
+ auctionEndPrice?: BN;
3256
3234
  }): Promise<TransactionSignature> {
3257
3235
  if (!newBaseAmount && !newLimitPrice && !newOraclePriceOffset) {
3258
3236
  throw new Error(
@@ -3268,23 +3246,38 @@ export class DriftClient {
3268
3246
  }
3269
3247
  const cancelOrderIx = await this.getCancelOrderByUserIdIx(userOrderId);
3270
3248
 
3249
+ const orderTypeHasTrigger = newOrderType
3250
+ ? isOneOfVariant(newOrderType, ['triggerlimit', 'triggerMarket'])
3251
+ : isOneOfVariant(openOrder.orderType, ['triggerLimit', 'triggerMarket']);
3252
+ const orderTypeHasLimitPrice = newOrderType
3253
+ ? isOneOfVariant(newOrderType, ['triggerLimit', 'limit'])
3254
+ : isOneOfVariant(openOrder.orderType, ['triggerLimit', 'limit']);
3255
+
3271
3256
  const newOrderParams: OptionalOrderParams = {
3272
3257
  orderType: newOrderType || openOrder.orderType,
3273
3258
  marketType: openOrder.marketType,
3274
3259
  direction: openOrder.direction,
3275
3260
  baseAssetAmount: newBaseAmount || openOrder.baseAssetAmount,
3276
- price: newLimitPrice || openOrder.price,
3261
+ price: orderTypeHasLimitPrice
3262
+ ? newLimitPrice || openOrder.price
3263
+ : undefined,
3277
3264
  marketIndex: openOrder.marketIndex,
3278
3265
  reduceOnly: openOrder.reduceOnly,
3279
- postOnly: openOrder.postOnly,
3266
+ postOnly: openOrder.postOnly
3267
+ ? PostOnlyParams.MUST_POST_ONLY
3268
+ : PostOnlyParams.NONE,
3280
3269
  immediateOrCancel: openOrder.immediateOrCancel,
3281
- triggerPrice: newTriggerPrice || openOrder.triggerPrice,
3282
- triggerCondition: openOrder.triggerCondition,
3270
+ triggerPrice: orderTypeHasTrigger
3271
+ ? newTriggerPrice || openOrder.triggerPrice
3272
+ : undefined,
3273
+ triggerCondition: orderTypeHasTrigger
3274
+ ? newTriggerCondition || openOrder.triggerCondition
3275
+ : undefined,
3283
3276
  oraclePriceOffset: newOraclePriceOffset || openOrder.oraclePriceOffset,
3284
- auctionDuration: openOrder.auctionDuration,
3277
+ auctionDuration: auctionDuration || openOrder.auctionDuration,
3285
3278
  maxTs: openOrder.maxTs,
3286
- auctionStartPrice: openOrder.auctionStartPrice,
3287
- auctionEndPrice: openOrder.auctionEndPrice,
3279
+ auctionStartPrice: auctionStartPrice || openOrder.auctionStartPrice,
3280
+ auctionEndPrice: auctionEndPrice || openOrder.auctionEndPrice,
3288
3281
  userOrderId: openOrder.userOrderId,
3289
3282
  };
3290
3283
  const placeOrderIx = isSpot
@@ -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.3",
2
+ "version": "2.17.0-beta.0",
3
3
  "name": "drift",
4
4
  "instructions": [
5
5
  {
@@ -4655,7 +4655,9 @@
4655
4655
  },
4656
4656
  {
4657
4657
  "name": "postOnly",
4658
- "type": "bool"
4658
+ "type": {
4659
+ "defined": "PostOnlyParam"
4660
+ }
4659
4661
  },
4660
4662
  {
4661
4663
  "name": "immediateOrCancel",
@@ -5862,6 +5864,23 @@
5862
5864
  ]
5863
5865
  }
5864
5866
  },
5867
+ {
5868
+ "name": "PostOnlyParam",
5869
+ "type": {
5870
+ "kind": "enum",
5871
+ "variants": [
5872
+ {
5873
+ "name": "None"
5874
+ },
5875
+ {
5876
+ "name": "MustPostOnly"
5877
+ },
5878
+ {
5879
+ "name": "TryPostOnly"
5880
+ }
5881
+ ]
5882
+ }
5883
+ },
5865
5884
  {
5866
5885
  "name": "TwapPeriod",
5867
5886
  "type": {
@@ -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
  }
package/src/types.ts CHANGED
@@ -846,7 +846,7 @@ export type OrderParams = {
846
846
  price: BN;
847
847
  marketIndex: number;
848
848
  reduceOnly: boolean;
849
- postOnly: boolean;
849
+ postOnly: PostOnlyParams;
850
850
  immediateOrCancel: boolean;
851
851
  triggerPrice: BN | null;
852
852
  triggerCondition: OrderTriggerCondition;
@@ -858,6 +858,12 @@ export type OrderParams = {
858
858
  auctionEndPrice: BN | null;
859
859
  };
860
860
 
861
+ export class PostOnlyParams {
862
+ static readonly NONE = { none: {} };
863
+ static readonly MUST_POST_ONLY = { mustPostOnly: {} }; // Tx fails if order can't be post only
864
+ static readonly TRY_POST_ONLY = { tryPostOnly: {} }; // Tx succeeds and order not placed if can't be post only
865
+ }
866
+
861
867
  export type NecessaryOrderParams = {
862
868
  orderType: OrderType;
863
869
  marketIndex: number;
@@ -878,7 +884,7 @@ export const DefaultOrderParams: OrderParams = {
878
884
  price: ZERO,
879
885
  marketIndex: 0,
880
886
  reduceOnly: false,
881
- postOnly: false,
887
+ postOnly: PostOnlyParams.NONE,
882
888
  immediateOrCancel: false,
883
889
  triggerPrice: null,
884
890
  triggerCondition: OrderTriggerCondition.ABOVE,
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;