Ethereum
هل أنت مستعد لاستدعاء هذا في الإنتاج؟
الخطة المجانية تغطي المشاريع الشخصية. الدفع حسب الاستخدام يتوسع دون بطاقة.
Ethereum
الخطة المجانية تغطي المشاريع الشخصية. الدفع حسب الاستخدام يتوسع دون بطاقة.
rust-web3 is the long-standing crate for talking to Ethereum from Rust, and it's what the examples here use. (The newer ethers-rs and alloy crates have taken over much new development, so check them if you're starting fresh — but rust-web3 still works and the patterns carry over.) It's built on Tokio, so the async runtime isn't optional: add tokio = { version = "1", features = ["full"] } alongside web3 = "0.19" in your Cargo.toml, and every call to chain 1 becomes an .await inside a Tokio task.
rust-web3 leans on Tokio for its async machinery, so the runtime is a hard dependency. Add tokio = { version = "1", features = ["full"] } and web3 = "0.19" to your Cargo.toml before connecting to Ethereum.
Connecting is two steps: build an HTTP transport with web3::transports::Http::new(url) pointing at the Ethereum endpoint, then wrap it in Web3::new(transport). The transport constructor itself returns a Result, and so does every network call — eth().balance(...) gives you Result<U256, web3::Error>. Lean on the ? operator inside your async fn to propagate those errors up rather than unwrapping; a dropped connection or a bad address becomes a typed error your caller handles, not a panic mid-request.
U256, Address, and H256 primitives.eth namesContract::from_json takes the eth() handle, an address, and the ABI bytes, and returns a Contract bound to that deployment on Ethereum. The two verbs to keep straight: contract.query(...) runs a read as an eth_call and decodes the return into the Rust type you ask for — perfect for an ERC-20 balanceOf — while contract.call(...) submits a state-changing transaction and returns its hash. Mixing them up is the most common beginner slip; query never touches the chain's state, call always does.
Rather than pass web3::Error around everywhere, derive a domain error enum with thiserror and fold the library error in through #[from], as the EthereumError below does. That gives you one error type to match on, with variants for invalid addresses and failed transactions that read clearly in a Result. Pair it with safe_ wrappers that do the wei-to-ether conversion at the boundary: a balance comes back as U256 wei, and dividing by 1e18 into an f64 is fine for display — just remember that f64 loses precision, so never feed that float back into anything that signs or settles on Ethereum.
subscribe_new_heads hands back a Stream of block headers, which means you need the WebSocket transport — eth_subscribe doesn't ride HTTP, so connect over wss://ethereum.therpc.io/YOUR_API_KEY rather than the HTTPS URL. Bring futures::StreamExt into scope and drain the stream with while let Some(block) = stream.next().await, matching each item as Ok(header) or an error. Since Ethereum produces a header roughly every 12 seconds, the loop spends most of its time parked on .await rather than spinning.
Mark async tests with #[tokio::test] so each one gets its own runtime — a plain #[test] can't .await. Point those tests at a local node on http://localhost:8545 (anvil or a geth dev chain) or a mainnet fork, not the production endpoint. Hammering the live Ethereum connection from a test suite burns CUs on every CI run and makes assertions flaky, since real chain 1 state moves with each block; a forked or local chain stays deterministic.