|
|
@@ -9,7 +9,13 @@ use {
|
|
|
price_update::PriceUpdateV2,
|
|
|
PYTH_PUSH_ORACLE_ID,
|
|
|
},
|
|
|
- pythnet_sdk::messages::FeedId,
|
|
|
+ pythnet_sdk::{
|
|
|
+ messages::{
|
|
|
+ FeedId,
|
|
|
+ Message,
|
|
|
+ },
|
|
|
+ wire::from_slice,
|
|
|
+ },
|
|
|
};
|
|
|
|
|
|
pub mod sdk;
|
|
|
@@ -22,6 +28,10 @@ pub enum PushOracleError {
|
|
|
UpdatesNotMonotonic,
|
|
|
#[msg("Trying to update price feed with the wrong feed id")]
|
|
|
PriceFeedMessageMismatch,
|
|
|
+ #[msg("The message in the update must be a PriceFeedMessage")]
|
|
|
+ UnsupportedMessageType,
|
|
|
+ #[msg("Could not deserialize the message in the update")]
|
|
|
+ DeserializeMessageFailed,
|
|
|
}
|
|
|
#[program]
|
|
|
pub mod pyth_push_oracle {
|
|
|
@@ -53,7 +63,7 @@ pub mod pyth_push_oracle {
|
|
|
let signer_seeds = &[&seeds[..]];
|
|
|
let cpi_context = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer_seeds);
|
|
|
|
|
|
-
|
|
|
+ // Get the timestamp of the price currently stored in the price feed account.
|
|
|
let current_timestamp = {
|
|
|
if ctx.accounts.price_feed_account.data_is_empty() {
|
|
|
0
|
|
|
@@ -64,20 +74,37 @@ pub mod pyth_push_oracle {
|
|
|
price_feed_account.price_message.publish_time
|
|
|
}
|
|
|
};
|
|
|
- pyth_solana_receiver::cpi::post_update(cpi_context, params)?;
|
|
|
- {
|
|
|
- let price_feed_account_data = ctx.accounts.price_feed_account.try_borrow_data()?;
|
|
|
- let price_feed_account =
|
|
|
- PriceUpdateV2::try_deserialize(&mut &price_feed_account_data[..])?;
|
|
|
|
|
|
- require!(
|
|
|
- price_feed_account.price_message.publish_time > current_timestamp,
|
|
|
- PushOracleError::UpdatesNotMonotonic
|
|
|
- );
|
|
|
- require!(
|
|
|
- price_feed_account.price_message.feed_id == feed_id,
|
|
|
- PushOracleError::PriceFeedMessageMismatch
|
|
|
- );
|
|
|
+ // Get the timestamp of the price in the arguments (that we are trying to put in the account).
|
|
|
+ // It is a little annoying that we have to redundantly deserialize the message here, but
|
|
|
+ // it is required to make txs pushing stale prices succeed w/o updating the on-chain price.
|
|
|
+ //
|
|
|
+ // Note that we don't do any validity checks on the proof etc. here. If the caller passes an
|
|
|
+ // invalid message with a newer timestamp, the validity checks will be performed by pyth_solana_receiver.
|
|
|
+ let message =
|
|
|
+ from_slice::<byteorder::BE, Message>(params.merkle_price_update.message.as_ref())
|
|
|
+ .map_err(|_| PushOracleError::DeserializeMessageFailed)?;
|
|
|
+ let next_timestamp = match message {
|
|
|
+ Message::PriceFeedMessage(price_feed_message) => price_feed_message.publish_time,
|
|
|
+ Message::TwapMessage(_) => {
|
|
|
+ return err!(PushOracleError::UnsupportedMessageType);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // Only update the price feed if the message contains a newer price. Pushing a stale price
|
|
|
+ // suceeds without changing the on-chain state.
|
|
|
+ if next_timestamp > current_timestamp {
|
|
|
+ pyth_solana_receiver::cpi::post_update(cpi_context, params)?;
|
|
|
+ {
|
|
|
+ let price_feed_account_data = ctx.accounts.price_feed_account.try_borrow_data()?;
|
|
|
+ let price_feed_account =
|
|
|
+ PriceUpdateV2::try_deserialize(&mut &price_feed_account_data[..])?;
|
|
|
+
|
|
|
+ require!(
|
|
|
+ price_feed_account.price_message.feed_id == feed_id,
|
|
|
+ PushOracleError::PriceFeedMessageMismatch
|
|
|
+ );
|
|
|
+ }
|
|
|
}
|
|
|
Ok(())
|
|
|
}
|