From ccb71eb2792edd535f4acdd6cb36cb6b630a4c8a Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Thu, 14 Mar 2024 14:01:39 -0400 Subject: [PATCH 1/7] feat(queries): query examples: logs, contract_storage and bytecode --- Cargo.toml | 1 + examples/queries/Cargo.toml | 32 +++++++++++++++++++ .../examples/query_contract_storage.rs | 29 +++++++++++++++++ .../examples/query_deployed_bytecode.rs | 28 ++++++++++++++++ examples/queries/examples/query_logs.rs | 32 +++++++++++++++++++ 5 files changed, 122 insertions(+) create mode 100644 examples/queries/Cargo.toml create mode 100644 examples/queries/examples/query_contract_storage.rs create mode 100644 examples/queries/examples/query_deployed_bytecode.rs create mode 100644 examples/queries/examples/query_logs.rs diff --git a/Cargo.toml b/Cargo.toml index 72c79195..d027b9b1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ license = "MIT OR Apache-2.0" homepage = "https://github.com/alloy-rs/examples" repository = "https://github.com/alloy-rs/examples" publish = false +exclude = ["examples/"] [workspace.dependencies] alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "d5967ab", default-features = false } diff --git a/examples/queries/Cargo.toml b/examples/queries/Cargo.toml new file mode 100644 index 00000000..7c4cbea7 --- /dev/null +++ b/examples/queries/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "examples-queries" +version = "0.0.0" +publish = false + +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +exclude.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dev-dependencies] +alloy-primitives.workspace = true +alloy-sol-types = { workspace = true, features = ["json"] } +alloy-provider = { workspace = true, features = ["pubsub", "ws"] } +alloy-contract.workspace = true +alloy-pubsub.workspace = true +alloy-rpc-types.workspace = true +alloy-rpc-client.workspace = true +alloy-rpc-trace-types.workspace = true +alloy-node-bindings.workspace = true +alloy-transport.workspace = true +alloy-transport-http.workspace = true +alloy-network.workspace = true +tokio = { version = "1.36.0", features = ["rt-multi-thread", "macros"] } +eyre = "0.6.12" +reqwest = "0.11.26" +futures-util = "0.3" diff --git a/examples/queries/examples/query_contract_storage.rs b/examples/queries/examples/query_contract_storage.rs new file mode 100644 index 00000000..ca11c5f1 --- /dev/null +++ b/examples/queries/examples/query_contract_storage.rs @@ -0,0 +1,29 @@ +use alloy_network::Ethereum; +use alloy_primitives::{address, fixed_bytes}; +use alloy_provider::{HttpProvider, Provider}; +use alloy_rpc_client::RpcClient; +use alloy_transport_http::Http; +use eyre::Result; +use reqwest::Client; + +#[tokio::main] +async fn main() -> Result<()> { + let provider = init(); + + // Get slot0 from USDC-ETH Uniswap V3 pool + let pool_address = address!("88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640"); + + let storage_slot = + fixed_bytes!("0000000000000000000000000000000000000000000000000000000000000000"); + + let storage = provider.get_storage_at(pool_address, storage_slot.into(), None).await?; + + println!("Slot 0: {:?}", storage); + + Ok(()) +} + +fn init() -> HttpProvider { + let http = Http::::new("https://eth.llamarpc.com".parse().unwrap()); + HttpProvider::new(RpcClient::new(http, true)) +} diff --git a/examples/queries/examples/query_deployed_bytecode.rs b/examples/queries/examples/query_deployed_bytecode.rs new file mode 100644 index 00000000..9b5a737b --- /dev/null +++ b/examples/queries/examples/query_deployed_bytecode.rs @@ -0,0 +1,28 @@ +use alloy_network::Ethereum; +use alloy_primitives::address; +use alloy_provider::{HttpProvider, Provider}; +use alloy_rpc_client::RpcClient; +use alloy_rpc_types::{BlockId, BlockNumberOrTag}; +use alloy_transport_http::Http; +use eyre::Result; +use reqwest::Client; + +#[tokio::main] +async fn main() -> Result<()> { + let provider = init(); + + // Get bytecode of USDC-ETH Uniswap V3 pool + let pool_address = address!("88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640"); + + let bytecode = + provider.get_code_at(pool_address, BlockId::Number(BlockNumberOrTag::Latest)).await?; + + println!("Bytecode: {:?}", bytecode); + + Ok(()) +} + +fn init() -> HttpProvider { + let http = Http::::new("https://eth.llamarpc.com".parse().unwrap()); + HttpProvider::new(RpcClient::new(http, true)) +} diff --git a/examples/queries/examples/query_logs.rs b/examples/queries/examples/query_logs.rs new file mode 100644 index 00000000..7d3c6530 --- /dev/null +++ b/examples/queries/examples/query_logs.rs @@ -0,0 +1,32 @@ +use alloy_network::Ethereum; +use alloy_provider::{HttpProvider, Provider}; +use alloy_rpc_client::RpcClient; +use alloy_rpc_types::Filter; +use alloy_transport_http::Http; +use eyre::Result; +use reqwest::Client; +#[tokio::main] +async fn main() -> Result<()> { + let provider = init(); + + // Get logs from the latest block + let latest_block = provider.get_block_number().await?; + let filter = Filter::new().from_block(latest_block); + // .address(vec![address!("1f9840a85d5aF5bf1D1762F925BDADdC4201F984")]) // Emitted by the + // UNI token + // .event("Transfer(address,address,uint256)") + // .event_signature(fixed_bytes!(" + // ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")); // Using transfer event + // signature + let logs = provider.get_logs(&filter).await?; + + for log in logs { + println!("{:?}", log); + } + Ok(()) +} + +fn init() -> HttpProvider { + let http = Http::::new("https://eth.llamarpc.com".parse().unwrap()); + HttpProvider::new(RpcClient::new(http, true)) +} From 7c6bdb585d1418f4773366bd64e4ab1345efc726 Mon Sep 17 00:00:00 2001 From: zerosnacks Date: Fri, 15 Mar 2024 12:55:00 +0000 Subject: [PATCH 2/7] minor cleanup --- README.md | 11 ++++------- examples/queries/examples/query_contract_storage.rs | 2 ++ examples/queries/examples/query_deployed_bytecode.rs | 2 ++ examples/queries/examples/query_logs.rs | 9 +++------ 4 files changed, 11 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 3d3de244..3ff443be 100644 --- a/README.md +++ b/README.md @@ -56,13 +56,10 @@ cargo run --example mnemonic_signer - [ ] Retry - [ ] RW - [ ] WS -- [ ] Queries - - [ ] Blocks - - [ ] Contracts - - [ ] Events - - [ ] Paginated logs - - [ ] UniswapV2 pair - - [ ] Transactions +- [x] Queries + - [x] [Contract storage](./examples/queries/examples/query_contract_storage.rs) + - [x] [Contract deployed bytecode](./examples/queries/examples/query_deployed_bytecode.rs) + - [x] [Logs](./examples/queries/examples/query_logs.rs) - [ ] Subscriptions - [ ] Watch blocks - [ ] Subscribe events by type diff --git a/examples/queries/examples/query_contract_storage.rs b/examples/queries/examples/query_contract_storage.rs index ca11c5f1..bbafb3d1 100644 --- a/examples/queries/examples/query_contract_storage.rs +++ b/examples/queries/examples/query_contract_storage.rs @@ -1,3 +1,5 @@ +//! Example of querying contract storage from the Ethereum network. + use alloy_network::Ethereum; use alloy_primitives::{address, fixed_bytes}; use alloy_provider::{HttpProvider, Provider}; diff --git a/examples/queries/examples/query_deployed_bytecode.rs b/examples/queries/examples/query_deployed_bytecode.rs index 9b5a737b..9b413212 100644 --- a/examples/queries/examples/query_deployed_bytecode.rs +++ b/examples/queries/examples/query_deployed_bytecode.rs @@ -1,3 +1,5 @@ +//! Example of querying deployed bytecode of a contract on Ethereum network. + use alloy_network::Ethereum; use alloy_primitives::address; use alloy_provider::{HttpProvider, Provider}; diff --git a/examples/queries/examples/query_logs.rs b/examples/queries/examples/query_logs.rs index 7d3c6530..61d7d936 100644 --- a/examples/queries/examples/query_logs.rs +++ b/examples/queries/examples/query_logs.rs @@ -1,3 +1,5 @@ +//! Example of how to query logs from the Ethereum network. + use alloy_network::Ethereum; use alloy_provider::{HttpProvider, Provider}; use alloy_rpc_client::RpcClient; @@ -5,6 +7,7 @@ use alloy_rpc_types::Filter; use alloy_transport_http::Http; use eyre::Result; use reqwest::Client; + #[tokio::main] async fn main() -> Result<()> { let provider = init(); @@ -12,12 +15,6 @@ async fn main() -> Result<()> { // Get logs from the latest block let latest_block = provider.get_block_number().await?; let filter = Filter::new().from_block(latest_block); - // .address(vec![address!("1f9840a85d5aF5bf1D1762F925BDADdC4201F984")]) // Emitted by the - // UNI token - // .event("Transfer(address,address,uint256)") - // .event_signature(fixed_bytes!(" - // ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")); // Using transfer event - // signature let logs = provider.get_logs(&filter).await?; for log in logs { From 3eab157288cb19739ab9add347b144a1c447f64d Mon Sep 17 00:00:00 2001 From: zerosnacks Date: Fri, 15 Mar 2024 12:57:26 +0000 Subject: [PATCH 3/7] clean up dependencies --- examples/queries/Cargo.toml | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/examples/queries/Cargo.toml b/examples/queries/Cargo.toml index 7c4cbea7..234d4329 100644 --- a/examples/queries/Cargo.toml +++ b/examples/queries/Cargo.toml @@ -1,32 +1,24 @@ [package] name = "examples-queries" -version = "0.0.0" -publish = false -authors.workspace = true +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 -exclude.workspace = true - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dev-dependencies] +alloy-network.workspace = true alloy-primitives.workspace = true -alloy-sol-types = { workspace = true, features = ["json"] } alloy-provider = { workspace = true, features = ["pubsub", "ws"] } -alloy-contract.workspace = true -alloy-pubsub.workspace = true alloy-rpc-types.workspace = true alloy-rpc-client.workspace = true -alloy-rpc-trace-types.workspace = true -alloy-node-bindings.workspace = true -alloy-transport.workspace = true alloy-transport-http.workspace = true -alloy-network.workspace = true -tokio = { version = "1.36.0", features = ["rt-multi-thread", "macros"] } -eyre = "0.6.12" -reqwest = "0.11.26" + +eyre.workspace = true futures-util = "0.3" +reqwest.workspace = true +tokio = { workspace = true, features = ["rt-multi-thread", "macros"] } From 5d42d985589716fab60195883f88562994b519b6 Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Sun, 17 Mar 2024 20:11:30 -0400 Subject: [PATCH 4/7] feat(queries): additional logs examples and nits --- Cargo.toml | 2 +- .../examples/query_contract_storage.rs | 9 ++--- examples/queries/examples/query_logs.rs | 39 ++++++++++++++++--- 3 files changed, 38 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4594c5e4..1b1d016d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,7 +58,7 @@ tokio = "1" # misc eyre = "0.6.12" -reqwest = { version = "0.11", default-features = false } +reqwest = { version = "0.11", default-features = true } ## serde serde = { version = "1.0", features = ["derive"] } diff --git a/examples/queries/examples/query_contract_storage.rs b/examples/queries/examples/query_contract_storage.rs index bbafb3d1..3867a13b 100644 --- a/examples/queries/examples/query_contract_storage.rs +++ b/examples/queries/examples/query_contract_storage.rs @@ -1,7 +1,7 @@ //! Example of querying contract storage from the Ethereum network. use alloy_network::Ethereum; -use alloy_primitives::{address, fixed_bytes}; +use alloy_primitives::{address, fixed_bytes, U256}; use alloy_provider::{HttpProvider, Provider}; use alloy_rpc_client::RpcClient; use alloy_transport_http::Http; @@ -15,10 +15,9 @@ async fn main() -> Result<()> { // Get slot0 from USDC-ETH Uniswap V3 pool let pool_address = address!("88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640"); - let storage_slot = - fixed_bytes!("0000000000000000000000000000000000000000000000000000000000000000"); + let storage_slot = U256::from(0); - let storage = provider.get_storage_at(pool_address, storage_slot.into(), None).await?; + let storage = provider.get_storage_at(pool_address, storage_slot, None).await?; println!("Slot 0: {:?}", storage); @@ -26,6 +25,6 @@ async fn main() -> Result<()> { } fn init() -> HttpProvider { - let http = Http::::new("https://eth.llamarpc.com".parse().unwrap()); + let http = Http::::new("https://eth.merkle.io".parse().unwrap()); HttpProvider::new(RpcClient::new(http, true)) } diff --git a/examples/queries/examples/query_logs.rs b/examples/queries/examples/query_logs.rs index 61d7d936..dc1d6682 100644 --- a/examples/queries/examples/query_logs.rs +++ b/examples/queries/examples/query_logs.rs @@ -1,29 +1,56 @@ -//! Example of how to query logs from the Ethereum network. - use alloy_network::Ethereum; +use alloy_primitives::{address, fixed_bytes}; use alloy_provider::{HttpProvider, Provider}; use alloy_rpc_client::RpcClient; use alloy_rpc_types::Filter; use alloy_transport_http::Http; use eyre::Result; -use reqwest::Client; - +use reqwest::{Client, Url}; #[tokio::main] async fn main() -> Result<()> { let provider = init(); // Get logs from the latest block let latest_block = provider.get_block_number().await?; + + // Get all logs from the latest block let filter = Filter::new().from_block(latest_block); + let logs = provider.get_logs(&filter).await?; for log in logs { println!("{:?}", log); } + + let tranfer_event_signature = + fixed_bytes!("ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"); + + // Get all logs from the latest block that match the transfer event signature/topic + let filter = Filter::new().event_signature(tranfer_event_signature).from_block(latest_block); + // You could also use the event name instead of the event signature like so: + // .event("Transfer(address,address,uint256)") + + let logs = provider.get_logs(&filter).await?; + + for log in logs { + println!("Transfer event: {:?}", log); + } + + let uni_address = address!("1f9840a85d5aF5bf1D1762F925BDADdC4201F984"); + + // Get all from the latest block emitted by the UNI token address + let filter = Filter::new().address(uni_address).from_block(latest_block); + + let logs = provider.get_logs(&filter).await?; + + for log in logs { + println!("UNI token logs: {:?}", log); + } + Ok(()) } fn init() -> HttpProvider { - let http = Http::::new("https://eth.llamarpc.com".parse().unwrap()); - HttpProvider::new(RpcClient::new(http, true)) + let http = Http::::new("https://eth.merkle.io".parse().unwrap()); + HttpProvider::new(RpcClient::new(http, false)) } From 39274845124d9324d8f4510556927410fdf393bf Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Sun, 17 Mar 2024 20:15:28 -0400 Subject: [PATCH 5/7] ci nits --- examples/queries/examples/query_contract_storage.rs | 2 +- examples/queries/examples/query_logs.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/queries/examples/query_contract_storage.rs b/examples/queries/examples/query_contract_storage.rs index 3867a13b..87e3d278 100644 --- a/examples/queries/examples/query_contract_storage.rs +++ b/examples/queries/examples/query_contract_storage.rs @@ -1,7 +1,7 @@ //! Example of querying contract storage from the Ethereum network. use alloy_network::Ethereum; -use alloy_primitives::{address, fixed_bytes, U256}; +use alloy_primitives::{address, U256}; use alloy_provider::{HttpProvider, Provider}; use alloy_rpc_client::RpcClient; use alloy_transport_http::Http; diff --git a/examples/queries/examples/query_logs.rs b/examples/queries/examples/query_logs.rs index dc1d6682..f9466ad6 100644 --- a/examples/queries/examples/query_logs.rs +++ b/examples/queries/examples/query_logs.rs @@ -5,7 +5,7 @@ use alloy_rpc_client::RpcClient; use alloy_rpc_types::Filter; use alloy_transport_http::Http; use eyre::Result; -use reqwest::{Client, Url}; +use reqwest::Client; #[tokio::main] async fn main() -> Result<()> { let provider = init(); From 126bb8e7f1c701572f3eef027f8fc9b7a81e13f4 Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Mon, 25 Mar 2024 10:59:41 -0400 Subject: [PATCH 6/7] fix: use alloy namespace --- Cargo.toml | 2 +- examples/queries/Cargo.toml | 13 ++++++------ .../examples/query_contract_storage.rs | 19 +++++++---------- .../examples/query_deployed_bytecode.rs | 21 +++++++------------ examples/queries/examples/query_logs.rs | 21 +++++++------------ 5 files changed, 31 insertions(+), 45 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a35a5994..35278591 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,7 +60,7 @@ tokio = "1" # misc eyre = "0.6.12" -reqwest = { version = "0.11", default-features = true } +reqwest = { version = "0.11", default-features = false } ## serde serde = { version = "1.0", features = ["derive"] } diff --git a/examples/queries/Cargo.toml b/examples/queries/Cargo.toml index 234d4329..d631f3b5 100644 --- a/examples/queries/Cargo.toml +++ b/examples/queries/Cargo.toml @@ -11,12 +11,13 @@ homepage.workspace = true repository.workspace = true [dev-dependencies] -alloy-network.workspace = true -alloy-primitives.workspace = true -alloy-provider = { workspace = true, features = ["pubsub", "ws"] } -alloy-rpc-types.workspace = true -alloy-rpc-client.workspace = true -alloy-transport-http.workspace = true +alloy.workspace = true +# alloy-network.workspace = true +# alloy-primitives.workspace = true +# alloy-provider = { workspace = true, features = ["pubsub", "ws"] } +# alloy-rpc-types.workspace = true +# alloy-rpc-client.workspace = true +# alloy-transport-http.workspace = true eyre.workspace = true futures-util = "0.3" diff --git a/examples/queries/examples/query_contract_storage.rs b/examples/queries/examples/query_contract_storage.rs index 87e3d278..e583a187 100644 --- a/examples/queries/examples/query_contract_storage.rs +++ b/examples/queries/examples/query_contract_storage.rs @@ -1,16 +1,16 @@ //! Example of querying contract storage from the Ethereum network. -use alloy_network::Ethereum; -use alloy_primitives::{address, U256}; -use alloy_provider::{HttpProvider, Provider}; -use alloy_rpc_client::RpcClient; -use alloy_transport_http::Http; +use alloy::{ + network::Ethereum, + primitives::{address, U256}, + providers::{Provider, RootProvider}, +}; use eyre::Result; -use reqwest::Client; #[tokio::main] async fn main() -> Result<()> { - let provider = init(); + let url = "https://eth.merkle.io".parse().unwrap(); + let provider = RootProvider::::new_http(url); // Get slot0 from USDC-ETH Uniswap V3 pool let pool_address = address!("88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640"); @@ -23,8 +23,3 @@ async fn main() -> Result<()> { Ok(()) } - -fn init() -> HttpProvider { - let http = Http::::new("https://eth.merkle.io".parse().unwrap()); - HttpProvider::new(RpcClient::new(http, true)) -} diff --git a/examples/queries/examples/query_deployed_bytecode.rs b/examples/queries/examples/query_deployed_bytecode.rs index 9b413212..990308f6 100644 --- a/examples/queries/examples/query_deployed_bytecode.rs +++ b/examples/queries/examples/query_deployed_bytecode.rs @@ -1,17 +1,17 @@ //! Example of querying deployed bytecode of a contract on Ethereum network. -use alloy_network::Ethereum; -use alloy_primitives::address; -use alloy_provider::{HttpProvider, Provider}; -use alloy_rpc_client::RpcClient; -use alloy_rpc_types::{BlockId, BlockNumberOrTag}; -use alloy_transport_http::Http; +use alloy::{ + network::Ethereum, + primitives::address, + providers::{Provider, RootProvider}, + rpc::types::eth::{BlockId, BlockNumberOrTag}, +}; use eyre::Result; -use reqwest::Client; #[tokio::main] async fn main() -> Result<()> { - let provider = init(); + let url = "https://eth.merkle.io".parse().unwrap(); + let provider = RootProvider::::new_http(url); // Get bytecode of USDC-ETH Uniswap V3 pool let pool_address = address!("88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640"); @@ -23,8 +23,3 @@ async fn main() -> Result<()> { Ok(()) } - -fn init() -> HttpProvider { - let http = Http::::new("https://eth.llamarpc.com".parse().unwrap()); - HttpProvider::new(RpcClient::new(http, true)) -} diff --git a/examples/queries/examples/query_logs.rs b/examples/queries/examples/query_logs.rs index f9466ad6..004418e5 100644 --- a/examples/queries/examples/query_logs.rs +++ b/examples/queries/examples/query_logs.rs @@ -1,14 +1,14 @@ -use alloy_network::Ethereum; -use alloy_primitives::{address, fixed_bytes}; -use alloy_provider::{HttpProvider, Provider}; -use alloy_rpc_client::RpcClient; -use alloy_rpc_types::Filter; -use alloy_transport_http::Http; +use alloy::{ + network::Ethereum, + primitives::{address, fixed_bytes}, + providers::{Provider, RootProvider}, + rpc::types::eth::Filter, +}; use eyre::Result; -use reqwest::Client; #[tokio::main] async fn main() -> Result<()> { - let provider = init(); + let url = "https://eth.merkle.io".parse().unwrap(); + let provider = RootProvider::::new_http(url); // Get logs from the latest block let latest_block = provider.get_block_number().await?; @@ -49,8 +49,3 @@ async fn main() -> Result<()> { Ok(()) } - -fn init() -> HttpProvider { - let http = Http::::new("https://eth.merkle.io".parse().unwrap()); - HttpProvider::new(RpcClient::new(http, false)) -} From 4cf656e1394e998f8052fb684f739cc38a02012a Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Mon, 25 Mar 2024 11:03:54 -0400 Subject: [PATCH 7/7] nits --- Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 35278591..6e8a9837 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,6 @@ license = "MIT OR Apache-2.0" homepage = "https://github.com/alloy-rs/examples" repository = "https://github.com/alloy-rs/examples" publish = false -exclude = ["examples/"] [workspace.dependencies] alloy = { git = "https://github.com/alloy-rs/alloy", rev = "fd8f065", features = [