feat(hermes): release v2 initial API (#1293)

The V2 API provide more functionality compared to the V1 such as
supporting benchmark proofs for multiple ids. This change bumps the
Hermes version to initiate a release and also fixes a couple of minor
things:
- Update build.rs to not panic on rebuilds
- Remove an unused benchmarks file
- Add all the V2 endpoints to docs
This commit is contained in:
Ali Behjati 2024-02-12 16:05:02 +01:00 committed by GitHub
parent 5dcf5cac4f
commit 22579edc6e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 8 additions and 138 deletions

2
hermes/Cargo.lock generated
View File

@ -1574,7 +1574,7 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "hermes"
version = "0.4.5"
version = "0.5.0"
dependencies = [
"anyhow",
"async-trait",

View File

@ -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"

View File

@ -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
"#;

View File

@ -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(

View File

@ -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<String>,
}
impl TryFrom<PriceUpdate> for PriceFeedsWithUpdateData {
type Error = anyhow::Error;
fn try_from(price_update: PriceUpdate) -> Result<Self> {
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::<Result<Vec<_>>>(),
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::<Vec<Vec<u8>>>();
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<PriceFeedsWithUpdateData>;
}
#[async_trait::async_trait]
impl Benchmarks for crate::state::State {
async fn get_verified_price_feeds(
&self,
price_ids: &[PriceIdentifier],
publish_time: UnixTimestamp,
) -> Result<PriceFeedsWithUpdateData> {
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()
}
}