|
|
@@ -6,9 +6,11 @@
|
|
|
extern crate alloc;
|
|
|
|
|
|
mod error;
|
|
|
+mod governance;
|
|
|
mod governance_structs;
|
|
|
#[cfg(test)]
|
|
|
mod integration_tests;
|
|
|
+mod pyth_operations;
|
|
|
#[cfg(test)]
|
|
|
mod pyth_governance_test;
|
|
|
mod structs;
|
|
|
@@ -20,9 +22,8 @@ use mock_instant::global::MockClock;
|
|
|
|
|
|
use alloc::vec::Vec;
|
|
|
use stylus_sdk::{
|
|
|
- alloy_primitives::{Address, FixedBytes, I32, I64, U16, U256, U32, U64},
|
|
|
+ alloy_primitives::{Address, FixedBytes, U16, U256, U32, U64},
|
|
|
alloy_sol_types::sol,
|
|
|
- call::Call,
|
|
|
prelude::*,
|
|
|
storage::{
|
|
|
StorageAddress, StorageBool, StorageFixedBytes, StorageMap, StorageU16, StorageU256,
|
|
|
@@ -30,22 +31,7 @@ use stylus_sdk::{
|
|
|
},
|
|
|
};
|
|
|
|
|
|
-use error::PythReceiverError;
|
|
|
-use governance_structs::*;
|
|
|
-use pythnet_sdk::{
|
|
|
- accumulators::merkle::{MerklePath, MerkleRoot},
|
|
|
- hashers::keccak256_160::Keccak160,
|
|
|
- messages::Message,
|
|
|
- wire::{
|
|
|
- from_slice,
|
|
|
- v1::{
|
|
|
- AccumulatorUpdateData, Proof, WormholeMessage, WormholePayload,
|
|
|
- PYTHNET_ACCUMULATOR_UPDATE_MAGIC,
|
|
|
- },
|
|
|
- },
|
|
|
-};
|
|
|
-use structs::{DataSource, DataSourceStorage, PriceFeedReturn, PriceFeedStorage, PriceReturn};
|
|
|
-use wormhole_vaas::{Readable, Vaa, Writeable};
|
|
|
+use structs::{DataSource, DataSourceStorage, PriceFeedStorage};
|
|
|
|
|
|
sol! {
|
|
|
event FeeSet(uint256 indexed old_fee, uint256 indexed new_fee);
|
|
|
@@ -125,475 +111,6 @@ impl PythReceiver {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- pub fn price_feed_exists(&self, id: [u8; 32]) -> bool {
|
|
|
- let id_fb = FixedBytes::<32>::from(id);
|
|
|
- let price_info = self.latest_price_info.get(id_fb);
|
|
|
- return price_info.publish_time.get() != U64::ZERO;
|
|
|
- }
|
|
|
-
|
|
|
- pub fn query_price_feed(&self, id: [u8; 32]) -> Result<PriceFeedReturn, PythReceiverError> {
|
|
|
- let id_fb = FixedBytes::<32>::from(id);
|
|
|
-
|
|
|
- let price_info = self.latest_price_info.get(id_fb);
|
|
|
-
|
|
|
- if price_info.publish_time.get() == U64::ZERO {
|
|
|
- return Err(PythReceiverError::PriceFeedNotFound);
|
|
|
- }
|
|
|
-
|
|
|
- Ok((
|
|
|
- id_fb,
|
|
|
- price_info.publish_time.get(),
|
|
|
- price_info.expo.get(),
|
|
|
- price_info.price.get(),
|
|
|
- price_info.conf.get(),
|
|
|
- price_info.ema_price.get(),
|
|
|
- price_info.ema_conf.get(),
|
|
|
- ))
|
|
|
- }
|
|
|
-
|
|
|
- pub fn get_price_unsafe(&self, id: [u8; 32]) -> Result<PriceReturn, PythReceiverError> {
|
|
|
- let id_fb = FixedBytes::<32>::from(id);
|
|
|
-
|
|
|
- let price_info = self.latest_price_info.get(id_fb);
|
|
|
-
|
|
|
- if price_info.publish_time.get() == U64::ZERO {
|
|
|
- return Err(PythReceiverError::PriceUnavailable);
|
|
|
- }
|
|
|
-
|
|
|
- Ok((
|
|
|
- price_info.price.get(),
|
|
|
- price_info.conf.get(),
|
|
|
- price_info.expo.get(),
|
|
|
- price_info.publish_time.get(),
|
|
|
- ))
|
|
|
- }
|
|
|
-
|
|
|
- pub fn get_price_no_older_than(
|
|
|
- &self,
|
|
|
- id: [u8; 32],
|
|
|
- age: u64,
|
|
|
- ) -> Result<PriceReturn, PythReceiverError> {
|
|
|
- let price_info = self.get_price_unsafe(id)?;
|
|
|
- if !self.is_no_older_than(price_info.3, age) {
|
|
|
- return Err(PythReceiverError::NewPriceUnavailable);
|
|
|
- }
|
|
|
- Ok(price_info)
|
|
|
- }
|
|
|
-
|
|
|
- pub fn get_ema_price_unsafe(&self, id: [u8; 32]) -> Result<PriceReturn, PythReceiverError> {
|
|
|
- let id_fb = FixedBytes::<32>::from(id);
|
|
|
- let price_info = self.latest_price_info.get(id_fb);
|
|
|
-
|
|
|
- if price_info.publish_time.get() == U64::ZERO {
|
|
|
- return Err(PythReceiverError::PriceUnavailable);
|
|
|
- }
|
|
|
-
|
|
|
- Ok((
|
|
|
- price_info.ema_price.get(),
|
|
|
- price_info.ema_conf.get(),
|
|
|
- price_info.expo.get(),
|
|
|
- price_info.publish_time.get(),
|
|
|
- ))
|
|
|
- }
|
|
|
-
|
|
|
- pub fn get_ema_price_no_older_than(
|
|
|
- &self,
|
|
|
- id: [u8; 32],
|
|
|
- age: u64,
|
|
|
- ) -> Result<PriceReturn, PythReceiverError> {
|
|
|
- let price_info = self.get_ema_price_unsafe(id)?;
|
|
|
- if !self.is_no_older_than(price_info.3, age) {
|
|
|
- return Err(PythReceiverError::NewPriceUnavailable);
|
|
|
- }
|
|
|
- Ok(price_info)
|
|
|
- }
|
|
|
-
|
|
|
- #[payable]
|
|
|
- pub fn update_price_feeds(
|
|
|
- &mut self,
|
|
|
- update_data: Vec<Vec<u8>>,
|
|
|
- ) -> Result<(), PythReceiverError> {
|
|
|
- for data in &update_data {
|
|
|
- self.update_price_feeds_internal(data.clone(), 0, 0, false)?;
|
|
|
- }
|
|
|
-
|
|
|
- let total_fee = self.get_update_fee(update_data)?;
|
|
|
-
|
|
|
- let value = self.vm().msg_value();
|
|
|
-
|
|
|
- if value < total_fee {
|
|
|
- return Err(PythReceiverError::InsufficientFee);
|
|
|
- }
|
|
|
- Ok(())
|
|
|
- }
|
|
|
-
|
|
|
- pub fn update_price_feeds_if_necessary(
|
|
|
- &mut self,
|
|
|
- update_data: Vec<Vec<u8>>,
|
|
|
- price_ids: Vec<[u8; 32]>,
|
|
|
- publish_times: Vec<u64>,
|
|
|
- ) -> Result<(), PythReceiverError> {
|
|
|
- if (price_ids.len() != publish_times.len())
|
|
|
- || (price_ids.is_empty() && publish_times.is_empty())
|
|
|
- {
|
|
|
- return Err(PythReceiverError::InvalidUpdateData);
|
|
|
- }
|
|
|
-
|
|
|
- for i in 0..price_ids.len() {
|
|
|
- if self.latest_price_info_publish_time(price_ids[i]) < publish_times[i] {
|
|
|
- self.update_price_feeds(update_data.clone())?;
|
|
|
- return Ok(());
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return Err(PythReceiverError::NoFreshUpdate);
|
|
|
- }
|
|
|
-
|
|
|
- fn latest_price_info_publish_time(&self, price_id: [u8; 32]) -> u64 {
|
|
|
- let price_id_fb: FixedBytes<32> = FixedBytes::from(price_id);
|
|
|
- let recent_price_info = self.latest_price_info.get(price_id_fb);
|
|
|
- recent_price_info.publish_time.get().to::<u64>()
|
|
|
- }
|
|
|
-
|
|
|
- fn update_price_feeds_internal(
|
|
|
- &mut self,
|
|
|
- update_data: Vec<u8>,
|
|
|
- min_publish_time: u64,
|
|
|
- max_publish_time: u64,
|
|
|
- unique: bool,
|
|
|
- ) -> Result<Vec<PriceFeedReturn>, PythReceiverError> {
|
|
|
- let price_pairs = self.parse_price_feed_updates_internal(
|
|
|
- update_data,
|
|
|
- min_publish_time,
|
|
|
- max_publish_time,
|
|
|
- unique,
|
|
|
- )?;
|
|
|
-
|
|
|
- for price_return in &price_pairs {
|
|
|
- let price_id_fb: FixedBytes<32> = FixedBytes::from(price_return.0);
|
|
|
- let mut recent_price_info = self.latest_price_info.setter(price_id_fb);
|
|
|
-
|
|
|
- if recent_price_info.publish_time.get() < price_return.1
|
|
|
- || recent_price_info.price.get() == I64::ZERO
|
|
|
- {
|
|
|
- recent_price_info
|
|
|
- .price_id
|
|
|
- .set(FixedBytes::from(price_return.0));
|
|
|
- recent_price_info.publish_time.set(price_return.1);
|
|
|
- recent_price_info.expo.set(price_return.2);
|
|
|
- recent_price_info.price.set(price_return.3);
|
|
|
- recent_price_info.conf.set(price_return.4);
|
|
|
- recent_price_info.ema_price.set(price_return.5);
|
|
|
- recent_price_info.ema_conf.set(price_return.6);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- Ok(price_pairs)
|
|
|
- }
|
|
|
-
|
|
|
- fn get_update_fee(&self, update_data: Vec<Vec<u8>>) -> Result<U256, PythReceiverError> {
|
|
|
- let mut total_num_updates: u64 = 0;
|
|
|
- for data in &update_data {
|
|
|
- let update_data_array: &[u8] = &data;
|
|
|
- let accumulator_update = AccumulatorUpdateData::try_from_slice(&update_data_array)
|
|
|
- .map_err(|_| PythReceiverError::InvalidAccumulatorMessage)?;
|
|
|
- match accumulator_update.proof {
|
|
|
- Proof::WormholeMerkle { vaa: _, updates } => {
|
|
|
- let num_updates = u64::try_from(updates.len())
|
|
|
- .map_err(|_| PythReceiverError::TooManyUpdates)?;
|
|
|
- total_num_updates += num_updates;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- Ok(
|
|
|
- U256::from(total_num_updates).saturating_mul(self.single_update_fee_in_wei.get())
|
|
|
- + self.transaction_fee_in_wei.get(),
|
|
|
- )
|
|
|
- }
|
|
|
-
|
|
|
- pub fn get_twap_update_fee(&self, _update_data: Vec<Vec<u8>>) -> U256 {
|
|
|
- U256::from(0u8)
|
|
|
- }
|
|
|
-
|
|
|
- pub fn parse_price_feed_updates(
|
|
|
- &mut self,
|
|
|
- update_data: Vec<u8>,
|
|
|
- price_ids: Vec<[u8; 32]>,
|
|
|
- min_publish_time: u64,
|
|
|
- max_publish_time: u64,
|
|
|
- ) -> Result<Vec<PriceFeedReturn>, PythReceiverError> {
|
|
|
- let price_feeds = self.parse_price_feed_updates_with_config(
|
|
|
- vec![update_data],
|
|
|
- price_ids,
|
|
|
- min_publish_time,
|
|
|
- max_publish_time,
|
|
|
- false,
|
|
|
- false,
|
|
|
- false,
|
|
|
- );
|
|
|
- price_feeds
|
|
|
- }
|
|
|
-
|
|
|
- pub fn parse_price_feed_updates_with_config(
|
|
|
- &mut self,
|
|
|
- update_data: Vec<Vec<u8>>,
|
|
|
- price_ids: Vec<[u8; 32]>,
|
|
|
- min_allowed_publish_time: u64,
|
|
|
- max_allowed_publish_time: u64,
|
|
|
- check_uniqueness: bool,
|
|
|
- check_update_data_is_minimal: bool,
|
|
|
- store_updates_if_fresh: bool,
|
|
|
- ) -> Result<Vec<PriceFeedReturn>, PythReceiverError> {
|
|
|
- let mut all_parsed_price_feeds = Vec::new();
|
|
|
- for data in &update_data {
|
|
|
- if store_updates_if_fresh {
|
|
|
- all_parsed_price_feeds.extend(self.update_price_feeds_internal(
|
|
|
- data.clone(),
|
|
|
- min_allowed_publish_time,
|
|
|
- max_allowed_publish_time,
|
|
|
- check_uniqueness,
|
|
|
- )?);
|
|
|
- } else {
|
|
|
- all_parsed_price_feeds.extend(self.parse_price_feed_updates_internal(
|
|
|
- data.clone(),
|
|
|
- min_allowed_publish_time,
|
|
|
- max_allowed_publish_time,
|
|
|
- check_uniqueness,
|
|
|
- )?);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if check_update_data_is_minimal && all_parsed_price_feeds.len() != price_ids.len() {
|
|
|
- return Err(PythReceiverError::InvalidUpdateData);
|
|
|
- }
|
|
|
-
|
|
|
- let mut result: Vec<PriceFeedReturn> = Vec::with_capacity(price_ids.len());
|
|
|
-
|
|
|
- for price_id in price_ids {
|
|
|
- if let Some(price_info) = all_parsed_price_feeds
|
|
|
- .iter()
|
|
|
- .find(|feed| feed.0 == price_id)
|
|
|
- {
|
|
|
- result.push(price_info.clone());
|
|
|
- } else {
|
|
|
- return Err(PythReceiverError::PriceFeedNotFoundWithinRange);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- Ok(result)
|
|
|
- }
|
|
|
-
|
|
|
- fn parse_price_feed_updates_internal(
|
|
|
- &mut self,
|
|
|
- update_data: Vec<u8>,
|
|
|
- min_allowed_publish_time: u64,
|
|
|
- max_allowed_publish_time: u64,
|
|
|
- check_uniqueness: bool,
|
|
|
- ) -> Result<Vec<PriceFeedReturn>, PythReceiverError> {
|
|
|
- let update_data_array: &[u8] = &update_data;
|
|
|
- // Check the first 4 bytes of the update_data_array for the magic header
|
|
|
- if update_data_array.len() < 4 {
|
|
|
- return Err(PythReceiverError::InvalidUpdateData);
|
|
|
- }
|
|
|
-
|
|
|
- let mut header = [0u8; 4];
|
|
|
- header.copy_from_slice(&update_data_array[0..4]);
|
|
|
-
|
|
|
- if &header != PYTHNET_ACCUMULATOR_UPDATE_MAGIC {
|
|
|
- return Err(PythReceiverError::InvalidAccumulatorMessage);
|
|
|
- }
|
|
|
-
|
|
|
- let accumulator_update = AccumulatorUpdateData::try_from_slice(&update_data_array)
|
|
|
- .map_err(|_| PythReceiverError::InvalidAccumulatorMessage)?;
|
|
|
-
|
|
|
- let mut price_feeds = Vec::new();
|
|
|
-
|
|
|
- match accumulator_update.proof {
|
|
|
- Proof::WormholeMerkle { vaa, updates } => {
|
|
|
- let wormhole: IWormholeContract = IWormholeContract::new(self.wormhole.get());
|
|
|
- let config = Call::new();
|
|
|
- wormhole
|
|
|
- .parse_and_verify_vm(config, Vec::from(vaa.clone()))
|
|
|
- .map_err(|_| PythReceiverError::InvalidWormholeMessage)?;
|
|
|
-
|
|
|
- let vaa_obj = Vaa::read(&mut Vec::from(vaa.clone()).as_slice())
|
|
|
- .map_err(|_| PythReceiverError::VaaVerificationFailed)?;
|
|
|
-
|
|
|
- let cur_emitter_address: &[u8; 32] = vaa_obj
|
|
|
- .body
|
|
|
- .emitter_address
|
|
|
- .as_slice()
|
|
|
- .try_into()
|
|
|
- .map_err(|_| PythReceiverError::InvalidEmitterAddress)?;
|
|
|
-
|
|
|
- let cur_data_source = DataSource {
|
|
|
- chain_id: U16::from(vaa_obj.body.emitter_chain),
|
|
|
- emitter_address: FixedBytes::from(cur_emitter_address),
|
|
|
- };
|
|
|
-
|
|
|
- if !self.is_valid_data_source.get(cur_data_source) {
|
|
|
- return Err(PythReceiverError::InvalidWormholeMessage);
|
|
|
- }
|
|
|
-
|
|
|
- let root_digest: MerkleRoot<Keccak160> = parse_wormhole_proof(vaa_obj)?;
|
|
|
-
|
|
|
- for update in updates {
|
|
|
- let message_vec = Vec::from(update.message);
|
|
|
- let proof: MerklePath<Keccak160> = update.proof;
|
|
|
-
|
|
|
- if !root_digest.check(proof, &message_vec) {
|
|
|
- return Err(PythReceiverError::InvalidMerkleProof);
|
|
|
- }
|
|
|
-
|
|
|
- let msg = from_slice::<byteorder::BE, Message>(&message_vec)
|
|
|
- .map_err(|_| PythReceiverError::InvalidAccumulatorMessage)?;
|
|
|
-
|
|
|
- match msg {
|
|
|
- Message::PriceFeedMessage(price_feed_message) => {
|
|
|
- let publish_time = price_feed_message.publish_time;
|
|
|
-
|
|
|
- if (min_allowed_publish_time > 0
|
|
|
- && publish_time < min_allowed_publish_time as i64)
|
|
|
- || (max_allowed_publish_time > 0
|
|
|
- && publish_time > max_allowed_publish_time as i64)
|
|
|
- {
|
|
|
- return Err(PythReceiverError::PriceFeedNotFoundWithinRange);
|
|
|
- }
|
|
|
-
|
|
|
- let price_id_fb = FixedBytes::<32>::from(price_feed_message.feed_id);
|
|
|
-
|
|
|
- if check_uniqueness {
|
|
|
- let prev_price_info = self.latest_price_info.get(price_id_fb);
|
|
|
- let prev_publish_time =
|
|
|
- prev_price_info.publish_time.get().to::<u64>();
|
|
|
-
|
|
|
- if prev_publish_time > 0
|
|
|
- && min_allowed_publish_time <= prev_publish_time
|
|
|
- {
|
|
|
- return Err(PythReceiverError::PriceFeedNotFoundWithinRange);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- let expo = I32::try_from(price_feed_message.exponent)
|
|
|
- .map_err(|_| PythReceiverError::InvalidUpdateData)?;
|
|
|
- let price = I64::try_from(price_feed_message.price)
|
|
|
- .map_err(|_| PythReceiverError::InvalidUpdateData)?;
|
|
|
- let ema_price = I64::try_from(price_feed_message.ema_price)
|
|
|
- .map_err(|_| PythReceiverError::InvalidUpdateData)?;
|
|
|
-
|
|
|
- let price_info_return = (
|
|
|
- price_id_fb,
|
|
|
- U64::from(publish_time),
|
|
|
- expo,
|
|
|
- price,
|
|
|
- U64::from(price_feed_message.conf),
|
|
|
- ema_price,
|
|
|
- U64::from(price_feed_message.ema_conf),
|
|
|
- );
|
|
|
-
|
|
|
- price_feeds.push(price_info_return);
|
|
|
- }
|
|
|
- _ => {
|
|
|
- return Err(PythReceiverError::InvalidAccumulatorMessageType);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- Ok(price_feeds)
|
|
|
- }
|
|
|
-
|
|
|
- pub fn parse_twap_price_feed_updates(
|
|
|
- &mut self,
|
|
|
- _update_data: Vec<Vec<u8>>,
|
|
|
- _price_ids: Vec<[u8; 32]>,
|
|
|
- ) -> Vec<PriceFeedReturn> {
|
|
|
- Vec::new()
|
|
|
- }
|
|
|
-
|
|
|
- pub fn parse_price_feed_updates_unique(
|
|
|
- &mut self,
|
|
|
- update_data: Vec<Vec<u8>>,
|
|
|
- price_ids: Vec<[u8; 32]>,
|
|
|
- min_publish_time: u64,
|
|
|
- max_publish_time: u64,
|
|
|
- ) -> Result<Vec<PriceFeedReturn>, PythReceiverError> {
|
|
|
- let price_feeds = self.parse_price_feed_updates_with_config(
|
|
|
- update_data,
|
|
|
- price_ids,
|
|
|
- min_publish_time,
|
|
|
- max_publish_time,
|
|
|
- true,
|
|
|
- false,
|
|
|
- false,
|
|
|
- );
|
|
|
- price_feeds
|
|
|
- }
|
|
|
-
|
|
|
- pub fn execute_governance_instruction(
|
|
|
- &mut self,
|
|
|
- data: Vec<u8>,
|
|
|
- ) -> Result<(), PythReceiverError> {
|
|
|
- let wormhole: IWormholeContract = IWormholeContract::new(self.wormhole.get());
|
|
|
- let config = Call::new();
|
|
|
-
|
|
|
- wormhole
|
|
|
- .parse_and_verify_vm(config, Vec::from(data.clone()))
|
|
|
- .map_err(|_| PythReceiverError::InvalidWormholeMessage)?;
|
|
|
-
|
|
|
- let vm = Vaa::read(&mut Vec::from(data.clone()).as_slice())
|
|
|
- .map_err(|_| PythReceiverError::InvalidVaa)?;
|
|
|
-
|
|
|
- verify_governance_vm(self, vm.clone())?;
|
|
|
-
|
|
|
- let instruction = governance_structs::parse_instruction(vm.body.payload.to_vec())
|
|
|
- .map_err(|_| PythReceiverError::InvalidGovernanceMessage)?;
|
|
|
-
|
|
|
- let chain_id_config = Call::new();
|
|
|
-
|
|
|
- let wormhole_id = wormhole
|
|
|
- .chain_id(chain_id_config)
|
|
|
- .map_err(|_| PythReceiverError::WormholeUninitialized)?;
|
|
|
-
|
|
|
- if instruction.target_chain_id != 0 && instruction.target_chain_id != wormhole_id {
|
|
|
- return Err(PythReceiverError::InvalidGovernanceTarget);
|
|
|
- }
|
|
|
-
|
|
|
- match instruction.payload {
|
|
|
- GovernancePayload::SetFee(payload) => {
|
|
|
- self.set_fee(payload.value, payload.expo);
|
|
|
- }
|
|
|
- GovernancePayload::SetDataSources(payload) => {
|
|
|
- set_data_sources(self, payload.sources);
|
|
|
- }
|
|
|
- GovernancePayload::SetWormholeAddress(payload) => {
|
|
|
- self.set_wormhole_address(payload.address, data.clone())?;
|
|
|
- }
|
|
|
- GovernancePayload::RequestGovernanceDataSourceTransfer(_) => {
|
|
|
- return Err(PythReceiverError::InvalidGovernanceMessage);
|
|
|
- }
|
|
|
- GovernancePayload::AuthorizeGovernanceDataSourceTransfer(payload) => {
|
|
|
- self.authorize_governance_transfer(payload.claim_vaa)?;
|
|
|
- }
|
|
|
- GovernancePayload::UpgradeContract(_payload) => {}
|
|
|
- GovernancePayload::SetValidPeriod(payload) => {
|
|
|
- self.set_valid_period(payload.valid_time_period_seconds);
|
|
|
- }
|
|
|
- GovernancePayload::SetTransactionFee(payload) => {
|
|
|
- self.set_transaction_fee(payload.value, payload.expo);
|
|
|
- }
|
|
|
- GovernancePayload::WithdrawFee(payload) => {
|
|
|
- self.withdraw_fee(payload.value, payload.expo, payload.target_address)?;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- Ok(())
|
|
|
- }
|
|
|
-
|
|
|
- fn upgrade_contract(&self, _new_implementation: FixedBytes<32>) {
|
|
|
- unimplemented!("Upgrade contract not yet implemented");
|
|
|
- }
|
|
|
|
|
|
fn is_no_older_than(&self, publish_time: U64, max_age: u64) -> bool {
|
|
|
self.get_current_timestamp()
|
|
|
@@ -614,249 +131,65 @@ impl PythReceiver {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- fn set_fee(&mut self, value: u64, expo: u64) {
|
|
|
- let new_fee = U256::from(value).saturating_mul(U256::from(10).pow(U256::from(expo)));
|
|
|
- let old_fee = self.single_update_fee_in_wei.get();
|
|
|
-
|
|
|
- self.single_update_fee_in_wei.set(new_fee);
|
|
|
-
|
|
|
- log(self.vm(), FeeSet { old_fee, new_fee });
|
|
|
+ pub fn price_feed_exists(&self, id: [u8; 32]) -> bool {
|
|
|
+ self.price_feed_exists_internal(id)
|
|
|
}
|
|
|
|
|
|
- fn set_valid_period(&mut self, valid_time_period_seconds: u64) {
|
|
|
- let old_valid_period = self.valid_time_period_seconds.get();
|
|
|
- let new_valid_period = U256::from(valid_time_period_seconds);
|
|
|
- self.valid_time_period_seconds.set(new_valid_period);
|
|
|
-
|
|
|
- log(
|
|
|
- self.vm(),
|
|
|
- ValidPeriodSet {
|
|
|
- old_valid_period,
|
|
|
- new_valid_period,
|
|
|
- },
|
|
|
- );
|
|
|
+ pub fn query_price_feed(&self, id: [u8; 32]) -> Result<structs::PriceFeedReturn, error::PythReceiverError> {
|
|
|
+ self.query_price_feed_internal(id)
|
|
|
}
|
|
|
|
|
|
- fn set_wormhole_address(
|
|
|
- &mut self,
|
|
|
- address: Address,
|
|
|
- data: Vec<u8>,
|
|
|
- ) -> Result<(), PythReceiverError> {
|
|
|
- let wormhole: IWormholeContract = IWormholeContract::new(address);
|
|
|
- let config = Call::new();
|
|
|
- wormhole
|
|
|
- .parse_and_verify_vm(config, data.clone())
|
|
|
- .map_err(|_| PythReceiverError::InvalidVaa)?;
|
|
|
-
|
|
|
- let vm = Vaa::read(&mut data.as_slice())
|
|
|
- .map_err(|_| PythReceiverError::VaaVerificationFailed)?;
|
|
|
-
|
|
|
- if vm.body.emitter_chain != self.governance_data_source_chain_id.get().to::<u16>() {
|
|
|
- return Err(PythReceiverError::InvalidGovernanceMessage);
|
|
|
- }
|
|
|
-
|
|
|
- if vm.body.emitter_address.as_slice()
|
|
|
- != self.governance_data_source_emitter_address.get().as_slice()
|
|
|
- {
|
|
|
- return Err(PythReceiverError::InvalidGovernanceMessage);
|
|
|
- }
|
|
|
-
|
|
|
- if vm.body.sequence.to::<u64>() <= self.last_executed_governance_sequence.get().to::<u64>()
|
|
|
- {
|
|
|
- return Err(PythReceiverError::InvalidWormholeAddressToSet);
|
|
|
- }
|
|
|
-
|
|
|
- let data = governance_structs::parse_instruction(vm.body.payload.to_vec())
|
|
|
- .map_err(|_| PythReceiverError::InvalidGovernanceMessage)?;
|
|
|
-
|
|
|
- match data.payload {
|
|
|
- GovernancePayload::SetWormholeAddress(payload) => {
|
|
|
- if payload.address != address {
|
|
|
- return Err(PythReceiverError::InvalidWormholeAddressToSet);
|
|
|
- }
|
|
|
- }
|
|
|
- _ => return Err(PythReceiverError::InvalidGovernanceMessage),
|
|
|
- }
|
|
|
-
|
|
|
- self.wormhole.set(address);
|
|
|
- Ok(())
|
|
|
+ pub fn get_price_unsafe(&self, id: [u8; 32]) -> Result<structs::PriceReturn, error::PythReceiverError> {
|
|
|
+ self.get_price_unsafe_internal(id)
|
|
|
}
|
|
|
|
|
|
- fn authorize_governance_transfer(
|
|
|
- &mut self,
|
|
|
- claim_vaa: Vec<u8>,
|
|
|
- ) -> Result<(), PythReceiverError> {
|
|
|
- let wormhole: IWormholeContract = IWormholeContract::new(self.wormhole.get());
|
|
|
- let config = Call::new();
|
|
|
- wormhole
|
|
|
- .parse_and_verify_vm(config, claim_vaa.clone())
|
|
|
- .map_err(|_| PythReceiverError::InvalidWormholeMessage)?;
|
|
|
-
|
|
|
- let claim_vm = Vaa::read(&mut Vec::from(claim_vaa).as_slice())
|
|
|
- .map_err(|_| PythReceiverError::VaaVerificationFailed)?;
|
|
|
-
|
|
|
- let instruction = governance_structs::parse_instruction(claim_vm.body.payload.to_vec())
|
|
|
- .map_err(|_| PythReceiverError::InvalidGovernanceMessage)?;
|
|
|
-
|
|
|
- let config2 = Call::new();
|
|
|
- if instruction.target_chain_id != 0
|
|
|
- && instruction.target_chain_id != wormhole.chain_id(config2).unwrap_or(0)
|
|
|
- {
|
|
|
- return Err(PythReceiverError::InvalidGovernanceTarget);
|
|
|
- }
|
|
|
-
|
|
|
- let request_payload = match instruction.payload {
|
|
|
- GovernancePayload::RequestGovernanceDataSourceTransfer(payload) => payload,
|
|
|
- _ => return Err(PythReceiverError::InvalidGovernanceMessage),
|
|
|
- };
|
|
|
-
|
|
|
- let current_index = self.governance_data_source_index.get().to::<u32>();
|
|
|
- let new_index = request_payload.governance_data_source_index;
|
|
|
-
|
|
|
- if current_index >= new_index {
|
|
|
- return Err(PythReceiverError::OldGovernanceMessage);
|
|
|
- }
|
|
|
-
|
|
|
- self.governance_data_source_index.set(U32::from(new_index));
|
|
|
- let old_data_source_emitter_address = self.governance_data_source_emitter_address.get();
|
|
|
-
|
|
|
- self.governance_data_source_chain_id
|
|
|
- .set(U16::from(claim_vm.body.emitter_chain));
|
|
|
- let emitter_bytes: [u8; 32] = claim_vm
|
|
|
- .body
|
|
|
- .emitter_address
|
|
|
- .as_slice()
|
|
|
- .try_into()
|
|
|
- .map_err(|_| PythReceiverError::InvalidEmitterAddress)?;
|
|
|
- self.governance_data_source_emitter_address
|
|
|
- .set(FixedBytes::from(emitter_bytes));
|
|
|
-
|
|
|
- let last_executed_governance_sequence = claim_vm.body.sequence.to::<u64>();
|
|
|
- self.last_executed_governance_sequence
|
|
|
- .set(U64::from(last_executed_governance_sequence));
|
|
|
-
|
|
|
- log(
|
|
|
- self.vm(),
|
|
|
- GovernanceDataSourceSet {
|
|
|
- old_chain_id: current_index as u16,
|
|
|
- old_emitter_address: old_data_source_emitter_address,
|
|
|
- new_chain_id: claim_vm.body.emitter_chain,
|
|
|
- new_emitter_address: FixedBytes::from(emitter_bytes),
|
|
|
- initial_sequence: last_executed_governance_sequence,
|
|
|
- },
|
|
|
- );
|
|
|
-
|
|
|
- Ok(())
|
|
|
+ pub fn get_price_no_older_than(&self, id: [u8; 32], age: u64) -> Result<structs::PriceReturn, error::PythReceiverError> {
|
|
|
+ self.get_price_no_older_than_internal(id, age)
|
|
|
}
|
|
|
|
|
|
- fn set_transaction_fee(&mut self, value: u64, expo: u64) {
|
|
|
- let new_fee = U256::from(value).saturating_mul(U256::from(10).pow(U256::from(expo)));
|
|
|
- let old_fee = self.transaction_fee_in_wei.get();
|
|
|
-
|
|
|
- self.transaction_fee_in_wei.set(new_fee);
|
|
|
-
|
|
|
- log(self.vm(), TransactionFeeSet { old_fee, new_fee });
|
|
|
+ pub fn get_ema_price_unsafe(&self, id: [u8; 32]) -> Result<structs::PriceReturn, error::PythReceiverError> {
|
|
|
+ self.get_ema_price_unsafe_internal(id)
|
|
|
}
|
|
|
|
|
|
- fn withdraw_fee(
|
|
|
- &mut self,
|
|
|
- value: u64,
|
|
|
- expo: u64,
|
|
|
- target_address: Address,
|
|
|
- ) -> Result<(), PythReceiverError> {
|
|
|
- let fee_to_withdraw =
|
|
|
- U256::from(value).saturating_mul(U256::from(10).pow(U256::from(expo)));
|
|
|
- let current_balance = self.vm().balance(self.vm().contract_address());
|
|
|
-
|
|
|
- if current_balance < fee_to_withdraw {
|
|
|
- return Err(PythReceiverError::InsufficientFee);
|
|
|
- }
|
|
|
-
|
|
|
- self.vm()
|
|
|
- .transfer_eth(target_address, fee_to_withdraw)
|
|
|
- .map_err(|_| PythReceiverError::InsufficientFee)?;
|
|
|
-
|
|
|
- log(
|
|
|
- self.vm(),
|
|
|
- FeeWithdrawn {
|
|
|
- target_address,
|
|
|
- fee_amount: fee_to_withdraw,
|
|
|
- },
|
|
|
- );
|
|
|
-
|
|
|
- Ok(())
|
|
|
+ pub fn get_ema_price_no_older_than(&self, id: [u8; 32], age: u64) -> Result<structs::PriceReturn, error::PythReceiverError> {
|
|
|
+ self.get_ema_price_no_older_than_internal(id, age)
|
|
|
}
|
|
|
-}
|
|
|
|
|
|
-fn verify_governance_vm(receiver: &mut PythReceiver, vm: Vaa) -> Result<(), PythReceiverError> {
|
|
|
- if vm.body.emitter_chain != receiver.governance_data_source_chain_id.get().to::<u16>() {
|
|
|
- return Err(PythReceiverError::InvalidGovernanceMessage);
|
|
|
+ #[payable]
|
|
|
+ pub fn update_price_feeds(&mut self, update_data: Vec<Vec<u8>>) -> Result<(), error::PythReceiverError> {
|
|
|
+ self.update_price_feeds_internal(update_data)
|
|
|
}
|
|
|
|
|
|
- if vm.body.emitter_address.as_slice()
|
|
|
- != receiver
|
|
|
- .governance_data_source_emitter_address
|
|
|
- .get()
|
|
|
- .as_slice()
|
|
|
- {
|
|
|
- return Err(PythReceiverError::InvalidGovernanceMessage);
|
|
|
+ pub fn update_price_feeds_if_necessary(&mut self, update_data: Vec<Vec<u8>>, price_ids: Vec<[u8; 32]>, publish_times: Vec<u64>) -> Result<(), error::PythReceiverError> {
|
|
|
+ self.update_price_feeds_if_necessary_internal(update_data, price_ids, publish_times)
|
|
|
}
|
|
|
|
|
|
- let current_sequence = vm.body.sequence.to::<u64>();
|
|
|
- let last_executed_sequence = receiver.last_executed_governance_sequence.get().to::<u64>();
|
|
|
-
|
|
|
- if current_sequence <= last_executed_sequence {
|
|
|
- return Err(PythReceiverError::GovernanceMessageAlreadyExecuted);
|
|
|
+ pub fn get_update_fee(&self, update_data: Vec<Vec<u8>>) -> Result<U256, error::PythReceiverError> {
|
|
|
+ self.get_update_fee_internal(update_data)
|
|
|
}
|
|
|
|
|
|
- receiver
|
|
|
- .last_executed_governance_sequence
|
|
|
- .set(U64::from(current_sequence));
|
|
|
-
|
|
|
- Ok(())
|
|
|
-}
|
|
|
+ pub fn get_twap_update_fee(&self, update_data: Vec<Vec<u8>>) -> U256 {
|
|
|
+ self.get_twap_update_fee_internal(update_data)
|
|
|
+ }
|
|
|
|
|
|
-fn parse_wormhole_proof(vaa: Vaa) -> Result<MerkleRoot<Keccak160>, PythReceiverError> {
|
|
|
- let message = WormholeMessage::try_from_bytes(vaa.body.payload.to_vec())
|
|
|
- .map_err(|_| PythReceiverError::PriceUnavailable)?;
|
|
|
- let root: MerkleRoot<Keccak160> = MerkleRoot::new(match message.payload {
|
|
|
- WormholePayload::Merkle(merkle_root) => merkle_root.root,
|
|
|
- });
|
|
|
- Ok(root)
|
|
|
-}
|
|
|
+ pub fn parse_price_feed_updates(&mut self, update_data: Vec<u8>, price_ids: Vec<[u8; 32]>, min_publish_time: u64, max_publish_time: u64) -> Result<Vec<structs::PriceFeedReturn>, error::PythReceiverError> {
|
|
|
+ self.parse_price_feed_updates_internal_wrapper(update_data, price_ids, min_publish_time, max_publish_time)
|
|
|
+ }
|
|
|
|
|
|
-fn set_data_sources(receiver: &mut PythReceiver, data_sources: Vec<DataSource>) {
|
|
|
- let mut old_data_sources = Vec::new();
|
|
|
- for i in 0..receiver.valid_data_sources.len() {
|
|
|
- if let Some(storage_data_source) = receiver.valid_data_sources.get(i) {
|
|
|
- let data_source = DataSource {
|
|
|
- chain_id: storage_data_source.chain_id.get(),
|
|
|
- emitter_address: storage_data_source.emitter_address.get(),
|
|
|
- };
|
|
|
- old_data_sources.push(data_source.emitter_address);
|
|
|
- receiver.is_valid_data_source.setter(data_source).set(false);
|
|
|
- }
|
|
|
+ pub fn parse_price_feed_updates_with_config(&mut self, update_data: Vec<Vec<u8>>, price_ids: Vec<[u8; 32]>, min_allowed_publish_time: u64, max_allowed_publish_time: u64, check_uniqueness: bool, check_update_data_is_minimal: bool, store_updates_if_fresh: bool) -> Result<Vec<structs::PriceFeedReturn>, error::PythReceiverError> {
|
|
|
+ self.parse_price_feed_updates_with_config_internal(update_data, price_ids, min_allowed_publish_time, max_allowed_publish_time, check_uniqueness, check_update_data_is_minimal, store_updates_if_fresh)
|
|
|
}
|
|
|
|
|
|
- receiver.valid_data_sources.erase();
|
|
|
+ pub fn parse_twap_price_feed_updates(&mut self, update_data: Vec<Vec<u8>>, price_ids: Vec<[u8; 32]>) -> Vec<structs::PriceFeedReturn> {
|
|
|
+ self.parse_twap_price_feed_updates_internal(update_data, price_ids)
|
|
|
+ }
|
|
|
|
|
|
- let mut new_data_sources = Vec::new();
|
|
|
- for data_source in data_sources {
|
|
|
- let mut storage_data_source = receiver.valid_data_sources.grow();
|
|
|
- storage_data_source.chain_id.set(data_source.chain_id);
|
|
|
- storage_data_source
|
|
|
- .emitter_address
|
|
|
- .set(data_source.emitter_address);
|
|
|
+ pub fn parse_price_feed_updates_unique(&mut self, update_data: Vec<Vec<u8>>, price_ids: Vec<[u8; 32]>, min_publish_time: u64, max_publish_time: u64) -> Result<Vec<structs::PriceFeedReturn>, error::PythReceiverError> {
|
|
|
+ self.parse_price_feed_updates_unique_internal(update_data, price_ids, min_publish_time, max_publish_time)
|
|
|
+ }
|
|
|
|
|
|
- new_data_sources.push(data_source.emitter_address);
|
|
|
- receiver.is_valid_data_source.setter(data_source).set(true);
|
|
|
+ pub fn execute_governance_instruction(&mut self, encoded_vaa: Vec<u8>) -> Result<(), error::PythReceiverError> {
|
|
|
+ self.execute_governance_instruction_internal(encoded_vaa)
|
|
|
}
|
|
|
|
|
|
- log(
|
|
|
- receiver.vm(),
|
|
|
- DataSourcesSet {
|
|
|
- old_data_sources,
|
|
|
- new_data_sources,
|
|
|
- },
|
|
|
- );
|
|
|
}
|