Install any skill in seconds. Free to start, no credit card required.
Get Started Free →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.
.claude/skills/internet-court-pyth/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 85% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 133% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 72% | 0% |
| case-10 | ✗→✓ | ▲ Improved | 37% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 152% | 0% |
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.
Pyth Network provides:
| 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
| 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
bash# TypeScript/JavaScript npm install @pythnetwork/hermes-client @pythnetwork/pyth-solana-receiver # Rust (add to Cargo.toml) # pyth-solana-receiver-sdk = "0.3.0"
typescriptimport { 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)}`); }
rustuse 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(()) }
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:
typescriptconst displayPrice = price * Math.pow(10, expo); // Example: price=19405100, expo=-2 → $194,051.00
Confidence intervals represent the uncertainty in the reported price:
typescript// 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:
typescriptconst maxConfidenceRatio = 0.02; // 2% const confidenceRatio = confidence / Math.abs(price); if (confidenceRatio > maxConfidenceRatio) { throw new Error("Price confidence too wide"); }
Exponential Moving Average prices smooth out short-term volatility:
ema_price and ema_conftypescript// Use EMA for less volatile applications const emaPrice = priceUpdate.emaPrice; const emaConf = priceUpdate.emaConf;
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.
typescriptimport { 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 ]);
typescriptimport { 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();
typescriptimport { 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); }
Add to Cargo.toml:
toml[dependencies] pyth-solana-receiver-sdk = "0.3.0" anchor-lang = "0.30.1"
rustuse 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>, }
rustpub 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, }
rust#[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) } }
rust// 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)?;
rust// 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);
rust// 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>,
rust// For liquidations, use EMA to avoid manipulation let ema_price = price_update.get_ema_price_no_older_than(&clock, 60)?;
typescripttry { const price = await client.getLatestPriceUpdates([feedId]); // Use price } catch (error) { // Fallback behavior or reject transaction console.error("Price unavailable:", error); }
typescript// Use shard ID to avoid congestion const transactionBuilder = pythReceiver.newTransactionBuilder({ shardId: Math.floor(Math.random() * 65536), // Random shard });
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| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 33,248 | 30,478 | -8% | 1 | 1 | 0% | 5,581 | 10,343 | +85% | 0 | 0 | — |
case-02 | pass→fail | 34,518 | 23,737 | -31% | 1 | 1 | 0% | 6,050 | 9,059 | +50% | 0 | 0 | — |
case-03 | pass→pass | 29,039 | 20,744 | -29% | 1 | 1 | 0% | 4,384 | 8,199 | +87% | 0 | 0 | — |
case-04 | pass→pass | 21,600 | 21,255 | -2% | 1 | 1 | 0% | 3,073 | 7,966 | +159% | 0 | 0 | — |
case-05 | pass→pass | 27,306 | 24,291 | -11% | 1 | 1 | 0% | 4,159 | 8,807 | +112% | 0 | 0 | — |
case-06 | pass→pass | 24,050 | 14,821 | -38% | 1 | 1 | 0% | 3,536 | 6,963 | +97% | 0 | 0 | — |
case-07 | pass→pass | 20,290 | 12,045 | -41% | 1 | 1 | 0% | 2,769 | 6,462 | +133% | 0 | 0 | — |
case-08 | fail→pass | 22,543 | 18,414 | -18% | 1 | 1 | 0% | 3,347 | 7,803 | +133% | 0 | 0 | — |
case-09 | fail→pass | 24,969 | 15,316 | -39% | 1 | 1 | 0% | 4,204 | 7,220 | +72% | 0 | 0 | — |
case-10 | fail→pass | 34,497 | 25,560 | -26% | 1 | 1 | 0% | 6,653 | 9,083 | +37% | 0 | 0 | — |
case-11 | fail→pass | 18,482 | 12,107 | -34% | 1 | 1 | 0% | 2,408 | 6,075 | +152% | 0 | 0 | — |
case-12 | fail→pass | 15,868 | 12,410 | -22% | 1 | 1 | 0% | 1,917 | 6,366 | +232% | 0 | 0 | — |
case-13 | pass→pass | 12,646 | 8,253 | -35% | 1 | 1 | 0% | 1,302 | 5,711 | +339% | 0 | 0 | — |
case-14 | fail→pass | 20,886 | 18,069 | -13% | 1 | 1 | 0% | 2,861 | 7,343 | +157% | 0 | 0 | — |
case-15 | pass→pass | 17,035 | 11,115 | -35% | 1 | 1 | 0% | 2,128 | 6,077 | +186% | 0 | 0 | — |
case-16 | fail→pass | 22,018 | 9,596 | -56% | 1 | 1 | 0% | 2,799 | 5,814 | +108% | 0 | 0 | — |
case-17 | pass→pass | 9,030 | 8,502 | -6% | 1 | 1 | 0% | 815 | 5,694 | +599% | 0 | 0 | — |
case-18 | pass→pass | 27,614 | 26,962 | -2% | 1 | 1 | 0% | 4,502 | 9,374 | +108% | 0 | 0 | — |
case-19 | fail→pass | 15,992 | 14,230 | -11% | 1 | 1 | 0% | 1,605 | 6,857 | +327% | 0 | 0 | — |
case-20 | pass→pass | 27,099 | 24,787 | -9% | 1 | 1 | 0% | 3,491 | 8,249 | +136% | 0 | 0 | — |
case-21 | pass→pass | 19,455 | 11,804 | -39% | 1 | 1 | 0% | 2,828 | 6,354 | +125% | 0 | 0 | — |
case-22 | fail→pass | 11,838 | 13,678 | +16% | 1 | 1 | 0% | 1,194 | 6,701 | +461% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +41 percentage points is the difference between those two pass rates over the 22 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.