1use crate::{
7 errors::{EvmError, TokenError},
8 evm::TraceEvm,
9 types::{TokenInfo, ERC20_TRANSFER_EVENT_SIGNATURE},
10};
11use alloy::{
12 primitives::{Address, Bytes, FixedBytes, TxKind, U256},
13 sol,
14 sol_types::SolCall,
15};
16use anyhow::Result;
17use revm::{
18 context::TxEnv,
19 context_interface::result::{ExecutionResult, Output},
20 database::Database,
21 ExecuteEvm,
22};
23
24sol! {
33 function name() public returns (string);
34 function symbol() public returns (string);
35 function decimals() public returns (uint8);
36 function balanceOf(address owner) public returns (uint256);
37 function totalSupply() public returns (uint256);
38}
39
40pub fn query_erc20_balance<DB, INSP>(
53 evm: &mut TraceEvm<DB, INSP>,
54 token_address: Address,
55 owner: Address,
56) -> Result<U256>
57where
58 DB: Database,
59{
60 let data: Bytes = balanceOfCall { owner }.abi_encode().into();
61
62 let tx = TxEnv::builder()
64 .caller(Address::ZERO)
65 .kind(TxKind::Call(token_address))
66 .chain_id(Some(evm.cfg.chain_id))
67 .data(data)
68 .nonce(0) .build_fill();
70 let ref_tx = evm
71 .transact(tx)
72 .map_err(|e| anyhow::anyhow!("Failed to query ERC20 balance: {}", e))?;
73 let value = match ref_tx.result {
74 ExecutionResult::Success {
75 output: Output::Call(value),
76 ..
77 } => value,
78 _ => return Err(anyhow::anyhow!("Failed to execute balanceOf call")),
79 };
80 let balance = balanceOfCall::abi_decode_returns(&value)?;
81
82 Ok(balance)
83}
84
85fn query_token_info<DB, INSP>(
101 evm: &mut TraceEvm<DB, INSP>,
102 token_address: Address,
103 name_encoded: Bytes,
104 symbol_encoded: Bytes,
105 decimals_encoded: Bytes,
106 total_supply_encoded: Bytes,
107) -> Result<TokenInfo, TokenError>
108where
109 DB: Database,
110{
111 let tx_name = TxEnv {
112 caller: Address::ZERO,
113 kind: TxKind::Call(token_address),
114 data: name_encoded,
115 chain_id: Some(evm.cfg.chain_id),
116 nonce: 0,
117 ..Default::default()
118 };
119 let ref_tx = evm
120 .transact(tx_name)
121 .map_err(|e| TokenError::AnyhowError(format!("Failed to query token name: {e}")))?;
122 let name = match ref_tx.result {
123 ExecutionResult::Success {
124 output: Output::Call(value),
125 ..
126 } => nameCall::abi_decode_returns(&value).map_err(|_| TokenError::NameDecode {
127 address: token_address.to_string(),
128 reason: "Failed to decode name".to_string(),
129 })?,
130 _ => {
131 return Err(TokenError::CallReverted {
132 address: token_address.to_string(),
133 })
134 }
135 };
136
137 let tx_symbol = TxEnv {
138 caller: Address::ZERO,
139 kind: TxKind::Call(token_address),
140 chain_id: Some(evm.cfg.chain_id),
141 data: symbol_encoded,
142 ..Default::default()
143 };
144 let ref_tx = evm
145 .transact(tx_symbol)
146 .map_err(|e| TokenError::AnyhowError(format!("Failed to query token symbol: {e}")))?;
147 let symbol = match ref_tx.result {
148 ExecutionResult::Success {
149 output: Output::Call(value),
150 ..
151 } => symbolCall::abi_decode_returns(&value).map_err(|_| TokenError::SymbolDecode {
152 address: token_address.to_string(),
153 reason: "Failed to decode symbol".to_string(),
154 })?,
155 _ => {
156 return Err(TokenError::CallReverted {
157 address: token_address.to_string(),
158 })
159 }
160 };
161
162 let tx_decimals = TxEnv {
163 kind: TxKind::Call(token_address),
164 data: decimals_encoded,
165 chain_id: Some(evm.cfg.chain_id),
166 ..Default::default()
167 };
168 let ref_tx = evm
169 .transact(tx_decimals)
170 .map_err(|e| TokenError::AnyhowError(format!("Failed to query token decimals: {e}")))?;
171 let decimals = match ref_tx.result {
172 ExecutionResult::Success {
173 output: Output::Call(value),
174 ..
175 } => decimalsCall::abi_decode_returns(&value).map_err(|_| TokenError::DecimalsDecode {
176 address: token_address.to_string(),
177 reason: "Failed to decode decimals".to_string(),
178 })?,
179 _ => {
180 return Err(TokenError::CallReverted {
181 address: token_address.to_string(),
182 })
183 }
184 };
185 let tx_total_supply = TxEnv {
186 kind: TxKind::Call(token_address),
187 data: total_supply_encoded,
188 chain_id: Some(evm.cfg.chain_id),
189 ..Default::default()
190 };
191 let ref_tx = evm
192 .transact(tx_total_supply)
193 .map_err(|e| TokenError::AnyhowError(format!("Failed to query token total supply: {e}")))?;
194 let total_supply = match ref_tx.result {
195 ExecutionResult::Success {
196 output: Output::Call(value),
197 ..
198 } => totalSupplyCall::abi_decode_returns(&value).map_err(|_| {
199 TokenError::TotalSupplyDecode {
200 address: token_address.to_string(),
201 reason: "Failed to decode total supply".to_string(),
202 }
203 })?,
204 _ => {
205 return Err(TokenError::CallReverted {
206 address: token_address.to_string(),
207 })
208 }
209 };
210
211 Ok(TokenInfo {
212 name,
213 symbol,
214 decimals,
215 total_supply,
216 })
217}
218
219pub fn get_token_infos<DB, INSP>(
231 evm: &mut TraceEvm<DB, INSP>,
232 tokens: &[Address],
233) -> Result<Vec<TokenInfo>, EvmError>
234where
235 DB: Database,
236{
237 let name_encoded: Bytes = nameCall {}.abi_encode().into();
238 let symbol_encoded: Bytes = symbolCall {}.abi_encode().into();
239 let decimals_encoded: Bytes = decimalsCall {}.abi_encode().into();
240 let total_supply_encoded: Bytes = totalSupplyCall {}.abi_encode().into();
241 let mut token_infos = Vec::with_capacity(tokens.len());
242 for token in tokens {
243 let token_info = query_token_info(
244 evm,
245 *token,
246 name_encoded.clone(),
247 symbol_encoded.clone(),
248 decimals_encoded.clone(),
249 total_supply_encoded.clone(),
250 )?;
251 token_infos.push(token_info);
252 }
253
254 Ok(token_infos)
255}
256
257pub fn parse_transfer_log(
270 topics: &[FixedBytes<32>],
271 data: &[u8],
272) -> Option<(Address, Address, U256)> {
273 if topics.len() < 3 || topics[0] != ERC20_TRANSFER_EVENT_SIGNATURE {
274 return None;
275 }
276 let amount = U256::from_be_slice(data);
277 if !amount.is_zero() {
278 Some((
279 Address::from_slice(&topics[1].as_slice()[12..]),
280 Address::from_slice(&topics[2].as_slice()[12..]),
281 amount,
282 ))
283 } else {
284 None
285 }
286}