rust-web3 is the primary crate for talking to Polygon from Rust. Polygon PoS is EVM-equivalent, so the crate's standard Ethereum interface works as-is against https://polygon.therpc.io/YOUR_API_KEY — chain ID 137 is just another EVM endpoint. The crate is fully async and built on Tokio, so add both to your Cargo.toml: tokio (version "1", features "full") and web3 ("0.19").
rust-web3 relies on Tokio for its async runtime, so your Polygon code runs inside a Tokio executor. Add tokio (version "1", features "full") and web3 ("0.19") to Cargo.toml before connecting to chain 137.
Web3-rs
Build the connection in two steps: create the HTTP transport with web3::transports::Http::new(url), passing your Polygon endpoint https://polygon.therpc.io/YOUR_API_KEY, then wrap it in Web3::new(transport). Every network method returns a Result, so propagate errors with ? inside your async functions rather than unwrapping — a transient failure talking to chain 137 should surface as an error, not a panic.
Key features: Tokio-based async support, type-safe interfaces, contract interactions on Polygon, transaction management, ENS resolution, and WebSocket transports for live subscriptions
Contract Integration
To work with a deployed Polygon contract, load it with Contract::from_json, passing the eth interface, the contract's address on chain 137, and its ABI bytes. Read-only view methods go through contract.query(...), which returns decoded values without spending MATIC, while state-changing methods go through contract.call(...), which submits a transaction and consumes gas.
For clean error handling, use thiserror to derive a typed error enum that wraps web3::Error alongside your own variants for invalid addresses and contract failures. Exposing safe_get_balance / safe_send wrappers around the raw calls lets you centralize that conversion — including turning a U256 wei balance into an f64 MATIC value for display, while keeping the precise integer type for any on-chain arithmetic.
subscribe_new_heads returns a Stream of block headers, but live subscriptions need a WebSocket transport — the HTTP Polygon endpoint cannot push events, so for streaming you must connect over the WebSocket variant rather than the HTTPS one used elsewhere. Pull in futures::StreamExt and drive the stream with a while let Some(block) = stream.next().await loop to react to each new block mined on chain 137.
Mark async test functions with #[tokio::test] so they run inside a Tokio runtime. Point those tests at a local node (http://localhost:8545) or a forked Polygon environment rather than the production endpoint — tests should be fast, deterministic, and free of network dependencies, and they should never burn real requests or risk sending live MATIC transactions against chain 137.