use bytes::Bytes;
use ethereum_types::{Address, U256};
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use url::Url;
use crate::{config::SecretUrl, errors::EthClientError};
#[derive(Debug, Serialize, Deserialize)]
#[serde(untagged)]
pub(crate) enum RpcRequestId {
Number(u64),
String(String),
}
#[derive(Serialize, Deserialize, Debug)]
pub(crate) struct RpcSuccessResponse {
pub(crate) id: RpcRequestId,
pub(crate) jsonrpc: String,
pub(crate) result: Value,
}
#[derive(Serialize, Deserialize, Debug)]
pub(crate) struct RpcErrorMetadata {
pub(crate) code: i32,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) data: Option<String>,
pub(crate) message: String,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct RpcErrorResponse {
pub(crate) id: RpcRequestId,
pub(crate) jsonrpc: String,
pub(crate) error: RpcErrorMetadata,
}
impl std::fmt::Display for RpcErrorResponse {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "RpcErrorResponse: {:?}", self)
}
}
#[derive(Deserialize, Debug)]
#[serde(untagged)]
pub(crate) enum RpcResponse {
Success(RpcSuccessResponse),
Error(RpcErrorResponse),
}
#[derive(Serialize, Deserialize, Debug)]
pub(crate) struct RpcRequest {
pub(crate) id: RpcRequestId,
pub(crate) jsonrpc: String,
pub(crate) method: String,
pub(crate) params: Option<Vec<Value>>,
}
#[derive(Debug, Clone)]
pub(crate) struct EthClient {
client: reqwest::Client,
pub(crate) url: SecretUrl,
pub(crate) svc_manager_addr: Address,
}
impl EthClient {
pub(crate) fn new(url: SecretUrl, svc_manager_addr: Address) -> Self {
Self {
client: reqwest::Client::new(),
url,
svc_manager_addr,
}
}
async fn send_request(&self, request: RpcRequest) -> Result<RpcResponse, EthClientError> {
let url: Url = self.url.clone().into();
self.client
.post(url)
.header("content-type", "application/json")
.body(serde_json::ser::to_string(&request).map_err(EthClientError::SerdeJSON)?)
.send()
.await?
.json::<RpcResponse>()
.await
.map_err(EthClientError::from)
}
pub(crate) async fn get_block_number(&self) -> Result<U256, EthClientError> {
let request = RpcRequest {
id: RpcRequestId::Number(1),
jsonrpc: "2.0".to_string(),
method: "eth_blockNumber".to_string(),
params: None,
};
match self.send_request(request).await {
Ok(RpcResponse::Success(result)) => {
serde_json::from_value(result.result).map_err(EthClientError::SerdeJSON)
}
Ok(RpcResponse::Error(error_response)) => Err(EthClientError::Rpc(error_response)),
Err(error) => Err(error),
}
}
pub(crate) async fn call(
&self,
to: Address,
calldata: Bytes,
block: Option<u64>,
) -> Result<String, EthClientError> {
let request = RpcRequest {
id: RpcRequestId::Number(1),
jsonrpc: "2.0".to_string(),
method: "eth_call".to_string(),
params: Some(vec![
json!({
"to": format!("{:#x}",to),
"input": format!("0x{:#x}", calldata),
"value": format!("{:#x}", 0),
"from": format!("{:#x}", Address::zero()),
}),
json!(match block {
Some(block) => format!("{:#x}", block),
None => "latest".to_string(),
}),
]),
};
match self.send_request(request).await {
Ok(RpcResponse::Success(result)) => {
serde_json::from_value(result.result).map_err(EthClientError::SerdeJSON)
}
Ok(RpcResponse::Error(error_response)) => Err(EthClientError::Rpc(error_response)),
Err(error) => Err(error),
}
}
}