avalanche-sdk 0.43.0

Avalanche API/SDK
Documentation
use std::io::{self, Error, ErrorKind};

use avalanche_types::key;
use ethers::prelude::*;
use ethers_providers::{Middleware, Provider};

impl<T> crate::wallet::Wallet<T>
where
    T: key::ReadOnly + key::SignOnly + Clone,
{
    pub async fn transfer_c(
        &self,
        http_rpc: Option<String>,
        receiver_h160_addr: H160,
        amount_to_transfer: U256,
        gas: Option<u64>,
        gas_price: Option<u64>,
        check_acceptance: bool,
    ) -> io::Result<()> {
        let (http_rpc_ep, c_provider) = if let Some(ep) = &http_rpc {
            let c_provider =
                Provider::<Http>::try_from(ep.to_string() + "/ext/bc/C/rpc").map_err(|e| {
                    Error::new(ErrorKind::Other, format!("failed to create c_ '{}'", e))
                })?;
            (ep.to_string(), c_provider)
        } else {
            (self.http_rpc.clone(), self.c_provider.clone())
        };

        let sender_h160_addr = self.keychain.keys[0].get_h160_address();
        log::info!(
            "transferring {} from {} to {} via {}",
            amount_to_transfer,
            sender_h160_addr,
            receiver_h160_addr,
            http_rpc_ep
        );

        let latest_nonce = c_provider
            .get_transaction_count(self.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);

        // TODO: support "GasFeeCap" and "GasTipCap"
        let gas_u256 = if let Some(v) = gas {
            U256::from(v)
        } else {
            U256::from(21000) // default
        };
        let mut tx_request = TransactionRequest::new()
            .from(self.h160_address)
            .to(receiver_h160_addr)
            .value(amount_to_transfer)
            .gas(gas_u256)
            .chain_id(self.c_chain_id_u256.as_u64())
            .nonce(latest_nonce);
        if let Some(gas_price) = gas_price {
            // set this to avoid
            // "transaction underpriced: address 0xabc have gas tip cap (0) < pool gas tip cap (225000000000)"
            tx_request = tx_request.gas_price(U256::from(gas_price))
        }

        let signer = SignerMiddleware::new(
            c_provider.clone(),
            self.local_wallet
                .clone()
                .with_chain_id(self.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)))?;
        // serde_json::to_string(&tx).unwrap()
        if let Some(v) = &tx {
            log::info!("{} successfully issued", v.hash());
        }

        if !check_acceptance {
            log::debug!("skipping checking acceptance...");
            return Ok(());
        }

        Ok(())
    }
}