edb_engine/utils/
artifact.rs

1// EDB - Ethereum Debugger
2// Copyright (C) 2024 Zhuo Zhang and Wuqi Zhang
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU Affero General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU Affero General Public License for more details.
13//
14// You should have received a copy of the GNU Affero General Public License
15// along with this program. If not, see <https://www.gnu.org/licenses/>.
16
17//! Contract artifact management and metadata handling.
18//!
19//! This module provides utilities for managing compiled contract artifacts, including
20//! metadata extraction, contract access, and constructor argument handling. The [`Artifact`]
21//! struct consolidates all compilation-related data for contracts used in debugging.
22//!
23//! # Core Functionality
24//!
25//! ## Artifact Management
26//! - **Metadata Storage**: Contract metadata including name, version, and constructor args
27//! - **Compilation Data**: Full Solidity compiler input and output
28//! - **Contract Access**: Easy access to compiled contract bytecode and ABI
29//! - **Constructor Handling**: Support for constructor argument extraction and validation
30//!
31//! ## Integration Points
32//! The artifact system integrates with:
33//! - **Etherscan**: Loading verified contract source code and metadata
34//! - **Foundry Compilers**: Handling Solidity compilation workflows
35//! - **Code Tweaking**: Supporting bytecode replacement through recompilation
36//! - **Analysis Engine**: Providing source code and ABI data for instrumentation
37
38use alloy_primitives::Bytes;
39use foundry_block_explorers::contract::Metadata;
40use foundry_compilers::artifacts::{CompilerOutput, Contract, SolcInput};
41use serde::{Deserialize, Serialize};
42use tracing::error;
43
44/// Compiled contract artifact with comprehensive metadata and compilation data.
45///
46/// This struct consolidates all information about a compiled contract, including
47/// the original metadata from Etherscan, compiler input configuration, and the
48/// complete compilation output. It serves as the primary container for contract
49/// data throughout the debugging workflow.
50#[derive(Debug, Clone, Serialize, Deserialize)]
51pub struct Artifact {
52    /// Metadata about the contract.
53    pub meta: Metadata,
54    /// Input for the Solidity compiler.
55    pub input: SolcInput,
56    /// Output from the Solidity compiler.
57    pub output: CompilerOutput,
58}
59
60impl Artifact {
61    /// Returns the contract name.
62    pub fn contract_name(&self) -> &str {
63        self.meta.contract_name.as_str()
64    }
65
66    /// Returns the compiler version.
67    pub fn compiler_version(&self) -> &str {
68        self.meta.compiler_version.as_str()
69    }
70
71    /// Returns the constructor arguments.
72    pub fn constructor_arguments(&self) -> &Bytes {
73        &self.meta.constructor_arguments
74    }
75
76    /// Subject contract
77    pub fn contract(&self) -> Option<&Contract> {
78        let contract_name = self.contract_name();
79
80        self.output
81            .contracts
82            .values()
83            .into_iter()
84            .find(|c| c.contains_key(contract_name))
85            .and_then(|contracts| contracts.get(contract_name))
86    }
87
88    /// Find creation hooks (one-to-one mapping)
89    pub fn find_creation_hooks<'a>(
90        &'a self,
91        recompiled: &'a Self,
92    ) -> Vec<(&'a Contract, &'a Contract, &'a Bytes)> {
93        let mut hooks = Vec::new();
94
95        for (path, contracts) in &self.output.contracts {
96            for (name, contract) in contracts {
97                if let Some(recompiled_contract) =
98                    recompiled.output.contracts.get(path).and_then(|c| c.get(name))
99                {
100                    hooks.push((contract, recompiled_contract, self.constructor_arguments()));
101                } else {
102                    error!("No recompiled contract found for {} in {}", name, path.display());
103                }
104            }
105        }
106
107        hooks
108    }
109}