mcpField
P
pyth

pyth

github.com/sendaifun数据与数据库130204 次安装13 周前更新
代码仓库

关于 pyth

这是一份将 Pyth Network 价格馈送接入 Solana 应用的开发指南,涵盖 Hermes 链下访问和 Rust 链上集成,并说明置信区间、数据时效与 EMA 价格的处理。

主要功能

  1. 01Hermes 价格查询与实时更新
  2. 02Solana 链上价格读取
  3. 03置信区间与 EMA 价格
  4. 04价格过期及账户校验指导

适用场景

  1. 01在应用中获取加密资产等行情
  2. 02在 Solana 程序中读取预言机价格
  3. 03为清算和抵押估值选择 EMA
  4. 04处理价格过期或置信度过宽

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/sendaifun/skills --skill pyth

安装后在 Claude Code 中输入/pyth确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

docs/
troubleshooting.md
examples/on-chain/
anchor-integration.rs
price-validation.rs
examples/price-feeds/
fetch-price.ts
multiple-prices.ts
examples/streaming/
real-time-updates.ts
resources/
api-reference.md
program-addresses.md
templates/
anchor-oracle.rs
pyth-client.ts

详细介绍

Pyth Network 开发指南

Pyth Network 是一种去中心化预言机,为加密货币、股票、外汇和大宗商品提供实时价格数据。本指南介绍如何将 Pyth 价格数据集成到 Solana 应用中。

概览

Pyth Network 提供:

  • 实时价格数据 - 通过拉取式预言机模型,每 400ms 更新一次
  • 置信区间 - 每个价格的统计不确定性范围
  • EMA 价格 - 指数移动平均价格(约 1 小时窗口)
  • 多资产支持 - 加密货币、股票、外汇、大宗商品、指数
  • 链上集成 - 适用于 Solana 程序的 CPI
  • 链下集成 - 通过 Hermes 提供 HTTP 和 WebSocket API

程序 ID

程序地址描述
Solana Receiverrec5EKMGg6MxZYaMdyBfgwp4d5rB9T1VQH5pJv5LtFJ将价格更新发布到 Solana
Price FeedpythWSnswVUd12oZpeFP8e9CVaEqJg25g1Vtc2biRsT存储价格数据

部署于:Solana Mainnet、Devnet、Eclipse Mainnet/Testnet、Sonic networks

常用价格数据 ID

资产十六进制数据 ID
BTC/USD0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43
ETH/USD0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace
SOL/USD0xef0d8b6fda2ceba41da15d4095d1da392a0d2f8ed0c6c7bc0f4cfac8c280b56d
USDC/USD0xeaa020c61cc479712813461ce153894a96a6c00b21ed0cfc2798d1f9a9e9c94a
USDT/USD0x2b89b9dc8fdf9f34709a5b106b472f0f39bb6ca9ce04b0fd7f2e971688e2e53b

完整列表:https://pyth.network/developers/price-feed-ids

快速开始

安装
# TypeScript/JavaScript
npm install @pythnetwork/hermes-client @pythnetwork/pyth-solana-receiver

# Rust (add to Cargo.toml)
# pyth-solana-receiver-sdk = "0.3.0"
获取价格(链下)
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)}`);
}
在链上使用价格(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(())
}

核心概念

价格结构

每个 Pyth 价格包含:

字段类型描述
pricei64定点格式的价格值
confu64置信区间(标准差)
expoi32缩放指数(例如,-8 表示除以 10^8)
publish_timei64价格的 Unix 时间戳

转换为显示价格:

const displayPrice = price * Math.pow(10, expo);
// Example: price=19405100, expo=-2 → $194,051.00
置信区间

置信区间表示报告价格的不确定性:

// 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;

最佳实践:拒绝置信度超过价格 2% 的价格:

const maxConfidenceRatio = 0.02; // 2%
const confidenceRatio = confidence / Math.abs(price);

if (confidenceRatio > maxConfidenceRatio) {
  throw new Error("Price confidence too wide");
}
EMA 价格

指数移动平均价格可平滑短期波动:

  • 约 1 小时的平均窗口(5921 个 Solana 区块槽位)
  • 按置信度的倒数加权(置信度越高,权重越大)
  • 适用于:清算、抵押品估值
  • 可通过 ema_price 和 ema_conf 获取
// Use EMA for less volatile applications
const emaPrice = priceUpdate.emaPrice;
const emaConf = priceUpdate.emaConf;

链下集成

Hermes 客户端

Hermes 是链下获取 Pyth 价格的推荐方式。

公共端点:https://hermes.pyth.network

用于生产环境时,请从 Pyth 数据提供商获取专用端点。

获取最新价格
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
]);
实时更新流
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();
将价格发布到 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);
}

链上集成(Rust)

设置

添加到 Cargo.toml:

[dependencies]
pyth-solana-receiver-sdk = "0.3.0"
anchor-lang = "0.30.1"
在 Anchor 程序中读取价格
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>,
}
使用价格进行计算
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,
}
多个价格数据
#[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)
    }
}

最佳实践

1. 始终检查数据是否过期
// 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. 验证置信区间
// 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. 验证账户所有权
// 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. 对敏感操作使用 EMA
// For liquidations, use EMA to avoid manipulation
let ema_price = price_update.get_ema_price_no_older_than(&clock, 60)?;
5. 处理价格不可用的情况
try {
  const price = await client.getLatestPriceUpdates([feedId]);
  // Use price
} catch (error) {
  // Fallback behavior or reject transaction
  console.error("Price unavailable:", error);
}
6. 考虑抢先交易
  • 对手方可能会在你的交易之前看到价格更新
  • 不要设计与价格更新竞速的逻辑
  • 使用适当的滑点容差

价格数据类型

固定价格数据账户
  • 由 Pyth 持续维护
  • 每个数据源都有固定地址
  • 始终包含最新价格
  • 所有用户共享(可能发生拥堵)
临时价格更新账户
  • 按每笔交易创建
  • 可指定分片 ID 以实现并行处理
  • 使用后可收回租金
  • 更适合高吞吐量应用
// Use shard ID to avoid congestion
const transactionBuilder = pythReceiver.newTransactionBuilder({
  shardId: Math.floor(Math.random() * 65536), // Random shard
});

资源

官方文档
GitHub 代码仓库
NPM 包
Rust Crate

技能结构

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

评论

支持 Markdown