@drift-labs/sdk 2.16.0-beta.2 → 2.16.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/dlob/DLOB.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,7 +1,7 @@
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
6
  import { Connection, PublicKey, TransactionSignature, ConfirmOptions, Transaction, TransactionInstruction, AccountMeta, Signer } from '@solana/web3.js';
7
7
  import { TokenFaucet } from './tokenFaucet';
@@ -212,6 +212,7 @@ export declare class DriftClient {
212
212
  closePosition(marketIndex: number, limitPrice?: BN): Promise<TransactionSignature>;
213
213
  /**
214
214
  * Modifies an open order by closing it and replacing it with a new order.
215
+ * @deprecated use modifyOrder instead
215
216
  * @param orderId: The open order to modify
216
217
  * @param newBaseAmount: The new base amount for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
217
218
  * @param newLimitPice: The new limit price for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
@@ -219,8 +220,29 @@ export declare class DriftClient {
219
220
  * @returns
220
221
  */
221
222
  modifyPerpOrder(orderId: number, newBaseAmount?: BN, newLimitPrice?: BN, newOraclePriceOffset?: number): Promise<TransactionSignature>;
223
+ /**
224
+ * Modifies an open order (spot or perp) by closing it and replacing it with a new order.
225
+ * @param orderId: The open order to modify
226
+ * @param newBaseAmount: The new base amount for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
227
+ * @param newLimitPice: The new limit price for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
228
+ * @param newOraclePriceOffset: The new oracle price offset for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
229
+ * @param newOrderType: Optional - New order type for the order.
230
+ * @param newTriggerPrice: Optional - Thew new trigger price for the order.
231
+ * @param isSpot: Optional - Set to true if the order is a spot order
232
+ * @returns
233
+ */
234
+ modifyOrder({ orderId, newBaseAmount, newLimitPrice, newOraclePriceOffset, newOrderType, newTriggerPrice, isSpot, }: {
235
+ orderId: number;
236
+ newBaseAmount?: BN;
237
+ newLimitPrice?: BN;
238
+ newOraclePriceOffset?: number;
239
+ newOrderType?: OrderType;
240
+ newTriggerPrice?: BN;
241
+ isSpot?: boolean;
242
+ }): Promise<TransactionSignature>;
222
243
  /**
223
244
  * Modifies an open order by closing it and replacing it with a new order.
245
+ * @deprecated use modifyOrderByUserOrderId instead
224
246
  * @param userOrderId: The open order to modify
225
247
  * @param newBaseAmount: The new base amount for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
226
248
  * @param newLimitPice: The new limit price for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
@@ -228,6 +250,26 @@ export declare class DriftClient {
228
250
  * @returns
229
251
  */
230
252
  modifyPerpOrderByUserOrderId(userOrderId: number, newBaseAmount?: BN, newLimitPrice?: BN, newOraclePriceOffset?: number): Promise<TransactionSignature>;
253
+ /**
254
+ * Modifies an open order by closing it and replacing it with a new order.
255
+ * @param userOrderId: The open order to modify
256
+ * @param newBaseAmount: The new base amount for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
257
+ * @param newLimitPice: The new limit price for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
258
+ * @param newOraclePriceOffset: The new oracle price offset for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
259
+ * @param newOrderType: Optional - New order type for the order.
260
+ * @param newTriggerPrice: Optional - Thew new trigger price for the order.
261
+ * @param isSpot: Set to true if the order is a spot order
262
+ * @returns
263
+ */
264
+ modifyOrderByUserOrderId({ userOrderId, newBaseAmount, newLimitPrice, newOraclePriceOffset, newOrderType, newTriggerPrice, isSpot, }: {
265
+ userOrderId: number;
266
+ newBaseAmount?: BN;
267
+ newLimitPrice?: BN;
268
+ newOraclePriceOffset?: number;
269
+ newOrderType?: OrderType;
270
+ newTriggerPrice?: BN;
271
+ isSpot?: boolean;
272
+ }): Promise<TransactionSignature>;
231
273
  settlePNLs(users: {
232
274
  settleeUserAccountPublicKey: PublicKey;
233
275
  settleeUserAccount: UserAccount;
@@ -1750,6 +1750,7 @@ class DriftClient {
1750
1750
  }
1751
1751
  /**
1752
1752
  * Modifies an open order by closing it and replacing it with a new order.
1753
+ * @deprecated use modifyOrder instead
1753
1754
  * @param orderId: The open order to modify
1754
1755
  * @param newBaseAmount: The new base amount for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
1755
1756
  * @param newLimitPice: The new limit price for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
@@ -1796,8 +1797,68 @@ class DriftClient {
1796
1797
  this.perpMarketLastSlotCache.set(newOrderParams.marketIndex, slot);
1797
1798
  return txSig;
1798
1799
  }
1800
+ /**
1801
+ * Modifies an open order (spot or perp) by closing it and replacing it with a new order.
1802
+ * @param orderId: The open order to modify
1803
+ * @param newBaseAmount: The new base amount for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
1804
+ * @param newLimitPice: The new limit price for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
1805
+ * @param newOraclePriceOffset: The new oracle price offset for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
1806
+ * @param newOrderType: Optional - New order type for the order.
1807
+ * @param newTriggerPrice: Optional - Thew new trigger price for the order.
1808
+ * @param isSpot: Optional - Set to true if the order is a spot order
1809
+ * @returns
1810
+ */
1811
+ async modifyOrder({ orderId, newBaseAmount, newLimitPrice, newOraclePriceOffset, newOrderType, newTriggerPrice, isSpot, }) {
1812
+ if (!newBaseAmount && !newLimitPrice && !newOraclePriceOffset) {
1813
+ throw new Error(`Must provide newBaseAmount or newLimitPrice or newOraclePriceOffset to modify order`);
1814
+ }
1815
+ const openOrder = this.getUser().getOrder(orderId);
1816
+ if (!openOrder) {
1817
+ throw new Error(`No open order with id ${orderId.toString()}`);
1818
+ }
1819
+ const cancelOrderIx = await this.getCancelOrderIx(orderId);
1820
+ const newOrderParams = {
1821
+ orderType: newOrderType || openOrder.orderType,
1822
+ marketType: openOrder.marketType,
1823
+ direction: openOrder.direction,
1824
+ baseAssetAmount: newBaseAmount || openOrder.baseAssetAmount,
1825
+ price: newLimitPrice || openOrder.price,
1826
+ marketIndex: openOrder.marketIndex,
1827
+ reduceOnly: openOrder.reduceOnly,
1828
+ postOnly: openOrder.postOnly,
1829
+ immediateOrCancel: openOrder.immediateOrCancel,
1830
+ triggerPrice: newTriggerPrice || openOrder.triggerPrice,
1831
+ triggerCondition: openOrder.triggerCondition,
1832
+ oraclePriceOffset: newOraclePriceOffset || openOrder.oraclePriceOffset,
1833
+ auctionDuration: openOrder.auctionDuration,
1834
+ maxTs: openOrder.maxTs,
1835
+ auctionStartPrice: openOrder.auctionStartPrice,
1836
+ auctionEndPrice: openOrder.auctionEndPrice,
1837
+ userOrderId: openOrder.userOrderId,
1838
+ };
1839
+ const placeOrderIx = isSpot
1840
+ ? await this.getPlaceSpotOrderIx(newOrderParams)
1841
+ : await this.getPlacePerpOrderIx(newOrderParams);
1842
+ const tx = new web3_js_1.Transaction();
1843
+ tx.add(web3_js_1.ComputeBudgetProgram.requestUnits({
1844
+ units: 1000000,
1845
+ additionalFee: 0,
1846
+ }));
1847
+ tx.add(cancelOrderIx);
1848
+ tx.add(placeOrderIx);
1849
+ const { txSig, slot } = await this.sendTransaction(tx, [], this.opts);
1850
+ if (isSpot) {
1851
+ this.spotMarketLastSlotCache.set(newOrderParams.marketIndex, slot);
1852
+ this.spotMarketLastSlotCache.set(numericConstants_1.QUOTE_SPOT_MARKET_INDEX, slot);
1853
+ }
1854
+ else {
1855
+ this.perpMarketLastSlotCache.set(newOrderParams.marketIndex, slot);
1856
+ }
1857
+ return txSig;
1858
+ }
1799
1859
  /**
1800
1860
  * Modifies an open order by closing it and replacing it with a new order.
1861
+ * @deprecated use modifyOrderByUserOrderId instead
1801
1862
  * @param userOrderId: The open order to modify
1802
1863
  * @param newBaseAmount: The new base amount for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
1803
1864
  * @param newLimitPice: The new limit price for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
@@ -1844,6 +1905,65 @@ class DriftClient {
1844
1905
  this.perpMarketLastSlotCache.set(newOrderParams.marketIndex, slot);
1845
1906
  return txSig;
1846
1907
  }
1908
+ /**
1909
+ * Modifies an open order by closing it and replacing it with a new order.
1910
+ * @param userOrderId: The open order to modify
1911
+ * @param newBaseAmount: The new base amount for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
1912
+ * @param newLimitPice: The new limit price for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
1913
+ * @param newOraclePriceOffset: The new oracle price offset for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
1914
+ * @param newOrderType: Optional - New order type for the order.
1915
+ * @param newTriggerPrice: Optional - Thew new trigger price for the order.
1916
+ * @param isSpot: Set to true if the order is a spot order
1917
+ * @returns
1918
+ */
1919
+ async modifyOrderByUserOrderId({ userOrderId, newBaseAmount, newLimitPrice, newOraclePriceOffset, newOrderType, newTriggerPrice, isSpot, }) {
1920
+ if (!newBaseAmount && !newLimitPrice && !newOraclePriceOffset) {
1921
+ throw new Error(`Must provide newBaseAmount or newLimitPrice or newOraclePriceOffset to modify order`);
1922
+ }
1923
+ const openOrder = this.getUser().getOrderByUserOrderId(userOrderId);
1924
+ if (!openOrder) {
1925
+ throw new Error(`No open order with user order id ${userOrderId.toString()}`);
1926
+ }
1927
+ const cancelOrderIx = await this.getCancelOrderByUserIdIx(userOrderId);
1928
+ const newOrderParams = {
1929
+ orderType: newOrderType || openOrder.orderType,
1930
+ marketType: openOrder.marketType,
1931
+ direction: openOrder.direction,
1932
+ baseAssetAmount: newBaseAmount || openOrder.baseAssetAmount,
1933
+ price: newLimitPrice || openOrder.price,
1934
+ marketIndex: openOrder.marketIndex,
1935
+ reduceOnly: openOrder.reduceOnly,
1936
+ postOnly: openOrder.postOnly,
1937
+ immediateOrCancel: openOrder.immediateOrCancel,
1938
+ triggerPrice: newTriggerPrice || openOrder.triggerPrice,
1939
+ triggerCondition: openOrder.triggerCondition,
1940
+ oraclePriceOffset: newOraclePriceOffset || openOrder.oraclePriceOffset,
1941
+ auctionDuration: openOrder.auctionDuration,
1942
+ maxTs: openOrder.maxTs,
1943
+ auctionStartPrice: openOrder.auctionStartPrice,
1944
+ auctionEndPrice: openOrder.auctionEndPrice,
1945
+ userOrderId: openOrder.userOrderId,
1946
+ };
1947
+ const placeOrderIx = isSpot
1948
+ ? await this.getPlaceSpotOrderIx(newOrderParams)
1949
+ : await this.getPlacePerpOrderIx(newOrderParams);
1950
+ const tx = new web3_js_1.Transaction();
1951
+ tx.add(web3_js_1.ComputeBudgetProgram.requestUnits({
1952
+ units: 1000000,
1953
+ additionalFee: 0,
1954
+ }));
1955
+ tx.add(cancelOrderIx);
1956
+ tx.add(placeOrderIx);
1957
+ const { txSig, slot } = await this.sendTransaction(tx, [], this.opts);
1958
+ if (isSpot) {
1959
+ this.spotMarketLastSlotCache.set(newOrderParams.marketIndex, slot);
1960
+ this.spotMarketLastSlotCache.set(numericConstants_1.QUOTE_SPOT_MARKET_INDEX, slot);
1961
+ }
1962
+ else {
1963
+ this.perpMarketLastSlotCache.set(newOrderParams.marketIndex, slot);
1964
+ }
1965
+ return txSig;
1966
+ }
1847
1967
  async settlePNLs(users, marketIndex) {
1848
1968
  const ixs = [];
1849
1969
  for (const { settleeUserAccountPublicKey, settleeUserAccount } of users) {
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.16.0-beta.2",
2
+ "version": "2.16.0-beta.3",
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;
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-beta.3",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "author": "crispheaney",
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
  );
@@ -3018,6 +3018,7 @@ export class DriftClient {
3018
3018
 
3019
3019
  /**
3020
3020
  * Modifies an open order by closing it and replacing it with a new order.
3021
+ * @deprecated use modifyOrder instead
3021
3022
  * @param orderId: The open order to modify
3022
3023
  * @param newBaseAmount: The new base amount for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
3023
3024
  * @param newLimitPice: The new limit price for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
@@ -3077,8 +3078,93 @@ export class DriftClient {
3077
3078
  return txSig;
3078
3079
  }
3079
3080
 
3081
+ /**
3082
+ * Modifies an open order (spot or perp) by closing it and replacing it with a new order.
3083
+ * @param orderId: The open order to modify
3084
+ * @param newBaseAmount: The new base amount for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
3085
+ * @param newLimitPice: The new limit price for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
3086
+ * @param newOraclePriceOffset: The new oracle price offset for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
3087
+ * @param newOrderType: Optional - New order type for the order.
3088
+ * @param newTriggerPrice: Optional - Thew new trigger price for the order.
3089
+ * @param isSpot: Optional - Set to true if the order is a spot order
3090
+ * @returns
3091
+ */
3092
+ public async modifyOrder({
3093
+ orderId,
3094
+ newBaseAmount,
3095
+ newLimitPrice,
3096
+ newOraclePriceOffset,
3097
+ newOrderType,
3098
+ newTriggerPrice,
3099
+ isSpot,
3100
+ }: {
3101
+ orderId: number;
3102
+ newBaseAmount?: BN;
3103
+ newLimitPrice?: BN;
3104
+ newOraclePriceOffset?: number;
3105
+ newOrderType?: OrderType;
3106
+ newTriggerPrice?: BN;
3107
+ isSpot?: boolean;
3108
+ }): Promise<TransactionSignature> {
3109
+ if (!newBaseAmount && !newLimitPrice && !newOraclePriceOffset) {
3110
+ throw new Error(
3111
+ `Must provide newBaseAmount or newLimitPrice or newOraclePriceOffset to modify order`
3112
+ );
3113
+ }
3114
+
3115
+ const openOrder = this.getUser().getOrder(orderId);
3116
+ if (!openOrder) {
3117
+ throw new Error(`No open order with id ${orderId.toString()}`);
3118
+ }
3119
+ const cancelOrderIx = await this.getCancelOrderIx(orderId);
3120
+
3121
+ const newOrderParams: OptionalOrderParams = {
3122
+ orderType: newOrderType || openOrder.orderType,
3123
+ marketType: openOrder.marketType,
3124
+ direction: openOrder.direction,
3125
+ baseAssetAmount: newBaseAmount || openOrder.baseAssetAmount,
3126
+ price: newLimitPrice || openOrder.price,
3127
+ marketIndex: openOrder.marketIndex,
3128
+ reduceOnly: openOrder.reduceOnly,
3129
+ postOnly: openOrder.postOnly,
3130
+ immediateOrCancel: openOrder.immediateOrCancel,
3131
+ triggerPrice: newTriggerPrice || openOrder.triggerPrice,
3132
+ triggerCondition: openOrder.triggerCondition,
3133
+ oraclePriceOffset: newOraclePriceOffset || openOrder.oraclePriceOffset,
3134
+ auctionDuration: openOrder.auctionDuration,
3135
+ maxTs: openOrder.maxTs,
3136
+ auctionStartPrice: openOrder.auctionStartPrice,
3137
+ auctionEndPrice: openOrder.auctionEndPrice,
3138
+ userOrderId: openOrder.userOrderId,
3139
+ };
3140
+ const placeOrderIx = isSpot
3141
+ ? await this.getPlaceSpotOrderIx(newOrderParams)
3142
+ : await this.getPlacePerpOrderIx(newOrderParams);
3143
+
3144
+ const tx = new Transaction();
3145
+ tx.add(
3146
+ ComputeBudgetProgram.requestUnits({
3147
+ units: 1_000_000,
3148
+ additionalFee: 0,
3149
+ })
3150
+ );
3151
+ tx.add(cancelOrderIx);
3152
+ tx.add(placeOrderIx);
3153
+ const { txSig, slot } = await this.sendTransaction(tx, [], this.opts);
3154
+
3155
+ if (isSpot) {
3156
+ this.spotMarketLastSlotCache.set(newOrderParams.marketIndex, slot);
3157
+ this.spotMarketLastSlotCache.set(QUOTE_SPOT_MARKET_INDEX, slot);
3158
+ } else {
3159
+ this.perpMarketLastSlotCache.set(newOrderParams.marketIndex, slot);
3160
+ }
3161
+
3162
+ return txSig;
3163
+ }
3164
+
3080
3165
  /**
3081
3166
  * Modifies an open order by closing it and replacing it with a new order.
3167
+ * @deprecated use modifyOrderByUserOrderId instead
3082
3168
  * @param userOrderId: The open order to modify
3083
3169
  * @param newBaseAmount: The new base amount for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
3084
3170
  * @param newLimitPice: The new limit price for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
@@ -3140,6 +3226,92 @@ export class DriftClient {
3140
3226
  return txSig;
3141
3227
  }
3142
3228
 
3229
+ /**
3230
+ * Modifies an open order by closing it and replacing it with a new order.
3231
+ * @param userOrderId: The open order to modify
3232
+ * @param newBaseAmount: The new base amount for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
3233
+ * @param newLimitPice: The new limit price for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
3234
+ * @param newOraclePriceOffset: The new oracle price offset for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
3235
+ * @param newOrderType: Optional - New order type for the order.
3236
+ * @param newTriggerPrice: Optional - Thew new trigger price for the order.
3237
+ * @param isSpot: Set to true if the order is a spot order
3238
+ * @returns
3239
+ */
3240
+ public async modifyOrderByUserOrderId({
3241
+ userOrderId,
3242
+ newBaseAmount,
3243
+ newLimitPrice,
3244
+ newOraclePriceOffset,
3245
+ newOrderType,
3246
+ newTriggerPrice,
3247
+ isSpot,
3248
+ }: {
3249
+ userOrderId: number;
3250
+ newBaseAmount?: BN;
3251
+ newLimitPrice?: BN;
3252
+ newOraclePriceOffset?: number;
3253
+ newOrderType?: OrderType;
3254
+ newTriggerPrice?: BN;
3255
+ isSpot?: boolean;
3256
+ }): Promise<TransactionSignature> {
3257
+ if (!newBaseAmount && !newLimitPrice && !newOraclePriceOffset) {
3258
+ throw new Error(
3259
+ `Must provide newBaseAmount or newLimitPrice or newOraclePriceOffset to modify order`
3260
+ );
3261
+ }
3262
+
3263
+ const openOrder = this.getUser().getOrderByUserOrderId(userOrderId);
3264
+ if (!openOrder) {
3265
+ throw new Error(
3266
+ `No open order with user order id ${userOrderId.toString()}`
3267
+ );
3268
+ }
3269
+ const cancelOrderIx = await this.getCancelOrderByUserIdIx(userOrderId);
3270
+
3271
+ const newOrderParams: OptionalOrderParams = {
3272
+ orderType: newOrderType || openOrder.orderType,
3273
+ marketType: openOrder.marketType,
3274
+ direction: openOrder.direction,
3275
+ baseAssetAmount: newBaseAmount || openOrder.baseAssetAmount,
3276
+ price: newLimitPrice || openOrder.price,
3277
+ marketIndex: openOrder.marketIndex,
3278
+ reduceOnly: openOrder.reduceOnly,
3279
+ postOnly: openOrder.postOnly,
3280
+ immediateOrCancel: openOrder.immediateOrCancel,
3281
+ triggerPrice: newTriggerPrice || openOrder.triggerPrice,
3282
+ triggerCondition: openOrder.triggerCondition,
3283
+ oraclePriceOffset: newOraclePriceOffset || openOrder.oraclePriceOffset,
3284
+ auctionDuration: openOrder.auctionDuration,
3285
+ maxTs: openOrder.maxTs,
3286
+ auctionStartPrice: openOrder.auctionStartPrice,
3287
+ auctionEndPrice: openOrder.auctionEndPrice,
3288
+ userOrderId: openOrder.userOrderId,
3289
+ };
3290
+ const placeOrderIx = isSpot
3291
+ ? await this.getPlaceSpotOrderIx(newOrderParams)
3292
+ : await this.getPlacePerpOrderIx(newOrderParams);
3293
+
3294
+ const tx = new Transaction();
3295
+ tx.add(
3296
+ ComputeBudgetProgram.requestUnits({
3297
+ units: 1_000_000,
3298
+ additionalFee: 0,
3299
+ })
3300
+ );
3301
+ tx.add(cancelOrderIx);
3302
+ tx.add(placeOrderIx);
3303
+ const { txSig, slot } = await this.sendTransaction(tx, [], this.opts);
3304
+
3305
+ if (isSpot) {
3306
+ this.spotMarketLastSlotCache.set(newOrderParams.marketIndex, slot);
3307
+ this.spotMarketLastSlotCache.set(QUOTE_SPOT_MARKET_INDEX, slot);
3308
+ } else {
3309
+ this.perpMarketLastSlotCache.set(newOrderParams.marketIndex, slot);
3310
+ }
3311
+
3312
+ return txSig;
3313
+ }
3314
+
3143
3315
  public async settlePNLs(
3144
3316
  users: {
3145
3317
  settleeUserAccountPublicKey: PublicKey;
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.16.0-beta.2",
2
+ "version": "2.16.0-beta.3",
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
  };
@@ -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(