Rust智能合约开发库pyth-sdk的使用,pyth-sdk提供区块链价格预言机与数据交互功能

Rust智能合约开发库pyth-sdk的使用,pyth-sdk提供区块链价格预言机与数据交互功能

Pyth Network Common Rust SDK

这个crate包含了Pyth Network的数据结构,这些数据结构在所有基于Rust的Pyth Network数据消费者之间共享。这个crate通常与特定平台的crate(如pyth-sdk-solana)结合使用。

使用方法

SDK有两个核心数据类型:

  • PriceFeed 是一个容器,包含产品(如BTC/USD)当前可用的所有定价信息
  • Price 表示带有不确定度的价格

使用这个SDK的典型方式是首先为你的应用所需的一个或多个产品获取PriceFeed。这一步通常使用上述提到的特定平台crate,它们提供了针对特定区块链的检索方法。一旦你有了PriceFeed,你可以调用下面的方法来获取你的应用需要的价格:

获取当前价格

从产品的PriceFeed获取当前价格:

const STALENESS_THRESHOLD : u64 = 60; // 陈旧性阈值(秒)
let current_timestamp = ...;
let current_price: Price = price_feed.get_price_no_older_than(current_timestamp, STALENESS_THRESHOLD).ok_or(StdError::not_found("Current price is not available"))?;
println!("price: ({} +- {}) x 10^{}", current_price.price, current_price.conf, current_price.expo);

价格会与一个代表价格不确定度的置信区间一起返回。两个值都表示为固定点数a * 10^e。如果当前价格不可用,该方法会返回None

EMA价格

PriceFeed包含一个指数加权移动平均(EMA)价格,代表近期价格的时间平均值。可以如下获取EMA价格:

const STALENESS_THRESHOLD : u64 = 60; // 陈旧性阈值(秒)
let current_timestamp = ...;
let ema_price: Price = price_feed.get_ema_price_no_older_than(current_timestamp, STALENESS_THRESHOLD).ok_or(StdError::not_found("EMA price is not available"))?;
println!("price: ({} +- {}) x 10^{}", ema_price.price, ema_price.conf, ema_price.expo);

操作价格

Price结构支持算术操作,允许你组合来自多个产品的价格。这些操作可以用来为一些没有直接在Pyth Network上列出的产品定价:

非美元价格

Pyth Network上列出的大多数资产都是以美元报价的,例如BTC/USD价格源提供每BTC的美元数。然而,一些应用希望以其他报价货币定价,如每BTC的ETH数。应用可以组合两个美元价格来以不同的报价货币定价资产:

let btc_usd: Price = ...;
let eth_usd: Price = ...;
// -8是结果的期望指数
let btc_eth: Price = btc_usd.get_price_in_quote(&eth_usd, -8);
println!("BTC/ETH price: ({} +- {}) x 10^{}", price.price, price.conf, price.expo);

为一篮子资产定价

应用也可以计算多个资产组合的价值:

let btc_usd: Price = ...;
let eth_usd: Price = ...;
// 每个资产的数量,以固定点a * 10^e表示
// 这代表0.1 BTC和0.05 ETH
// -8是结果的期望指数
let basket_price: Price = Price::price_basket(&[
    (btc_usd, 10, -2),
    (eth_usd, 5, -2)
  ], -8);
println!("0.1 BTC and 0.05 ETH are worth: ({} +- {}) x 10^{} USD",
         basket_price.price, basket_price.conf, basket_price.expo);

这个操作对于定价很有用,例如由两种基础货币支持的LP代币。

使用流动性调整价格

应用可以调整Pyth价格以纳入大仓位对市场的影响和流动性,因为大仓位的有效交易价格与中间价和盘口价格不同。假设应用想要基于100 BTC卖出90%中间价的事实来估值100 BTC卖出的有效执行价格。基于假设市场影响与卖出数量成正比,应用可以结合当前的Pyth价格和其流动性观点来计算调整后的价格:

let btc_usd: Price = ...;
// deposits是协议中存入的代币总数
let deposits: u64 = 100;
// deposits_endpoint代表rate_discount_final生效时的代币存入量
let deposits_endpoint: u64 = 1000;
// rate_discount_initial和_final是百分比,以固定点表示为x * 10^discount_exponent
// 例如50且discount_exponent = -2表示50%
let rate_discount_initial: u64 = 100;
let rate_discount_final: u64 = 90;
let discount_exponent: i32 = 2;

let price_collateral: Price = btc_usd.get_collateral_valuation_price(
    deposits,
    deposits_endpoint,
    rate_discount_initial,
    rate_discount_final,
    discount_exponent).ok_or(StdError::not_found("Issue with querying collateral price"))?;
println!("The valuation price for the collateral given {} tokens deposited is ({} +- {}) x 10^{} USD",
         deposits, price_collateral.price, price_collateral.conf, price_collateral.expo);

