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