openzeppelin_monitor/services/blockchain/transports/evm/http.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
//! EVM transport implementation for blockchain interactions.
//!
//! This module provides a client implementation for interacting with EVM-compatible nodes
//! by wrapping the HttpTransportClient. This allows for consistent behavior with other
//! transport implementations while providing specific EVM-focused functionality.
use reqwest_middleware::ClientWithMiddleware;
use reqwest_retry::policies::ExponentialBackoff;
use serde::Serialize;
use serde_json::Value;
use crate::{
models::Network,
services::blockchain::transports::{
BlockchainTransport, HttpTransportClient, RotatingTransport, TransientErrorRetryStrategy,
},
};
/// A client for interacting with EVM-compatible blockchain nodes
///
/// This implementation wraps the HttpTransportClient to provide consistent
/// behavior with other transport implementations while offering EVM-specific
/// functionality. It handles connection management, request retries, and
/// endpoint rotation for EVM-based networks.
#[derive(Clone, Debug)]
pub struct EVMTransportClient {
/// The underlying HTTP transport client that handles actual RPC communications
pub http_client: HttpTransportClient,
}
impl EVMTransportClient {
/// Creates a new EVM transport client by initializing an HTTP transport client
///
/// # Arguments
/// * `network` - Network configuration containing RPC URLs and other network details
///
/// # Returns
/// * `Result<Self, anyhow::Error>` - A new client instance or connection error
pub async fn new(network: &Network) -> Result<Self, anyhow::Error> {
let test_connection_payload =
Some(r#"{"id":1,"jsonrpc":"2.0","method":"net_version","params":[]}"#.to_string());
let http_client = HttpTransportClient::new(network, test_connection_payload).await?;
Ok(Self { http_client })
}
}
#[async_trait::async_trait]
impl BlockchainTransport for EVMTransportClient {
/// Gets the current active RPC URL
///
/// # Returns
/// * `String` - The currently active RPC endpoint URL
async fn get_current_url(&self) -> String {
self.http_client.get_current_url().await
}
/// Sends a raw JSON-RPC request to the EVM node
///
/// # Arguments
/// * `method` - The JSON-RPC method to call
/// * `params` - Optional parameters to pass with the request
///
/// # Returns
/// * `Result<Value, anyhow::Error>` - The JSON response or error
async fn send_raw_request<P>(
&self,
method: &str,
params: Option<P>,
) -> Result<Value, anyhow::Error>
where
P: Into<Value> + Send + Clone + Serialize,
{
self.http_client.send_raw_request(method, params).await
}
/// Sets a new retry policy for the transport
///
/// # Arguments
/// * `retry_policy` - The new retry policy to use
/// * `retry_strategy` - The new retry strategy to use
///
/// # Returns
/// * `Result<(), anyhow::Error>` - Success or error status
fn set_retry_policy(
&mut self,
retry_policy: ExponentialBackoff,
retry_strategy: Option<TransientErrorRetryStrategy>,
) -> Result<(), anyhow::Error> {
self.http_client
.set_retry_policy(retry_policy, retry_strategy)?;
Ok(())
}
/// Update endpoint manager with a new client
///
/// # Arguments
/// * `client` - The new client to use for the endpoint manager
fn update_endpoint_manager_client(
&mut self,
client: ClientWithMiddleware,
) -> Result<(), anyhow::Error> {
self.http_client.update_endpoint_manager_client(client)
}
}
#[async_trait::async_trait]
impl RotatingTransport for EVMTransportClient {
/// Tests connection to a specific URL
///
/// # Arguments
/// * `url` - The URL to test connection with
///
/// # Returns
/// * `Result<(), anyhow::Error>` - Success or error status
async fn try_connect(&self, url: &str) -> Result<(), anyhow::Error> {
self.http_client.try_connect(url).await
}
/// Updates the client to use a new URL
///
/// # Arguments
/// * `url` - The new URL to use for subsequent requests
///
/// # Returns
/// * `Result<(), anyhow::Error>` - Success or error status
async fn update_client(&self, url: &str) -> Result<(), anyhow::Error> {
self.http_client.update_client(url).await
}
}