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