This document outlines the necessary work to prepare evm-proxy-tools for open source release, inspired by burntsushi's idiomatic Rust style (ripgrep, regex, walkdir).
The crate detects and reads EVM proxy contract implementations. Core functionality works (9 tests pass), but the API design, documentation, and code organization need significant improvements to meet open source quality standards.
Current State:
- Compiles with 1 dead code warning
- 8 clippy warnings (minor)
- No public documentation
- Mixed abstraction levels
- Inconsistent naming conventions
Problem: ProxyDetector trait exists but is underutilized. Detection and reading are separate concepts but not well abstracted.
Solution: Create a clean trait hierarchy inspired by burntsushi's approach:
/// A detector that can identify proxy patterns from bytecode.
pub trait Detector {
/// The type of result this detector produces.
type Match;
/// Attempts to detect a proxy pattern in the given bytecode.
/// Returns `None` if this detector doesn't match.
fn detect(&self, bytecode: &[u8]) -> Option<Self::Match>;
}
/// A reader that can resolve proxy implementation addresses.
pub trait Reader {
/// Read the implementation address(es) for a detected proxy.
fn read<P: Provider>(
&self,
provider: &P,
address: Address,
block: Option<u64>,
) -> impl Future<Output = Result<Implementation, ReadError>>;
}Tasks:
- Rename
ProxyDetectortrait toDetector - Create
Readertrait for implementation resolution - Make
MinimalProxyandStorageSlotProxypublic with trait impls - Add
DetectorChainfor composing multiple detectors
Problem: Functions return Option<(ProxyType, ProxyDispatch)> - tuple is not self-documenting.
Solution:
/// The result of proxy detection.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Detection {
/// The type of proxy detected.
pub proxy_type: ProxyType,
/// How to dispatch to find the implementation.
pub dispatch: Dispatch,
}Tasks:
- Create
Detectionstruct - Rename
ProxyDispatchtoDispatch(shorter, still clear) - Update all detection functions to return
Option<Detection>
Problem: Several naming issues violate Rust conventions:
ProxyType::EIP_1167uses underscores (should beEip1167)ProxyDispatch::Facet_EIP_2535mixes conventionsFacetStorageSlotis ambiguous#[allow(non_camel_case_types)]is a code smell
Tasks:
- Rename all
EIP_*variants toEip*(e.g.,Eip1167,Eip1967) - Rename
Facet_EIP_2535toDiamondFacets - Rename
FacetStorageSlottoDiamondStorage - Remove
#[allow(non_camel_case_types)] - Add
#[non_exhaustive]to enums for future compatibility
Problem: ProxyReadError exists but ProxyDetectError is unused. No unified error story.
Solution: Single, comprehensive error type:
/// Errors that can occur during proxy detection or reading.
#[derive(Debug)]
pub struct Error {
kind: ErrorKind,
}
#[derive(Debug)]
enum ErrorKind {
/// No proxy pattern detected.
NotAProxy,
/// RPC communication failed.
Rpc(String),
/// Storage value is not a valid address.
InvalidStorageValue,
/// Proxy delegates to external contract (cannot resolve directly).
ExternalProxy { address: Address, selector: u32 },
/// EVM execution failed during detection.
Execution(String),
}
impl Error {
/// Returns true if this error indicates no proxy was found.
pub fn is_not_proxy(&self) -> bool { ... }
/// If this is an external proxy error, returns the external address.
pub fn external_address(&self) -> Option<Address> { ... }
}Tasks:
- Create unified
Errortype withErrorKindinner enum - Add helper methods for common error queries
- Implement
std::error::Errorproperly - Remove unused
ProxyDetectError::Custom - Consider adding
#[non_exhaustive]for future error variants
src/
lib.rs # Minimal re-exports
detect.rs # Detection logic + EVM tracing (too much)
read.rs # Implementation reading
types.rs # ProxyType, ProxyDispatch
proxy_inspector.rs # REVM inspector + DB
consts.rs # Magic constants
utils.rs # Byte conversion utilities
src/
lib.rs # Public API, re-exports, top-level docs
error.rs # Unified error types
types.rs # Detection, ProxyType, Dispatch, Implementation
detect/
mod.rs # Detector trait, DetectorChain, detect()
minimal.rs # EIP-1167, EIP-7511, EIP-3448 (static proxies)
storage.rs # EIP-897, EIP-1967, EIP-1822 (storage slot proxies)
diamond.rs # EIP-2535 diamond detection
read/
mod.rs # Reader trait, read()
storage.rs # Read from storage slots
diamond.rs # Read diamond facets
evm/
mod.rs # EVM execution helpers
inspector.rs # ProxyInspector
db.rs # ProxyDetectDB
constants.rs # Storage slots, byte patterns
util.rs # Internal utilities (not pub)
Tasks:
- Split
detect.rsintodetect/module with submodules - Move
ProxyInspectorandProxyDetectDBtoevm/module - Create
read/module structure - Keep internal utilities private (
pub(crate))
Problem: No crate documentation. README is minimal.
Tasks:
-
Add comprehensive
//!doc at top oflib.rs:- What the crate does
- Quick start example
- Supported proxy types with links to EIPs
- Feature flags (if any)
-
Expand README.md:
- Badge section (crates.io, docs.rs, CI)
- Installation instructions
- Usage examples
- Supported proxy standards table
- Contributing guidelines link
Problem: Zero doc comments on public items.
Tasks: Add /// docs to ALL public items:
-
ProxyType- document each variant with EIP links -
Dispatch- explain each dispatch mechanism -
Detection- usage examples -
Implementation- explain Single vs Multiple vs Facets -
get_proxy_type()- main entry point, needs examples -
get_proxy_implementation()- async usage example - All error types and variants
Tasks:
- Add comments explaining magic hex constants in
consts.rs - Document the EVM tracing strategy in
proxy_inspector.rs - Explain the "taint tracking" approach for storage slot detection
- Add
# Safetysections if any unsafe code is added
Current warnings (8):
needless_borrowinextract_minimal_contractmanual_mapin detection chain (2 instances)identity_opin utils (3 instances with<< 0)single_matchin inspector (match vs if)uninlined_format_argsin binaries (4 instances)
Tasks:
- Run
cargo clippy --fixfor auto-fixable issues - Manually fix remaining warnings
- Add
#![warn(clippy::all, clippy::pedantic)]to lib.rs - Address or explicitly allow pedantic warnings
- Remove unused
ProxyDetectError::Customvariant - Remove or implement commented
Tainterstructs inproxy_inspector.rs - Clean up commented code throughout
Problem: Using u32 for function selectors is error-prone.
Tasks:
- Create
Selectornewtype:pub struct Selector([u8; 4]); - Implement
From<[u8; 4]>,From<u32>,Display,Debug - Replace
u32withSelectorinProxyDispatch::External - Replace
u32inProxyImplementation::Facets
Problem: Three similar byte-to-u32 functions with manual bit shifting.
Tasks:
- Use
u32::from_be_bytes/u32::from_le_bytes - Reduce to single generic function or remove if standard lib suffices
Problem: read_diamond_implementation() returns empty vec with TODO.
Tasks:
- Implement storage-based diamond facet reading
- Parse diamond storage layout (facet array structure)
- Add tests with real diamond contract bytecode
let detector = Detector::builder()
.with_minimal_proxies(true)
.with_storage_proxies(true)
.with_diamonds(true)
.build();
let result = detector.detect(&bytecode)?;Problem: get_proxy_implementation is async-only.
Tasks:
- Consider
blockingfeature flag for sync API - Or document how to use with
tokio::runtime::Runtime::block_on
Current: 9 tests covering happy paths.
Tasks:
- Add tests for error cases
- Add tests for edge cases (empty bytecode, malformed proxies)
- Add property-based tests for byte pattern matching
- Add integration tests with real RPC (behind feature flag)
Tasks:
- Fix
author→authors = ["snf"] - Add
description,license,repository,keywords,categories - Add
rust-versionMSRV - Review and minimize dependencies
- Add feature flags for optional functionality
Tasks:
- Add GitHub Actions workflow:
cargo checkcargo testcargo clippy -- -D warningscargo fmt -- --checkcargo doc
- Add Dependabot for dependency updates
- Add CHANGELOG.md
- Add CONTRIBUTING.md
Tasks:
- Verify LICENSE file is complete
- Add SPDX headers to source files (optional)
- Add license badge to README
- Fix Cargo.toml metadata
- Fix all clippy warnings
- Remove dead code
- Add crate-level documentation
- Create
Detectionstruct - Rename enum variants (remove underscores)
- Create unified
Errortype - Refactor
Detectortrait
- Split into
detect/,read/,evm/modules - Create
Readertrait - Add public API documentation
- Complete diamond implementation
- Add
Selectornewtype - Expand test coverage
- Set up CI/CD
- Final README and CHANGELOG
-
cargo clippy -- -D warningspasses -
cargo testpasses with >80% coverage -
cargo docgenerates without warnings - All public items have documentation
- README includes working examples
- CI pipeline is green
- Crate compiles on stable Rust (document MSRV)
- EIP-1167: Minimal Proxy Contract
- EIP-1967: Proxy Storage Slots
- EIP-2535: Diamond Standard
- ripgrep - API design inspiration
- walkdir - Trait design inspiration
- Rust API Guidelines