## 透過 Pyth 取得及時價格 - [Documentation](https://docs.pyth.network/price-feeds/use-real-time-data/solana#write-frontend-code) - [Crate: pyth-solana-receiver-sdk](https://crates.io/crates/pyth-solana-receiver-sdk) - [Package: https://www.npmjs.com/package/@pythnetwork/pyth-solana-receiver](https://www.npmjs.com/package/@pythnetwork/pyth-solana-receiver) - [Price Feed Ids](https://pyth.network/developers/price-feed-ids) - [其他參考程式:send_usd](https://github.com/pyth-network/pyth-examples/tree/main/price_feeds/solana/send_usd) ## 步驟 - 設置 devnet RPC 與環境,取得 program id 並填入合約。 - Dependencies 與版本 ``` [dependencies] anchor-lang = "0.30.0" solana-program = "1.18.2" pyth-solana-receiver-sdk = "0.3.1" ``` ## Contract Code ``` // 引入模組 use anchor_lang::prelude::*; use pyth_solana_receiver_sdk::price_update::get_feed_id_from_hex; use pyth_solana_receiver_sdk::price_update::{PriceUpdateV2}; declare_id!("YOUR-PROGRAM-ID"); #[program] pub mod pyth { use super::*; // get_price 是一個 public function 接受一個參數為 Context<Sample> 類型 pub fn get_price(ctx: Context<Sample>) -> Result<()> { let price_update = &mut ctx.accounts.price_update; // get_price_no_older_than will fail if the price update is more than 30 seconds old let maximum_age: u64 = 30; // get_price_no_older_than will fail if the price update is for a different price feed. // This string is the id of the BTC/USD feed. See https://pyth.network/developers/price-feed-ids for all available IDs. let feed_id: [u8; 32] = get_feed_id_from_hex("0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43")?; let price = price_update.get_price_no_older_than(&Clock::get()?, maximum_age, &feed_id)?; // Sample output: // The price is (7160106530699 ± 5129162301) * 10^-8 msg!("The price is ({} ± {}) * 10^{}", price.price, price.conf, price.exponent); Ok(()) } } // Sample 這個 Structure 裡面定義兩個 accounts // signer 和 PriceUpdateV2 這個 struct (裡面包含價格更新數據) #[derive(Accounts)] #[instruction()] pub struct Sample<'info> { #[account(mut)] pub payer: Signer<'info>, // Add this account to any instruction Context that needs price data. /// CHECK: The price data is trusted as it comes from a known oracle source pub price_update: Account<'info, PriceUpdateV2>, } ``` Test Code ``` import * as anchor from "@coral-xyz/anchor"; import { Program } from "@project-serum/anchor"; import { Pyth } from "../target/types/pyth"; import { Keypair, LAMPORTS_PER_SOL, SystemProgram, Transaction } from "@solana/web3.js"; import { PythSolanaReceiver } from "@pythnetwork/pyth-solana-receiver"; describe("pyth", () => { anchor.setProvider(anchor.AnchorProvider.env()); const provider = anchor.getProvider() as anchor.AnchorProvider; const connection = provider.connection; const program = anchor.workspace.Pyth as Program<Pyth>; const payer = Keypair.generate(); const wallet = new anchor.Wallet(payer); const pythSolanaReceiver = new PythSolanaReceiver({ connection, wallet }); const priceFeedAccount = pythSolanaReceiver .getPriceFeedAccountAddress(0, "0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43") .toBase58(); const confirm = async (signature: string): Promise<string> => { const block = await connection.getLatestBlockhash(); await connection.confirmTransaction({ signature, ...block, }); return signature; }; const log = async (signature: string): Promise<string> => { console.log( `Your transaction signature: https://explorer.solana.com/transaction/${signature}?cluster=devnet` ); return signature; }; // Transfer Devnet SOL 到測試錢包: payer it("Transfer SOL to payer for testing", async () => { let tx = new Transaction(); tx.add( SystemProgram.transfer({ fromPubkey: provider.publicKey, toPubkey: payer.publicKey, lamports: 0.1 * LAMPORTS_PER_SOL, }) ); await provider.sendAndConfirm(tx).then(log); }); // 呼叫合約取得報價 it("Gets the price", async () => { await program.methods .getPrice() .accounts({ payer: payer.publicKey, priceUpdate: priceFeedAccount, }) .signers([payer]) .rpc() .then(confirm) .then(log); }); }); ``` ## Issues ### 合約 Error - error[E0277]: the trait bound `PriceUpdateV2: anchor_lang::AccountDeserialize` is not satisfied - 解法:將 anchor-lang 0.29.0 改為 0.30.0 ### 測試 Error - Error: Package subpath './dist/lib/client' is not defined by "exports" in /mnt/c/Users/2017x/pyth/pyth/node_modules/rpc-websockets/package.json - 解法:將 ```rpc-websockets``` 套件從 7.11.2 降版為 7.11.0 - Error: AnchorError occurred. Error Code: PriceTooOld. Error Number: 16000. Error Message: This price feed update's age exceeds the requested maximum age. - 解法:更改合約中 ```maximum_age``` 的值(記得重新 build & deploy)