use ethers::{ abi::AbiEncode, types::{Address, U256}, }; use eyre::Result; use serde::{Deserialize, Serialize}; use std::{fmt::Display, net::SocketAddr, str::FromStr, sync::Arc}; use jsonrpsee::{ core::{async_trait, Error}, http_server::{HttpServerBuilder, HttpServerHandle}, proc_macros::rpc, }; use common::utils::{hex_str_to_bytes, u64_to_hex_string}; use super::Client; pub struct Rpc { client: Arc, handle: Option, } impl Rpc { pub fn new(client: Arc) -> Self { Rpc { client, handle: None, } } pub async fn start(&mut self) -> Result { let rpc_inner = RpcInner { client: self.client.clone(), }; let (handle, addr) = start(rpc_inner).await?; self.handle = Some(handle); Ok(addr) } } #[rpc(client, server, namespace = "eth")] trait EthRpc { #[method(name = "getBalance")] async fn get_balance(&self, address: &str, block: &str) -> Result; #[method(name = "getTransactionCount")] async fn get_transaction_count(&self, address: &str, block: &str) -> Result; #[method(name = "getCode")] async fn get_code(&self, address: &str, block: &str) -> Result; #[method(name = "call")] async fn call(&self, opts: CallOpts, block: &str) -> Result; #[method(name = "estimateGas")] async fn estimate_gas(&self, opts: CallOpts) -> Result; #[method(name = "chainId")] fn chain_id(&self) -> Result; #[method(name = "gasPrice")] async fn gas_price(&self) -> Result; #[method(name = "maxPriorityFeePerGas")] async fn max_priority_fee_per_gas(&self) -> Result; #[method(name = "blockNumber")] async fn block_number(&self) -> Result; } struct RpcInner { pub client: Arc, } #[async_trait] impl EthRpcServer for RpcInner { async fn get_balance(&self, address: &str, block: &str) -> Result { match block { "latest" => { let address = convert_err(Address::from_str(address))?; let balance = convert_err(self.client.get_balance(&address).await)?; Ok(balance.encode_hex()) } _ => Err(Error::Custom("Invalid Block Number".to_string())), } } async fn get_transaction_count(&self, address: &str, block: &str) -> Result { match block { "latest" => { let address = convert_err(Address::from_str(address))?; let nonce = convert_err(self.client.get_nonce(&address).await)?; Ok(nonce.encode_hex()) } _ => Err(Error::Custom("Invalid Block Number".to_string())), } } async fn get_code(&self, address: &str, block: &str) -> Result { match block { "latest" => { let address = convert_err(Address::from_str(address))?; let code = convert_err(self.client.get_code(&address).await)?; Ok(hex::encode(code)) } _ => Err(Error::Custom("Invalid Block Number".to_string())), } } async fn call(&self, opts: CallOpts, block: &str) -> Result { match block { "latest" => { let to = convert_err(Address::from_str(&opts.to))?; let data = convert_err(hex_str_to_bytes(&opts.data.unwrap_or("0x".to_string())))?; let value = convert_err(U256::from_str_radix( &opts.value.unwrap_or("0x0".to_string()), 16, ))?; let res = convert_err(self.client.call(&to, &data, value).await)?; Ok(hex::encode(res)) } _ => Err(Error::Custom("Invalid Block Number".to_string())), } } async fn estimate_gas(&self, opts: CallOpts) -> Result { let to = convert_err(Address::from_str(&opts.to))?; let data = convert_err(hex_str_to_bytes(&opts.data.unwrap_or("0x".to_string())))?; let value = convert_err(U256::from_str_radix( &opts.value.unwrap_or("0x0".to_string()), 16, ))?; let gas = convert_err(self.client.estimate_gas(&to, &data, value).await)?; Ok(u64_to_hex_string(gas)) } fn chain_id(&self) -> Result { let id = self.client.chain_id(); Ok(u64_to_hex_string(id)) } async fn gas_price(&self) -> Result { let gas_price = convert_err(self.client.get_gas_price().await)?; Ok(gas_price.encode_hex()) } async fn max_priority_fee_per_gas(&self) -> Result { let tip = convert_err(self.client.get_priority_fee().await)?; Ok(tip.encode_hex()) } async fn block_number(&self) -> Result { let num = convert_err(self.client.get_block_number().await)?; Ok(u64_to_hex_string(num)) } } async fn start(rpc: RpcInner) -> Result<(HttpServerHandle, SocketAddr)> { let server = HttpServerBuilder::default().build("127.0.0.1:8545").await?; let addr = server.local_addr()?; let handle = server.start(rpc.into_rpc())?; Ok((handle, addr)) } fn convert_err(res: Result) -> Result { res.map_err(|err| Error::Custom(err.to_string())) } #[derive(Deserialize, Serialize)] pub struct CallOpts { from: Option, to: String, gas: Option, value: Option, data: Option, }