@@ -283,10 +283,13 @@ pub struct Rational {
283
283
pub denom : i64 ,
284
284
}
285
285
286
- /// Price accounts represent a continuously-updating price feed for a product.
287
- #[ derive( Copy , Clone , Debug , Default , PartialEq , Eq ) ]
288
286
#[ 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
+ {
290
293
/// pyth magic number
291
294
pub magic : u32 ,
292
295
/// program version
@@ -336,18 +339,102 @@ pub struct PriceAccount {
336
339
/// aggregate price info
337
340
pub agg : PriceInfo ,
338
341
/// 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
+ }
340
382
}
341
383
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
+
342
425
#[ cfg( target_endian = "little" ) ]
343
- unsafe impl Zeroable for PriceAccount {
426
+ unsafe impl < const N : usize , T : Default + Copy > Zeroable for GenericPriceAccount < N , T > {
344
427
}
345
428
346
429
#[ 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 > {
348
431
}
349
432
350
- impl PriceAccount {
433
+ impl < const N : usize , T > GenericPriceAccount < N , T >
434
+ where
435
+ T : Default ,
436
+ T : Copy ,
437
+ {
351
438
pub fn get_publish_time ( & self ) -> UnixTimestamp {
352
439
match self . agg . status {
353
440
PriceStatus :: Trading => self . timestamp ,
@@ -456,8 +543,11 @@ pub fn load_product_account(data: &[u8]) -> Result<&ProductAccount, PythError> {
456
543
}
457
544
458
545
/// 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 ) ?;
461
551
462
552
if pyth_price. magic != MAGIC {
463
553
return Err ( PythError :: InvalidAccountData ) ;
@@ -737,4 +827,59 @@ mod test {
737
827
738
828
assert_eq ! ( price_account. get_price_no_older_than( & clock, 1 ) , None ) ;
739
829
}
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
+ }
740
885
}
0 commit comments