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(ð_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(ð_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!()
}
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);
}
注意事项
- 价格数据需要定期更新,确保使用最新数据
- 检查价格置信区间,避免使用低质量数据
- 处理价格不可用的情况
- 考虑价格延迟问题
- 在生产环境中添加适当的错误处理
进阶功能
- 订阅价格更新
- 批量获取多个价格对
- 验证价格更新签名
- 处理跨链价格数据
完整示例代码
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示例是一个命令行工具,可以查询多个价格对的数据。