@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.
- package/lib/accounts/bulkAccountLoader.js +3 -3
- package/lib/accounts/fetch.js +2 -2
- package/lib/accounts/pollingDriftClientAccountSubscriber.js +7 -7
- package/lib/accounts/pollingTokenAccountSubscriber.js +2 -2
- package/lib/accounts/pollingUserAccountSubscriber.js +2 -2
- package/lib/accounts/pollingUserStatsAccountSubscriber.js +2 -2
- package/lib/accounts/webSocketAccountSubscriber.js +1 -1
- package/lib/accounts/webSocketDriftClientAccountSubscriber.js +3 -3
- package/lib/addresses/marketAddresses.js +1 -1
- package/lib/addresses/pda.js +1 -5
- package/lib/adminClient.js +57 -61
- package/lib/dlob/DLOB.d.ts +8 -8
- package/lib/dlob/DLOB.js +91 -84
- package/lib/dlob/DLOBNode.js +7 -7
- package/lib/dlob/NodeList.js +2 -2
- package/lib/driftClient.d.ts +3 -3
- package/lib/driftClient.js +83 -87
- package/lib/events/eventSubscriber.js +2 -2
- package/lib/events/pollingLogProvider.js +1 -1
- package/lib/examples/makeTradeExample.js +9 -9
- package/lib/factory/bigNum.js +9 -9
- package/lib/factory/oracleClient.js +2 -2
- package/lib/idl/drift.json +1 -1
- package/lib/index.js +1 -5
- package/lib/math/amm.js +23 -23
- package/lib/math/auction.js +6 -6
- package/lib/math/exchangeStatus.js +2 -2
- package/lib/math/funding.js +2 -2
- package/lib/math/margin.js +5 -5
- package/lib/math/market.js +12 -12
- package/lib/math/oracles.js +1 -1
- package/lib/math/orders.js +23 -23
- package/lib/math/position.js +5 -5
- package/lib/math/repeg.js +1 -1
- package/lib/math/spotBalance.js +7 -7
- package/lib/math/spotPosition.js +3 -3
- package/lib/math/trade.d.ts +35 -3
- package/lib/math/trade.js +225 -47
- package/lib/oracles/oracleClientCache.js +1 -1
- package/lib/oracles/pythClient.js +1 -1
- package/lib/tokenFaucet.js +1 -5
- package/lib/tx/retryTxSender.js +1 -1
- package/lib/tx/utils.d.ts +1 -1
- package/lib/tx/utils.js +7 -4
- package/lib/types.d.ts +4 -0
- package/lib/user.js +47 -47
- package/lib/userMap/userMap.js +1 -1
- package/lib/userMap/userStatsMap.js +3 -3
- package/lib/userStats.js +2 -2
- package/package.json +1 -1
- package/src/dlob/DLOB.ts +34 -26
- package/src/driftClient.ts +11 -4
- package/src/idl/drift.json +1 -1
- package/src/math/trade.ts +322 -16
- package/src/tx/utils.ts +11 -3
- package/src/types.ts +5 -0
- 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 (
|
|
34
|
-
oldPrice =
|
|
33
|
+
if (types_2.isVariant(direction, 'long')) {
|
|
34
|
+
oldPrice = market_1.calculateAskPrice(market, oraclePriceData);
|
|
35
35
|
}
|
|
36
36
|
else {
|
|
37
|
-
oldPrice =
|
|
37
|
+
oldPrice = market_1.calculateBidPrice(market, oraclePriceData);
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
else {
|
|
41
|
-
oldPrice =
|
|
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 } =
|
|
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 =
|
|
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
|
-
|
|
66
|
+
assert_1.assert(newPrice.lte(oldPrice));
|
|
67
67
|
}
|
|
68
68
|
else {
|
|
69
|
-
|
|
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 =
|
|
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 } =
|
|
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] =
|
|
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 =
|
|
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
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
const reservePriceBefore =
|
|
142
|
-
const bidPriceBefore =
|
|
143
|
-
const askPriceBefore =
|
|
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 } =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
236
|
-
|
|
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
|
|
261
|
+
* @param usersToSkip
|
|
262
262
|
*/
|
|
263
|
-
function calculateEstimatedPerpEntryPrice(assetType, amount, direction, market, oraclePriceData, dlob, slot,
|
|
263
|
+
function calculateEstimatedPerpEntryPrice(assetType, amount, direction, market, oraclePriceData, dlob, slot, usersToSkip = new Map()) {
|
|
264
264
|
if (amount.eq(numericConstants_1.ZERO)) {
|
|
265
|
-
return
|
|
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 =
|
|
268
|
-
const limitOrders = dlob[takerIsLong ? '
|
|
269
|
-
|
|
270
|
-
|
|
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
|
|
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
|
-
|
|
285
|
-
? anchor_1.BN.min(limitOrderPrice,
|
|
286
|
-
: anchor_1.BN.max(limitOrderPrice,
|
|
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 =
|
|
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] =
|
|
309
|
-
|
|
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 =
|
|
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] =
|
|
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(
|
|
412
|
+
.sub(bestPrice)
|
|
378
413
|
.mul(numericConstants_1.PRICE_PRECISION)
|
|
379
|
-
.div(
|
|
414
|
+
.div(bestPrice)
|
|
380
415
|
.abs();
|
|
381
|
-
return
|
|
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 =
|
|
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 =
|
|
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()),
|
package/lib/tokenFaucet.js
CHANGED
|
@@ -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
|
-
|
|
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];
|
package/lib/tx/retryTxSender.js
CHANGED
|
@@ -93,7 +93,7 @@ class RetryTxSender {
|
|
|
93
93
|
catch (err) {
|
|
94
94
|
throw new Error('signature must be base58 encoded: ' + signature);
|
|
95
95
|
}
|
|
96
|
-
|
|
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
|
|
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.
|
|
9
|
+
tx.add(web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({
|
|
11
10
|
units: computeUnits,
|
|
12
|
-
|
|
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[]>;
|