parent
7674a3a07c
commit
07b01118d2
|
@ -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.
|
||||
|
|
|
@ -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<Store>, 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<Store>, 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
|
||||
|
|
|
@ -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<PriceIdInput>,
|
||||
/// If true, include the `metadata` field in the response with additional metadata about
|
||||
/// the price update.
|
||||
#[serde(default)]
|
||||
#[param(value_type = Option<bool>, 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<bool>, 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<RpcPriceFeed>])
|
||||
(status = 200, description = "Price updates retrieved successfully", body = [Vec<RpcPriceFeed>])
|
||||
),
|
||||
params(
|
||||
LatestPriceFeedsQueryParams
|
||||
|
|
|
@ -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<RpcPriceFeedMetadata>,
|
||||
/// Vaa binary represented in base64.
|
||||
#[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>,
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue