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