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;
10use std::convert::TryInto;
11
12use crate::{
13 check::{check_account_key},
14 price_update::OriginSolanaPriceUpdateV2,
15};
16
17pub const PYTH_SOL_USD_FEED: Pubkey = pubkey!("7UVimffxr9ow1uXYxsr4LHAcV58mLzhmwaeKvJ1pjLiE");
18
19pub const PRICE_FEED_DISCRIMATOR: [u8; 8] = [34, 241, 35, 99, 157, 126, 244, 205];
20
21pub fn parse_price(data: &[u8]) -> Result<OriginSolanaPriceUpdateV2, ProgramError> {
22 let suffix = &data[..8];
23 if suffix != PRICE_FEED_DISCRIMATOR {
24 msg!("discrimator err");
25 return Err(ProgramError::InvalidArgument);
26 }
27 Ok(OriginSolanaPriceUpdateV2::new(data)?)
28}
29
30pub fn get_oracle_price_fp32(
32 account: &AccountInfo,
33 clock: &Clock,
34 maximum_age: u64,
35) -> Result<u128, ProgramError> {
36 check_account_key(account, &PYTH_SOL_USD_FEED)?;
37 let data = &account.data.borrow();
38 let update = parse_price(data)?;
39
40 let actual_feed_id = update.0.price_message.feed_id;
41
42 let pyth_solana_receiver_sdk::price_update::Price { price, exponent, .. } =
43 update.0
44 .get_price_no_older_than(clock, maximum_age, &actual_feed_id)
45 .map_err(|e| {
46 msg!("pyth error: {:?}", e);
47 ProgramError::InvalidArgument
48 })?;
49
50 let raw_price = price as i128;
51 let fp32_price = if exponent < 0 {
52 (raw_price << 32) / 10i128.pow((-exponent) as u32)
53 } else {
54 (raw_price << 32) * 10i128.pow(exponent as u32)
55 };
56
57 Ok(fp32_price as u128)
58}
59
60pub fn get_domain_price_sol(
63 domain_price_usd: u64,
64 sol_pyth_feed_account: &AccountInfo,
65 clock: &Clock,
66) -> Result<u64, ProgramError> {
67 #[cfg(feature = "devnet")]
68 let query_deviation = 600_000;
69 #[cfg(not(feature = "devnet"))]
70 let query_deviation = 60;
71
72 let sol_price_fp32 = get_oracle_price_fp32(sol_pyth_feed_account, clock, query_deviation)?;
73
74 let usd_amount = domain_price_usd / 1_000_000;
77
78 let lamports = ((usd_amount as u128) << 32)
79 .checked_mul(1_000_000_000u128)
80 .ok_or(ProgramError::InvalidArgument)?
81 / sol_price_fp32;
82
83 msg!("{:?} usd = {:?} lamports", domain_price_usd, lamports);
84
85 Ok(lamports.try_into().map_err(|_| ProgramError::InvalidArgument)?)
86}