11// cast estimate subcommands
22use crate :: {
3+ cmd:: utils:: { handle_traces, TraceResult } ,
34 opts:: { EthereumOpts , TransactionOpts } ,
45 utils:: { self , parse_ether_value} ,
56} ;
67use cast:: { Cast , TxBuilder } ;
78use clap:: Parser ;
8- use ethers:: types:: { BlockId , NameOrAddress , U256 } ;
9+ use ethers:: {
10+ solc:: EvmVersion ,
11+ types:: { BlockId , NameOrAddress , U256 } ,
12+ } ;
913use eyre:: WrapErr ;
10- use foundry_config:: Config ;
14+ use forge:: executor:: opts:: EvmOpts ;
15+ use foundry_config:: { find_project_root_path, Config } ;
16+ use foundry_evm:: trace:: TracingExecutor ;
1117use std:: str:: FromStr ;
1218
19+ type Provider =
20+ ethers:: providers:: Provider < ethers:: providers:: RetryClient < ethers:: providers:: Http > > ;
21+
1322/// CLI arguments for `cast call`.
1423#[ derive( Debug , Parser ) ]
1524pub struct CallArgs {
@@ -31,13 +40,41 @@ pub struct CallArgs {
3140 ) ]
3241 data : Option < String > ,
3342
43+ /// Forks the remote rpc, executes the transaction locally and prints a trace
44+ #[ clap( long, default_value_t = false ) ]
45+ trace : bool ,
46+
47+ /// Can only be used with "--trace"
48+ ///
49+ /// opens an interactive debugger
50+ #[ clap( long, requires = "trace" ) ]
51+ debug : bool ,
52+
53+ /// Can only be used with "--trace"
54+ ///
55+ /// prints a more verbose trace
56+ #[ clap( long, requires = "trace" ) ]
57+ verbose : bool ,
58+
59+ /// Can only be used with "--trace"
60+ /// Labels to apply to the traces.
61+ ///
62+ /// Format: `address:label`
63+ #[ clap( long, requires = "trace" ) ]
64+ labels : Vec < String > ,
65+
66+ /// Can only be used with "--trace"
67+ ///
68+ /// The EVM Version to use.
69+ #[ clap( long, requires = "trace" ) ]
70+ evm_version : Option < EvmVersion > ,
71+
3472 /// The block height to query at.
3573 ///
3674 /// Can also be the tags earliest, finalized, safe, latest, or pending.
3775 #[ clap( long, short) ]
3876 block : Option < BlockId > ,
3977
40- /// Simulate a contract deployment.
4178 #[ clap( subcommand) ]
4279 command : Option < CallSubcommands > ,
4380
@@ -50,6 +87,7 @@ pub struct CallArgs {
5087
5188#[ derive( Debug , Parser ) ]
5289pub enum CallSubcommands {
90+ /// ignores the address field and simulates creating a contract
5391 #[ clap( name = "--create" ) ]
5492 Create {
5593 /// Bytecode of contract.
@@ -73,54 +111,158 @@ pub enum CallSubcommands {
73111
74112impl CallArgs {
75113 pub async fn run ( self ) -> eyre:: Result < ( ) > {
76- let CallArgs { to, sig, args, data, tx, eth, command, block } = self ;
114+ let CallArgs {
115+ to,
116+ sig,
117+ args,
118+ data,
119+ tx,
120+ eth,
121+ command,
122+ block,
123+ trace,
124+ evm_version,
125+ debug,
126+ verbose,
127+ labels,
128+ } = self ;
77129
78130 let config = Config :: from ( & eth) ;
79131 let provider = utils:: get_provider ( & config) ?;
80132 let chain = utils:: get_chain ( config. chain_id , & provider) . await ?;
81133 let sender = eth. wallet . sender ( ) . await ;
82134
83- let mut builder = TxBuilder :: new ( & provider, sender, to, chain, tx. legacy ) . await ?;
135+ let mut builder: TxBuilder < ' _ , Provider > =
136+ TxBuilder :: new ( & provider, sender, to, chain, tx. legacy ) . await ?;
137+
84138 builder
85139 . gas ( tx. gas_limit )
86140 . etherscan_api_key ( config. get_etherscan_api_key ( Some ( chain) ) )
87141 . gas_price ( tx. gas_price )
88142 . priority_gas_price ( tx. priority_gas_price )
89143 . nonce ( tx. nonce ) ;
144+
90145 match command {
91146 Some ( CallSubcommands :: Create { code, sig, args, value } ) => {
92- builder. value ( value) ;
147+ if trace {
148+ let figment = Config :: figment_with_root ( find_project_root_path ( None ) . unwrap ( ) )
149+ . merge ( eth. rpc ) ;
150+
151+ let evm_opts = figment. extract :: < EvmOpts > ( ) ?;
152+
153+ let ( env, fork, chain) =
154+ TracingExecutor :: get_fork_material ( & config, evm_opts) . await ?;
93155
94- let mut data = hex:: decode ( code. strip_prefix ( "0x" ) . unwrap_or ( & code) ) ?;
156+ let mut executor =
157+ foundry_evm:: trace:: TracingExecutor :: new ( env, fork, evm_version, debug)
158+ . await ;
95159
96- if let Some ( s) = sig {
97- let ( mut sigdata, _func) = builder. create_args ( & s, args) . await ?;
98- data. append ( & mut sigdata) ;
160+ let trace = match executor. deploy (
161+ sender,
162+ code. into ( ) ,
163+ value. unwrap_or ( U256 :: zero ( ) ) ,
164+ None ,
165+ ) {
166+ Ok ( deploy_result) => TraceResult :: from ( deploy_result) ,
167+ Err ( evm_err) => TraceResult :: try_from ( evm_err) ?,
168+ } ;
169+
170+ handle_traces ( trace, & config, chain, labels, verbose, debug) . await ?;
171+
172+ return Ok ( ( ) )
99173 }
100174
101- builder. set_data ( data) ;
175+ // fill the builder after the conditional so we dont move values
176+ fill_create ( & mut builder, value, code, sig, args) . await ?;
102177 }
103178 _ => {
104- builder. value ( tx. value ) ;
179+ // fill first here because we need to use the builder in the conditional
180+ fill_tx ( & mut builder, tx. value , sig, args, data) . await ?;
105181
106- if let Some ( sig) = sig {
107- builder. set_args ( sig. as_str ( ) , args) . await ?;
108- }
109- if let Some ( data) = data {
110- // Note: `sig+args` and `data` are mutually exclusive
111- builder. set_data (
112- hex:: decode ( data) . wrap_err ( "Expected hex encoded function data" ) ?,
113- ) ;
182+ if trace {
183+ let figment = Config :: figment_with_root ( find_project_root_path ( None ) . unwrap ( ) )
184+ . merge ( eth. rpc ) ;
185+
186+ let evm_opts = figment. extract :: < EvmOpts > ( ) ?;
187+
188+ let ( env, fork, chain) =
189+ TracingExecutor :: get_fork_material ( & config, evm_opts) . await ?;
190+
191+ let mut executor =
192+ foundry_evm:: trace:: TracingExecutor :: new ( env, fork, evm_version, debug)
193+ . await ;
194+
195+ let ( tx, _) = builder. build ( ) ;
196+
197+ let trace = TraceResult :: from ( executor. call_raw_committing (
198+ sender,
199+ tx. to_addr ( ) . copied ( ) . expect ( "an address to be here" ) ,
200+ tx. data ( ) . cloned ( ) . unwrap_or_default ( ) . to_vec ( ) . into ( ) ,
201+ tx. value ( ) . copied ( ) . unwrap_or_default ( ) ,
202+ ) ?) ;
203+
204+ handle_traces ( trace, & config, chain, labels, verbose, debug) . await ?;
205+
206+ return Ok ( ( ) )
114207 }
115208 }
116209 } ;
117210
118- let builder_output = builder. build ( ) ;
211+ let builder_output: (
212+ ethers:: types:: transaction:: eip2718:: TypedTransaction ,
213+ Option < ethers:: abi:: Function > ,
214+ ) = builder. build ( ) ;
215+
119216 println ! ( "{}" , Cast :: new( provider) . call( builder_output, block) . await ?) ;
217+
120218 Ok ( ( ) )
121219 }
122220}
123221
222+ /// fills the builder from create arg
223+ async fn fill_create (
224+ builder : & mut TxBuilder < ' _ , Provider > ,
225+ value : Option < U256 > ,
226+ code : String ,
227+ sig : Option < String > ,
228+ args : Vec < String > ,
229+ ) -> eyre:: Result < ( ) > {
230+ builder. value ( value) ;
231+
232+ let mut data = hex:: decode ( code. strip_prefix ( "0x" ) . unwrap_or ( & code) ) ?;
233+
234+ if let Some ( s) = sig {
235+ let ( mut sigdata, _func) = builder. create_args ( & s, args) . await ?;
236+ data. append ( & mut sigdata) ;
237+ }
238+
239+ builder. set_data ( data) ;
240+
241+ Ok ( ( ) )
242+ }
243+
244+ /// fills the builder from args
245+ async fn fill_tx (
246+ builder : & mut TxBuilder < ' _ , Provider > ,
247+ value : Option < U256 > ,
248+ sig : Option < String > ,
249+ args : Vec < String > ,
250+ data : Option < String > ,
251+ ) -> eyre:: Result < ( ) > {
252+ builder. value ( value) ;
253+
254+ if let Some ( sig) = sig {
255+ builder. set_args ( sig. as_str ( ) , args) . await ?;
256+ }
257+
258+ if let Some ( data) = data {
259+ // Note: `sig+args` and `data` are mutually exclusive
260+ builder. set_data ( hex:: decode ( data) . wrap_err ( "Expected hex encoded function data" ) ?) ;
261+ }
262+
263+ Ok ( ( ) )
264+ }
265+
124266#[ cfg( test) ]
125267mod tests {
126268 use super :: * ;
0 commit comments