Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(apps/price_pusher): update price config file references, add Solana balance tracker, and enable metrics #2508

Merged
merged 1 commit into from
Mar 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/price_pusher/config.solana.mainnet.sample.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"dynamic-jito-tips": true,
"jito-bundle-size": "5",
"updates-per-jito-bundle": "6",
"price-config-file": "./price-config.yaml",
"price-config-file": "./price-config.stable.sample.yaml",
"price-service-endpoint": "https://hermes.pyth.network/",
"pyth-contract-address": "pythWSnswVUd12oZpeFP8e9CVaEqJg25g1Vtc2biRsT",
"pushing-frequency": "30"
Expand Down
2 changes: 1 addition & 1 deletion apps/price_pusher/config.solana.testnet.sample.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"endpoint": "https://api.devnet.solana.com",
"keypair-file": "./id.json",
"shard-id": 1,
"price-config-file": "./price-config.yaml",
"price-config-file": "./price-config.stable.sample.yaml",
"price-service-endpoint": "https://hermes.pyth.network/",
"pyth-contract-address": "pythWSnswVUd12oZpeFP8e9CVaEqJg25g1Vtc2biRsT",
"pushing-frequency": "30"
Expand Down
2 changes: 1 addition & 1 deletion apps/price_pusher/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@pythnetwork/price-pusher",
"version": "9.1.3",
"version": "9.1.4",
"description": "Pyth Price Pusher",
"homepage": "https://pyth.network",
"main": "lib/index.js",
Expand Down
94 changes: 94 additions & 0 deletions apps/price_pusher/src/solana/balance-tracker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { Connection, PublicKey, LAMPORTS_PER_SOL } from "@solana/web3.js";
import {
BaseBalanceTracker,
BaseBalanceTrackerConfig,
IBalanceTracker,
} from "../interface";
import { DurationInSeconds } from "../utils";
import { PricePusherMetrics } from "../metrics";
import { Logger } from "pino";

/**
* Solana-specific configuration for balance tracker
*/
export interface SolanaBalanceTrackerConfig extends BaseBalanceTrackerConfig {
/** Solana connection instance */
connection: Connection;
/** Solana public key */
publicKey: PublicKey;
}

/**
* Solana-specific implementation of the balance tracker
*/
export class SolanaBalanceTracker extends BaseBalanceTracker {
private connection: Connection;
private publicKey: PublicKey;

constructor(config: SolanaBalanceTrackerConfig) {
super({
...config,
logger: config.logger.child({ module: "SolanaBalanceTracker" }),
});

this.connection = config.connection;
this.publicKey = config.publicKey;
}

/**
* Solana-specific implementation of balance update
*/
protected async updateBalance(): Promise<void> {
try {
const balanceInLamports = await this.connection.getBalance(
this.publicKey,
);

// Convert from lamports to SOL
const balanceInSol = balanceInLamports / LAMPORTS_PER_SOL;

this.metrics.updateWalletBalance(
this.address,
this.network,
balanceInSol,
);
this.logger.debug(
`Updated Solana wallet balance: ${this.address} = ${balanceInSol.toString()} SOL (${balanceInLamports} lamports)`,
);
} catch (error) {
this.logger.error(
{ error },
"Error fetching Solana wallet balance for metrics",
);
}
}
}

/**
* Parameters for creating a Solana balance tracker
*/
export interface CreateSolanaBalanceTrackerParams {
connection: Connection;
publicKey: PublicKey;
network: string;
updateInterval: DurationInSeconds;
metrics: PricePusherMetrics;
logger: Logger;
}

/**
* Factory function to create a balance tracker for Solana
*/
export function createSolanaBalanceTracker(
params: CreateSolanaBalanceTrackerParams,
): IBalanceTracker {
return new SolanaBalanceTracker({
connection: params.connection,
publicKey: params.publicKey,
address: params.publicKey.toString(),
network: params.network,
updateInterval: params.updateInterval,
metrics: params.metrics,
logger: params.logger,
});
}
43 changes: 38 additions & 5 deletions apps/price_pusher/src/solana/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ import pino from "pino";
import { Logger } from "pino";
import { HermesClient } from "@pythnetwork/hermes-client";
import { filterInvalidPriceItems } from "../utils";
import { PricePusherMetrics } from "../metrics";
import { createSolanaBalanceTracker } from "./balance-tracker";
import { IBalanceTracker } from "../interface";

export default {
command: "solana",
describe: "run price pusher for solana",
Expand Down Expand Up @@ -99,6 +103,8 @@ export default {
...options.pushingFrequency,
...options.logLevel,
...options.controllerLogLevel,
...options.enableMetrics,
...options.metricsPort,
},
handler: async function (argv: any) {
const {
Expand All @@ -122,6 +128,8 @@ export default {
treasuryId,
logLevel,
controllerLogLevel,
enableMetrics,
metricsPort,
} = argv;

const logger = pino({ level: logLevel });
Expand All @@ -130,6 +138,14 @@ export default {

const hermesClient = new HermesClient(priceServiceEndpoint);

// Initialize metrics if enabled
let metrics: PricePusherMetrics | undefined;
if (enableMetrics) {
metrics = new PricePusherMetrics(logger.child({ module: "Metrics" }));
metrics.start(metricsPort);
logger.info(`Metrics server started on port ${metricsPort}`);
}

let priceItems = priceConfigs.map(({ id, alias }) => ({ id, alias }));

// Better to filter out invalid price items before creating the pyth listener
Expand All @@ -152,11 +168,10 @@ export default {
logger.child({ module: "PythPriceListener" }),
);

const wallet = new NodeWallet(
Keypair.fromSecretKey(
Uint8Array.from(JSON.parse(fs.readFileSync(keypairFile, "ascii"))),
),
const keypair = Keypair.fromSecretKey(
Uint8Array.from(JSON.parse(fs.readFileSync(keypairFile, "ascii"))),
);
const wallet = new NodeWallet(keypair);

const connection = new Connection(endpoint, "processed");
const pythSolanaReceiver = new PythSolanaReceiver({
Expand All @@ -166,6 +181,21 @@ export default {
treasuryId: treasuryId,
});

// Create and start the balance tracker if metrics are enabled
if (metrics) {
const balanceTracker: IBalanceTracker = createSolanaBalanceTracker({
connection,
publicKey: keypair.publicKey,
network: "solana",
updateInterval: 60,
metrics,
logger,
});

// Start the balance tracker
await balanceTracker.start();
}

// Fetch the account lookup table if provided
const lookupTableAccount = addressLookupTableAccount
? await connection
Expand Down Expand Up @@ -220,7 +250,10 @@ export default {
solanaPriceListener,
solanaPricePusher,
logger.child({ module: "Controller" }, { level: controllerLogLevel }),
{ pushingFrequency },
{
pushingFrequency,
metrics,
},
);

controller.start();
Expand Down
Loading