diff --git a/hermes/Cargo.lock b/hermes/Cargo.lock index f2ebc6807b..9c110ac1a9 100644 --- a/hermes/Cargo.lock +++ b/hermes/Cargo.lock @@ -1574,7 +1574,7 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermes" -version = "0.4.5" +version = "0.5.0" dependencies = [ "anyhow", "async-trait", diff --git a/hermes/Cargo.toml b/hermes/Cargo.toml index b1767a4410..4faf945559 100644 --- a/hermes/Cargo.toml +++ b/hermes/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hermes" -version = "0.4.5" +version = "0.5.0" description = "Hermes is an agent that provides Verified Prices from the Pythnet Pyth Oracle." edition = "2021" diff --git a/hermes/build.rs b/hermes/build.rs index 095a175c3a..68e5f07792 100644 --- a/hermes/build.rs +++ b/hermes/build.rs @@ -14,12 +14,15 @@ fn main() { // directory as a mini-repo with wormhole and googleapis as remotes, so we can copy out the // TREEISH paths we want. let protobuf_setup = r#" + set -euo pipefail git init . git clean -df - git remote add wormhole https://github.com/wormhole-foundation/wormhole.git - git remote add googleapis https://github.com/googleapis/googleapis.git + git remote add wormhole https://github.com/wormhole-foundation/wormhole.git || true + git remote add googleapis https://github.com/googleapis/googleapis.git || true git fetch --depth=1 wormhole main git fetch --depth=1 googleapis master + git reset + rm -rf proto/ git read-tree --prefix=proto/ -u wormhole/main:proto git read-tree --prefix=proto/google/api/ -u googleapis/master:google/api "#; diff --git a/hermes/src/api.rs b/hermes/src/api.rs index b4d74f63ba..add1deab8d 100644 --- a/hermes/src/api.rs +++ b/hermes/src/api.rs @@ -122,6 +122,7 @@ pub async fn run(opts: RunOptions, state: ApiState) -> Result<()> { rest::latest_vaas, rest::price_feed_ids, rest::latest_price_updates, + rest::timestamp_price_updates, ), components( schemas( diff --git a/hermes/src/api/benchmarks.rs b/hermes/src/api/benchmarks.rs deleted file mode 100644 index 63f945f758..0000000000 --- a/hermes/src/api/benchmarks.rs +++ /dev/null @@ -1,134 +0,0 @@ -//! This module communicates with Pyth Benchmarks, an API for historical price feeds and their updates. - -use { - crate::{ - aggregate::{ - PriceFeedUpdate, - PriceFeedsWithUpdateData, - UnixTimestamp, - }, - api::types::PriceUpdate, - }, - anyhow::Result, - base64::{ - engine::general_purpose::STANDARD as base64_standard_engine, - Engine as _, - }, - pyth_sdk::{ - Price, - PriceFeed, - PriceIdentifier, - }, - serde::Deserialize, -}; - -const BENCHMARKS_REQUEST_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(30); - -#[derive(Deserialize, Debug, Clone)] -enum BlobEncoding { - #[serde(rename = "base64")] - Base64, - #[serde(rename = "hex")] - Hex, -} - -#[derive(Deserialize, Debug, Clone)] -struct BinaryBlob { - pub encoding: BlobEncoding, - pub data: Vec, -} - -impl TryFrom for PriceFeedsWithUpdateData { - type Error = anyhow::Error; - fn try_from(price_update: PriceUpdate) -> Result { - let price_feeds = match price_update.parsed { - Some(parsed_updates) => parsed_updates - .into_iter() - .map(|parsed_price_update| { - Ok(PriceFeedUpdate { - price_feed: PriceFeed::new( - parsed_price_update.id, - Price { - price: parsed_price_update.price.price, - conf: parsed_price_update.price.conf, - expo: parsed_price_update.price.expo, - publish_time: parsed_price_update.price.publish_time, - }, - Price { - price: parsed_price_update.ema_price.price, - conf: parsed_price_update.ema_price.conf, - expo: parsed_price_update.ema_price.expo, - publish_time: parsed_price_update.ema_price.publish_time, - }, - ), - slot: parsed_price_update.metadata.slot, - received_at: parsed_price_update.metadata.proof_available_time, - update_data: None, // This field is not available in ParsedPriceUpdate - prev_publish_time: parsed_price_update.metadata.prev_publish_time, - }) - }) - .collect::>>(), - None => Err(anyhow::anyhow!("No parsed price updates available")), - }?; - - let update_data = price_update - .binary - .data - .iter() - .map(|hex_str| hex::decode(hex_str).unwrap_or_default()) - .collect::>>(); - - Ok(PriceFeedsWithUpdateData { - price_feeds, - update_data, - }) - } -} - -#[async_trait::async_trait] -pub trait Benchmarks { - async fn get_verified_price_feeds( - &self, - price_ids: &[PriceIdentifier], - publish_time: UnixTimestamp, - ) -> Result; -} - -#[async_trait::async_trait] -impl Benchmarks for crate::state::State { - async fn get_verified_price_feeds( - &self, - price_ids: &[PriceIdentifier], - publish_time: UnixTimestamp, - ) -> Result { - let endpoint = self - .benchmarks_endpoint - .as_ref() - .ok_or_else(|| anyhow::anyhow!("Benchmarks endpoint is not set"))? - .join(&format!("/v1/updates/price/{}", publish_time)) - .unwrap(); - - let client = reqwest::Client::new(); - let mut request = client - .get(endpoint) - .timeout(BENCHMARKS_REQUEST_TIMEOUT) - .query(&[("encoding", "hex")]) - .query(&[("parsed", "true")]); - - for price_id in price_ids { - request = request.query(&[("ids", price_id)]) - } - - let response = request.send().await?; - - if response.status() != reqwest::StatusCode::OK { - return Err(anyhow::anyhow!(format!( - "Price update for price ids {:?} with publish time {} not found in benchmarks. Status code: {}, message: {}", - price_ids, publish_time, response.status(), response.text().await? - ))); - } - - let price_update: PriceUpdate = response.json().await?; - price_update.try_into() - } -}