这里,deposits表示存入的抵押品总量。get_collateral_valuation_price接受协议中的总存入量,并在(0, rate_discount_inital)和(deposits_endpoint, rate_discount_final)之间线性插值来计算deposits处的折扣。需要注意的是,这个函数会根据提供的折扣和存入输入来缩放价格,但不会改变置信度。

要调整借入仓位的估值价格,协议可以类似地结合当前的Pyth价格和他们估计的流动性,但使用get_borrow_valuation_price函数代替get_collateral_valuation_price

完整示例代码

use pyth_sdk::{Price, PriceFeed};
use std::error::Error;

fn main() -> Result<(), Box<dyn Error>> {
    // 假设我们已经从区块链获取了PriceFeed
    let price_feed: PriceFeed = get_price_feed_from_blockchain();
    
    // 获取当前价格
    const STALENESS_THRESHOLD: u64 = 60; // 60秒陈旧性阈值
    let current_timestamp = get_current_timestamp();
    let current_price = price_feed
        .get_price_no_older_than(current_timestamp, STALENESS_THRESHOLD)
        .ok_or("Current price not available")?;
    
    println!("Current price: ({} ± {}) × 10^{}", 
        current_price.price, current_price.conf, current_price.expo);
    
    // 获取EMA价格
    let ema_price = price_feed
        .get_ema_price_no_older_than(current_timestamp, STALENESS_THRESHOLD)
        .ok_or("EMA price not available")?;
    
    println!("EMA price: ({} ± {}) × 10^{}", 
        ema_price.price, ema_price.conf, ema_price.expo);
    
    // 计算BTC/ETH价格
    let btc_usd: Price = get_btc_usd_price();
    let eth_usd: Price = get_eth_usd_price();
    let btc_eth = btc_usd.get_price_in_quote(&eth_usd, -8)?;
    println!("BTC/ETH price: ({} ± {}) × 10^{}", 
        btc_eth.price, btc_eth.conf, btc_eth.expo);
    
    // 计算一篮子资产价值
    let basket_price = Price::price_basket(&[
        (btc_usd, 10, -2), // 0.1 BTC
        (eth_usd, 5, -2)   // 0.05 ETH
    ], -8)?;
    
    println!("Basket value: ({} ± {}) × 10^{} USD", 
        basket_price.price, basket_price.conf, basket_price.expo);
    
    Ok(())
}

// 模拟函数 - 实际实现会从区块链获取数据
fn get_price_feed_from_blockchain() -> PriceFeed {
    // 实际实现会从区块链获取PriceFeed
    unimplemented!()
}

fn get_current_timestamp() -> u64 {
    // 获取当前时间戳
    unimplemented!()
}

fn get_btc_usd_price() -> Price {
    // 获取BTC/USD价格
    unimplemented!()
}

fn get_eth_usd_price() -> Price {
    // 获取ETH/USD价格
    unimplemented!()
}

1 回复

Rust智能合约开发库pyth-sdk的使用指南

概述

pyth-sdk是一个用于与Pyth网络价格预言机交互的Rust开发库。Pyth网络提供高精度、实时的金融市场数据,适用于区块链智能合约开发。该SDK使开发者能够轻松获取各种资产(加密货币、股票、外汇等)的最新价格信息。

主要功能

  • 获取Pyth网络中的最新价格数据
  • 解析Pyth价格账户数据
  • 验证价格更新
  • 支持Solana和EVM兼容链

安装方法

在Cargo.toml中添加依赖:

[dependencies]
pyth-sdk = "0.10.0"
solana-sdk = "1.14.0"  # 如果开发Solana程序需要添加

基本使用方法

1. 初始化客户端

use pyth_sdk_solana::{load_price_feed_from_account, PriceFeed};

// 从Solana账户加载价格数据
let price_account_data: &[u8] = ...; // 从链上获取的账户数据
let price_feed: PriceFeed = load_price_feed_from_account(price_account_data).unwrap();

2. 获取最新价格

// 获取当前价格
if let Some(current_price) = price_feed.get_current_price() {
    println!(
        "Price: {} ± {} ({} confidence)",
        current_price.price,
        current_price.confidence,
        current_price.publish_time
    );
}

3. 获取EMA(指数移动平均)价格

if let Some(ema_price) = price_feed.get_ema_price() {
    println!(
        "EMA Price: {} ± {}",
        ema_price.price,
        ema_price.confidence
    );
}

完整示例(Solana程序)

use pyth_sdk_solana::{load_price_feed_from_account, PriceFeed};
use solana_program::{
    account_info::AccountInfo,
    entrypoint,
    entrypoint::ProgramResult,
    msg,
    pubkey::Pubkey,
};

entrypoint!(process_instruction);

