使用Rust进行以太坊开发教程:账户操作实战指南

·

本篇章是《用Rust来做以太坊开发》系列的第二篇,专门带你用 ethers-rs 从零实现 账户余额查询、ERC20代币查询、钱包创建、地址验证 等核心能力。即使你刚入门 Rust,也能一步步跟上节奏。

为什么要用Rust写以太坊应用

随着以太坊 去中心化 需求上升,性能、内存安全、并发 成为开发生命线。Rust 的类型系统与零成本抽象天然适合加密货币场景,ethers-rs 又提供了完整的高层级 API,让 Rust 开发者一键接入以太坊生态。下面我们正式进入主题:账户。


前置依赖

请在 Cargo.toml 中一次性补足依赖,后续所有示例都无需再改:

[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"

1. 查询ETH余额:简单两步骤

关键词:Provider、get_balance、wei转换、utils

场景:节点已运行,目标地址 0x8626f6940E2eb28930eFb4CeF49B2d1F2C9C1199,需要立即知道账户持有多少 ETH

use ethers::{prelude::*, utils};

const RPC_URL: &str = "https://cloudflare-eth.com";

#[tokio::main]
async fn main() -> eyre::Result<()> {
    let provider = Provider::<Http>::try_from(RPC_URL)?;
    let balance_wei = provider
        .get_balance("0x8626f6940E2eb28930eFb4CeF49B2d1F2C9C1199".parse()?, None)
        .await?;

    println!("余额: {} 枚 ETH",  utils::format_ether(balance_wei));
    println!("余额: {} wei",  balance_wei);
    Ok(())
}

要点回顾


2. 查询任意ERC20代币

关键词:ERC20、abigen、智能合约、合约地址、代币余额

继续查询同地址持有的某个 ERC20 代币,原理就是调用该合约的 balanceOf 方法。
use ethers::{prelude::*, utils};
use std::sync::Arc;

abigen!(
    IERC20,
    r#"[
        function balanceOf(address holder) external view returns (uint256)
        function totalSupply() external view returns (uint256)
    ]"#,
);

const RPC_URL: &str = "https://cloudflare-eth.com";

#[tokio::main]
async fn main() -> eyre::Result<()> {
    let provider = Provider::<Http>::try_from(RPC_URL)?;
    let client = Arc::new(provider);

    let token: Address = "0xEB1774bc66930a417A76Df89885CeE7c1A29f405".parse()?;
    let account: Address = "0x8626f6940E2eb28930eFb4CeF49B2d1F2C9C1199".parse()?;

    let contract = IERC20::new(token, client.clone());
    let bal = contract.balance_of(account).call().await?;
    println!("ERC20 合约余额: {bal} wei");

    Ok(())
}

温馨提示
👉 深入理解 abigen!3分钟带你掌握 Rust 生成合约绑定


3. 从零创建新钱包

关键词:LocalWallet、私钥、助记词、wallet、H256、Signer

钱包本质是 256 位二进制私钥;这里的演示包含:

use ethers::{core::rand::thread_rng, signers::*};
use ethers::types::H256;

#[tokio::main]
async fn main() -> eyre::Result<()> {
    // 1. 随机创建
    let random_wallet = LocalWallet::new(&mut thread_rng());
    println!("新私钥: {:?}", hex::encode(random_wallet.signer().to_bytes()));
    println!("地址:  {:x}", random_wallet.address());

    // 2. 从固定字节恢复
    let prikey_bytes: [u8; 32] = hex_literal::hex!(
        "fe9f116e0a9ced0b9ca875ca11f8707cdd807f1caf9e2d738dc01ca4d0a668fa"
    );
    let restored_wallet = Wallet::from_bytes(&prikey_bytes)?;
    println!("恢复地址: {:x}", restored_wallet.address());
    Ok(())
}

隐私提醒


4. 在Rust里验证以太坊地址

关键词:regex、地址验证、十六进制、校验格式

编写正则,两秒判断:

#[tokio::main]
async fn main() -> eyre::Result<()> {
    let re = regex::Regex::new(r"^0x[0-9a-fA-F]{40}$")?;
    println!("有效地址: {}", re.is_match("0x323b5d4c32345ced77393b3530b1eed0f346429d"));
    println!("非法字符: {}", !re.is_match("0xZYXb5d4c32345ced77393b3530b1eed0f346429d"));
    Ok(())
}

5. 区分合约地址与普通账户

关键词:字节码、代码大小、合约、EOA(外部账户)

以太坊账户只分为两类:合约账户EOA。简单规则:查询 get_code,长度 > 1 即为合约。

use ethers::prelude::*;

const RPC_URL: &str = "https://cloudflare-eth.com";

#[tokio::main]
async fn main() -> eyre::Result<()> {
    let provider = Provider::<Http>::try_from(RPC_URL)?;
    let code = provider
        .get_code("0xEB1774bc66930a417A76Df89885CeE7c1A29f405".parse()?, None)
        .await?;

    if !code.is_empty() {
        println!("该地址 *是* 合约");
    } else {
        println!("该地址 *不是* 合约");
    }
    Ok(())
}

补充知道:
👉 想知道更多合约部署细节?这份全链路指南不容错过


实战总结

至此,你已能用Rust完成:

下一步,你可以把交易签名、与 DApp 交互、调用链上计算统统收入囊中。


常见问题 FAQ

  1. Q:get_balance 始终没时间戳参数?
    A:第二个参数为可选 BlockId,传 None 即取最新区块高度,亦可传 BlockNumber::Latest
  2. Q:ERC20 balanceOf 报错 ContractRevert
    A:大概率传错合约地址或方法签名,对一下 abigen 生成的接口与链上字节码一致性。
  3. Q:为什么钱包初始化还要 thread_rng()
    A:Rust 标准库故意限制加密随机源;必须用 rand crate 的 ThreadRng 获取 安全随机数
  4. Q:验证合约时返回空字节数组仍被误判为合约?
    A:极少数空合约部署后会留下大小为 0x0 的代码,可结合事件日志二次确认。
  5. Q:私钥放内存不安全怎么办?
    A:实战用 Keystore 文件+密码,或 硬件钱包(Trezor/Ledger);可集成 eth-keystore crate 实现加密持久化。