Sitelet https://github.com/alloy-rs/examples/pull/250/files
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[workspace]
members = ["helpers", "examples/*", "benches"]
members = ["example-support", "helpers", "examples/*", "benches"]
resolver = "2"

[workspace.package]
Expand Down Expand Up @@ -151,4 +151,5 @@ rlp-derive = "0.2.0"
alloy-rlp = "0.3.12"

# helpers
example-support = { path = "example-support" }
helpers = { path = "helpers" }
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,27 @@ To run an example, use the command `cargo run --example <Example>`:
cargo run --example mnemonic_signer
```

Examples that query a live network or fork it with Anvil require you to supply an endpoint:

```sh
RPC_URL=https://your-ethereum-endpoint cargo run --example http
```

WebSocket and IPC examples use `WS_URL` and `IPC_PATH`, respectively. Examples with authentication
also list their required credential variables in the error they return.

The fallback-layer example accepts a comma-separated list instead:

```sh
RPC_URLS=https://first-endpoint,https://second-endpoint cargo run --example fallback_layer
```

Use an endpoint for the network named by the example. For example, `any_network` expects an
Arbitrum Sepolia endpoint, while mainnet contract and ENS examples expect Ethereum mainnet.
Examples that fork a network also require [`anvil`](https://getfoundry.sh/anvil/overview/) in
`PATH`. Hardware-wallet and cloud-signer examples require the device or credentials described in
their source code.

## Overview

This repository contains the following examples:
Expand Down
16 changes: 16 additions & 0 deletions example-support/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "example-support"
publish.workspace = true
version.workspace = true
edition.workspace = true
rust-version.workspace = true
authors.workspace = true
license.workspace = true
homepage.workspace = true
repository.workspace = true

[lints]
workspace = true

[dependencies]
eyre.workspace = true
48 changes: 48 additions & 0 deletions example-support/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//! Shared configuration helpers for the examples workspace.

use eyre::{bail, Result, WrapErr};

/// Returns a non-empty environment variable or an actionable error.
pub fn required_env(name: &str) -> Result<String> {
let value =
std::env::var(name).wrap_err_with(|| format!("{name} must be set for this example"))?;

if value.trim().is_empty() {
bail!("{name} must not be empty");
}

Ok(value)
}

/// Returns the JSON-RPC endpoint configured through `RPC_URL`.
pub fn rpc_url() -> Result<String> {
required_env("RPC_URL").wrap_err("RPC_URL must contain a JSON-RPC endpoint")
}

/// Returns the WebSocket endpoint configured through `WS_URL`.
pub fn ws_url() -> Result<String> {
required_env("WS_URL").wrap_err("WS_URL must contain a WebSocket JSON-RPC endpoint")
}

/// Returns the IPC socket path configured through `IPC_PATH`.
pub fn ipc_path() -> Result<String> {
required_env("IPC_PATH").wrap_err("IPC_PATH must contain a JSON-RPC IPC socket path")
}

/// Returns the comma-separated JSON-RPC endpoints configured through `RPC_URLS`.
pub fn rpc_urls() -> Result<Vec<String>> {
let value = std::env::var("RPC_URLS")
.wrap_err("RPC_URLS must be set to comma-separated JSON-RPC endpoints for this example")?;
let urls: Vec<_> = value
.split(',')
.map(str::trim)
.filter(|url| !url.is_empty())
.map(ToOwned::to_owned)
.collect();

if urls.len() < 2 {
bail!("RPC_URLS must contain at least two JSON-RPC endpoints");
}

Ok(urls)
}
1 change: 1 addition & 0 deletions examples/advanced/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ workspace = true
foundry-fork-db.workspace = true
alloy.workspace = true
alloy-evm.workspace = true
example-support.workspace = true
helpers.workspace = true

# reth
Expand Down
5 changes: 3 additions & 2 deletions examples/advanced/examples/any_network.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use alloy::{
signers::local::PrivateKeySigner,
sol,
};
use example_support::{required_env, rpc_url};
use eyre::Result;

// The address of the contract below deployed to Arbitrum Sepolia.
Expand Down Expand Up @@ -44,10 +45,10 @@ struct ArbOtherFields {
async fn main() -> Result<()> {
// [RISK WARNING! Writing a private key in the code file is insecure behavior.]
// The following code is for testing only. Set up signer from private key, be aware of danger.
let signer: PrivateKeySigner = "<PRIVATE_KEY>".parse().expect("should parse private key");
let signer: PrivateKeySigner = required_env("PRIVATE_KEY")?.parse()?;

// Create a provider with the Arbitrum Sepolia network and the wallet.
let rpc_url = "https://sepolia-rollup.arbitrum.io/rpc".parse()?;
let rpc_url = rpc_url()?.parse()?;
let provider =
ProviderBuilder::new().network::<AnyNetwork>().wallet(signer).connect_http(rpc_url);

Expand Down
6 changes: 4 additions & 2 deletions examples/advanced/examples/uniswap_u256_alloy_simulation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use alloy::{
sol,
sol_types::SolCall,
};
use example_support::rpc_url;
use eyre::Result;
use helpers::alloy::{
get_amount_in, get_amount_out, get_sushi_pair, get_uniswap_pair, set_hash_storage_slot,
Expand Down Expand Up @@ -38,8 +39,9 @@ async fn main() -> Result<()> {
let sushi_pair = get_sushi_pair();

let wallet_address = address!("0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266");
let provider = ProviderBuilder::new()
.connect_anvil_with_wallet_and_config(|a| a.fork("https://reth-ethereum.ithaca.xyz/rpc"))?;
let rpc_url = rpc_url()?;
let provider =
ProviderBuilder::new().connect_anvil_with_wallet_and_config(|anvil| anvil.fork(rpc_url))?;

let executor = FlashBotsMultiCall::deploy(provider.clone(), wallet_address).await?;
let iweth = IERC20::new(WETH_ADDR, provider.clone());
Expand Down
1 change: 1 addition & 0 deletions examples/contracts/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ workspace = true

[dev-dependencies]
alloy.workspace = true
example-support.workspace = true

eyre.workspace = true
tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
Expand Down
3 changes: 2 additions & 1 deletion examples/contracts/examples/interact_with_abi.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Example of generating code from ABI file using the `sol!` macro to interact with the contract.

use alloy::{primitives::address, providers::ProviderBuilder, sol};
use example_support::rpc_url;
use eyre::Result;

// Codegen from ABI file to interact with the contract.
Expand All @@ -15,7 +16,7 @@ sol!(
async fn main() -> Result<()> {
// Spin up a forked Anvil node.
// Ensure `anvil` is available in $PATH.
let rpc_url = "https://reth-ethereum.ithaca.xyz/rpc";
let rpc_url = rpc_url()?;
let provider =
ProviderBuilder::new().connect_anvil_with_wallet_and_config(|anvil| anvil.fork(rpc_url))?;

Expand Down
3 changes: 2 additions & 1 deletion examples/contracts/examples/simulation_uni_v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use crate::helpers::{
get_amount_in, get_amount_out, get_sushi_pair, get_uniswap_pair, set_hash_storage_slot,
DAI_ADDR, WETH_ADDR,
};
use example_support::rpc_url;
use eyre::Result;

sol! {
Expand All @@ -39,7 +40,7 @@ sol!(
async fn main() -> Result<()> {
// Spawn `anvil` and fork mainnet
// Make sure you have `anvil` in $PATH
let anvil = Anvil::new().fork("https://reth-ethereum.ithaca.xyz/rpc").try_spawn()?;
let anvil = Anvil::new().fork(rpc_url()?).try_spawn()?;

// Get the pool contract interfaces
let uniswap_pair = get_uniswap_pair();
Expand Down
1 change: 1 addition & 0 deletions examples/ens/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ workspace = true

[dev-dependencies]
alloy.workspace = true
example-support.workspace = true

eyre.workspace = true
futures-util.workspace = true
Expand Down
3 changes: 2 additions & 1 deletion examples/ens/examples/address_lookup.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
//! Example of looking up ENS names from Ethereum addresses.

use alloy::{ens::ProviderEnsExt, primitives::address, providers::ProviderBuilder};
use example_support::rpc_url;
use eyre::Result;

#[tokio::main]
async fn main() -> Result<()> {
// Create a provider.
let rpc_url = "https://reth-ethereum.ithaca.xyz/rpc".parse()?;
let rpc_url = rpc_url()?.parse()?;
let provider = ProviderBuilder::new().connect_http(rpc_url);

// Vitalik's Ethereum address.
Expand Down
3 changes: 2 additions & 1 deletion examples/ens/examples/name_resolution.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
//! Example of resolving ENS names to Ethereum addresses.

use alloy::{ens::ProviderEnsExt, providers::ProviderBuilder};
use example_support::rpc_url;
use eyre::Result;

#[tokio::main]
async fn main() -> Result<()> {
// Create a provider.
let rpc_url = "https://reth-ethereum.ithaca.xyz/rpc".parse()?;
let rpc_url = rpc_url()?.parse()?;
let provider = ProviderBuilder::new().connect_http(rpc_url);

// Resolve the ENS name "vitalik.eth" to its Ethereum address.
Expand Down
1 change: 1 addition & 0 deletions examples/layers/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ workspace = true

[dev-dependencies]
alloy = { workspace = true, features = ["hyper"] }
example-support.workspace = true

eyre.workspace = true
tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
Expand Down
19 changes: 10 additions & 9 deletions examples/layers/examples/fallback_layer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,24 @@ use alloy::{
layers::FallbackLayer,
},
};
use example_support::rpc_urls;
use eyre::Result;
use tower::ServiceBuilder;

#[tokio::main]
async fn main() -> Result<()> {
let _ = tracing_subscriber::fmt::try_init();

// Configure the fallback layer
// Configure the fallback layer for the endpoints supplied in `RPC_URLS`.
let rpc_urls = rpc_urls()?;
let active_transport_count =
NonZeroUsize::new(rpc_urls.len()).expect("rpc_urls() rejects an empty list");
let fallback_layer =
FallbackLayer::default().with_active_transport_count(NonZeroUsize::new(3).unwrap());

// Define your list of transports to use
let transports = vec![
Http::new(Url::parse("https://reth-ethereum.ithaca.xyz/rpc")?),
Http::new(Url::parse("https://eth.llamarpc.com")?),
Http::new(Url::parse("https://ethereum-rpc.publicnode.com")?),
];
FallbackLayer::default().with_active_transport_count(active_transport_count);

// Define one transport for each configured endpoint.
let transports =
rpc_urls.iter().map(|url| Ok(Http::new(Url::parse(url)?))).collect::<Result<Vec<_>>>()?;

// Apply the FallbackLayer to the transports
let transport = ServiceBuilder::new().layer(fallback_layer).service(transports);
Expand Down
1 change: 1 addition & 0 deletions examples/node-bindings/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ workspace = true

[dev-dependencies]
alloy.workspace = true
example-support.workspace = true

eyre.workspace = true
tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
Loading
Loading