From 548ae53f7e1cdf07a708161deef2793037ef3715 Mon Sep 17 00:00:00 2001 From: Miguel Palhas Date: Mon, 3 Feb 2025 17:53:44 +0000 Subject: [PATCH] Supporting EIP1967 direct and beacon proxies --- Cargo.lock | 77 +++++++++++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 1 + src/eip1167.rs | 8 ++---- src/eip1967.rs | 56 ++++++++++++++++++++++++++++++++++++ src/lib.rs | 26 ++++++++++++----- 5 files changed, 154 insertions(+), 14 deletions(-) create mode 100644 src/eip1967.rs diff --git a/Cargo.lock b/Cargo.lock index 848c819..3638fe0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,15 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + [[package]] name = "allocator-api2" version = "0.2.21" @@ -1165,6 +1174,7 @@ version = "0.1.0" dependencies = [ "alloy", "lazy_static", + "rstest", "thiserror", "tokio", ] @@ -1321,6 +1331,12 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" +[[package]] +name = "futures-timer" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" + [[package]] name = "futures-util" version = "0.3.31" @@ -2289,7 +2305,7 @@ dependencies = [ "once_cell", "socket2", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -2356,12 +2372,41 @@ dependencies = [ "bitflags", ] +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + [[package]] name = "regex-syntax" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +[[package]] +name = "relative-path" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" + [[package]] name = "reqwest" version = "0.12.12" @@ -2443,6 +2488,36 @@ dependencies = [ "rustc-hex", ] +[[package]] +name = "rstest" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03e905296805ab93e13c1ec3a03f4b6c4f35e9498a3d5fa96dc626d22c03cd89" +dependencies = [ + "futures-timer", + "futures-util", + "rstest_macros", + "rustc_version 0.4.1", +] + +[[package]] +name = "rstest_macros" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef0053bbffce09062bee4bcc499b0fbe7a57b879f1efe088d6d8d4c7adcdef9b" +dependencies = [ + "cfg-if", + "glob", + "proc-macro-crate", + "proc-macro2", + "quote", + "regex", + "relative-path", + "rustc_version 0.4.1", + "syn 2.0.96", + "unicode-ident", +] + [[package]] name = "ruint" version = "1.12.3" diff --git a/Cargo.toml b/Cargo.toml index ac5b4fa..66d0d6b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" [dependencies] alloy = { version = "0.11.0", features = ["providers", "reqwest-rustls-tls"] } +rstest = "0.24.0" thiserror = "2.0.11" [dev-dependencies] diff --git a/src/eip1167.rs b/src/eip1167.rs index 5e21ea1..6287004 100644 --- a/src/eip1167.rs +++ b/src/eip1167.rs @@ -1,7 +1,4 @@ -use alloy::{ - hex, - primitives::{bytes, Address, Bytes}, -}; +use alloy::primitives::{bytes, Address, Bytes}; const EIP1167_PREFIX: Bytes = bytes!("363d3d373d3d3d363d"); const EIP1167_SUFFIX: Bytes = bytes!("57fd5bf3"); @@ -23,13 +20,12 @@ pub(crate) fn detect_eip1167_minimal_proxy(code: &Bytes) -> Option
{ let address_pos = EIP1167_PREFIX.len() + 1; let suffix = &code[address_pos + address_len + EIP1167_SUFFIX_OFFSET_FROM_ADDRESS_END..]; - dbg!(&hex::encode(suffix)); if !suffix.starts_with(&EIP1167_SUFFIX) { return None; } let address_hex = &code[address_pos..address_pos + address_len]; - let address = Address::from_slice(address_hex); + let address = Address::left_padding_from(address_hex); Some(address) } diff --git a/src/eip1967.rs b/src/eip1967.rs new file mode 100644 index 0000000..b907f39 --- /dev/null +++ b/src/eip1967.rs @@ -0,0 +1,56 @@ +use alloy::{ + network::Network, + primitives::{b256, Address, Bytes, B256, U256}, + providers::Provider, +}; + +use crate::{error::DetectProxyResult, ProxyType}; + +// bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1) +const EIP1967_LOGIC_SLOT: B256 = + b256!("0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc"); +const EIP1967_BEACON_SLOT: B256 = + b256!("0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50"); + +pub(crate) async fn detect_eip1967_direct_proxy>( + address: Address, + provider: P, +) -> DetectProxyResult> +where + N: Network, +{ + if let Ok(Some(addr)) = storage_slot_as_address(&provider, address, EIP1967_LOGIC_SLOT).await { + return Ok(Some(ProxyType::Eip1967Direct(addr))); + } + + if let Ok(Some(addr)) = storage_slot_as_address(&provider, address, EIP1967_BEACON_SLOT).await { + return Ok(Some(ProxyType::Eip1967Beacon(addr))); + } + + Ok(None) +} + +async fn storage_slot_as_address>( + provider: P, + address: Address, + slot: B256, +) -> DetectProxyResult> +where + N: Network, +{ + let slot = provider + .get_storage_at(address, slot.into()) + .latest() + .await?; + + if !slot.is_zero() { + return Ok(Some(u256_to_address(slot))); + } + + Ok(None) +} + +fn u256_to_address(u256: U256) -> Address { + let bytes: Bytes = u256.to_be_bytes::<32>().into(); + Address::from_slice(&bytes[12..]) +} diff --git a/src/lib.rs b/src/lib.rs index 8a8e029..a928be6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ mod eip1167; +mod eip1967; mod error; use alloy::{network::Network, primitives::Address, providers::Provider}; @@ -7,6 +8,8 @@ use error::DetectProxyResult; #[derive(Debug, PartialEq)] pub enum ProxyType { Eip1167(Address), + Eip1967Direct(Address), + Eip1967Beacon(Address), } pub async fn detect_proxy>( @@ -22,6 +25,10 @@ where return Ok(Some(ProxyType::Eip1167(address))); } + if let Some(proxy_type) = eip1967::detect_eip1967_direct_proxy(address, provider).await? { + return Ok(Some(proxy_type)); + } + Ok(None) } @@ -30,9 +37,8 @@ mod tests { use super::*; use alloy::{primitives::address, providers::ProviderBuilder, transports::http::reqwest::Url}; use lazy_static::lazy_static; + use rstest::*; - const MAINNET_EIP1167_PROXY: Address = address!("0x6d5d9b6ec51c15f45bfa4c460502403351d5b999"); - const MAINNET_EIP1167_IMPL: Address = address!("0x210fF9Ced719E9bf2444DbC3670BAC99342126fA"); lazy_static! { static ref MAINNET_RPC: Url = Url::parse( &std::env::var("ETH_MAINNET_RPC").unwrap_or("https://eth.rpc.blxrbdn.com".to_string()) @@ -40,13 +46,19 @@ mod tests { .unwrap(); } + #[rstest] + #[case::eip1167(address!("0x6d5d9b6ec51c15f45bfa4c460502403351d5b999"), ProxyType::Eip1167(address!("0x210fF9Ced719E9bf2444DbC3670BAC99342126fA")))] + #[case::eip1167_vanity(address!("0xa81043fd06D57D140f6ad8C2913DbE87fdecDd5F"), ProxyType::Eip1167(address!("0x0000000010fd301be3200e67978e3cc67c962f48")))] + #[case::eip1967_direct(address!("0xA7AeFeaD2F25972D80516628417ac46b3F2604Af"), ProxyType::Eip1967Direct(address!("0x4bd844f72a8edd323056130a86fc624d0dbcf5b0")))] + #[case::eip1967_direct(address!("0x8260b9eC6d472a34AD081297794d7Cc00181360a"), ProxyType::Eip1967Direct(address!("0xe4e4003afe3765aca8149a82fc064c0b125b9e5a")))] + #[case::eip1967_beacon(address!("0xDd4e2eb37268B047f55fC5cAf22837F9EC08A881"), ProxyType::Eip1967Beacon(address!("0xb3e0edda8c2aeabfdece18ad7ac1ea86eb7d583b")))] + #[case::eip1967_beacon(address!("0x114f1388fAB456c4bA31B1850b244Eedcd024136"), ProxyType::Eip1967Beacon(address!("0xbe86f647b167567525ccaafcd6f881f1ee558216")))] #[tokio::test] - async fn mainnet_eip1167() { + async fn mainnet(#[case] proxy: Address, #[case] impl_: ProxyType) { let provider = ProviderBuilder::new().on_http(MAINNET_RPC.clone()); - let result = detect_proxy(MAINNET_EIP1167_PROXY, &provider) - .await - .unwrap(); - assert_eq!(result, Some(ProxyType::Eip1167(MAINNET_EIP1167_IMPL))); + let result = detect_proxy(proxy, &provider).await.unwrap(); + + assert_eq!(result, Some(impl_)); } }