Skip to content

Commit 42a7dd2

Browse files
authored
Merge pull request #2508 from pyth-network/solana-pusher-prom
feat(apps/price_pusher): update price config file references, add Solana balance tracker, and enable metrics
2 parents 87150c9 + 1726486 commit 42a7dd2

5 files changed

+135
-8
lines changed

apps/price_pusher/config.solana.mainnet.sample.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"dynamic-jito-tips": true,
99
"jito-bundle-size": "5",
1010
"updates-per-jito-bundle": "6",
11-
"price-config-file": "./price-config.yaml",
11+
"price-config-file": "./price-config.stable.sample.yaml",
1212
"price-service-endpoint": "https://hermes.pyth.network/",
1313
"pyth-contract-address": "pythWSnswVUd12oZpeFP8e9CVaEqJg25g1Vtc2biRsT",
1414
"pushing-frequency": "30"

apps/price_pusher/config.solana.testnet.sample.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"endpoint": "https://api.devnet.solana.com",
33
"keypair-file": "./id.json",
44
"shard-id": 1,
5-
"price-config-file": "./price-config.yaml",
5+
"price-config-file": "./price-config.stable.sample.yaml",
66
"price-service-endpoint": "https://hermes.pyth.network/",
77
"pyth-contract-address": "pythWSnswVUd12oZpeFP8e9CVaEqJg25g1Vtc2biRsT",
88
"pushing-frequency": "30"

apps/price_pusher/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@pythnetwork/price-pusher",
3-
"version": "9.1.3",
3+
"version": "9.1.4",
44
"description": "Pyth Price Pusher",
55
"homepage": "https://pyth.network",
66
"main": "lib/index.js",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import { Connection, PublicKey, LAMPORTS_PER_SOL } from "@solana/web3.js";
2+
import {
3+
BaseBalanceTracker,
4+
BaseBalanceTrackerConfig,
5+
IBalanceTracker,
6+
} from "../interface";
7+
import { DurationInSeconds } from "../utils";
8+
import { PricePusherMetrics } from "../metrics";
9+
import { Logger } from "pino";
10+
11+
/**
12+
* Solana-specific configuration for balance tracker
13+
*/
14+
export interface SolanaBalanceTrackerConfig extends BaseBalanceTrackerConfig {
15+
/** Solana connection instance */
16+
connection: Connection;
17+
/** Solana public key */
18+
publicKey: PublicKey;
19+
}
20+
21+
/**
22+
* Solana-specific implementation of the balance tracker
23+
*/
24+
export class SolanaBalanceTracker extends BaseBalanceTracker {
25+
private connection: Connection;
26+
private publicKey: PublicKey;
27+
28+
constructor(config: SolanaBalanceTrackerConfig) {
29+
super({
30+
...config,
31+
logger: config.logger.child({ module: "SolanaBalanceTracker" }),
32+
});
33+
34+
this.connection = config.connection;
35+
this.publicKey = config.publicKey;
36+
}
37+
38+
/**
39+
* Solana-specific implementation of balance update
40+
*/
41+
protected async updateBalance(): Promise<void> {
42+
try {
43+
const balanceInLamports = await this.connection.getBalance(
44+
this.publicKey,
45+
);
46+
47+
// Convert from lamports to SOL
48+
const balanceInSol = balanceInLamports / LAMPORTS_PER_SOL;
49+
50+
this.metrics.updateWalletBalance(
51+
this.address,
52+
this.network,
53+
balanceInSol,
54+
);
55+
this.logger.debug(
56+
`Updated Solana wallet balance: ${this.address} = ${balanceInSol.toString()} SOL (${balanceInLamports} lamports)`,
57+
);
58+
} catch (error) {
59+
this.logger.error(
60+
{ error },
61+
"Error fetching Solana wallet balance for metrics",
62+
);
63+
}
64+
}
65+
}
66+
67+
/**
68+
* Parameters for creating a Solana balance tracker
69+
*/
70+
export interface CreateSolanaBalanceTrackerParams {
71+
connection: Connection;
72+
publicKey: PublicKey;
73+
network: string;
74+
updateInterval: DurationInSeconds;
75+
metrics: PricePusherMetrics;
76+
logger: Logger;
77+
}
78+
79+
/**
80+
* Factory function to create a balance tracker for Solana
81+
*/
82+
export function createSolanaBalanceTracker(
83+
params: CreateSolanaBalanceTrackerParams,
84+
): IBalanceTracker {
85+
return new SolanaBalanceTracker({
86+
connection: params.connection,
87+
publicKey: params.publicKey,
88+
address: params.publicKey.toString(),
89+
network: params.network,
90+
updateInterval: params.updateInterval,
91+
metrics: params.metrics,
92+
logger: params.logger,
93+
});
94+
}

