本文介绍了如何在Solana程序中使用Pyth SDK获取实时价格数据,详细步骤包括创建Anchor项目、定义价格数据结构、获取并格式化价格数据,并最终在Solana的devnet上测试程序。
几个python库最近引入了影响本指南和Solana Playground使用的突破性更改。我们正在努力更新本指南以反映这些变化。同时, 你可以从python文档中看到一个示例实现。
Pyth 是一个预言机,它以简单易用的方式将实时数据费用带到链上。Pyth 直接与 第一方数据发布者 合作,将金融资产定价数据带到链上。Pyth 聚合了加密货币、股票、外汇货币和商品的数据。通过与众多数据提供者合作,Pyth 能够提供高度的数据完整性和安全性。在本指南中,我们将学习如何在你的 Solana 程序中从 Pyth 获取价格数据。
在本指南中,你将学习如何使用 Pyth SDK 将实时定价数据集成到你的程序中。你将:
在本指南中,我们将使用 Solana Playground。我们的测试是在 以下依赖项(2023 年 5 月 30 日)下进行的:
依赖项 | 版本 |
---|---|
anchor-lang | 0.27.0 |
solana-program | 1.14.17 |
pyth-sdk-solana | 0.7.0 |
Pyth 是一个数据预言机,它将各种资产类别的价格源带到 Solana 区块链上。“Pyth 价格更新在 Pythnet 上创建,并通过 Wormhole 网络(一个跨链消息协议)流式传输到链下。这些更新被签名,因此 Pyth 链上程序可以验证其真实性。” *(来源:Pyth 文档) Pythnet 是一个由 Solana 区块链驱动的私有集群。由于 Pyth 使用 Wormhole,它可以将数据带到多个区块链。
对于 Solana,资产和定价数据通过 Solana 账户存储并在链上广播。Pyth 中存在三种主要类型的账户:
今天的练习将主要关注价格账户。有关其他账户类型的更多信息,请查看 Pyth 文档。
Anchor 新手?
Anchor 是一个流行的开发框架,用于在 Solana 上构建程序。 要开始使用,请查看我们的 Anchor 入门指南。
我们将使用 Solana Playground 来加速我们的开发。Solana Playground 是一个基于 Web 的 IDE,允许你编写、部署和测试 Solana 程序。如果你更喜欢使用自己的本地 Anchor 项目,只需确保在你的
Cargo.toml
文件中添加pyth-sdk-solana = "0.7.1"
。
前往 beta.solpg.io 并点击“➕”创建一个新项目:
让我们创建一个新程序,该程序将从 Pyth 获取价格源并将其记录到 Solana 程序日志中。打开 src>lib.rs
,它应该已经预填充了一个简单的程序。继续删除默认内容。
让我们从导入我们程序所需的依赖项开始。我们将需要以下依赖项:
use anchor_lang::prelude::*;
use pyth_sdk_solana::{load_price_feed_from_account_info};
use std::str::FromStr;
// 这是你程序的公钥,它会在你构建项目时自动更新。
declare_id!("11111111111111111111111111111111");
除了 Anchor 依赖项外,我们还需要导入 Pyth SDK 的 load_price_feed_from_account_info
和 Rust 标准库中的 FromStr
特性(这将允许我们将字符串地址转换为公钥)。
Solana Playground 将使用默认的 declare_id!
字段来定义你的程序 ID。这将在你构建程序时自动更新。
我们还将定义两个常量:
HovQMDrbAgAYPCmHVSrezcSmkMtXSSUsLDFANExrZh2J
。在你的导入下方添加以下声明:
const BTC_USDC_FEED: &str = "HovQMDrbAgAYPCmHVSrezcSmkMtXSSUsLDFANExrZh2J";
const STALENESS_THRESHOLD: u64 = 60; // 陈旧阈值,单位为秒
你可以随意尝试不同的价格源和陈旧阈值,但在我们的示例中,我们将寻找在过去 60 秒内创建的 BTC/USD 价格源。
让我们为我们的价格源指令定义一个结构体。该结构体将定义我们必须传递给我们的程序以获取价格源的账户。在你的常量下方添加:
#[derive(Accounts)]
pub struct FetchBitcoinPrice<'info> {
#[account(mut)]
pub signer: Signer<'info>,
#[account(address = Pubkey::from_str(BTC_USDC_FEED).unwrap() @ FeedError::InvalidPriceFeed)]
pub price_feed: AccountInfo<'info>,
}
#[error_code]
pub enum FeedError {
#[msg("Invalid Price Feed")]
InvalidPriceFeed,
}
我们定义了一个名为 FetchBitcoinPrice
的结构体,它将有两个账户:
signer
:这将是签署交易并支付交易费用的账户。price_feed
:这是我们想要获取的价格源账户。我们使用 Anchor 的 address
约束(了解更多关于 Anchor 约束的信息,请查看我们的指南 这里)。我们使用 Pubkey::from_str
函数将我们的 BTC_USDC_FEED
字符串转换为公钥。我们还定义了一个错误代码 InvalidPriceFeed
,如果价格源账户无效,将抛出该错误。让我们定义我们的程序并创建一个函数来获取价格源。在你的结构体下方添加:
#[program]
mod hello_pyth {
use super::*;
pub fn fetch_btc_price(ctx: Context<FetchBitcoinPrice>) -> Result<()> {
// 1-获取最新价格
// 2-格式化显示值,四舍五入到最接近的美元
// 3-记录结果
Ok(())
}
}
我们定义了一个名为 hello_pyth
的程序和一个名为 fetch_btc_price
的函数,该函数将接受类型为 FetchBitcoinPrice
的 Context
并返回一个 Result
(要么是 Ok
,要么是 Err
)。
为了获取最新价格,我们将价格源地址 &ctx.accounts.price_feed
传递给 Pyth 的 load_price_feed_from_account_info()
函数:
// 1-获取最新价格
let price_account_info = &ctx.accounts.price_feed;
let price_feed = load_price_feed_from_account_info( &price_account_info ).unwrap();
let current_timestamp = Clock::get()?.unix_timestamp;
let current_price = price_feed.get_price_no_older_than(current_timestamp, STALENESS_THRESHOLD).unwrap();
我们还使用 Clock
结构体获取当前时间戳,并定义一个 current_price
变量,只要它不比我们之前指定的陈旧阈值更旧,就会获取价格。
Pyth 定价数据存储为 64 位有符号整数和 32 位有符号指数。
详细信息
Pyth 的 Price 结构体 以下是 Pyth 的 Price 结构体的定义(来源):
pub struct Price {
/// 价格。
#[serde(with = "utils::as_string")] // 确保转换为 json 时的准确性。
#[schemars(with = "String")]
pub price: i64,
/// 置信区间。
#[serde(with = "utils::as_string")]
#[schemars(with = "String")]
pub conf: u64,
/// 指数。
pub expo: i32,
/// 发布时间。
pub publish_time: UnixTimestamp,
}
为了以人类可读的格式记录价格(例如,27400 而不是 27400000000000),我们需要将 price
和置信区间(conf
)转换为 u64
,然后将其除以小数位数(我们也必须将其转换为 u64
)。由于 Pyth 将其存储为负数,我们必须在除法之前将其转换为正数。将以下内容添加到你的 fetch_btc_price
函数中:
// 2-格式化显示值,四舍五入到最接近的美元
let display_price = u64::try_from(current_price.price).unwrap() / 10u64.pow(u32::try_from(-current_price.expo).unwrap());
let display_confidence = u64::try_from(current_price.conf).unwrap() / 10u64.pow(u32::try_from(-current_price.expo).unwrap());
最后,我们将使用 msg!
将结果记录到 Solana 的程序日志中。你的最终指令应该如下所示:
#[program]
mod hello_pyth {
use super::*;
pub fn fetch_btc_price(ctx: Context<FetchBitcoinPrice>) -> Result<()> {
// 1-获取最新价格
let price_account_info = &ctx.accounts.price_feed;
let price_feed = load_price_feed_from_account_info( &price_account_info ).unwrap();
let current_timestamp = Clock::get()?.unix_timestamp;
let current_price = price_feed.get_price_no_older_than(current_timestamp, STALENESS_THRESHOLD).unwrap();
// 2-格式化显示值,四舍五入到最接近的美元
let display_price = u64::try_from(current_price.price).unwrap() / 10u64.pow(u32::try_from(-current_price.expo).unwrap());
let display_confidence = u64::try_from(current_price.conf).unwrap() / 10u64.pow(u32::try_from(-current_price.expo).unwrap());
// 3-记录结果
msg!("BTC/USD 价格: ({} +- {})", display_price, display_confidence);
Ok(())
}
}
干得漂亮!让我们测试一下。
点击左侧菜单中的 🔧 Build 来编译你的程序。你应该在控制台中看到成功消息:
Building...
Build successful. Completed in 3.17s.
如果你按照说明操作,你应该不会遇到任何错误,但如果你遇到了,请尝试按照说明进行调试。如果你需要帮助,请随时在 Discord 上联系我们。我们很乐意帮助你!
由于此项目仅用于演示目的,我们可以使用一个“一次性”钱包。Solana Playground 使得创建一个钱包变得容易。你应该在浏览器窗口的左下角看到一个红色的“未连接”点。点击它:
Solana Playground 将为你生成一个钱包(或者你可以导入自己的钱包)。你可以将其保存以备将来使用,当你准备好时点击继续。一个新的钱包将被初始化并连接到 Solana devnet。你将需要大约 5 个 Devnet SOL 来部署程序(查看我们的 关于空投 Devnet SOL 的指南)。
你应该点击左下角的“⚙️”图标以验证你处于“devnet”上。你也可以从下拉列表中选择“custom”,如果你想连接到你的 QuickNode Devnet 端点。
💡 创建一个 QuickNode 端点
要在 Solana 上构建,你需要一个 API 端点来连接网络。你可以使用公共节点或部署和管理你自己的基础设施;但是,如果你希望获得 8 倍更快的响应时间,你可以将繁重的工作交给我们。
了解为什么超过 50% 的 Solana 项目选择 QuickNode,并在此处注册免费账户 here。我们将使用一个 Solana Devnet 端点。
复制 HTTP 提供者链接:
当你准备好时,点击页面左侧的工具图标 🛠,然后点击“Deploy”。这将需要几分钟,但当它完成时,你应该会看到一个成功消息。
现在你的程序已经在链上了,让我们测试一下。点击页面左侧的“🧪”图标。Solana Playground 构建了一个易于使用的界面来测试程序。通过点击切换箭头展开 fetchBtcPrice
指令:
选择 My address
作为签名者,并粘贴你在程序中使用的相同 Pyth 价格源(我们使用了 HovQMDrbAgAYPCmHVSrezcSmkMtXSSUsLDFANExrZh2J
)。
点击“Test”将交易发送到 devnet 集群。你应该在控制台中看到成功消息:
Testing 'fetchBtcPrice'...
✅ Test 'fetchBtcPrice' passed.
你还会看到一个带有 Solana Explorer 链接的通知弹出窗口。打开它。如果通知在你点击之前消失了,你可以再次测试你的指令,或者在 Solana Explorer 中通过搜索你的程序 ID 来找到交易(例如,explorer.solana.com/address/YOUR_PROGRAM_ID?cluster=devnet )。你的程序 ID 可以在 🛠️ 工具菜单中的“Program ID”和 lib.rs
中的 declare_id
语句中找到。
当你在 Solana Explorer 中打开交易时,你应该看到一个“Program Instructions Logs”部分,其中包括 BTC 的美元价格:
干得漂亮!
你可以在 Solana Playground 上访问我们的完整代码。
你刚刚构建了一个程序,该程序从 Pyth 获取 BTC 的价格并将其记录到 Solana 的程序日志中。你可以使用相同的模式创建从 Pyth 获取任何数据的程序,并将其作为构建更复杂程序的起点。
有疑问或想法想分享吗?请在 Discord 或 Twitter 上给我们留言!
让我们知道 如果你有任何反馈或新主题的请求。我们很乐意听取你的意见。
- 原文链接: quicknode.com/guides/sol...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!