From 07b01118d224815c26a1fdb038de48d015e5ef06 Mon Sep 17 00:00:00 2001 From: Jayant Krishnamurthy Date: Sat, 29 Jul 2023 21:11:03 -0700 Subject: [PATCH] [hermes] Add docs for 1 method (#995) * doc this one method * revert --- hermes/README.md | 9 +++++++++ hermes/src/api.rs | 20 +++++++++----------- hermes/src/api/rest.rs | 22 +++++++++++++++------- hermes/src/api/types.rs | 32 ++++++++++++++++---------------- 4 files changed, 49 insertions(+), 34 deletions(-) diff --git a/hermes/README.md b/hermes/README.md index 30a01801..eb6bbc57 100644 --- a/hermes/README.md +++ b/hermes/README.md @@ -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. 5. **Run the node**: To run Hermes for Pythnet, use the following command: + ```bash ./target/release/hermes run \ --pythnet-http-endpoint https://pythnet-rpc/ \ --pythnet-ws-endpoint wss://pythnet-rpc/ ``` + 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. + 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 For users who simply want to run the software, this section can be skipped. diff --git a/hermes/src/api.rs b/hermes/src/api.rs index 728373c1..65f36e0b 100644 --- a/hermes/src/api.rs +++ b/hermes/src/api.rs @@ -3,24 +3,18 @@ use { crate::store::Store, anyhow::Result, axum::{ + extract::Extension, routing::get, Router, }, + serde_qs::axum::QsQueryConfig, std::sync::Arc, tokio::{ signal, sync::mpsc::Receiver, }, tower_http::cors::CorsLayer, - utoipa::{ - openapi::security::{ - ApiKey, - ApiKeyValue, - SecurityScheme, - }, - Modify, - OpenApi, - }, + utoipa::OpenApi, utoipa_swagger_ui::SwaggerUi, }; @@ -54,7 +48,7 @@ pub async fn run(store: Arc, mut update_rx: Receiver<()>, rpc_addr: Strin rest::latest_price_feeds, ), components( - schemas(types::RpcPriceFeedMetadata, types::RpcPriceFeed) + schemas(types::RpcPriceFeedMetadata, types::RpcPriceFeed, types::PriceIdInput) ), tags( (name = "hermes", description = "Pyth Real-Time Pricing API") @@ -80,7 +74,11 @@ pub async fn run(store: Arc, mut update_rx: Receiver<()>, rpc_addr: Strin .route("/api/get_vaa_ccip", get(rest::get_vaa_ccip)) .route("/api/price_feed_ids", get(rest::price_feed_ids)) .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 diff --git a/hermes/src/api/rest.rs b/hermes/src/api/rest.rs index a8f3080d..e84d9241 100644 --- a/hermes/src/api/rest.rs +++ b/hermes/src/api/rest.rs @@ -97,26 +97,34 @@ pub async fn latest_vaas( } #[derive(Debug, serde::Deserialize, IntoParams)] +#[into_params(parameter_in=Query)] 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, + /// If true, include the `metadata` field in the response with additional metadata about + /// the price update. #[serde(default)] - #[param(value_type = Option, required = false, nullable = true)] 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)] - #[param(value_type = Option, required = false, nullable = true)] binary: bool, } -/// Get the latest prices by price feed ids. -/// -/// Get the latest price updates for a provided collection of price feed ids. +/// Get the latest price updates by price feed id. /// +/// Given a collection of price feed ids, retrieve the latest Pyth price for each price feed. #[utoipa::path( get, path = "/api/latest_price_feeds", responses( - (status = 200, description = "Price feeds retrieved successfully", body = [Vec]) + (status = 200, description = "Price updates retrieved successfully", body = [Vec]) ), params( LatestPriceFeedsQueryParams diff --git a/hermes/src/api/types.rs b/hermes/src/api/types.rs index d9b84f7f..9b4b58b0 100644 --- a/hermes/src/api/types.rs +++ b/hermes/src/api/types.rs @@ -19,23 +19,21 @@ use { Price, PriceIdentifier, }, - utoipa::{ - openapi::{ - RefOr, - Schema, - }, - IntoParams, - ToSchema, - }, + utoipa::ToSchema, wormhole_sdk::Chain, }; -/// PriceIdInput is a wrapper around a 32-byte hex string. -/// that supports a flexible deserialization from a hex string. -/// It supports both 0x-prefixed and non-prefixed hex strings, -/// and also supports both lower and upper case characters. -#[derive(Debug, Clone, Deref, DerefMut)] +/// A price id is a 32-byte hex string, optionally prefixed with "0x". +/// Price ids are case insensitive. +/// +/// Examples: +/// * 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]); // TODO: Use const generics instead of macro. impl_deserialize_for_hex_string_wrapper!(PriceIdInput, 32); @@ -50,15 +48,17 @@ type Base64String = String; #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)] pub struct RpcPriceFeedMetadata { - #[schema(value_type = u64)] + #[schema(value_type = u64, example=85480034)] pub slot: Slot, + #[schema(example = 26)] pub emitter_chain: u16, - #[schema(value_type = i64)] + #[schema(value_type = i64, example=1690576641)] pub price_service_receive_time: UnixTimestamp, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)] pub struct RpcPriceFeed { + #[schema(example = "e62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43")] pub id: PriceIdentifier, pub price: Price, pub ema_price: Price, @@ -66,7 +66,7 @@ pub struct RpcPriceFeed { pub metadata: Option, /// Vaa binary represented in base64. #[serde(skip_serializing_if = "Option::is_none")] - #[schema(value_type = Option)] + #[schema(value_type = Option, 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, }