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