99 price_update:: PriceUpdateV2 ,
1010 PYTH_PUSH_ORACLE_ID ,
1111 } ,
12- pythnet_sdk:: messages:: FeedId ,
12+ pythnet_sdk:: {
13+ messages:: {
14+ FeedId ,
15+ Message ,
16+ } ,
17+ wire:: from_slice,
18+ } ,
1319} ;
1420
1521pub mod sdk;
@@ -22,6 +28,10 @@ pub enum PushOracleError {
2228 UpdatesNotMonotonic ,
2329 #[ msg( "Trying to update price feed with the wrong feed id" ) ]
2430 PriceFeedMessageMismatch ,
31+ #[ msg( "The message in the update must be a PriceFeedMessage" ) ]
32+ UnsupportedMessageType ,
33+ #[ msg( "Could not deserialize the message in the update" ) ]
34+ DeserializeMessageFailed ,
2535}
2636#[ program]
2737pub mod pyth_push_oracle {
@@ -53,7 +63,7 @@ pub mod pyth_push_oracle {
5363 let signer_seeds = & [ & seeds[ ..] ] ;
5464 let cpi_context = CpiContext :: new_with_signer ( cpi_program, cpi_accounts, signer_seeds) ;
5565
56-
66+ // Get the timestamp of the price currently stored in the price feed account.
5767 let current_timestamp = {
5868 if ctx. accounts . price_feed_account . data_is_empty ( ) {
5969 0
@@ -64,20 +74,37 @@ pub mod pyth_push_oracle {
6474 price_feed_account. price_message . publish_time
6575 }
6676 } ;
67- pyth_solana_receiver:: cpi:: post_update ( cpi_context, params) ?;
68- {
69- let price_feed_account_data = ctx. accounts . price_feed_account . try_borrow_data ( ) ?;
70- let price_feed_account =
71- PriceUpdateV2 :: try_deserialize ( & mut & price_feed_account_data[ ..] ) ?;
7277
73- require ! (
74- price_feed_account. price_message. publish_time > current_timestamp,
75- PushOracleError :: UpdatesNotMonotonic
76- ) ;
77- require ! (
78- price_feed_account. price_message. feed_id == feed_id,
79- PushOracleError :: PriceFeedMessageMismatch
80- ) ;
78+ // Get the timestamp of the price in the arguments (that we are trying to put in the account).
79+ // It is a little annoying that we have to redundantly deserialize the message here, but
80+ // it is required to make txs pushing stale prices succeed w/o updating the on-chain price.
81+ //
82+ // Note that we don't do any validity checks on the proof etc. here. If the caller passes an
83+ // invalid message with a newer timestamp, the validity checks will be performed by pyth_solana_receiver.
84+ let message =
85+ from_slice :: < byteorder:: BE , Message > ( params. merkle_price_update . message . as_ref ( ) )
86+ . map_err ( |_| PushOracleError :: DeserializeMessageFailed ) ?;
87+ let next_timestamp = match message {
88+ Message :: PriceFeedMessage ( price_feed_message) => price_feed_message. publish_time ,
89+ Message :: TwapMessage ( _) => {
90+ return err ! ( PushOracleError :: UnsupportedMessageType ) ;
91+ }
92+ } ;
93+
94+ // Only update the price feed if the message contains a newer price. Pushing a stale price
95+ // suceeds without changing the on-chain state.
96+ if next_timestamp > current_timestamp {
97+ pyth_solana_receiver:: cpi:: post_update ( cpi_context, params) ?;
98+ {
99+ let price_feed_account_data = ctx. accounts . price_feed_account . try_borrow_data ( ) ?;
100+ let price_feed_account =
101+ PriceUpdateV2 :: try_deserialize ( & mut & price_feed_account_data[ ..] ) ?;
102+
103+ require ! (
104+ price_feed_account. price_message. feed_id == feed_id,
105+ PushOracleError :: PriceFeedMessageMismatch
106+ ) ;
107+ }
81108 }
82109 Ok ( ( ) )
83110 }
0 commit comments