## 透過 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)