This series replicates "Ethereum Development with Go" using Rust's ethers-rs library. It serves as a fast-paced tutorial for ethers-rs, focusing on code implementation rather than foundational concepts.
In this installment, we explore account management, covering:
- Account balances
- Token balances
- Wallet generation
- Key storage
- Address validation
Dependencies
ethers = { version = "2.0", features = ["rustls", "ws"] }
tokio = { version = "1", features = ["full"] }
eyre = "0.6"
hex = { package = "const-hex", version = "1.6", features = ["hex"] }
regex = "1.10.2"Account Balances
Querying balances using hardhat test accounts:
use ethers::prelude::*;
use ethers::utils;
const RPC_URL: &str = "https://cloudflare-eth.com";
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let provider = Provider::<Http>::try_from(RPC_URL)?;
let balance = provider
.get_balance("0x8626f6940E2eb28930eFb4CeF49B2d1F2C9C1199", None)
.await?;
println!("Balance: {} ether", utils::format_ether(balance));
println!("Balance: {balance:?} wei");
Ok(())
}- Output: Displays balance in ETH (via
format_ether) and wei (rawU256value).
Token Balances (ERC20)
Fetching ERC20 balances involves calling the token contract’s balanceOf method:
use ethers::prelude::*;
use ethers::types::Address;
use std::sync::Arc;
const RPC_URL: &str = "https://cloudflare-eth.com";
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let provider = Provider::<Http>::try_from(RPC_URL)?;
abigen!(
IERC20,
r#"[
function balanceOf(address account) external view returns (uint256)
]"#,
);
let erc20_address: Address = "0xEB1774bc66930a417A76Df89885CeE7c1A29f405".parse()?;
let account_address: Address = "0x8626f6940E2eb28930eFb4CeF49B2d1F2C9C1199".parse()?;
let client = Arc::new(provider);
let contract = IERC20::new(erc20_address, client);
if let Ok(balance) = contract.balance_of(account_address).call().await {
println!("Token Balance: {balance:?}");
}
Ok(())
}- Key Tools:
abigen!macro generates type-safe contract interfaces.
👉 Explore more Rust Ethereum tools
Wallet Generation
Create wallets from private keys or random entropy:
use ethers::signers::{LocalWallet, Signer, Wallet};
use ethers::types::H256;
#[tokio::main]
async fn main() -> eyre::Result<()> {
// Random wallet
let wallet = LocalWallet::new(&mut rand::thread_rng());
println!("Private Key: {:?}", wallet.signer().to_bytes());
println!("Address: {:?}", wallet.address());
// From predefined bytes
let privkey = hex::decode("fe9f116e0a9ced0b9ca875ca11f8707cdd807f1caf9e2d738dc01ca4d0a668fa")?;
let wallet = Wallet::from_bytes(&privkey)?;
println!("Address: {:?}", wallet.address());
Ok(())
}- Note: Private keys are 32-byte values (64 hex chars).
Address Validation
1. Syntax Check
Validate hex format via regex:
let re = regex::Regex::new(r"^0x[0-9a-fA-F]{40}$").unwrap();
println!("Valid: {}", re.is_match("0x323b5d4c32345ced77393b3530b1eed0f346429d")); // true
println!("Valid: {}", re.is_match("0xZYXb...")); // false2. Contract vs. EOA
Check stored code length:
let code = provider.get_code("0xEB1774bc...", None).await?;
if !code.is_empty() {
println!("Contract address");
} else {
println!("Externally owned account (EOA)");
}FAQs
Q1: How does abigen! work?
It compiles Solidity-like ABIs into Rust interfaces at build time, enabling type-safe contract calls.
Q2: What’s the difference between ETH and ERC20 balances?
ETH is native to Ethereum; ERC20 balances are tracked by token contracts.
Q3: Are private keys case-sensitive?
No. Hex values (e.g., 0x1aBc...) are case-insensitive.
👉 Secure your keys with best practices
Conclusion
This guide covered core account operations—balance checks, wallet creation, and address validation—using ethers-rs. Key takeaways:
- Use
Providerfor on-chain queries. - Leverage
abigen!for contract interactions. - Always secure private keys.
For deeper dives, refer to:
- Official ethers-rs Docs
- Ethereum Development with Go (adapted here in Rust).
### Key SEO Enhancements:
- **Keywords**: Ethereum, Rust, `ethers-rs`, ERC20, wallet, private key.
- **Structure**: Hierarchical Markdown headings (`##`, `###`).