diff --git a/Cargo.lock b/Cargo.lock index 0db79f1..d1c22d2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3564,7 +3564,7 @@ dependencies = [ [[package]] name = "pyth-agent" -version = "2.12.3" +version = "3.0.0" dependencies = [ "anyhow", "async-trait", diff --git a/Cargo.toml b/Cargo.toml index 5ccefea..84c94ff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pyth-agent" -version = "2.12.3" +version = "3.0.0" edition = "2024" [[bin]] diff --git a/Dockerfile b/Dockerfile index 08cc713..173a78f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM rust:slim-bookworm as builder -RUN apt update && apt install -y curl libssl-dev pkg-config && apt clean all +RUN apt update && apt install -y curl libssl-dev pkg-config build-essential && apt clean all ADD . /agent WORKDIR /agent diff --git a/config/config.sample.pythnet.toml b/config/config.sample.pythnet.toml index d5ffbca..870cb16 100644 --- a/config/config.sample.pythnet.toml +++ b/config/config.sample.pythnet.toml @@ -10,7 +10,7 @@ rpc_urls = ["https://api2.pythnet.pyth.network"] # WS(S) endpoint of the RRC node. This is used to subscribe to account changes on the network. # This can be omitted when oracle.subscriber_enabled is set to false. -wss_url = "wss://api2.pythnet.pyth.network" +wss_urls = ["wss://api2.pythnet.pyth.network"] # Path to your publishing keypair. key_store.publish_keypair_path = "/path/to/keypair.json" diff --git a/config/config.sample.pythtest.toml b/config/config.sample.pythtest.toml index 6890660..bf5f915 100644 --- a/config/config.sample.pythtest.toml +++ b/config/config.sample.pythtest.toml @@ -10,7 +10,7 @@ rpc_urls = ["https://api.pythtest.pyth.network"] # WS(S) endpoint of the RRC node. This is used to subscribe to account changes # on the network. This can be omitted when oracle.subscriber_enabled is set to # false. -wss_url = "wss://api.pythtest.pyth.network" +wss_urls = ["wss://api.pythtest.pyth.network"] # Path to your publishing keypair. key_store.publish_keypair_path = "/path/to/keypair.json" diff --git a/config/config.toml b/config/config.toml index 6cf270d..b232e3c 100644 --- a/config/config.toml +++ b/config/config.toml @@ -37,7 +37,7 @@ rpc_urls = ["https://api.pythtest.pyth.network"] # WS(S) endpoint of the RRC node. This is used to subscribe to account changes on the network. # This can be omitted when oracle.subscriber_enabled is set to false. -wss_url = "wss://api.pythtest.pyth.network" +wss_urls = ["wss://api.pythtest.pyth.network"] # Path to the keypair used to publish price updates. If set to a # non-existent file path, the system expects a keypair to be loaded @@ -218,3 +218,24 @@ exporter_timeout_duration = "3s" # Endpoint URL for the OpenTelemetry exporter exporter_endpoint = "http://127.0.0.1:4317" + +## Configuration for Pyth Lazer ## + +# [pyth_lazer] +# URL for the history service +# history_url = "https://pyth-lazer-staging.dourolabs.app" + +# URLs for the Lazer relayers to connect to +# relayer_urls = ["wss://pyth-lazer-staging.dourolabs.app"] + +# Unique identifier for this publisher +# publisher_id = 1 + +# Authorization token for connecting to relayers +# authorization_token = "your-auth-token" + +# Path to the publisher's secret key file +# publish_keypair_path = "/path/to/publisher-key.json" + +# Duration between price updates (defaults to 200ms if not specified) +# publish_interval_duration = "200ms" diff --git a/integration-tests/Dockerfile b/integration-tests/Dockerfile index d674e64..476850d 100644 --- a/integration-tests/Dockerfile +++ b/integration-tests/Dockerfile @@ -12,7 +12,7 @@ ENV PATH="${PATH}:/root/.local/bin" RUN poetry config virtualenvs.in-project true # Install Solana Tool Suite -RUN sh -c "$(curl -sSfL https://release.solana.com/v1.14.17/install)" +RUN sh -c "$(curl -sSfL https://release.anza.xyz/v2.2.1/install)" ENV PATH="${PATH}:/root/.local/share/solana/install/active_release/bin" ADD . /agent diff --git a/src/agent/services/lazer_exporter.rs b/src/agent/services/lazer_exporter.rs index 6082d9b..7e0c6c5 100644 --- a/src/agent/services/lazer_exporter.rs +++ b/src/agent/services/lazer_exporter.rs @@ -19,6 +19,7 @@ use { reqwest::Client, serde::Deserialize, std::{ + path::PathBuf, sync::Arc, time::Duration, }, @@ -48,7 +49,7 @@ pub struct Config { pub relayer_urls: Vec, pub publisher_id: u32, pub authorization_token: String, - publisher_secret_key: PublisherSecretKey, + pub publish_keypair_path: PathBuf, #[serde(with = "humantime_serde", default = "default_publish_interval")] pub publish_interval_duration: Duration, } @@ -62,7 +63,7 @@ impl std::fmt::Debug for PublisherSecretKey { } fn default_publish_interval() -> Duration { - Duration::from_millis(10) + Duration::from_millis(200) } struct RelayerSender { @@ -85,13 +86,14 @@ impl RelayerSender { } async fn connect_to_relayer( - url: &Url, + mut url: Url, token: &str, ) -> Result<( SplitSink>, TungsteniteMessage>, SplitStream>>, )> { tracing::info!("connecting to the relayer at {}", url); + url.set_path("/v1/transaction"); let mut req = url.clone().into_client_request()?; let headers = req.headers_mut(); headers.insert( @@ -112,7 +114,7 @@ async fn connect_to_relayers( let mut relayer_receivers = Vec::new(); for url in config.relayer_urls.clone() { let (relayer_sender, relayer_receiver) = - connect_to_relayer(&url, &config.authorization_token).await?; + connect_to_relayer(url, &config.authorization_token).await?; relayer_senders.push(relayer_sender); relayer_receivers.push(relayer_receiver); } @@ -172,7 +174,10 @@ mod lazer_exporter { }, state::local::LocalStore, }, - anyhow::bail, + anyhow::{ + Context, + bail, + }, ed25519_dalek::{ Signer, SigningKey, @@ -197,6 +202,7 @@ mod lazer_exporter { lazer_transaction::Payload, }, }, + solana_sdk::signer::keypair, std::{ collections::HashMap, sync::Arc, @@ -259,7 +265,21 @@ mod lazer_exporter { stream_map.insert(config.relayer_urls[i].clone(), receiver); } - let signing_key = SigningKey::from_bytes(&config.publisher_secret_key.0); + // Read the keypair from the file using Solana SDK because it's the same key used by the Pythnet publisher + let publish_keypair = match keypair::read_keypair_file(&config.publish_keypair_path) { + Ok(k) => k, + Err(e) => { + tracing::error!( + error = ?e, + publish_keypair_path = config.publish_keypair_path.display().to_string(), + "Reading publish keypair returned an error. ", + ); + bail!("Reading publish keypair returned an error. "); + } + }; + + let signing_key = SigningKey::from_keypair_bytes(&publish_keypair.to_bytes()) + .context("Failed to create signing key from keypair")?; let mut publish_interval = tokio::time::interval(config.publish_interval_duration); loop {