@@ -12,22 +12,15 @@ use bytemuck::{
1212 PodCastError ,
1313 Zeroable ,
1414} ;
15- use pyth_sdk:: {
16- PriceIdentifier ,
17- ProductIdentifier ,
18- } ;
15+ use pyth_sdk:: PriceIdentifier ;
1916use solana_program:: pubkey:: Pubkey ;
2017use std:: mem:: size_of;
2118
2219pub use pyth_sdk:: {
2320 Price ,
2421 PriceFeed ,
25- PriceStatus ,
2622} ;
2723
28- use solana_program:: clock:: Clock ;
29- use solana_program:: sysvar:: Sysvar ;
30-
3124use crate :: VALID_SLOT_PERIOD ;
3225
3326use crate :: PythError ;
@@ -115,6 +108,37 @@ impl Default for PriceType {
115108 }
116109}
117110
111+
112+ /// Represents availability status of a price feed.
113+ #[ derive(
114+ Copy ,
115+ Clone ,
116+ Debug ,
117+ PartialEq ,
118+ Eq ,
119+ BorshSerialize ,
120+ BorshDeserialize ,
121+ serde:: Serialize ,
122+ serde:: Deserialize ,
123+ ) ]
124+ #[ repr( C ) ]
125+ pub enum PriceStatus {
126+ /// The price feed is not currently updating for an unknown reason.
127+ Unknown ,
128+ /// The price feed is updating as expected.
129+ Trading ,
130+ /// The price feed is not currently updating because trading in the product has been halted.
131+ Halted ,
132+ /// The price feed is not currently updating because an auction is setting the price.
133+ Auction ,
134+ }
135+
136+ impl Default for PriceStatus {
137+ fn default ( ) -> Self {
138+ PriceStatus :: Unknown
139+ }
140+ }
141+
118142/// Mapping accounts form a linked-list containing the listing of all products on Pyth.
119143#[ derive( Copy , Clone , Debug , PartialEq , Eq ) ]
120144#[ repr( C ) ]
@@ -323,38 +347,38 @@ unsafe impl Pod for PriceAccount {
323347
324348impl PriceAccount {
325349 pub fn to_price_feed ( & self , price_key : & Pubkey ) -> PriceFeed {
326- let mut status = self . agg . status ;
327- let mut prev_price = self . prev_price ;
328- let mut prev_conf = self . prev_conf ;
329- let mut prev_publish_time = self . prev_timestamp ;
330-
331- if let Ok ( clock) = Clock :: get ( ) {
332- if matches ! ( status, PriceStatus :: Trading )
333- && clock. slot . saturating_sub ( self . agg . pub_slot ) > VALID_SLOT_PERIOD
334- {
335- status = PriceStatus :: Unknown ;
336- prev_price = self . agg . price ;
337- prev_conf = self . agg . conf ;
338- prev_publish_time = self . timestamp ;
350+ let status = self . agg . status ;
351+
352+ let price = if matches ! ( status, PriceStatus :: Trading ) {
353+ Price {
354+ conf : self . agg . conf ,
355+ expo : self . expo ,
356+ price : self . agg . price ,
357+ publish_time : self . timestamp ,
358+ }
359+ } else {
360+ Price {
361+ conf : self . prev_conf ,
362+ expo : self . expo ,
363+ price : self . prev_price ,
364+ publish_time : self . prev_timestamp ,
339365 }
366+ } ;
367+
368+ let mut ema_price = Price {
369+ conf : self . ema_conf . val as u64 ,
370+ expo : self . expo ,
371+ price : self . ema_price . val ,
372+ publish_time : self . timestamp ,
373+ } ;
374+
375+ // Ema price only gets updated if status is Trading. So it's update timestamp will be
376+ // prev_timestamp when the status is not Trading.
377+ if !matches ! ( status, PriceStatus :: Trading ) {
378+ ema_price. publish_time = self . prev_timestamp ;
340379 }
341380
342- PriceFeed :: new (
343- PriceIdentifier :: new ( price_key. to_bytes ( ) ) ,
344- status,
345- self . timestamp ,
346- self . expo ,
347- self . num ,
348- self . num_qt ,
349- ProductIdentifier :: new ( self . prod . to_bytes ( ) ) ,
350- self . agg . price ,
351- self . agg . conf ,
352- self . ema_price . val ,
353- self . ema_conf . val as u64 ,
354- prev_price,
355- prev_conf,
356- prev_publish_time,
357- )
381+ PriceFeed :: new ( PriceIdentifier :: new ( price_key. to_bytes ( ) ) , price, ema_price)
358382 }
359383}
360384
@@ -447,3 +471,117 @@ fn get_attr_str(buf: &[u8]) -> (&str, &[u8]) {
447471 let remaining_buf = & buf[ len + 1 ..] ;
448472 ( str, remaining_buf)
449473}
474+
475+ #[ cfg( test) ]
476+ mod test {
477+ use pyth_sdk:: {
478+ Identifier ,
479+ Price ,
480+ PriceFeed ,
481+ } ;
482+ use solana_program:: pubkey:: Pubkey ;
483+
484+ use super :: {
485+ PriceAccount ,
486+ PriceInfo ,
487+ PriceStatus ,
488+ Rational ,
489+ } ;
490+
491+
492+ #[ test]
493+ fn test_trading_price_to_price_feed ( ) {
494+ let price_account = PriceAccount {
495+ expo : 5 ,
496+ agg : PriceInfo {
497+ price : 10 ,
498+ conf : 20 ,
499+ status : PriceStatus :: Trading ,
500+ ..Default :: default ( )
501+ } ,
502+ timestamp : 200 ,
503+ prev_timestamp : 100 ,
504+ ema_price : Rational {
505+ val : 40 ,
506+ ..Default :: default ( )
507+ } ,
508+ ema_conf : Rational {
509+ val : 50 ,
510+ ..Default :: default ( )
511+ } ,
512+ prev_price : 60 ,
513+ prev_conf : 70 ,
514+ ..Default :: default ( )
515+ } ;
516+
517+ let pubkey = Pubkey :: new_from_array ( [ 3 ; 32 ] ) ;
518+ let price_feed = price_account. to_price_feed ( & pubkey) ;
519+
520+ assert_eq ! (
521+ price_feed,
522+ PriceFeed :: new(
523+ Identifier :: new( pubkey. to_bytes( ) ) ,
524+ Price {
525+ conf: 20 ,
526+ price: 10 ,
527+ expo: 5 ,
528+ publish_time: 200 ,
529+ } ,
530+ Price {
531+ conf: 50 ,
532+ price: 40 ,
533+ expo: 5 ,
534+ publish_time: 200 ,
535+ }
536+ )
537+ ) ;
538+ }
539+
540+ #[ test]
541+ fn test_non_trading_price_to_price_feed ( ) {
542+ let price_account = PriceAccount {
543+ expo : 5 ,
544+ agg : PriceInfo {
545+ price : 10 ,
546+ conf : 20 ,
547+ status : PriceStatus :: Unknown ,
548+ ..Default :: default ( )
549+ } ,
550+ timestamp : 200 ,
551+ prev_timestamp : 100 ,
552+ ema_price : Rational {
553+ val : 40 ,
554+ ..Default :: default ( )
555+ } ,
556+ ema_conf : Rational {
557+ val : 50 ,
558+ ..Default :: default ( )
559+ } ,
560+ prev_price : 60 ,
561+ prev_conf : 70 ,
562+ ..Default :: default ( )
563+ } ;
564+
565+ let pubkey = Pubkey :: new_from_array ( [ 3 ; 32 ] ) ;
566+ let price_feed = price_account. to_price_feed ( & pubkey) ;
567+
568+ assert_eq ! (
569+ price_feed,
570+ PriceFeed :: new(
571+ Identifier :: new( pubkey. to_bytes( ) ) ,
572+ Price {
573+ conf: 70 ,
574+ price: 60 ,
575+ expo: 5 ,
576+ publish_time: 100 ,
577+ } ,
578+ Price {
579+ conf: 50 ,
580+ price: 40 ,
581+ expo: 5 ,
582+ publish_time: 100 ,
583+ }
584+ )
585+ ) ;
586+ }
587+ }
0 commit comments