@drift-labs/sdk 2.54.0-beta.3 → 2.54.0-beta.4
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/lib/accounts/pollingInsuranceFundStakeAccountSubscriber.js +0 -1
- package/lib/driftClient.d.ts +5 -1
- package/lib/driftClient.js +24 -13
- package/lib/jupiter/jupiterClient.d.ts +6 -0
- package/lib/jupiter/jupiterClient.js +2 -2
- package/lib/math/funding.js +24 -1
- package/lib/math/oracles.js +2 -2
- package/lib/math/superStake.js +3 -1
- package/lib/priorityFee/averageOverSlotsStrategy.d.ts +0 -5
- package/lib/priorityFee/averageOverSlotsStrategy.js +1 -13
- package/lib/priorityFee/maxOverSlotsStrategy.d.ts +0 -5
- package/lib/priorityFee/maxOverSlotsStrategy.js +1 -13
- package/lib/priorityFee/priorityFeeSubscriber.d.ts +5 -4
- package/lib/priorityFee/priorityFeeSubscriber.js +15 -21
- package/package.json +1 -1
- package/src/accounts/pollingInsuranceFundStakeAccountSubscriber.ts +0 -1
- package/src/driftClient.ts +71 -20
- package/src/jupiter/jupiterClient.ts +8 -2
- package/src/math/funding.ts +28 -1
- package/src/math/oracles.ts +2 -2
- package/src/math/superStake.ts +3 -1
- package/src/priorityFee/averageOverSlotsStrategy.ts +1 -16
- package/src/priorityFee/maxOverSlotsStrategy.ts +1 -16
- package/src/priorityFee/priorityFeeSubscriber.ts +22 -26
- package/tests/amm/test.ts +275 -2
- package/tests/dlob/test.ts +2 -2
- package/tests/tx/priorityFeeStrategy.ts +2 -2
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.54.0-beta.
|
|
1
|
+
2.54.0-beta.4
|
package/lib/driftClient.d.ts
CHANGED
|
@@ -310,7 +310,7 @@ export declare class DriftClient {
|
|
|
310
310
|
direction?: PositionDirection;
|
|
311
311
|
}, placeOrderParams: OrderParams[], txParams?: TxParams, subAccountId?: number): Promise<TransactionSignature>;
|
|
312
312
|
placeOrders(params: OrderParams[], txParams?: TxParams, subAccountId?: number): Promise<TransactionSignature>;
|
|
313
|
-
getPlaceOrdersIx(params:
|
|
313
|
+
getPlaceOrdersIx(params: OptionalOrderParams[], subAccountId?: number): Promise<TransactionInstruction>;
|
|
314
314
|
fillPerpOrder(userAccountPublicKey: PublicKey, user: UserAccount, order?: Pick<Order, 'marketIndex' | 'orderId'>, makerInfo?: MakerInfo | MakerInfo[], referrerInfo?: ReferrerInfo, txParams?: TxParams, fillerPublicKey?: number): Promise<TransactionSignature>;
|
|
315
315
|
getFillPerpOrderIx(userAccountPublicKey: PublicKey, userAccount: UserAccount, order: Pick<Order, 'marketIndex' | 'orderId'>, makerInfo?: MakerInfo | MakerInfo[], referrerInfo?: ReferrerInfo, fillerSubAccountId?: number): Promise<TransactionInstruction>;
|
|
316
316
|
getRevertFillIx(fillerPublicKey?: PublicKey): Promise<TransactionInstruction>;
|
|
@@ -427,6 +427,10 @@ export declare class DriftClient {
|
|
|
427
427
|
updateUserOpenOrdersCount(userAccountPublicKey: PublicKey, user: UserAccount, txParams?: TxParams, fillerPublicKey?: PublicKey): Promise<TransactionSignature>;
|
|
428
428
|
getUpdateUserOpenOrdersCountIx(userAccountPublicKey: PublicKey, userAccount: UserAccount, fillerPublicKey?: PublicKey): Promise<TransactionInstruction>;
|
|
429
429
|
placeAndTakePerpOrder(orderParams: OptionalOrderParams, makerInfo?: MakerInfo | MakerInfo[], referrerInfo?: ReferrerInfo, txParams?: TxParams, subAccountId?: number): Promise<TransactionSignature>;
|
|
430
|
+
placeAndTakePerpWithAdditionalOrders(orderParams: OptionalOrderParams, makerInfo?: MakerInfo | MakerInfo[], referrerInfo?: ReferrerInfo, bracketOrdersParams?: OptionalOrderParams[], txParams?: TxParams, subAccountId?: number, cancelExistingOrders?: boolean): Promise<{
|
|
431
|
+
txSig: TransactionSignature;
|
|
432
|
+
signedCancelExistingOrdersTx?: Transaction;
|
|
433
|
+
}>;
|
|
430
434
|
getPlaceAndTakePerpOrderIx(orderParams: OptionalOrderParams, makerInfo?: MakerInfo | MakerInfo[], referrerInfo?: ReferrerInfo, subAccountId?: number): Promise<TransactionInstruction>;
|
|
431
435
|
placeAndMakePerpOrder(orderParams: OptionalOrderParams, takerInfo: TakerInfo, referrerInfo?: ReferrerInfo, txParams?: TxParams, subAccountId?: number): Promise<TransactionSignature>;
|
|
432
436
|
getPlaceAndMakePerpOrderIx(orderParams: OptionalOrderParams, takerInfo: TakerInfo, referrerInfo?: ReferrerInfo, subAccountId?: number): Promise<TransactionInstruction>;
|
package/lib/driftClient.js
CHANGED
|
@@ -1422,12 +1422,7 @@ class DriftClient {
|
|
|
1422
1422
|
async sendMarketOrderAndGetSignedFillTx(orderParams, userAccountPublicKey, userAccount, makerInfo, txParams, bracketOrdersParams = new Array(), referrerInfo, cancelExistingOrders) {
|
|
1423
1423
|
const marketIndex = orderParams.marketIndex;
|
|
1424
1424
|
const orderId = userAccount.nextOrderId;
|
|
1425
|
-
const
|
|
1426
|
-
const placePerpOrderIx = await this.getPlacePerpOrderIx(orderParams, userAccount.subAccountId);
|
|
1427
|
-
for (const bracketOrderParams of bracketOrdersParams) {
|
|
1428
|
-
const placeBracketOrderIx = await this.getPlacePerpOrderIx(bracketOrderParams, userAccount.subAccountId);
|
|
1429
|
-
bracketOrderIxs.push(placeBracketOrderIx);
|
|
1430
|
-
}
|
|
1425
|
+
const ordersIx = await this.getPlaceOrdersIx([orderParams, ...bracketOrdersParams], userAccount.subAccountId);
|
|
1431
1426
|
let cancelOrdersIx;
|
|
1432
1427
|
let cancelExistingOrdersTx;
|
|
1433
1428
|
if (cancelExistingOrders && (0, types_1.isVariant)(orderParams.marketType, 'perp')) {
|
|
@@ -1437,7 +1432,7 @@ class DriftClient {
|
|
|
1437
1432
|
}
|
|
1438
1433
|
// use versioned transactions if there is a lookup table account and wallet is compatible
|
|
1439
1434
|
if (this.txVersion === 0) {
|
|
1440
|
-
const versionedMarketOrderTx = await this.buildTransaction(
|
|
1435
|
+
const versionedMarketOrderTx = await this.buildTransaction(ordersIx, txParams, 0);
|
|
1441
1436
|
const fillPerpOrderIx = await this.getFillPerpOrderIx(userAccountPublicKey, userAccount, {
|
|
1442
1437
|
orderId,
|
|
1443
1438
|
marketIndex,
|
|
@@ -1459,10 +1454,7 @@ class DriftClient {
|
|
|
1459
1454
|
};
|
|
1460
1455
|
}
|
|
1461
1456
|
else {
|
|
1462
|
-
const marketOrderTx = (0, utils_1.wrapInTx)(
|
|
1463
|
-
if (bracketOrderIxs.length > 0) {
|
|
1464
|
-
marketOrderTx.add(...bracketOrderIxs);
|
|
1465
|
-
}
|
|
1457
|
+
const marketOrderTx = (0, utils_1.wrapInTx)(ordersIx, txParams === null || txParams === void 0 ? void 0 : txParams.computeUnits, txParams === null || txParams === void 0 ? void 0 : txParams.computeUnitsPrice);
|
|
1466
1458
|
// Apply the latest blockhash to the txs so that we can sign before sending them
|
|
1467
1459
|
const currentBlockHash = (await this.connection.getLatestBlockhash('finalized')).blockhash;
|
|
1468
1460
|
marketOrderTx.recentBlockhash = currentBlockHash;
|
|
@@ -1708,7 +1700,8 @@ class DriftClient {
|
|
|
1708
1700
|
readableSpotMarketIndexes,
|
|
1709
1701
|
useMarketLastSlotCache: true,
|
|
1710
1702
|
});
|
|
1711
|
-
|
|
1703
|
+
const formattedParams = params.map((item) => (0, orderParams_1.getOrderParams)(item));
|
|
1704
|
+
return await this.program.instruction.placeOrders(formattedParams, {
|
|
1712
1705
|
accounts: {
|
|
1713
1706
|
state: await this.getStatePublicKey(),
|
|
1714
1707
|
user,
|
|
@@ -2168,7 +2161,6 @@ class DriftClient {
|
|
|
2168
2161
|
slippageBps,
|
|
2169
2162
|
swapMode,
|
|
2170
2163
|
onlyDirectRoutes,
|
|
2171
|
-
excludeDexes: ['Raydium CLMM'], // temp exclude to workaround bug with raydium clmm
|
|
2172
2164
|
});
|
|
2173
2165
|
quote = fetchedQuote;
|
|
2174
2166
|
}
|
|
@@ -2412,6 +2404,25 @@ class DriftClient {
|
|
|
2412
2404
|
this.perpMarketLastSlotCache.set(orderParams.marketIndex, slot);
|
|
2413
2405
|
return txSig;
|
|
2414
2406
|
}
|
|
2407
|
+
async placeAndTakePerpWithAdditionalOrders(orderParams, makerInfo, referrerInfo, bracketOrdersParams = new Array(), txParams, subAccountId, cancelExistingOrders) {
|
|
2408
|
+
let signedCancelExistingOrdersTx;
|
|
2409
|
+
if (cancelExistingOrders && (0, types_1.isVariant)(orderParams.marketType, 'perp')) {
|
|
2410
|
+
const cancelOrdersIx = await this.getCancelOrdersIx(orderParams.marketType, orderParams.marketIndex, null, subAccountId);
|
|
2411
|
+
const cancelExistingOrdersTx = await this.buildTransaction([cancelOrdersIx], txParams, this.txVersion);
|
|
2412
|
+
// @ts-ignore
|
|
2413
|
+
signedCancelExistingOrdersTx = await this.provider.wallet.signTransaction(cancelExistingOrdersTx);
|
|
2414
|
+
}
|
|
2415
|
+
const ixs = [];
|
|
2416
|
+
const placeAndTakeIx = await this.getPlaceAndTakePerpOrderIx(orderParams, makerInfo, referrerInfo, subAccountId);
|
|
2417
|
+
ixs.push(placeAndTakeIx);
|
|
2418
|
+
if (bracketOrdersParams.length > 0) {
|
|
2419
|
+
const bracketOrdersIx = await this.getPlaceOrdersIx(bracketOrdersParams, subAccountId);
|
|
2420
|
+
ixs.push(bracketOrdersIx);
|
|
2421
|
+
}
|
|
2422
|
+
const { txSig, slot } = await this.sendTransaction(await this.buildTransaction(ixs, txParams), [], this.opts);
|
|
2423
|
+
this.perpMarketLastSlotCache.set(orderParams.marketIndex, slot);
|
|
2424
|
+
return { txSig, signedCancelExistingOrdersTx };
|
|
2425
|
+
}
|
|
2415
2426
|
async getPlaceAndTakePerpOrderIx(orderParams, makerInfo, referrerInfo, subAccountId) {
|
|
2416
2427
|
orderParams = (0, orderParams_1.getOrderParams)(orderParams, { marketType: types_1.MarketType.PERP });
|
|
2417
2428
|
const userStatsPublicKey = await this.getUserStatsAccountPublicKey();
|
|
@@ -195,6 +195,12 @@ export interface QuoteResponse {
|
|
|
195
195
|
* @memberof QuoteResponse
|
|
196
196
|
*/
|
|
197
197
|
timeTaken?: number;
|
|
198
|
+
/**
|
|
199
|
+
*
|
|
200
|
+
* @type {string}
|
|
201
|
+
* @memberof QuoteResponse
|
|
202
|
+
*/
|
|
203
|
+
error?: string;
|
|
198
204
|
}
|
|
199
205
|
export declare class JupiterClient {
|
|
200
206
|
url: string;
|
|
@@ -44,7 +44,7 @@ class JupiterClient {
|
|
|
44
44
|
* @param onlyDirectRoutes whether to only return direct routes
|
|
45
45
|
*/
|
|
46
46
|
async getQuote({ inputMint, outputMint, amount, maxAccounts = 50, // 50 is an estimated amount with buffer
|
|
47
|
-
slippageBps = 50, swapMode = 'ExactIn', onlyDirectRoutes = false, excludeDexes
|
|
47
|
+
slippageBps = 50, swapMode = 'ExactIn', onlyDirectRoutes = false, excludeDexes, }) {
|
|
48
48
|
const params = new URLSearchParams({
|
|
49
49
|
inputMint: inputMint.toString(),
|
|
50
50
|
outputMint: outputMint.toString(),
|
|
@@ -53,7 +53,7 @@ class JupiterClient {
|
|
|
53
53
|
swapMode,
|
|
54
54
|
onlyDirectRoutes: onlyDirectRoutes.toString(),
|
|
55
55
|
maxAccounts: maxAccounts.toString(),
|
|
56
|
-
excludeDexes: excludeDexes.join(','),
|
|
56
|
+
...(excludeDexes && { excludeDexes: excludeDexes.join(',') }),
|
|
57
57
|
}).toString();
|
|
58
58
|
const quote = await (await (0, node_fetch_1.default)(`${this.url}/v6/quote?${params}`)).json();
|
|
59
59
|
return quote;
|
package/lib/math/funding.js
CHANGED
|
@@ -6,6 +6,7 @@ const numericConstants_1 = require("../constants/numericConstants");
|
|
|
6
6
|
const types_1 = require("../types");
|
|
7
7
|
const amm_1 = require("./amm");
|
|
8
8
|
const oracles_1 = require("./oracles");
|
|
9
|
+
const utils_1 = require("./utils");
|
|
9
10
|
function calculateLiveMarkTwap(market, oraclePriceData, markPrice, now, period = new anchor_1.BN(3600)) {
|
|
10
11
|
now = now || new anchor_1.BN((Date.now() / 1000).toFixed(0));
|
|
11
12
|
const lastMarkTwapWithMantissa = market.amm.lastMarkPriceTwap;
|
|
@@ -73,7 +74,9 @@ async function calculateAllEstimatedFundingRate(market, oraclePriceData, markPri
|
|
|
73
74
|
// }
|
|
74
75
|
const twapSpread = markTwap.sub(oracleTwap);
|
|
75
76
|
const twapSpreadWithOffset = twapSpread.add(oracleTwap.abs().div(numericConstants_1.FUNDING_RATE_OFFSET_DENOMINATOR));
|
|
76
|
-
const
|
|
77
|
+
const maxSpread = getMaxPriceDivergenceForFundingRate(market, oracleTwap);
|
|
78
|
+
const clampedSpreadWithOffset = (0, utils_1.clampBN)(twapSpreadWithOffset, maxSpread.mul(new anchor_1.BN(-1)), maxSpread);
|
|
79
|
+
const twapSpreadPct = clampedSpreadWithOffset
|
|
77
80
|
.mul(numericConstants_1.PRICE_PRECISION)
|
|
78
81
|
.mul(new anchor_1.BN(100))
|
|
79
82
|
.div(oracleTwap);
|
|
@@ -135,6 +138,26 @@ async function calculateAllEstimatedFundingRate(market, oraclePriceData, markPri
|
|
|
135
138
|
return [markTwap, oracleTwap, lowerboundEst, cappedAltEst, interpEst];
|
|
136
139
|
}
|
|
137
140
|
exports.calculateAllEstimatedFundingRate = calculateAllEstimatedFundingRate;
|
|
141
|
+
function getMaxPriceDivergenceForFundingRate(market, oracleTwap) {
|
|
142
|
+
if ((0, types_1.isVariant)(market.contractTier, 'a')) {
|
|
143
|
+
return oracleTwap.divn(33);
|
|
144
|
+
}
|
|
145
|
+
else if ((0, types_1.isVariant)(market.contractTier, 'b')) {
|
|
146
|
+
return oracleTwap.divn(33);
|
|
147
|
+
}
|
|
148
|
+
else if ((0, types_1.isVariant)(market.contractTier, 'c')) {
|
|
149
|
+
return oracleTwap.divn(20);
|
|
150
|
+
}
|
|
151
|
+
else if ((0, types_1.isVariant)(market.contractTier, 'speculative')) {
|
|
152
|
+
return oracleTwap.divn(10);
|
|
153
|
+
}
|
|
154
|
+
else if ((0, types_1.isVariant)(market.contractTier, 'isolated')) {
|
|
155
|
+
return oracleTwap.divn(10);
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
return oracleTwap.divn(10);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
138
161
|
/**
|
|
139
162
|
*
|
|
140
163
|
* @param market
|
package/lib/math/oracles.js
CHANGED
|
@@ -25,8 +25,8 @@ function isOracleValid(amm, oraclePriceData, oracleGuardRails, slot) {
|
|
|
25
25
|
.mul(numericConstants_1.BID_ASK_SPREAD_PRECISION)
|
|
26
26
|
.div(oraclePriceData.price)
|
|
27
27
|
.gt(oracleGuardRails.validity.confidenceIntervalMaxSize);
|
|
28
|
-
const oracleIsStale =
|
|
29
|
-
.sub(
|
|
28
|
+
const oracleIsStale = new index_1.BN(slot)
|
|
29
|
+
.sub(oraclePriceData.slot)
|
|
30
30
|
.gt(oracleGuardRails.validity.slotsBeforeStaleForAmm);
|
|
31
31
|
return !(!oraclePriceData.hasSufficientNumberOfDataPoints ||
|
|
32
32
|
oracleIsStale ||
|
package/lib/math/superStake.js
CHANGED
|
@@ -233,7 +233,9 @@ async function calculateSolEarned({ marketIndex, user, depositRecords, }) {
|
|
|
233
233
|
const now = Date.now() / 1000;
|
|
234
234
|
const timestamps = [
|
|
235
235
|
now,
|
|
236
|
-
...depositRecords
|
|
236
|
+
...depositRecords
|
|
237
|
+
.filter((r) => r.marketIndex === marketIndex)
|
|
238
|
+
.map((r) => r.ts.toNumber()),
|
|
237
239
|
];
|
|
238
240
|
let lstRatios = new Map();
|
|
239
241
|
const getMsolPrice = async (timestamp) => {
|
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
import { PriorityFeeStrategy } from './types';
|
|
2
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
3
|
calculate(samples: {
|
|
9
4
|
slot: number;
|
|
10
5
|
prioritizationFee: number;
|
|
@@ -2,27 +2,15 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.AverageOverSlotsStrategy = void 0;
|
|
4
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
5
|
calculate(samples) {
|
|
12
6
|
if (samples.length === 0) {
|
|
13
7
|
return 0;
|
|
14
8
|
}
|
|
15
|
-
const stopSlot = samples[0].slot - this.lookbackSlots;
|
|
16
9
|
let runningSumFees = 0;
|
|
17
|
-
let countFees = 0;
|
|
18
10
|
for (let i = 0; i < samples.length; i++) {
|
|
19
|
-
if (samples[i].slot <= stopSlot) {
|
|
20
|
-
return runningSumFees / countFees;
|
|
21
|
-
}
|
|
22
11
|
runningSumFees += samples[i].prioritizationFee;
|
|
23
|
-
countFees++;
|
|
24
12
|
}
|
|
25
|
-
return runningSumFees /
|
|
13
|
+
return runningSumFees / samples.length;
|
|
26
14
|
}
|
|
27
15
|
}
|
|
28
16
|
exports.AverageOverSlotsStrategy = AverageOverSlotsStrategy;
|
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
import { PriorityFeeStrategy } from './types';
|
|
2
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
3
|
calculate(samples: {
|
|
9
4
|
slot: number;
|
|
10
5
|
prioritizationFee: number;
|
|
@@ -2,26 +2,14 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.MaxOverSlotsStrategy = void 0;
|
|
4
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
5
|
calculate(samples) {
|
|
12
6
|
if (samples.length === 0) {
|
|
13
7
|
return 0;
|
|
14
8
|
}
|
|
15
9
|
// Assuming samples are sorted in descending order of slot.
|
|
16
|
-
const stopSlot = samples[0].slot - this.lookbackSlots;
|
|
17
10
|
let currMaxFee = samples[0].prioritizationFee;
|
|
18
11
|
for (let i = 0; i < samples.length; i++) {
|
|
19
|
-
|
|
20
|
-
return currMaxFee;
|
|
21
|
-
}
|
|
22
|
-
if (samples[i].prioritizationFee > currMaxFee) {
|
|
23
|
-
currMaxFee = samples[i].prioritizationFee;
|
|
24
|
-
}
|
|
12
|
+
currMaxFee = Math.max(samples[i].prioritizationFee, currMaxFee);
|
|
25
13
|
}
|
|
26
14
|
return currMaxFee;
|
|
27
15
|
}
|
|
@@ -9,13 +9,17 @@ export declare class PriorityFeeSubscriber {
|
|
|
9
9
|
customStrategy?: PriorityFeeStrategy;
|
|
10
10
|
averageStrategy: AverageOverSlotsStrategy;
|
|
11
11
|
maxStrategy: MaxOverSlotsStrategy;
|
|
12
|
+
lookbackDistance: number;
|
|
12
13
|
intervalId?: ReturnType<typeof setTimeout>;
|
|
13
14
|
latestPriorityFee: number;
|
|
14
|
-
lastStrategyResult: number;
|
|
15
15
|
lastCustomStrategyResult: number;
|
|
16
16
|
lastAvgStrategyResult: number;
|
|
17
17
|
lastMaxStrategyResult: number;
|
|
18
18
|
lastSlotSeen: number;
|
|
19
|
+
/**
|
|
20
|
+
* @param props
|
|
21
|
+
* customStrategy : strategy to return the priority fee to use based on recent samples. defaults to AVERAGE.
|
|
22
|
+
*/
|
|
19
23
|
constructor({ connection, frequencyMs, addresses, customStrategy, slotsToCheck, }: {
|
|
20
24
|
connection: Connection;
|
|
21
25
|
frequencyMs: number;
|
|
@@ -23,9 +27,6 @@ export declare class PriorityFeeSubscriber {
|
|
|
23
27
|
customStrategy?: PriorityFeeStrategy;
|
|
24
28
|
slotsToCheck?: number;
|
|
25
29
|
});
|
|
26
|
-
get avgPriorityFee(): number;
|
|
27
|
-
get maxPriorityFee(): number;
|
|
28
|
-
get customPriorityFee(): number;
|
|
29
30
|
subscribe(): Promise<void>;
|
|
30
31
|
load(): Promise<void>;
|
|
31
32
|
unsubscribe(): Promise<void>;
|
|
@@ -4,11 +4,14 @@ exports.PriorityFeeSubscriber = void 0;
|
|
|
4
4
|
const averageOverSlotsStrategy_1 = require("./averageOverSlotsStrategy");
|
|
5
5
|
const maxOverSlotsStrategy_1 = require("./maxOverSlotsStrategy");
|
|
6
6
|
class PriorityFeeSubscriber {
|
|
7
|
+
/**
|
|
8
|
+
* @param props
|
|
9
|
+
* customStrategy : strategy to return the priority fee to use based on recent samples. defaults to AVERAGE.
|
|
10
|
+
*/
|
|
7
11
|
constructor({ connection, frequencyMs, addresses, customStrategy, slotsToCheck = 10, }) {
|
|
8
12
|
this.averageStrategy = new averageOverSlotsStrategy_1.AverageOverSlotsStrategy();
|
|
9
13
|
this.maxStrategy = new maxOverSlotsStrategy_1.MaxOverSlotsStrategy();
|
|
10
14
|
this.latestPriorityFee = 0;
|
|
11
|
-
this.lastStrategyResult = 0;
|
|
12
15
|
this.lastCustomStrategyResult = 0;
|
|
13
16
|
this.lastAvgStrategyResult = 0;
|
|
14
17
|
this.lastMaxStrategyResult = 0;
|
|
@@ -16,25 +19,13 @@ class PriorityFeeSubscriber {
|
|
|
16
19
|
this.connection = connection;
|
|
17
20
|
this.frequencyMs = frequencyMs;
|
|
18
21
|
this.addresses = addresses;
|
|
19
|
-
if (
|
|
20
|
-
this.
|
|
21
|
-
this.maxStrategy = new maxOverSlotsStrategy_1.MaxOverSlotsStrategy(slotsToCheck);
|
|
22
|
+
if (!customStrategy) {
|
|
23
|
+
this.customStrategy = new averageOverSlotsStrategy_1.AverageOverSlotsStrategy();
|
|
22
24
|
}
|
|
23
|
-
|
|
25
|
+
else {
|
|
24
26
|
this.customStrategy = customStrategy;
|
|
25
27
|
}
|
|
26
|
-
|
|
27
|
-
get avgPriorityFee() {
|
|
28
|
-
return Math.floor(this.lastAvgStrategyResult);
|
|
29
|
-
}
|
|
30
|
-
get maxPriorityFee() {
|
|
31
|
-
return Math.floor(this.lastMaxStrategyResult);
|
|
32
|
-
}
|
|
33
|
-
get customPriorityFee() {
|
|
34
|
-
if (!this.customStrategy) {
|
|
35
|
-
console.error('Custom strategy not set');
|
|
36
|
-
}
|
|
37
|
-
return Math.floor(this.lastCustomStrategyResult);
|
|
28
|
+
this.lookbackDistance = slotsToCheck;
|
|
38
29
|
}
|
|
39
30
|
async subscribe() {
|
|
40
31
|
if (this.intervalId) {
|
|
@@ -45,19 +36,22 @@ class PriorityFeeSubscriber {
|
|
|
45
36
|
async load() {
|
|
46
37
|
// @ts-ignore
|
|
47
38
|
const rpcJSONResponse = await this.connection._rpcRequest('getRecentPrioritizationFees', [this.addresses]);
|
|
48
|
-
// getRecentPrioritizationFees returns results unsorted
|
|
49
39
|
const results = rpcJSONResponse === null || rpcJSONResponse === void 0 ? void 0 : rpcJSONResponse.result;
|
|
50
40
|
if (!results.length)
|
|
51
41
|
return;
|
|
42
|
+
// # Sort and filter results based on the slot lookback setting
|
|
52
43
|
const descResults = results.sort((a, b) => b.slot - a.slot);
|
|
53
44
|
const mostRecentResult = descResults[0];
|
|
45
|
+
const cutoffSlot = mostRecentResult.slot - this.lookbackDistance;
|
|
46
|
+
const resultsToUse = descResults.filter((result) => result.slot >= cutoffSlot);
|
|
47
|
+
// # Handle results
|
|
54
48
|
this.latestPriorityFee = mostRecentResult.prioritizationFee;
|
|
55
49
|
this.lastSlotSeen = mostRecentResult.slot;
|
|
56
|
-
this.lastAvgStrategyResult = this.averageStrategy.calculate(
|
|
57
|
-
this.lastMaxStrategyResult = this.maxStrategy.calculate(
|
|
50
|
+
this.lastAvgStrategyResult = this.averageStrategy.calculate(resultsToUse);
|
|
51
|
+
this.lastMaxStrategyResult = this.maxStrategy.calculate(resultsToUse);
|
|
58
52
|
if (this.customStrategy) {
|
|
59
53
|
this.lastCustomStrategyResult =
|
|
60
|
-
this.customStrategy.calculate(
|
|
54
|
+
this.customStrategy.calculate(resultsToUse);
|
|
61
55
|
}
|
|
62
56
|
}
|
|
63
57
|
async unsubscribe() {
|
package/package.json
CHANGED
package/src/driftClient.ts
CHANGED
|
@@ -2573,21 +2573,12 @@ export class DriftClient {
|
|
|
2573
2573
|
}> {
|
|
2574
2574
|
const marketIndex = orderParams.marketIndex;
|
|
2575
2575
|
const orderId = userAccount.nextOrderId;
|
|
2576
|
-
const bracketOrderIxs = [];
|
|
2577
2576
|
|
|
2578
|
-
const
|
|
2579
|
-
orderParams,
|
|
2577
|
+
const ordersIx = await this.getPlaceOrdersIx(
|
|
2578
|
+
[orderParams, ...bracketOrdersParams],
|
|
2580
2579
|
userAccount.subAccountId
|
|
2581
2580
|
);
|
|
2582
2581
|
|
|
2583
|
-
for (const bracketOrderParams of bracketOrdersParams) {
|
|
2584
|
-
const placeBracketOrderIx = await this.getPlacePerpOrderIx(
|
|
2585
|
-
bracketOrderParams,
|
|
2586
|
-
userAccount.subAccountId
|
|
2587
|
-
);
|
|
2588
|
-
bracketOrderIxs.push(placeBracketOrderIx);
|
|
2589
|
-
}
|
|
2590
|
-
|
|
2591
2582
|
let cancelOrdersIx: TransactionInstruction;
|
|
2592
2583
|
let cancelExistingOrdersTx: Transaction;
|
|
2593
2584
|
if (cancelExistingOrders && isVariant(orderParams.marketType, 'perp')) {
|
|
@@ -2609,7 +2600,7 @@ export class DriftClient {
|
|
|
2609
2600
|
// use versioned transactions if there is a lookup table account and wallet is compatible
|
|
2610
2601
|
if (this.txVersion === 0) {
|
|
2611
2602
|
const versionedMarketOrderTx = await this.buildTransaction(
|
|
2612
|
-
|
|
2603
|
+
ordersIx,
|
|
2613
2604
|
txParams,
|
|
2614
2605
|
0
|
|
2615
2606
|
);
|
|
@@ -2658,15 +2649,11 @@ export class DriftClient {
|
|
|
2658
2649
|
};
|
|
2659
2650
|
} else {
|
|
2660
2651
|
const marketOrderTx = wrapInTx(
|
|
2661
|
-
|
|
2652
|
+
ordersIx,
|
|
2662
2653
|
txParams?.computeUnits,
|
|
2663
2654
|
txParams?.computeUnitsPrice
|
|
2664
2655
|
);
|
|
2665
2656
|
|
|
2666
|
-
if (bracketOrderIxs.length > 0) {
|
|
2667
|
-
marketOrderTx.add(...bracketOrderIxs);
|
|
2668
|
-
}
|
|
2669
|
-
|
|
2670
2657
|
// Apply the latest blockhash to the txs so that we can sign before sending them
|
|
2671
2658
|
const currentBlockHash = (
|
|
2672
2659
|
await this.connection.getLatestBlockhash('finalized')
|
|
@@ -3093,7 +3080,7 @@ export class DriftClient {
|
|
|
3093
3080
|
}
|
|
3094
3081
|
|
|
3095
3082
|
public async getPlaceOrdersIx(
|
|
3096
|
-
params:
|
|
3083
|
+
params: OptionalOrderParams[],
|
|
3097
3084
|
subAccountId?: number
|
|
3098
3085
|
): Promise<TransactionInstruction> {
|
|
3099
3086
|
const user = await this.getUserAccountPublicKey(subAccountId);
|
|
@@ -3118,7 +3105,9 @@ export class DriftClient {
|
|
|
3118
3105
|
useMarketLastSlotCache: true,
|
|
3119
3106
|
});
|
|
3120
3107
|
|
|
3121
|
-
|
|
3108
|
+
const formattedParams = params.map((item) => getOrderParams(item));
|
|
3109
|
+
|
|
3110
|
+
return await this.program.instruction.placeOrders(formattedParams, {
|
|
3122
3111
|
accounts: {
|
|
3123
3112
|
state: await this.getStatePublicKey(),
|
|
3124
3113
|
user,
|
|
@@ -3868,7 +3857,6 @@ export class DriftClient {
|
|
|
3868
3857
|
slippageBps,
|
|
3869
3858
|
swapMode,
|
|
3870
3859
|
onlyDirectRoutes,
|
|
3871
|
-
excludeDexes: ['Raydium CLMM'], // temp exclude to workaround bug with raydium clmm
|
|
3872
3860
|
});
|
|
3873
3861
|
|
|
3874
3862
|
quote = fetchedQuote;
|
|
@@ -4335,6 +4323,69 @@ export class DriftClient {
|
|
|
4335
4323
|
return txSig;
|
|
4336
4324
|
}
|
|
4337
4325
|
|
|
4326
|
+
public async placeAndTakePerpWithAdditionalOrders(
|
|
4327
|
+
orderParams: OptionalOrderParams,
|
|
4328
|
+
makerInfo?: MakerInfo | MakerInfo[],
|
|
4329
|
+
referrerInfo?: ReferrerInfo,
|
|
4330
|
+
bracketOrdersParams = new Array<OptionalOrderParams>(),
|
|
4331
|
+
txParams?: TxParams,
|
|
4332
|
+
subAccountId?: number,
|
|
4333
|
+
cancelExistingOrders?: boolean
|
|
4334
|
+
): Promise<{
|
|
4335
|
+
txSig: TransactionSignature;
|
|
4336
|
+
signedCancelExistingOrdersTx?: Transaction;
|
|
4337
|
+
}> {
|
|
4338
|
+
let signedCancelExistingOrdersTx: Transaction;
|
|
4339
|
+
|
|
4340
|
+
if (cancelExistingOrders && isVariant(orderParams.marketType, 'perp')) {
|
|
4341
|
+
const cancelOrdersIx = await this.getCancelOrdersIx(
|
|
4342
|
+
orderParams.marketType,
|
|
4343
|
+
orderParams.marketIndex,
|
|
4344
|
+
null,
|
|
4345
|
+
subAccountId
|
|
4346
|
+
);
|
|
4347
|
+
|
|
4348
|
+
const cancelExistingOrdersTx = await this.buildTransaction(
|
|
4349
|
+
[cancelOrdersIx],
|
|
4350
|
+
txParams,
|
|
4351
|
+
this.txVersion
|
|
4352
|
+
);
|
|
4353
|
+
|
|
4354
|
+
// @ts-ignore
|
|
4355
|
+
signedCancelExistingOrdersTx = await this.provider.wallet.signTransaction(
|
|
4356
|
+
cancelExistingOrdersTx
|
|
4357
|
+
);
|
|
4358
|
+
}
|
|
4359
|
+
|
|
4360
|
+
const ixs = [];
|
|
4361
|
+
|
|
4362
|
+
const placeAndTakeIx = await this.getPlaceAndTakePerpOrderIx(
|
|
4363
|
+
orderParams,
|
|
4364
|
+
makerInfo,
|
|
4365
|
+
referrerInfo,
|
|
4366
|
+
subAccountId
|
|
4367
|
+
);
|
|
4368
|
+
|
|
4369
|
+
ixs.push(placeAndTakeIx);
|
|
4370
|
+
|
|
4371
|
+
if (bracketOrdersParams.length > 0) {
|
|
4372
|
+
const bracketOrdersIx = await this.getPlaceOrdersIx(
|
|
4373
|
+
bracketOrdersParams,
|
|
4374
|
+
subAccountId
|
|
4375
|
+
);
|
|
4376
|
+
ixs.push(bracketOrdersIx);
|
|
4377
|
+
}
|
|
4378
|
+
|
|
4379
|
+
const { txSig, slot } = await this.sendTransaction(
|
|
4380
|
+
await this.buildTransaction(ixs, txParams),
|
|
4381
|
+
[],
|
|
4382
|
+
this.opts
|
|
4383
|
+
);
|
|
4384
|
+
this.perpMarketLastSlotCache.set(orderParams.marketIndex, slot);
|
|
4385
|
+
|
|
4386
|
+
return { txSig, signedCancelExistingOrdersTx };
|
|
4387
|
+
}
|
|
4388
|
+
|
|
4338
4389
|
public async getPlaceAndTakePerpOrderIx(
|
|
4339
4390
|
orderParams: OptionalOrderParams,
|
|
4340
4391
|
makerInfo?: MakerInfo | MakerInfo[],
|
|
@@ -210,6 +210,12 @@ export interface QuoteResponse {
|
|
|
210
210
|
* @memberof QuoteResponse
|
|
211
211
|
*/
|
|
212
212
|
timeTaken?: number;
|
|
213
|
+
/**
|
|
214
|
+
*
|
|
215
|
+
* @type {string}
|
|
216
|
+
* @memberof QuoteResponse
|
|
217
|
+
*/
|
|
218
|
+
error?: string;
|
|
213
219
|
}
|
|
214
220
|
|
|
215
221
|
export class JupiterClient {
|
|
@@ -279,7 +285,7 @@ export class JupiterClient {
|
|
|
279
285
|
slippageBps = 50,
|
|
280
286
|
swapMode = 'ExactIn',
|
|
281
287
|
onlyDirectRoutes = false,
|
|
282
|
-
excludeDexes
|
|
288
|
+
excludeDexes,
|
|
283
289
|
}: {
|
|
284
290
|
inputMint: PublicKey;
|
|
285
291
|
outputMint: PublicKey;
|
|
@@ -298,7 +304,7 @@ export class JupiterClient {
|
|
|
298
304
|
swapMode,
|
|
299
305
|
onlyDirectRoutes: onlyDirectRoutes.toString(),
|
|
300
306
|
maxAccounts: maxAccounts.toString(),
|
|
301
|
-
excludeDexes: excludeDexes.join(','),
|
|
307
|
+
...(excludeDexes && { excludeDexes: excludeDexes.join(',') }),
|
|
302
308
|
}).toString();
|
|
303
309
|
const quote = await (await fetch(`${this.url}/v6/quote?${params}`)).json();
|
|
304
310
|
return quote;
|
package/src/math/funding.ts
CHANGED
|
@@ -11,6 +11,7 @@ import { PerpMarketAccount, isVariant } from '../types';
|
|
|
11
11
|
import { OraclePriceData } from '../oracles/types';
|
|
12
12
|
import { calculateBidAskPrice } from './amm';
|
|
13
13
|
import { calculateLiveOracleTwap } from './oracles';
|
|
14
|
+
import { clampBN } from './utils';
|
|
14
15
|
|
|
15
16
|
function calculateLiveMarkTwap(
|
|
16
17
|
market: PerpMarketAccount,
|
|
@@ -160,8 +161,15 @@ export async function calculateAllEstimatedFundingRate(
|
|
|
160
161
|
const twapSpreadWithOffset = twapSpread.add(
|
|
161
162
|
oracleTwap.abs().div(FUNDING_RATE_OFFSET_DENOMINATOR)
|
|
162
163
|
);
|
|
164
|
+
const maxSpread = getMaxPriceDivergenceForFundingRate(market, oracleTwap);
|
|
163
165
|
|
|
164
|
-
const
|
|
166
|
+
const clampedSpreadWithOffset = clampBN(
|
|
167
|
+
twapSpreadWithOffset,
|
|
168
|
+
maxSpread.mul(new BN(-1)),
|
|
169
|
+
maxSpread
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
const twapSpreadPct = clampedSpreadWithOffset
|
|
165
173
|
.mul(PRICE_PRECISION)
|
|
166
174
|
.mul(new BN(100))
|
|
167
175
|
.div(oracleTwap);
|
|
@@ -234,6 +242,25 @@ export async function calculateAllEstimatedFundingRate(
|
|
|
234
242
|
return [markTwap, oracleTwap, lowerboundEst, cappedAltEst, interpEst];
|
|
235
243
|
}
|
|
236
244
|
|
|
245
|
+
function getMaxPriceDivergenceForFundingRate(
|
|
246
|
+
market: PerpMarketAccount,
|
|
247
|
+
oracleTwap: BN
|
|
248
|
+
) {
|
|
249
|
+
if (isVariant(market.contractTier, 'a')) {
|
|
250
|
+
return oracleTwap.divn(33);
|
|
251
|
+
} else if (isVariant(market.contractTier, 'b')) {
|
|
252
|
+
return oracleTwap.divn(33);
|
|
253
|
+
} else if (isVariant(market.contractTier, 'c')) {
|
|
254
|
+
return oracleTwap.divn(20);
|
|
255
|
+
} else if (isVariant(market.contractTier, 'speculative')) {
|
|
256
|
+
return oracleTwap.divn(10);
|
|
257
|
+
} else if (isVariant(market.contractTier, 'isolated')) {
|
|
258
|
+
return oracleTwap.divn(10);
|
|
259
|
+
} else {
|
|
260
|
+
return oracleTwap.divn(10);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
237
264
|
/**
|
|
238
265
|
*
|
|
239
266
|
* @param market
|
package/src/math/oracles.ts
CHANGED
|
@@ -47,8 +47,8 @@ export function isOracleValid(
|
|
|
47
47
|
.div(oraclePriceData.price)
|
|
48
48
|
.gt(oracleGuardRails.validity.confidenceIntervalMaxSize);
|
|
49
49
|
|
|
50
|
-
const oracleIsStale =
|
|
51
|
-
.sub(
|
|
50
|
+
const oracleIsStale = new BN(slot)
|
|
51
|
+
.sub(oraclePriceData.slot)
|
|
52
52
|
.gt(oracleGuardRails.validity.slotsBeforeStaleForAmm);
|
|
53
53
|
|
|
54
54
|
return !(
|
package/src/math/superStake.ts
CHANGED
|
@@ -439,7 +439,9 @@ export async function calculateSolEarned({
|
|
|
439
439
|
const now = Date.now() / 1000;
|
|
440
440
|
const timestamps: number[] = [
|
|
441
441
|
now,
|
|
442
|
-
...depositRecords
|
|
442
|
+
...depositRecords
|
|
443
|
+
.filter((r) => r.marketIndex === marketIndex)
|
|
444
|
+
.map((r) => r.ts.toNumber()),
|
|
443
445
|
];
|
|
444
446
|
|
|
445
447
|
let lstRatios = new Map<number, number>();
|
|
@@ -1,30 +1,15 @@
|
|
|
1
1
|
import { PriorityFeeStrategy } from './types';
|
|
2
2
|
|
|
3
3
|
export class AverageOverSlotsStrategy implements PriorityFeeStrategy {
|
|
4
|
-
private lookbackSlots: number;
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* @param lookbackSlots The number of slots to look back from the max slot in the sample
|
|
8
|
-
*/
|
|
9
|
-
constructor(lookbackSlots = 10) {
|
|
10
|
-
this.lookbackSlots = lookbackSlots;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
4
|
calculate(samples: { slot: number; prioritizationFee: number }[]): number {
|
|
14
5
|
if (samples.length === 0) {
|
|
15
6
|
return 0;
|
|
16
7
|
}
|
|
17
|
-
const stopSlot = samples[0].slot - this.lookbackSlots;
|
|
18
8
|
let runningSumFees = 0;
|
|
19
|
-
let countFees = 0;
|
|
20
9
|
|
|
21
10
|
for (let i = 0; i < samples.length; i++) {
|
|
22
|
-
if (samples[i].slot <= stopSlot) {
|
|
23
|
-
return runningSumFees / countFees;
|
|
24
|
-
}
|
|
25
11
|
runningSumFees += samples[i].prioritizationFee;
|
|
26
|
-
countFees++;
|
|
27
12
|
}
|
|
28
|
-
return runningSumFees /
|
|
13
|
+
return runningSumFees / samples.length;
|
|
29
14
|
}
|
|
30
15
|
}
|
|
@@ -1,30 +1,15 @@
|
|
|
1
1
|
import { PriorityFeeStrategy } from './types';
|
|
2
2
|
|
|
3
3
|
export class MaxOverSlotsStrategy implements PriorityFeeStrategy {
|
|
4
|
-
private lookbackSlots: number;
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* @param lookbackSlots The number of slots to look back from the max slot in the sample
|
|
8
|
-
*/
|
|
9
|
-
constructor(lookbackSlots = 10) {
|
|
10
|
-
this.lookbackSlots = lookbackSlots;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
4
|
calculate(samples: { slot: number; prioritizationFee: number }[]): number {
|
|
14
5
|
if (samples.length === 0) {
|
|
15
6
|
return 0;
|
|
16
7
|
}
|
|
17
8
|
// Assuming samples are sorted in descending order of slot.
|
|
18
|
-
const stopSlot = samples[0].slot - this.lookbackSlots;
|
|
19
9
|
let currMaxFee = samples[0].prioritizationFee;
|
|
20
10
|
|
|
21
11
|
for (let i = 0; i < samples.length; i++) {
|
|
22
|
-
|
|
23
|
-
return currMaxFee;
|
|
24
|
-
}
|
|
25
|
-
if (samples[i].prioritizationFee > currMaxFee) {
|
|
26
|
-
currMaxFee = samples[i].prioritizationFee;
|
|
27
|
-
}
|
|
12
|
+
currMaxFee = Math.max(samples[i].prioritizationFee, currMaxFee);
|
|
28
13
|
}
|
|
29
14
|
return currMaxFee;
|
|
30
15
|
}
|
|
@@ -10,16 +10,20 @@ export class PriorityFeeSubscriber {
|
|
|
10
10
|
customStrategy?: PriorityFeeStrategy;
|
|
11
11
|
averageStrategy = new AverageOverSlotsStrategy();
|
|
12
12
|
maxStrategy = new MaxOverSlotsStrategy();
|
|
13
|
+
lookbackDistance: number;
|
|
13
14
|
|
|
14
15
|
intervalId?: ReturnType<typeof setTimeout>;
|
|
15
16
|
|
|
16
17
|
latestPriorityFee = 0;
|
|
17
|
-
lastStrategyResult = 0;
|
|
18
18
|
lastCustomStrategyResult = 0;
|
|
19
19
|
lastAvgStrategyResult = 0;
|
|
20
20
|
lastMaxStrategyResult = 0;
|
|
21
21
|
lastSlotSeen = 0;
|
|
22
22
|
|
|
23
|
+
/**
|
|
24
|
+
* @param props
|
|
25
|
+
* customStrategy : strategy to return the priority fee to use based on recent samples. defaults to AVERAGE.
|
|
26
|
+
*/
|
|
23
27
|
public constructor({
|
|
24
28
|
connection,
|
|
25
29
|
frequencyMs,
|
|
@@ -36,28 +40,12 @@ export class PriorityFeeSubscriber {
|
|
|
36
40
|
this.connection = connection;
|
|
37
41
|
this.frequencyMs = frequencyMs;
|
|
38
42
|
this.addresses = addresses;
|
|
39
|
-
if (
|
|
40
|
-
this.
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
if (customStrategy) {
|
|
43
|
+
if (!customStrategy) {
|
|
44
|
+
this.customStrategy = new AverageOverSlotsStrategy();
|
|
45
|
+
} else {
|
|
44
46
|
this.customStrategy = customStrategy;
|
|
45
47
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
public get avgPriorityFee(): number {
|
|
49
|
-
return Math.floor(this.lastAvgStrategyResult);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
public get maxPriorityFee(): number {
|
|
53
|
-
return Math.floor(this.lastMaxStrategyResult);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
public get customPriorityFee(): number {
|
|
57
|
-
if (!this.customStrategy) {
|
|
58
|
-
console.error('Custom strategy not set');
|
|
59
|
-
}
|
|
60
|
-
return Math.floor(this.lastCustomStrategyResult);
|
|
48
|
+
this.lookbackDistance = slotsToCheck;
|
|
61
49
|
}
|
|
62
50
|
|
|
63
51
|
public async subscribe(): Promise<void> {
|
|
@@ -75,21 +63,29 @@ export class PriorityFeeSubscriber {
|
|
|
75
63
|
[this.addresses]
|
|
76
64
|
);
|
|
77
65
|
|
|
78
|
-
// getRecentPrioritizationFees returns results unsorted
|
|
79
66
|
const results: { slot: number; prioritizationFee: number }[] =
|
|
80
67
|
rpcJSONResponse?.result;
|
|
68
|
+
|
|
81
69
|
if (!results.length) return;
|
|
82
|
-
const descResults = results.sort((a, b) => b.slot - a.slot);
|
|
83
70
|
|
|
71
|
+
// # Sort and filter results based on the slot lookback setting
|
|
72
|
+
const descResults = results.sort((a, b) => b.slot - a.slot);
|
|
84
73
|
const mostRecentResult = descResults[0];
|
|
74
|
+
const cutoffSlot = mostRecentResult.slot - this.lookbackDistance;
|
|
75
|
+
|
|
76
|
+
const resultsToUse = descResults.filter(
|
|
77
|
+
(result) => result.slot >= cutoffSlot
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
// # Handle results
|
|
85
81
|
this.latestPriorityFee = mostRecentResult.prioritizationFee;
|
|
86
82
|
this.lastSlotSeen = mostRecentResult.slot;
|
|
87
83
|
|
|
88
|
-
this.lastAvgStrategyResult = this.averageStrategy.calculate(
|
|
89
|
-
this.lastMaxStrategyResult = this.maxStrategy.calculate(
|
|
84
|
+
this.lastAvgStrategyResult = this.averageStrategy.calculate(resultsToUse);
|
|
85
|
+
this.lastMaxStrategyResult = this.maxStrategy.calculate(resultsToUse);
|
|
90
86
|
if (this.customStrategy) {
|
|
91
87
|
this.lastCustomStrategyResult =
|
|
92
|
-
this.customStrategy.calculate(
|
|
88
|
+
this.customStrategy.calculate(resultsToUse);
|
|
93
89
|
}
|
|
94
90
|
}
|
|
95
91
|
|
package/tests/amm/test.ts
CHANGED
|
@@ -28,6 +28,9 @@ import {
|
|
|
28
28
|
squareRootBN,
|
|
29
29
|
calculateReferencePriceOffset,
|
|
30
30
|
calculateInventoryLiquidityRatio,
|
|
31
|
+
ContractTier,
|
|
32
|
+
isOracleValid,
|
|
33
|
+
OracleGuardRails,
|
|
31
34
|
} from '../../src';
|
|
32
35
|
import { mockPerpMarkets } from '../dlob/helpers';
|
|
33
36
|
|
|
@@ -875,11 +878,12 @@ describe('AMM Tests', () => {
|
|
|
875
878
|
const mockMarket1 = myMockPerpMarkets[0];
|
|
876
879
|
const mockAmm = mockMarket1.amm;
|
|
877
880
|
const now = new BN(new Date().getTime() / 1000); //todo
|
|
881
|
+
const slot = 999999999;
|
|
878
882
|
|
|
879
883
|
const oraclePriceData = {
|
|
880
884
|
price: new BN(13.553 * PRICE_PRECISION.toNumber()),
|
|
881
|
-
slot: new BN(
|
|
882
|
-
confidence: new BN(
|
|
885
|
+
slot: new BN(slot),
|
|
886
|
+
confidence: new BN(1000),
|
|
883
887
|
hasSufficientNumberOfDataPoints: true,
|
|
884
888
|
};
|
|
885
889
|
mockAmm.oracleStd = new BN(0.18 * PRICE_PRECISION.toNumber());
|
|
@@ -901,6 +905,127 @@ describe('AMM Tests', () => {
|
|
|
901
905
|
const liveOracleStd = calculateLiveOracleStd(mockAmm, oraclePriceData, now);
|
|
902
906
|
console.log('liveOracleStd:', liveOracleStd.toNumber());
|
|
903
907
|
assert(liveOracleStd.eq(new BN(192962)));
|
|
908
|
+
|
|
909
|
+
const oracleGuardRails: OracleGuardRails = {
|
|
910
|
+
priceDivergence: {
|
|
911
|
+
markOraclePercentDivergence: PERCENTAGE_PRECISION.divn(10),
|
|
912
|
+
oracleTwap5MinPercentDivergence: PERCENTAGE_PRECISION.divn(10),
|
|
913
|
+
},
|
|
914
|
+
validity: {
|
|
915
|
+
slotsBeforeStaleForAmm: new BN(10),
|
|
916
|
+
slotsBeforeStaleForMargin: new BN(60),
|
|
917
|
+
confidenceIntervalMaxSize: new BN(20000),
|
|
918
|
+
tooVolatileRatio: new BN(5),
|
|
919
|
+
},
|
|
920
|
+
};
|
|
921
|
+
|
|
922
|
+
// good oracle
|
|
923
|
+
assert(isOracleValid(mockAmm, oraclePriceData, oracleGuardRails, slot + 5));
|
|
924
|
+
|
|
925
|
+
// conf too high
|
|
926
|
+
assert(
|
|
927
|
+
!isOracleValid(
|
|
928
|
+
mockAmm,
|
|
929
|
+
{
|
|
930
|
+
price: new BN(13.553 * PRICE_PRECISION.toNumber()),
|
|
931
|
+
slot: new BN(slot),
|
|
932
|
+
confidence: new BN(13.553 * PRICE_PRECISION.toNumber() * 0.021),
|
|
933
|
+
hasSufficientNumberOfDataPoints: true,
|
|
934
|
+
},
|
|
935
|
+
oracleGuardRails,
|
|
936
|
+
slot
|
|
937
|
+
)
|
|
938
|
+
);
|
|
939
|
+
|
|
940
|
+
// not hasSufficientNumberOfDataPoints
|
|
941
|
+
assert(
|
|
942
|
+
!isOracleValid(
|
|
943
|
+
mockAmm,
|
|
944
|
+
{
|
|
945
|
+
price: new BN(13.553 * PRICE_PRECISION.toNumber()),
|
|
946
|
+
slot: new BN(slot),
|
|
947
|
+
confidence: new BN(1),
|
|
948
|
+
hasSufficientNumberOfDataPoints: false,
|
|
949
|
+
},
|
|
950
|
+
oracleGuardRails,
|
|
951
|
+
slot
|
|
952
|
+
)
|
|
953
|
+
);
|
|
954
|
+
|
|
955
|
+
// negative oracle price
|
|
956
|
+
assert(
|
|
957
|
+
!isOracleValid(
|
|
958
|
+
mockAmm,
|
|
959
|
+
{
|
|
960
|
+
price: new BN(-1 * PRICE_PRECISION.toNumber()),
|
|
961
|
+
slot: new BN(slot),
|
|
962
|
+
confidence: new BN(1),
|
|
963
|
+
hasSufficientNumberOfDataPoints: true,
|
|
964
|
+
},
|
|
965
|
+
oracleGuardRails,
|
|
966
|
+
slot
|
|
967
|
+
)
|
|
968
|
+
);
|
|
969
|
+
|
|
970
|
+
// too delayed for amm
|
|
971
|
+
assert(
|
|
972
|
+
!isOracleValid(
|
|
973
|
+
mockAmm,
|
|
974
|
+
{
|
|
975
|
+
price: new BN(13.553 * PRICE_PRECISION.toNumber()),
|
|
976
|
+
slot: new BN(slot),
|
|
977
|
+
confidence: new BN(1),
|
|
978
|
+
hasSufficientNumberOfDataPoints: true,
|
|
979
|
+
},
|
|
980
|
+
oracleGuardRails,
|
|
981
|
+
slot + 100
|
|
982
|
+
)
|
|
983
|
+
);
|
|
984
|
+
|
|
985
|
+
// im passing stale slot (should not call oracle invalid)
|
|
986
|
+
assert(
|
|
987
|
+
isOracleValid(
|
|
988
|
+
mockAmm,
|
|
989
|
+
{
|
|
990
|
+
price: new BN(13.553 * PRICE_PRECISION.toNumber()),
|
|
991
|
+
slot: new BN(slot + 100),
|
|
992
|
+
confidence: new BN(1),
|
|
993
|
+
hasSufficientNumberOfDataPoints: true,
|
|
994
|
+
},
|
|
995
|
+
oracleGuardRails,
|
|
996
|
+
slot
|
|
997
|
+
)
|
|
998
|
+
);
|
|
999
|
+
|
|
1000
|
+
// too volatile (more than 5x higher)
|
|
1001
|
+
assert(
|
|
1002
|
+
!isOracleValid(
|
|
1003
|
+
mockAmm,
|
|
1004
|
+
{
|
|
1005
|
+
price: new BN(113.553 * PRICE_PRECISION.toNumber()),
|
|
1006
|
+
slot: new BN(slot + 5),
|
|
1007
|
+
confidence: new BN(1),
|
|
1008
|
+
hasSufficientNumberOfDataPoints: true,
|
|
1009
|
+
},
|
|
1010
|
+
oracleGuardRails,
|
|
1011
|
+
slot
|
|
1012
|
+
)
|
|
1013
|
+
);
|
|
1014
|
+
|
|
1015
|
+
// too volatile (more than 1/5 lower)
|
|
1016
|
+
assert(
|
|
1017
|
+
!isOracleValid(
|
|
1018
|
+
mockAmm,
|
|
1019
|
+
{
|
|
1020
|
+
price: new BN(0.553 * PRICE_PRECISION.toNumber()),
|
|
1021
|
+
slot: new BN(slot + 5),
|
|
1022
|
+
confidence: new BN(1),
|
|
1023
|
+
hasSufficientNumberOfDataPoints: true,
|
|
1024
|
+
},
|
|
1025
|
+
oracleGuardRails,
|
|
1026
|
+
slot
|
|
1027
|
+
)
|
|
1028
|
+
);
|
|
904
1029
|
});
|
|
905
1030
|
|
|
906
1031
|
it('predicted funding rate mock1', async () => {
|
|
@@ -1065,6 +1190,154 @@ describe('AMM Tests', () => {
|
|
|
1065
1190
|
assert(est2.eq(new BN('-719')));
|
|
1066
1191
|
});
|
|
1067
1192
|
|
|
1193
|
+
it('predicted funding rate mock clamp', async () => {
|
|
1194
|
+
const myMockPerpMarkets = _.cloneDeep(mockPerpMarkets);
|
|
1195
|
+
const mockMarket1 = myMockPerpMarkets[0];
|
|
1196
|
+
|
|
1197
|
+
// make it like OP
|
|
1198
|
+
const now = new BN(1688881915);
|
|
1199
|
+
|
|
1200
|
+
mockMarket1.amm.fundingPeriod = new BN(3600);
|
|
1201
|
+
mockMarket1.amm.lastFundingRateTs = new BN(1688864415);
|
|
1202
|
+
|
|
1203
|
+
const currentMarkPrice = new BN(1.2242 * PRICE_PRECISION.toNumber()); // trading at a premium
|
|
1204
|
+
const oraclePriceData: OraclePriceData = {
|
|
1205
|
+
price: new BN(1.924 * PRICE_PRECISION.toNumber()),
|
|
1206
|
+
slot: new BN(0),
|
|
1207
|
+
confidence: new BN(1),
|
|
1208
|
+
hasSufficientNumberOfDataPoints: true,
|
|
1209
|
+
};
|
|
1210
|
+
mockMarket1.amm.historicalOracleData.lastOraclePrice = new BN(
|
|
1211
|
+
1.9535 * PRICE_PRECISION.toNumber()
|
|
1212
|
+
);
|
|
1213
|
+
|
|
1214
|
+
// mockMarket1.amm.pegMultiplier = new BN(1.897573 * 1e3);
|
|
1215
|
+
|
|
1216
|
+
mockMarket1.amm.lastMarkPriceTwap = new BN(
|
|
1217
|
+
1.218363 * PRICE_PRECISION.toNumber()
|
|
1218
|
+
);
|
|
1219
|
+
mockMarket1.amm.lastBidPriceTwap = new BN(
|
|
1220
|
+
1.218363 * PRICE_PRECISION.toNumber()
|
|
1221
|
+
);
|
|
1222
|
+
mockMarket1.amm.lastAskPriceTwap = new BN(
|
|
1223
|
+
1.218364 * PRICE_PRECISION.toNumber()
|
|
1224
|
+
);
|
|
1225
|
+
mockMarket1.amm.lastMarkPriceTwapTs = new BN(1688878815);
|
|
1226
|
+
|
|
1227
|
+
mockMarket1.amm.historicalOracleData.lastOraclePriceTwap = new BN(
|
|
1228
|
+
1.820964 * PRICE_PRECISION.toNumber()
|
|
1229
|
+
);
|
|
1230
|
+
mockMarket1.amm.historicalOracleData.lastOraclePriceTwapTs = new BN(
|
|
1231
|
+
1688879991
|
|
1232
|
+
);
|
|
1233
|
+
mockMarket1.contractTier = ContractTier.A;
|
|
1234
|
+
|
|
1235
|
+
const [
|
|
1236
|
+
_markTwapLive,
|
|
1237
|
+
_oracleTwapLive,
|
|
1238
|
+
_lowerboundEst,
|
|
1239
|
+
_cappedAltEst,
|
|
1240
|
+
_interpEst,
|
|
1241
|
+
] = await calculateAllEstimatedFundingRate(
|
|
1242
|
+
mockMarket1,
|
|
1243
|
+
oraclePriceData,
|
|
1244
|
+
currentMarkPrice,
|
|
1245
|
+
now
|
|
1246
|
+
);
|
|
1247
|
+
|
|
1248
|
+
// console.log(_markTwapLive.toString());
|
|
1249
|
+
// console.log(_oracleTwapLive.toString());
|
|
1250
|
+
// console.log(_lowerboundEst.toString());
|
|
1251
|
+
// console.log(_cappedAltEst.toString());
|
|
1252
|
+
// console.log(_interpEst.toString());
|
|
1253
|
+
// console.log('-----');
|
|
1254
|
+
|
|
1255
|
+
let [markTwapLive, oracleTwapLive, est1, est2] =
|
|
1256
|
+
await calculateLongShortFundingRateAndLiveTwaps(
|
|
1257
|
+
mockMarket1,
|
|
1258
|
+
oraclePriceData,
|
|
1259
|
+
currentMarkPrice,
|
|
1260
|
+
now
|
|
1261
|
+
);
|
|
1262
|
+
|
|
1263
|
+
console.log(
|
|
1264
|
+
'markTwapLive:',
|
|
1265
|
+
mockMarket1.amm.lastMarkPriceTwap.toString(),
|
|
1266
|
+
'->',
|
|
1267
|
+
markTwapLive.toString()
|
|
1268
|
+
);
|
|
1269
|
+
console.log(
|
|
1270
|
+
'oracTwapLive:',
|
|
1271
|
+
mockMarket1.amm.historicalOracleData.lastOraclePriceTwap.toString(),
|
|
1272
|
+
'->',
|
|
1273
|
+
oracleTwapLive.toString()
|
|
1274
|
+
);
|
|
1275
|
+
console.log('pred funding:', est1.toString(), est2.toString());
|
|
1276
|
+
|
|
1277
|
+
assert(markTwapLive.eq(new BN('1680634')));
|
|
1278
|
+
assert(oracleTwapLive.eq(new BN('1876031')));
|
|
1279
|
+
assert(est1.eq(est2));
|
|
1280
|
+
assert(est2.eq(new BN('-126261')));
|
|
1281
|
+
|
|
1282
|
+
mockMarket1.contractTier = ContractTier.C;
|
|
1283
|
+
|
|
1284
|
+
[markTwapLive, oracleTwapLive, est1, est2] =
|
|
1285
|
+
await calculateLongShortFundingRateAndLiveTwaps(
|
|
1286
|
+
mockMarket1,
|
|
1287
|
+
oraclePriceData,
|
|
1288
|
+
currentMarkPrice,
|
|
1289
|
+
now
|
|
1290
|
+
);
|
|
1291
|
+
|
|
1292
|
+
console.log(
|
|
1293
|
+
'markTwapLive:',
|
|
1294
|
+
mockMarket1.amm.lastMarkPriceTwap.toString(),
|
|
1295
|
+
'->',
|
|
1296
|
+
markTwapLive.toString()
|
|
1297
|
+
);
|
|
1298
|
+
console.log(
|
|
1299
|
+
'oracTwapLive:',
|
|
1300
|
+
mockMarket1.amm.historicalOracleData.lastOraclePriceTwap.toString(),
|
|
1301
|
+
'->',
|
|
1302
|
+
oracleTwapLive.toString()
|
|
1303
|
+
);
|
|
1304
|
+
console.log('pred funding:', est1.toString(), est2.toString());
|
|
1305
|
+
|
|
1306
|
+
assert(markTwapLive.eq(new BN('1680634')));
|
|
1307
|
+
assert(oracleTwapLive.eq(new BN('1876031')));
|
|
1308
|
+
assert(est1.eq(est2));
|
|
1309
|
+
assert(est2.eq(new BN('-208332')));
|
|
1310
|
+
|
|
1311
|
+
mockMarket1.contractTier = ContractTier.SPECULATIVE;
|
|
1312
|
+
|
|
1313
|
+
[markTwapLive, oracleTwapLive, est1, est2] =
|
|
1314
|
+
await calculateLongShortFundingRateAndLiveTwaps(
|
|
1315
|
+
mockMarket1,
|
|
1316
|
+
oraclePriceData,
|
|
1317
|
+
currentMarkPrice,
|
|
1318
|
+
now
|
|
1319
|
+
);
|
|
1320
|
+
|
|
1321
|
+
console.log(
|
|
1322
|
+
'markTwapLive:',
|
|
1323
|
+
mockMarket1.amm.lastMarkPriceTwap.toString(),
|
|
1324
|
+
'->',
|
|
1325
|
+
markTwapLive.toString()
|
|
1326
|
+
);
|
|
1327
|
+
console.log(
|
|
1328
|
+
'oracTwapLive:',
|
|
1329
|
+
mockMarket1.amm.historicalOracleData.lastOraclePriceTwap.toString(),
|
|
1330
|
+
'->',
|
|
1331
|
+
oracleTwapLive.toString()
|
|
1332
|
+
);
|
|
1333
|
+
console.log('pred funding:', est1.toString(), est2.toString());
|
|
1334
|
+
|
|
1335
|
+
assert(markTwapLive.eq(new BN('1680634')));
|
|
1336
|
+
assert(oracleTwapLive.eq(new BN('1876031')));
|
|
1337
|
+
assert(est1.eq(est2));
|
|
1338
|
+
assert(est2.eq(new BN('-416666')));
|
|
1339
|
+
});
|
|
1340
|
+
|
|
1068
1341
|
it('orderbook L2 gen (no topOfBookQuoteAmounts, 10 numOrders, low liquidity)', async () => {
|
|
1069
1342
|
const myMockPerpMarkets = _.cloneDeep(mockPerpMarkets);
|
|
1070
1343
|
|
package/tests/dlob/test.ts
CHANGED
|
@@ -76,7 +76,7 @@ function insertOrderToDLOB(
|
|
|
76
76
|
auctionEndPrice,
|
|
77
77
|
maxTs,
|
|
78
78
|
},
|
|
79
|
-
userAccount,
|
|
79
|
+
userAccount.toString(),
|
|
80
80
|
slot.toNumber()
|
|
81
81
|
);
|
|
82
82
|
}
|
|
@@ -127,7 +127,7 @@ function insertTriggerOrderToDLOB(
|
|
|
127
127
|
auctionEndPrice,
|
|
128
128
|
maxTs,
|
|
129
129
|
},
|
|
130
|
-
userAccount,
|
|
130
|
+
userAccount.toString(),
|
|
131
131
|
slot.toNumber()
|
|
132
132
|
);
|
|
133
133
|
}
|
|
@@ -66,7 +66,7 @@ describe('PriorityFeeStrategy', () => {
|
|
|
66
66
|
});
|
|
67
67
|
|
|
68
68
|
it('MaxOverSlotsStrategy should calculate the max prioritization fee over slots', () => {
|
|
69
|
-
const maxOverSlotsStrategy = new MaxOverSlotsStrategy(
|
|
69
|
+
const maxOverSlotsStrategy = new MaxOverSlotsStrategy();
|
|
70
70
|
const samples = [
|
|
71
71
|
{ slot: 6, prioritizationFee: 432 },
|
|
72
72
|
{ slot: 3, prioritizationFee: 543 },
|
|
@@ -80,7 +80,7 @@ describe('PriorityFeeStrategy', () => {
|
|
|
80
80
|
});
|
|
81
81
|
|
|
82
82
|
it('AverageOverSlotsStrategy should calculate the average prioritization fee over slots', () => {
|
|
83
|
-
const averageOverSlotsStrategy = new AverageOverSlotsStrategy(
|
|
83
|
+
const averageOverSlotsStrategy = new AverageOverSlotsStrategy();
|
|
84
84
|
const samples = [
|
|
85
85
|
{ slot: 6, prioritizationFee: 432 },
|
|
86
86
|
{ slot: 3, prioritizationFee: 543 },
|