apps/price_pusher/src/solana/command.ts

+38-5
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ import pino from "pino";
2121
import { Logger } from "pino";
2222
import { HermesClient } from "@pythnetwork/hermes-client";
2323
import { filterInvalidPriceItems } from "../utils";
24+
import { PricePusherMetrics } from "../metrics";
25+
import { createSolanaBalanceTracker } from "./balance-tracker";
26+
import { IBalanceTracker } from "../interface";
27+
2428
export default {
2529
command: "solana",
2630
describe: "run price pusher for solana",
@@ -99,6 +103,8 @@ export default {
99103
...options.pushingFrequency,
100104
...options.logLevel,
101105
...options.controllerLogLevel,
106+
...options.enableMetrics,
107+
...options.metricsPort,
102108
},
103109
handler: async function (argv: any) {
104110
const {
@@ -122,6 +128,8 @@ export default {
122128
treasuryId,
123129
logLevel,
124130
controllerLogLevel,
131+
enableMetrics,
132+
metricsPort,
125133
} = argv;
126134

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

131139
const hermesClient = new HermesClient(priceServiceEndpoint);
132140

141+
// Initialize metrics if enabled
142+
let metrics: PricePusherMetrics | undefined;
143+
if (enableMetrics) {
144+
metrics = new PricePusherMetrics(logger.child({ module: "Metrics" }));
145+
metrics.start(metricsPort);
146+
logger.info(`Metrics server started on port ${metricsPort}`);
147+
}
148+
133149
let priceItems = priceConfigs.map(({ id, alias }) => ({ id, alias }));
134150

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

155-
const wallet = new NodeWallet(
156-
Keypair.fromSecretKey(
157-
Uint8Array.from(JSON.parse(fs.readFileSync(keypairFile, "ascii"))),
158-
),
171+
const keypair = Keypair.fromSecretKey(
172+
Uint8Array.from(JSON.parse(fs.readFileSync(keypairFile, "ascii"))),
159173
);
174+
const wallet = new NodeWallet(keypair);
160175

161176
const connection = new Connection(endpoint, "processed");
162177
const pythSolanaReceiver = new PythSolanaReceiver({
@@ -166,6 +181,21 @@ export default {
166181
treasuryId: treasuryId,
167182
});
168183

184+
// Create and start the balance tracker if metrics are enabled
185+
if (metrics) {
186+
const balanceTracker: IBalanceTracker = createSolanaBalanceTracker({
187+
connection,
188+
publicKey: keypair.publicKey,
189+
network: "solana",
190+
updateInterval: 60,
191+
metrics,
192+
logger,
193+
});
194+
195+
// Start the balance tracker
196+
await balanceTracker.start();
197+
}
198+
169199
// Fetch the account lookup table if provided
170200
const lookupTableAccount = addressLookupTableAccount
171201
? await connection
@@ -220,7 +250,10 @@ export default {
220250
solanaPriceListener,
221251
solanaPricePusher,
222252
logger.child({ module: "Controller" }, { level: controllerLogLevel }),
223-
{ pushingFrequency },
253+
{
254+
pushingFrequency,
255+
metrics,
256+
},
224257
);
225258

226259
controller.start();

0 commit comments

Comments
 (0)