@drift-labs/sdk 2.12.0-beta.2 → 2.12.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.
Files changed (57) hide show
  1. package/lib/accounts/bulkAccountLoader.js +3 -3
  2. package/lib/accounts/fetch.js +2 -2
  3. package/lib/accounts/pollingDriftClientAccountSubscriber.js +7 -7
  4. package/lib/accounts/pollingTokenAccountSubscriber.js +2 -2
  5. package/lib/accounts/pollingUserAccountSubscriber.js +2 -2
  6. package/lib/accounts/pollingUserStatsAccountSubscriber.js +2 -2
  7. package/lib/accounts/webSocketAccountSubscriber.js +1 -1
  8. package/lib/accounts/webSocketDriftClientAccountSubscriber.js +3 -3
  9. package/lib/addresses/marketAddresses.js +1 -1
  10. package/lib/addresses/pda.js +1 -5
  11. package/lib/adminClient.js +57 -61
  12. package/lib/dlob/DLOB.d.ts +8 -8
  13. package/lib/dlob/DLOB.js +91 -84
  14. package/lib/dlob/DLOBNode.js +7 -7
  15. package/lib/dlob/NodeList.js +2 -2
  16. package/lib/driftClient.d.ts +3 -3
  17. package/lib/driftClient.js +83 -87
  18. package/lib/events/eventSubscriber.js +2 -2
  19. package/lib/events/pollingLogProvider.js +1 -1
  20. package/lib/examples/makeTradeExample.js +9 -9
  21. package/lib/factory/bigNum.js +9 -9
  22. package/lib/factory/oracleClient.js +2 -2
  23. package/lib/idl/drift.json +1 -1
  24. package/lib/index.js +1 -5
  25. package/lib/math/amm.js +23 -23
  26. package/lib/math/auction.js +6 -6
  27. package/lib/math/exchangeStatus.js +2 -2
  28. package/lib/math/funding.js +2 -2
  29. package/lib/math/margin.js +5 -5
  30. package/lib/math/market.js +12 -12
  31. package/lib/math/oracles.js +1 -1
  32. package/lib/math/orders.js +23 -23
  33. package/lib/math/position.js +5 -5
  34. package/lib/math/repeg.js +1 -1
  35. package/lib/math/spotBalance.js +7 -7
  36. package/lib/math/spotPosition.js +3 -3
  37. package/lib/math/trade.d.ts +35 -3
  38. package/lib/math/trade.js +225 -47
  39. package/lib/oracles/oracleClientCache.js +1 -1
  40. package/lib/oracles/pythClient.js +1 -1
  41. package/lib/tokenFaucet.js +1 -5
  42. package/lib/tx/retryTxSender.js +1 -1
  43. package/lib/tx/utils.d.ts +1 -1
  44. package/lib/tx/utils.js +7 -4
  45. package/lib/types.d.ts +4 -0
  46. package/lib/user.js +47 -47
  47. package/lib/userMap/userMap.js +1 -1
  48. package/lib/userMap/userStatsMap.js +3 -3
  49. package/lib/userStats.js +2 -2
  50. package/package.json +1 -1
  51. package/src/dlob/DLOB.ts +34 -26
  52. package/src/driftClient.ts +11 -4
  53. package/src/idl/drift.json +1 -1
  54. package/src/math/trade.ts +322 -16
  55. package/src/tx/utils.ts +11 -3
  56. package/src/types.ts +5 -0
  57. package/tests/dlob/test.ts +307 -38
package/lib/math/trade.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.calculateEstimatedPerpEntryPrice = exports.calculateTargetPriceTrade = exports.calculateTradeAcquiredAmounts = exports.calculateTradeSlippage = void 0;
3
+ exports.calculateEstimatedSpotEntryPrice = exports.calculateEstimatedPerpEntryPrice = exports.calculateTargetPriceTrade = exports.calculateTradeAcquiredAmounts = exports.calculateTradeSlippage = void 0;
4
4
  const types_1 = require("../types");
5
5
  const anchor_1 = require("@project-serum/anchor");
6
6
  const assert_1 = require("../assert/assert");
