foundry_evm_traces/decoder/
precompiles.rs1use crate::{CallTrace, DecodedCallData};
2use alloy_primitives::{Address, B256, U256, hex};
3use alloy_sol_types::{SolCall, abi, sol};
4use foundry_evm_core::precompiles::{
5 BLAKE_2F, EC_ADD, EC_MUL, EC_PAIRING, EC_RECOVER, IDENTITY, MOD_EXP, POINT_EVALUATION,
6 RIPEMD_160, SHA_256,
7};
8use itertools::Itertools;
9use revm_inspectors::tracing::types::DecodedCallTrace;
10
11sol! {
12interface Precompiles {
17 struct EcPairingInput {
18 uint256 x1;
19 uint256 y1;
20 uint256 x2;
21 uint256 y2;
22 uint256 x3;
23 uint256 y3;
24 }
25
26 function ecrecover(bytes32 hash, uint8 v, uint256 r, uint256 s) returns (address publicAddress);
27 function sha256(bytes data) returns (bytes32 hash);
28 function ripemd(bytes data) returns (bytes20 hash);
29 function identity(bytes data) returns (bytes data);
30 function modexp(uint256 Bsize, uint256 Esize, uint256 Msize, bytes B, bytes E, bytes M) returns (bytes value);
31 function ecadd(uint256 x1, uint256 y1, uint256 x2, uint256 y2) returns (uint256 x, uint256 y);
32 function ecmul(uint256 x1, uint256 y1, uint256 s) returns (uint256 x, uint256 y);
33 function ecpairing(EcPairingInput[] input) returns (bool success);
34 function blake2f(uint32 rounds, uint64[8] h, uint64[16] m, uint64[2] t, bool f) returns (uint64[8] h);
35 function pointEvaluation(bytes32 versionedHash, bytes32 z, bytes32 y, bytes1[48] commitment, bytes1[48] proof) returns (bytes value);
36}
37}
38use Precompiles::*;
39
40pub(super) fn is_known_precompile(address: Address, _chain_id: u64) -> bool {
41 address[..19].iter().all(|&x| x == 0)
42 && matches!(
43 address,
44 EC_RECOVER
45 | SHA_256
46 | RIPEMD_160
47 | IDENTITY
48 | MOD_EXP
49 | EC_ADD
50 | EC_MUL
51 | EC_PAIRING
52 | BLAKE_2F
53 | POINT_EVALUATION
54 )
55}
56
57pub(super) fn decode(trace: &CallTrace, _chain_id: u64) -> Option<DecodedCallTrace> {
59 if !is_known_precompile(trace.address, _chain_id) {
60 return None;
61 }
62
63 for &precompile in PRECOMPILES {
64 if trace.address == precompile.address() {
65 let signature = precompile.signature();
66
67 let args = precompile
68 .decode_call(&trace.data)
69 .unwrap_or_else(|_| vec![trace.data.to_string()]);
70
71 let return_data = precompile
72 .decode_return(&trace.output)
73 .unwrap_or_else(|_| vec![trace.output.to_string()]);
74 let return_data = if return_data.len() == 1 {
75 return_data.into_iter().next().unwrap()
76 } else {
77 format!("({})", return_data.join(", "))
78 };
79
80 return Some(DecodedCallTrace {
81 label: Some("PRECOMPILES".to_string()),
82 call_data: Some(DecodedCallData { signature: signature.to_string(), args }),
83 return_data: Some(return_data),
84 });
85 }
86 }
87
88 None
89}
90
91pub(super) trait Precompile {
92 fn address(&self) -> Address;
93 fn signature(&self) -> &'static str;
94
95 fn decode_call(&self, data: &[u8]) -> alloy_sol_types::Result<Vec<String>> {
96 Ok(vec![hex::encode_prefixed(data)])
97 }
98
99 fn decode_return(&self, data: &[u8]) -> alloy_sol_types::Result<Vec<String>> {
100 Ok(vec![hex::encode_prefixed(data)])
101 }
102}
103
104const PRECOMPILES: &[&dyn Precompile] = &[
108 &Ecrecover,
109 &Sha256,
110 &Ripemd160,
111 &Identity,
112 &ModExp,
113 &EcAdd,
114 &Ecmul,
115 &Ecpairing,
116 &Blake2f,
117 &PointEvaluation,
118];
119
120struct Ecrecover;
121impl Precompile for Ecrecover {
122 fn address(&self) -> Address {
123 EC_RECOVER
124 }
125
126 fn signature(&self) -> &'static str {
127 ecrecoverCall::SIGNATURE
128 }
129
130 fn decode_call(&self, data: &[u8]) -> alloy_sol_types::Result<Vec<String>> {
131 let ecrecoverCall { hash, v, r, s } = ecrecoverCall::abi_decode_raw(data)?;
132 Ok(vec![hash.to_string(), v.to_string(), r.to_string(), s.to_string()])
133 }
134
135 fn decode_return(&self, data: &[u8]) -> alloy_sol_types::Result<Vec<String>> {
136 let ret = ecrecoverCall::abi_decode_returns(data)?;
137 Ok(vec![ret.to_string()])
138 }
139}
140
141struct Sha256;
142impl Precompile for Sha256 {
143 fn address(&self) -> Address {
144 SHA_256
145 }
146
147 fn signature(&self) -> &'static str {
148 sha256Call::SIGNATURE
149 }
150
151 fn decode_return(&self, data: &[u8]) -> alloy_sol_types::Result<Vec<String>> {
152 let ret = sha256Call::abi_decode_returns(data)?;
153 Ok(vec![ret.to_string()])
154 }
155}
156
157struct Ripemd160;
158impl Precompile for Ripemd160 {
159 fn address(&self) -> Address {
160 RIPEMD_160
161 }
162
163 fn signature(&self) -> &'static str {
164 ripemdCall::SIGNATURE
165 }
166
167 fn decode_return(&self, data: &[u8]) -> alloy_sol_types::Result<Vec<String>> {
168 let ret = ripemdCall::abi_decode_returns(data)?;
169 Ok(vec![ret.to_string()])
170 }
171}
172
173struct Identity;
174impl Precompile for Identity {
175 fn address(&self) -> Address {
176 IDENTITY
177 }
178
179 fn signature(&self) -> &'static str {
180 identityCall::SIGNATURE
181 }
182}
183
184struct ModExp;
185impl Precompile for ModExp {
186 fn address(&self) -> Address {
187 MOD_EXP
188 }
189
190 fn signature(&self) -> &'static str {
191 modexpCall::SIGNATURE
192 }
193
194 fn decode_call(&self, data: &[u8]) -> alloy_sol_types::Result<Vec<String>> {
195 let mut decoder = abi::Decoder::new(data);
196 let b_size = decoder.take_offset()?;
197 let e_size = decoder.take_offset()?;
198 let m_size = decoder.take_offset()?;
199 let b = decoder.take_slice(b_size)?;
200 let e = decoder.take_slice(e_size)?;
201 let m = decoder.take_slice(m_size)?;
202 Ok(vec![
203 b_size.to_string(),
204 e_size.to_string(),
205 m_size.to_string(),
206 hex::encode_prefixed(b),
207 hex::encode_prefixed(e),
208 hex::encode_prefixed(m),
209 ])
210 }
211}
212
213struct EcAdd;
214impl Precompile for EcAdd {
215 fn address(&self) -> Address {
216 EC_ADD
217 }
218
219 fn signature(&self) -> &'static str {
220 ecaddCall::SIGNATURE
221 }
222
223 fn decode_call(&self, data: &[u8]) -> alloy_sol_types::Result<Vec<String>> {
224 let ecaddCall { x1, y1, x2, y2 } = ecaddCall::abi_decode_raw(data)?;
225 Ok(vec![x1.to_string(), y1.to_string(), x2.to_string(), y2.to_string()])
226 }
227
228 fn decode_return(&self, data: &[u8]) -> alloy_sol_types::Result<Vec<String>> {
229 let ecaddReturn { x, y } = ecaddCall::abi_decode_returns(data)?;
230 Ok(vec![x.to_string(), y.to_string()])
231 }
232}
233
234struct Ecmul;
235impl Precompile for Ecmul {
236 fn address(&self) -> Address {
237 EC_MUL
238 }
239
240 fn signature(&self) -> &'static str {
241 ecmulCall::SIGNATURE
242 }
243
244 fn decode_call(&self, data: &[u8]) -> alloy_sol_types::Result<Vec<String>> {
245 let ecmulCall { x1, y1, s } = ecmulCall::abi_decode_raw(data)?;
246 Ok(vec![x1.to_string(), y1.to_string(), s.to_string()])
247 }
248
249 fn decode_return(&self, data: &[u8]) -> alloy_sol_types::Result<Vec<String>> {
250 let ecmulReturn { x, y } = ecmulCall::abi_decode_returns(data)?;
251 Ok(vec![x.to_string(), y.to_string()])
252 }
253}
254
255struct Ecpairing;
256impl Precompile for Ecpairing {
257 fn address(&self) -> Address {
258 EC_PAIRING
259 }
260
261 fn signature(&self) -> &'static str {
262 ecpairingCall::SIGNATURE
263 }
264
265 fn decode_call(&self, data: &[u8]) -> alloy_sol_types::Result<Vec<String>> {
266 let mut decoder = abi::Decoder::new(data);
267 let mut values = Vec::new();
268 let mut tmp = <[&B256; 6]>::default();
270 while !decoder.is_empty() {
271 for tmp in &mut tmp {
272 *tmp = decoder.take_word()?;
273 }
274 values.push(iter_to_string(tmp.iter().map(|x| U256::from_be_bytes(x.0))));
275 }
276 Ok(values)
277 }
278
279 fn decode_return(&self, data: &[u8]) -> alloy_sol_types::Result<Vec<String>> {
280 let ret = ecpairingCall::abi_decode_returns(data)?;
281 Ok(vec![ret.to_string()])
282 }
283}
284
285struct Blake2f;
286impl Precompile for Blake2f {
287 fn address(&self) -> Address {
288 BLAKE_2F
289 }
290
291 fn signature(&self) -> &'static str {
292 blake2fCall::SIGNATURE
293 }
294
295 fn decode_call(&self, data: &[u8]) -> alloy_sol_types::Result<Vec<String>> {
296 decode_blake2f(data)
297 }
298}
299
300fn decode_blake2f<'a>(data: &'a [u8]) -> alloy_sol_types::Result<Vec<String>> {
301 let mut decoder = abi::Decoder::new(data);
302 let rounds = u32::from_be_bytes(decoder.take_slice(4)?.try_into().unwrap());
303 let u64_le_list =
304 |x: &'a [u8]| x.chunks_exact(8).map(|x| u64::from_le_bytes(x.try_into().unwrap()));
305 let h = u64_le_list(decoder.take_slice(64)?);
306 let m = u64_le_list(decoder.take_slice(128)?);
307 let t = u64_le_list(decoder.take_slice(16)?);
308 let f = decoder.take_slice(1)?[0];
309 Ok(vec![
310 rounds.to_string(),
311 iter_to_string(h),
312 iter_to_string(m),
313 iter_to_string(t),
314 f.to_string(),
315 ])
316}
317
318struct PointEvaluation;
319impl Precompile for PointEvaluation {
320 fn address(&self) -> Address {
321 POINT_EVALUATION
322 }
323
324 fn signature(&self) -> &'static str {
325 pointEvaluationCall::SIGNATURE
326 }
327
328 fn decode_call(&self, data: &[u8]) -> alloy_sol_types::Result<Vec<String>> {
329 let mut decoder = abi::Decoder::new(data);
330 let versioned_hash = decoder.take_word()?;
331 let z = decoder.take_word()?;
332 let y = decoder.take_word()?;
333 let commitment = decoder.take_slice(48)?;
334 let proof = decoder.take_slice(48)?;
335 Ok(vec![
336 versioned_hash.to_string(),
337 z.to_string(),
338 y.to_string(),
339 hex::encode_prefixed(commitment),
340 hex::encode_prefixed(proof),
341 ])
342 }
343}
344
345fn iter_to_string<I: Iterator<Item = T>, T: std::fmt::Display>(iter: I) -> String {
346 format!("[{}]", iter.format(", "))
347}
348
349#[cfg(test)]
350mod tests {
351 use super::*;
352 use alloy_primitives::hex;
353
354 #[test]
355 fn ecpairing() {
356 let data = hex!(
358 "
359 26bbb723f965460ca7282cd75f0e3e7c67b15817f7cee60856b394936ed02917
360 0fbe873ac672168143a91535450bab6c412dce8dc8b66a88f2da6e245f9282df
361 13cd4f0451538ece5014fe6688b197aefcc611a5c6a7c319f834f2188ba04b08
362 126ff07e81490a1b6ae92b2d9e700c8e23e9d5c7f6ab857027213819a6c9ae7d
363 04183624c9858a56c54deb237c26cb4355bc2551312004e65fc5b299440b15a3
364 2e4b11aa549ad6c667057b18be4f4437fda92f018a59430ebb992fa3462c9ca1
365 2d4d9aa7e302d9df41749d5507949d05dbea33fbb16c643b22f599a2be6df2e2
366 14bedd503c37ceb061d8ec60209fe345ce89830a19230301f076caff004d1926
367 0967032fcbf776d1afc985f88877f182d38480a653f2decaa9794cbc3bf3060c
368 0e187847ad4c798374d0d6732bf501847dd68bc0e071241e0213bc7fc13db7ab
369 304cfbd1e08a704a99f5e847d93f8c3caafddec46b7a0d379da69a4d112346a7
370 1739c1b1a457a8c7313123d24d2f9192f896b7c63eea05a9d57f06547ad0cec8
371 001d6fedb032f70e377635238e0563f131670001f6abf439adb3a9d5d52073c6
372 1889afe91e4e367f898a7fcd6464e5ca4e822fe169bccb624f6aeb87e4d060bc
373 198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2
374 1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed
375 090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b
376 12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa
377 2dde6d7baf0bfa09329ec8d44c38282f5bf7f9ead1914edd7dcaebb498c84519
378 0c359f868a85c6e6c1ea819cfab4a867501a3688324d74df1fe76556558b1937
379 29f41c6e0e30802e2749bfb0729810876f3423e6f24829ad3e30adb1934f1c8a
380 030e7a5f70bb5daa6e18d80d6d447e772efb0bb7fb9d0ffcd54fc5a48af1286d
381 0ea726b117e48cda8bce2349405f006a84cdd3dcfba12efc990df25970a27b6d
382 30364cd4f8a293b1a04f0153548d3e01baad091c69097ca4e9f26be63e4095b5
383 "
384 );
385 let decoded = Ecpairing.decode_call(&data).unwrap();
386 assert_eq!(decoded.len(), 4);
388 }
389}