1use alloy_consensus::{
4 BlockHeader, Eip658Value, Receipt, ReceiptWithBloom, Signed, Transaction as TxTrait, TxEip1559,
5 TxEip2930, TxEip4844Variant, TxEip7702, TxEnvelope, TxLegacy, TxReceipt, Typed2718,
6 transaction::TxHashRef,
7};
8use alloy_network::{
9 AnyReceiptEnvelope, AnyRpcBlock, AnyRpcHeader, AnyRpcTransaction, AnyTransactionReceipt,
10 AnyTxEnvelope, BlockResponse, Network, ReceiptResponse, primitives::HeaderResponse,
11};
12use alloy_primitives::{
13 Address, Bloom, Bytes, FixedBytes, I256, Signature, U8, U64, U256, Uint, hex,
14};
15use alloy_rpc_types::{
16 AccessListItem, Block, BlockTransactions, Header, Log, Transaction, TransactionReceipt,
17};
18use alloy_serde::{OtherFields, WithOtherFields};
19use foundry_primitives::{FoundryReceiptEnvelope, FoundryTxEnvelope, FoundryTxReceipt};
20use op_alloy_consensus::{OpTxEnvelope, TxDeposit};
21use revm::context_interface::transaction::SignedAuthorization;
22use serde::Deserialize;
23use tempo_alloy::{
24 primitives::{
25 AASigned, TempoSignature, TempoTransaction, TempoTxEnvelope,
26 transaction::{Call, PrimitiveSignature},
27 },
28 rpc::{TempoHeaderResponse, TempoTransactionReceipt},
29};
30
31const NAME_COLUMN_LEN: usize = 20usize;
33
34pub trait UIfmt {
45 fn pretty(&self) -> String;
47}
48
49impl<T: UIfmt> UIfmt for &T {
50 fn pretty(&self) -> String {
51 (*self).pretty()
52 }
53}
54
55impl<T: UIfmt> UIfmt for Option<T> {
56 fn pretty(&self) -> String {
57 if let Some(inner) = self { inner.pretty() } else { String::new() }
58 }
59}
60
61impl<T: UIfmt> UIfmt for [T] {
62 fn pretty(&self) -> String {
63 if !self.is_empty() {
64 let mut s = String::with_capacity(self.len() * 64);
65 s.push_str("[\n");
66 for item in self {
67 for line in item.pretty().lines() {
68 s.push('\t');
69 s.push_str(line);
70 s.push('\n');
71 }
72 }
73 s.push(']');
74 s
75 } else {
76 "[]".to_string()
77 }
78 }
79}
80
81impl UIfmt for String {
82 fn pretty(&self) -> String {
83 self.to_string()
84 }
85}
86
87impl UIfmt for u64 {
88 fn pretty(&self) -> String {
89 self.to_string()
90 }
91}
92
93impl UIfmt for u128 {
94 fn pretty(&self) -> String {
95 self.to_string()
96 }
97}
98
99impl UIfmt for bool {
100 fn pretty(&self) -> String {
101 self.to_string()
102 }
103}
104
105impl<const BITS: usize, const LIMBS: usize> UIfmt for Uint<BITS, LIMBS> {
106 fn pretty(&self) -> String {
107 self.to_string()
108 }
109}
110
111impl UIfmt for I256 {
112 fn pretty(&self) -> String {
113 self.to_string()
114 }
115}
116
117impl UIfmt for Address {
118 fn pretty(&self) -> String {
119 self.to_string()
120 }
121}
122
123impl UIfmt for Bloom {
124 fn pretty(&self) -> String {
125 self.to_string()
126 }
127}
128
129impl UIfmt for Vec<u8> {
130 fn pretty(&self) -> String {
131 self[..].pretty()
132 }
133}
134
135impl UIfmt for Bytes {
136 fn pretty(&self) -> String {
137 self[..].pretty()
138 }
139}
140
141impl<const N: usize> UIfmt for [u8; N] {
142 fn pretty(&self) -> String {
143 self[..].pretty()
144 }
145}
146
147impl<const N: usize> UIfmt for FixedBytes<N> {
148 fn pretty(&self) -> String {
149 self[..].pretty()
150 }
151}
152
153impl UIfmt for [u8] {
154 fn pretty(&self) -> String {
155 hex::encode_prefixed(self)
156 }
157}
158
159impl UIfmt for Eip658Value {
160 fn pretty(&self) -> String {
161 match self {
162 Self::Eip658(status) => if *status { "1 (success)" } else { "0 (failed)" }.to_string(),
163 Self::PostState(state) => state.pretty(),
164 }
165 }
166}
167
168impl UIfmt for Signature {
169 fn pretty(&self) -> String {
170 format!("[r: {}, s: {}, y_parity: {}]", self.r(), self.s(), self.v())
171 }
172}
173
174impl UIfmt for AnyTransactionReceipt {
175 fn pretty(&self) -> String {
176 let Self {
177 inner:
178 TransactionReceipt {
179 transaction_hash,
180 transaction_index,
181 block_hash,
182 block_number,
183 from,
184 to,
185 gas_used,
186 contract_address,
187 effective_gas_price,
188 inner:
189 AnyReceiptEnvelope {
190 r#type: transaction_type,
191 inner:
192 ReceiptWithBloom {
193 receipt: Receipt { status, cumulative_gas_used, logs },
194 logs_bloom,
195 },
196 },
197 blob_gas_price,
198 blob_gas_used,
199 },
200 other,
201 } = self;
202
203 let mut pretty = format!(
204 "
205blockHash {}
206blockNumber {}
207contractAddress {}
208cumulativeGasUsed {}
209effectiveGasPrice {}
210from {}
211gasUsed {}
212logs {}
213logsBloom {}
214root {}
215status {}
216transactionHash {}
217transactionIndex {}
218type {}
219blobGasPrice {}
220blobGasUsed {}",
221 block_hash.pretty(),
222 block_number.pretty(),
223 contract_address.pretty(),
224 cumulative_gas_used.pretty(),
225 effective_gas_price.pretty(),
226 from.pretty(),
227 gas_used.pretty(),
228 serde_json::to_string(&logs).unwrap(),
229 logs_bloom.pretty(),
230 self.state_root().pretty(),
231 status.pretty(),
232 transaction_hash.pretty(),
233 transaction_index.pretty(),
234 transaction_type,
235 blob_gas_price.pretty(),
236 blob_gas_used.pretty()
237 );
238
239 if let Some(to) = to {
240 pretty.push_str(&format!("\nto {}", to.pretty()));
241 }
242
243 pretty.push_str(&other.pretty());
245
246 pretty
247 }
248}
249
250impl UIfmt for Log {
251 fn pretty(&self) -> String {
252 format!(
253 "
254address: {}
255blockHash: {}
256blockNumber: {}
257data: {}
258logIndex: {}
259removed: {}
260topics: {}
261transactionHash: {}
262transactionIndex: {}",
263 self.address().pretty(),
264 self.block_hash.pretty(),
265 self.block_number.pretty(),
266 self.data().data.pretty(),
267 self.log_index.pretty(),
268 self.removed.pretty(),
269 self.topics().pretty(),
270 self.transaction_hash.pretty(),
271 self.transaction_index.pretty(),
272 )
273 }
274}
275
276impl<T: UIfmt, H: HeaderResponse + UIfmtHeaderExt> UIfmt for Block<T, H> {
277 fn pretty(&self) -> String {
278 format!(
279 "
280{}
281transactions: {}",
282 pretty_generic_header_response(&self.header),
283 self.transactions.pretty()
284 )
285 }
286}
287
288impl<T: UIfmt> UIfmt for BlockTransactions<T> {
289 fn pretty(&self) -> String {
290 match self {
291 Self::Hashes(hashes) => hashes.pretty(),
292 Self::Full(transactions) => transactions.pretty(),
293 Self::Uncle => String::new(),
294 }
295 }
296}
297
298impl UIfmt for OtherFields {
299 fn pretty(&self) -> String {
300 let mut s = String::with_capacity(self.len() * 30);
301 if !self.is_empty() {
302 s.push('\n');
303 }
304 for (key, value) in self {
305 let val = EthValue::from(value.clone()).pretty();
306 let offset = NAME_COLUMN_LEN.saturating_sub(key.len());
307 s.push_str(key);
308 s.extend(std::iter::repeat_n(' ', offset + 1));
309 s.push_str(&val);
310 s.push('\n');
311 }
312 s
313 }
314}
315
316impl UIfmt for AccessListItem {
317 fn pretty(&self) -> String {
318 let mut s = String::with_capacity(42 + self.storage_keys.len() * 66);
319 s.push_str(self.address.pretty().as_str());
320 s.push_str(" => ");
321 s.push_str(self.storage_keys.pretty().as_str());
322 s
323 }
324}
325
326impl UIfmt for TxLegacy {
327 fn pretty(&self) -> String {
328 format!(
329 "
330chainId {}
331nonce {}
332gasPrice {}
333gasLimit {}
334to {}
335value {}
336input {}",
337 self.chain_id.pretty(),
338 self.nonce.pretty(),
339 self.gas_price.pretty(),
340 self.gas_limit.pretty(),
341 self.to().pretty(),
342 self.value.pretty(),
343 self.input.pretty(),
344 )
345 }
346}
347
348impl UIfmt for TxEip2930 {
349 fn pretty(&self) -> String {
350 format!(
351 "
352chainId {}
353nonce {}
354gasPrice {}
355gasLimit {}
356to {}
357value {}
358accessList {}
359input {}",
360 self.chain_id.pretty(),
361 self.nonce.pretty(),
362 self.gas_price.pretty(),
363 self.gas_limit.pretty(),
364 self.to().pretty(),
365 self.value.pretty(),
366 self.access_list.pretty(),
367 self.input.pretty(),
368 )
369 }
370}
371
372impl UIfmt for TxEip1559 {
373 fn pretty(&self) -> String {
374 format!(
375 "
376chainId {}
377nonce {}
378gasLimit {}
379maxFeePerGas {}
380maxPriorityFeePerGas {}
381to {}
382value {}
383accessList {}
384input {}",
385 self.chain_id.pretty(),
386 self.nonce.pretty(),
387 self.gas_limit.pretty(),
388 self.max_fee_per_gas.pretty(),
389 self.max_priority_fee_per_gas.pretty(),
390 self.to().pretty(),
391 self.value.pretty(),
392 self.access_list.pretty(),
393 self.input.pretty(),
394 )
395 }
396}
397
398impl UIfmt for TxEip4844Variant {
399 fn pretty(&self) -> String {
400 use alloy_consensus::TxEip4844;
401 let tx: &TxEip4844 = match self {
402 Self::TxEip4844(tx) => tx,
403 Self::TxEip4844WithSidecar(tx) => tx.tx(),
404 };
405 format!(
406 "
407chainId {}
408nonce {}
409gasLimit {}
410maxFeePerGas {}
411maxPriorityFeePerGas {}
412to {}
413value {}
414accessList {}
415blobVersionedHashes {}
416maxFeePerBlobGas {}
417input {}",
418 tx.chain_id.pretty(),
419 tx.nonce.pretty(),
420 tx.gas_limit.pretty(),
421 tx.max_fee_per_gas.pretty(),
422 tx.max_priority_fee_per_gas.pretty(),
423 tx.to.pretty(),
424 tx.value.pretty(),
425 tx.access_list.pretty(),
426 tx.blob_versioned_hashes.pretty(),
427 tx.max_fee_per_blob_gas.pretty(),
428 tx.input.pretty(),
429 )
430 }
431}
432
433impl UIfmt for TxEip7702 {
434 fn pretty(&self) -> String {
435 format!(
436 "
437chainId {}
438nonce {}
439gasLimit {}
440maxFeePerGas {}
441maxPriorityFeePerGas {}
442to {}
443value {}
444accessList {}
445authorizationList {}
446input {}",
447 self.chain_id.pretty(),
448 self.nonce.pretty(),
449 self.gas_limit.pretty(),
450 self.max_fee_per_gas.pretty(),
451 self.max_priority_fee_per_gas.pretty(),
452 self.to.pretty(),
453 self.value.pretty(),
454 self.access_list.pretty(),
455 self.authorization_list.pretty(),
456 self.input.pretty(),
457 )
458 }
459}
460
461impl UIfmt for TxDeposit {
462 fn pretty(&self) -> String {
463 format!(
464 "
465sourceHash {}
466from {}
467to {}
468mint {}
469value {}
470gasLimit {}
471isSystemTransaction {}
472input {}",
473 self.source_hash.pretty(),
474 self.from.pretty(),
475 self.to().pretty(),
476 self.mint.pretty(),
477 self.value.pretty(),
478 self.gas_limit.pretty(),
479 self.is_system_transaction,
480 self.input.pretty(),
481 )
482 }
483}
484
485impl UIfmt for Call {
486 fn pretty(&self) -> String {
487 format!(
488 "to: {}, value: {}, input: {}",
489 self.to.into_to().pretty(),
490 self.value.pretty(),
491 self.input.pretty(),
492 )
493 }
494}
495
496impl UIfmt for TempoTransaction {
497 fn pretty(&self) -> String {
498 format!(
499 "
500chainId {}
501feeToken {}
502maxPriorityFeePerGas {}
503maxFeePerGas {}
504gasLimit {}
505calls {}
506accessList {}
507nonceKey {}
508nonce {}
509feePayerSignature {}
510validBefore {}
511validAfter {}",
512 self.chain_id.pretty(),
513 self.fee_token.pretty(),
514 self.max_priority_fee_per_gas.pretty(),
515 self.max_fee_per_gas.pretty(),
516 self.gas_limit.pretty(),
517 self.calls.pretty(),
518 self.access_list.pretty(),
519 self.nonce_key.pretty(),
520 self.nonce.pretty(),
521 self.fee_payer_signature.pretty(),
522 self.valid_after.pretty(),
523 self.valid_before.pretty(),
524 )
525 }
526}
527
528impl UIfmt for TempoSignature {
529 fn pretty(&self) -> String {
530 serde_json::to_string(self).unwrap_or_default()
531 }
532}
533
534impl UIfmt for AASigned {
535 fn pretty(&self) -> String {
536 format!(
537 "
538hash {}
539type {}
540{}
541tempoSignature {}",
542 self.hash().pretty(),
543 self.tx().ty(),
544 self.tx().pretty().trim_start(),
545 self.signature().pretty(),
546 )
547 }
548}
549
550impl<T: UIfmt + Typed2718> UIfmt for Signed<T>
551where
552 Self: TxHashRef,
553{
554 fn pretty(&self) -> String {
555 format!(
556 "
557hash {}
558type {}
559{}
560r {}
561s {}
562yParity {}",
563 self.tx_hash().pretty(),
564 self.ty(),
565 self.tx().pretty().trim_start(),
566 FixedBytes::from(self.signature().r()).pretty(),
567 FixedBytes::from(self.signature().s()).pretty(),
568 (if self.signature().v() { 1u64 } else { 0 }).pretty(),
569 )
570 }
571}
572
573impl UIfmt for TxEnvelope {
574 fn pretty(&self) -> String {
575 match self {
576 Self::Legacy(tx) => tx.pretty(),
577 Self::Eip2930(tx) => tx.pretty(),
578 Self::Eip1559(tx) => tx.pretty(),
579 Self::Eip4844(tx) => tx.pretty(),
580 Self::Eip7702(tx) => tx.pretty(),
581 }
582 }
583}
584
585impl UIfmt for AnyTxEnvelope {
586 fn pretty(&self) -> String {
587 match self {
588 Self::Ethereum(envelop) => envelop.pretty(),
589 Self::Unknown(tx) => {
590 format!(
591 "
592hash {}
593type {:#x}
594{}
595 ",
596 tx.hash.pretty(),
597 tx.ty(),
598 tx.inner.fields.pretty().trim_start(),
599 )
600 }
601 }
602 }
603}
604
605impl UIfmt for OpTxEnvelope {
606 fn pretty(&self) -> String {
607 match self {
608 Self::Legacy(tx) => tx.pretty(),
609 Self::Eip2930(tx) => tx.pretty(),
610 Self::Eip1559(tx) => tx.pretty(),
611 Self::Eip7702(tx) => tx.pretty(),
612 Self::Deposit(tx) => tx.pretty(),
613 }
614 }
615}
616
617impl UIfmt for TempoTxEnvelope {
618 fn pretty(&self) -> String {
619 match self {
620 Self::Legacy(tx) => tx.pretty(),
621 Self::Eip2930(tx) => tx.pretty(),
622 Self::Eip1559(tx) => tx.pretty(),
623 Self::Eip7702(tx) => tx.pretty(),
624 Self::AA(tx) => tx.pretty(),
625 }
626 }
627}
628
629impl<T: UIfmt> UIfmt for Transaction<T> {
630 fn pretty(&self) -> String {
631 format!(
632 "
633blockHash {}
634blockNumber {}
635from {}
636transactionIndex {}
637effectiveGasPrice {}
638{}",
639 self.block_hash.pretty(),
640 self.block_number.pretty(),
641 self.inner.signer().pretty(),
642 self.transaction_index.pretty(),
643 self.effective_gas_price.pretty(),
644 self.inner.inner().pretty().trim_start(),
645 )
646 }
647}
648
649impl<T: UIfmt> UIfmt for op_alloy_rpc_types::Transaction<T> {
650 fn pretty(&self) -> String {
651 format!(
652 "
653depositNonce {}
654depositReceiptVersion {}
655{}",
656 self.deposit_nonce.pretty(),
657 self.deposit_receipt_version.pretty(),
658 self.inner.pretty().trim_start(),
659 )
660 }
661}
662
663impl UIfmt for AnyRpcBlock {
664 fn pretty(&self) -> String {
665 self.0.pretty()
666 }
667}
668
669impl UIfmt for AnyRpcTransaction {
670 fn pretty(&self) -> String {
671 self.0.pretty()
672 }
673}
674
675impl<T: UIfmt> UIfmt for WithOtherFields<T> {
676 fn pretty(&self) -> String {
677 format!("{}{}", self.inner.pretty(), self.other.pretty())
678 }
679}
680
681#[derive(Clone, Debug, Deserialize)]
683#[serde(untagged)]
684#[expect(missing_docs)]
685pub enum EthValue {
686 U64(U64),
687 Address(Address),
688 U256(U256),
689 U64Array(Vec<U64>),
690 U256Array(Vec<U256>),
691 Other(serde_json::Value),
692}
693
694impl From<serde_json::Value> for EthValue {
695 fn from(val: serde_json::Value) -> Self {
696 serde_json::from_value(val).expect("infallible")
697 }
698}
699
700impl UIfmt for EthValue {
701 fn pretty(&self) -> String {
702 match self {
703 Self::U64(num) => num.pretty(),
704 Self::U256(num) => num.pretty(),
705 Self::Address(addr) => addr.pretty(),
706 Self::U64Array(arr) => arr.pretty(),
707 Self::U256Array(arr) => arr.pretty(),
708 Self::Other(val) => val.to_string().trim_matches('"').to_string(),
709 }
710 }
711}
712
713impl UIfmt for SignedAuthorization {
714 fn pretty(&self) -> String {
715 let signed_authorization = serde_json::to_string(self).unwrap_or("<invalid>".to_string());
716
717 match self.recover_authority() {
718 Ok(authority) => format!(
719 "{{recoveredAuthority: {authority}, signedAuthority: {signed_authorization}}}",
720 ),
721 Err(e) => format!(
722 "{{recoveredAuthority: <error: {e}>, signedAuthority: {signed_authorization}}}",
723 ),
724 }
725 }
726}
727
728impl<T> UIfmt for FoundryReceiptEnvelope<T>
729where
730 T: UIfmt + Clone + core::fmt::Debug + PartialEq + Eq,
731{
732 fn pretty(&self) -> String {
733 let receipt = self.as_receipt();
734 let deposit_info = match self {
735 Self::Deposit(d) => {
736 format!(
737 "
738depositNonce {}
739depositReceiptVersion {}",
740 d.receipt.deposit_nonce.pretty(),
741 d.receipt.deposit_receipt_version.pretty()
742 )
743 }
744 _ => String::new(),
745 };
746
747 format!(
748 "
749status {}
750cumulativeGasUsed {}
751logs {}
752logsBloom {}
753type {}{}",
754 receipt.status.pretty(),
755 receipt.cumulative_gas_used.pretty(),
756 receipt.logs.pretty(),
757 self.logs_bloom().pretty(),
758 self.tx_type() as u8,
759 deposit_info
760 )
761 }
762}
763
764impl UIfmt for FoundryTxReceipt {
765 fn pretty(&self) -> String {
766 let receipt = &self.0.inner;
767 let other = &self.0.other;
768
769 let mut pretty = format!(
770 "
771blockHash {}
772blockNumber {}
773contractAddress {}
774cumulativeGasUsed {}
775effectiveGasPrice {}
776from {}
777gasUsed {}
778logs {}
779logsBloom {}
780root {}
781status {}
782transactionHash {}
783transactionIndex {}
784type {}
785blobGasPrice {}
786blobGasUsed {}",
787 receipt.block_hash().pretty(),
788 receipt.block_number().pretty(),
789 receipt.contract_address().pretty(),
790 receipt.cumulative_gas_used().pretty(),
791 receipt.effective_gas_price().pretty(),
792 receipt.from().pretty(),
793 receipt.gas_used().pretty(),
794 serde_json::to_string(receipt.inner.logs()).unwrap(),
795 receipt.inner.logs_bloom().pretty(),
796 self.state_root().pretty(),
797 receipt.status().pretty(),
798 receipt.transaction_hash().pretty(),
799 receipt.transaction_index().pretty(),
800 receipt.inner.tx_type() as u8,
801 receipt.blob_gas_price().pretty(),
802 receipt.blob_gas_used().pretty()
803 );
804
805 if let Some(to) = receipt.to() {
806 pretty.push_str(&format!("\nto {}", to.pretty()));
807 }
808
809 pretty.push_str(&other.pretty());
811
812 pretty
813 }
814}
815
816pub trait UIfmtHeaderExt {
817 fn size_pretty(&self) -> String;
818}
819
820impl UIfmtHeaderExt for Header {
821 fn size_pretty(&self) -> String {
822 self.size.pretty()
823 }
824}
825
826impl UIfmtHeaderExt for AnyRpcHeader {
827 fn size_pretty(&self) -> String {
828 self.size.pretty()
829 }
830}
831
832impl UIfmtHeaderExt for TempoHeaderResponse {
833 fn size_pretty(&self) -> String {
834 self.inner.size.pretty()
835 }
836}
837
838pub trait UIfmtSignatureExt {
839 fn signature_pretty(&self) -> Option<(String, String, String)>;
840}
841
842impl UIfmtSignatureExt for TxEnvelope {
843 fn signature_pretty(&self) -> Option<(String, String, String)> {
844 let sig = self.signature();
845 Some((
846 FixedBytes::from(sig.r()).pretty(),
847 FixedBytes::from(sig.s()).pretty(),
848 U8::from_le_slice(&sig.as_bytes()[64..]).pretty(),
849 ))
850 }
851}
852
853impl UIfmtSignatureExt for AnyTxEnvelope {
854 fn signature_pretty(&self) -> Option<(String, String, String)> {
855 self.as_envelope().and_then(|envelope| envelope.signature_pretty())
856 }
857}
858
859impl UIfmtSignatureExt for OpTxEnvelope {
860 fn signature_pretty(&self) -> Option<(String, String, String)> {
861 self.signature().map(|sig| {
862 (
863 FixedBytes::from(sig.r()).pretty(),
864 FixedBytes::from(sig.s()).pretty(),
865 U8::from_le_slice(&sig.as_bytes()[64..]).pretty(),
866 )
867 })
868 }
869}
870
871impl UIfmtSignatureExt for FoundryTxEnvelope {
872 fn signature_pretty(&self) -> Option<(String, String, String)> {
873 self.clone().try_into_eth().ok().and_then(|envelope| envelope.signature_pretty())
874 }
875}
876
877impl UIfmtSignatureExt for TempoTxEnvelope {
878 fn signature_pretty(&self) -> Option<(String, String, String)> {
879 let sig = match self {
880 Self::Legacy(tx) => Some(tx.signature()),
881 Self::Eip2930(tx) => Some(tx.signature()),
882 Self::Eip1559(tx) => Some(tx.signature()),
883 Self::Eip7702(tx) => Some(tx.signature()),
884 Self::AA(tempo_tx) => {
885 if let TempoSignature::Primitive(PrimitiveSignature::Secp256k1(sig)) =
886 tempo_tx.signature()
887 {
888 Some(sig)
889 } else {
890 None
891 }
892 }
893 }?;
894 Some((
895 FixedBytes::from(sig.r()).pretty(),
896 FixedBytes::from(sig.s()).pretty(),
897 U8::from_le_slice(&sig.as_bytes()[64..]).pretty(),
898 ))
899 }
900}
901
902pub trait UIfmtReceiptExt {
903 fn logs_pretty(&self) -> String;
904 fn logs_bloom_pretty(&self) -> String;
905 fn tx_type_pretty(&self) -> String;
906}
907
908impl UIfmtReceiptExt for AnyTransactionReceipt {
909 fn logs_pretty(&self) -> String {
910 serde_json::to_string(&self.inner.inner.inner.receipt.logs).unwrap_or_default()
911 }
912
913 fn logs_bloom_pretty(&self) -> String {
914 self.inner.inner.inner.logs_bloom.pretty()
915 }
916
917 fn tx_type_pretty(&self) -> String {
918 self.inner.inner.r#type.to_string()
919 }
920}
921
922impl UIfmtReceiptExt for FoundryTxReceipt {
923 fn logs_pretty(&self) -> String {
924 serde_json::to_string(self.0.inner.inner.logs()).unwrap_or_default()
925 }
926
927 fn logs_bloom_pretty(&self) -> String {
928 self.0.inner.inner.logs_bloom().pretty()
929 }
930
931 fn tx_type_pretty(&self) -> String {
932 (self.0.inner.inner.tx_type() as u8).to_string()
933 }
934}
935
936impl UIfmt for TempoTransactionReceipt {
937 fn pretty(&self) -> String {
938 let receipt = &self.inner;
939
940 let mut pretty = format!(
941 "
942blockHash {}
943blockNumber {}
944contractAddress {}
945cumulativeGasUsed {}
946effectiveGasPrice {}
947from {}
948gasUsed {}
949logs {}
950logsBloom {}
951root {}
952status {}
953transactionHash {}
954transactionIndex {}
955type {}
956feePayer {}
957feeToken {}",
958 receipt.block_hash().pretty(),
959 receipt.block_number().pretty(),
960 receipt.contract_address().pretty(),
961 receipt.cumulative_gas_used().pretty(),
962 receipt.effective_gas_price().pretty(),
963 receipt.from().pretty(),
964 receipt.gas_used().pretty(),
965 serde_json::to_string(receipt.inner.logs()).unwrap(),
966 receipt.inner.logs_bloom.pretty(),
967 self.state_root().pretty(),
968 receipt.status().pretty(),
969 receipt.transaction_hash().pretty(),
970 receipt.transaction_index().pretty(),
971 receipt.inner.receipt.tx_type as u8,
972 self.fee_payer.pretty(),
973 self.fee_token.pretty(),
974 );
975
976 if let Some(to) = receipt.to() {
977 pretty.push_str(&format!("\nto {}", to.pretty()));
978 }
979
980 pretty
981 }
982}
983
984impl UIfmtReceiptExt for TempoTransactionReceipt {
985 fn logs_pretty(&self) -> String {
986 serde_json::to_string(self.inner.inner.logs()).unwrap_or_default()
987 }
988
989 fn logs_bloom_pretty(&self) -> String {
990 self.inner.inner.logs_bloom.pretty()
991 }
992
993 fn tx_type_pretty(&self) -> String {
994 (self.inner.inner.receipt.tx_type as u8).to_string()
995 }
996}
997
998pub fn get_pretty_tx_attr<N>(transaction: &N::TransactionResponse, attr: &str) -> Option<String>
1000where
1001 N: Network,
1002 N::TxEnvelope: UIfmtSignatureExt,
1003{
1004 let (r, s, v) = transaction.as_ref().signature_pretty().unwrap_or_default();
1005 match attr {
1006 "blockHash" | "block_hash" => {
1007 Some(alloy_network::TransactionResponse::block_hash(transaction).pretty())
1008 }
1009 "blockNumber" | "block_number" => {
1010 Some(alloy_network::TransactionResponse::block_number(transaction).pretty())
1011 }
1012 "from" => Some(alloy_network::TransactionResponse::from(transaction).pretty()),
1013 "gas" => Some(TxTrait::gas_limit(transaction).pretty()),
1014 "gasPrice" | "gas_price" => Some(TxTrait::max_fee_per_gas(transaction).pretty()),
1015 "hash" => Some(alloy_network::TransactionResponse::tx_hash(transaction).pretty()),
1016 "input" => Some(TxTrait::input(transaction).pretty()),
1017 "nonce" => Some(TxTrait::nonce(transaction).to_string()),
1018 "s" => Some(s),
1019 "r" => Some(r),
1020 "to" => Some(TxTrait::to(transaction).pretty()),
1021 "transactionIndex" | "transaction_index" => {
1022 Some(alloy_network::TransactionResponse::transaction_index(transaction).pretty())
1023 }
1024 "v" => Some(v),
1025 "value" => Some(TxTrait::value(transaction).pretty()),
1026 _ => None,
1027 }
1028}
1029
1030pub fn get_pretty_block_attr<N>(block: &N::BlockResponse, attr: &str) -> Option<String>
1031where
1032 N: Network,
1033 N::BlockResponse: BlockResponse<Header = N::HeaderResponse>,
1034 N::HeaderResponse: UIfmtHeaderExt,
1035{
1036 match attr {
1037 "baseFeePerGas" | "base_fee_per_gas" => Some(block.header().base_fee_per_gas().pretty()),
1038 "difficulty" => Some(block.header().difficulty().pretty()),
1039 "extraData" | "extra_data" => Some(block.header().extra_data().pretty()),
1040 "gasLimit" | "gas_limit" => Some(block.header().gas_limit().pretty()),
1041 "gasUsed" | "gas_used" => Some(block.header().gas_used().pretty()),
1042 "hash" => Some(block.header().hash().pretty()),
1043 "logsBloom" | "logs_bloom" => Some(block.header().logs_bloom().pretty()),
1044 "miner" | "author" => Some(block.header().beneficiary().pretty()),
1045 "mixHash" | "mix_hash" => Some(block.header().mix_hash().pretty()),
1046 "nonce" => Some(block.header().nonce().pretty()),
1047 "number" => Some(block.header().number().pretty()),
1048 "parentHash" | "parent_hash" => Some(block.header().parent_hash().pretty()),
1049 "transactionsRoot" | "transactions_root" => {
1050 Some(block.header().transactions_root().pretty())
1051 }
1052 "receiptsRoot" | "receipts_root" => Some(block.header().receipts_root().pretty()),
1053 "sha3Uncles" | "sha_3_uncles" => Some(block.header().ommers_hash().pretty()),
1054 "size" => Some(block.header().size_pretty()),
1055 "stateRoot" | "state_root" => Some(block.header().state_root().pretty()),
1056 "timestamp" => Some(block.header().timestamp().pretty()),
1057 "totalDifficulty" | "total_difficulty" => Some(block.header().difficulty().pretty()),
1058 "blobGasUsed" | "blob_gas_used" => Some(block.header().blob_gas_used().pretty()),
1059 "excessBlobGas" | "excess_blob_gas" => Some(block.header().excess_blob_gas().pretty()),
1060 "requestsHash" | "requests_hash" => Some(block.header().requests_hash().pretty()),
1061 other => {
1062 if let Some(value) = block.other_fields().and_then(|fields| fields.get(other)) {
1063 let val = EthValue::from(value.clone());
1064 return Some(val.pretty());
1065 }
1066 None
1067 }
1068 }
1069}
1070
1071pub fn get_pretty_receipt_attr<N>(receipt: &N::ReceiptResponse, attr: &str) -> Option<String>
1072where
1073 N: Network,
1074 N::ReceiptResponse: ReceiptResponse + UIfmtReceiptExt,
1075{
1076 match attr {
1077 "blockHash" | "block_hash" => Some(receipt.block_hash().pretty()),
1078 "blockNumber" | "block_number" => Some(receipt.block_number().pretty()),
1079 "contractAddress" | "contract_address" => Some(receipt.contract_address().pretty()),
1080 "cumulativeGasUsed" | "cumulative_gas_used" => Some(receipt.cumulative_gas_used().pretty()),
1081 "effectiveGasPrice" | "effective_gas_price" => Some(receipt.effective_gas_price().pretty()),
1082 "from" => Some(receipt.from().pretty()),
1083 "gasUsed" | "gas_used" => Some(receipt.gas_used().pretty()),
1084 "logs" => Some(receipt.logs_pretty()),
1085 "logsBloom" | "logs_bloom" => Some(receipt.logs_bloom_pretty()),
1086 "root" | "stateRoot" | "state_root" => Some(receipt.state_root().pretty()),
1087 "status" | "statusCode" | "status_code" => Some(receipt.status().pretty()),
1088 "transactionHash" | "transaction_hash" => Some(receipt.transaction_hash().pretty()),
1089 "transactionIndex" | "transaction_index" => Some(receipt.transaction_index().pretty()),
1090 "to" => Some(receipt.to().pretty()),
1091 "type" | "transaction_type" => Some(receipt.tx_type_pretty()),
1092 "blobGasPrice" | "blob_gas_price" => Some(receipt.blob_gas_price().pretty()),
1093 "blobGasUsed" | "blob_gas_used" => Some(receipt.blob_gas_used().pretty()),
1094 _ => None,
1095 }
1096}
1097
1098fn pretty_generic_header_response<H: HeaderResponse + UIfmtHeaderExt>(header: &H) -> String {
1099 format!(
1100 "
1101baseFeePerGas {}
1102difficulty {}
1103extraData {}
1104gasLimit {}
1105gasUsed {}
1106hash {}
1107logsBloom {}
1108miner {}
1109mixHash {}
1110nonce {}
1111number {}
1112parentHash {}
1113parentBeaconRoot {}
1114transactionsRoot {}
1115receiptsRoot {}
1116sha3Uncles {}
1117size {}
1118stateRoot {}
1119timestamp {} ({})
1120withdrawalsRoot {}
1121totalDifficulty {}
1122blobGasUsed {}
1123excessBlobGas {}
1124requestsHash {}",
1125 header.base_fee_per_gas().pretty(),
1126 header.difficulty().pretty(),
1127 header.extra_data().pretty(),
1128 header.gas_limit().pretty(),
1129 header.gas_used().pretty(),
1130 header.hash().pretty(),
1131 header.logs_bloom().pretty(),
1132 header.beneficiary().pretty(),
1133 header.mix_hash().pretty(),
1134 header.nonce().pretty(),
1135 header.number().pretty(),
1136 header.parent_hash().pretty(),
1137 header.parent_beacon_block_root().pretty(),
1138 header.transactions_root().pretty(),
1139 header.receipts_root().pretty(),
1140 header.ommers_hash().pretty(),
1141 header.size_pretty(),
1142 header.state_root().pretty(),
1143 header.timestamp().pretty(),
1144 fmt_timestamp(header.timestamp()),
1145 header.withdrawals_root().pretty(),
1146 header.difficulty().pretty(),
1147 header.blob_gas_used().pretty(),
1148 header.excess_blob_gas().pretty(),
1149 header.requests_hash().pretty(),
1150 )
1151}
1152
1153fn fmt_timestamp(timestamp: u64) -> String {
1157 if timestamp > 2147483647 {
1159 chrono::DateTime::from_timestamp_millis(timestamp as i64)
1161 .expect("block timestamp in range")
1162 .to_rfc3339()
1163 } else {
1164 chrono::DateTime::from_timestamp(timestamp as i64, 0)
1166 .expect("block timestamp in range")
1167 .to_rfc2822()
1168 }
1169}
1170
1171#[cfg(test)]
1172mod tests {
1173 use super::*;
1174 use alloy_primitives::B256;
1175 use alloy_rpc_types::Authorization;
1176 use foundry_primitives::FoundryNetwork;
1177 use similar_asserts::assert_eq;
1178 use std::str::FromStr;
1179
1180 #[test]
1181 fn format_date_time() {
1182 let timestamp = 1756454738u64;
1184
1185 let datetime = fmt_timestamp(timestamp);
1186 assert_eq!(datetime, "Fri, 29 Aug 2025 08:05:38 +0000");
1187 let datetime = fmt_timestamp(timestamp * 1000);
1188 assert_eq!(datetime, "2025-08-29T08:05:38+00:00");
1189 }
1190
1191 #[test]
1192 fn can_format_bytes32() {
1193 let val = hex::decode("7465737400000000000000000000000000000000000000000000000000000000")
1194 .unwrap();
1195 let mut b32 = [0u8; 32];
1196 b32.copy_from_slice(&val);
1197
1198 assert_eq!(
1199 b32.pretty(),
1200 "0x7465737400000000000000000000000000000000000000000000000000000000"
1201 );
1202 let b: Bytes = val.into();
1203 assert_eq!(b.pretty(), b32.pretty());
1204 }
1205
1206 #[test]
1207 fn can_pretty_print_optimism_tx() {
1208 let s = r#"
1209 {
1210 "blockHash": "0x02b853cf50bc1c335b70790f93d5a390a35a166bea9c895e685cc866e4961cae",
1211 "blockNumber": "0x1b4",
1212 "from": "0x3b179DcfC5fAa677044c27dCe958e4BC0ad696A6",
1213 "gas": "0x11cbbdc",
1214 "gasPrice": "0x0",
1215 "hash": "0x2642e960d3150244e298d52b5b0f024782253e6d0b2c9a01dd4858f7b4665a3f",
1216 "input": "0xd294f093",
1217 "nonce": "0xa2",
1218 "to": "0x4a16A42407AA491564643E1dfc1fd50af29794eF",
1219 "transactionIndex": "0x0",
1220 "value": "0x0",
1221 "v": "0x38",
1222 "r": "0x6fca94073a0cf3381978662d46cf890602d3e9ccf6a31e4b69e8ecbd995e2bee",
1223 "s": "0xe804161a2b56a37ca1f6f4c4b8bce926587afa0d9b1acc5165e6556c959d583",
1224 "depositNonce": "",
1225 "depositReceiptVersion": "0x1"
1226 }
1227 "#;
1228
1229 let tx: op_alloy_rpc_types::Transaction<OpTxEnvelope> = serde_json::from_str(s).unwrap();
1230 assert_eq!(
1231 tx.pretty().trim(),
1232 r"
1233depositNonce
1234depositReceiptVersion 1
1235blockHash 0x02b853cf50bc1c335b70790f93d5a390a35a166bea9c895e685cc866e4961cae
1236blockNumber 436
1237from 0x3b179DcfC5fAa677044c27dCe958e4BC0ad696A6
1238transactionIndex 0
1239effectiveGasPrice 0
1240hash 0x2642e960d3150244e298d52b5b0f024782253e6d0b2c9a01dd4858f7b4665a3f
1241type 0
1242chainId 10
1243nonce 162
1244gasPrice 0
1245gasLimit 18660316
1246to 0x4a16A42407AA491564643E1dfc1fd50af29794eF
1247value 0
1248input 0xd294f093
1249r 0x6fca94073a0cf3381978662d46cf890602d3e9ccf6a31e4b69e8ecbd995e2bee
1250s 0x0e804161a2b56a37ca1f6f4c4b8bce926587afa0d9b1acc5165e6556c959d583
1251yParity 1
1252"
1253 .trim()
1254 );
1255 }
1256
1257 #[test]
1258 fn can_pretty_print_optimism_tx_through_any() {
1259 let s = r#"
1260 {
1261 "blockHash": "0x02b853cf50bc1c335b70790f93d5a390a35a166bea9c895e685cc866e4961cae",
1262 "blockNumber": "0x1b4",
1263 "from": "0x3b179DcfC5fAa677044c27dCe958e4BC0ad696A6",
1264 "gas": "0x11cbbdc",
1265 "gasPrice": "0x0",
1266 "hash": "0x2642e960d3150244e298d52b5b0f024782253e6d0b2c9a01dd4858f7b4665a3f",
1267 "input": "0xd294f093",
1268 "nonce": "0xa2",
1269 "to": "0x4a16A42407AA491564643E1dfc1fd50af29794eF",
1270 "transactionIndex": "0x0",
1271 "value": "0x0",
1272 "v": "0x38",
1273 "r": "0x6fca94073a0cf3381978662d46cf890602d3e9ccf6a31e4b69e8ecbd995e2bee",
1274 "s": "0xe804161a2b56a37ca1f6f4c4b8bce926587afa0d9b1acc5165e6556c959d583",
1275 "queueOrigin": "sequencer",
1276 "txType": "",
1277 "l1TxOrigin": null,
1278 "l1BlockNumber": "0xc1a65c",
1279 "l1Timestamp": "0x60d34b60",
1280 "index": "0x1b3",
1281 "queueIndex": null,
1282 "rawTransaction": "0xf86681a28084011cbbdc944a16a42407aa491564643e1dfc1fd50af29794ef8084d294f09338a06fca94073a0cf3381978662d46cf890602d3e9ccf6a31e4b69e8ecbd995e2beea00e804161a2b56a37ca1f6f4c4b8bce926587afa0d9b1acc5165e6556c959d583"
1283 }
1284 "#;
1285
1286 let tx: WithOtherFields<Transaction<AnyTxEnvelope>> = serde_json::from_str(s).unwrap();
1287 assert_eq!(tx.pretty().trim(),
1288 r"
1289blockHash 0x02b853cf50bc1c335b70790f93d5a390a35a166bea9c895e685cc866e4961cae
1290blockNumber 436
1291from 0x3b179DcfC5fAa677044c27dCe958e4BC0ad696A6
1292transactionIndex 0
1293effectiveGasPrice 0
1294hash 0x2642e960d3150244e298d52b5b0f024782253e6d0b2c9a01dd4858f7b4665a3f
1295type 0
1296chainId 10
1297nonce 162
1298gasPrice 0
1299gasLimit 18660316
1300to 0x4a16A42407AA491564643E1dfc1fd50af29794eF
1301value 0
1302input 0xd294f093
1303r 0x6fca94073a0cf3381978662d46cf890602d3e9ccf6a31e4b69e8ecbd995e2bee
1304s 0x0e804161a2b56a37ca1f6f4c4b8bce926587afa0d9b1acc5165e6556c959d583
1305yParity 1
1306index 435
1307l1BlockNumber 12691036
1308l1Timestamp 1624460128
1309l1TxOrigin null
1310queueIndex null
1311queueOrigin sequencer
1312rawTransaction 0xf86681a28084011cbbdc944a16a42407aa491564643e1dfc1fd50af29794ef8084d294f09338a06fca94073a0cf3381978662d46cf890602d3e9ccf6a31e4b69e8ecbd995e2beea00e804161a2b56a37ca1f6f4c4b8bce926587afa0d9b1acc5165e6556c959d583
1313txType 0
1314".trim()
1315 );
1316 }
1317
1318 #[test]
1319 fn can_pretty_print_eip2930() {
1320 let s = r#"{
1321 "type": "0x1",
1322 "blockHash": "0x2b27fe2bbc8ce01ac7ae8bf74f793a197cf7edbe82727588811fa9a2c4776f81",
1323 "blockNumber": "0x12b1d",
1324 "from": "0x2b371c0262ceab27face32fbb5270ddc6aa01ba4",
1325 "gas": "0x6bdf",
1326 "gasPrice": "0x3b9aca00",
1327 "hash": "0xbddbb685774d8a3df036ed9fb920b48f876090a57e9e90ee60921e0510ef7090",
1328 "input": "0x9c0e3f7a0000000000000000000000000000000000000000000000000000000000000078000000000000000000000000000000000000000000000000000000000000002a",
1329 "nonce": "0x1c",
1330 "to": "0x8e730df7c70d33118d9e5f79ab81aed0be6f6635",
1331 "transactionIndex": "0x2",
1332 "value": "0x0",
1333 "v": "0x1",
1334 "r": "0x2a98c51c2782f664d3ce571fef0491b48f5ebbc5845fa513192e6e6b24ecdaa1",
1335 "s": "0x29b8e0c67aa9c11327e16556c591dc84a7aac2f6fc57c7f93901be8ee867aebc",
1336 "chainId": "0x66a",
1337 "accessList": [
1338 { "address": "0x2b371c0262ceab27face32fbb5270ddc6aa01ba4", "storageKeys": ["0x1122334455667788990011223344556677889900112233445566778899001122", "0x0000000000000000000000000000000000000000000000000000000000000000"] },
1339 { "address": "0x8e730df7c70d33118d9e5f79ab81aed0be6f6635", "storageKeys": [] }
1340 ]
1341 }
1342 "#;
1343 let tx: Transaction = serde_json::from_str(s).unwrap();
1344 assert_eq!(tx.pretty().trim(),
1345 r"
1346blockHash 0x2b27fe2bbc8ce01ac7ae8bf74f793a197cf7edbe82727588811fa9a2c4776f81
1347blockNumber 76573
1348from 0x2b371c0262CEAb27fAcE32FBB5270dDc6Aa01ba4
1349transactionIndex 2
1350effectiveGasPrice 1000000000
1351hash 0xbddbb685774d8a3df036ed9fb920b48f876090a57e9e90ee60921e0510ef7090
1352type 1
1353chainId 1642
1354nonce 28
1355gasPrice 1000000000
1356gasLimit 27615
1357to 0x8E730Df7C70D33118D9e5F79ab81aEd0bE6F6635
1358value 0
1359accessList [
1360 0x2b371c0262CEAb27fAcE32FBB5270dDc6Aa01ba4 => [
1361 0x1122334455667788990011223344556677889900112233445566778899001122
1362 0x0000000000000000000000000000000000000000000000000000000000000000
1363 ]
1364 0x8E730Df7C70D33118D9e5F79ab81aEd0bE6F6635 => []
1365]
1366input 0x9c0e3f7a0000000000000000000000000000000000000000000000000000000000000078000000000000000000000000000000000000000000000000000000000000002a
1367r 0x2a98c51c2782f664d3ce571fef0491b48f5ebbc5845fa513192e6e6b24ecdaa1
1368s 0x29b8e0c67aa9c11327e16556c591dc84a7aac2f6fc57c7f93901be8ee867aebc
1369yParity 1
1370".trim()
1371 );
1372 }
1373
1374 #[test]
1375 fn can_pretty_print_eip1559() {
1376 let s = r#"{
1377 "type": "0x2",
1378 "blockHash": "0x61abbe5e22738de0462046f5a5d6c4cd6bc1f3a6398e4457d5e293590e721125",
1379 "blockNumber": "0x7647",
1380 "from": "0xbaadf00d42264eeb3fafe6799d0b56cf55df0f00",
1381 "gas": "0x186a0",
1382 "hash": "0xa7231d4da0576fade5d3b9481f4cd52459ec59b9bbdbf4f60d6cd726b2a3a244",
1383 "input": "0x48600055323160015500",
1384 "nonce": "0x12c",
1385 "to": null,
1386 "transactionIndex": "0x41",
1387 "value": "0x0",
1388 "v": "0x1",
1389 "yParity": "0x1",
1390 "r": "0x396864e5f9132327defdb1449504252e1fa6bce73feb8cd6f348a342b198af34",
1391 "s": "0x44dbba72e6d3304104848277143252ee43627c82f02d1ef8e404e1bf97c70158",
1392 "gasPrice": "0x4a817c800",
1393 "maxFeePerGas": "0x4a817c800",
1394 "maxPriorityFeePerGas": "0x4a817c800",
1395 "chainId": "0x66a",
1396 "accessList": [
1397 {
1398 "address": "0xc141a9a7463e6c4716d9fc0c056c054f46bb2993",
1399 "storageKeys": [
1400 "0x0000000000000000000000000000000000000000000000000000000000000000"
1401 ]
1402 }
1403 ]
1404 }
1405"#;
1406 let tx: Transaction = serde_json::from_str(s).unwrap();
1407 assert_eq!(
1408 tx.pretty().trim(),
1409 r"
1410blockHash 0x61abbe5e22738de0462046f5a5d6c4cd6bc1f3a6398e4457d5e293590e721125
1411blockNumber 30279
1412from 0xBaaDF00d42264eEb3FAFe6799d0b56cf55DF0F00
1413transactionIndex 65
1414effectiveGasPrice 20000000000
1415hash 0xa7231d4da0576fade5d3b9481f4cd52459ec59b9bbdbf4f60d6cd726b2a3a244
1416type 2
1417chainId 1642
1418nonce 300
1419gasLimit 100000
1420maxFeePerGas 20000000000
1421maxPriorityFeePerGas 20000000000
1422to
1423value 0
1424accessList [
1425 0xC141a9A7463e6C4716d9FC0C056C054F46Bb2993 => [
1426 0x0000000000000000000000000000000000000000000000000000000000000000
1427 ]
1428]
1429input 0x48600055323160015500
1430r 0x396864e5f9132327defdb1449504252e1fa6bce73feb8cd6f348a342b198af34
1431s 0x44dbba72e6d3304104848277143252ee43627c82f02d1ef8e404e1bf97c70158
1432yParity 1
1433"
1434 .trim()
1435 );
1436 }
1437
1438 #[test]
1439 fn can_pretty_print_eip4884() {
1440 let s = r#"{
1441 "blockHash": "0xfc2715ff196e23ae613ed6f837abd9035329a720a1f4e8dce3b0694c867ba052",
1442 "blockNumber": "0x2a1cb",
1443 "from": "0xad01b55d7c3448b8899862eb335fbb17075d8de2",
1444 "gas": "0x5208",
1445 "gasPrice": "0x1d1a94a201c",
1446 "maxFeePerGas": "0x1d1a94a201c",
1447 "maxPriorityFeePerGas": "0x1d1a94a201c",
1448 "maxFeePerBlobGas": "0x3e8",
1449 "hash": "0x5ceec39b631763ae0b45a8fb55c373f38b8fab308336ca1dc90ecd2b3cf06d00",
1450 "input": "0x",
1451 "nonce": "0x1b483",
1452 "to": "0x000000000000000000000000000000000000f1c1",
1453 "transactionIndex": "0x0",
1454 "value": "0x0",
1455 "type": "0x3",
1456 "accessList": [],
1457 "chainId": "0x1a1f0ff42",
1458 "blobVersionedHashes": [
1459 "0x01a128c46fc61395706686d6284f83c6c86dfc15769b9363171ea9d8566e6e76"
1460 ],
1461 "v": "0x0",
1462 "r": "0x343c6239323a81ef61293cb4a4d37b6df47fbf68114adb5dd41581151a077da1",
1463 "s": "0x48c21f6872feaf181d37cc4f9bbb356d3f10b352ceb38d1c3b190d749f95a11b",
1464 "yParity": "0x0"
1465 }
1466"#;
1467 let tx: Transaction = serde_json::from_str(s).unwrap();
1468 assert_eq!(
1469 tx.pretty().trim(),
1470 r"
1471blockHash 0xfc2715ff196e23ae613ed6f837abd9035329a720a1f4e8dce3b0694c867ba052
1472blockNumber 172491
1473from 0xAD01b55d7c3448B8899862eb335FBb17075d8DE2
1474transactionIndex 0
1475effectiveGasPrice 2000000000028
1476hash 0x5ceec39b631763ae0b45a8fb55c373f38b8fab308336ca1dc90ecd2b3cf06d00
1477type 3
1478chainId 7011893058
1479nonce 111747
1480gasLimit 21000
1481maxFeePerGas 2000000000028
1482maxPriorityFeePerGas 2000000000028
1483to 0x000000000000000000000000000000000000f1C1
1484value 0
1485accessList []
1486blobVersionedHashes [
1487 0x01a128c46fc61395706686d6284f83c6c86dfc15769b9363171ea9d8566e6e76
1488]
1489maxFeePerBlobGas 1000
1490input 0x
1491r 0x343c6239323a81ef61293cb4a4d37b6df47fbf68114adb5dd41581151a077da1
1492s 0x48c21f6872feaf181d37cc4f9bbb356d3f10b352ceb38d1c3b190d749f95a11b
1493yParity 0
1494"
1495 .trim()
1496 );
1497 }
1498
1499 #[test]
1500 fn print_block_w_txs() {
1501 let block = r#"{"number":"0x3","hash":"0xda53da08ef6a3cbde84c33e51c04f68c3853b6a3731f10baa2324968eee63972","parentHash":"0x689c70c080ca22bc0e681694fa803c1aba16a69c8b6368fed5311d279eb9de90","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","transactionsRoot":"0x7270c1c4440180f2bd5215809ee3d545df042b67329499e1ab97eb759d31610d","stateRoot":"0x29f32984517a7d25607da485b23cefabfd443751422ca7e603395e1de9bc8a4b","receiptsRoot":"0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2","miner":"0x0000000000000000000000000000000000000000","difficulty":"0x0","totalDifficulty":"0x0","extraData":"0x","size":"0x3e8","gasLimit":"0x6691b7","gasUsed":"0x5208","timestamp":"0x5ecedbb9","transactions":[{"hash":"0xc3c5f700243de37ae986082fd2af88d2a7c2752a0c0f7b9d6ac47c729d45e067","nonce":"0x2","blockHash":"0xda53da08ef6a3cbde84c33e51c04f68c3853b6a3731f10baa2324968eee63972","blockNumber":"0x3","transactionIndex":"0x0","from":"0xfdcedc3bfca10ecb0890337fbdd1977aba84807a","to":"0xdca8ce283150ab773bcbeb8d38289bdb5661de1e","value":"0x0","gas":"0x15f90","gasPrice":"0x4a817c800","input":"0x","v":"0x25","r":"0x19f2694eb9113656dbea0b925e2e7ceb43df83e601c4116aee9c0dd99130be88","s":"0x73e5764b324a4f7679d890a198ba658ba1c8cd36983ff9797e10b1b89dbb448e"}],"uncles":[]}"#;
1502 let block: Block = serde_json::from_str(block).unwrap();
1503 let output = "
1504blockHash 0xda53da08ef6a3cbde84c33e51c04f68c3853b6a3731f10baa2324968eee63972
1505blockNumber 3
1506from 0xFdCeDC3bFca10eCb0890337fbdD1977aba84807a
1507transactionIndex 0
1508effectiveGasPrice 20000000000
1509hash 0xc3c5f700243de37ae986082fd2af88d2a7c2752a0c0f7b9d6ac47c729d45e067
1510type 0
1511chainId 1
1512nonce 2
1513gasPrice 20000000000
1514gasLimit 90000
1515to 0xdca8ce283150AB773BCbeB8d38289bdB5661dE1e
1516value 0
1517input 0x
1518r 0x19f2694eb9113656dbea0b925e2e7ceb43df83e601c4116aee9c0dd99130be88
1519s 0x73e5764b324a4f7679d890a198ba658ba1c8cd36983ff9797e10b1b89dbb448e
1520yParity 0"
1521 .to_string();
1522 let txs = match block.transactions() {
1523 BlockTransactions::Full(txs) => txs,
1524 _ => panic!("not full transactions"),
1525 };
1526 let generated = txs[0].pretty();
1527 assert_eq!(generated.as_str(), output.as_str());
1528 }
1529
1530 #[test]
1531 fn uifmt_option_u64() {
1532 assert_eq!(None::<U64>.pretty(), "");
1533 assert_eq!(U64::from(100).pretty(), "100");
1534 assert_eq!(Some(U64::from(100)).pretty(), "100");
1535 }
1536
1537 #[test]
1538 fn uifmt_option_h64() {
1539 assert_eq!(None::<B256>.pretty(), "");
1540 assert_eq!(
1541 B256::with_last_byte(100).pretty(),
1542 "0x0000000000000000000000000000000000000000000000000000000000000064",
1543 );
1544 assert_eq!(
1545 Some(B256::with_last_byte(100)).pretty(),
1546 "0x0000000000000000000000000000000000000000000000000000000000000064",
1547 );
1548 }
1549
1550 #[test]
1551 fn uifmt_option_bytes() {
1552 assert_eq!(None::<Bytes>.pretty(), "");
1553 assert_eq!(
1554 Bytes::from_str("0x0000000000000000000000000000000000000000000000000000000000000064")
1555 .unwrap()
1556 .pretty(),
1557 "0x0000000000000000000000000000000000000000000000000000000000000064",
1558 );
1559 assert_eq!(
1560 Some(
1561 Bytes::from_str(
1562 "0x0000000000000000000000000000000000000000000000000000000000000064"
1563 )
1564 .unwrap()
1565 )
1566 .pretty(),
1567 "0x0000000000000000000000000000000000000000000000000000000000000064",
1568 );
1569 }
1570
1571 #[test]
1572 fn test_pretty_tx_attr() {
1573 let block = r#"{"number":"0x3","hash":"0xda53da08ef6a3cbde84c33e51c04f68c3853b6a3731f10baa2324968eee63972","parentHash":"0x689c70c080ca22bc0e681694fa803c1aba16a69c8b6368fed5311d279eb9de90","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","transactionsRoot":"0x7270c1c4440180f2bd5215809ee3d545df042b67329499e1ab97eb759d31610d","stateRoot":"0x29f32984517a7d25607da485b23cefabfd443751422ca7e603395e1de9bc8a4b","receiptsRoot":"0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2","miner":"0x0000000000000000000000000000000000000000","difficulty":"0x0","totalDifficulty":"0x0","extraData":"0x","size":"0x3e8","gasLimit":"0x6691b7","gasUsed":"0x5208","timestamp":"0x5ecedbb9","transactions":[{"hash":"0xc3c5f700243de37ae986082fd2af88d2a7c2752a0c0f7b9d6ac47c729d45e067","nonce":"0x2","blockHash":"0xda53da08ef6a3cbde84c33e51c04f68c3853b6a3731f10baa2324968eee63972","blockNumber":"0x3","transactionIndex":"0x0","from":"0xfdcedc3bfca10ecb0890337fbdd1977aba84807a","to":"0xdca8ce283150ab773bcbeb8d38289bdb5661de1e","value":"0x0","gas":"0x15f90","gasPrice":"0x4a817c800","input":"0x","v":"0x25","r":"0x19f2694eb9113656dbea0b925e2e7ceb43df83e601c4116aee9c0dd99130be88","s":"0x73e5764b324a4f7679d890a198ba658ba1c8cd36983ff9797e10b1b89dbb448e"}],"uncles":[]}"#;
1574 let block: <FoundryNetwork as Network>::BlockResponse =
1575 serde_json::from_str(block).unwrap();
1576 let txs = match block.transactions() {
1577 BlockTransactions::Full(txes) => txes,
1578 _ => panic!("not full transactions"),
1579 };
1580
1581 assert_eq!(None, get_pretty_tx_attr::<FoundryNetwork>(&txs[0], ""));
1582 assert_eq!(
1583 Some("3".to_string()),
1584 get_pretty_tx_attr::<FoundryNetwork>(&txs[0], "blockNumber")
1585 );
1586 assert_eq!(
1587 Some("0xFdCeDC3bFca10eCb0890337fbdD1977aba84807a".to_string()),
1588 get_pretty_tx_attr::<FoundryNetwork>(&txs[0], "from")
1589 );
1590 assert_eq!(Some("90000".to_string()), get_pretty_tx_attr::<FoundryNetwork>(&txs[0], "gas"));
1591 assert_eq!(
1592 Some("20000000000".to_string()),
1593 get_pretty_tx_attr::<FoundryNetwork>(&txs[0], "gasPrice")
1594 );
1595 assert_eq!(
1596 Some("0xc3c5f700243de37ae986082fd2af88d2a7c2752a0c0f7b9d6ac47c729d45e067".to_string()),
1597 get_pretty_tx_attr::<FoundryNetwork>(&txs[0], "hash")
1598 );
1599 assert_eq!(Some("0x".to_string()), get_pretty_tx_attr::<FoundryNetwork>(&txs[0], "input"));
1600 assert_eq!(Some("2".to_string()), get_pretty_tx_attr::<FoundryNetwork>(&txs[0], "nonce"));
1601 assert_eq!(
1602 Some("0x19f2694eb9113656dbea0b925e2e7ceb43df83e601c4116aee9c0dd99130be88".to_string()),
1603 get_pretty_tx_attr::<FoundryNetwork>(&txs[0], "r")
1604 );
1605 assert_eq!(
1606 Some("0x73e5764b324a4f7679d890a198ba658ba1c8cd36983ff9797e10b1b89dbb448e".to_string()),
1607 get_pretty_tx_attr::<FoundryNetwork>(&txs[0], "s")
1608 );
1609 assert_eq!(
1610 Some("0xdca8ce283150AB773BCbeB8d38289bdB5661dE1e".into()),
1611 get_pretty_tx_attr::<FoundryNetwork>(&txs[0], "to")
1612 );
1613 assert_eq!(
1614 Some("0".to_string()),
1615 get_pretty_tx_attr::<FoundryNetwork>(&txs[0], "transactionIndex")
1616 );
1617 assert_eq!(Some("27".to_string()), get_pretty_tx_attr::<FoundryNetwork>(&txs[0], "v"));
1618 assert_eq!(Some("0".to_string()), get_pretty_tx_attr::<FoundryNetwork>(&txs[0], "value"));
1619 }
1620
1621 #[test]
1622 fn test_pretty_block_attr() {
1623 let json = serde_json::json!(
1624 {
1625 "baseFeePerGas": "0x7",
1626 "miner": "0x0000000000000000000000000000000000000001",
1627 "number": "0x1b4",
1628 "hash": "0x0e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331",
1629 "parentHash": "0x9646252be9520f6e71339a8df9c55e4d7619deeb018d2a3f2d21fc165dde5eb5",
1630 "mixHash": "0x1010101010101010101010101010101010101010101010101010101010101010",
1631 "nonce": "0x0000000000000000",
1632 "sealFields": [
1633 "0xe04d296d2460cfb8472af2c5fd05b5a214109c25688d3704aed5484f9a7792f2",
1634 "0x0000000000000042"
1635 ],
1636 "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
1637 "logsBloom": "0x0e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331",
1638 "transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
1639 "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
1640 "stateRoot": "0xd5855eb08b3387c0af375e9cdb6acfc05eb8f519e419b874b6ff2ffda7ed1dff",
1641 "difficulty": "0x27f07",
1642 "totalDifficulty": "0x27f07",
1643 "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000",
1644 "size": "0x27f07",
1645 "gasLimit": "0x9f759",
1646 "minGasPrice": "0x9f759",
1647 "gasUsed": "0x9f759",
1648 "timestamp": "0x54e34e8e",
1649 "transactions": [],
1650 "uncles": [],
1651 }
1652 );
1653
1654 let block: <FoundryNetwork as Network>::BlockResponse =
1655 serde_json::from_value(json).unwrap();
1656
1657 assert_eq!(None, get_pretty_block_attr::<FoundryNetwork>(&block, ""));
1658 assert_eq!(
1659 Some("7".to_string()),
1660 get_pretty_block_attr::<FoundryNetwork>(&block, "baseFeePerGas")
1661 );
1662 assert_eq!(
1663 Some("163591".to_string()),
1664 get_pretty_block_attr::<FoundryNetwork>(&block, "difficulty")
1665 );
1666 assert_eq!(
1667 Some("0x0000000000000000000000000000000000000000000000000000000000000000".to_string()),
1668 get_pretty_block_attr::<FoundryNetwork>(&block, "extraData")
1669 );
1670 assert_eq!(
1671 Some("653145".to_string()),
1672 get_pretty_block_attr::<FoundryNetwork>(&block, "gasLimit")
1673 );
1674 assert_eq!(
1675 Some("653145".to_string()),
1676 get_pretty_block_attr::<FoundryNetwork>(&block, "gasUsed")
1677 );
1678 assert_eq!(
1679 Some("0x0e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331".to_string()),
1680 get_pretty_block_attr::<FoundryNetwork>(&block, "hash")
1681 );
1682 assert_eq!(Some("0x0e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331".to_string()), get_pretty_block_attr::<FoundryNetwork>(&block, "logsBloom"));
1683 assert_eq!(
1684 Some("0x0000000000000000000000000000000000000001".to_string()),
1685 get_pretty_block_attr::<FoundryNetwork>(&block, "miner")
1686 );
1687 assert_eq!(
1688 Some("0x1010101010101010101010101010101010101010101010101010101010101010".to_string()),
1689 get_pretty_block_attr::<FoundryNetwork>(&block, "mixHash")
1690 );
1691 assert_eq!(
1692 Some("0x0000000000000000".to_string()),
1693 get_pretty_block_attr::<FoundryNetwork>(&block, "nonce")
1694 );
1695 assert_eq!(
1696 Some("436".to_string()),
1697 get_pretty_block_attr::<FoundryNetwork>(&block, "number")
1698 );
1699 assert_eq!(
1700 Some("0x9646252be9520f6e71339a8df9c55e4d7619deeb018d2a3f2d21fc165dde5eb5".to_string()),
1701 get_pretty_block_attr::<FoundryNetwork>(&block, "parentHash")
1702 );
1703 assert_eq!(
1704 Some("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".to_string()),
1705 get_pretty_block_attr::<FoundryNetwork>(&block, "transactionsRoot")
1706 );
1707 assert_eq!(
1708 Some("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".to_string()),
1709 get_pretty_block_attr::<FoundryNetwork>(&block, "receiptsRoot")
1710 );
1711 assert_eq!(
1712 Some("0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347".to_string()),
1713 get_pretty_block_attr::<FoundryNetwork>(&block, "sha3Uncles")
1714 );
1715 assert_eq!(
1716 Some("163591".to_string()),
1717 get_pretty_block_attr::<FoundryNetwork>(&block, "size")
1718 );
1719 assert_eq!(
1720 Some("0xd5855eb08b3387c0af375e9cdb6acfc05eb8f519e419b874b6ff2ffda7ed1dff".to_string()),
1721 get_pretty_block_attr::<FoundryNetwork>(&block, "stateRoot")
1722 );
1723 assert_eq!(
1724 Some("1424182926".to_string()),
1725 get_pretty_block_attr::<FoundryNetwork>(&block, "timestamp")
1726 );
1727 assert_eq!(
1728 Some("163591".to_string()),
1729 get_pretty_block_attr::<FoundryNetwork>(&block, "totalDifficulty")
1730 );
1731 }
1732
1733 #[test]
1734 fn test_receipt_other_fields_alignment() {
1735 let receipt_json = serde_json::json!(
1736 {
1737 "status": "0x1",
1738 "cumulativeGasUsed": "0x74e483",
1739 "logs": [],
1740 "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
1741 "type": "0x2",
1742 "transactionHash": "0x91181b0dca3b29aa136eeb2f536be5ce7b0aebc949be1c44b5509093c516097d",
1743 "transactionIndex": "0x10",
1744 "blockHash": "0x54bafb12e8cea9bb355fbf03a4ac49e42a2a1a80fa6cf4364b342e2de6432b5d",
1745 "blockNumber": "0x7b1ab93",
1746 "gasUsed": "0xc222",
1747 "effectiveGasPrice": "0x18961",
1748 "from": "0x2d815240a61731c75fa01b2793e1d3ed09f289d0",
1749 "to": "0x4200000000000000000000000000000000000000",
1750 "contractAddress": null,
1751 "l1BaseFeeScalar": "0x146b",
1752 "l1BlobBaseFee": "0x6a83078",
1753 "l1BlobBaseFeeScalar": "0xf79c5",
1754 "l1Fee": "0x51a9af7fd3",
1755 "l1GasPrice": "0x972fe4acc",
1756 "l1GasUsed": "0x640"
1757 });
1758
1759 let receipt: AnyTransactionReceipt = serde_json::from_value(receipt_json).unwrap();
1760 let formatted = receipt.pretty();
1761
1762 let expected = r#"
1763blockHash 0x54bafb12e8cea9bb355fbf03a4ac49e42a2a1a80fa6cf4364b342e2de6432b5d
1764blockNumber 129084307
1765contractAddress
1766cumulativeGasUsed 7660675
1767effectiveGasPrice 100705
1768from 0x2D815240A61731c75Fa01b2793E1D3eD09F289d0
1769gasUsed 49698
1770logs []
1771logsBloom 0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
1772root
1773status 1 (success)
1774transactionHash 0x91181b0dca3b29aa136eeb2f536be5ce7b0aebc949be1c44b5509093c516097d
1775transactionIndex 16
1776type 2
1777blobGasPrice
1778blobGasUsed
1779to 0x4200000000000000000000000000000000000000
1780l1BaseFeeScalar 5227
1781l1BlobBaseFee 111685752
1782l1BlobBaseFeeScalar 1014213
1783l1Fee 350739202003
1784l1GasPrice 40583973580
1785l1GasUsed 1600
1786"#;
1787
1788 assert_eq!(formatted.trim(), expected.trim());
1789 }
1790
1791 #[test]
1792 fn test_uifmt_for_signed_authorization() {
1793 let inner = Authorization {
1794 chain_id: U256::from(1),
1795 address: "0x000000000000000000000000000000000000dead".parse::<Address>().unwrap(),
1796 nonce: 42,
1797 };
1798 let signed_authorization =
1799 SignedAuthorization::new_unchecked(inner, 1, U256::from(20), U256::from(30));
1800
1801 assert_eq!(
1802 signed_authorization.pretty(),
1803 r#"{recoveredAuthority: 0xf3eaBD0de6Ca1aE7fC4D81FfD6C9a40e5D5D7e30, signedAuthority: {"chainId":"0x1","address":"0x000000000000000000000000000000000000dead","nonce":"0x2a","yParity":"0x1","r":"0x14","s":"0x1e"}}"#
1804 );
1805 }
1806
1807 #[test]
1808 fn can_pretty_print_tempo_tx() {
1809 let s = r#"{
1810 "type":"0x76",
1811 "chainId":"0xa5bd",
1812 "feeToken":"0x20c0000000000000000000000000000000000001",
1813 "maxPriorityFeePerGas":"0x0",
1814 "maxFeePerGas":"0x2cb417800",
1815 "gas":"0x2d178",
1816 "calls":[
1817 {
1818 "data":null,
1819 "input":"0x095ea7b3000000000000000000000000dec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000989680",
1820 "to":"0x20c0000000000000000000000000000000000000",
1821 "value":"0x0"
1822 },
1823 {
1824 "data":null,
1825 "input":"0xf8856c0f00000000000000000000000020c000000000000000000000000000000000000000000000000000000000000020c00000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000989680000000000000000000000000000000000000000000000000000000000097d330",
1826 "to":"0xdec0000000000000000000000000000000000000",
1827 "value":"0x0"
1828 }
1829 ],
1830 "accessList":[],
1831 "nonceKey":"0x0",
1832 "nonce":"0x0",
1833 "feePayerSignature":null,
1834 "validBefore":null,
1835 "validAfter":null,
1836 "keyAuthorization":null,
1837 "aaAuthorizationList":[],
1838 "signature":{
1839 "pubKeyX":"0xaacc80b21e45fb11f349424dce3a2f23547f60c0ff2f8bcaede2a247545ce8dd",
1840 "pubKeyY":"0x87abf0dbb7a5c9507efae2e43833356651b45ac576c2e61cec4e9c0f41fcbf6e",
1841 "r":"0xcfd45c3b19745a42f80b134dcb02a8ba099a0e4e7be1984da54734aa81d8f29f",
1842 "s":"0x74bb9170ae6d25bd510c83fe35895ee5712efe13980a5edc8094c534e23af85e",
1843 "type":"webAuthn",
1844 "webauthnData":"0x7b98b7a8e6c68d7eac741a52e6fdae0560ce3c16ef5427ad46d7a54d0ed86dd41d000000007b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a2238453071464a7a50585167546e645473643649456659457776323173516e626966374c4741776e4b43626b222c226f726967696e223a2268747470733a2f2f74656d706f2d6465782e76657263656c2e617070222c2263726f73734f726967696e223a66616c73657d"
1845 },
1846 "hash":"0x6d6d8c102064e6dee44abad2024a8b1d37959230baab80e70efbf9b0c739c4fd",
1847 "blockHash":"0xc82b23589ceef5341ed307d33554714db6f9eefd4187a9ca8abc910a325b4689",
1848 "blockNumber":"0x321fde",
1849 "transactionIndex":"0x0",
1850 "from":"0x566ff0f4a6114f8072ecdc8a7a8a13d8d0c6b45f",
1851 "gasPrice":"0x2540be400"
1852 }"#;
1853
1854 let tx: Transaction<TempoTxEnvelope> = serde_json::from_str(s).unwrap();
1855
1856 assert_eq!(
1857 tx.pretty().trim(),
1858 r#"
1859blockHash 0xc82b23589ceef5341ed307d33554714db6f9eefd4187a9ca8abc910a325b4689
1860blockNumber 3284958
1861from 0x566Ff0f4a6114F8072ecDC8A7A8A13d8d0C6B45F
1862transactionIndex 0
1863effectiveGasPrice 10000000000
1864hash 0x6d6d8c102064e6dee44abad2024a8b1d37959230baab80e70efbf9b0c739c4fd
1865type 118
1866chainId 42429
1867feeToken 0x20C0000000000000000000000000000000000001
1868maxPriorityFeePerGas 0
1869maxFeePerGas 12000000000
1870gasLimit 184696
1871calls [
1872 to: 0x20C0000000000000000000000000000000000000, value: 0, input: 0x095ea7b3000000000000000000000000dec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000989680
1873 to: 0xDEc0000000000000000000000000000000000000, value: 0, input: 0xf8856c0f00000000000000000000000020c000000000000000000000000000000000000000000000000000000000000020c00000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000989680000000000000000000000000000000000000000000000000000000000097d330
1874]
1875accessList []
1876nonceKey 0
1877nonce 0
1878feePayerSignature
1879validBefore
1880validAfter
1881tempoSignature {"type":"webAuthn","r":"0xcfd45c3b19745a42f80b134dcb02a8ba099a0e4e7be1984da54734aa81d8f29f","s":"0x74bb9170ae6d25bd510c83fe35895ee5712efe13980a5edc8094c534e23af85e","pubKeyX":"0xaacc80b21e45fb11f349424dce3a2f23547f60c0ff2f8bcaede2a247545ce8dd","pubKeyY":"0x87abf0dbb7a5c9507efae2e43833356651b45ac576c2e61cec4e9c0f41fcbf6e","webauthnData":"0x7b98b7a8e6c68d7eac741a52e6fdae0560ce3c16ef5427ad46d7a54d0ed86dd41d000000007b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a2238453071464a7a50585167546e645473643649456659457776323173516e626966374c4741776e4b43626b222c226f726967696e223a2268747470733a2f2f74656d706f2d6465782e76657263656c2e617070222c2263726f73734f726967696e223a66616c73657d"}
1882"#
1883 .trim()
1884 );
1885 }
1886
1887 #[test]
1888 fn can_pretty_print_tempo_receipt() {
1889 let s = r#"{"type":"0x76","status":"0x1","cumulativeGasUsed":"0x176d7f4","logs":[{"address":"0x20c0000000000000000000000000000000000000","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000a70ab0448e66cd77995bfbba5c5b64b41a85f3fd","0x0000000000000000000000000000000000000000000000000000000000000001"],"data":"0x00000000000000000000000000000000000000000000000000000000000003e8","blockHash":"0x860f788b251ece768e63b0d3906d156f652d843848b71c7fe81faacd49139d66","blockNumber":"0x69a1d7","blockTimestamp":"0x69a5d790","transactionHash":"0x04548a0ea27e2cccc1479af3c2ff02da4d4d3ea46af8e8d7edaa49f6ea27073f","transactionIndex":"0x63","logIndex":"0xb8","removed":false},{"address":"0x20c0000000000000000000000000000000000003","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000a70ab0448e66cd77995bfbba5c5b64b41a85f3fd","0x000000000000000000000000feec000000000000000000000000000000000000"],"data":"0x0000000000000000000000000000000000000000000000000000000000000417","blockHash":"0x860f788b251ece768e63b0d3906d156f652d843848b71c7fe81faacd49139d66","blockNumber":"0x69a1d7","blockTimestamp":"0x69a5d790","transactionHash":"0x04548a0ea27e2cccc1479af3c2ff02da4d4d3ea46af8e8d7edaa49f6ea27073f","transactionIndex":"0x63","logIndex":"0xb9","removed":false}],"logsBloom":"0x00000000000000000000000000000000000000000000010000000000000000000000000000000000000000000100000000000000000000000000000000040008000004200000000000000008000000000000000000040000000000000400000000000002000000000000000000000000000000000000000000000010000000000000000000000000000000000020000000000000800000000000000000000000000020000000000000000000000000000000000400000000000000000000000000000002000000000000000400000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000","transactionHash":"0x04548a0ea27e2cccc1479af3c2ff02da4d4d3ea46af8e8d7edaa49f6ea27073f","transactionIndex":"0x63","blockHash":"0x860f788b251ece768e63b0d3906d156f652d843848b71c7fe81faacd49139d66","blockNumber":"0x69a1d7","gasUsed":"0xcc6a","effectiveGasPrice":"0x4a817c802","from":"0xa70ab0448e66cd77995bfbba5c5b64b41a85f3fd","to":"0x20c0000000000000000000000000000000000000","contractAddress":null,"feeToken":"0x20c0000000000000000000000000000000000003","feePayer":"0xa70ab0448e66cd77995bfbba5c5b64b41a85f3fd"}"#;
1890
1891 let tx: TempoTransactionReceipt = serde_json::from_str(s).unwrap();
1892
1893 assert_eq!(
1894 tx.pretty().trim(),
1895 r#"
1896blockHash 0x860f788b251ece768e63b0d3906d156f652d843848b71c7fe81faacd49139d66
1897blockNumber 6922711
1898contractAddress
1899cumulativeGasUsed 24565748
1900effectiveGasPrice 20000000002
1901from 0xa70ab0448e66cD77995bfBBa5c5b64B41a85F3fd
1902gasUsed 52330
1903logs [{"address":"0x20c0000000000000000000000000000000000000","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000a70ab0448e66cd77995bfbba5c5b64b41a85f3fd","0x0000000000000000000000000000000000000000000000000000000000000001"],"data":"0x00000000000000000000000000000000000000000000000000000000000003e8","blockHash":"0x860f788b251ece768e63b0d3906d156f652d843848b71c7fe81faacd49139d66","blockNumber":"0x69a1d7","blockTimestamp":"0x69a5d790","transactionHash":"0x04548a0ea27e2cccc1479af3c2ff02da4d4d3ea46af8e8d7edaa49f6ea27073f","transactionIndex":"0x63","logIndex":"0xb8","removed":false},{"address":"0x20c0000000000000000000000000000000000003","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000a70ab0448e66cd77995bfbba5c5b64b41a85f3fd","0x000000000000000000000000feec000000000000000000000000000000000000"],"data":"0x0000000000000000000000000000000000000000000000000000000000000417","blockHash":"0x860f788b251ece768e63b0d3906d156f652d843848b71c7fe81faacd49139d66","blockNumber":"0x69a1d7","blockTimestamp":"0x69a5d790","transactionHash":"0x04548a0ea27e2cccc1479af3c2ff02da4d4d3ea46af8e8d7edaa49f6ea27073f","transactionIndex":"0x63","logIndex":"0xb9","removed":false}]
1904logsBloom 0x00000000000000000000000000000000000000000000010000000000000000000000000000000000000000000100000000000000000000000000000000040008000004200000000000000008000000000000000000040000000000000400000000000002000000000000000000000000000000000000000000000010000000000000000000000000000000000020000000000000800000000000000000000000000020000000000000000000000000000000000400000000000000000000000000000002000000000000000400000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000
1905root
1906status true
1907transactionHash 0x04548a0ea27e2cccc1479af3c2ff02da4d4d3ea46af8e8d7edaa49f6ea27073f
1908transactionIndex 99
1909type 118
1910feePayer 0xa70ab0448e66cD77995bfBBa5c5b64B41a85F3fd
1911feeToken 0x20C0000000000000000000000000000000000003
1912to 0x20C0000000000000000000000000000000000000
1913"#
1914 .trim()
1915 );
1916 }
1917
1918 #[test]
1919 fn test_foundry_tx_receipt_uifmt() {
1920 use alloy_network::AnyTransactionReceipt;
1921 use foundry_primitives::FoundryTxReceipt;
1922
1923 let s = r#"{"type":"0x2","status":"0x1","cumulativeGasUsed":"0x5208","logs":[],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","transactionHash":"0x1234567890123456789012345678901234567890123456789012345678901234","transactionIndex":"0x0","blockHash":"0xabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcd","blockNumber":"0x1","gasUsed":"0x5208","effectiveGasPrice":"0x3b9aca00","from":"0x1234567890123456789012345678901234567890","to":"0x0987654321098765432109876543210987654321","contractAddress":null}"#;
1925 let any_receipt: AnyTransactionReceipt = serde_json::from_str(s).unwrap();
1926 let foundry_receipt = FoundryTxReceipt::try_from(any_receipt).unwrap();
1927
1928 let pretty_output = foundry_receipt.pretty();
1929
1930 assert!(pretty_output.contains("blockHash"));
1932 assert!(pretty_output.contains("blockNumber"));
1933 assert!(pretty_output.contains("status"));
1934 assert!(pretty_output.contains("gasUsed"));
1935 assert!(pretty_output.contains("transactionHash"));
1936 assert!(pretty_output.contains("type"));
1937
1938 assert!(
1940 pretty_output
1941 .contains("0x1234567890123456789012345678901234567890123456789012345678901234")
1942 );
1943
1944 assert!(pretty_output.contains("true"));
1946 }
1947
1948 #[test]
1949 fn test_get_pretty_receipt_attr() {
1950 let receipt_json = serde_json::json!({
1951 "type": "0x2",
1952 "status": "0x1",
1953 "cumulativeGasUsed": "0x5208",
1954 "logs": [],
1955 "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
1956 "transactionHash": "0x1234567890123456789012345678901234567890123456789012345678901234",
1957 "transactionIndex": "0x0",
1958 "blockHash": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcd",
1959 "blockNumber": "0x1",
1960 "gasUsed": "0x5208",
1961 "effectiveGasPrice": "0x3b9aca00",
1962 "from": "0x1234567890123456789012345678901234567890",
1963 "to": "0x0987654321098765432109876543210987654321",
1964 "contractAddress": null
1965 });
1966
1967 let receipt: <FoundryNetwork as Network>::ReceiptResponse =
1968 serde_json::from_value(receipt_json).unwrap();
1969
1970 assert_eq!(
1972 Some("0xabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcd".to_string()),
1973 get_pretty_receipt_attr::<FoundryNetwork>(&receipt, "blockHash")
1974 );
1975 assert_eq!(
1976 Some("1".to_string()),
1977 get_pretty_receipt_attr::<FoundryNetwork>(&receipt, "blockNumber")
1978 );
1979 assert_eq!(
1980 Some("0x1234567890123456789012345678901234567890123456789012345678901234".to_string()),
1981 get_pretty_receipt_attr::<FoundryNetwork>(&receipt, "transactionHash")
1982 );
1983 assert_eq!(
1984 Some("21000".to_string()),
1985 get_pretty_receipt_attr::<FoundryNetwork>(&receipt, "gasUsed")
1986 );
1987 assert_eq!(
1988 Some("true".to_string()),
1989 get_pretty_receipt_attr::<FoundryNetwork>(&receipt, "status")
1990 );
1991 assert_eq!(
1992 Some("2".to_string()),
1993 get_pretty_receipt_attr::<FoundryNetwork>(&receipt, "type")
1994 );
1995 assert_eq!(
1996 Some("[]".to_string()),
1997 get_pretty_receipt_attr::<FoundryNetwork>(&receipt, "logs")
1998 );
1999 assert!(get_pretty_receipt_attr::<FoundryNetwork>(&receipt, "logsBloom").is_some());
2000 }
2001}