1use 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
25pub static EDB_STATE_VAR_FLAG: &str = "_edb_state_var_";
27
28#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
30pub enum AbiEntryTy {
31 Function,
33 StateVariable(u64),
35}
36
37#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
39pub struct CallableAbiEntry {
40 pub name: String,
42 pub ty: AbiEntryTy,
44 pub inputs: Vec<String>,
46 pub outputs: Vec<String>,
48 pub abi: Function,
50}
51
52#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
54pub enum ContractTy {
55 Normal,
57 Proxy,
59 Implementation,
61}
62
63#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
65pub struct CallableAbiInfo {
66 pub address: Address,
68 pub contract_ty: ContractTy,
70 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 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 pub fn is_state_variable(&self) -> bool {
114 matches!(self.ty, AbiEntryTy::StateVariable(_))
115 }
116
117 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
167pub 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
179pub 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 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}