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