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