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