Rust proporciona varias bibliotecas para el desarrollo en Ethereum, siendo web3-rs la opción principal para interacciones con la blockchain.
Implementación Rust de la biblioteca Web3.
use web3::Web3;
use web3::types::{Address, H256, TransactionParameters, U256};
use std::str::FromStr;
struct EthereumClient {
web3: Web3<web3::transports::Http>,
}
impl EthereumClient {
pub async fn new(url: &str) -> Result<Self, web3::Error> {
let transport = web3::transports::Http::new(url)?;
let web3 = Web3::new(transport);
Ok(Self { web3 })
}
pub async fn get_balance(&self, address: &str) -> Result<U256, web3::Error> {
let address = Address::from_str(address)
.map_err(|_| web3::Error::InvalidAddress)?;
self.web3.eth().balance(address, None).await
}
pub async fn send_transaction(
&self,
from: Address,
to: Address,
value: U256,
) -> Result<H256, web3::Error> {
let tx = TransactionParameters {
to: Some(to),
value,
from,
..Default::default()
};
self.web3.eth().send_transaction(tx).await
}
}
Trabajando con contratos inteligentes en Rust:
use web3::contract::{Contract, Options};
use web3::types::{Address, U256};
struct SmartContract {
contract: Contract<web3::transports::Http>,
}
impl SmartContract {
pub async fn new(
web3: Web3<web3::transports::Http>,
address: Address,
abi: &[u8],
) -> Result<Self, web3::Error> {
let contract = Contract::from_json(
web3.eth(),
address,
abi,
)?;
Ok(Self { contract })
}
pub async fn call_method<T: serde::de::DeserializeOwned>(
&self,
method: &str,
params: &[web3::contract::tokens::Tokenize],
) -> Result<T, web3::Error> {
let result = self.contract.query(
method,
params,
None,
Options::default(),
None,
).await?;
Ok(result)
}
}
Manejo robusto de errores en Rust:
use thiserror::Error;
#[derive(Error, Debug)]
pub enum EthereumError {
#[error("Web3 error: {0}")]
Web3Error(#[from] web3::Error),
#[error("Invalid address: {0}")]
InvalidAddress(String),
#[error("Contract error: {0}")]
ContractError(String),
#[error("Transaction failed: {0}")]
TransactionError(String),
}
impl EthereumClient {
pub async fn safe_get_balance(&self, address: &str) -> Result<f64, EthereumError> {
let wei = self.get_balance(address).await?;
let eth = wei.as_u128() as f64 / 1e18;
Ok(eth)
}
}
Suscríbete a eventos Ethereum:
use futures::StreamExt;
impl EthereumClient {
pub async fn monitor_blocks(&self) -> Result<(), web3::Error> {
let mut stream = self.web3.eth_subscribe().subscribe_new_heads().await?;
while let Some(block) = stream.next().await {
match block {
Ok(header) => {
println!("New block: {}", header.number.unwrap_or_default());
}
Err(e) => {
eprintln!("Error: {}", e);
}
}
}
Ok(())
}
}
Ejemplo de pruebas de interacciones Ethereum:
#[cfg(test)]
mod tests {
use super::*;
use tokio;
#[tokio::test]
async fn test_balance_query() {
let client = EthereumClient::new("http://localhost:8545")
.await
.expect("Failed to create client");
let balance = client
.get_balance("0x742d35Cc6634C0532925a3b844Bc454e4438f44e")
.await
.expect("Failed to get balance");
assert!(balance > U256::zero());
}
}