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