diff --git a/Cargo.lock b/Cargo.lock index ab6558cf6a6..744dee7a2b6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3610,9 +3610,11 @@ dependencies = [ name = "example-txpool-tracing" version = "0.0.0" dependencies = [ + "alloy-network", "alloy-primitives", "alloy-rpc-types-trace", "clap", + "eyre", "futures-util", "reth-ethereum", ] diff --git a/examples/txpool-tracing/Cargo.toml b/examples/txpool-tracing/Cargo.toml index 57c93485ccf..df72dd193f9 100644 --- a/examples/txpool-tracing/Cargo.toml +++ b/examples/txpool-tracing/Cargo.toml @@ -6,8 +6,12 @@ edition.workspace = true license.workspace = true [dependencies] -reth-ethereum = { workspace = true, features = ["node", "pool", "cli"] } +reth-ethereum = { workspace = true, features = ["node", "pool", "cli", "rpc"] } + +alloy-primitives.workspace = true alloy-rpc-types-trace.workspace = true +alloy-network.workspace = true + clap = { workspace = true, features = ["derive"] } futures-util.workspace = true -alloy-primitives.workspace = true +eyre.workspace = true diff --git a/examples/txpool-tracing/src/main.rs b/examples/txpool-tracing/src/main.rs index 655b8889f6d..a1b61422cb9 100644 --- a/examples/txpool-tracing/src/main.rs +++ b/examples/txpool-tracing/src/main.rs @@ -21,6 +21,8 @@ use reth_ethereum::{ rpc::eth::primitives::TransactionRequest, }; +mod submit; + fn main() { Cli::::parse() .run(|builder, args| async move { diff --git a/examples/txpool-tracing/src/submit.rs b/examples/txpool-tracing/src/submit.rs new file mode 100644 index 00000000000..04744f37244 --- /dev/null +++ b/examples/txpool-tracing/src/submit.rs @@ -0,0 +1,133 @@ +//! Transaction submission functionality for the txpool tracing example +#![allow(unused)] +#![allow(clippy::too_many_arguments)] + +use alloy_network::{Ethereum, EthereumWallet, NetworkWallet, TransactionBuilder}; +use alloy_primitives::{Address, TxHash, U256}; +use futures_util::StreamExt; +use reth_ethereum::{ + node::api::{FullNodeComponents, NodeTypes}, + pool::{PoolTransaction, TransactionEvent, TransactionOrigin, TransactionPool}, + primitives::SignerRecoverable, + rpc::eth::primitives::TransactionRequest, + EthPrimitives, TransactionSigned, +}; + +/// Submit a transaction to the transaction pool +/// +/// This function demonstrates how to create, sign, and submit a transaction +/// to the reth transaction pool. +pub async fn submit_transaction( + node: &FC, + wallet: &EthereumWallet, + to: Address, + data: Vec, + nonce: u64, + chain_id: u64, + gas_limit: u64, + max_priority_fee_per_gas: u128, + max_fee_per_gas: u128, +) -> eyre::Result +where + // This enforces `EthPrimitives` types for this node, this unlocks the proper conversions when + FC: FullNodeComponents>, +{ + // Create the transaction request + let request = TransactionRequest::default() + .with_to(to) + .with_input(data) + .with_nonce(nonce) + .with_chain_id(chain_id) + .with_gas_limit(gas_limit) + .with_max_priority_fee_per_gas(max_priority_fee_per_gas) + .with_max_fee_per_gas(max_fee_per_gas); + + // Sign the transaction + let transaction: TransactionSigned = + NetworkWallet::::sign_request(wallet, request).await?.into(); + // Get the transaction hash before submitting + let tx_hash = *transaction.hash(); + + // Recover the transaction + let transaction = transaction.try_into_recovered()?; + + // Convert to pool transaction type + let pool_transaction = + ::Transaction::try_from_consensus(transaction) + .map_err(|e| eyre::eyre!("Failed to convert to pool transaction: {e}"))?; + + // Submit the transaction to the pool and get event stream + let mut tx_events = node + .pool() + .add_transaction_and_subscribe(TransactionOrigin::Local, pool_transaction) + .await?; + + // Wait for the transaction to be added to the pool + while let Some(event) = tx_events.next().await { + match event { + TransactionEvent::Mined(_) => { + println!("Transaction was mined: {:?}", tx_events.hash()); + break; + } + TransactionEvent::Pending => { + println!("Transaction added to pending pool: {:?}", tx_events.hash()); + break; + } + TransactionEvent::Discarded => { + return Err(eyre::eyre!("Transaction discarded: {:?}", tx_events.hash(),)); + } + _ => { + // Continue waiting for added or rejected event + } + } + } + + Ok(tx_hash) +} + +/// Helper function to submit a simple ETH transfer transaction +/// +/// This will first populate a tx request, sign it then submit to the pool in the required format. +pub async fn submit_eth_transfer( + node: &FC, + wallet: &EthereumWallet, + to: Address, + value: U256, + nonce: u64, + chain_id: u64, + gas_limit: u64, + max_priority_fee_per_gas: u128, + max_fee_per_gas: u128, +) -> eyre::Result +where + FC: FullNodeComponents>, +{ + // Create the transaction request for ETH transfer + let request = TransactionRequest::default() + .with_to(to) + .with_value(value) + .with_nonce(nonce) + .with_chain_id(chain_id) + .with_gas_limit(gas_limit) + .with_max_priority_fee_per_gas(max_priority_fee_per_gas) + .with_max_fee_per_gas(max_fee_per_gas); + + // Sign the transaction + let transaction: TransactionSigned = + NetworkWallet::::sign_request(wallet, request).await?.into(); + // Recover the transaction + let transaction = transaction.try_into_recovered()?; + + // Get the transaction hash + let tx_hash = *transaction.hash(); + + // Convert to pool transaction type + let pool_transaction = + ::Transaction::try_from_consensus(transaction) + .map_err(|e| eyre::eyre!("Failed to convert to pool transaction: {e}"))?; + + // Submit the transaction to the pool + node.pool().add_transaction(TransactionOrigin::Local, pool_transaction).await?; + + Ok(tx_hash) +}