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