use std::io::{self, Error, ErrorKind};
use avalanche_types::key;
use ethers::prelude::*;
use tokio::time::Duration;
pub const DEFAULT_GAS: u64 = 21000;
#[derive(Clone, Debug)]
pub struct Tx<T>
where
T: key::secp256k1::ReadOnly + key::secp256k1::SignOnly + Clone,
{
pub inner: crate::wallet::evm::c::C<T>,
pub receiver: H160,
pub amount: U256,
pub gas: U256,
pub gas_price: U256,
pub check_acceptance: bool,
pub poll_initial_wait: Duration,
pub poll_interval: Duration,
pub poll_timeout: Duration,
}
impl<T> Tx<T>
where
T: key::secp256k1::ReadOnly + key::secp256k1::SignOnly + Clone,
{
pub fn new(ev: &crate::wallet::evm::c::C<T>) -> Self {
Self {
inner: ev.clone(),
receiver: H160::zero(),
amount: U256::from(0),
gas: U256::from(DEFAULT_GAS),
gas_price: U256::from(0),
check_acceptance: false,
poll_initial_wait: Duration::from_millis(500),
poll_interval: Duration::from_millis(700),
poll_timeout: Duration::from_secs(300),
}
}
#[must_use]
pub fn receiver(mut self, receiver: H160) -> Self {
self.receiver = receiver;
self
}
#[must_use]
pub fn amount(mut self, amount: U256) -> Self {
self.amount = amount;
self
}
#[must_use]
pub fn gas(mut self, gas: U256) -> Self {
self.gas = gas;
self
}
#[must_use]
pub fn gas_u64(mut self, gas: u64) -> Self {
self.gas = U256::from(gas);
self
}
#[must_use]
pub fn gas_price(mut self, gas_price: U256) -> Self {
self.gas_price = gas_price;
self
}
#[must_use]
pub fn gas_price_u64(mut self, gas_price: u64) -> Self {
self.gas_price = U256::from(gas_price);
self
}
#[must_use]
pub fn check_acceptance(mut self, check_acceptance: bool) -> Self {
self.check_acceptance = check_acceptance;
self
}
#[must_use]
pub fn poll_initial_wait(mut self, poll_initial_wait: Duration) -> Self {
self.poll_initial_wait = poll_initial_wait;
self
}
#[must_use]
pub fn poll_interval(mut self, poll_interval: Duration) -> Self {
self.poll_interval = poll_interval;
self
}
#[must_use]
pub fn poll_timeout(mut self, poll_timeout: Duration) -> Self {
self.poll_timeout = poll_timeout;
self
}
pub async fn issue(&self) -> io::Result<H256> {
let picked_http_rpc = self.inner.inner.pick_http_rpc();
log::info!(
"transferring C-chain {} AVAX from {} to {} via {}",
self.amount,
self.inner.inner.h160_address,
self.receiver,
picked_http_rpc.1
);
let latest_nonce = self.inner.inner.c_providers[picked_http_rpc.0]
.get_transaction_count(
self.inner.inner.h160_address,
Some(BlockNumber::Latest.into()),
)
.await
.map_err(|e| {
Error::new(
ErrorKind::Other,
format!("failed get_transaction_count '{}'", e),
)
})?;
log::debug!("latest nonce for sender: {}", latest_nonce);
let mut tx_request = TransactionRequest::new()
.from(self.inner.inner.h160_address)
.to(self.receiver)
.value(self.amount)
.chain_id(self.inner.inner.c_chain_id_u256.as_u64())
.nonce(latest_nonce);
if !self.gas.is_zero() {
tx_request = tx_request.gas(self.gas)
}
if !self.gas_price.is_zero() {
tx_request = tx_request.gas_price(self.gas_price)
}
let signer = SignerMiddleware::new(
self.inner.inner.c_providers[picked_http_rpc.0].clone(),
self.inner
.inner
.local_wallet
.clone()
.with_chain_id(self.inner.inner.c_chain_id_u256.as_u64()),
);
let pending_tx = signer
.send_transaction(tx_request, None)
.await
.map_err(|e| {
Error::new(
ErrorKind::Other,
format!("failed to send_transaction '{}'", e),
)
})?;
let tx_receipt = pending_tx.await.map_err(|e| {
Error::new(
ErrorKind::Other,
format!("failed to wait for pending tx '{}'", e),
)
})?;
if tx_receipt.is_none() {
return Err(Error::new(ErrorKind::Other, "tx dropped from mempool"));
}
let receipt = tx_receipt.unwrap();
let tx = signer
.get_transaction(receipt.transaction_hash)
.await
.map_err(|e| Error::new(ErrorKind::Other, format!("failed get_transaction '{}'", e)))?;
if let Some(v) = &tx {
log::info!("{} successfully issued", v.hash());
} else {
log::warn!("transaction not found in get_transaction");
}
if !self.check_acceptance {
log::debug!("skipping checking acceptance...");
return Ok(receipt.transaction_hash);
}
Ok(receipt.transaction_hash)
}
}