@@ -283,10 +283,13 @@ pub struct Rational {
283283 pub denom : i64 ,
284284}
285285
286- /// Price accounts represent a continuously-updating price feed for a product.
287- #[ derive( Copy , Clone , Debug , Default , PartialEq , Eq ) ]
288286#[ repr( C ) ]
289- pub struct PriceAccount {
287+ #[ derive( Copy , Clone , Debug , PartialEq , Eq ) ]
288+ pub struct GenericPriceAccount < const N : usize , T >
289+ where
290+ T : Default ,
291+ T : Copy ,
292+ {
290293 /// pyth magic number
291294 pub magic : u32 ,
292295 /// program version
@@ -336,18 +339,102 @@ pub struct PriceAccount {
336339 /// aggregate price info
337340 pub agg : PriceInfo ,
338341 /// price components one per quoter
339- pub comp : [ PriceComp ; 32 ] ,
342+ pub comp : [ PriceComp ; N ] ,
343+ /// additional extended account data
344+ pub extended : T ,
345+ }
346+
347+ impl < const N : usize , T > Default for GenericPriceAccount < N , T >
348+ where
349+ T : Default ,
350+ T : Copy ,
351+ {
352+ fn default ( ) -> Self {
353+ Self {
354+ magic : Default :: default ( ) ,
355+ ver : Default :: default ( ) ,
356+ atype : Default :: default ( ) ,
357+ size : Default :: default ( ) ,
358+ ptype : Default :: default ( ) ,
359+ expo : Default :: default ( ) ,
360+ num : Default :: default ( ) ,
361+ num_qt : Default :: default ( ) ,
362+ last_slot : Default :: default ( ) ,
363+ valid_slot : Default :: default ( ) ,
364+ ema_price : Default :: default ( ) ,
365+ ema_conf : Default :: default ( ) ,
366+ timestamp : Default :: default ( ) ,
367+ min_pub : Default :: default ( ) ,
368+ drv2 : Default :: default ( ) ,
369+ drv3 : Default :: default ( ) ,
370+ drv4 : Default :: default ( ) ,
371+ prod : Default :: default ( ) ,
372+ next : Default :: default ( ) ,
373+ prev_slot : Default :: default ( ) ,
374+ prev_price : Default :: default ( ) ,
375+ prev_conf : Default :: default ( ) ,
376+ prev_timestamp : Default :: default ( ) ,
377+ agg : Default :: default ( ) ,
378+ comp : [ Default :: default ( ) ; N ] ,
379+ extended : Default :: default ( ) ,
380+ }
381+ }
340382}
341383
384+ impl < const N : usize , T > std:: ops:: Deref for GenericPriceAccount < N , T >
385+ where
386+ T : Default ,
387+ T : Copy ,
388+ {
389+ type Target = T ;
390+ fn deref ( & self ) -> & Self :: Target {
391+ & self . extended
392+ }
393+ }
394+
395+ #[ repr( C ) ]
396+ #[ derive( Copy , Clone , Debug , Default , Pod , Zeroable , PartialEq , Eq ) ]
397+ pub struct PriceCumulative {
398+ /// Cumulative sum of price * slot_gap
399+ pub price : i128 ,
400+ /// Cumulative sum of conf * slot_gap
401+ pub conf : u128 ,
402+ /// Cumulative number of slots where the price wasn't recently updated (within
403+ /// PC_MAX_SEND_LATENCY slots). This field should be used to calculate the downtime
404+ /// as a percent of slots between two times `T` and `t` as follows:
405+ /// `(T.num_down_slots - t.num_down_slots) / (T.agg_.pub_slot_ - t.agg_.pub_slot_)`
406+ pub num_down_slots : u64 ,
407+ /// Padding for alignment
408+ pub unused : u64 ,
409+ }
410+
411+ #[ derive( Copy , Clone , Debug , Default , PartialEq , Eq ) ]
412+ pub struct PriceAccountExt {
413+ pub price_cumulative : PriceCumulative ,
414+ }
415+
416+ /// Backwards compatibility.
417+ pub type PriceAccount = GenericPriceAccount < 32 , ( ) > ;
418+
419+ /// Solana-specific Pyth account where the old 32-element publishers are present.
420+ pub type SolanaPriceAccount = GenericPriceAccount < 32 , ( ) > ;
421+
422+ /// Pythnet-specific Price accountw ith upgraded 64-element publishers and extended fields.
423+ pub type PythnetPriceAccount = GenericPriceAccount < 64 , PriceAccountExt > ;
424+
342425#[ cfg( target_endian = "little" ) ]
343- unsafe impl Zeroable for PriceAccount {
426+ unsafe impl < const N : usize , T : Default + Copy > Zeroable for GenericPriceAccount < N , T > {
344427}
345428
346429#[ cfg( target_endian = "little" ) ]
347- unsafe impl Pod for PriceAccount {
430+ unsafe impl < const N : usize , T : Default + Copy + ' static > Pod for GenericPriceAccount < N , T > {
348431}
349432
350- impl PriceAccount {
433+ impl < const N : usize , T > GenericPriceAccount < N , T >
434+ where
435+ T : Default ,
436+ T : Copy ,
437+ {
351438 pub fn get_publish_time ( & self ) -> UnixTimestamp {
352439 match self . agg . status {
353440 PriceStatus :: Trading => self . timestamp ,
@@ -456,8 +543,11 @@ pub fn load_product_account(data: &[u8]) -> Result<&ProductAccount, PythError> {
456543}
457544
458545/// Get a `Price` account from the raw byte value of a Solana account.
459- pub fn load_price_account ( data : & [ u8 ] ) -> Result < & PriceAccount , PythError > {
460- let pyth_price = load :: < PriceAccount > ( data) . map_err ( |_| PythError :: InvalidAccountData ) ?;
546+ pub fn load_price_account < const N : usize , T : Default + Copy + ' static > (
547+ data : & [ u8 ] ,
548+ ) -> Result < & GenericPriceAccount < N , T > , PythError > {
549+ let pyth_price =
550+ load :: < GenericPriceAccount < N , T > > ( data) . map_err ( |_| PythError :: InvalidAccountData ) ?;
461551
462552 if pyth_price. magic != MAGIC {
463553 return Err ( PythError :: InvalidAccountData ) ;
@@ -737,4 +827,59 @@ mod test {
737827
738828 assert_eq ! ( price_account. get_price_no_older_than( & clock, 1 ) , None ) ;
739829 }
830+
831+ #[ test]
832+ fn test_price_feed_representations_equal ( ) {
833+ #[ repr( C ) ]
834+ #[ derive( Copy , Clone , Debug , Default , PartialEq , Eq ) ]
835+ pub struct OldPriceAccount {
836+ pub magic : u32 ,
837+ pub ver : u32 ,
838+ pub atype : u32 ,
839+ pub size : u32 ,
840+ pub ptype : crate :: state:: PriceType ,
841+ pub expo : i32 ,
842+ pub num : u32 ,
843+ pub num_qt : u32 ,
844+ pub last_slot : u64 ,
845+ pub valid_slot : u64 ,
846+ pub ema_price : Rational ,
847+ pub ema_conf : Rational ,
848+ pub timestamp : i64 ,
849+ pub min_pub : u8 ,
850+ pub drv2 : u8 ,
851+ pub drv3 : u16 ,
852+ pub drv4 : u32 ,
853+ pub prod : Pubkey ,
854+ pub next : Pubkey ,
855+ pub prev_slot : u64 ,
856+ pub prev_price : i64 ,
857+ pub prev_conf : u64 ,
858+ pub prev_timestamp : i64 ,
859+ pub agg : PriceInfo ,
860+ pub comp : [ crate :: state:: PriceComp ; 32 ] ,
861+ }
862+
863+ let old = OldPriceAccount :: default ( ) ;
864+ let new = PriceAccount :: default ( ) ;
865+
866+ // Equal Sized?
867+ assert_eq ! (
868+ std:: mem:: size_of:: <OldPriceAccount >( ) ,
869+ std:: mem:: size_of:: <PriceAccount >( ) ,
870+ ) ;
871+
872+ // Equal Byte Representation?
873+ unsafe {
874+ let old_b = std:: slice:: from_raw_parts (
875+ & old as * const OldPriceAccount as * const u8 ,
876+ std:: mem:: size_of :: < OldPriceAccount > ( ) ,
877+ ) ;
878+ let new_b = std:: slice:: from_raw_parts (
879+ & new as * const PriceAccount as * const u8 ,
880+ std:: mem:: size_of :: < PriceAccount > ( ) ,
881+ ) ;
882+ assert_eq ! ( old_b, new_b) ;
883+ }
884+ }
740885}
0 commit comments