web3_utils/
pyth.rs

1use solana_program::{
2    account_info::AccountInfo,
3    clock::Clock,
4    msg,
5    program_error::ProgramError,
6    pubkey::{Pubkey},
7    pubkey,
8    sysvar::Sysvar,
9};
10use pyth_solana_receiver_sdk::{
11    self,
12    price_update::Price,
13};
14use std::convert::TryInto;
15
16use crate::{check::{check_account_key}, price_update::OriginSolanaPriceUpdateV2};
17
18pub const SOL_MINT: Pubkey = pubkey!("So11111111111111111111111111111111111111112");
19
20pub const PYTH_SOL_USD_FEED: Pubkey = pubkey!("7UVimffxr9ow1uXYxsr4LHAcV58mLzhmwaeKvJ1pjLiE");
21
22pub const PRICE_FEED_DISCRIMATOR: [u8; 8] = [34, 241, 35, 99, 157, 126, 244, 205];
23
24pub const PYTH_PRICE_FEED: [u8; 32] = [
25    239, 13, 139, 111, 218, 44, 235, 164, 29, 161, 93, 64, 149, 209, 218, 57, 42, 13,
26    47, 142, 208, 198, 199, 188, 15, 76, 250, 200, 194, 128, 181, 109,
27];
28
29pub fn parse_price(data: &[u8]) -> Result<OriginSolanaPriceUpdateV2, ProgramError> {
30    // now the pyth accounts are anchor account
31    let suffix = &data[..8];
32    if suffix != PRICE_FEED_DISCRIMATOR {
33        msg!("discrimator err");
34        return Err(ProgramError::InvalidArgument);
35    }
36    msg!("discrimator OK");
37    let update = OriginSolanaPriceUpdateV2::new(data)?;
38
39    Ok(update)
40}
41 
42pub fn get_oracle_price_fp32(
43    account: &AccountInfo,
44    clock: &Clock,
45    maximum_age: u64,
46) -> Result<u64, ProgramError> {
47    check_account_key(account, &PYTH_SOL_USD_FEED)?;
48    msg!("pyth account ok");
49
50    let data = &account.data.borrow();
51    let update = parse_price(data)?;
52    msg!("get the update ok");
53
54    let Price { price, exponent, .. } = update.0
55        .get_price_no_older_than(clock, maximum_age, &PYTH_PRICE_FEED)
56        .map_err(|_| ProgramError::InvalidArgument)?;
57    msg!("get the price ok");
58
59    let price = if exponent > 0 {
60        ((price as u128) << 32) * 10u128.pow(exponent as u32)
61    } else {
62        ((price as u128) << 32) / 10u128.pow((-exponent) as u32)
63    };
64
65    let corrected_price = (price * 10u128.pow(6)) / 10u128.pow(9);
66
67    let final_price: u64 = corrected_price
68        .try_into()
69        .map_err(|_| ProgramError::InvalidArgument)?;
70    msg!("get the correct price ok");
71
72    msg!("Pyth SOL/USD FP32 price: {:?}", final_price);
73
74    Ok(final_price)
75}
76
77
78pub fn get_domain_price_sol(
79    domain_price_usd: u64,
80    sol_pyth_feed_account: &AccountInfo,
81) -> Result<u64, ProgramError> {
82
83    let clock = Clock::get()
84        .map_err(|_| ProgramError::InvalidArgument)?;
85    msg!("get clock ok");
86
87    #[cfg(feature="devnet")]
88    let query_deviation = 6000;
89    #[cfg(not(feature="devnet"))]
90    let query_deviation = 60;
91
92    msg!("now the deviation: {:?}", query_deviation);
93
94    let sol_price = get_oracle_price_fp32(
95        &sol_pyth_feed_account, &clock, query_deviation)
96        .map_err(|_| ProgramError::InvalidArgument)?;
97
98    Ok(domain_price_usd * sol_price)
99}