From 75ca119226c61b4e9877701277cf61bd6ddb0e78 Mon Sep 17 00:00:00 2001 From: wphan Date: Tue, 23 Jan 2024 23:22:20 -0800 Subject: [PATCH 1/4] add helius method to PriorityFeeSubscriber --- .../priorityFee/averageOverSlotsStrategy.ts | 3 +- sdk/src/priorityFee/averageStrategy.ts | 3 +- sdk/src/priorityFee/ewmaStrategy.ts | 3 +- .../priorityFee/heliusPriorityFeeMethod.ts | 54 +++++++ sdk/src/priorityFee/index.ts | 2 + sdk/src/priorityFee/maxOverSlotsStrategy.ts | 3 +- sdk/src/priorityFee/priorityFeeSubscriber.ts | 135 ++++++++++++------ .../priorityFee/solanaPriorityFeeMethod.ts | 28 ++++ sdk/src/priorityFee/types.ts | 29 +++- sdk/src/tx/baseTxSender.ts | 6 +- sdk/src/tx/retryTxSender.ts | 6 +- sdk/tests/tx/priorityFeeStrategy.ts | 4 +- 12 files changed, 218 insertions(+), 58 deletions(-) create mode 100644 sdk/src/priorityFee/heliusPriorityFeeMethod.ts create mode 100644 sdk/src/priorityFee/solanaPriorityFeeMethod.ts diff --git a/sdk/src/priorityFee/averageOverSlotsStrategy.ts b/sdk/src/priorityFee/averageOverSlotsStrategy.ts index 334100561..c4ba4a135 100644 --- a/sdk/src/priorityFee/averageOverSlotsStrategy.ts +++ b/sdk/src/priorityFee/averageOverSlotsStrategy.ts @@ -1,7 +1,8 @@ +import { SolanaPriorityFeeResponse } from './solanaPriorityFeeMethod'; import { PriorityFeeStrategy } from './types'; export class AverageOverSlotsStrategy implements PriorityFeeStrategy { - calculate(samples: { slot: number; prioritizationFee: number }[]): number { + calculate(samples: SolanaPriorityFeeResponse[]): number { if (samples.length === 0) { return 0; } diff --git a/sdk/src/priorityFee/averageStrategy.ts b/sdk/src/priorityFee/averageStrategy.ts index 024d9f315..d5dc99b60 100644 --- a/sdk/src/priorityFee/averageStrategy.ts +++ b/sdk/src/priorityFee/averageStrategy.ts @@ -1,7 +1,8 @@ +import { SolanaPriorityFeeResponse } from './solanaPriorityFeeMethod'; import { PriorityFeeStrategy } from './types'; export class AverageStrategy implements PriorityFeeStrategy { - calculate(samples: { slot: number; prioritizationFee: number }[]): number { + calculate(samples: SolanaPriorityFeeResponse[]): number { return ( samples.reduce((a, b) => { return a + b.prioritizationFee; diff --git a/sdk/src/priorityFee/ewmaStrategy.ts b/sdk/src/priorityFee/ewmaStrategy.ts index 7a2296135..d37a0216e 100644 --- a/sdk/src/priorityFee/ewmaStrategy.ts +++ b/sdk/src/priorityFee/ewmaStrategy.ts @@ -1,3 +1,4 @@ +import { SolanaPriorityFeeResponse } from './solanaPriorityFeeMethod'; import { PriorityFeeStrategy } from './types'; class EwmaStrategy implements PriorityFeeStrategy { @@ -11,7 +12,7 @@ class EwmaStrategy implements PriorityFeeStrategy { } // samples provided in desc slot order - calculate(samples: { slot: number; prioritizationFee: number }[]): number { + calculate(samples: SolanaPriorityFeeResponse[]): number { if (samples.length === 0) { return 0; } diff --git a/sdk/src/priorityFee/heliusPriorityFeeMethod.ts b/sdk/src/priorityFee/heliusPriorityFeeMethod.ts new file mode 100644 index 000000000..d7976b97a --- /dev/null +++ b/sdk/src/priorityFee/heliusPriorityFeeMethod.ts @@ -0,0 +1,54 @@ +import { PublicKey } from '@solana/web3.js'; +import fetch from 'node-fetch'; + +export enum HeliusPriorityLevel { + MIN = 'min', // 25th percentile + LOW = 'low', // 25th percentile + MEDIUM = 'medium', // 50th percentile + HIGH = 'high', // 75th percentile + VERY_HIGH = 'veryHigh', // 95th percentile + UNSAFE_MAX = 'unsafeMax', // 100th percentile +} + +export type HeliusPriorityFeeLevels = { + [key in HeliusPriorityLevel]: number; +}; + +export type HeliusPriorityFeeResponse = { + jsonrpc: string; + result: { + priorityFeeEstimate?: number; + priorityFeeLevels?: HeliusPriorityFeeLevels; + }; + id: string; +}; + +const heliusUrlBase = 'https://mainnet.helius-rpc.com/?api-key='; + +/// Fetches the priority fee from the Helius API +/// https://docs.helius.dev/solana-rpc-nodes/alpha-priority-fee-api +export async function fetchHeliusPriorityFee( + apiKey: string, + lookbackDistance: number, + addresses: PublicKey[] +): Promise { + const response = await fetch(heliusUrlBase + apiKey, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + jsonrpc: '2.0', + id: '1', + method: 'getPriorityFeeEstimate', + params: [ + { + accountKeys: addresses.map((address) => address.toBase58()), + options: { + includeAllPriorityFeeLevels: true, + lookbackSlots: lookbackDistance, + }, + }, + ], + }), + }); + return await response.json(); +} diff --git a/sdk/src/priorityFee/index.ts b/sdk/src/priorityFee/index.ts index 4772f9190..55221f834 100644 --- a/sdk/src/priorityFee/index.ts +++ b/sdk/src/priorityFee/index.ts @@ -4,4 +4,6 @@ export * from './ewmaStrategy'; export * from './maxOverSlotsStrategy'; export * from './maxStrategy'; export * from './priorityFeeSubscriber'; +export * from './solanaPriorityFeeMethod'; +export * from './heliusPriorityFeeMethod'; export * from './types'; diff --git a/sdk/src/priorityFee/maxOverSlotsStrategy.ts b/sdk/src/priorityFee/maxOverSlotsStrategy.ts index dfda6a0df..cbaa5f033 100644 --- a/sdk/src/priorityFee/maxOverSlotsStrategy.ts +++ b/sdk/src/priorityFee/maxOverSlotsStrategy.ts @@ -1,7 +1,8 @@ +import { SolanaPriorityFeeResponse } from './solanaPriorityFeeMethod'; import { PriorityFeeStrategy } from './types'; export class MaxOverSlotsStrategy implements PriorityFeeStrategy { - calculate(samples: { slot: number; prioritizationFee: number }[]): number { + calculate(samples: SolanaPriorityFeeResponse[]): number { if (samples.length === 0) { return 0; } diff --git a/sdk/src/priorityFee/priorityFeeSubscriber.ts b/sdk/src/priorityFee/priorityFeeSubscriber.ts index faeb9e989..9f456e270 100644 --- a/sdk/src/priorityFee/priorityFeeSubscriber.ts +++ b/sdk/src/priorityFee/priorityFeeSubscriber.ts @@ -1,7 +1,17 @@ import { Connection, PublicKey } from '@solana/web3.js'; -import { PriorityFeeStrategy } from './types'; +import { + PriorityFeeMethod, + PriorityFeeStrategy, + PriorityFeeSubscriberConfig, +} from './types'; import { AverageOverSlotsStrategy } from './averageOverSlotsStrategy'; import { MaxOverSlotsStrategy } from './maxOverSlotsStrategy'; +import { fetchSolanaPriorityFee } from './solanaPriorityFeeMethod'; +import { + HeliusPriorityFeeLevels, + HeliusPriorityLevel, + fetchHeliusPriorityFee, +} from './heliusPriorityFeeMethod'; export class PriorityFeeSubscriber { connection: Connection; @@ -10,8 +20,12 @@ export class PriorityFeeSubscriber { customStrategy?: PriorityFeeStrategy; averageStrategy = new AverageOverSlotsStrategy(); maxStrategy = new MaxOverSlotsStrategy(); + priorityFeeMethod = PriorityFeeMethod.SOLANA; lookbackDistance: number; + heliusApiKey?: string; + lastHeliusSample?: HeliusPriorityFeeLevels; + intervalId?: ReturnType; latestPriorityFee = 0; @@ -24,28 +38,35 @@ export class PriorityFeeSubscriber { * @param props * customStrategy : strategy to return the priority fee to use based on recent samples. defaults to AVERAGE. */ - public constructor({ - connection, - frequencyMs, - addresses, - customStrategy, - slotsToCheck = 10, - }: { - connection: Connection; - frequencyMs: number; - addresses: PublicKey[]; - customStrategy?: PriorityFeeStrategy; - slotsToCheck?: number; - }) { - this.connection = connection; - this.frequencyMs = frequencyMs; - this.addresses = addresses; - if (!customStrategy) { - this.customStrategy = new AverageOverSlotsStrategy(); - } else { - this.customStrategy = customStrategy; + public constructor(config: PriorityFeeSubscriberConfig) { + this.connection = config.connection; + this.frequencyMs = config.frequencyMs; + this.addresses = config.addresses; + if (config.customStrategy) { + this.customStrategy = config.customStrategy; + } + this.lookbackDistance = config.slotsToCheck ?? 50; + if (config.priorityFeeMethod) { + this.priorityFeeMethod = config.priorityFeeMethod; + + if ( + this.priorityFeeMethod === PriorityFeeMethod.HELIUS && + config.heliusApiKey === undefined + ) { + throw new Error( + 'Helius API key must be provided to use HELIUS priority fee API' + ); + } + this.heliusApiKey = config.heliusApiKey; + } + + if (this.priorityFeeMethod === PriorityFeeMethod.SOLANA) { + if (this.connection === undefined) { + throw new Error( + 'connection must be provided to use SOLANA priority fee API' + ); + } } - this.lookbackDistance = slotsToCheck; } public async subscribe(): Promise { @@ -56,36 +77,60 @@ export class PriorityFeeSubscriber { this.intervalId = setInterval(this.load.bind(this), this.frequencyMs); } - public async load(): Promise { - // @ts-ignore - const rpcJSONResponse: any = await this.connection._rpcRequest( - 'getRecentPrioritizationFees', - [this.addresses] + private async loadForSolana(): Promise { + const samples = await fetchSolanaPriorityFee( + this.connection!, + this.lookbackDistance, + this.addresses ); + console.log(samples); + this.latestPriorityFee = samples[0].prioritizationFee; + this.lastSlotSeen = samples[0].slot; + + this.lastAvgStrategyResult = this.averageStrategy.calculate(samples); + this.lastMaxStrategyResult = this.maxStrategy.calculate(samples); + if (this.customStrategy) { + this.lastCustomStrategyResult = this.customStrategy.calculate(samples); + } + } - const results: { slot: number; prioritizationFee: number }[] = - rpcJSONResponse?.result; + private async loadForHelius(): Promise { + const sample = await fetchHeliusPriorityFee( + this.heliusApiKey, + this.lookbackDistance, + this.addresses + ); + this.lastHeliusSample = sample.result.priorityFeeLevels; + } - if (!results.length) return; + public getHeliusPriorityFeeLevel( + level: HeliusPriorityLevel = HeliusPriorityLevel.MEDIUM + ): number { + if (this.lastHeliusSample === undefined) { + return 0; + } + return this.lastHeliusSample[level]; + } - // # Sort and filter results based on the slot lookback setting - const descResults = results.sort((a, b) => b.slot - a.slot); - const mostRecentResult = descResults[0]; - const cutoffSlot = mostRecentResult.slot - this.lookbackDistance; + public getCustomStrategyResult(): number { + return this.lastCustomStrategyResult; + } - const resultsToUse = descResults.filter( - (result) => result.slot >= cutoffSlot - ); + public getAvgStrategyResult(): number { + return this.lastAvgStrategyResult; + } - // # Handle results - this.latestPriorityFee = mostRecentResult.prioritizationFee; - this.lastSlotSeen = mostRecentResult.slot; + public getMaxStrategyResult(): number { + return this.lastMaxStrategyResult; + } - this.lastAvgStrategyResult = this.averageStrategy.calculate(resultsToUse); - this.lastMaxStrategyResult = this.maxStrategy.calculate(resultsToUse); - if (this.customStrategy) { - this.lastCustomStrategyResult = - this.customStrategy.calculate(resultsToUse); + public async load(): Promise { + if (this.priorityFeeMethod === PriorityFeeMethod.SOLANA) { + await this.loadForSolana(); + } else if (this.priorityFeeMethod === PriorityFeeMethod.HELIUS) { + await this.loadForHelius(); + } else { + throw new Error(`${this.priorityFeeMethod} load not implemented`); } } diff --git a/sdk/src/priorityFee/solanaPriorityFeeMethod.ts b/sdk/src/priorityFee/solanaPriorityFeeMethod.ts new file mode 100644 index 000000000..2fd2c7aff --- /dev/null +++ b/sdk/src/priorityFee/solanaPriorityFeeMethod.ts @@ -0,0 +1,28 @@ +import { Connection, PublicKey } from '@solana/web3.js'; + +export type SolanaPriorityFeeResponse = { + slot: number; + prioritizationFee: number; +}; + +export async function fetchSolanaPriorityFee( + connection: Connection, + lookbackDistance: number, + addresses: PublicKey[] +): Promise { + // @ts-ignore + const rpcJSONResponse: any = await connection._rpcRequest( + 'getRecentPrioritizationFees', + [addresses] + ); + + const results: SolanaPriorityFeeResponse[] = rpcJSONResponse?.result; + + if (!results.length) return; + + // Sort and filter results based on the slot lookback setting + const descResults = results.sort((a, b) => b.slot - a.slot); + const cutoffSlot = descResults[0].slot - lookbackDistance; + + return descResults.filter((result) => result.slot >= cutoffSlot); +} diff --git a/sdk/src/priorityFee/types.ts b/sdk/src/priorityFee/types.ts index 84c4a9c6b..1167d1b46 100644 --- a/sdk/src/priorityFee/types.ts +++ b/sdk/src/priorityFee/types.ts @@ -1,5 +1,32 @@ +import { Connection, PublicKey } from '@solana/web3.js'; +import { SolanaPriorityFeeResponse } from './solanaPriorityFeeMethod'; +import { HeliusPriorityFeeResponse } from './heliusPriorityFeeMethod'; + export interface PriorityFeeStrategy { // calculate the priority fee for a given set of samples. // expect samples to be sorted in descending order (by slot) - calculate(samples: { slot: number; prioritizationFee: number }[]): number; + calculate( + samples: SolanaPriorityFeeResponse[] | HeliusPriorityFeeResponse + ): number; } + +export enum PriorityFeeMethod { + SOLANA = 'solana', + HELIUS = 'helius', +} + +export type PriorityFeeSubscriberConfig = { + /// rpc connection, optional if using priorityFeeMethod.HELIUS + connection?: Connection; + /// frequency to make RPC calls to update priority fee samples, in milliseconds + frequencyMs: number; + /// addresses you plan to write lock, used to determine priority fees + addresses: PublicKey[]; + /// custom strategy to calculate priority fees + customStrategy?: PriorityFeeStrategy; + /// method for fetching priority fee samples + priorityFeeMethod?: PriorityFeeMethod; + /// lookback window to determine priority fees, in slots. + slotsToCheck?: number; + heliusApiKey?: string; +}; diff --git a/sdk/src/tx/baseTxSender.ts b/sdk/src/tx/baseTxSender.ts index 70014cea5..5780aa1a4 100644 --- a/sdk/src/tx/baseTxSender.ts +++ b/sdk/src/tx/baseTxSender.ts @@ -34,7 +34,7 @@ export abstract class BaseTxSender implements TxSender { additionalConnections: Connection[]; timeoutCount = 0; confirmationStrategy: ConfirmationStrategy; - additionalTxSenderCallbacks: ((base58EncodedTx: string)=>void)[]; + additionalTxSenderCallbacks: ((base58EncodedTx: string) => void)[]; public constructor({ connection, @@ -51,7 +51,7 @@ export abstract class BaseTxSender implements TxSender { timeout?: number; additionalConnections?; confirmationStrategy?: ConfirmationStrategy; - additionalTxSenderCallbacks?: ((base58EncodedTx: string)=>void)[]; + additionalTxSenderCallbacks?: ((base58EncodedTx: string) => void)[]; }) { this.connection = connection; this.wallet = wallet; @@ -337,7 +337,7 @@ export abstract class BaseTxSender implements TxSender { console.error(e); }); }); - this.additionalTxSenderCallbacks?.map(callback => { + this.additionalTxSenderCallbacks?.map((callback) => { callback(bs58.encode(rawTx)); }); } diff --git a/sdk/src/tx/retryTxSender.ts b/sdk/src/tx/retryTxSender.ts index 2a4f18492..d9b77f836 100644 --- a/sdk/src/tx/retryTxSender.ts +++ b/sdk/src/tx/retryTxSender.ts @@ -28,7 +28,7 @@ export class RetryTxSender extends BaseTxSender { retrySleep = DEFAULT_RETRY, additionalConnections = new Array(), confirmationStrategy = ConfirmationStrategy.Combo, - additionalTxSenderCallbacks = [] + additionalTxSenderCallbacks = [], }: { connection: Connection; wallet: IWallet; @@ -37,7 +37,7 @@ export class RetryTxSender extends BaseTxSender { retrySleep?: number; additionalConnections?; confirmationStrategy?: ConfirmationStrategy; - additionalTxSenderCallbacks?: ((base58EncodedTx: string)=>void)[]; + additionalTxSenderCallbacks?: ((base58EncodedTx: string) => void)[]; }) { super({ connection, @@ -46,7 +46,7 @@ export class RetryTxSender extends BaseTxSender { timeout, additionalConnections, confirmationStrategy, - additionalTxSenderCallbacks + additionalTxSenderCallbacks, }); this.connection = connection; this.wallet = wallet; diff --git a/sdk/tests/tx/priorityFeeStrategy.ts b/sdk/tests/tx/priorityFeeStrategy.ts index 1296e0a89..4e09d08a5 100644 --- a/sdk/tests/tx/priorityFeeStrategy.ts +++ b/sdk/tests/tx/priorityFeeStrategy.ts @@ -76,7 +76,7 @@ describe('PriorityFeeStrategy', () => { { slot: 1, prioritizationFee: 1000 }, ]; const maxOverSlots = maxOverSlotsStrategy.calculate(samples); - expect(maxOverSlots).to.equal(832); + expect(maxOverSlots).to.equal(1000); }); it('AverageOverSlotsStrategy should calculate the average prioritization fee over slots', () => { @@ -90,6 +90,6 @@ describe('PriorityFeeStrategy', () => { { slot: 1, prioritizationFee: 1000 }, ]; const averageOverSlots = averageOverSlotsStrategy.calculate(samples); - expect(averageOverSlots).to.equal(454.4); + expect(averageOverSlots).to.approximately(545.33333, 0.00001); }); }); From fabf6ba588eead4266984c944e49d1f038f590d8 Mon Sep 17 00:00:00 2001 From: wphan Date: Wed, 24 Jan 2024 09:09:53 -0800 Subject: [PATCH 2/4] eagerly convert PubicKey to string --- sdk/src/priorityFee/heliusPriorityFeeMethod.ts | 4 ++-- sdk/src/priorityFee/priorityFeeSubscriber.ts | 4 ++-- sdk/src/priorityFee/solanaPriorityFeeMethod.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/sdk/src/priorityFee/heliusPriorityFeeMethod.ts b/sdk/src/priorityFee/heliusPriorityFeeMethod.ts index d7976b97a..cf880f677 100644 --- a/sdk/src/priorityFee/heliusPriorityFeeMethod.ts +++ b/sdk/src/priorityFee/heliusPriorityFeeMethod.ts @@ -30,7 +30,7 @@ const heliusUrlBase = 'https://mainnet.helius-rpc.com/?api-key='; export async function fetchHeliusPriorityFee( apiKey: string, lookbackDistance: number, - addresses: PublicKey[] + addresses: string[] ): Promise { const response = await fetch(heliusUrlBase + apiKey, { method: 'POST', @@ -41,7 +41,7 @@ export async function fetchHeliusPriorityFee( method: 'getPriorityFeeEstimate', params: [ { - accountKeys: addresses.map((address) => address.toBase58()), + accountKeys: addresses, options: { includeAllPriorityFeeLevels: true, lookbackSlots: lookbackDistance, diff --git a/sdk/src/priorityFee/priorityFeeSubscriber.ts b/sdk/src/priorityFee/priorityFeeSubscriber.ts index 9f456e270..95edef914 100644 --- a/sdk/src/priorityFee/priorityFeeSubscriber.ts +++ b/sdk/src/priorityFee/priorityFeeSubscriber.ts @@ -16,7 +16,7 @@ import { export class PriorityFeeSubscriber { connection: Connection; frequencyMs: number; - addresses: PublicKey[]; + addresses: string[]; customStrategy?: PriorityFeeStrategy; averageStrategy = new AverageOverSlotsStrategy(); maxStrategy = new MaxOverSlotsStrategy(); @@ -41,7 +41,7 @@ export class PriorityFeeSubscriber { public constructor(config: PriorityFeeSubscriberConfig) { this.connection = config.connection; this.frequencyMs = config.frequencyMs; - this.addresses = config.addresses; + this.addresses = config.addresses.map((address) => address.toBase58()); if (config.customStrategy) { this.customStrategy = config.customStrategy; } diff --git a/sdk/src/priorityFee/solanaPriorityFeeMethod.ts b/sdk/src/priorityFee/solanaPriorityFeeMethod.ts index 2fd2c7aff..f7897a311 100644 --- a/sdk/src/priorityFee/solanaPriorityFeeMethod.ts +++ b/sdk/src/priorityFee/solanaPriorityFeeMethod.ts @@ -8,7 +8,7 @@ export type SolanaPriorityFeeResponse = { export async function fetchSolanaPriorityFee( connection: Connection, lookbackDistance: number, - addresses: PublicKey[] + addresses: string[] ): Promise { // @ts-ignore const rpcJSONResponse: any = await connection._rpcRequest( From 06ab95f4290f15c8ba2fa6783cf3073a270eb83d Mon Sep 17 00:00:00 2001 From: wphan Date: Wed, 24 Jan 2024 09:30:00 -0800 Subject: [PATCH 3/4] use helius from connection obj, or optional rpc url --- .../priorityFee/heliusPriorityFeeMethod.ts | 6 ++--- sdk/src/priorityFee/priorityFeeSubscriber.ts | 26 ++++++++++--------- sdk/src/priorityFee/types.ts | 3 ++- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/sdk/src/priorityFee/heliusPriorityFeeMethod.ts b/sdk/src/priorityFee/heliusPriorityFeeMethod.ts index cf880f677..276c40374 100644 --- a/sdk/src/priorityFee/heliusPriorityFeeMethod.ts +++ b/sdk/src/priorityFee/heliusPriorityFeeMethod.ts @@ -23,16 +23,14 @@ export type HeliusPriorityFeeResponse = { id: string; }; -const heliusUrlBase = 'https://mainnet.helius-rpc.com/?api-key='; - /// Fetches the priority fee from the Helius API /// https://docs.helius.dev/solana-rpc-nodes/alpha-priority-fee-api export async function fetchHeliusPriorityFee( - apiKey: string, + heliusRpcUrl: string, lookbackDistance: number, addresses: string[] ): Promise { - const response = await fetch(heliusUrlBase + apiKey, { + const response = await fetch(heliusRpcUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ diff --git a/sdk/src/priorityFee/priorityFeeSubscriber.ts b/sdk/src/priorityFee/priorityFeeSubscriber.ts index 95edef914..bf5094df8 100644 --- a/sdk/src/priorityFee/priorityFeeSubscriber.ts +++ b/sdk/src/priorityFee/priorityFeeSubscriber.ts @@ -23,7 +23,7 @@ export class PriorityFeeSubscriber { priorityFeeMethod = PriorityFeeMethod.SOLANA; lookbackDistance: number; - heliusApiKey?: string; + heliusRpcUrl?: string; lastHeliusSample?: HeliusPriorityFeeLevels; intervalId?: ReturnType; @@ -49,15 +49,17 @@ export class PriorityFeeSubscriber { if (config.priorityFeeMethod) { this.priorityFeeMethod = config.priorityFeeMethod; - if ( - this.priorityFeeMethod === PriorityFeeMethod.HELIUS && - config.heliusApiKey === undefined - ) { - throw new Error( - 'Helius API key must be provided to use HELIUS priority fee API' - ); + if (this.priorityFeeMethod === PriorityFeeMethod.HELIUS) { + if (config.heliusRpcUrl === undefined) { + if (this.connection.rpcEndpoint.includes('helius')) { + this.heliusRpcUrl = this.connection.rpcEndpoint; + } else { + throw new Error( + 'Connection must be helius, or heliusRpcUrl must be provided to use PriorityFeeMethod.HELIUS' + ); + } + } } - this.heliusApiKey = config.heliusApiKey; } if (this.priorityFeeMethod === PriorityFeeMethod.SOLANA) { @@ -74,6 +76,7 @@ export class PriorityFeeSubscriber { return; } + await this.load(); this.intervalId = setInterval(this.load.bind(this), this.frequencyMs); } @@ -83,7 +86,6 @@ export class PriorityFeeSubscriber { this.lookbackDistance, this.addresses ); - console.log(samples); this.latestPriorityFee = samples[0].prioritizationFee; this.lastSlotSeen = samples[0].slot; @@ -96,11 +98,11 @@ export class PriorityFeeSubscriber { private async loadForHelius(): Promise { const sample = await fetchHeliusPriorityFee( - this.heliusApiKey, + this.heliusRpcUrl, this.lookbackDistance, this.addresses ); - this.lastHeliusSample = sample.result.priorityFeeLevels; + this.lastHeliusSample = sample?.result?.priorityFeeLevels ?? undefined; } public getHeliusPriorityFeeLevel( diff --git a/sdk/src/priorityFee/types.ts b/sdk/src/priorityFee/types.ts index 1167d1b46..7bc24b855 100644 --- a/sdk/src/priorityFee/types.ts +++ b/sdk/src/priorityFee/types.ts @@ -28,5 +28,6 @@ export type PriorityFeeSubscriberConfig = { priorityFeeMethod?: PriorityFeeMethod; /// lookback window to determine priority fees, in slots. slotsToCheck?: number; - heliusApiKey?: string; + /// url for helius rpc, required if using priorityFeeMethod.HELIUS + heliusRpcUrl?: string; }; From bade66db7f9ccff739150e1913c423a523d70d91 Mon Sep 17 00:00:00 2001 From: wphan Date: Wed, 24 Jan 2024 10:40:47 -0800 Subject: [PATCH 4/4] lints --- sdk/src/priorityFee/heliusPriorityFeeMethod.ts | 1 - sdk/src/priorityFee/priorityFeeSubscriber.ts | 8 +++----- sdk/src/priorityFee/solanaPriorityFeeMethod.ts | 2 +- sdk/src/priorityFee/types.ts | 2 +- 4 files changed, 5 insertions(+), 8 deletions(-) diff --git a/sdk/src/priorityFee/heliusPriorityFeeMethod.ts b/sdk/src/priorityFee/heliusPriorityFeeMethod.ts index 276c40374..0621a3c92 100644 --- a/sdk/src/priorityFee/heliusPriorityFeeMethod.ts +++ b/sdk/src/priorityFee/heliusPriorityFeeMethod.ts @@ -1,4 +1,3 @@ -import { PublicKey } from '@solana/web3.js'; import fetch from 'node-fetch'; export enum HeliusPriorityLevel { diff --git a/sdk/src/priorityFee/priorityFeeSubscriber.ts b/sdk/src/priorityFee/priorityFeeSubscriber.ts index bf5094df8..c19107cac 100644 --- a/sdk/src/priorityFee/priorityFeeSubscriber.ts +++ b/sdk/src/priorityFee/priorityFeeSubscriber.ts @@ -1,4 +1,4 @@ -import { Connection, PublicKey } from '@solana/web3.js'; +import { Connection } from '@solana/web3.js'; import { PriorityFeeMethod, PriorityFeeStrategy, @@ -34,16 +34,14 @@ export class PriorityFeeSubscriber { lastMaxStrategyResult = 0; lastSlotSeen = 0; - /** - * @param props - * customStrategy : strategy to return the priority fee to use based on recent samples. defaults to AVERAGE. - */ public constructor(config: PriorityFeeSubscriberConfig) { this.connection = config.connection; this.frequencyMs = config.frequencyMs; this.addresses = config.addresses.map((address) => address.toBase58()); if (config.customStrategy) { this.customStrategy = config.customStrategy; + } else { + this.customStrategy = this.averageStrategy; } this.lookbackDistance = config.slotsToCheck ?? 50; if (config.priorityFeeMethod) { diff --git a/sdk/src/priorityFee/solanaPriorityFeeMethod.ts b/sdk/src/priorityFee/solanaPriorityFeeMethod.ts index f7897a311..9dbed72f1 100644 --- a/sdk/src/priorityFee/solanaPriorityFeeMethod.ts +++ b/sdk/src/priorityFee/solanaPriorityFeeMethod.ts @@ -1,4 +1,4 @@ -import { Connection, PublicKey } from '@solana/web3.js'; +import { Connection } from '@solana/web3.js'; export type SolanaPriorityFeeResponse = { slot: number; diff --git a/sdk/src/priorityFee/types.ts b/sdk/src/priorityFee/types.ts index 7bc24b855..3d9c19fb9 100644 --- a/sdk/src/priorityFee/types.ts +++ b/sdk/src/priorityFee/types.ts @@ -22,7 +22,7 @@ export type PriorityFeeSubscriberConfig = { frequencyMs: number; /// addresses you plan to write lock, used to determine priority fees addresses: PublicKey[]; - /// custom strategy to calculate priority fees + /// custom strategy to calculate priority fees, defaults to AVERAGE customStrategy?: PriorityFeeStrategy; /// method for fetching priority fee samples priorityFeeMethod?: PriorityFeeMethod;