fn process_instruction(
    _program_id: &Pubkey,
    accounts: &[AccountInfo],
    _instruction_data: &[u8],
) -> ProgramResult {
    // 第一个账户是Pyth价格账户
    let pyth_price_account = &accounts[0];
    
    // 加载价格数据
    let price_f feed = load_price_feed_from_account(&pyth_price_account.data.borrow())
        .map_err(|e| {
            msg!("Failed to load price feed: {:?}", e);
            ProgramError::InvalidAccountData
        })?;
    
    // 获取当前价格
    if let Some(current_price) = price_feed.get_current_price() {
        msg!("Current price: {}", current_price.price);
        msg!("Confidence interval: ±{}", current_price.confidence);
        msg!("Last updated: {}", current_price.publish_time);
    } else {
        msg!("No current price available");
    }
    
    Ok(())
}

EVM链使用示例

use pyth_sdk::*;
use std::str::FromStr;

#[tokio::main]
async fn main() {
    // 初始化Pyth客户端
    let pyth = PythClient::new(
        "https://xc-mainnet.pyth.network".to_string(),
        "https://rpc.ankr.com/eth".to_string(),
    ).await.unwrap();
    
    // 获取ETH/USD价格ID
    let price_id = PriceIdentifier::from_str(
        "0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace"
    ).unwrap();
    
    // 获取最新价格更新数据
    let price_update = pyth.get_price_update(&price_id).await.unwrap();
    
    println!("ETH/USD Price: {}", price_update.price.price);
    println!("Confidence: ±{}", price_update.price.confidence);
    println!("Exponent: {}", price_update.price.expo);
}

注意事项

  1. 价格数据需要定期更新,确保使用最新数据
  2. 检查价格置信区间,避免使用低质量数据
  3. 处理价格不可用的情况
  4. 考虑价格延迟问题
  5. 在生产环境中添加适当的错误处理

进阶功能

  • 订阅价格更新
  • 批量获取多个价格对
  • 验证价格更新签名
  • 处理跨链价格数据

完整示例代码

Solana完整示例

use pyth_sdk_solana::{load_price_feed_from_account, PriceFeed};
use solana_program::{
    account_info::{AccountInfo, next_account_info},
    entrypoint,
    entrypoint::ProgramResult,
    msg,
    program_error::ProgramError,
    pubkey::Pubkey,
};

// 定义程序入口点
entrypoint!(process_instruction);

fn process_instruction(
    program_id: &Pubkey,
    accounts: &[AccountInfo],
    _instruction_data: &[u8],
) -> ProgramResult {
    // 获取账户迭代器
    let accounts_iter = &mut accounts.iter();
    
    // 获取Pyth价格账户
    let pyth_price_account = next_account_info(accounts_iter)?;
    
    // 验证程序ID
    if pyth_price_account.owner != program_id {
        msg!("Account does not have the correct program id");
        return Err(ProgramError::IncorrectProgramId);
    }
    
    // 加载价格数据
    let price_feed = load_price_feed_from_account(&pyth_price_account.data.borrow())
        .map_err(|e| {
            msg!("Failed to load price feed: {:?}", e);
            ProgramError::InvalidAccountData
        })?;
    
    // 获取当前价格
    if let Some(current_price) = price_feed.get_current_price() {
        msg!("Current price: {}", current_price.price);
        msg!("Confidence interval: ±{}", current_price.confidence);
        msg!("Last updated: {}", current_price.publish_time);
        
        // 获取EMA价格
        if let Some(ema_price) = price_feed.get_ema_price() {
            msg!("EMA Price: {}", ema_price.price);
            msg!("EMA Confidence: ±{}", ema_price.confidence);
        }
    } else {
        msg!("No current price available");
    }
    
    Ok(())
}

EVM完整示例

use pyth_sdk::{PythClient, PriceIdentifier, PriceUpdate};
use std::str::FromStr;
use tokio;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 初始化Pyth客户端
    let pyth = PythClient::new(
        "https://xc-mainnet.pyth.network".to_string(),
        "https://rpc.ankr.com/eth".to_string(),
    ).await?;
    
    // 定义要查询的价格对ID
    let price_ids = vec![
        // ETH/USD
        "0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace",
        // BTC/USD
        "0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43",
    ];
    
    // 批量获取价格更新
    for price_id_str in price_ids {
        let price_id = PriceIdentifier::from_str(price_id_str)?;
        
        match pyth.get_price_update(&price_id).await {
            Ok(price_update) => {
                println!("Price ID: {}", price_id_str);
                println!("Price: {}", price_update.price.price);
                println!("Confidence: ±{}", price_update.price.confidence);
                println!("Exponent: {}", price_update.price.expo);
                println!("Publish Time: {}", price_update.price.publish_time);
                println!("-----------------------------------");
            }
            Err(e) => {
                eprintln!("Failed to get price update for {}: {}", price_id_str, e);
            }
        }
    }
    
    Ok(())
}

以上示例展示了如何在Solana和EVM链上使用pyth-sdk获取价格数据。Solana示例是一个完整的智能合约程序,而EVM示例是一个命令行工具,可以查询多个价格对的数据。

回到顶部