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