@drift-labs/sdk 2.52.0-beta.5 → 2.52.0-beta.6
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/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/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/package.json +1 -1
- package/src/priorityFee/averageOverSlotsStrategy.ts +30 -0
- package/src/priorityFee/averageStrategy.ts +11 -0
- package/src/priorityFee/ewmaStrategy.ts +40 -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/tests/tx/priorityFeeStrategy.ts +97 -0
- package/tests/user/helpers.ts +1 -0
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.52.0-beta.
|
|
1
|
+
2.52.0-beta.6
|
|
@@ -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,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) {
|
package/package.json
CHANGED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { PriorityFeeStrategy } from './types';
|
|
2
|
+
|
|
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
|
+
calculate(samples: { slot: number; prioritizationFee: number }[]): number {
|
|
14
|
+
if (samples.length === 0) {
|
|
15
|
+
return 0;
|
|
16
|
+
}
|
|
17
|
+
const stopSlot = samples[0].slot - this.lookbackSlots;
|
|
18
|
+
let runningSumFees = 0;
|
|
19
|
+
let countFees = 0;
|
|
20
|
+
|
|
21
|
+
for (let i = 0; i < samples.length; i++) {
|
|
22
|
+
if (samples[i].slot <= stopSlot) {
|
|
23
|
+
return runningSumFees / countFees;
|
|
24
|
+
}
|
|
25
|
+
runningSumFees += samples[i].prioritizationFee;
|
|
26
|
+
countFees++;
|
|
27
|
+
}
|
|
28
|
+
return runningSumFees / countFees;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { PriorityFeeStrategy } from './types';
|
|
2
|
+
|
|
3
|
+
export class AverageStrategy implements PriorityFeeStrategy {
|
|
4
|
+
calculate(samples: { slot: number; prioritizationFee: number }[]): number {
|
|
5
|
+
return (
|
|
6
|
+
samples.reduce((a, b) => {
|
|
7
|
+
return a + b.prioritizationFee;
|
|
8
|
+
}, 0) / samples.length
|
|
9
|
+
);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { PriorityFeeStrategy } from './types';
|
|
2
|
+
|
|
3
|
+
class EwmaStrategy implements PriorityFeeStrategy {
|
|
4
|
+
private halfLife: number;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @param halfLife The half life of the EWMA in slots. Default is 25 slots, approx 10 seconds.
|
|
8
|
+
*/
|
|
9
|
+
constructor(halfLife = 25) {
|
|
10
|
+
this.halfLife = halfLife;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// samples provided in desc slot order
|
|
14
|
+
calculate(samples: { slot: number; prioritizationFee: number }[]): number {
|
|
15
|
+
if (samples.length === 0) {
|
|
16
|
+
return 0;
|
|
17
|
+
}
|
|
18
|
+
if (samples.length === 1) {
|
|
19
|
+
return samples[0].prioritizationFee;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let ewma = 0;
|
|
23
|
+
|
|
24
|
+
const samplesReversed = samples.slice().reverse();
|
|
25
|
+
for (let i = 0; i < samplesReversed.length; i++) {
|
|
26
|
+
if (i === 0) {
|
|
27
|
+
ewma = samplesReversed[i].prioritizationFee;
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
const gap = samplesReversed[i].slot - samplesReversed[i - 1].slot;
|
|
31
|
+
const alpha = 1 - Math.exp((Math.log(0.5) / this.halfLife) * gap);
|
|
32
|
+
|
|
33
|
+
ewma = alpha * samplesReversed[i].prioritizationFee + (1 - alpha) * ewma;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return ewma;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export { EwmaStrategy };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { PriorityFeeStrategy } from './types';
|
|
2
|
+
|
|
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
|
+
calculate(samples: { slot: number; prioritizationFee: number }[]): number {
|
|
14
|
+
if (samples.length === 0) {
|
|
15
|
+
return 0;
|
|
16
|
+
}
|
|
17
|
+
// Assuming samples are sorted in descending order of slot.
|
|
18
|
+
const stopSlot = samples[0].slot - this.lookbackSlots;
|
|
19
|
+
let currMaxFee = samples[0].prioritizationFee;
|
|
20
|
+
|
|
21
|
+
for (let i = 0; i < samples.length; i++) {
|
|
22
|
+
if (samples[i].slot <= stopSlot) {
|
|
23
|
+
return currMaxFee;
|
|
24
|
+
}
|
|
25
|
+
if (samples[i].prioritizationFee > currMaxFee) {
|
|
26
|
+
currMaxFee = samples[i].prioritizationFee;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return currMaxFee;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -1,35 +1,63 @@
|
|
|
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
|
|
|
3
6
|
export class PriorityFeeSubscriber {
|
|
4
7
|
connection: Connection;
|
|
5
8
|
frequencyMs: number;
|
|
6
9
|
addresses: PublicKey[];
|
|
7
|
-
|
|
10
|
+
customStrategy?: PriorityFeeStrategy;
|
|
11
|
+
averageStrategy = new AverageOverSlotsStrategy();
|
|
12
|
+
maxStrategy = new MaxOverSlotsStrategy();
|
|
8
13
|
|
|
9
14
|
intervalId?: ReturnType<typeof setTimeout>;
|
|
10
15
|
|
|
11
16
|
latestPriorityFee = 0;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
17
|
+
lastStrategyResult = 0;
|
|
18
|
+
lastCustomStrategyResult = 0;
|
|
19
|
+
lastAvgStrategyResult = 0;
|
|
20
|
+
lastMaxStrategyResult = 0;
|
|
16
21
|
lastSlotSeen = 0;
|
|
17
22
|
|
|
18
23
|
public constructor({
|
|
19
24
|
connection,
|
|
20
25
|
frequencyMs,
|
|
21
26
|
addresses,
|
|
27
|
+
customStrategy,
|
|
22
28
|
slotsToCheck = 10,
|
|
23
29
|
}: {
|
|
24
30
|
connection: Connection;
|
|
25
31
|
frequencyMs: number;
|
|
26
32
|
addresses: PublicKey[];
|
|
33
|
+
customStrategy?: PriorityFeeStrategy;
|
|
27
34
|
slotsToCheck?: number;
|
|
28
35
|
}) {
|
|
29
36
|
this.connection = connection;
|
|
30
37
|
this.frequencyMs = frequencyMs;
|
|
31
38
|
this.addresses = addresses;
|
|
32
|
-
|
|
39
|
+
if (slotsToCheck) {
|
|
40
|
+
this.averageStrategy = new AverageOverSlotsStrategy(slotsToCheck);
|
|
41
|
+
this.maxStrategy = new MaxOverSlotsStrategy(slotsToCheck);
|
|
42
|
+
}
|
|
43
|
+
if (customStrategy) {
|
|
44
|
+
this.customStrategy = customStrategy;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
public get avgPriorityFee(): number {
|
|
49
|
+
return this.lastAvgStrategyResult;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
public get maxPriorityFee(): number {
|
|
53
|
+
return this.lastMaxStrategyResult;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
public get customPriorityFee(): number {
|
|
57
|
+
if (!this.customStrategy) {
|
|
58
|
+
console.error('Custom strategy not set');
|
|
59
|
+
}
|
|
60
|
+
return this.lastCustomStrategyResult;
|
|
33
61
|
}
|
|
34
62
|
|
|
35
63
|
public async subscribe(): Promise<void> {
|
|
@@ -47,23 +75,22 @@ export class PriorityFeeSubscriber {
|
|
|
47
75
|
[this.addresses]
|
|
48
76
|
);
|
|
49
77
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
if (!descResults?.length) return;
|
|
78
|
+
// getRecentPrioritizationFees returns results unsorted
|
|
79
|
+
const results: { slot: number; prioritizationFee: number }[] =
|
|
80
|
+
rpcJSONResponse?.result;
|
|
81
|
+
if (!results.length) return;
|
|
82
|
+
const descResults = results.sort((a, b) => b.slot - a.slot);
|
|
56
83
|
|
|
57
84
|
const mostRecentResult = descResults[0];
|
|
58
85
|
this.latestPriorityFee = mostRecentResult.prioritizationFee;
|
|
59
86
|
this.lastSlotSeen = mostRecentResult.slot;
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
87
|
+
|
|
88
|
+
this.lastAvgStrategyResult = this.averageStrategy.calculate(descResults);
|
|
89
|
+
this.lastMaxStrategyResult = this.maxStrategy.calculate(descResults);
|
|
90
|
+
if (this.customStrategy) {
|
|
91
|
+
this.lastCustomStrategyResult =
|
|
92
|
+
this.customStrategy.calculate(descResults);
|
|
93
|
+
}
|
|
67
94
|
}
|
|
68
95
|
|
|
69
96
|
public async unsubscribe(): Promise<void> {
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { expect } from 'chai';
|
|
2
|
+
import { AverageStrategy } from '../../src/priorityFee/averageStrategy';
|
|
3
|
+
import { MaxStrategy } from '../../src/priorityFee/maxStrategy';
|
|
4
|
+
import { EwmaStrategy } from '../../src/priorityFee/ewmaStrategy';
|
|
5
|
+
import { MaxOverSlotsStrategy } from '../../src/priorityFee/maxOverSlotsStrategy';
|
|
6
|
+
import { AverageOverSlotsStrategy } from '../../src/priorityFee/averageOverSlotsStrategy';
|
|
7
|
+
|
|
8
|
+
describe('PriorityFeeStrategy', () => {
|
|
9
|
+
it('AverageStrategy should calculate the average prioritization fee', () => {
|
|
10
|
+
const averageStrategy = new AverageStrategy();
|
|
11
|
+
const samples = [
|
|
12
|
+
{ slot: 3, prioritizationFee: 300 },
|
|
13
|
+
{ slot: 2, prioritizationFee: 200 },
|
|
14
|
+
{ slot: 1, prioritizationFee: 100 },
|
|
15
|
+
];
|
|
16
|
+
const average = averageStrategy.calculate(samples);
|
|
17
|
+
expect(average).to.equal(200);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('MaxStrategy should calculate the maximum prioritization fee', () => {
|
|
21
|
+
const maxStrategy = new MaxStrategy();
|
|
22
|
+
const samples = [
|
|
23
|
+
{ slot: 3, prioritizationFee: 300 },
|
|
24
|
+
{ slot: 2, prioritizationFee: 200 },
|
|
25
|
+
{ slot: 1, prioritizationFee: 100 },
|
|
26
|
+
];
|
|
27
|
+
const max = maxStrategy.calculate(samples);
|
|
28
|
+
expect(max).to.equal(300);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('EwmaStrategy should calculate the ewma prioritization fee', () => {
|
|
32
|
+
// halflife of 5 alots
|
|
33
|
+
const ewmaStrategy = new EwmaStrategy(5);
|
|
34
|
+
const samples = [
|
|
35
|
+
{ slot: 6, prioritizationFee: 0 },
|
|
36
|
+
{ slot: 2, prioritizationFee: 0 },
|
|
37
|
+
{ slot: 2, prioritizationFee: 0 },
|
|
38
|
+
{ slot: 2, prioritizationFee: 0 },
|
|
39
|
+
{ slot: 2, prioritizationFee: 0 },
|
|
40
|
+
{ slot: 1, prioritizationFee: 1000 },
|
|
41
|
+
];
|
|
42
|
+
const ewma = ewmaStrategy.calculate(samples);
|
|
43
|
+
expect(ewma).to.be.approximately(500, 0.00001);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('EwmaStrategy should calculate the ewma prioritization fee, length 1', () => {
|
|
47
|
+
// halflife of 5 alots
|
|
48
|
+
const ewmaStrategy = new EwmaStrategy(5);
|
|
49
|
+
const samples = [
|
|
50
|
+
{ slot: 6, prioritizationFee: 1000 },
|
|
51
|
+
];
|
|
52
|
+
const ewma = ewmaStrategy.calculate(samples);
|
|
53
|
+
expect(ewma).to.be.approximately(1000, 0.00001);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('EwmaStrategy should calculate the ewma prioritization fee, length 6', () => {
|
|
57
|
+
const ewmaStrategy = new EwmaStrategy(5);
|
|
58
|
+
const samples = [
|
|
59
|
+
{ slot: 6, prioritizationFee: 1000 },
|
|
60
|
+
{ slot: 5, prioritizationFee: 570 },
|
|
61
|
+
{ slot: 4, prioritizationFee: 860 },
|
|
62
|
+
{ slot: 3, prioritizationFee: 530 },
|
|
63
|
+
{ slot: 2, prioritizationFee: 701 },
|
|
64
|
+
{ slot: 1, prioritizationFee: 230 },
|
|
65
|
+
];
|
|
66
|
+
const ewma = ewmaStrategy.calculate(samples);
|
|
67
|
+
expect(ewma).to.be.approximately(490.43706, 0.00001);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('MaxOverSlotsStrategy should calculate the max prioritization fee over slots', () => {
|
|
71
|
+
const maxOverSlotsStrategy = new MaxOverSlotsStrategy(5);
|
|
72
|
+
const samples = [
|
|
73
|
+
{ slot: 6, prioritizationFee: 432 },
|
|
74
|
+
{ slot: 3, prioritizationFee: 543 },
|
|
75
|
+
{ slot: 3, prioritizationFee: 342 },
|
|
76
|
+
{ slot: 3, prioritizationFee: 832 },
|
|
77
|
+
{ slot: 2, prioritizationFee: 123 },
|
|
78
|
+
{ slot: 1, prioritizationFee: 1000 },
|
|
79
|
+
];
|
|
80
|
+
const maxOverSlots = maxOverSlotsStrategy.calculate(samples);
|
|
81
|
+
expect(maxOverSlots).to.equal(832);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('AverageOverSlotsStrategy should calculate the average prioritization fee over slots', () => {
|
|
85
|
+
const averageOverSlotsStrategy = new AverageOverSlotsStrategy(5);
|
|
86
|
+
const samples = [
|
|
87
|
+
{ slot: 6, prioritizationFee: 432 },
|
|
88
|
+
{ slot: 3, prioritizationFee: 543 },
|
|
89
|
+
{ slot: 3, prioritizationFee: 342 },
|
|
90
|
+
{ slot: 3, prioritizationFee: 832 },
|
|
91
|
+
{ slot: 2, prioritizationFee: 123 },
|
|
92
|
+
{ slot: 1, prioritizationFee: 1000 },
|
|
93
|
+
];
|
|
94
|
+
const averageOverSlots = averageOverSlotsStrategy.calculate(samples);
|
|
95
|
+
expect(averageOverSlots).to.equal(454.4);
|
|
96
|
+
});
|
|
97
|
+
});
|
package/tests/user/helpers.ts
CHANGED