- Pyth Network Development Guide
- Pyth Network is a decentralized oracle providing real-time price feeds for cryptocurrencies, equities, forex, and commodities. This guide covers integrating Pyth price feeds into Solana applications.
- Overview
- Pyth Network provides:
- Real-Time Price Feeds
- - 400ms update frequency with pull oracle model
- Confidence Intervals
- - Statistical uncertainty bounds for each price
- EMA Prices
- - Exponential moving average prices (~1 hour window)
- Multi-Asset Support
- - Crypto, equities, FX, commodities, indices
- On-Chain Integration
- - CPI for Solana programs
- Off-Chain Integration
- - HTTP and WebSocket APIs via Hermes
- Program IDs
- Program
- Address
- Description
- Solana Receiver
- rec5EKMGg6MxZYaMdyBfgwp4d5rB9T1VQH5pJv5LtFJ
- Posts price updates to Solana
- Price Feed
- pythWSnswVUd12oZpeFP8e9CVaEqJg25g1Vtc2biRsT
- Stores price feed data
- Deployed on
- Solana Mainnet, Devnet, Eclipse Mainnet/Testnet, Sonic networks Popular Price Feed IDs Asset Hex Feed ID BTC/USD 0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43 ETH/USD 0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace SOL/USD 0xef0d8b6fda2ceba41da15d4095d1da392a0d2f8ed0c6c7bc0f4cfac8c280b56d USDC/USD 0xeaa020c61cc479712813461ce153894a96a6c00b21ed0cfc2798d1f9a9e9c94a USDT/USD 0x2b89b9dc8fdf9f34709a5b106b472f0f39bb6ca9ce04b0fd7f2e971688e2e53b Full list: https://pyth.network/developers/price-feed-ids Quick Start Installation
TypeScript/JavaScript
npm install @pythnetwork/hermes-client @pythnetwork/pyth-solana-receiver
Rust (add to Cargo.toml)
pyth-solana-receiver-sdk = "0.3.0"
Fetch Price (Off-Chain)
import
{
HermesClient
}
from
"@pythnetwork/hermes-client"
;
const
client
=
new
HermesClient
(
"https://hermes.pyth.network"
)
;
const
priceIds
=
[
"0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43"
,
// BTC/USD
]
;
const
priceUpdates
=
await
client
.
getLatestPriceUpdates
(
priceIds
)
;
for
(
const
update
of
priceUpdates
.
parsed
)
{
const
price
=
update
.
price
;
const
displayPrice
=
Number
(
price
.
price
)
*
Math
.
pow
(
10
,
price
.
expo
)
;
console
.
log
(
Price: $
${
displayPrice
.
toFixed
(
2
)
}
)
;
console
.
log
(
Confidence: ±
${
Number
(
price
.
conf
)
*
Math
.
pow
(
10
,
price
.
expo
)
}
)
;
}
Use Price On-Chain (Rust/Anchor)
use
anchor_lang
::
prelude
::
*
;
use
pyth_solana_receiver_sdk
::
price_update
::
PriceUpdateV2
;
[derive(Accounts)]
- pub
- struct
- UsePrice
- <
- 'info
- >
- {
- pub
- price_update
- :
- Account
- <
- 'info
- ,
- PriceUpdateV2
- >
- ,
- }
- pub
- fn
- use_price
- (
- ctx
- :
- Context
- <
- UsePrice
- >
- )
- ->
- Result
- <
- (
- )
- >
- {
- let
- price_update
- =
- &
- ctx
- .
- accounts
- .
- price_update
- ;
- let
- clock
- =
- Clock
- ::
- get
- (
- )
- ?
- ;
- // Get price no older than 60 seconds
- let
- price
- =
- price_update
- .
- get_price_no_older_than
- (
- &
- clock
- ,
- 60
- ,
- // max age in seconds
- )
- ?
- ;
- msg!
- (
- "Price: {} × 10^{}"
- ,
- price
- .
- price
- ,
- price
- .
- exponent
- )
- ;
- msg!
- (
- "Confidence: ±{}"
- ,
- price
- .
- conf
- )
- ;
- Ok
- (
- (
- )
- )
- }
- Core Concepts
- Price Structure
- Each Pyth price contains:
- Field
- Type
- Description
- price
- i64
- Price value in fixed-point format
- conf
- u64
- Confidence interval (standard deviation)
- expo
- i32
- Exponent for scaling (e.g., -8 means divide by 10^8)
- publish_time
- i64
- Unix timestamp of price
- Converting to display price:
- const
- displayPrice
- =
- price
- *
- Math
- .
- pow
- (
- 10
- ,
- expo
- )
- ;
- // Example: price=19405100, expo=-2 → $194,051.00
- Confidence Intervals
- Confidence intervals represent the uncertainty in the reported price:
- // Price is $50,000 ± $50 means:
- // - 68% chance true price is between $49,950 - $50,050
- // - Use confidence for risk management
- const
- price
- =
- 50000
- ;
- const
- confidence
- =
- 50
- ;
- // Safe lower bound (conservative)
- const
- safeLowerBound
- =
- price
- -
- confidence
- ;
- // Safe upper bound (conservative)
- const
- safeUpperBound
- =
- price
- +
- confidence
- ;
- Best Practice
- Reject prices with confidence > 2% of price:
const
maxConfidenceRatio
=
0.02
;
// 2%
const
confidenceRatio
=
confidence
/
Math
.
abs
(
price
)
;
if
(
confidenceRatio
maxConfidenceRatio ) { throw new Error ( "Price confidence too wide" ) ; } EMA Prices Exponential Moving Average prices smooth out short-term volatility: ~1 hour averaging window (5921 Solana slots) Weighted by inverse confidence (tight confidence = more weight) Good for: liquidations, collateral valuation Available as ema_price and ema_conf // Use EMA for less volatile applications const emaPrice = priceUpdate . emaPrice ; const emaConf = priceUpdate . emaConf ; Off-Chain Integration Hermes Client Hermes is the recommended way to fetch Pyth prices off-chain. Public Endpoint : https://hermes.pyth.network For production, get a dedicated endpoint from a Pyth data provider. Fetching Latest Prices import { HermesClient } from "@pythnetwork/hermes-client" ; const client = new HermesClient ( "https://hermes.pyth.network" ) ; // Single price const btcPrice = await client . getLatestPriceUpdates ( [ "0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43" ] ) ; // Multiple prices in one request const prices = await client . getLatestPriceUpdates ( [ "0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43" , // BTC "0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace" , // ETH "0xef0d8b6fda2ceba41da15d4095d1da392a0d2f8ed0c6c7bc0f4cfac8c280b56d" , // SOL ] ) ; Streaming Real-Time Updates import { HermesClient } from "@pythnetwork/hermes-client" ; const client = new HermesClient ( "https://hermes.pyth.network" ) ; const priceIds = [ "0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43" ] ; // Subscribe to real-time updates via SSE const eventSource = await client . getPriceUpdatesStream ( priceIds , { parsed : true , } ) ; eventSource . onmessage = ( event ) => { const data = JSON . parse ( event . data ) ; console . log ( "Price update:" , data ) ; } ; eventSource . onerror = ( error ) => { console . error ( "Stream error:" , error ) ; eventSource . close ( ) ; } ; // Close when done // eventSource.close(); Posting Prices to Solana import { PythSolanaReceiver } from "@pythnetwork/pyth-solana-receiver" ; import { HermesClient } from "@pythnetwork/hermes-client" ; import { Connection , Keypair } from "@solana/web3.js" ; const connection = new Connection ( "https://api.mainnet-beta.solana.com" ) ; const wallet = Keypair . fromSecretKey ( / your key / ) ; const hermesClient = new HermesClient ( "https://hermes.pyth.network" ) ; const pythReceiver = new PythSolanaReceiver ( { connection , wallet } ) ; // Fetch price update data const priceUpdateData = await hermesClient . getLatestPriceUpdates ( [ "0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43" ] ) ; // Build transaction to post price const transactionBuilder = pythReceiver . newTransactionBuilder ( ) ; await transactionBuilder . addPostPriceUpdates ( priceUpdateData . binary . data ) ; // Add your program instruction that uses the price // transactionBuilder.addInstruction(yourInstruction); // Send transaction const transactions = await transactionBuilder . buildVersionedTransactions ( { computeUnitPriceMicroLamports : 50000 , } ) ; for ( const tx of transactions ) { const sig = await connection . sendTransaction ( tx ) ; console . log ( "Transaction:" , sig ) ; } On-Chain Integration (Rust) Setup Add to Cargo.toml : [ dependencies ] pyth-solana-receiver-sdk = "0.3.0" anchor-lang = "0.30.1" Reading Price in Anchor Program use anchor_lang :: prelude :: * ; use pyth_solana_receiver_sdk :: price_update :: { PriceUpdateV2 , get_feed_id_from_hex } ; declare_id! ( "YourProgramId..." ) ; // BTC/USD price feed ID const BTC_USD_FEED_ID : & str = "0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43" ;
[program]
pub mod my_program { use super :: * ; pub fn check_price ( ctx : Context < CheckPrice
) -> Result < ( )
{ let price_update = & ctx . accounts . price_update ; let clock = Clock :: get ( ) ? ; // Verify this is the correct feed let feed_id = get_feed_id_from_hex ( BTC_USD_FEED_ID ) ? ; // Get price no older than 60 seconds let price = price_update . get_price_no_older_than_with_custom_verification ( & clock , 60 , & feed_id , ctx . accounts . price_update . to_account_info ( ) . owner , ) ? ; msg! ( "BTC/USD Price: {} × 10^{}" , price . price , price . exponent ) ; msg! ( "Confidence: ±{}" , price . conf ) ; Ok ( ( ) ) } }
[derive(Accounts)]
pub struct CheckPrice < 'info
{
[account(
constraint = price_update.to_account_info().owner == &pyth_solana_receiver_sdk::ID )] pub price_update : Account < 'info , PriceUpdateV2
, } Using Price for Calculations pub fn swap_with_oracle ( ctx : Context < SwapWithOracle
, amount_in : u64 , ) -> Result < ( )
{ let price_update = & ctx . accounts . price_update ; let clock = Clock :: get ( ) ? ; // Get price with staleness check let price = price_update . get_price_no_older_than ( & clock , 30 ) ? ; // Validate confidence (max 1% of price) let conf_ratio = ( price . conf as u128 * 10000 ) / ( price . price . unsigned_abs ( ) as u128 ) ; require! ( conf_ratio <= 100 , ErrorCode :: ConfidenceTooWide ) ; // Convert price to usable format // price.price is in fixed-point with price.exponent let price_scaled = if price . exponent = 0 { ( price . price as u128 ) * 10_u128 . pow ( price . exponent as u32 ) } else { ( price . price as u128 ) / 10_u128 . pow ( ( - price . exponent ) as u32 ) } ; // Calculate output amount using oracle price let amount_out = ( amount_in as u128 ) . checked_mul ( price_scaled ) . ok_or ( ErrorCode :: MathOverflow ) ? / 1_000_000 ; // Adjust for decimals msg! ( "Swap {} -> {} using price {}" , amount_in , amount_out , price_scaled ) ; Ok ( ( ) ) }
[error_code]
pub enum ErrorCode {
[msg(
"Price confidence interval too wide" )] ConfidenceTooWide ,
[msg(
"Math overflow" )] MathOverflow , } Multiple Price Feeds
[derive(Accounts)]
pub struct Liquidation < 'info
{
[account(
constraint = collateral_price.to_account_info().owner == &pyth_solana_receiver_sdk::ID )] pub collateral_price : Account < 'info , PriceUpdateV2
,
[account(
constraint = debt_price.to_account_info().owner == &pyth_solana_receiver_sdk::ID )] pub debt_price : Account < 'info , PriceUpdateV2
, } pub fn check_liquidation ( ctx : Context < Liquidation
) -> Result < bool
{ let clock = Clock :: get ( ) ? ; let collateral = ctx . accounts . collateral_price . get_price_no_older_than ( & clock , 60 ) ? ; let debt = ctx . accounts . debt_price . get_price_no_older_than ( & clock , 60 ) ? ; // Normalize to same exponent for comparison let collateral_value = normalize_price ( collateral . price , collateral . exponent ) ; let debt_value = normalize_price ( debt . price , debt . exponent ) ; // Check if undercollateralized let is_liquidatable = collateral_value < debt_value * 150 / 100 ; // 150% ratio Ok ( is_liquidatable ) } fn normalize_price ( price : i64 , expo : i32 ) -> i128 { let target_expo = - 8 ; // Normalize to 8 decimals let adjustment = expo - target_expo ; if adjustment = 0 { ( price as i128 ) * 10_i128 . pow ( adjustment as u32 ) } else { ( price as i128 ) / 10_i128 . pow ( ( - adjustment ) as u32 ) } } Best Practices 1. Always Check Staleness // Don't use old prices - set appropriate max age let max_age_seconds = 60 ; let price = price_update . get_price_no_older_than ( & clock , max_age_seconds ) ? ; 2. Validate Confidence Intervals // Reject prices with wide confidence (high uncertainty) const MAX_CONF_BPS : u64 = 200 ; // 2% let conf_bps = ( price . conf as u128 * 10000 ) / ( price . price . unsigned_abs ( ) as u128 ) ; require! ( conf_bps <= MAX_CONF_BPS as u128 , ErrorCode :: ConfidenceTooWide ) ; 3. Verify Account Ownership // Always verify the price account is owned by Pyth
[account(
constraint = price_update.to_account_info().owner == &pyth_solana_receiver_sdk::ID )] pub price_update : Account < 'info , PriceUpdateV2
, 4. Use EMA for Sensitive Operations // For liquidations, use EMA to avoid manipulation let ema_price = price_update . get_ema_price_no_older_than ( & clock , 60 ) ? ; 5. Handle Price Unavailability try { const price = await client . getLatestPriceUpdates ( [ feedId ] ) ; // Use price } catch ( error ) { // Fallback behavior or reject transaction console . error ( "Price unavailable:" , error ) ; } 6. Consider Frontrunning Adversaries may see price updates before your transaction Don't design logic that races against price updates Use appropriate slippage tolerances Price Feed Types Fixed Price Feed Accounts Maintained continuously by Pyth Fixed address per feed Always has most recent price Shared by all users (potential congestion) Ephemeral Price Update Accounts Created per transaction Can specify shard ID for parallelization Rent can be recovered after use Better for high-throughput applications // Use shard ID to avoid congestion const transactionBuilder = pythReceiver . newTransactionBuilder ( { shardId : Math . floor ( Math . random ( ) * 65536 ) , // Random shard } ) ; Resources Official Documentation Pyth Documentation Price Feed IDs Solana Integration Guide GitHub Repositories pyth-network/pyth-crosschain pyth-network/pyth-sdk-rs NPM Packages @pythnetwork/hermes-client @pythnetwork/pyth-solana-receiver Rust Crates pyth-solana-receiver-sdk Skill Structure pyth/ ├── SKILL.md # This file ├── resources/ │ ├── program-addresses.md # All program IDs and feed IDs │ └── api-reference.md # SDK API reference ├── examples/ │ ├── price-feeds/ │ │ ├── fetch-price.ts # Basic price fetching │ │ └── multiple-prices.ts # Multiple price feeds │ ├── on-chain/ │ │ ├── anchor-integration.rs # Anchor program example │ │ └── price-validation.rs # Price validation patterns │ └── streaming/ │ └── real-time-updates.ts # WebSocket streaming ├── templates/ │ ├── pyth-client.ts # TypeScript client template │ └── anchor-oracle.rs # Anchor program template └── docs/ └── troubleshooting.md # Common issues and solutions