edb_common/types/
abi.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
17use std::{borrow::Borrow, fmt};
18
19use alloy_json_abi::Function;
20use alloy_primitives::Address;
21use foundry_compilers::artifacts::Contract;
22use serde::{Deserialize, Serialize};
23use tracing::error;
24
25/// Magic flag to identify state variables in the instrumented code.
26pub static EDB_STATE_VAR_FLAG: &str = "_edb_state_var_";
27
28/// The type of an callable ABI entry from outside the contract.
29#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
30pub enum AbiEntryTy {
31    /// A function that can be called.
32    Function,
33    /// A state variable that can be read.
34    StateVariable(u64),
35}
36
37/// Information about a callable entry in an ABI.
38#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
39pub struct CallableAbiEntry {
40    /// The name of the callable entry.
41    pub name: String,
42    /// The type of the callable entry.
43    pub ty: AbiEntryTy,
44    /// The input types of the callable entry.
45    pub inputs: Vec<String>,
46    /// The output types of the callable entry.
47    pub outputs: Vec<String>,
48    /// The actually function abi
49    pub abi: Function,
50}
51
52/// Contract type in terms of proxy pattern.
53#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
54pub enum ContractTy {
55    /// A normal contract.
56    Normal,
57    /// A proxy contract.
58    Proxy,
59    /// An implementation contract.
60    Implementation,
61}
62
63/// Information about all callable ABI entries of a contract.
64#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
65pub struct CallableAbiInfo {
66    /// The address of the contract.
67    pub address: Address,
68    /// The type of the address (normal, proxy, implementation).
69    pub contract_ty: ContractTy,
70    /// The callable ABI entries of the contract.
71    pub entries: Vec<CallableAbiEntry>,
72}
73
74impl<T> From<T> for CallableAbiEntry
75where
76    T: Borrow<Function>,
77{
78    fn from(function: T) -> Self {
79        let mut name = function.borrow().name.clone();
80
81        if let Some(pos) = name.find(EDB_STATE_VAR_FLAG) {
82            let uvid = name[pos + EDB_STATE_VAR_FLAG.len()..].parse::<u64>().unwrap_or_else(|e| {
83                error!(
84                    error=?e,
85                    "Failed to parse state variable ID from function name: {}",
86                    name
87                );
88                0
89            });
90            name.truncate(pos);
91            Self {
92                name,
93                ty: AbiEntryTy::StateVariable(uvid),
94                inputs: function.borrow().inputs.iter().map(|i| i.ty.clone()).collect(),
95                outputs: function.borrow().outputs.iter().map(|o| o.ty.clone()).collect(),
96                abi: function.borrow().clone(),
97            }
98        } else {
99            // This is a normal function.
100            Self {
101                name,
102                ty: AbiEntryTy::Function,
103                inputs: function.borrow().inputs.iter().map(|i| i.ty.clone()).collect(),
104                outputs: function.borrow().outputs.iter().map(|o| o.ty.clone()).collect(),
105                abi: function.borrow().clone(),
106            }
107        }
108    }
109}
110
111impl CallableAbiEntry {
112    /// Is this entry a state variable?
113    pub fn is_state_variable(&self) -> bool {
114        matches!(self.ty, AbiEntryTy::StateVariable(_))
115    }
116
117    /// Is this entry a function?
118    pub fn is_function(&self) -> bool {
119        matches!(self.ty, AbiEntryTy::Function)
120    }
121}
122
123impl fmt::Display for AbiEntryTy {
124    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
125        match self {
126            Self::Function => write!(f, "Function"),
127            Self::StateVariable(_) => write!(f, "State Variable"),
128        }
129    }
130}
131
132impl fmt::Display for CallableAbiEntry {
133    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
134        write!(f, "{:16} {}({})", format!("[{}]", self.ty), self.name, self.inputs.join(", "),)?;
135
136        if self.outputs.len() == 1 {
137            write!(f, " return {}", self.outputs[0])
138        } else if self.outputs.len() > 1 {
139            write!(f, " return ({})", self.outputs.join(", "))
140        } else {
141            Ok(())
142        }
143    }
144}
145
146impl fmt::Display for ContractTy {
147    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
148        match self {
149            Self::Normal => write!(f, "Normal"),
150            Self::Proxy => write!(f, "Proxy"),
151            Self::Implementation => write!(f, "Implementation"),
152        }
153    }
154}
155
156impl fmt::Display for CallableAbiInfo {
157    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
158        writeln!(f, "Contract: {} ({})", self.address, self.contract_ty)?;
159        writeln!(f, "Callable ABI Entries ({} entries):", self.entries.len())?;
160        for entry in &self.entries {
161            writeln!(f, "  {entry}")?;
162        }
163        Ok(())
164    }
165}
166
167/// Parse the callable ABI information from a contract.
168pub fn parse_callable_abi_info<T>(
169    addr: Address,
170    contract: T,
171    contract_ty: ContractTy,
172) -> CallableAbiInfo
173where
174    T: Borrow<Contract>,
175{
176    CallableAbiInfo { address: addr, contract_ty, entries: parse_callable_abi_entries(contract) }
177}
178
179/// Parse callable ABI entries from a contract
180pub fn parse_callable_abi_entries<T>(contract: T) -> Vec<CallableAbiEntry>
181where
182    T: Borrow<Contract>,
183{
184    let mut entries: Vec<CallableAbiEntry> = contract
185        .borrow()
186        .abi
187        .as_ref()
188        .map(|json_abi| json_abi.functions().map(CallableAbiEntry::from).collect())
189        .unwrap_or_default();
190
191    // Sort the entries by type (state variables first), then by name
192    entries.sort_by(|a, b| match (&a.ty, &b.ty) {
193        (AbiEntryTy::StateVariable(_), AbiEntryTy::Function) => std::cmp::Ordering::Greater,
194        (AbiEntryTy::Function, AbiEntryTy::StateVariable(_)) => std::cmp::Ordering::Less,
195        _ => a.name.cmp(&b.name),
196    });
197
198    entries
199}