@@ -18,15 +18,19 @@ use bytemuck::{
1818 Pod , PodCastError , Zeroable ,
1919} ;
2020
21+ #[ cfg( target_arch = "bpf" ) ]
22+ use solana_program:: { clock:: Clock , sysvar:: Sysvar } ;
23+
2124solana_program:: declare_id!( "PythC11111111111111111111111111111111111111" ) ;
2225
23- pub const MAGIC : u32 = 0xa1b2c3d4 ;
24- pub const VERSION_2 : u32 = 2 ;
25- pub const VERSION : u32 = VERSION_2 ;
26- pub const MAP_TABLE_SIZE : usize = 640 ;
27- pub const PROD_ACCT_SIZE : usize = 512 ;
28- pub const PROD_HDR_SIZE : usize = 48 ;
29- pub const PROD_ATTR_SIZE : usize = PROD_ACCT_SIZE - PROD_HDR_SIZE ;
26+ pub const MAGIC : u32 = 0xa1b2c3d4 ;
27+ pub const VERSION_2 : u32 = 2 ;
28+ pub const VERSION : u32 = VERSION_2 ;
29+ pub const MAP_TABLE_SIZE : usize = 640 ;
30+ pub const PROD_ACCT_SIZE : usize = 512 ;
31+ pub const PROD_HDR_SIZE : usize = 48 ;
32+ pub const PROD_ATTR_SIZE : usize = PROD_ACCT_SIZE - PROD_HDR_SIZE ;
33+ pub const MAX_SEND_LATENCY : u64 = 25 ;
3034
3135/// The type of Pyth account determines what data it contains
3236#[ derive( Copy , Clone ) ]
@@ -180,15 +184,15 @@ pub struct Ema
180184 /// The current value of the EMA
181185 pub val : i64 ,
182186 /// numerator state for next update
183- numer : i64 ,
187+ pub numer : i64 ,
184188 /// denominator state for next update
185- denom : i64
189+ pub denom : i64
186190}
187191
188192/// Price accounts represent a continuously-updating price feed for a product.
189193#[ derive( Copy , Clone ) ]
190194#[ repr( C ) ]
191- pub struct Price
195+ pub struct PriceAccountData
192196{
193197 /// pyth magic number
194198 pub magic : u32 ,
@@ -235,14 +239,71 @@ pub struct Price
235239 /// price components one per quoter
236240 pub comp : [ PriceComp ; 32 ]
237241}
242+ #[ derive( Copy , Clone ) ]
243+ #[ repr( C ) ]
244+ pub struct Price {
245+ /// account type
246+ pub atype : u32 ,
247+ /// price account size
248+ pub size : u32 ,
249+ /// price or calculation type
250+ pub ptype : PriceType ,
251+ /// price exponent
252+ pub expo : i32 ,
253+ /// number of component prices
254+ pub num : u32 ,
255+ /// number of quoters that make up aggregate
256+ pub num_qt : u32 ,
257+ /// slot of last valid (not unknown) aggregate price
258+ pub last_slot : u64 ,
259+ /// valid slot-time of agg. price
260+ pub valid_slot : u64 ,
261+ /// time-weighted average price
262+ pub twap : Ema ,
263+ /// time-weighted average confidence interval
264+ pub twac : Ema ,
265+ /// product account key
266+ pub prod : AccKey ,
267+ /// next Price account in linked list
268+ pub next : AccKey ,
269+ /// valid slot of previous update
270+ pub prev_slot : u64 ,
271+ /// aggregate price of previous update
272+ pub prev_price : i64 ,
273+ /// confidence interval of previous update
274+ pub prev_conf : u64 ,
275+ /// aggregate price info
276+ pub agg : PriceInfo ,
277+ }
238278
239279#[ cfg( target_endian = "little" ) ]
240- unsafe impl Zeroable for Price { }
280+ unsafe impl Zeroable for PriceAccountData { }
241281
242282#[ cfg( target_endian = "little" ) ]
243- unsafe impl Pod for Price { }
283+ unsafe impl Pod for PriceAccountData { }
244284
245285impl Price {
286+ fn from_price_account_data ( price_account_data : & PriceAccountData ) -> Self {
287+ Price {
288+ atype : price_account_data. atype ,
289+ size : price_account_data. size ,
290+ ptype : price_account_data. ptype ,
291+ expo : price_account_data. expo ,
292+ num : price_account_data. num ,
293+ num_qt : price_account_data. num_qt ,
294+ last_slot : price_account_data. last_slot ,
295+ valid_slot : price_account_data. valid_slot ,
296+ twap : price_account_data. twap ,
297+ twac : price_account_data. twac ,
298+ prod : price_account_data. prod ,
299+ next : price_account_data. next ,
300+ prev_slot : price_account_data. prev_slot ,
301+ prev_price : price_account_data. prev_price ,
302+ prev_conf : price_account_data. prev_conf ,
303+ agg : price_account_data. agg
304+ }
305+ }
306+
246307 /**
247308 * Get the current price and confidence interval as fixed-point numbers of the form a * 10^e.
248309 * Returns a struct containing the current price, confidence interval, and the exponent for both
@@ -378,19 +439,38 @@ pub fn load_product(data: &[u8]) -> Result<&Product, PythError> {
378439}
379440
380441/** Get a `Price` account from the raw byte value of a Solana account. */
381- pub fn load_price ( data : & [ u8 ] ) -> Result < & Price , PythError > {
382- let pyth_price = load :: < Price > ( & data) . map_err ( |_| PythError :: InvalidAccountData ) ?;
442+ pub fn load_price_account_data ( data : & [ u8 ] ) -> Result < & PriceAccountData , PythError > {
443+ let price_account_data = load :: < PriceAccountData > ( & data) . map_err ( |_| PythError :: InvalidAccountData ) ?;
383444
384- if pyth_price . magic != MAGIC {
445+ if price_account_data . magic != MAGIC {
385446 return Err ( PythError :: InvalidAccountData ) ;
386447 }
387- if pyth_price . ver != VERSION_2 {
448+ if price_account_data . ver != VERSION_2 {
388449 return Err ( PythError :: BadVersionNumber ) ;
389450 }
390- if pyth_price . atype != AccountType :: Price as u32 {
451+ if price_account_data . atype != AccountType :: Price as u32 {
391452 return Err ( PythError :: WrongAccountType ) ;
392453 }
393454
455+ return Ok ( price_account_data) ;
456+ }
457+
458+ /** Get a modified `Price` struct from the raw byte value of a Solana Price account.
459+ * If used on-chain it will update the status to unknown if price is not updated for a long time.
460+ */
461+ pub fn load_price ( data : & [ u8 ] ) -> Result < Price , PythError > {
462+ let price_account_data = load_price_account_data ( data) ?;
463+
464+ #[ allow( unused_mut) ]
465+ let mut pyth_price = Price :: from_price_account_data ( price_account_data) ;
466+
467+ #[ cfg( target_arch = "bpf" ) ]
468+ if let PriceStatus :: Trading = pyth_price. agg . status {
469+ if Clock :: get ( ) . unwrap ( ) . slot - pyth_price. agg . pub_slot > MAX_SEND_LATENCY {
470+ pyth_price. agg . status = PriceStatus :: Unknown ;
471+ }
472+ }
473+
394474 return Ok ( pyth_price) ;
395475}
396476
0 commit comments