Skills pyth
Complete guide for Pyth Network - decentralized oracle providing real-time price feeds for DeFi. Covers price feed integration, confidence intervals, EMA prices, on-chain CPI, off-chain fetching, and streaming updates for Solana applications.
git clone https://github.com/sendaifun/skills
T=$(mktemp -d) && git clone --depth=1 https://github.com/sendaifun/skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/pyth" ~/.claude/skills/sendaifun-skills-pyth && rm -rf "$T"
skills/pyth/SKILL.mdPyth 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 | | Posts price updates to Solana |
| Price Feed | | Stores price feed data |
Deployed on: Solana Mainnet, Devnet, Eclipse Mainnet/Testnet, Sonic networks
Popular Price Feed IDs
| Asset | Hex Feed ID |
|---|---|
| BTC/USD | |
| ETH/USD | |
| SOL/USD | |
| USDC/USD | |
| USDT/USD | |
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 |
|---|---|---|
| i64 | Price value in fixed-point format |
| u64 | Confidence interval (standard deviation) |
| i32 | Exponent for scaling (e.g., -8 means divide by 10^8) |
| 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
andema_priceema_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
GitHub Repositories
NPM Packages
Rust Crates
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