use crate::jsonrpc::error::Web3Error;
use crate::types::{Block, Log, NewFilter, SyncingStatus, TransactionRequest, TransactionResponse};
use crate::types::{ConciseBlock, TransactionReceipt};
use clarity::rlp::downcast_u64;
use clarity::Address;
use num256::Uint256;
use std::time::Duration;
use std::time::Instant;
use super::core::Web3;
impl Web3 {
pub async fn eth_accounts(&self) -> Result<Vec<Address>, Web3Error> {
self.jsonrpc_client
.request_method("eth_accounts", Vec::<String>::new(), self.timeout)
.await
}
pub async fn eth_chainid(&self) -> Result<u64, Web3Error> {
let ret: Result<Uint256, Web3Error> = self
.jsonrpc_client
.request_method("eth_chainId", Vec::<String>::new(), self.timeout)
.await;
let value = ret?;
Ok(downcast_u64(value)?)
}
pub async fn eth_get_logs(&self, new_filter: NewFilter) -> Result<Vec<Log>, Web3Error> {
self.jsonrpc_client
.request_method("eth_getLogs", vec![new_filter], self.timeout)
.await
}
pub async fn eth_get_transaction_count(&self, address: Address) -> Result<Uint256, Web3Error> {
match self.eth_syncing().await? {
false => {
self.jsonrpc_client
.request_method(
"eth_getTransactionCount",
vec![address.to_string(), "latest".to_string()],
self.timeout,
)
.await
}
true => Err(Web3Error::SyncingNode(
"Cannot perform eth_getTransactionCount".to_string(),
)),
}
}
pub async fn eth_gas_price(&self) -> Result<Uint256, Web3Error> {
match self.eth_syncing().await? {
false => {
let median_gas = self
.jsonrpc_client
.request_method("eth_gasPrice", Vec::<String>::new(), self.timeout)
.await?;
if let Some(gas) = self.get_base_fee_per_gas().await? {
if median_gas < gas {
Ok(gas)
} else {
Ok(median_gas)
}
} else {
Ok(median_gas)
}
}
_ => Err(Web3Error::SyncingNode(
"Cannot perform eth_gas_price".to_string(),
)),
}
}
pub async fn eth_estimate_gas(
&self,
mut transaction: TransactionRequest,
) -> Result<Uint256, Web3Error> {
if let Ok(true) = self.eth_syncing().await {
warn!("Eth Node is still syncing, request may not work if block is not synced");
}
let nonce = self
.eth_get_transaction_count(transaction.get_from())
.await?;
let balance = self.eth_get_balance(transaction.get_from()).await?;
let gas = self.simulated_gas_price_and_limit(balance).await?;
transaction.set_nonce(nonce);
transaction.set_gas_limit(gas.limit);
transaction.set_gas_price(gas.price);
self.jsonrpc_client
.request_method("eth_estimateGas", vec![transaction], self.timeout)
.await
}
pub async fn eth_get_balance(&self, address: Address) -> Result<Uint256, Web3Error> {
match self.eth_syncing().await? {
false => {
self.jsonrpc_client
.request_method(
"eth_getBalance",
vec![address.to_string(), "latest".to_string()],
self.timeout,
)
.await
}
true => Err(Web3Error::SyncingNode(
"Cannot perform eth_getBalance".to_string(),
)),
}
}
pub async fn eth_syncing(&self) -> Result<bool, Web3Error> {
let res: SyncingStatus = self
.jsonrpc_client
.request_method("eth_syncing", Vec::<String>::new(), self.timeout)
.await?;
match res {
SyncingStatus::Syncing { .. } => Ok(true),
SyncingStatus::NotSyncing(..) => Ok(false),
}
}
pub async fn eth_get_code(
&self,
address: Address,
height: Option<Uint256>,
) -> Result<Vec<u8>, Web3Error> {
let height = match height {
Some(h) => format!("{h:#x}"),
None => "latest".to_string(),
};
let res: String = self
.jsonrpc_client
.request_method(
"eth_getCode",
vec![address.to_string(), height],
self.timeout,
)
.await?;
let res = clarity::utils::hex_str_to_bytes(&res)?;
Ok(res)
}
pub async fn eth_synced_block_number(&self) -> Result<Uint256, Web3Error> {
self.jsonrpc_client
.request_method("eth_blockNumber", Vec::<String>::new(), self.timeout)
.await
}
pub async fn eth_block_number(&self) -> Result<Uint256, Web3Error> {
match self.eth_syncing().await? {
false => self.eth_synced_block_number().await,
true => Err(Web3Error::SyncingNode(
"Cannot perform eth_block_number".to_string(),
)),
}
}
pub async fn eth_get_block_by_number(&self, block_number: Uint256) -> Result<Block, Web3Error> {
let latest_known_block = self.eth_synced_block_number().await?;
if block_number <= latest_known_block {
self.jsonrpc_client
.request_method(
"eth_getBlockByNumber",
(format!("{block_number:#x}"), true),
self.timeout,
)
.await
} else if self.eth_syncing().await? {
Err(Web3Error::SyncingNode(
"Cannot perform eth_get_block_by_number".to_string(),
))
} else {
Err(Web3Error::BadInput(
"Cannot perform eth_get_block_by_number, block number invalid".to_string(),
))
}
}
pub async fn eth_get_concise_block_by_number(
&self,
block_number: Uint256,
) -> Result<ConciseBlock, Web3Error> {
let latest_known_block = self.eth_synced_block_number().await?;
if block_number <= latest_known_block {
self.jsonrpc_client
.request_method(
"eth_getBlockByNumber",
(format!("{block_number:#x}"), false),
self.timeout,
)
.await
} else if self.eth_syncing().await? {
Err(Web3Error::SyncingNode(
"Cannot perform eth_get_concise_block_by_number".to_string(),
))
} else {
Err(Web3Error::BadInput(
"Cannot perform eth_get_concise_block_by_number, block number invalid".to_string(),
))
}
}
pub async fn eth_get_latest_block(&self) -> Result<ConciseBlock, Web3Error> {
match self.eth_syncing().await? {
false => {
self.jsonrpc_client
.request_method("eth_getBlockByNumber", ("latest", false), self.timeout)
.await
}
_ => Err(Web3Error::SyncingNode(
"Cannot perform eth_get_latest_block".to_string(),
)),
}
}
pub async fn eth_get_latest_block_full(&self) -> Result<Block, Web3Error> {
match self.eth_syncing().await? {
false => {
self.jsonrpc_client
.request_method("eth_getBlockByNumber", ("latest", true), self.timeout)
.await
}
_ => Err(Web3Error::SyncingNode(
"Cannot perform eth_get_latest_block".to_string(),
)),
}
}
pub async fn eth_get_finalized_block(&self) -> Result<ConciseBlock, Web3Error> {
match self.eth_syncing().await? {
false => {
self.jsonrpc_client
.request_method("eth_getBlockByNumber", ("finalized", false), self.timeout)
.await
}
_ => Err(Web3Error::SyncingNode(
"Cannot perform eth_get_latest_block".to_string(),
)),
}
}
pub async fn eth_get_finalized_block_full(&self) -> Result<Block, Web3Error> {
match self.eth_syncing().await? {
false => {
self.jsonrpc_client
.request_method("eth_getBlockByNumber", ("finalized", true), self.timeout)
.await
}
_ => Err(Web3Error::SyncingNode(
"Cannot perform eth_get_latest_block".to_string(),
)),
}
}
pub async fn eth_get_transaction_by_hash(
&self,
hash: Uint256,
) -> Result<Option<TransactionResponse>, Web3Error> {
if let Ok(true) = self.eth_syncing().await {
warn!("Eth node is currently syncing, eth_get_transaction_by_hash may not work if transaction is not synced");
}
self.jsonrpc_client
.request_method(
"eth_getTransactionByHash",
vec![format!("{hash:#066x}")],
self.timeout,
)
.await
}
pub async fn eth_get_transaction_receipt(
&self,
hash: Uint256,
) -> Result<Option<TransactionReceipt>, Web3Error> {
if let Ok(true) = self.eth_syncing().await {
warn!("Eth node is currently syncing, eth_get_transaction_by_receipt may not work if transaction is not synced");
}
self.jsonrpc_client
.request_method(
"eth_getTransactionReceipt",
vec![format!("{hash:#066x}")],
self.timeout,
)
.await
}
pub async fn get_base_fee_per_gas(&self) -> Result<Option<Uint256>, Web3Error> {
match self.eth_get_latest_block().await {
Ok(eth_block) => Ok(eth_block.base_fee_per_gas),
Err(e) => Err(e),
}
}
pub async fn wait_for_next_block(&self, timeout: Duration) -> Result<(), Web3Error> {
let start = Instant::now();
let mut last_height: Option<Uint256> = None;
while Instant::now() - start < timeout {
match (self.eth_block_number().await, last_height) {
(Ok(n), None) => last_height = Some(n),
(Ok(block_height), Some(last_height)) => {
if block_height > last_height {
return Ok(());
}
}
(Err(_), _) => {}
}
}
Err(Web3Error::NoBlockProduced { time: timeout })
}
pub async fn check_if_address_is_contract(&self, address: Address) -> Result<bool, Web3Error> {
match self.eth_get_code(address, None).await {
Ok(code) => Ok(!code.is_empty()),
Err(e) => Err(e),
}
}
}