use crate::client::{Client, ClientType, CryptoClient};
use crate::types::{Amount, Currency, Symbol, WalletBalance};
use crate::types::{ClientOptions, SyncItem, SyncStats};
use crate::Result;
use async_trait::async_trait;
use num_bigint::BigInt;
use rpc_json_client::{ClientBuilder, RpcClient};
use serde_json::json;
use std::sync::Arc;
#[derive(Clone)]
pub struct EthereumRpcClient {
client: Arc<RpcClient>,
}
pub fn get_client(client_type: ClientType, options: ClientOptions) -> crate::Result<Box<Client>> {
match client_type {
ClientType::RPC => Ok(Box::new(EthereumRpcClient::new(options))),
_ => Err(crate::error::ClientError::UnsupportedType(client_type)),
}
}
impl EthereumRpcClient {
pub fn new(options: ClientOptions) -> Self {
let client = ClientBuilder::new(&options.url).with_retry().build();
EthereumRpcClient {
client: Arc::new(client),
}
}
}
#[async_trait]
impl CryptoClient for EthereumRpcClient {
async fn sync_stats(&self) -> SyncStats {
let result: serde_json::Value = match self.client.execute("eth_syncing", &[]).await {
Ok(r) => r,
Err(_) => {
async_std::task::sleep(std::time::Duration::from_secs(60)).await;
return SyncStats {
current_block: 0,
syncing: true,
sync_item: SyncItem::Block,
estimated_sync_item_remaining: 0.0,
};
}
};
if let Some(syncing) = result.as_bool() {
let result: serde_json::Value =
self.client.execute("eth_blockNumber", &[]).await.unwrap();
let block =
u64::from_str_radix(result.as_str().unwrap().trim_start_matches("0x"), 16).unwrap();
if syncing == false {
return SyncStats {
current_block: block,
syncing: false,
sync_item: SyncItem::Block,
estimated_sync_item_remaining: 0.0,
};
}
}
dbg!(&result);
let current_block = u64::from_str_radix(
result
.as_object()
.unwrap()
.get("currentBlock")
.unwrap()
.as_str()
.unwrap()
.trim_start_matches("0x"),
16,
)
.unwrap();
let tip = u64::from_str_radix(
result
.as_object()
.unwrap()
.get("highestBlock")
.unwrap()
.as_str()
.unwrap()
.trim_start_matches("0x"),
16,
)
.unwrap();
SyncStats {
current_block,
sync_item: SyncItem::Block,
estimated_sync_item_remaining: (tip - current_block) as f64,
syncing: true,
}
}
async fn wallet_balance(&self, identifier: &str) -> Result<WalletBalance> {
let balance_hex: String = self
.client
.execute("eth_getBalance", &[json!(identifier), json!("latest")])
.await?;
let confirmed_balance =
BigInt::parse_bytes(balance_hex.trim_start_matches("0x").as_bytes(), 16).unwrap();
let unconfirmed_balance_hex: String = self
.client
.execute("eth_getBalance", &[json!(identifier), json!("pending")])
.await?;
let unconfirmed_balance = BigInt::parse_bytes(
unconfirmed_balance_hex.trim_start_matches("0x").as_bytes(),
16,
)
.unwrap();
Ok(WalletBalance {
confirmed_balance: Amount {
value: confirmed_balance,
currency: Currency {
symbol: Symbol::ETH,
decimals: 18,
},
},
unconfirmed_balance: Amount {
value: unconfirmed_balance,
currency: Currency {
symbol: Symbol::ETH,
decimals: 18,
},
},
})
}
fn client_type(&self) -> ClientType {
ClientType::RPC
}
}