use std::fmt::Debug;
use async_trait::async_trait;
use serde::Serialize;
use crate::{
eip::{eip2718::TypedTransactionRequest, eip712::TypedData},
errors::Result,
prelude::Address,
primitives::{Bytes, Eip1559Signature},
};
#[async_trait]
pub trait Signer {
async fn sign_transaction<T>(
&self,
request: T,
with_account: Option<&Address>,
) -> Result<Bytes>
where
T: TryInto<TypedTransactionRequest> + Send,
T::Error: Debug + Send;
async fn sign_typed_data<V>(
&self,
typed_data: TypedData<V>,
with_account: Option<&Address>,
) -> Result<Eip1559Signature>
where
V: Serialize + Send;
async fn signer_accounts(&self) -> Result<Vec<Address>>;
}
#[cfg(feature = "clients")]
#[cfg_attr(docsrs, doc(cfg(feature = "clients")))]
mod with_client {
use reweb3_num::types::U256;
use crate::{
clients::{
Block, BlockNumberOrTag, Client, ClientExt, FeeHistory, Filter, FilterEvents,
SyncingStatus, Transaction, TransactionReceipt,
},
eip::eip2718::LegacyTransactionRequest,
errors::Error,
prelude::keccak256,
primitives::{balance::TransferOptions, Address, Bytes, H256},
};
use super::*;
#[async_trait]
pub trait SignerWithProvider: Signer + ClientExt + Send + Sync {
async fn deploy_contract(
&self,
bytecode: &str,
signature: &str,
mut call_data: Bytes,
ops: TransferOptions,
) -> Result<H256> {
let mut bytecode: Bytes = bytecode.parse()?;
bytecode.0.append(&mut call_data.0);
self.send_raw_transaction(signature, None, call_data, ops)
.await
}
async fn call_contract_with_transaction(
&self,
signature: &str,
contract_address: &Address,
call_data: Bytes,
ops: TransferOptions,
) -> Result<H256> {
self.send_raw_transaction(signature, Some(&contract_address), call_data, ops)
.await
}
async fn send_raw_transaction(
&self,
method_name: &str,
to: Option<&Address>,
mut call_data: Bytes,
ops: TransferOptions,
) -> Result<H256> {
let address = {
let mut accounts = self.signer_accounts().await?;
if accounts.is_empty() {
return Err(Error::SignerAccounts);
}
accounts.remove(0)
};
let nonce = self.eth_get_transaction_count(address.clone()).await?;
log::debug!(
target: method_name,
"Fetch account {} nonce, {}",
address.to_checksum_string(),
nonce
);
let chain_id = self.eth_chainid().await?;
log::debug!(target: method_name, "Fetch chain_id, {}", chain_id);
let call_data = if to.is_some() {
let mut selector_name = keccak256(method_name.as_bytes()).0[0..4].to_vec();
selector_name.append(&mut call_data.0);
selector_name.into()
} else {
call_data
};
let mut tx = LegacyTransactionRequest {
chain_id: Some(chain_id.into()),
nonce: Some(nonce),
to: to.map(|c| c.clone()),
data: Some(call_data),
value: ops.value,
..Default::default()
};
let gas = self
.eth_estimate_gas(tx.clone(), None::<BlockNumberOrTag>)
.await?;
log::debug!(target: method_name, "Fetch estimate gas, {}", gas);
tx.gas = Some(gas);
let gas_price = if let Some(gas_price) = ops.gas_price {
gas_price
} else {
self.eth_gas_price().await?
};
log::debug!(target: method_name, "Fetch gas price, {}", gas_price);
tx.gas_price = Some(gas_price);
log::debug!(
target: method_name,
"Try sign transaction, {}",
serde_json::to_string(&tx)?,
);
let signed_tx = self.sign_transaction(tx, Some(&address)).await?;
log::debug!(
target: method_name,
"Signed transaction, {}",
signed_tx.to_string()
);
let hash = self.eth_send_raw_transaction(signed_tx).await?;
log::debug!(target: method_name, "Send transaction success, {}", hash);
Ok(hash)
}
async fn balance(&self) -> Result<U256> {
let addresses = self.signer_accounts().await?;
if addresses.is_empty() {
return Err(Error::Other("Signer account list is empty".to_string()));
}
Ok(self.eth_get_balance(addresses[0].clone()).await?)
}
}
#[async_trait]
impl<S, C> Signer for (S, C)
where
S: Signer + Sync + Send + Unpin,
C: Client + Sync + Send + Unpin,
{
async fn sign_transaction<T>(
&self,
request: T,
with_account: Option<&Address>,
) -> Result<Bytes>
where
T: TryInto<TypedTransactionRequest> + Send,
T::Error: Debug + Send,
{
self.0.sign_transaction(request, with_account).await
}
async fn sign_typed_data<V>(
&self,
typed_data: TypedData<V>,
with_account: Option<&Address>,
) -> Result<Eip1559Signature>
where
V: Serialize + Send,
{
self.0.sign_typed_data(typed_data, with_account).await
}
async fn signer_accounts(&self) -> Result<Vec<Address>> {
self.0.signer_accounts().await
}
}
#[async_trait]
impl<S, C> Client for (S, C)
where
S: Signer + Sync + Send + Unpin,
C: Client + Sync + Send + Unpin,
{
async fn eth_blocknumber(&self) -> Result<U256> {
self.1.eth_blocknumber().await
}
async fn eth_chainid(&self) -> Result<u64> {
self.1.eth_chainid().await
}
async fn eth_getblockbyhash<H>(&self, hash: H, hydrated: bool) -> Result<Option<Block>>
where
H: TryInto<H256> + Send,
H::Error: Debug + Send,
{
self.1.eth_getblockbyhash(hash, hydrated).await
}
async fn eth_getblockbynumber<N>(
&self,
number_or_tag: N,
hydrated: bool,
) -> Result<Option<Block>>
where
N: TryInto<BlockNumberOrTag> + Send,
N::Error: Debug + Send,
{
self.1.eth_getblockbynumber(number_or_tag, hydrated).await
}
async fn eth_get_block_transaction_count_by_hash<H>(&self, hash: H) -> Result<u64>
where
H: TryInto<H256> + Send,
H::Error: Debug + Send,
{
self.1.eth_get_block_transaction_count_by_hash(hash).await
}
async fn eth_get_uncle_count_by_block_hash<H>(&self, hash: H) -> Result<u64>
where
H: TryInto<H256> + Send,
H::Error: Debug + Send,
{
self.1.eth_get_uncle_count_by_block_hash(hash).await
}
async fn eth_get_uncle_count_by_block_number<N>(&self, number_or_tag: N) -> Result<u64>
where
N: TryInto<BlockNumberOrTag> + Send,
N::Error: Debug + Send,
{
self.1
.eth_get_uncle_count_by_block_number(number_or_tag)
.await
}
async fn eth_syncing(&self) -> Result<SyncingStatus> {
self.1.eth_syncing().await
}
async fn eth_coinbase(&self) -> Result<Address> {
self.1.eth_coinbase().await
}
async fn eth_accounts(&self) -> Result<Vec<Address>> {
self.1.eth_accounts().await
}
async fn eth_call<TX, BT>(
&self,
transaction: TX,
block_number_or_tag: Option<BT>,
) -> Result<Bytes>
where
TX: TryInto<TypedTransactionRequest> + Send,
TX::Error: Debug + Send,
BT: TryInto<BlockNumberOrTag> + Send,
BT::Error: Debug + Send,
{
self.1.eth_call(transaction, block_number_or_tag).await
}
async fn eth_estimate_gas<TX, BT>(
&self,
transaction: TX,
block_number_or_tag: Option<BT>,
) -> Result<U256>
where
TX: TryInto<TypedTransactionRequest> + Send,
TX::Error: Debug + Send,
BT: TryInto<BlockNumberOrTag> + Send,
BT::Error: Debug + Send,
{
self.1
.eth_estimate_gas(transaction, block_number_or_tag)
.await
}
async fn eth_create_accesslist<TX, BT>(
&self,
transaction: TX,
block_number_or_tag: Option<BT>,
) -> Result<U256>
where
TX: TryInto<Transaction> + Send,
TX::Error: Debug + Send,
BT: TryInto<BlockNumberOrTag> + Send,
BT::Error: Debug + Send,
{
self.1
.eth_create_accesslist(transaction, block_number_or_tag)
.await
}
async fn eth_gas_price(&self) -> Result<U256> {
self.1.eth_gas_price().await
}
async fn eth_max_priority_fee_per_gas(&self) -> Result<U256> {
self.1.eth_max_priority_fee_per_gas().await
}
async fn eth_fee_history<N, BT, RP>(
&self,
block_count: N,
newest_block: BT,
reward_percentiles: RP,
) -> Result<FeeHistory>
where
N: TryInto<U256> + Send,
N::Error: Debug + Send,
BT: TryInto<BlockNumberOrTag> + Send,
BT::Error: Debug + Send,
RP: AsRef<[f64]> + Send,
{
self.1
.eth_fee_history(block_count, newest_block, reward_percentiles)
.await
}
async fn eth_new_filter<F>(&self, filter: F) -> Result<U256>
where
F: TryInto<Filter> + Send,
F::Error: Debug + Send,
{
self.1.eth_new_filter(filter).await
}
async fn eth_new_block_filter(&self) -> Result<U256> {
self.1.eth_new_block_filter().await
}
async fn eth_new_pending_transaction_filter(&self) -> Result<U256> {
self.1.eth_new_pending_transaction_filter().await
}
async fn eth_uninstall_filter<N>(&self, id: N) -> Result<bool>
where
N: TryInto<U256> + Send,
N::Error: Debug + Send,
{
self.1.eth_uninstall_filter(id).await
}
async fn eth_get_filter_changes<N>(&self, id: N) -> Result<Option<FilterEvents>>
where
N: TryInto<U256> + Send,
N::Error: Debug + Send,
{
self.1.eth_get_filter_changes(id).await
}
async fn eth_get_filter_logs<N>(&self, id: N) -> Result<FilterEvents>
where
N: TryInto<U256> + Send,
N::Error: Debug + Send,
{
self.1.eth_get_filter_logs(id).await
}
async fn eth_get_logs<F>(&self, filter: F) -> Result<FilterEvents>
where
F: TryInto<Filter> + Send,
F::Error: Debug + Send,
{
self.1.eth_get_logs(filter).await
}
async fn eth_sign_transaction<T>(&self, transaction: T) -> Result<Bytes>
where
T: TryInto<Transaction> + Send,
T::Error: Debug + Send,
{
self.1.eth_sign_transaction(transaction).await
}
async fn eth_get_balance<A>(&self, address: A) -> Result<U256>
where
A: TryInto<Address> + Send,
A::Error: Debug + Send,
{
self.1.eth_get_balance(address).await
}
async fn eth_get_transaction_count<A>(&self, address: A) -> Result<U256>
where
A: TryInto<Address> + Send,
A::Error: Debug + Send,
{
self.1.eth_get_transaction_count(address).await
}
async fn eth_send_raw_transaction<B>(&self, raw: B) -> Result<H256>
where
B: TryInto<Bytes> + Send,
B::Error: Debug + Send,
{
self.1.eth_send_raw_transaction(raw).await
}
async fn eth_get_transaction_by_hash<H>(&self, tx_hash: H) -> Result<Option<Transaction>>
where
H: TryInto<H256> + Send,
H::Error: Debug + Send,
{
self.1.eth_get_transaction_by_hash(tx_hash).await
}
async fn eth_get_transaction_receipt<H>(
&self,
tx_hash: H,
) -> Result<Option<TransactionReceipt>>
where
H: TryInto<H256> + Send,
H::Error: Debug + Send,
{
self.1.eth_get_transaction_receipt(tx_hash).await
}
}
}
#[cfg(feature = "clients")]
pub use with_client::*;