@@ -3,6 +3,7 @@ use crate::cmd::forge::{init::get_commit_hash, script::verify::VerifyBundle};
33use cast:: { executor:: inspector:: DEFAULT_CREATE2_DEPLOYER , CallKind } ;
44use ethers:: {
55 abi:: { Abi , Address } ,
6+ core:: utils:: to_checksum,
67 prelude:: { artifacts:: Libraries , ArtifactId , NameOrAddress , TransactionReceipt , TxHash } ,
78 types:: transaction:: eip2718:: TypedTransaction ,
89} ;
@@ -28,6 +29,7 @@ const DRY_RUN_DIR: &str = "dry-run";
2829#[ derive( Deserialize , Serialize , Clone ) ]
2930pub struct ScriptSequence {
3031 pub transactions : VecDeque < TransactionWithMetadata > ,
32+ #[ serde( serialize_with = "wrapper::serialize_receipts" ) ]
3133 pub receipts : Vec < TransactionReceipt > ,
3234 pub libraries : Vec < String > ,
3335 pub pending : Vec < TxHash > ,
@@ -254,20 +256,21 @@ impl Drop for ScriptSequence {
254256pub struct AdditionalContract {
255257 #[ serde( rename = "transactionType" ) ]
256258 pub opcode : CallKind ,
259+ #[ serde( serialize_with = "wrapper::serialize_addr" ) ]
257260 pub address : Address ,
258261 #[ serde( with = "hex" ) ]
259262 pub init_code : Vec < u8 > ,
260263}
261264
262- #[ derive( Deserialize , Serialize , Clone , Default ) ]
265+ #[ derive( Serialize , Deserialize , Clone , Default ) ]
263266#[ serde( rename_all = "camelCase" ) ]
264267pub struct TransactionWithMetadata {
265268 pub hash : Option < TxHash > ,
266269 #[ serde( rename = "transactionType" ) ]
267270 pub opcode : CallKind ,
268271 #[ serde( default = "default_string" ) ]
269272 pub contract_name : Option < String > ,
270- #[ serde( default = "default_address" ) ]
273+ #[ serde( default = "default_address" , serialize_with = "wrapper::serialize_opt_addr" ) ]
271274 pub contract_address : Option < Address > ,
272275 #[ serde( default = "default_string" ) ]
273276 pub function : Option < String > ,
@@ -441,6 +444,207 @@ fn sig_to_file_name(sig: &str) -> String {
441444 sig. to_string ( )
442445}
443446
447+ // wrapper for modifying ethers-rs type serialization
448+ mod wrapper {
449+ use super :: * ;
450+ use ethers:: types:: { Bloom , Bytes , Log , H256 , U256 , U64 } ;
451+
452+ pub fn serialize_addr < S > ( addr : & Address , serializer : S ) -> Result < S :: Ok , S :: Error >
453+ where
454+ S : serde:: Serializer ,
455+ {
456+ serializer. serialize_str ( & to_checksum ( addr, None ) )
457+ }
458+
459+ pub fn serialize_opt_addr < S > ( opt : & Option < Address > , serializer : S ) -> Result < S :: Ok , S :: Error >
460+ where
461+ S : serde:: Serializer ,
462+ {
463+ match opt {
464+ Some ( addr) => serialize_addr ( addr, serializer) ,
465+ None => serializer. serialize_none ( ) ,
466+ }
467+ }
468+
469+ pub fn serialize_vec_with_wrapped < S , T , WrappedType > (
470+ vec : & [ T ] ,
471+ serializer : S ,
472+ ) -> Result < S :: Ok , S :: Error >
473+ where
474+ S : serde:: Serializer ,
475+ T : Clone ,
476+ WrappedType : serde:: Serialize + From < T > ,
477+ {
478+ serializer. collect_seq ( vec. iter ( ) . cloned ( ) . map ( WrappedType :: from) )
479+ }
480+
481+ // copied from https://github.com/gakonst/ethers-rs
482+ #[ derive( Serialize , Deserialize ) ]
483+ struct WrappedLog {
484+ /// H160. the contract that emitted the log
485+ #[ serde( serialize_with = "serialize_addr" ) ]
486+ pub address : Address ,
487+
488+ /// topics: Array of 0 to 4 32 Bytes of indexed log arguments.
489+ /// (In solidity: The first topic is the hash of the signature of the event
490+ /// (e.g. `Deposit(address,bytes32,uint256)`), except you declared the event
491+ /// with the anonymous specifier.)
492+ pub topics : Vec < H256 > ,
493+
494+ /// Data
495+ pub data : Bytes ,
496+
497+ /// Block Hash
498+ #[ serde( rename = "blockHash" ) ]
499+ #[ serde( skip_serializing_if = "Option::is_none" ) ]
500+ pub block_hash : Option < H256 > ,
501+
502+ /// Block Number
503+ #[ serde( rename = "blockNumber" ) ]
504+ #[ serde( skip_serializing_if = "Option::is_none" ) ]
505+ pub block_number : Option < U64 > ,
506+
507+ /// Transaction Hash
508+ #[ serde( rename = "transactionHash" ) ]
509+ #[ serde( skip_serializing_if = "Option::is_none" ) ]
510+ pub transaction_hash : Option < H256 > ,
511+
512+ /// Transaction Index
513+ #[ serde( rename = "transactionIndex" ) ]
514+ #[ serde( skip_serializing_if = "Option::is_none" ) ]
515+ pub transaction_index : Option < U64 > ,
516+
517+ /// Integer of the log index position in the block. None if it's a pending log.
518+ #[ serde( rename = "logIndex" ) ]
519+ #[ serde( skip_serializing_if = "Option::is_none" ) ]
520+ pub log_index : Option < U256 > ,
521+
522+ /// Integer of the transactions index position log was created from.
523+ /// None when it's a pending log.
524+ #[ serde( rename = "transactionLogIndex" ) ]
525+ #[ serde( skip_serializing_if = "Option::is_none" ) ]
526+ pub transaction_log_index : Option < U256 > ,
527+
528+ /// Log Type
529+ #[ serde( rename = "logType" ) ]
530+ #[ serde( skip_serializing_if = "Option::is_none" ) ]
531+ pub log_type : Option < String > ,
532+
533+ /// True when the log was removed, due to a chain reorganization.
534+ /// false if it's a valid log.
535+ #[ serde( skip_serializing_if = "Option::is_none" ) ]
536+ pub removed : Option < bool > ,
537+ }
538+ impl From < Log > for WrappedLog {
539+ fn from ( log : Log ) -> Self {
540+ Self {
541+ address : log. address ,
542+ topics : log. topics ,
543+ data : log. data ,
544+ block_hash : log. block_hash ,
545+ block_number : log. block_number ,
546+ transaction_hash : log. transaction_hash ,
547+ transaction_index : log. transaction_index ,
548+ log_index : log. log_index ,
549+ transaction_log_index : log. transaction_log_index ,
550+ log_type : log. log_type ,
551+ removed : log. removed ,
552+ }
553+ }
554+ }
555+
556+ fn serialize_logs < S : serde:: Serializer > (
557+ logs : & [ Log ] ,
558+ serializer : S ,
559+ ) -> Result < S :: Ok , S :: Error > {
560+ serialize_vec_with_wrapped :: < S , Log , WrappedLog > ( logs, serializer)
561+ }
562+
563+ // "Receipt" of an executed transaction: details of its execution.
564+ // copied from https://github.com/gakonst/ethers-rs
565+ #[ derive( Default , Clone , Serialize , Deserialize ) ]
566+ pub struct WrappedTransactionReceipt {
567+ /// Transaction hash.
568+ #[ serde( rename = "transactionHash" ) ]
569+ pub transaction_hash : H256 ,
570+ /// Index within the block.
571+ #[ serde( rename = "transactionIndex" ) ]
572+ pub transaction_index : U64 ,
573+ /// Hash of the block this transaction was included within.
574+ #[ serde( rename = "blockHash" ) ]
575+ pub block_hash : Option < H256 > ,
576+ /// Number of the block this transaction was included within.
577+ #[ serde( rename = "blockNumber" ) ]
578+ pub block_number : Option < U64 > ,
579+ /// address of the sender.
580+ #[ serde( serialize_with = "serialize_addr" ) ]
581+ pub from : Address ,
582+ // address of the receiver. null when its a contract creation transaction.
583+ #[ serde( serialize_with = "serialize_opt_addr" ) ]
584+ pub to : Option < Address > ,
585+ /// Cumulative gas used within the block after this was executed.
586+ #[ serde( rename = "cumulativeGasUsed" ) ]
587+ pub cumulative_gas_used : U256 ,
588+ /// Gas used by this transaction alone.
589+ ///
590+ /// Gas used is `None` if the the client is running in light client mode.
591+ #[ serde( rename = "gasUsed" ) ]
592+ pub gas_used : Option < U256 > ,
593+ /// Contract address created, or `None` if not a deployment.
594+ #[ serde( rename = "contractAddress" , serialize_with = "serialize_opt_addr" ) ]
595+ pub contract_address : Option < Address > ,
596+ /// Logs generated within this transaction.
597+ #[ serde( serialize_with = "serialize_logs" ) ]
598+ pub logs : Vec < Log > ,
599+ /// Status: either 1 (success) or 0 (failure). Only present after activation of [EIP-658](https://eips.ethereum.org/EIPS/eip-658)
600+ pub status : Option < U64 > ,
601+ /// State root. Only present before activation of [EIP-658](https://eips.ethereum.org/EIPS/eip-658)
602+ #[ serde( default , skip_serializing_if = "Option::is_none" ) ]
603+ pub root : Option < H256 > ,
604+ /// Logs bloom
605+ #[ serde( rename = "logsBloom" ) ]
606+ pub logs_bloom : Bloom ,
607+ /// Transaction type, Some(1) for AccessList transaction, None for Legacy
608+ #[ serde( rename = "type" , default , skip_serializing_if = "Option::is_none" ) ]
609+ pub transaction_type : Option < U64 > ,
610+ /// The price paid post-execution by the transaction (i.e. base fee + priority fee).
611+ /// Both fields in 1559-style transactions are *maximums* (max fee + max priority fee), the
612+ /// amount that's actually paid by users can only be determined post-execution
613+ #[ serde( rename = "effectiveGasPrice" , default , skip_serializing_if = "Option::is_none" ) ]
614+ pub effective_gas_price : Option < U256 > ,
615+ }
616+ impl From < TransactionReceipt > for WrappedTransactionReceipt {
617+ fn from ( receipt : TransactionReceipt ) -> Self {
618+ Self {
619+ transaction_hash : receipt. transaction_hash ,
620+ transaction_index : receipt. transaction_index ,
621+ block_hash : receipt. block_hash ,
622+ block_number : receipt. block_number ,
623+ from : receipt. from ,
624+ to : receipt. to ,
625+ cumulative_gas_used : receipt. cumulative_gas_used ,
626+ gas_used : receipt. gas_used ,
627+ contract_address : receipt. contract_address ,
628+ logs : receipt. logs ,
629+ status : receipt. status ,
630+ root : receipt. root ,
631+ logs_bloom : receipt. logs_bloom ,
632+ transaction_type : receipt. transaction_type ,
633+ effective_gas_price : receipt. effective_gas_price ,
634+ }
635+ }
636+ }
637+
638+ pub fn serialize_receipts < S : serde:: Serializer > (
639+ receipts : & [ TransactionReceipt ] ,
640+ serializer : S ,
641+ ) -> Result < S :: Ok , S :: Error > {
642+ serialize_vec_with_wrapped :: < S , TransactionReceipt , wrapper:: WrappedTransactionReceipt > (
643+ receipts, serializer,
644+ )
645+ }
646+ }
647+
444648#[ cfg( test) ]
445649mod tests {
446650 use super :: * ;
0 commit comments