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_config::{Chain, NamedChain};
5use foundry_evm_core::{
6 precompiles::{
7 BLAKE_2F, BLS12_G1ADD, BLS12_G1MSM, BLS12_G2ADD, BLS12_G2MSM, BLS12_MAP_FP_TO_G1,
8 BLS12_MAP_FP2_TO_G2, BLS12_PAIRING_CHECK, CELO_TRANSFER, EC_ADD, EC_MUL, EC_PAIRING,
9 EC_RECOVER, IDENTITY, MOD_EXP, P256_VERIFY, POINT_EVALUATION, RIPEMD_160, SHA_256,
10 },
11 tempo::{TEMPO_PRECOMPILE_ADDRESSES, TEMPO_TIP20_TOKENS},
12};
13use itertools::Itertools;
14use revm_inspectors::tracing::types::DecodedCallTrace;
15
16sol! {
17interface Precompiles {
22 struct EcPairingInput {
23 uint256 x1;
24 uint256 y1;
25 uint256 x2;
26 uint256 y2;
27 uint256 x3;
28 uint256 y3;
29 }
30
31 function ecrecover(bytes32 hash, uint8 v, uint256 r, uint256 s) returns (address publicAddress);
32 function sha256(bytes data) returns (bytes32 hash);
33 function ripemd(bytes data) returns (bytes20 hash);
34 function identity(bytes data) returns (bytes data);
35 function modexp(uint256 Bsize, uint256 Esize, uint256 Msize, bytes B, bytes E, bytes M) returns (bytes value);
36 function ecadd(uint256 x1, uint256 y1, uint256 x2, uint256 y2) returns (uint256 x, uint256 y);
37 function ecmul(uint256 x1, uint256 y1, uint256 s) returns (uint256 x, uint256 y);
38 function ecpairing(EcPairingInput[] input) returns (bool success);
39 function blake2f(uint32 rounds, uint64[8] h, uint64[16] m, uint64[2] t, bool f) returns (uint64[8] h);
40 function pointEvaluation(bytes32 versionedHash, bytes32 z, bytes32 y, bytes1[48] commitment, bytes1[48] proof) returns (bytes value);
41
42 function bls12G1Add(bytes p1, bytes p2) returns (bytes result);
44 function bls12G1Msm(bytes[] scalarsAndPoints) returns (bytes result);
45 function bls12G2Add(bytes p1, bytes p2) returns (bytes result);
46 function bls12G2Msm(bytes[] scalarsAndPoints) returns (bytes result);
47 function bls12PairingCheck(bytes[] pairs) returns (bool success);
48 function bls12MapFpToG1(bytes fp) returns (bytes result);
49 function bls12MapFp2ToG2(bytes fp2) returns (bytes result);
50
51 function p256Verify(bytes32 hash, uint256 r, uint256 s, uint256 qx, uint256 qy) returns (bool success);
53}
54}
55use Precompiles::*;
56
57pub(super) fn is_known_precompile(address: Address, chain_id: Option<u64>) -> bool {
58 let is_standard = address[..19].iter().all(|&x| x == 0)
60 && matches!(
61 address,
62 EC_RECOVER
63 | SHA_256
64 | RIPEMD_160
65 | IDENTITY
66 | MOD_EXP
67 | EC_ADD
68 | EC_MUL
69 | EC_PAIRING
70 | BLAKE_2F
71 | POINT_EVALUATION
72 | BLS12_G1ADD
73 | BLS12_G1MSM
74 | BLS12_G2ADD
75 | BLS12_G2MSM
76 | BLS12_PAIRING_CHECK
77 | BLS12_MAP_FP_TO_G1
78 | BLS12_MAP_FP2_TO_G2
79 | P256_VERIFY
80 );
81 if is_standard {
82 return true;
83 }
84 if chain_id.is_some_and(|id| Chain::from_id(id).is_tempo())
86 && (TEMPO_PRECOMPILE_ADDRESSES.contains(&address) || TEMPO_TIP20_TOKENS.contains(&address))
87 {
88 return true;
89 }
90 if chain_id.is_some_and(|id| {
92 matches!(Chain::from_id(id).named(), Some(NamedChain::Celo | NamedChain::CeloSepolia))
93 }) && address == CELO_TRANSFER
94 {
95 return true;
96 }
97 false
98}
99
100pub(super) fn decode(trace: &CallTrace, chain_id: Option<u64>) -> Option<DecodedCallTrace> {
102 if !is_known_precompile(trace.address, chain_id) {
103 return None;
104 }
105
106 for &precompile in PRECOMPILES {
107 if trace.address == precompile.address() {
108 let signature = precompile.signature(&trace.data);
109
110 let args = precompile
111 .decode_call(&trace.data)
112 .unwrap_or_else(|_| vec![trace.data.to_string()]);
113
114 let return_data = precompile
115 .decode_return(&trace.output)
116 .unwrap_or_else(|_| vec![trace.output.to_string()]);
117 let return_data = if return_data.len() == 1 {
118 return_data.into_iter().next().unwrap()
119 } else {
120 format!("({})", return_data.join(", "))
121 };
122
123 return Some(DecodedCallTrace {
124 label: Some("PRECOMPILES".to_string()),
125 call_data: Some(DecodedCallData { signature: signature.to_string(), args }),
126 return_data: Some(return_data),
127 });
128 }
129 }
130
131 None
132}
133
134pub(super) trait Precompile {
135 fn address(&self) -> Address;
136 fn signature(&self, data: &[u8]) -> &'static str;
137
138 fn decode_call(&self, data: &[u8]) -> alloy_sol_types::Result<Vec<String>> {
139 Ok(vec![hex::encode_prefixed(data)])
140 }
141
142 fn decode_return(&self, data: &[u8]) -> alloy_sol_types::Result<Vec<String>> {
143 Ok(vec![hex::encode_prefixed(data)])
144 }
145}
146
147const PRECOMPILES: &[&dyn Precompile] = &[
151 &Ecrecover,
152 &Sha256,
153 &Ripemd160,
154 &Identity,
155 &ModExp,
156 &EcAdd,
157 &Ecmul,
158 &Ecpairing,
159 &Blake2f,
160 &PointEvaluation,
161 &Bls12G1Add,
162 &Bls12G1Msm,
163 &Bls12G2Add,
164 &Bls12G2Msm,
165 &Bls12PairingCheck,
166 &Bls12MapFpToG1,
167 &Bls12MapFp2ToG2,
168 &P256Verify,
169];
170
171struct Ecrecover;
172impl Precompile for Ecrecover {
173 fn address(&self) -> Address {
174 EC_RECOVER
175 }
176
177 fn signature(&self, _: &[u8]) -> &'static str {
178 ecrecoverCall::SIGNATURE
179 }
180
181 fn decode_call(&self, data: &[u8]) -> alloy_sol_types::Result<Vec<String>> {
182 let ecrecoverCall { hash, v, r, s } = ecrecoverCall::abi_decode_raw(data)?;
183 Ok(vec![hash.to_string(), v.to_string(), r.to_string(), s.to_string()])
184 }
185
186 fn decode_return(&self, data: &[u8]) -> alloy_sol_types::Result<Vec<String>> {
187 let ret = ecrecoverCall::abi_decode_returns(data)?;
188 Ok(vec![ret.to_string()])
189 }
190}
191
192struct Sha256;
193impl Precompile for Sha256 {
194 fn address(&self) -> Address {
195 SHA_256
196 }
197
198 fn signature(&self, _: &[u8]) -> &'static str {
199 sha256Call::SIGNATURE
200 }
201
202 fn decode_return(&self, data: &[u8]) -> alloy_sol_types::Result<Vec<String>> {
203 let ret = sha256Call::abi_decode_returns(data)?;
204 Ok(vec![ret.to_string()])
205 }
206}
207
208struct Ripemd160;
209impl Precompile for Ripemd160 {
210 fn address(&self) -> Address {
211 RIPEMD_160
212 }
213
214 fn signature(&self, _: &[u8]) -> &'static str {
215 ripemdCall::SIGNATURE
216 }
217
218 fn decode_return(&self, data: &[u8]) -> alloy_sol_types::Result<Vec<String>> {
219 let ret = ripemdCall::abi_decode_returns(data)?;
220 Ok(vec![ret.to_string()])
221 }
222}
223
224struct Identity;
225impl Precompile for Identity {
226 fn address(&self) -> Address {
227 IDENTITY
228 }
229
230 fn signature(&self, _: &[u8]) -> &'static str {
231 identityCall::SIGNATURE
232 }
233}
234
235struct ModExp;
236impl Precompile for ModExp {
237 fn address(&self) -> Address {
238 MOD_EXP
239 }
240
241 fn signature(&self, _: &[u8]) -> &'static str {
242 modexpCall::SIGNATURE
243 }
244
245 fn decode_call(&self, data: &[u8]) -> alloy_sol_types::Result<Vec<String>> {
246 let mut decoder = abi::Decoder::new(data);
247 let b_size = decoder.take_offset()?;
248 let e_size = decoder.take_offset()?;
249 let m_size = decoder.take_offset()?;
250 let b = decoder.take_slice(b_size)?;
251 let e = decoder.take_slice(e_size)?;
252 let m = decoder.take_slice(m_size)?;
253 Ok(vec![
254 b_size.to_string(),
255 e_size.to_string(),
256 m_size.to_string(),
257 hex::encode_prefixed(b),
258 hex::encode_prefixed(e),
259 hex::encode_prefixed(m),
260 ])
261 }
262}
263
264struct EcAdd;
265impl Precompile for EcAdd {
266 fn address(&self) -> Address {
267 EC_ADD
268 }
269
270 fn signature(&self, _: &[u8]) -> &'static str {
271 ecaddCall::SIGNATURE
272 }
273
274 fn decode_call(&self, data: &[u8]) -> alloy_sol_types::Result<Vec<String>> {
275 let ecaddCall { x1, y1, x2, y2 } = ecaddCall::abi_decode_raw(data)?;
276 Ok(vec![x1.to_string(), y1.to_string(), x2.to_string(), y2.to_string()])
277 }
278
279 fn decode_return(&self, data: &[u8]) -> alloy_sol_types::Result<Vec<String>> {
280 let ecaddReturn { x, y } = ecaddCall::abi_decode_returns(data)?;
281 Ok(vec![x.to_string(), y.to_string()])
282 }
283}
284
285struct Ecmul;
286impl Precompile for Ecmul {
287 fn address(&self) -> Address {
288 EC_MUL
289 }
290
291 fn signature(&self, _: &[u8]) -> &'static str {
292 ecmulCall::SIGNATURE
293 }
294
295 fn decode_call(&self, data: &[u8]) -> alloy_sol_types::Result<Vec<String>> {
296 let ecmulCall { x1, y1, s } = ecmulCall::abi_decode_raw(data)?;
297 Ok(vec![x1.to_string(), y1.to_string(), s.to_string()])
298 }
299
300 fn decode_return(&self, data: &[u8]) -> alloy_sol_types::Result<Vec<String>> {
301 let ecmulReturn { x, y } = ecmulCall::abi_decode_returns(data)?;
302 Ok(vec![x.to_string(), y.to_string()])
303 }
304}
305
306struct Ecpairing;
307impl Precompile for Ecpairing {
308 fn address(&self) -> Address {
309 EC_PAIRING
310 }
311
312 fn signature(&self, _: &[u8]) -> &'static str {
313 ecpairingCall::SIGNATURE
314 }
315
316 fn decode_call(&self, data: &[u8]) -> alloy_sol_types::Result<Vec<String>> {
317 let mut decoder = abi::Decoder::new(data);
318 let mut values = Vec::new();
319 let mut tmp = <[&B256; 6]>::default();
321 while !decoder.is_empty() {
322 for tmp in &mut tmp {
323 *tmp = decoder.take_word()?;
324 }
325 values.push(iter_to_string(tmp.iter().map(|x| U256::from_be_bytes(x.0))));
326 }
327 Ok(values)
328 }
329
330 fn decode_return(&self, data: &[u8]) -> alloy_sol_types::Result<Vec<String>> {
331 let ret = ecpairingCall::abi_decode_returns(data)?;
332 Ok(vec![ret.to_string()])
333 }
334}
335
336struct Blake2f;
337impl Precompile for Blake2f {
338 fn address(&self) -> Address {
339 BLAKE_2F
340 }
341
342 fn signature(&self, _: &[u8]) -> &'static str {
343 blake2fCall::SIGNATURE
344 }
345
346 fn decode_call(&self, data: &[u8]) -> alloy_sol_types::Result<Vec<String>> {
347 decode_blake2f(data)
348 }
349}
350
351fn decode_blake2f<'a>(data: &'a [u8]) -> alloy_sol_types::Result<Vec<String>> {
352 let mut decoder = abi::Decoder::new(data);
353 let rounds = u32::from_be_bytes(decoder.take_slice(4)?.try_into().unwrap());
354 let u64_le_list =
355 |x: &'a [u8]| x.chunks_exact(8).map(|x| u64::from_le_bytes(x.try_into().unwrap()));
356 let h = u64_le_list(decoder.take_slice(64)?);
357 let m = u64_le_list(decoder.take_slice(128)?);
358 let t = u64_le_list(decoder.take_slice(16)?);
359 let f = decoder.take_slice(1)?[0];
360 Ok(vec![
361 rounds.to_string(),
362 iter_to_string(h),
363 iter_to_string(m),
364 iter_to_string(t),
365 f.to_string(),
366 ])
367}
368
369struct PointEvaluation;
370impl Precompile for PointEvaluation {
371 fn address(&self) -> Address {
372 POINT_EVALUATION
373 }
374
375 fn signature(&self, _: &[u8]) -> &'static str {
376 pointEvaluationCall::SIGNATURE
377 }
378
379 fn decode_call(&self, data: &[u8]) -> alloy_sol_types::Result<Vec<String>> {
380 let mut decoder = abi::Decoder::new(data);
381 let versioned_hash = decoder.take_word()?;
382 let z = decoder.take_word()?;
383 let y = decoder.take_word()?;
384 let commitment = decoder.take_slice(48)?;
385 let proof = decoder.take_slice(48)?;
386 Ok(vec![
387 versioned_hash.to_string(),
388 z.to_string(),
389 y.to_string(),
390 hex::encode_prefixed(commitment),
391 hex::encode_prefixed(proof),
392 ])
393 }
394}
395
396fn iter_to_string<I: Iterator<Item = T>, T: std::fmt::Display>(iter: I) -> String {
397 format!("[{}]", iter.format(", "))
398}
399
400const G1_POINT_SIZE: usize = 128;
401const G2_POINT_SIZE: usize = 256;
402const SCALAR_SIZE: usize = 32;
403const FP_SIZE: usize = 64;
404
405struct Bls12G1Add;
406impl Precompile for Bls12G1Add {
407 fn address(&self) -> Address {
408 BLS12_G1ADD
409 }
410
411 fn signature(&self, _: &[u8]) -> &'static str {
412 bls12G1AddCall::SIGNATURE
413 }
414
415 fn decode_call(&self, data: &[u8]) -> alloy_sol_types::Result<Vec<String>> {
416 let (p1, rest) = take_at_most(data, G1_POINT_SIZE);
417 let (p2, _) = take_at_most(rest, G1_POINT_SIZE);
418 Ok(vec![hex::encode_prefixed(p1), hex::encode_prefixed(p2)])
419 }
420}
421
422struct Bls12G1Msm;
423impl Precompile for Bls12G1Msm {
424 fn address(&self) -> Address {
425 BLS12_G1MSM
426 }
427
428 fn signature(&self, _: &[u8]) -> &'static str {
429 bls12G1MsmCall::SIGNATURE
430 }
431
432 fn decode_call(&self, data: &[u8]) -> alloy_sol_types::Result<Vec<String>> {
433 let pair_size = G1_POINT_SIZE + SCALAR_SIZE;
434 Ok(data.chunks(pair_size).map(hex::encode_prefixed).collect())
435 }
436}
437
438struct Bls12G2Add;
439impl Precompile for Bls12G2Add {
440 fn address(&self) -> Address {
441 BLS12_G2ADD
442 }
443
444 fn signature(&self, _: &[u8]) -> &'static str {
445 bls12G2AddCall::SIGNATURE
446 }
447
448 fn decode_call(&self, data: &[u8]) -> alloy_sol_types::Result<Vec<String>> {
449 let (p1, rest) = take_at_most(data, G2_POINT_SIZE);
450 let (p2, _) = take_at_most(rest, G2_POINT_SIZE);
451 Ok(vec![hex::encode_prefixed(p1), hex::encode_prefixed(p2)])
452 }
453}
454
455struct Bls12G2Msm;
456impl Precompile for Bls12G2Msm {
457 fn address(&self) -> Address {
458 BLS12_G2MSM
459 }
460
461 fn signature(&self, _: &[u8]) -> &'static str {
462 bls12G2MsmCall::SIGNATURE
463 }
464
465 fn decode_call(&self, data: &[u8]) -> alloy_sol_types::Result<Vec<String>> {
466 let pair_size = G2_POINT_SIZE + SCALAR_SIZE;
467 Ok(data.chunks(pair_size).map(hex::encode_prefixed).collect())
468 }
469}
470
471struct Bls12PairingCheck;
472impl Precompile for Bls12PairingCheck {
473 fn address(&self) -> Address {
474 BLS12_PAIRING_CHECK
475 }
476
477 fn signature(&self, _: &[u8]) -> &'static str {
478 bls12PairingCheckCall::SIGNATURE
479 }
480
481 fn decode_call(&self, data: &[u8]) -> alloy_sol_types::Result<Vec<String>> {
482 let pair_size = G1_POINT_SIZE + G2_POINT_SIZE;
483 Ok(data.chunks(pair_size).map(hex::encode_prefixed).collect())
484 }
485
486 fn decode_return(&self, data: &[u8]) -> alloy_sol_types::Result<Vec<String>> {
487 let ret = bls12PairingCheckCall::abi_decode_returns(data)?;
488 Ok(vec![ret.to_string()])
489 }
490}
491
492struct Bls12MapFpToG1;
493impl Precompile for Bls12MapFpToG1 {
494 fn address(&self) -> Address {
495 BLS12_MAP_FP_TO_G1
496 }
497
498 fn signature(&self, _: &[u8]) -> &'static str {
499 bls12MapFpToG1Call::SIGNATURE
500 }
501
502 fn decode_call(&self, data: &[u8]) -> alloy_sol_types::Result<Vec<String>> {
503 let (fp, _) = take_at_most(data, FP_SIZE);
504 Ok(vec![hex::encode_prefixed(fp)])
505 }
506}
507
508struct Bls12MapFp2ToG2;
509impl Precompile for Bls12MapFp2ToG2 {
510 fn address(&self) -> Address {
511 BLS12_MAP_FP2_TO_G2
512 }
513
514 fn signature(&self, _: &[u8]) -> &'static str {
515 bls12MapFp2ToG2Call::SIGNATURE
516 }
517
518 fn decode_call(&self, data: &[u8]) -> alloy_sol_types::Result<Vec<String>> {
519 let (fp2, _) = take_at_most(data, G1_POINT_SIZE);
520 Ok(vec![hex::encode_prefixed(fp2)])
521 }
522}
523
524struct P256Verify;
525impl Precompile for P256Verify {
526 fn address(&self) -> Address {
527 P256_VERIFY
528 }
529
530 fn signature(&self, _: &[u8]) -> &'static str {
531 p256VerifyCall::SIGNATURE
532 }
533
534 fn decode_call(&self, data: &[u8]) -> alloy_sol_types::Result<Vec<String>> {
535 let p256VerifyCall { hash, r, s, qx, qy } = p256VerifyCall::abi_decode_raw(data)?;
536 Ok(vec![hash.to_string(), r.to_string(), s.to_string(), qx.to_string(), qy.to_string()])
537 }
538
539 fn decode_return(&self, data: &[u8]) -> alloy_sol_types::Result<Vec<String>> {
540 let ret = p256VerifyCall::abi_decode_returns(data)?;
541 Ok(vec![ret.to_string()])
542 }
543}
544
545fn take_at_most(data: &[u8], n: usize) -> (&[u8], &[u8]) {
546 let n = n.min(data.len());
547 data.split_at(n)
548}
549
550#[cfg(test)]
551mod tests {
552 use super::*;
553 use alloy_primitives::hex;
554
555 #[test]
556 fn ecpairing() {
557 let data = hex!(
559 "
560 26bbb723f965460ca7282cd75f0e3e7c67b15817f7cee60856b394936ed02917
561 0fbe873ac672168143a91535450bab6c412dce8dc8b66a88f2da6e245f9282df
562 13cd4f0451538ece5014fe6688b197aefcc611a5c6a7c319f834f2188ba04b08
563 126ff07e81490a1b6ae92b2d9e700c8e23e9d5c7f6ab857027213819a6c9ae7d
564 04183624c9858a56c54deb237c26cb4355bc2551312004e65fc5b299440b15a3
565 2e4b11aa549ad6c667057b18be4f4437fda92f018a59430ebb992fa3462c9ca1
566 2d4d9aa7e302d9df41749d5507949d05dbea33fbb16c643b22f599a2be6df2e2
567 14bedd503c37ceb061d8ec60209fe345ce89830a19230301f076caff004d1926
568 0967032fcbf776d1afc985f88877f182d38480a653f2decaa9794cbc3bf3060c
569 0e187847ad4c798374d0d6732bf501847dd68bc0e071241e0213bc7fc13db7ab
570 304cfbd1e08a704a99f5e847d93f8c3caafddec46b7a0d379da69a4d112346a7
571 1739c1b1a457a8c7313123d24d2f9192f896b7c63eea05a9d57f06547ad0cec8
572 001d6fedb032f70e377635238e0563f131670001f6abf439adb3a9d5d52073c6
573 1889afe91e4e367f898a7fcd6464e5ca4e822fe169bccb624f6aeb87e4d060bc
574 198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2
575 1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed
576 090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b
577 12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa
578 2dde6d7baf0bfa09329ec8d44c38282f5bf7f9ead1914edd7dcaebb498c84519
579 0c359f868a85c6e6c1ea819cfab4a867501a3688324d74df1fe76556558b1937
580 29f41c6e0e30802e2749bfb0729810876f3423e6f24829ad3e30adb1934f1c8a
581 030e7a5f70bb5daa6e18d80d6d447e772efb0bb7fb9d0ffcd54fc5a48af1286d
582 0ea726b117e48cda8bce2349405f006a84cdd3dcfba12efc990df25970a27b6d
583 30364cd4f8a293b1a04f0153548d3e01baad091c69097ca4e9f26be63e4095b5
584 "
585 );
586 let decoded = Ecpairing.decode_call(&data).unwrap();
587 assert_eq!(decoded.len(), 4);
589 }
590}