@@ -30,15 +30,15 @@ const MAXPCT = new anchor_1.BN(1000); //percentage units are [0,1000] => [0,1]
30
30
  function calculateTradeSlippage(direction, amount, market, inputAssetType = 'quote', oraclePriceData, useSpread = true) {
31
31
  let oldPrice;
32
32
  if (useSpread && market.amm.baseSpread > 0) {
33
- if ((0, types_2.isVariant)(direction, 'long')) {
34
- oldPrice = (0, market_1.calculateAskPrice)(market, oraclePriceData);
33
+ if (types_2.isVariant(direction, 'long')) {
34
+ oldPrice = market_1.calculateAskPrice(market, oraclePriceData);
35
35
  }
36
36
  else {
37
- oldPrice = (0, market_1.calculateBidPrice)(market, oraclePriceData);
37
+ oldPrice = market_1.calculateBidPrice(market, oraclePriceData);
38
38
  }
39
39
  }
40
40
  else {
41
- oldPrice = (0, market_1.calculateReservePrice)(market, oraclePriceData);
41
+ oldPrice = market_1.calculateReservePrice(market, oraclePriceData);
42
42
  }
43
43
  if (amount.eq(numericConstants_1.ZERO)) {
44
44
  return [numericConstants_1.ZERO, numericConstants_1.ZERO, oldPrice, oldPrice];
@@ -50,7 +50,7 @@ function calculateTradeSlippage(direction, amount, market, inputAssetType = 'quo
50
50
  .div(acquiredBaseReserve.abs());
51
51
  let amm;
52
52
  if (useSpread && market.amm.baseSpread > 0) {
53
- const { baseAssetReserve, quoteAssetReserve, sqrtK, newPeg } = (0, amm_1.calculateUpdatedAMMSpreadReserves)(market.amm, direction, oraclePriceData);
53
+ const { baseAssetReserve, quoteAssetReserve, sqrtK, newPeg } = amm_1.calculateUpdatedAMMSpreadReserves(market.amm, direction, oraclePriceData);
54
54
  amm = {
55
55
  baseAssetReserve,
56
56
  quoteAssetReserve,
@@ -61,12 +61,12 @@ function calculateTradeSlippage(direction, amount, market, inputAssetType = 'quo
61
61
  else {
62
62
  amm = market.amm;
63
63
  }
64
- const newPrice = (0, amm_1.calculatePrice)(amm.baseAssetReserve.sub(acquiredBaseReserve), amm.quoteAssetReserve.sub(acquiredQuoteReserve), amm.pegMultiplier);
64
+ const newPrice = amm_1.calculatePrice(amm.baseAssetReserve.sub(acquiredBaseReserve), amm.quoteAssetReserve.sub(acquiredQuoteReserve), amm.pegMultiplier);
65
65
  if (direction == types_1.PositionDirection.SHORT) {
66
- (0, assert_1.assert)(newPrice.lte(oldPrice));
66
+ assert_1.assert(newPrice.lte(oldPrice));
67
67
  }
68
68
  else {
69
- (0, assert_1.assert)(oldPrice.lte(newPrice));
69
+ assert_1.assert(oldPrice.lte(newPrice));
70
70
  }
71
71
  const pctMaxSlippage = newPrice
72
72
  .sub(oldPrice)
@@ -96,10 +96,10 @@ function calculateTradeAcquiredAmounts(direction, amount, market, inputAssetType
96
96
  if (amount.eq(numericConstants_1.ZERO)) {
97
97
  return [numericConstants_1.ZERO, numericConstants_1.ZERO, numericConstants_1.ZERO];
98
98
  }
99
- const swapDirection = (0, amm_1.getSwapDirection)(inputAssetType, direction);
99
+ const swapDirection = amm_1.getSwapDirection(inputAssetType, direction);
100
100
  let amm;
101
101
  if (useSpread && market.amm.baseSpread > 0) {
102
- const { baseAssetReserve, quoteAssetReserve, sqrtK, newPeg } = (0, amm_1.calculateUpdatedAMMSpreadReserves)(market.amm, direction, oraclePriceData);
102
+ const { baseAssetReserve, quoteAssetReserve, sqrtK, newPeg } = amm_1.calculateUpdatedAMMSpreadReserves(market.amm, direction, oraclePriceData);
103
103
  amm = {
104
104
  baseAssetReserve,
105
105
  quoteAssetReserve,
@@ -110,10 +110,10 @@ function calculateTradeAcquiredAmounts(direction, amount, market, inputAssetType
110
110
  else {
111
111
  amm = market.amm;
112
112
  }
113
- const [newQuoteAssetReserve, newBaseAssetReserve] = (0, amm_1.calculateAmmReservesAfterSwap)(amm, inputAssetType, amount, swapDirection);
113
+ const [newQuoteAssetReserve, newBaseAssetReserve] = amm_1.calculateAmmReservesAfterSwap(amm, inputAssetType, amount, swapDirection);
114
114
  const acquiredBase = amm.baseAssetReserve.sub(newBaseAssetReserve);
115
115
  const acquiredQuote = amm.quoteAssetReserve.sub(newQuoteAssetReserve);
116
- const acquiredQuoteAssetAmount = (0, amm_1.calculateQuoteAssetAmountSwapped)(acquiredQuote.abs(), amm.pegMultiplier, swapDirection);
116
+ const acquiredQuoteAssetAmount = amm_1.calculateQuoteAssetAmountSwapped(acquiredQuote.abs(), amm.pegMultiplier, swapDirection);
117
117
  return [acquiredBase, acquiredQuote, acquiredQuoteAssetAmount];
118
118
  }
119
119
  exports.calculateTradeAcquiredAmounts = calculateTradeAcquiredAmounts;
@@ -135,12 +135,12 @@ exports.calculateTradeAcquiredAmounts = calculateTradeAcquiredAmounts;
135
135
  * ]
136
136
  */
137
137
  function calculateTargetPriceTrade(market, targetPrice, pct = MAXPCT, outputAssetType = 'quote', oraclePriceData, useSpread = true) {
138
- (0, assert_1.assert)(market.amm.baseAssetReserve.gt(numericConstants_1.ZERO));
139
- (0, assert_1.assert)(targetPrice.gt(numericConstants_1.ZERO));
140
- (0, assert_1.assert)(pct.lte(MAXPCT) && pct.gt(numericConstants_1.ZERO));
141
- const reservePriceBefore = (0, market_1.calculateReservePrice)(market, oraclePriceData);
142
- const bidPriceBefore = (0, market_1.calculateBidPrice)(market, oraclePriceData);
143
- const askPriceBefore = (0, market_1.calculateAskPrice)(market, oraclePriceData);
138
+ assert_1.assert(market.amm.baseAssetReserve.gt(numericConstants_1.ZERO));
139
+ assert_1.assert(targetPrice.gt(numericConstants_1.ZERO));
140
+ assert_1.assert(pct.lte(MAXPCT) && pct.gt(numericConstants_1.ZERO));
141
+ const reservePriceBefore = market_1.calculateReservePrice(market, oraclePriceData);
142
+ const bidPriceBefore = market_1.calculateBidPrice(market, oraclePriceData);
143
+ const askPriceBefore = market_1.calculateAskPrice(market, oraclePriceData);
144
144
  let direction;
145
145
  if (targetPrice.gt(reservePriceBefore)) {
146
146
  const priceGap = targetPrice.sub(reservePriceBefore);
@@ -160,7 +160,7 @@ function calculateTargetPriceTrade(market, targetPrice, pct = MAXPCT, outputAsse
160
160
  let quoteAssetReserveBefore;
161
161
  let peg = market.amm.pegMultiplier;
162
162
  if (useSpread && market.amm.baseSpread > 0) {
163
- const { baseAssetReserve, quoteAssetReserve, newPeg } = (0, amm_1.calculateUpdatedAMMSpreadReserves)(market.amm, direction, oraclePriceData);
163
+ const { baseAssetReserve, quoteAssetReserve, newPeg } = amm_1.calculateUpdatedAMMSpreadReserves(market.amm, direction, oraclePriceData);
164
164
  baseAssetReserveBefore = baseAssetReserve;
165
165
  quoteAssetReserveBefore = quoteAssetReserve;
166
166
  peg = newPeg;
@@ -190,9 +190,9 @@ function calculateTargetPriceTrade(market, targetPrice, pct = MAXPCT, outputAsse
190
190
  }
191
191
  else if (reservePriceBefore.gt(targetPrice)) {
192
192
  // overestimate y2
193
- baseAssetReserveAfter = (0, utils_1.squareRootBN)(k.div(targetPrice).mul(peg).div(numericConstants_1.PEG_PRECISION).sub(biasModifier)).sub(new anchor_1.BN(1));
193
+ baseAssetReserveAfter = utils_1.squareRootBN(k.div(targetPrice).mul(peg).div(numericConstants_1.PEG_PRECISION).sub(biasModifier)).sub(new anchor_1.BN(1));
194
194
  quoteAssetReserveAfter = k.div(numericConstants_1.PRICE_PRECISION).div(baseAssetReserveAfter);
195
- markPriceAfter = (0, amm_1.calculatePrice)(baseAssetReserveAfter, quoteAssetReserveAfter, peg);
195
+ markPriceAfter = amm_1.calculatePrice(baseAssetReserveAfter, quoteAssetReserveAfter, peg);
196
196
  direction = types_1.PositionDirection.SHORT;
197
197
  tradeSize = quoteAssetReserveBefore
198
198
  .sub(quoteAssetReserveAfter)
@@ -203,9 +203,9 @@ function calculateTargetPriceTrade(market, targetPrice, pct = MAXPCT, outputAsse
203
203
  }
204
204
  else if (reservePriceBefore.lt(targetPrice)) {
205
205
  // underestimate y2
206
- baseAssetReserveAfter = (0, utils_1.squareRootBN)(k.div(targetPrice).mul(peg).div(numericConstants_1.PEG_PRECISION).add(biasModifier)).add(new anchor_1.BN(1));
206
+ baseAssetReserveAfter = utils_1.squareRootBN(k.div(targetPrice).mul(peg).div(numericConstants_1.PEG_PRECISION).add(biasModifier)).add(new anchor_1.BN(1));
207
207
  quoteAssetReserveAfter = k.div(numericConstants_1.PRICE_PRECISION).div(baseAssetReserveAfter);
208
- markPriceAfter = (0, amm_1.calculatePrice)(baseAssetReserveAfter, quoteAssetReserveAfter, peg);
208
+ markPriceAfter = amm_1.calculatePrice(baseAssetReserveAfter, quoteAssetReserveAfter, peg);
209
209
  direction = types_1.PositionDirection.LONG;
210
210
  tradeSize = quoteAssetReserveAfter
211
211
  .sub(quoteAssetReserveBefore)
@@ -232,8 +232,8 @@ function calculateTargetPriceTrade(market, targetPrice, pct = MAXPCT, outputAsse
232
232
  .mul(numericConstants_1.AMM_TO_QUOTE_PRECISION_RATIO)
233
233
  .mul(numericConstants_1.PRICE_PRECISION)
234
234
  .div(baseSize.abs());
235
- (0, assert_1.assert)(tp1.sub(tp2).lte(originalDiff), 'Target Price Calculation incorrect');
236
- (0, assert_1.assert)(tp2.lte(tp1) || tp2.sub(tp1).abs() < 100000, 'Target Price Calculation incorrect' +
235
+ assert_1.assert(tp1.sub(tp2).lte(originalDiff), 'Target Price Calculation incorrect');
236
+ assert_1.assert(tp2.lte(tp1) || tp2.sub(tp1).abs() < 100000, 'Target Price Calculation incorrect' +
237
237
  tp2.toString() +
238
238
  '>=' +
239
239
  tp1.toString() +
@@ -258,39 +258,59 @@ exports.calculateTargetPriceTrade = calculateTargetPriceTrade;
258
258
  * @param oraclePriceData
259
259
  * @param dlob
260
260
  * @param slot
261
- * @param minPerpAuctionDuration
261
+ * @param usersToSkip
262
262
  */
263
- function calculateEstimatedPerpEntryPrice(assetType, amount, direction, market, oraclePriceData, dlob, slot, minPerpAuctionDuration) {
263
+ function calculateEstimatedPerpEntryPrice(assetType, amount, direction, market, oraclePriceData, dlob, slot, usersToSkip = new Map()) {
264
264
  if (amount.eq(numericConstants_1.ZERO)) {
265
- return [numericConstants_1.ZERO, numericConstants_1.ZERO];
265
+ return {
266
+ entryPrice: numericConstants_1.ZERO,
267
+ priceImpact: numericConstants_1.ZERO,
268
+ bestPrice: numericConstants_1.ZERO,
269
+ worstPrice: numericConstants_1.ZERO,
270
+ baseFilled: numericConstants_1.ZERO,
271
+ quoteFilled: numericConstants_1.ZERO,
272
+ };
266
273
  }
267
- const takerIsLong = (0, types_2.isVariant)(direction, 'long');
268
- const limitOrders = dlob[takerIsLong ? 'getRestingLimitAsks' : 'getRestingLimitBids'](market.marketIndex, slot, types_1.MarketType.PERP, oraclePriceData, minPerpAuctionDuration);
269
- const swapDirection = (0, amm_1.getSwapDirection)(assetType, direction);
270
- const { baseAssetReserve, quoteAssetReserve, sqrtK, newPeg } = (0, amm_1.calculateUpdatedAMMSpreadReserves)(market.amm, direction, oraclePriceData);
274
+ const takerIsLong = types_2.isVariant(direction, 'long');
275
+ const limitOrders = dlob[takerIsLong ? 'getMakerLimitAsks' : 'getMakerLimitBids'](market.marketIndex, slot, types_1.MarketType.PERP, oraclePriceData, takerIsLong
276
+ ? market_1.calculateBidPrice(market, oraclePriceData)
277
+ : market_1.calculateAskPrice(market, oraclePriceData));
278
+ const swapDirection = amm_1.getSwapDirection(assetType, direction);
279
+ const { baseAssetReserve, quoteAssetReserve, sqrtK, newPeg } = amm_1.calculateUpdatedAMMSpreadReserves(market.amm, direction, oraclePriceData);
271
280
  const amm = {
272
281
  baseAssetReserve,
273
282
  quoteAssetReserve,
274
283
  sqrtK: sqrtK,
275
284
  pegMultiplier: newPeg,
276
285
  };
286
+ const [ammBids, ammAsks] = amm_1.calculateMarketOpenBidAsk(market.amm.baseAssetReserve, market.amm.minBaseAssetReserve, market.amm.maxBaseAssetReserve, market.amm.orderStepSize);
287
+ let ammLiquidity;
288
+ if (assetType === 'base') {
289
+ ammLiquidity = takerIsLong ? ammAsks.abs() : ammBids;
290
+ }
291
+ else {
292
+ const [afterSwapQuoteReserves, _] = amm_1.calculateAmmReservesAfterSwap(amm, 'base', takerIsLong ? ammAsks.abs() : ammBids, amm_1.getSwapDirection('base', direction));
293
+ ammLiquidity = amm_1.calculateQuoteAssetAmountSwapped(amm.quoteAssetReserve.sub(afterSwapQuoteReserves).abs(), amm.pegMultiplier, swapDirection);
294
+ }
277
295
  const invariant = amm.sqrtK.mul(amm.sqrtK);
278
- let initialPrice = (0, amm_1.calculatePrice)(amm.baseAssetReserve, amm.quoteAssetReserve, amm.pegMultiplier);
296
+ let bestPrice = amm_1.calculatePrice(amm.baseAssetReserve, amm.quoteAssetReserve, amm.pegMultiplier);
279
297
  let cumulativeBaseFilled = numericConstants_1.ZERO;
280
298
  let cumulativeQuoteFilled = numericConstants_1.ZERO;
281
299
  let limitOrder = limitOrders.next().value;
282
300
  if (limitOrder) {
283
301
  const limitOrderPrice = limitOrder.getPrice(oraclePriceData, slot);
284
- initialPrice = takerIsLong
285
- ? anchor_1.BN.min(limitOrderPrice, initialPrice)
286
- : anchor_1.BN.max(limitOrderPrice, initialPrice);
302
+ bestPrice = takerIsLong
303
+ ? anchor_1.BN.min(limitOrderPrice, bestPrice)
304
+ : anchor_1.BN.max(limitOrderPrice, bestPrice);
287
305
  }
306
+ let worstPrice = bestPrice;
288
307
  if (assetType === 'base') {
289
- while (!cumulativeBaseFilled.eq(amount)) {
308
+ while (!cumulativeBaseFilled.eq(amount) &&
309
+ (ammLiquidity.gt(numericConstants_1.ZERO) || limitOrder)) {
290
310
  const limitOrderPrice = limitOrder === null || limitOrder === void 0 ? void 0 : limitOrder.getPrice(oraclePriceData, slot);
291
311
  let maxAmmFill;
292
312
  if (limitOrderPrice) {
293
- const newBaseReserves = (0, utils_1.squareRootBN)(invariant
313
+ const newBaseReserves = utils_1.squareRootBN(invariant
294
314
  .mul(numericConstants_1.PRICE_PRECISION)
295
315
  .mul(amm.pegMultiplier)
296
316
  .div(limitOrderPrice)
@@ -303,22 +323,29 @@ function calculateEstimatedPerpEntryPrice(assetType, amount, direction, market,
303
323
  else {
304
324
  maxAmmFill = amount.sub(cumulativeBaseFilled);
305
325
  }
326
+ maxAmmFill = anchor_1.BN.min(maxAmmFill, ammLiquidity);
306
327
  if (maxAmmFill.gt(numericConstants_1.ZERO)) {
307
328
  const baseFilled = anchor_1.BN.min(amount.sub(cumulativeBaseFilled), maxAmmFill);
308
- const [afterSwapQuoteReserves, afterSwapBaseReserves] = (0, amm_1.calculateAmmReservesAfterSwap)(amm, 'base', baseFilled, swapDirection);
309
- const quoteFilled = (0, amm_1.calculateQuoteAssetAmountSwapped)(amm.quoteAssetReserve.sub(afterSwapQuoteReserves).abs(), amm.pegMultiplier, swapDirection);
329
+ const [afterSwapQuoteReserves, afterSwapBaseReserves] = amm_1.calculateAmmReservesAfterSwap(amm, 'base', baseFilled, swapDirection);
330
+ ammLiquidity = ammLiquidity.sub(baseFilled);
331
+ const quoteFilled = amm_1.calculateQuoteAssetAmountSwapped(amm.quoteAssetReserve.sub(afterSwapQuoteReserves).abs(), amm.pegMultiplier, swapDirection);
310
332
  cumulativeBaseFilled = cumulativeBaseFilled.add(baseFilled);
311
333
  cumulativeQuoteFilled = cumulativeQuoteFilled.add(quoteFilled);
312
334
  amm.baseAssetReserve = afterSwapBaseReserves;
313
335
  amm.quoteAssetReserve = afterSwapQuoteReserves;
336
+ worstPrice = amm_1.calculatePrice(amm.baseAssetReserve, amm.quoteAssetReserve, amm.pegMultiplier);
314
337
  if (cumulativeBaseFilled.eq(amount)) {
315
338
  break;
316
339
  }
317
340
  }
341
+ if (limitOrder && usersToSkip.has(limitOrder.userAccount)) {
342
+ continue;
343
+ }
318
344
  const baseFilled = anchor_1.BN.min(limitOrder.order.baseAssetAmount.sub(limitOrder.order.baseAssetAmountFilled), amount.sub(cumulativeBaseFilled));
319
345
  const quoteFilled = baseFilled.mul(limitOrderPrice).div(numericConstants_1.BASE_PRECISION);
320
346
  cumulativeBaseFilled = cumulativeBaseFilled.add(baseFilled);
321
347
  cumulativeQuoteFilled = cumulativeQuoteFilled.add(quoteFilled);
348
+ worstPrice = limitOrderPrice;
322
349
  if (cumulativeBaseFilled.eq(amount)) {
323
350
  break;
324
351
  }
@@ -326,11 +353,12 @@ function calculateEstimatedPerpEntryPrice(assetType, amount, direction, market,
326
353
  }
327
354
  }
328
355
  else {
329
- while (!cumulativeQuoteFilled.eq(amount)) {
356
+ while (!cumulativeQuoteFilled.eq(amount) &&
357
+ (ammLiquidity.gt(numericConstants_1.ZERO) || limitOrder)) {
330
358
  const limitOrderPrice = limitOrder === null || limitOrder === void 0 ? void 0 : limitOrder.getPrice(oraclePriceData, slot);
331
359
  let maxAmmFill;
332
360
  if (limitOrderPrice) {
333
- const newQuoteReserves = (0, utils_1.squareRootBN)(invariant
361
+ const newQuoteReserves = utils_1.squareRootBN(invariant
334
362
  .mul(numericConstants_1.PEG_PRECISION)
335
363
  .mul(limitOrderPrice)
336
364
  .div(amm.pegMultiplier)
@@ -343,9 +371,11 @@ function calculateEstimatedPerpEntryPrice(assetType, amount, direction, market,
343
371
  else {
344
372
  maxAmmFill = amount.sub(cumulativeQuoteFilled);
345
373
  }
374
+ maxAmmFill = anchor_1.BN.min(maxAmmFill, ammLiquidity);
346
375
  if (maxAmmFill.gt(numericConstants_1.ZERO)) {
347
376
  const quoteFilled = anchor_1.BN.min(amount.sub(cumulativeQuoteFilled), maxAmmFill);
348
- const [afterSwapQuoteReserves, afterSwapBaseReserves] = (0, amm_1.calculateAmmReservesAfterSwap)(amm, 'quote', quoteFilled, swapDirection);
377
+ const [afterSwapQuoteReserves, afterSwapBaseReserves] = amm_1.calculateAmmReservesAfterSwap(amm, 'quote', quoteFilled, swapDirection);
378
+ ammLiquidity = ammLiquidity.sub(quoteFilled);
349
379
  const baseFilled = afterSwapBaseReserves
350
380
  .sub(amm.baseAssetReserve)
351
381
  .abs();
@@ -353,10 +383,14 @@ function calculateEstimatedPerpEntryPrice(assetType, amount, direction, market,
353
383
  cumulativeQuoteFilled = cumulativeQuoteFilled.add(quoteFilled);
354
384
  amm.baseAssetReserve = afterSwapBaseReserves;
355
385
  amm.quoteAssetReserve = afterSwapQuoteReserves;
386
+ worstPrice = amm_1.calculatePrice(amm.baseAssetReserve, amm.quoteAssetReserve, amm.pegMultiplier);
356
387
  if (cumulativeQuoteFilled.eq(amount)) {
357
388
  break;
358
389
  }
359
390
  }
391
+ if (limitOrder && usersToSkip.has(limitOrder.userAccount)) {
392
+ continue;
393
+ }
360
394
  const quoteFilled = anchor_1.BN.min(limitOrder.order.baseAssetAmount
361
395
  .sub(limitOrder.order.baseAssetAmountFilled)
362
396
  .mul(limitOrderPrice)
@@ -364,6 +398,7 @@ function calculateEstimatedPerpEntryPrice(assetType, amount, direction, market,
364
398
  const baseFilled = quoteFilled.mul(numericConstants_1.BASE_PRECISION).div(limitOrderPrice);
365
399
  cumulativeBaseFilled = cumulativeBaseFilled.add(baseFilled);
366
400
  cumulativeQuoteFilled = cumulativeQuoteFilled.add(quoteFilled);
401
+ worstPrice = limitOrderPrice;
367
402
  if (cumulativeQuoteFilled.eq(amount)) {
368
403
  break;
369
404
  }
@@ -374,10 +409,153 @@ function calculateEstimatedPerpEntryPrice(assetType, amount, direction, market,
374
409
  .mul(numericConstants_1.BASE_PRECISION)
375
410
  .div(cumulativeBaseFilled);
376
411
  const priceImpact = entryPrice
377
- .sub(initialPrice)
412
+ .sub(bestPrice)
378
413
  .mul(numericConstants_1.PRICE_PRECISION)
379
- .div(initialPrice)
414
+ .div(bestPrice)
380
415
  .abs();
381
- return [entryPrice, priceImpact];
416
+ return {
417
+ entryPrice,
418
+ priceImpact,
419
+ bestPrice,
420
+ worstPrice,
421
+ baseFilled: cumulativeBaseFilled,
422
+ quoteFilled: cumulativeQuoteFilled,
423
+ };
382
424
  }
383
425
  exports.calculateEstimatedPerpEntryPrice = calculateEstimatedPerpEntryPrice;
426
+ /**
427
+ * Calculates the estimated entry price and price impact of order, in base or quote
428
+ * Price impact is based on the difference between the entry price and the best bid/ask price (whether it's dlob or serum)
429
+ *
430
+ * @param assetType
431
+ * @param amount
432
+ * @param direction
433
+ * @param market
434
+ * @param oraclePriceData
435
+ * @param dlob
436
+ * @param serumBids
437
+ * @param serumAsks
438
+ * @param slot
439
+ * @param usersToSkip
440
+ */
441
+ function calculateEstimatedSpotEntryPrice(assetType, amount, direction, market, oraclePriceData, dlob, serumBids, serumAsks, slot, usersToSkip = new Map()) {
442
+ if (amount.eq(numericConstants_1.ZERO)) {
443
+ return {
444
+ entryPrice: numericConstants_1.ZERO,
445
+ priceImpact: numericConstants_1.ZERO,
446
+ bestPrice: numericConstants_1.ZERO,
447
+ worstPrice: numericConstants_1.ZERO,
448
+ baseFilled: numericConstants_1.ZERO,
449
+ quoteFilled: numericConstants_1.ZERO,
450
+ };
451
+ }
452
+ const basePrecision = new anchor_1.BN(Math.pow(10, market.decimals));
453
+ const takerIsLong = types_2.isVariant(direction, 'long');
454
+ const dlobLimitOrders = dlob[takerIsLong ? 'getMakerLimitAsks' : 'getMakerLimitBids'](market.marketIndex, slot, types_1.MarketType.SPOT, oraclePriceData);
455
+ const serumLimitOrders = takerIsLong
456
+ ? serumAsks.getL2(100)
457
+ : serumBids.getL2(100);
458
+ let cumulativeBaseFilled = numericConstants_1.ZERO;
459
+ let cumulativeQuoteFilled = numericConstants_1.ZERO;
460
+ let dlobLimitOrder = dlobLimitOrders.next().value;
461
+ let serumLimitOrder = serumLimitOrders.shift();
462
+ const dlobLimitOrderPrice = dlobLimitOrder === null || dlobLimitOrder === void 0 ? void 0 : dlobLimitOrder.getPrice(oraclePriceData, slot);
463
+ const serumLimitOrderPrice = serumLimitOrder
464
+ ? new anchor_1.BN(serumLimitOrder[0] * numericConstants_1.PRICE_PRECISION.toNumber())
465
+ : undefined;
466
+ const bestPrice = takerIsLong
467
+ ? anchor_1.BN.min(serumLimitOrderPrice || numericConstants_1.BN_MAX, dlobLimitOrderPrice || numericConstants_1.BN_MAX)
468
+ : anchor_1.BN.max(serumLimitOrderPrice || numericConstants_1.ZERO, dlobLimitOrderPrice || numericConstants_1.ZERO);
469
+ let worstPrice = bestPrice;
470
+ if (assetType === 'base') {
471
+ while (!cumulativeBaseFilled.eq(amount) &&
472
+ (dlobLimitOrder || serumLimitOrder)) {
473
+ const dlobLimitOrderPrice = dlobLimitOrder === null || dlobLimitOrder === void 0 ? void 0 : dlobLimitOrder.getPrice(oraclePriceData, slot);
474
+ const serumLimitOrderPrice = serumLimitOrder
475
+ ? new anchor_1.BN(serumLimitOrder[0] * numericConstants_1.PRICE_PRECISION.toNumber())
476
+ : undefined;
477
+ const useSerum = takerIsLong
478
+ ? (serumLimitOrderPrice || numericConstants_1.BN_MAX).lt(dlobLimitOrderPrice || numericConstants_1.BN_MAX)
479
+ : (serumLimitOrderPrice || numericConstants_1.ZERO).gt(dlobLimitOrderPrice || numericConstants_1.ZERO);
480
+ if (!useSerum) {
481
+ if (dlobLimitOrder && usersToSkip.has(dlobLimitOrder.userAccount)) {
482
+ continue;
483
+ }
484
+ const baseFilled = anchor_1.BN.min(dlobLimitOrder.order.baseAssetAmount.sub(dlobLimitOrder.order.baseAssetAmountFilled), amount.sub(cumulativeBaseFilled));
485
+ const quoteFilled = baseFilled
486
+ .mul(dlobLimitOrderPrice)
487
+ .div(basePrecision);
488
+ cumulativeBaseFilled = cumulativeBaseFilled.add(baseFilled);
489
+ cumulativeQuoteFilled = cumulativeQuoteFilled.add(quoteFilled);
490
+ worstPrice = dlobLimitOrder;
491
+ dlobLimitOrder = dlobLimitOrders.next().value;
492
+ }
493
+ else {
494
+ const baseFilled = anchor_1.BN.min(new anchor_1.BN(serumLimitOrder[1] * basePrecision.toNumber()), amount.sub(cumulativeBaseFilled));
495
+ const quoteFilled = baseFilled
496
+ .mul(serumLimitOrderPrice)
497
+ .div(basePrecision);
498
+ cumulativeBaseFilled = cumulativeBaseFilled.add(baseFilled);
499
+ cumulativeQuoteFilled = cumulativeQuoteFilled.add(quoteFilled);
500
+ worstPrice = serumLimitOrderPrice;
501
+ serumLimitOrder = serumLimitOrders.shift();
502
+ }
503
+ }
504
+ }
505
+ else {
506
+ while (!cumulativeQuoteFilled.eq(amount) &&
507
+ (dlobLimitOrder || serumLimitOrder)) {
508
+ const dlobLimitOrderPrice = dlobLimitOrder === null || dlobLimitOrder === void 0 ? void 0 : dlobLimitOrder.getPrice(oraclePriceData, slot);
509
+ const serumLimitOrderPrice = serumLimitOrder
510
+ ? new anchor_1.BN(serumLimitOrder[0] * numericConstants_1.PRICE_PRECISION.toNumber())
511
+ : undefined;
512
+ const useSerum = takerIsLong
513
+ ? (serumLimitOrderPrice || numericConstants_1.BN_MAX).lt(dlobLimitOrderPrice || numericConstants_1.BN_MAX)
514
+ : (serumLimitOrderPrice || numericConstants_1.ZERO).gt(dlobLimitOrderPrice || numericConstants_1.ZERO);
515
+ if (!useSerum) {
516
+ if (dlobLimitOrder && usersToSkip.has(dlobLimitOrder.userAccount)) {
517
+ continue;
518
+ }
519
+ const quoteFilled = anchor_1.BN.min(dlobLimitOrder.order.baseAssetAmount
520
+ .sub(dlobLimitOrder.order.baseAssetAmountFilled)
521
+ .mul(dlobLimitOrderPrice)
522
+ .div(basePrecision), amount.sub(cumulativeQuoteFilled));
523
+ const baseFilled = quoteFilled
524
+ .mul(basePrecision)
525
+ .div(dlobLimitOrderPrice);
526
+ cumulativeBaseFilled = cumulativeBaseFilled.add(baseFilled);
527
+ cumulativeQuoteFilled = cumulativeQuoteFilled.add(quoteFilled);
528
+ worstPrice = dlobLimitOrderPrice;
529
+ dlobLimitOrder = dlobLimitOrders.next().value;
530
+ }
531
+ else {
532
+ const serumOrderBaseAmount = new anchor_1.BN(serumLimitOrder[1] * basePrecision.toNumber());
533
+ const quoteFilled = anchor_1.BN.min(serumOrderBaseAmount.mul(serumLimitOrderPrice).div(basePrecision), amount.sub(cumulativeQuoteFilled));
534
+ const baseFilled = quoteFilled
535
+ .mul(basePrecision)
536
+ .div(serumLimitOrderPrice);
537
+ cumulativeBaseFilled = cumulativeBaseFilled.add(baseFilled);
538
+ cumulativeQuoteFilled = cumulativeQuoteFilled.add(quoteFilled);
539
+ worstPrice = serumLimitOrderPrice;
540
+ serumLimitOrder = serumLimitOrders.shift();
541
+ }
542
+ }
543
+ }
544
+ const entryPrice = cumulativeQuoteFilled
545
+ .mul(basePrecision)
546
+ .div(cumulativeBaseFilled);
547
+ const priceImpact = entryPrice
548
+ .sub(bestPrice)
549
+ .mul(numericConstants_1.PRICE_PRECISION)
550
+ .div(bestPrice)
551
+ .abs();
552
+ return {
553
+ entryPrice,
554
+ priceImpact,
555
+ bestPrice,
556
+ worstPrice,
557
+ baseFilled: cumulativeBaseFilled,
558
+ quoteFilled: cumulativeQuoteFilled,
559
+ };
560
+ }
561
+ exports.calculateEstimatedSpotEntryPrice = calculateEstimatedSpotEntryPrice;
@@ -11,7 +11,7 @@ class OracleClientCache {
11
11
  if (this.cache.has(key)) {
12
12
  return this.cache.get(key);
13
13
  }
14
- const client = (0, oracleClient_1.getOracleClient)(oracleSource, connection);
14
+ const client = oracleClient_1.getOracleClient(oracleSource, connection);
15
15
  this.cache.set(key, client);
16
16
  return client;
17
17
  }
@@ -13,7 +13,7 @@ class PythClient {
13
13
  return this.getOraclePriceDataFromBuffer(accountInfo.data);
14
14
  }
15
15
  getOraclePriceDataFromBuffer(buffer) {
16
- const priceData = (0, client_1.parsePriceData)(buffer);
16
+ const priceData = client_1.parsePriceData(buffer);
17
17
  return {
18
18
  price: convertPythPrice(priceData.aggregate.price, priceData.exponent),
19
19
  slot: new anchor_1.BN(priceData.lastSlot.toString()),
@@ -1,11 +1,7 @@
1
1
  "use strict";
2
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
3
  if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
4
+ Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
9
5
  }) : (function(o, m, k, k2) {
10
6
  if (k2 === undefined) k2 = k;
11
7
  o[k2] = m[k];
@@ -93,7 +93,7 @@ class RetryTxSender {
93
93
  catch (err) {
94
94
  throw new Error('signature must be base58 encoded: ' + signature);
95
95
  }
96
- (0, assert_1.default)(decodedSignature.length === 64, 'signature has invalid length');
96
+ assert_1.default(decodedSignature.length === 64, 'signature has invalid length');
97
97
  const start = Date.now();
98
98
  const subscriptionCommitment = commitment || this.provider.opts.commitment;
99
99
  const subscriptionIds = new Array();
package/lib/tx/utils.d.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  import { Transaction, TransactionInstruction } from '@solana/web3.js';
2
- export declare function wrapInTx(instruction: TransactionInstruction, computeUnits?: number): Transaction;
2
+ export declare function wrapInTx(instruction: TransactionInstruction, computeUnits?: number, computeUnitsPrice?: number): Transaction;
package/lib/tx/utils.js CHANGED
@@ -3,13 +3,16 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.wrapInTx = void 0;
4
4
  const web3_js_1 = require("@solana/web3.js");
5
5
  const COMPUTE_UNITS_DEFAULT = 200000;
6
- function wrapInTx(instruction, computeUnits = 600000 // TODO, requires less code change
7
- ) {
6
+ function wrapInTx(instruction, computeUnits = 600000, computeUnitsPrice = 0) {
8
7
  const tx = new web3_js_1.Transaction();
9
8
  if (computeUnits != COMPUTE_UNITS_DEFAULT) {
10
- tx.add(web3_js_1.ComputeBudgetProgram.requestUnits({
9
+ tx.add(web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({
11
10
  units: computeUnits,
12
- additionalFee: 0,
11
+ }));
12
+ }
13
+ if (computeUnitsPrice != 0) {
14
+ tx.add(web3_js_1.ComputeBudgetProgram.setComputeUnitPrice({
15
+ microLamports: computeUnitsPrice,
13
16
  }));
14
17
  }
15
18
  return tx.add(instruction);
package/lib/types.d.ts CHANGED
@@ -923,6 +923,10 @@ export declare type ReferrerInfo = {
923
923
  referrer: PublicKey;
924
924
  referrerStats: PublicKey;
925
925
  };
926
+ export declare type TxParams = {
927
+ computeUnits?: number;
928
+ computeUnitsPrice?: number;
929
+ };
926
930
  export interface IWallet {
927
931
  signTransaction(tx: Transaction): Promise<Transaction>;
928
932
  signAllTransactions(txs: Transaction[]): Promise<Transaction[]>;