[hermes] Add docs for 1 method (#995)

* doc this one method

* revert
This commit is contained in:
Jayant Krishnamurthy 2023-07-29 21:11:03 -07:00 committed by GitHub
parent 7674a3a07c
commit 07b01118d2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 49 additions and 34 deletions

View File

@ -35,14 +35,23 @@ To set up and run a Hermes node, follow the steps below:
``` ```
This will create a binary in the target/release directory. This will create a binary in the target/release directory.
5. **Run the node**: To run Hermes for Pythnet, use the following command: 5. **Run the node**: To run Hermes for Pythnet, use the following command:
```bash ```bash
./target/release/hermes run \ ./target/release/hermes run \
--pythnet-http-endpoint https://pythnet-rpc/ \ --pythnet-http-endpoint https://pythnet-rpc/ \
--pythnet-ws-endpoint wss://pythnet-rpc/ --pythnet-ws-endpoint wss://pythnet-rpc/
``` ```
Your Hermes node will now start and connect to the specified networks. You Your Hermes node will now start and connect to the specified networks. You
can interact with the node using the REST and Websocket APIs on port 33999. can interact with the node using the REST and Websocket APIs on port 33999.
For local development, you can also run the node with cargo watch to restart
it automatically when the code changes:
```bash
cargo watch -w src -x "run -- run --pythnet-http-endpoint https://pythnet.rpcpool.com --pythnet-ws-endpoint wss://pythnet.rpcpool.com"
```
## Architecture Overview ## Architecture Overview
For users who simply want to run the software, this section can be skipped. For users who simply want to run the software, this section can be skipped.

View File

@ -3,24 +3,18 @@ use {
crate::store::Store, crate::store::Store,
anyhow::Result, anyhow::Result,
axum::{ axum::{
extract::Extension,
routing::get, routing::get,
Router, Router,
}, },
serde_qs::axum::QsQueryConfig,
std::sync::Arc, std::sync::Arc,
tokio::{ tokio::{
signal, signal,
sync::mpsc::Receiver, sync::mpsc::Receiver,
}, },
tower_http::cors::CorsLayer, tower_http::cors::CorsLayer,
utoipa::{ utoipa::OpenApi,
openapi::security::{
ApiKey,
ApiKeyValue,
SecurityScheme,
},
Modify,
OpenApi,
},
utoipa_swagger_ui::SwaggerUi, utoipa_swagger_ui::SwaggerUi,
}; };
@ -54,7 +48,7 @@ pub async fn run(store: Arc<Store>, mut update_rx: Receiver<()>, rpc_addr: Strin
rest::latest_price_feeds, rest::latest_price_feeds,
), ),
components( components(
schemas(types::RpcPriceFeedMetadata, types::RpcPriceFeed) schemas(types::RpcPriceFeedMetadata, types::RpcPriceFeed, types::PriceIdInput)
), ),
tags( tags(
(name = "hermes", description = "Pyth Real-Time Pricing API") (name = "hermes", description = "Pyth Real-Time Pricing API")
@ -80,7 +74,11 @@ pub async fn run(store: Arc<Store>, mut update_rx: Receiver<()>, rpc_addr: Strin
.route("/api/get_vaa_ccip", get(rest::get_vaa_ccip)) .route("/api/get_vaa_ccip", get(rest::get_vaa_ccip))
.route("/api/price_feed_ids", get(rest::price_feed_ids)) .route("/api/price_feed_ids", get(rest::price_feed_ids))
.with_state(state.clone()) .with_state(state.clone())
.layer(CorsLayer::permissive()); // Permissive CORS layer to allow all origins // Permissive CORS layer to allow all origins
.layer(CorsLayer::permissive())
// non-strict mode permits escaped [] in URL parameters.
// 5 is the allowed depth (also the default value for this parameter).
.layer(Extension(QsQueryConfig::new(false)));
// Call dispatch updates to websocket every 1 seconds // Call dispatch updates to websocket every 1 seconds

View File

@ -97,26 +97,34 @@ pub async fn latest_vaas(
} }
#[derive(Debug, serde::Deserialize, IntoParams)] #[derive(Debug, serde::Deserialize, IntoParams)]
#[into_params(parameter_in=Query)]
pub struct LatestPriceFeedsQueryParams { pub struct LatestPriceFeedsQueryParams {
#[param(value_type = String)] /// Get the most recent price update for these price feed ids.
/// Provide this parameter multiple times to retrieve multiple price updates,
/// ids[]=a12...&ids[]=b4c...
#[param(
rename = "ids[]",
example = "e62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43"
)]
ids: Vec<PriceIdInput>, ids: Vec<PriceIdInput>,
/// If true, include the `metadata` field in the response with additional metadata about
/// the price update.
#[serde(default)] #[serde(default)]
#[param(value_type = Option<bool>, required = false, nullable = true)]
verbose: bool, verbose: bool,
/// If true, include the binary price update in the `vaa` field of each returned feed.
/// This binary data can be submitted to Pyth contracts to update the on-chain price.
#[serde(default)] #[serde(default)]
#[param(value_type = Option<bool>, required = false, nullable = true)]
binary: bool, binary: bool,
} }
/// Get the latest prices by price feed ids. /// Get the latest price updates by price feed id.
///
/// Get the latest price updates for a provided collection of price feed ids.
/// ///
/// Given a collection of price feed ids, retrieve the latest Pyth price for each price feed.
#[utoipa::path( #[utoipa::path(
get, get,
path = "/api/latest_price_feeds", path = "/api/latest_price_feeds",
responses( responses(
(status = 200, description = "Price feeds retrieved successfully", body = [Vec<RpcPriceFeed>]) (status = 200, description = "Price updates retrieved successfully", body = [Vec<RpcPriceFeed>])
), ),
params( params(
LatestPriceFeedsQueryParams LatestPriceFeedsQueryParams

View File

@ -19,23 +19,21 @@ use {
Price, Price,
PriceIdentifier, PriceIdentifier,
}, },
utoipa::{ utoipa::ToSchema,
openapi::{
RefOr,
Schema,
},
IntoParams,
ToSchema,
},
wormhole_sdk::Chain, wormhole_sdk::Chain,
}; };
/// PriceIdInput is a wrapper around a 32-byte hex string. /// A price id is a 32-byte hex string, optionally prefixed with "0x".
/// that supports a flexible deserialization from a hex string. /// Price ids are case insensitive.
/// It supports both 0x-prefixed and non-prefixed hex strings, ///
/// and also supports both lower and upper case characters. /// Examples:
#[derive(Debug, Clone, Deref, DerefMut)] /// * 0x63f341689d98a12ef60a5cff1d7f85c70a9e17bf1575f0e7c0b2512d48b1c8b3
/// * 63f341689d98a12ef60a5cff1d7f85c70a9e17bf1575f0e7c0b2512d48b1c8b3
///
/// See https://pyth.network/developers/price-feed-ids for a list of all price feed ids.
#[derive(Debug, Clone, Deref, DerefMut, ToSchema)]
#[schema(value_type=String)]
pub struct PriceIdInput([u8; 32]); pub struct PriceIdInput([u8; 32]);
// TODO: Use const generics instead of macro. // TODO: Use const generics instead of macro.
impl_deserialize_for_hex_string_wrapper!(PriceIdInput, 32); impl_deserialize_for_hex_string_wrapper!(PriceIdInput, 32);
@ -50,15 +48,17 @@ type Base64String = String;
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)] #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)]
pub struct RpcPriceFeedMetadata { pub struct RpcPriceFeedMetadata {
#[schema(value_type = u64)] #[schema(value_type = u64, example=85480034)]
pub slot: Slot, pub slot: Slot,
#[schema(example = 26)]
pub emitter_chain: u16, pub emitter_chain: u16,
#[schema(value_type = i64)] #[schema(value_type = i64, example=1690576641)]
pub price_service_receive_time: UnixTimestamp, pub price_service_receive_time: UnixTimestamp,
} }
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)] #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)]
pub struct RpcPriceFeed { pub struct RpcPriceFeed {
#[schema(example = "e62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43")]
pub id: PriceIdentifier, pub id: PriceIdentifier,
pub price: Price, pub price: Price,
pub ema_price: Price, pub ema_price: Price,
@ -66,7 +66,7 @@ pub struct RpcPriceFeed {
pub metadata: Option<RpcPriceFeedMetadata>, pub metadata: Option<RpcPriceFeedMetadata>,
/// Vaa binary represented in base64. /// Vaa binary represented in base64.
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
#[schema(value_type = Option<String>)] #[schema(value_type = Option<String>, example="UE5BVQEAAAADuAEAAAADDQC1H7meY5fTed0FsykIb8dt+7nKpbuzfvU2DplDi+dcUl8MC+UIkS65+rkiq+zmNBxE2gaxkBkjdIicZ/fBo+X7AAEqp+WtlWb84np8jJfLpuQ2W+l5KXTigsdAhz5DyVgU3xs+EnaIZxBwcE7EKzjMam+V9rlRy0CGsiQ1kjqqLzfAAQLsoVO0Vu5gVmgc8XGQ7xYhoz36rsBgMjG+e3l/B01esQi/KzPuBf/Ar8Sg5aSEOvEU0muSDb+KIr6d8eEC+FtcAAPZEaBSt4ysXVL84LUcJemQD3SiG30kOfUpF8o7/wI2M2Jf/LyCsbKEQUyLtLbZqnJBSfZJR5AMsrnHDqngMLEGAAY4UDG9GCpRuPvg8hOlsrXuPP3zq7yVPqyG0SG+bNo8rEhP5b1vXlHdG4bZsutX47d5VZ6xnFROKudx3T3/fnWUAQgAU1+kUFc3e0ZZeX1dLRVEryNIVyxMQIcxWwdey+jlIAYowHRM0fJX3Scs80OnT/CERwh5LMlFyU1w578NqxW+AQl2E/9fxjgUTi8crOfDpwsUsmOWw0+Q5OUGhELv/2UZoHAjsaw9OinWUggKACo4SdpPlHYldoWF+J2yGWOW+F4iAQre4c+ocb6a9uSWOnTldFkioqhd9lhmV542+VonCvuy4Tu214NP+2UNd/4Kk3KJCf3iziQJrCBeLi1cLHdLUikgAQtvRFR/nepcF9legl+DywAkUHi5/1MNjlEQvlHyh2XbMiS85yu7/9LgM6Sr+0ukfZY5mSkOcvUkpHn+T+Nw/IrQAQ7lty5luvKUmBpI3ITxSmojJ1aJ0kj/dc0ZcQk+/qo0l0l3/eRLkYjw5j+MZKA8jEubrHzUCke98eSoj8l08+PGAA+DAKNtCwNZe4p6J1Ucod8Lo5RKFfA84CPLVyEzEPQFZ25U9grUK6ilF4GhEia/ndYXLBt3PGW3qa6CBBPM7rH3ABGAyYEtUwzB4CeVedA5o6cKpjRkIebqDNSOqltsr+w7kXdfFVtsK2FMGFZNt5rbpIR+ppztoJ6eOKHmKmi9nQ99ARKkTxRErOs9wJXNHaAuIRV38o1pxRrlQRzGsRuKBqxcQEpC8OPFpyKYcp6iD5l7cO/gRDTamLFyhiUBwKKMP07FAWTEJv8AAAAAABrhAfrtrFhR4yubI7X5QRqMK6xKrj7U3XuBHdGnLqSqcQAAAAAAGp0GAUFVV1YAAAAAAAUYUmIAACcQBsfKUtr4PgZbIXRxRESU79PjE4IBAFUA5i32yLSoX+GmfbRNwS3l2zMPesZrctxliv7fD0pBW0MAAAKqqMJFwAAAAAAqE/NX////+AAAAABkxCb7AAAAAGTEJvoAAAKqIcWxYAAAAAAlR5m4CP/mPsh1IezjYpDlJ4GRb5q4fTs2LjtyO6M0XgVimrIQ4kSh1qg7JKW4gbGkyRntVFR9JO/GNd3FPDit0BK6M+JzXh/h12YNCz9wxlZTvXrNtWNbzqT+91pvl5cphhSPMfAHyEzTPaGR9tKDy9KNu56pmhaY32d2vfEWQmKo22guegeR98oDxs67MmnUraco46a3zEnac2Bm80pasUgMO24=")]
pub vaa: Option<Base64String>, pub vaa: Option<Base64String>,
} }