9
9
price_update:: PriceUpdateV2 ,
10
10
PYTH_PUSH_ORACLE_ID ,
11
11
} ,
12
- pythnet_sdk:: messages:: FeedId ,
12
+ pythnet_sdk:: {
13
+ messages:: {
14
+ FeedId ,
15
+ Message ,
16
+ } ,
17
+ wire:: from_slice,
18
+ } ,
13
19
} ;
14
20
15
21
pub mod sdk;
@@ -22,6 +28,10 @@ pub enum PushOracleError {
22
28
UpdatesNotMonotonic ,
23
29
#[ msg( "Trying to update price feed with the wrong feed id" ) ]
24
30
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 ,
25
35
}
26
36
#[ program]
27
37
pub mod pyth_push_oracle {
@@ -53,7 +63,7 @@ pub mod pyth_push_oracle {
53
63
let signer_seeds = & [ & seeds[ ..] ] ;
54
64
let cpi_context = CpiContext :: new_with_signer ( cpi_program, cpi_accounts, signer_seeds) ;
55
65
56
-
66
+ // Get the timestamp of the price currently stored in the price feed account.
57
67
let current_timestamp = {
58
68
if ctx. accounts . price_feed_account . data_is_empty ( ) {
59
69
0
@@ -64,20 +74,37 @@ pub mod pyth_push_oracle {
64
74
price_feed_account. price_message . publish_time
65
75
}
66
76
} ;
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[ ..] ) ?;
72
77
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
+ }
81
108
}
82
109
Ok ( ( ) )
83
110
}
0 commit comments