Atla ve ana içeriğe git

Rust ethers-rs: How to send transactions

In this guide we'll look at how to send both a legacy and an EIP-1559 transaction in Rust using the ethers-rs library.

We'll use Sepolia, so make sure to have some test ETH - if you don't, no worries, head over to the MetaMask Developer faucet.

Before jumping into it, whoever is new to Rust make sure to:

  • Install Rust

  • Create a new project with cargo new infura_rs reference)

  • Now let's edit the Cargo.toml file and add these dependencies:

[dependencies]
ethers = "2.0"
eyre = "0.6.8"
hex = "0.4.3"
tokio = { version = "1.28.2", features = ["full"] }
serde_json = "1.0.96"

Edit the src/main.rs file and add the following code:

use ethers::{
core::{types::TransactionRequest},
middleware::SignerMiddleware,
providers::{Http, Middleware, Provider},
signers::{LocalWallet, Signer},
utils,
prelude::*
};
use eyre::Result;
use std::convert::TryFrom;

#[tokio::main]
async fn main() -> Result<()> {

// connect to the network
let provider = Provider::<Http>::try_from("https://sepolia.infura.io/v3/INFURA_API_KEY")?;

let chain_id = provider.get_chainid().await?;

// define the signer
// for simplicity replace the private key (without 0x), ofc it always recommended to load it from an .env file or external vault
let wallet: LocalWallet = "SIGNER_PRIVATE_KEY"
.parse::<LocalWallet>()?
.with_chain_id(chain_id.as_u64());

let to_address = "<to_address_goes_here>";

// connect the wallet to the provider
let client = SignerMiddleware::new(provider, wallet);

// craft the transaction
// it knows to figure out the default gas value and determine the next nonce so no need to explicitly add them unless you want to
let tx = TransactionRequest::new()
.to(to_address)
.value(U256::from(utils::parse_ether(0.01)?));

// send it!
let pending_tx = client.send_transaction(tx, None).await?;

// get the mined tx
let receipt = pending_tx.await?.ok_or_else(|| eyre::format_err!("tx dropped from mempool"))?;
let tx = client.get_transaction(receipt.transaction_hash).await?;

println!("Sent tx: {}\n", serde_json::to_string(&tx)?);
println!("Tx receipt: {}", serde_json::to_string(&receipt)?);

Ok(())
}

Compile and run it with cargo run - you should see a similar output:

Sent tx: {"hash":"0xb4...","nonce":"0xa",...,"type":"0x0","chainId":"0xaa36a7"}
Tx receipt: {"transactionHash":"0xb4...",...,"type":"0x0","effectiveGasPrice":"0xcbe0"}

Notice that you've just sent a legacy transaction ("type":"0x0").

To send an EIP-1559 transaction ("type":"0x2"), the TransactionRequest would become Eip1559TransactionRequest. Here's how the code would change:

use ethers::{
core::{types::TransactionRequest},
middleware::SignerMiddleware,
providers::{Http, Middleware, Provider},
signers::{LocalWallet, Signer},
utils,
prelude::*
};
use eyre::Result;
use std::convert::TryFrom;
use types::Eip1559TransactionRequest;

#[tokio::main]
async fn main() -> Result<()> {

// connect to the network
let provider = Provider::<Http>::try_from("https://sepolia.infura.io/v3/INFURA_API_KEY")?;

let chain_id = provider.get_chainid().await?;

// define the signer
// it's always recommended to load it from an .env file or external vault
let wallet: LocalWallet = "SIGNER_PRIVATE_KEY"
.parse::<LocalWallet>()?
.with_chain_id(chain_id.as_u64());

let to_address = "<to_address_goes_here>";

// connect the wallet to the provider
let client = SignerMiddleware::new(provider, wallet);

// craft the transaction
// this also knows to estimate the `max_priority_fee_per_gas` but added it manually just to show how it would look
let tx = Eip1559TransactionRequest::new()
.to(to_address)
.value(U256::from(utils::parse_ether(0.01)?))
.max_priority_fee_per_gas(U256::from(2000000000_u128)); // 2 Gwei

// send
let pending_tx = client.send_transaction(tx, None).await?;

// get the mined tx
let receipt = pending_tx.await?.ok_or_else(|| eyre::format_err!("tx dropped from mempool"))?;
let tx = client.get_transaction(receipt.transaction_hash).await?;

println!("Sent tx: {}\n", serde_json::to_string(&tx)?);
println!("Tx receipt: {}", serde_json::to_string(&receipt)?);

Ok(())
}
Was this helpful?
Connect MetaMask to provide feedback
What is this?
This is a trial feedback system that uses Verax to record your feedback as onchain attestations on Linea Mainnet. When you vote, submit a transaction in your wallet.