@drift-labs/sdk 2.52.0-beta.1 → 2.52.0-beta.10
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/VERSION +1 -1
- package/examples/phoenix.ts +11 -4
- package/lib/accounts/webSocketAccountSubscriber.d.ts +1 -1
- package/lib/accounts/webSocketAccountSubscriber.js +7 -4
- package/lib/accounts/webSocketProgramAccountSubscriber.d.ts +1 -1
- package/lib/accounts/webSocketProgramAccountSubscriber.js +7 -4
- package/lib/dlob/orderBookLevels.d.ts +22 -0
- package/lib/dlob/orderBookLevels.js +115 -1
- package/lib/driftClient.d.ts +3 -1
- package/lib/driftClient.js +30 -8
- package/lib/events/webSocketLogProvider.js +3 -3
- package/lib/factory/bigNum.d.ts +1 -1
- package/lib/factory/bigNum.js +5 -2
- package/lib/idl/drift.json +52 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -1
- package/lib/math/amm.d.ts +5 -1
- package/lib/math/amm.js +62 -13
- package/lib/math/state.js +3 -0
- package/lib/orderSubscriber/OrderSubscriber.js +1 -0
- package/lib/orderSubscriber/WebsocketSubscription.d.ts +4 -1
- package/lib/orderSubscriber/WebsocketSubscription.js +25 -1
- package/lib/orderSubscriber/types.d.ts +1 -0
- package/lib/phoenix/phoenixSubscriber.js +10 -6
- package/lib/priorityFee/averageOverSlotsStrategy.d.ts +12 -0
- package/lib/priorityFee/averageOverSlotsStrategy.js +28 -0
- package/lib/priorityFee/averageStrategy.d.ts +7 -0
- package/lib/priorityFee/averageStrategy.js +11 -0
- package/lib/priorityFee/ewmaStrategy.d.ts +13 -0
- package/lib/priorityFee/ewmaStrategy.js +33 -0
- package/lib/priorityFee/index.d.ts +7 -0
- package/lib/priorityFee/index.js +23 -0
- package/lib/priorityFee/maxOverSlotsStrategy.d.ts +12 -0
- package/lib/priorityFee/maxOverSlotsStrategy.js +29 -0
- package/lib/priorityFee/maxStrategy.d.ts +7 -0
- package/lib/priorityFee/maxStrategy.js +9 -0
- package/lib/priorityFee/priorityFeeSubscriber.d.ts +15 -4
- package/lib/priorityFee/priorityFeeSubscriber.js +38 -14
- package/lib/priorityFee/types.d.ts +6 -0
- package/lib/priorityFee/types.js +2 -0
- package/lib/slot/SlotSubscriber.d.ts +11 -3
- package/lib/slot/SlotSubscriber.js +40 -4
- package/lib/types.d.ts +4 -1
- package/lib/types.js +1 -0
- package/lib/user.d.ts +1 -1
- package/lib/user.js +15 -8
- package/lib/userMap/userMap.d.ts +3 -0
- package/lib/userMap/userMap.js +9 -0
- package/package.json +1 -1
- package/src/accounts/webSocketAccountSubscriber.ts +7 -4
- package/src/accounts/webSocketProgramAccountSubscriber.ts +7 -4
- package/src/dlob/orderBookLevels.ts +136 -0
- package/src/driftClient.ts +46 -8
- package/src/events/webSocketLogProvider.ts +3 -3
- package/src/factory/bigNum.ts +11 -2
- package/src/idl/drift.json +52 -1
- package/src/index.ts +1 -1
- package/src/math/amm.ts +159 -25
- package/src/math/state.ts +3 -0
- package/src/orderSubscriber/OrderSubscriber.ts +1 -0
- package/src/orderSubscriber/WebsocketSubscription.ts +28 -0
- package/src/orderSubscriber/types.ts +1 -0
- package/src/phoenix/phoenixSubscriber.ts +14 -8
- package/src/priorityFee/averageOverSlotsStrategy.ts +30 -0
- package/src/priorityFee/averageStrategy.ts +11 -0
- package/src/priorityFee/ewmaStrategy.ts +40 -0
- package/src/priorityFee/index.ts +7 -0
- package/src/priorityFee/maxOverSlotsStrategy.ts +31 -0
- package/src/priorityFee/maxStrategy.ts +7 -0
- package/src/priorityFee/priorityFeeSubscriber.ts +46 -19
- package/src/priorityFee/types.ts +5 -0
- package/src/slot/SlotSubscriber.ts +52 -5
- package/src/types.ts +2 -1
- package/src/user.ts +25 -8
- package/src/userMap/userMap.ts +12 -0
- package/tests/amm/test.ts +219 -11
- package/tests/bn/test.ts +27 -0
- package/tests/dlob/helpers.ts +1 -1
- package/tests/dlob/test.ts +372 -2
- package/tests/tx/priorityFeeStrategy.ts +97 -0
- package/tests/user/helpers.ts +1 -0
package/lib/math/amm.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.calculateMaxBaseAssetAmountFillable = exports.calculateQuoteAssetAmountSwapped = exports.calculateMaxBaseAssetAmountToTrade = exports.calculateTerminalPrice = exports.getSwapDirection = exports.calculateSwapOutput = exports.calculateSpreadReserves = exports.calculateSpread = exports.calculateSpreadBN = exports.calculateVolSpreadBN = exports.calculateMaxSpread = exports.calculateEffectiveLeverage = exports.calculateInventoryScale = exports.calculateMarketOpenBidAsk = exports.calculateAmmReservesAfterSwap = exports.calculatePrice = exports.calculateBidAskPrice = exports.calculateUpdatedAMMSpreadReserves = exports.calculateUpdatedAMM = exports.calculateNewAmm = exports.calculateOptimalPegAndBudget = exports.calculatePegFromTargetPrice = void 0;
|
|
3
|
+
exports.calculateMaxBaseAssetAmountFillable = exports.calculateQuoteAssetAmountSwapped = exports.calculateMaxBaseAssetAmountToTrade = exports.calculateTerminalPrice = exports.getSwapDirection = exports.calculateSwapOutput = exports.calculateSpreadReserves = exports.calculateSpread = exports.calculateSpreadBN = exports.calculateVolSpreadBN = exports.calculateMaxSpread = exports.calculateEffectiveLeverage = exports.calculateReferencePriceOffset = exports.calculateInventoryScale = exports.calculateInventoryLiquidityRatio = exports.calculateMarketOpenBidAsk = exports.calculateAmmReservesAfterSwap = exports.calculatePrice = exports.calculateBidAskPrice = exports.calculateUpdatedAMMSpreadReserves = exports.calculateUpdatedAMM = exports.calculateNewAmm = exports.calculateOptimalPegAndBudget = exports.calculatePegFromTargetPrice = void 0;
|
|
4
4
|
const anchor_1 = require("@coral-xyz/anchor");
|
|
5
5
|
const numericConstants_1 = require("../constants/numericConstants");
|
|
6
6
|
const types_1 = require("../types");
|
|
@@ -201,11 +201,7 @@ function calculateMarketOpenBidAsk(baseAssetReserve, minBaseAssetReserve, maxBas
|
|
|
201
201
|
return [openBids, openAsks];
|
|
202
202
|
}
|
|
203
203
|
exports.calculateMarketOpenBidAsk = calculateMarketOpenBidAsk;
|
|
204
|
-
function
|
|
205
|
-
if (baseAssetAmountWithAmm.eq(numericConstants_1.ZERO)) {
|
|
206
|
-
return 1;
|
|
207
|
-
}
|
|
208
|
-
const MAX_BID_ASK_INVENTORY_SKEW_FACTOR = numericConstants_1.BID_ASK_SPREAD_PRECISION.mul(new anchor_1.BN(10));
|
|
204
|
+
function calculateInventoryLiquidityRatio(baseAssetAmountWithAmm, baseAssetReserve, minBaseAssetReserve, maxBaseAssetReserve) {
|
|
209
205
|
// inventory skew
|
|
210
206
|
const [openBids, openAsks] = calculateMarketOpenBidAsk(baseAssetReserve, minBaseAssetReserve, maxBaseAssetReserve);
|
|
211
207
|
const minSideLiquidity = anchor_1.BN.min(openBids.abs(), openAsks.abs());
|
|
@@ -213,6 +209,15 @@ function calculateInventoryScale(baseAssetAmountWithAmm, baseAssetReserve, minBa
|
|
|
213
209
|
.mul(numericConstants_1.PERCENTAGE_PRECISION)
|
|
214
210
|
.div(anchor_1.BN.max(minSideLiquidity, numericConstants_1.ONE))
|
|
215
211
|
.abs(), numericConstants_1.PERCENTAGE_PRECISION);
|
|
212
|
+
return inventoryScaleBN;
|
|
213
|
+
}
|
|
214
|
+
exports.calculateInventoryLiquidityRatio = calculateInventoryLiquidityRatio;
|
|
215
|
+
function calculateInventoryScale(baseAssetAmountWithAmm, baseAssetReserve, minBaseAssetReserve, maxBaseAssetReserve, directionalSpread, maxSpread) {
|
|
216
|
+
if (baseAssetAmountWithAmm.eq(numericConstants_1.ZERO)) {
|
|
217
|
+
return 1;
|
|
218
|
+
}
|
|
219
|
+
const MAX_BID_ASK_INVENTORY_SKEW_FACTOR = numericConstants_1.BID_ASK_SPREAD_PRECISION.mul(new anchor_1.BN(10));
|
|
220
|
+
const inventoryScaleBN = calculateInventoryLiquidityRatio(baseAssetAmountWithAmm, baseAssetReserve, minBaseAssetReserve, maxBaseAssetReserve);
|
|
216
221
|
const inventoryScaleMaxBN = anchor_1.BN.max(MAX_BID_ASK_INVENTORY_SKEW_FACTOR, new anchor_1.BN(maxSpread)
|
|
217
222
|
.mul(numericConstants_1.BID_ASK_SPREAD_PRECISION)
|
|
218
223
|
.div(new anchor_1.BN(Math.max(directionalSpread, 1))));
|
|
@@ -220,6 +225,36 @@ function calculateInventoryScale(baseAssetAmountWithAmm, baseAssetReserve, minBa
|
|
|
220
225
|
return inventoryScaleCapped;
|
|
221
226
|
}
|
|
222
227
|
exports.calculateInventoryScale = calculateInventoryScale;
|
|
228
|
+
function calculateReferencePriceOffset(reservePrice, last24hAvgFundingRate, liquidityFraction, oracleTwapFast, markTwapFast, oracleTwapSlow, markTwapSlow, maxOffsetPct) {
|
|
229
|
+
if (last24hAvgFundingRate.eq(numericConstants_1.ZERO)) {
|
|
230
|
+
return numericConstants_1.ZERO;
|
|
231
|
+
}
|
|
232
|
+
const maxOffsetInPrice = new anchor_1.BN(maxOffsetPct)
|
|
233
|
+
.mul(reservePrice)
|
|
234
|
+
.div(numericConstants_1.PERCENTAGE_PRECISION);
|
|
235
|
+
// Calculate quote denominated market premium
|
|
236
|
+
const markPremiumMinute = (0, __1.clampBN)(markTwapFast.sub(oracleTwapFast), maxOffsetInPrice.mul(new anchor_1.BN(-1)), maxOffsetInPrice);
|
|
237
|
+
const markPremiumHour = (0, __1.clampBN)(markTwapSlow.sub(oracleTwapSlow), maxOffsetInPrice.mul(new anchor_1.BN(-1)), maxOffsetInPrice);
|
|
238
|
+
// Convert last24hAvgFundingRate to quote denominated premium
|
|
239
|
+
const markPremiumDay = (0, __1.clampBN)(last24hAvgFundingRate.div(numericConstants_1.FUNDING_RATE_BUFFER_PRECISION).mul(new anchor_1.BN(24)), maxOffsetInPrice.mul(new anchor_1.BN(-1)), maxOffsetInPrice);
|
|
240
|
+
// Take average clamped premium as the price-based offset
|
|
241
|
+
const markPremiumAvg = markPremiumMinute
|
|
242
|
+
.add(markPremiumHour)
|
|
243
|
+
.add(markPremiumDay)
|
|
244
|
+
.div(new anchor_1.BN(3));
|
|
245
|
+
const markPremiumAvgPct = markPremiumAvg
|
|
246
|
+
.mul(numericConstants_1.PRICE_PRECISION)
|
|
247
|
+
.div(reservePrice);
|
|
248
|
+
const inventoryPct = (0, __1.clampBN)(liquidityFraction.mul(new anchor_1.BN(maxOffsetPct)).div(numericConstants_1.PERCENTAGE_PRECISION), maxOffsetInPrice.mul(new anchor_1.BN(-1)), maxOffsetInPrice);
|
|
249
|
+
// Only apply when inventory is consistent with recent and 24h market premium
|
|
250
|
+
let offsetPct = markPremiumAvgPct.add(inventoryPct);
|
|
251
|
+
if (!(0, __1.sigNum)(inventoryPct).eq((0, __1.sigNum)(markPremiumAvgPct))) {
|
|
252
|
+
offsetPct = numericConstants_1.ZERO;
|
|
253
|
+
}
|
|
254
|
+
const clampedOffsetPct = (0, __1.clampBN)(offsetPct, new anchor_1.BN(-maxOffsetPct), new anchor_1.BN(maxOffsetPct));
|
|
255
|
+
return clampedOffsetPct;
|
|
256
|
+
}
|
|
257
|
+
exports.calculateReferencePriceOffset = calculateReferencePriceOffset;
|
|
223
258
|
function calculateEffectiveLeverage(baseSpread, quoteAssetReserve, terminalQuoteAssetReserve, pegMultiplier, netBaseAssetAmount, reservePrice, totalFeeMinusDistributions) {
|
|
224
259
|
// vAMM skew
|
|
225
260
|
const netBaseAssetValue = quoteAssetReserve
|
|
@@ -283,6 +318,8 @@ function calculateSpreadBN(baseSpread, lastOracleReservePriceSpreadPct, lastOrac
|
|
|
283
318
|
halfRevenueRetreatAmount: 0,
|
|
284
319
|
longSpreadwRevRetreat: 0,
|
|
285
320
|
shortSpreadwRevRetreat: 0,
|
|
321
|
+
longSpreadwOffsetShrink: 0,
|
|
322
|
+
shortSpreadwOffsetShrink: 0,
|
|
286
323
|
totalSpread: 0,
|
|
287
324
|
longSpread: 0,
|
|
288
325
|
shortSpread: 0,
|
|
@@ -380,11 +417,13 @@ function calculateSpreadBN(baseSpread, lastOracleReservePriceSpreadPct, lastOrac
|
|
|
380
417
|
return [longSpread, shortSpread];
|
|
381
418
|
}
|
|
382
419
|
exports.calculateSpreadBN = calculateSpreadBN;
|
|
383
|
-
function calculateSpread(amm, oraclePriceData, now) {
|
|
420
|
+
function calculateSpread(amm, oraclePriceData, now, reservePrice) {
|
|
384
421
|
if (amm.baseSpread == 0 || amm.curveUpdateIntensity == 0) {
|
|
385
422
|
return [amm.baseSpread / 2, amm.baseSpread / 2];
|
|
386
423
|
}
|
|
387
|
-
|
|
424
|
+
if (!reservePrice) {
|
|
425
|
+
reservePrice = calculatePrice(amm.baseAssetReserve, amm.quoteAssetReserve, amm.pegMultiplier);
|
|
426
|
+
}
|
|
388
427
|
const targetPrice = (oraclePriceData === null || oraclePriceData === void 0 ? void 0 : oraclePriceData.price) || reservePrice;
|
|
389
428
|
const confInterval = oraclePriceData.confidence || numericConstants_1.ZERO;
|
|
390
429
|
const targetMarkSpreadPct = reservePrice
|
|
@@ -410,10 +449,15 @@ function calculateSpreadReserves(amm, oraclePriceData, now) {
|
|
|
410
449
|
quoteAssetReserve: amm.quoteAssetReserve,
|
|
411
450
|
};
|
|
412
451
|
}
|
|
413
|
-
|
|
452
|
+
let spreadFraction = new anchor_1.BN(spread / 2);
|
|
453
|
+
// make non-zero
|
|
454
|
+
if (spreadFraction.eq(numericConstants_1.ZERO)) {
|
|
455
|
+
spreadFraction = spread >= 0 ? new anchor_1.BN(1) : new anchor_1.BN(-1);
|
|
456
|
+
}
|
|
414
457
|
const quoteAssetReserveDelta = amm.quoteAssetReserve.div(numericConstants_1.BID_ASK_SPREAD_PRECISION.div(spreadFraction));
|
|
415
458
|
let quoteAssetReserve;
|
|
416
|
-
if ((0, types_1.isVariant)(direction, 'long'))
|
|
459
|
+
if ((spread >= 0 && (0, types_1.isVariant)(direction, 'long')) ||
|
|
460
|
+
(spread <= 0 && (0, types_1.isVariant)(direction, 'short'))) {
|
|
417
461
|
quoteAssetReserve = amm.quoteAssetReserve.add(quoteAssetReserveDelta);
|
|
418
462
|
}
|
|
419
463
|
else {
|
|
@@ -425,9 +469,14 @@ function calculateSpreadReserves(amm, oraclePriceData, now) {
|
|
|
425
469
|
quoteAssetReserve,
|
|
426
470
|
};
|
|
427
471
|
}
|
|
428
|
-
const
|
|
429
|
-
|
|
430
|
-
const
|
|
472
|
+
const reservePrice = calculatePrice(amm.baseAssetReserve, amm.quoteAssetReserve, amm.pegMultiplier);
|
|
473
|
+
// always allow 10 bps of price offset, up to a fifth of the market's max_spread
|
|
474
|
+
const maxOffset = Math.max(amm.maxSpread / 5, numericConstants_1.PERCENTAGE_PRECISION.toNumber() / 1000);
|
|
475
|
+
const liquidityFraction = calculateInventoryLiquidityRatio(amm.baseAssetAmountWithAmm, amm.baseAssetReserve, amm.minBaseAssetReserve, amm.maxBaseAssetReserve);
|
|
476
|
+
const referencePriceOffset = calculateReferencePriceOffset(reservePrice, amm.last24HAvgFundingRate, liquidityFraction, amm.historicalOracleData.lastOraclePriceTwap5Min, amm.lastMarkPriceTwap5Min, amm.historicalOracleData.lastOraclePriceTwap, amm.lastMarkPriceTwap, maxOffset);
|
|
477
|
+
const [longSpread, shortSpread] = calculateSpread(amm, oraclePriceData, now, reservePrice);
|
|
478
|
+
const askReserves = calculateSpreadReserve(longSpread + referencePriceOffset.toNumber(), types_1.PositionDirection.LONG, amm);
|
|
479
|
+
const bidReserves = calculateSpreadReserve(shortSpread + referencePriceOffset.toNumber(), types_1.PositionDirection.SHORT, amm);
|
|
431
480
|
return [bidReserves, askReserves];
|
|
432
481
|
}
|
|
433
482
|
exports.calculateSpreadReserves = calculateSpreadReserves;
|
package/lib/math/state.js
CHANGED
|
@@ -22,6 +22,9 @@ function calculateInitUserFee(stateAccount) {
|
|
|
22
22
|
}
|
|
23
23
|
exports.calculateInitUserFee = calculateInitUserFee;
|
|
24
24
|
function getMaxNumberOfSubAccounts(stateAccount) {
|
|
25
|
+
if (stateAccount.maxNumberOfSubAccounts <= 5) {
|
|
26
|
+
return new __1.BN(stateAccount.maxNumberOfSubAccounts);
|
|
27
|
+
}
|
|
25
28
|
return new __1.BN(stateAccount.maxNumberOfSubAccounts).muln(100);
|
|
26
29
|
}
|
|
27
30
|
exports.getMaxNumberOfSubAccounts = getMaxNumberOfSubAccounts;
|
|
@@ -28,6 +28,7 @@ class OrderSubscriber {
|
|
|
28
28
|
commitment: this.commitment,
|
|
29
29
|
skipInitialLoad: config.subscriptionConfig.skipInitialLoad,
|
|
30
30
|
resubTimeoutMs: config.subscriptionConfig.resubTimeoutMs,
|
|
31
|
+
resyncIntervalMs: config.subscriptionConfig.resyncIntervalMs,
|
|
31
32
|
});
|
|
32
33
|
}
|
|
33
34
|
if ((_a = config.fastDecode) !== null && _a !== void 0 ? _a : true) {
|
|
@@ -5,12 +5,15 @@ export declare class WebsocketSubscription {
|
|
|
5
5
|
private commitment;
|
|
6
6
|
private skipInitialLoad;
|
|
7
7
|
private resubTimeoutMs?;
|
|
8
|
+
private resyncIntervalMs?;
|
|
8
9
|
private subscriber?;
|
|
9
|
-
|
|
10
|
+
private resyncTimeoutId?;
|
|
11
|
+
constructor({ orderSubscriber, commitment, skipInitialLoad, resubTimeoutMs, resyncIntervalMs, }: {
|
|
10
12
|
orderSubscriber: OrderSubscriber;
|
|
11
13
|
commitment: Commitment;
|
|
12
14
|
skipInitialLoad?: boolean;
|
|
13
15
|
resubTimeoutMs?: number;
|
|
16
|
+
resyncIntervalMs?: number;
|
|
14
17
|
});
|
|
15
18
|
subscribe(): Promise<void>;
|
|
16
19
|
unsubscribe(): Promise<void>;
|
|
@@ -4,11 +4,12 @@ exports.WebsocketSubscription = void 0;
|
|
|
4
4
|
const memcmp_1 = require("../memcmp");
|
|
5
5
|
const webSocketProgramAccountSubscriber_1 = require("../accounts/webSocketProgramAccountSubscriber");
|
|
6
6
|
class WebsocketSubscription {
|
|
7
|
-
constructor({ orderSubscriber, commitment, skipInitialLoad = false, resubTimeoutMs, }) {
|
|
7
|
+
constructor({ orderSubscriber, commitment, skipInitialLoad = false, resubTimeoutMs, resyncIntervalMs, }) {
|
|
8
8
|
this.orderSubscriber = orderSubscriber;
|
|
9
9
|
this.commitment = commitment;
|
|
10
10
|
this.skipInitialLoad = skipInitialLoad;
|
|
11
11
|
this.resubTimeoutMs = resubTimeoutMs;
|
|
12
|
+
this.resyncIntervalMs = resyncIntervalMs;
|
|
12
13
|
}
|
|
13
14
|
async subscribe() {
|
|
14
15
|
if (this.subscriber) {
|
|
@@ -25,12 +26,35 @@ class WebsocketSubscription {
|
|
|
25
26
|
if (!this.skipInitialLoad) {
|
|
26
27
|
await this.orderSubscriber.fetch();
|
|
27
28
|
}
|
|
29
|
+
if (this.resyncIntervalMs) {
|
|
30
|
+
const recursiveResync = () => {
|
|
31
|
+
this.resyncTimeoutId = setTimeout(() => {
|
|
32
|
+
this.orderSubscriber
|
|
33
|
+
.fetch()
|
|
34
|
+
.catch((e) => {
|
|
35
|
+
console.error('Failed to resync in OrderSubscriber');
|
|
36
|
+
console.log(e);
|
|
37
|
+
})
|
|
38
|
+
.finally(() => {
|
|
39
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
40
|
+
if (!this.resyncTimeoutId)
|
|
41
|
+
return;
|
|
42
|
+
recursiveResync();
|
|
43
|
+
});
|
|
44
|
+
}, this.resyncIntervalMs);
|
|
45
|
+
};
|
|
46
|
+
recursiveResync();
|
|
47
|
+
}
|
|
28
48
|
}
|
|
29
49
|
async unsubscribe() {
|
|
30
50
|
if (!this.subscriber)
|
|
31
51
|
return;
|
|
32
52
|
await this.subscriber.unsubscribe();
|
|
33
53
|
this.subscriber = undefined;
|
|
54
|
+
if (this.resyncTimeoutId !== undefined) {
|
|
55
|
+
clearTimeout(this.resyncTimeoutId);
|
|
56
|
+
this.resyncTimeoutId = undefined;
|
|
57
|
+
}
|
|
34
58
|
}
|
|
35
59
|
}
|
|
36
60
|
exports.WebsocketSubscription = WebsocketSubscription;
|
|
@@ -99,14 +99,18 @@ class PhoenixSubscriber {
|
|
|
99
99
|
return this.getL2Levels('asks');
|
|
100
100
|
}
|
|
101
101
|
*getL2Levels(side) {
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
const
|
|
102
|
+
const tickSize = this.market.data.header
|
|
103
|
+
.tickSizeInQuoteAtomsPerBaseUnit;
|
|
104
|
+
const baseLotsToRawBaseUnits = this.market.baseLotsToRawBaseUnits(1);
|
|
105
|
+
const basePrecision = new anchor_1.BN(Math.pow(10, this.market.data.header.baseParams.decimals) *
|
|
106
|
+
baseLotsToRawBaseUnits);
|
|
107
|
+
const pricePrecision = numericConstants_1.PRICE_PRECISION.div(tickSize);
|
|
108
|
+
const ladder = (0, phoenix_sdk_1.getMarketLadder)(this.market, this.lastSlot, this.lastUnixTimestamp, 20);
|
|
105
109
|
for (let i = 0; i < ladder[side].length; i++) {
|
|
106
|
-
const {
|
|
107
|
-
const size =
|
|
110
|
+
const { priceInTicks, sizeInBaseLots } = ladder[side][i];
|
|
111
|
+
const size = sizeInBaseLots.mul(basePrecision);
|
|
108
112
|
yield {
|
|
109
|
-
price: new anchor_1.BN(
|
|
113
|
+
price: priceInTicks.mul(new anchor_1.BN(pricePrecision)),
|
|
110
114
|
size,
|
|
111
115
|
sources: {
|
|
112
116
|
phoenix: size,
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { PriorityFeeStrategy } from './types';
|
|
2
|
+
export declare class AverageOverSlotsStrategy implements PriorityFeeStrategy {
|
|
3
|
+
private lookbackSlots;
|
|
4
|
+
/**
|
|
5
|
+
* @param lookbackSlots The number of slots to look back from the max slot in the sample
|
|
6
|
+
*/
|
|
7
|
+
constructor(lookbackSlots?: number);
|
|
8
|
+
calculate(samples: {
|
|
9
|
+
slot: number;
|
|
10
|
+
prioritizationFee: number;
|
|
11
|
+
}[]): number;
|
|
12
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AverageOverSlotsStrategy = void 0;
|
|
4
|
+
class AverageOverSlotsStrategy {
|
|
5
|
+
/**
|
|
6
|
+
* @param lookbackSlots The number of slots to look back from the max slot in the sample
|
|
7
|
+
*/
|
|
8
|
+
constructor(lookbackSlots = 10) {
|
|
9
|
+
this.lookbackSlots = lookbackSlots;
|
|
10
|
+
}
|
|
11
|
+
calculate(samples) {
|
|
12
|
+
if (samples.length === 0) {
|
|
13
|
+
return 0;
|
|
14
|
+
}
|
|
15
|
+
const stopSlot = samples[0].slot - this.lookbackSlots;
|
|
16
|
+
let runningSumFees = 0;
|
|
17
|
+
let countFees = 0;
|
|
18
|
+
for (let i = 0; i < samples.length; i++) {
|
|
19
|
+
if (samples[i].slot <= stopSlot) {
|
|
20
|
+
return runningSumFees / countFees;
|
|
21
|
+
}
|
|
22
|
+
runningSumFees += samples[i].prioritizationFee;
|
|
23
|
+
countFees++;
|
|
24
|
+
}
|
|
25
|
+
return runningSumFees / countFees;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
exports.AverageOverSlotsStrategy = AverageOverSlotsStrategy;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AverageStrategy = void 0;
|
|
4
|
+
class AverageStrategy {
|
|
5
|
+
calculate(samples) {
|
|
6
|
+
return (samples.reduce((a, b) => {
|
|
7
|
+
return a + b.prioritizationFee;
|
|
8
|
+
}, 0) / samples.length);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
exports.AverageStrategy = AverageStrategy;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { PriorityFeeStrategy } from './types';
|
|
2
|
+
declare class EwmaStrategy implements PriorityFeeStrategy {
|
|
3
|
+
private halfLife;
|
|
4
|
+
/**
|
|
5
|
+
* @param halfLife The half life of the EWMA in slots. Default is 25 slots, approx 10 seconds.
|
|
6
|
+
*/
|
|
7
|
+
constructor(halfLife?: number);
|
|
8
|
+
calculate(samples: {
|
|
9
|
+
slot: number;
|
|
10
|
+
prioritizationFee: number;
|
|
11
|
+
}[]): number;
|
|
12
|
+
}
|
|
13
|
+
export { EwmaStrategy };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EwmaStrategy = void 0;
|
|
4
|
+
class EwmaStrategy {
|
|
5
|
+
/**
|
|
6
|
+
* @param halfLife The half life of the EWMA in slots. Default is 25 slots, approx 10 seconds.
|
|
7
|
+
*/
|
|
8
|
+
constructor(halfLife = 25) {
|
|
9
|
+
this.halfLife = halfLife;
|
|
10
|
+
}
|
|
11
|
+
// samples provided in desc slot order
|
|
12
|
+
calculate(samples) {
|
|
13
|
+
if (samples.length === 0) {
|
|
14
|
+
return 0;
|
|
15
|
+
}
|
|
16
|
+
if (samples.length === 1) {
|
|
17
|
+
return samples[0].prioritizationFee;
|
|
18
|
+
}
|
|
19
|
+
let ewma = 0;
|
|
20
|
+
const samplesReversed = samples.slice().reverse();
|
|
21
|
+
for (let i = 0; i < samplesReversed.length; i++) {
|
|
22
|
+
if (i === 0) {
|
|
23
|
+
ewma = samplesReversed[i].prioritizationFee;
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
const gap = samplesReversed[i].slot - samplesReversed[i - 1].slot;
|
|
27
|
+
const alpha = 1 - Math.exp((Math.log(0.5) / this.halfLife) * gap);
|
|
28
|
+
ewma = alpha * samplesReversed[i].prioritizationFee + (1 - alpha) * ewma;
|
|
29
|
+
}
|
|
30
|
+
return ewma;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
exports.EwmaStrategy = EwmaStrategy;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
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);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./averageOverSlotsStrategy"), exports);
|
|
18
|
+
__exportStar(require("./averageStrategy"), exports);
|
|
19
|
+
__exportStar(require("./ewmaStrategy"), exports);
|
|
20
|
+
__exportStar(require("./maxOverSlotsStrategy"), exports);
|
|
21
|
+
__exportStar(require("./maxStrategy"), exports);
|
|
22
|
+
__exportStar(require("./priorityFeeSubscriber"), exports);
|
|
23
|
+
__exportStar(require("./types"), exports);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { PriorityFeeStrategy } from './types';
|
|
2
|
+
export declare class MaxOverSlotsStrategy implements PriorityFeeStrategy {
|
|
3
|
+
private lookbackSlots;
|
|
4
|
+
/**
|
|
5
|
+
* @param lookbackSlots The number of slots to look back from the max slot in the sample
|
|
6
|
+
*/
|
|
7
|
+
constructor(lookbackSlots?: number);
|
|
8
|
+
calculate(samples: {
|
|
9
|
+
slot: number;
|
|
10
|
+
prioritizationFee: number;
|
|
11
|
+
}[]): number;
|
|
12
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MaxOverSlotsStrategy = void 0;
|
|
4
|
+
class MaxOverSlotsStrategy {
|
|
5
|
+
/**
|
|
6
|
+
* @param lookbackSlots The number of slots to look back from the max slot in the sample
|
|
7
|
+
*/
|
|
8
|
+
constructor(lookbackSlots = 10) {
|
|
9
|
+
this.lookbackSlots = lookbackSlots;
|
|
10
|
+
}
|
|
11
|
+
calculate(samples) {
|
|
12
|
+
if (samples.length === 0) {
|
|
13
|
+
return 0;
|
|
14
|
+
}
|
|
15
|
+
// Assuming samples are sorted in descending order of slot.
|
|
16
|
+
const stopSlot = samples[0].slot - this.lookbackSlots;
|
|
17
|
+
let currMaxFee = samples[0].prioritizationFee;
|
|
18
|
+
for (let i = 0; i < samples.length; i++) {
|
|
19
|
+
if (samples[i].slot <= stopSlot) {
|
|
20
|
+
return currMaxFee;
|
|
21
|
+
}
|
|
22
|
+
if (samples[i].prioritizationFee > currMaxFee) {
|
|
23
|
+
currMaxFee = samples[i].prioritizationFee;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return currMaxFee;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
exports.MaxOverSlotsStrategy = MaxOverSlotsStrategy;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MaxStrategy = void 0;
|
|
4
|
+
class MaxStrategy {
|
|
5
|
+
calculate(samples) {
|
|
6
|
+
return Math.max(...samples.map((result) => result.prioritizationFee));
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
exports.MaxStrategy = MaxStrategy;
|
|
@@ -1,20 +1,31 @@
|
|
|
1
1
|
import { Connection, PublicKey } from '@solana/web3.js';
|
|
2
|
+
import { PriorityFeeStrategy } from './types';
|
|
3
|
+
import { AverageOverSlotsStrategy } from './averageOverSlotsStrategy';
|
|
4
|
+
import { MaxOverSlotsStrategy } from './maxOverSlotsStrategy';
|
|
2
5
|
export declare class PriorityFeeSubscriber {
|
|
3
6
|
connection: Connection;
|
|
4
7
|
frequencyMs: number;
|
|
5
8
|
addresses: PublicKey[];
|
|
6
|
-
|
|
9
|
+
customStrategy?: PriorityFeeStrategy;
|
|
10
|
+
averageStrategy: AverageOverSlotsStrategy;
|
|
11
|
+
maxStrategy: MaxOverSlotsStrategy;
|
|
7
12
|
intervalId?: ReturnType<typeof setTimeout>;
|
|
8
13
|
latestPriorityFee: number;
|
|
9
|
-
|
|
10
|
-
|
|
14
|
+
lastStrategyResult: number;
|
|
15
|
+
lastCustomStrategyResult: number;
|
|
16
|
+
lastAvgStrategyResult: number;
|
|
17
|
+
lastMaxStrategyResult: number;
|
|
11
18
|
lastSlotSeen: number;
|
|
12
|
-
constructor({ connection, frequencyMs, addresses, slotsToCheck, }: {
|
|
19
|
+
constructor({ connection, frequencyMs, addresses, customStrategy, slotsToCheck, }: {
|
|
13
20
|
connection: Connection;
|
|
14
21
|
frequencyMs: number;
|
|
15
22
|
addresses: PublicKey[];
|
|
23
|
+
customStrategy?: PriorityFeeStrategy;
|
|
16
24
|
slotsToCheck?: number;
|
|
17
25
|
});
|
|
26
|
+
get avgPriorityFee(): number;
|
|
27
|
+
get maxPriorityFee(): number;
|
|
28
|
+
get customPriorityFee(): number;
|
|
18
29
|
subscribe(): Promise<void>;
|
|
19
30
|
load(): Promise<void>;
|
|
20
31
|
unsubscribe(): Promise<void>;
|
|
@@ -1,18 +1,40 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.PriorityFeeSubscriber = void 0;
|
|
4
|
+
const averageOverSlotsStrategy_1 = require("./averageOverSlotsStrategy");
|
|
5
|
+
const maxOverSlotsStrategy_1 = require("./maxOverSlotsStrategy");
|
|
4
6
|
class PriorityFeeSubscriber {
|
|
5
|
-
constructor({ connection, frequencyMs, addresses, slotsToCheck = 10, }) {
|
|
7
|
+
constructor({ connection, frequencyMs, addresses, customStrategy, slotsToCheck = 10, }) {
|
|
8
|
+
this.averageStrategy = new averageOverSlotsStrategy_1.AverageOverSlotsStrategy();
|
|
9
|
+
this.maxStrategy = new maxOverSlotsStrategy_1.MaxOverSlotsStrategy();
|
|
6
10
|
this.latestPriorityFee = 0;
|
|
7
|
-
|
|
8
|
-
this.
|
|
9
|
-
|
|
10
|
-
this.
|
|
11
|
+
this.lastStrategyResult = 0;
|
|
12
|
+
this.lastCustomStrategyResult = 0;
|
|
13
|
+
this.lastAvgStrategyResult = 0;
|
|
14
|
+
this.lastMaxStrategyResult = 0;
|
|
11
15
|
this.lastSlotSeen = 0;
|
|
12
16
|
this.connection = connection;
|
|
13
17
|
this.frequencyMs = frequencyMs;
|
|
14
18
|
this.addresses = addresses;
|
|
15
|
-
|
|
19
|
+
if (slotsToCheck) {
|
|
20
|
+
this.averageStrategy = new averageOverSlotsStrategy_1.AverageOverSlotsStrategy(slotsToCheck);
|
|
21
|
+
this.maxStrategy = new maxOverSlotsStrategy_1.MaxOverSlotsStrategy(slotsToCheck);
|
|
22
|
+
}
|
|
23
|
+
if (customStrategy) {
|
|
24
|
+
this.customStrategy = customStrategy;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
get avgPriorityFee() {
|
|
28
|
+
return this.lastAvgStrategyResult;
|
|
29
|
+
}
|
|
30
|
+
get maxPriorityFee() {
|
|
31
|
+
return this.lastMaxStrategyResult;
|
|
32
|
+
}
|
|
33
|
+
get customPriorityFee() {
|
|
34
|
+
if (!this.customStrategy) {
|
|
35
|
+
console.error('Custom strategy not set');
|
|
36
|
+
}
|
|
37
|
+
return this.lastCustomStrategyResult;
|
|
16
38
|
}
|
|
17
39
|
async subscribe() {
|
|
18
40
|
if (this.intervalId) {
|
|
@@ -21,20 +43,22 @@ class PriorityFeeSubscriber {
|
|
|
21
43
|
this.intervalId = setInterval(this.load.bind(this), this.frequencyMs);
|
|
22
44
|
}
|
|
23
45
|
async load() {
|
|
24
|
-
var _a, _b, _c;
|
|
25
46
|
// @ts-ignore
|
|
26
47
|
const rpcJSONResponse = await this.connection._rpcRequest('getRecentPrioritizationFees', [this.addresses]);
|
|
27
|
-
|
|
28
|
-
|
|
48
|
+
// getRecentPrioritizationFees returns results unsorted
|
|
49
|
+
const results = rpcJSONResponse === null || rpcJSONResponse === void 0 ? void 0 : rpcJSONResponse.result;
|
|
50
|
+
if (!results.length)
|
|
29
51
|
return;
|
|
52
|
+
const descResults = results.sort((a, b) => b.slot - a.slot);
|
|
30
53
|
const mostRecentResult = descResults[0];
|
|
31
54
|
this.latestPriorityFee = mostRecentResult.prioritizationFee;
|
|
32
55
|
this.lastSlotSeen = mostRecentResult.slot;
|
|
33
|
-
this.
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
56
|
+
this.lastAvgStrategyResult = this.averageStrategy.calculate(descResults);
|
|
57
|
+
this.lastMaxStrategyResult = this.maxStrategy.calculate(descResults);
|
|
58
|
+
if (this.customStrategy) {
|
|
59
|
+
this.lastCustomStrategyResult =
|
|
60
|
+
this.customStrategy.calculate(descResults);
|
|
61
|
+
}
|
|
38
62
|
}
|
|
39
63
|
async unsubscribe() {
|
|
40
64
|
if (this.intervalId) {
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
+
/// <reference types="node" />
|
|
2
3
|
import { Connection } from '@solana/web3.js';
|
|
3
4
|
import { EventEmitter } from 'events';
|
|
4
5
|
import StrictEventEmitter from 'strict-event-emitter-types/types/src';
|
|
5
|
-
type SlotSubscriberConfig = {
|
|
6
|
+
type SlotSubscriberConfig = {
|
|
7
|
+
resubTimeoutMs?: number;
|
|
8
|
+
};
|
|
6
9
|
export interface SlotSubscriberEvents {
|
|
7
10
|
newSlot: (newSlot: number) => void;
|
|
8
11
|
}
|
|
@@ -11,9 +14,14 @@ export declare class SlotSubscriber {
|
|
|
11
14
|
currentSlot: number;
|
|
12
15
|
subscriptionId: number;
|
|
13
16
|
eventEmitter: StrictEventEmitter<EventEmitter, SlotSubscriberEvents>;
|
|
14
|
-
|
|
17
|
+
timeoutId?: NodeJS.Timeout;
|
|
18
|
+
resubTimeoutMs?: number;
|
|
19
|
+
isUnsubscribing: boolean;
|
|
20
|
+
receivingData: boolean;
|
|
21
|
+
constructor(connection: Connection, config?: SlotSubscriberConfig);
|
|
15
22
|
subscribe(): Promise<void>;
|
|
23
|
+
private setTimeout;
|
|
16
24
|
getSlot(): number;
|
|
17
|
-
unsubscribe(): Promise<void>;
|
|
25
|
+
unsubscribe(onResub?: boolean): Promise<void>;
|
|
18
26
|
}
|
|
19
27
|
export {};
|