1- use std :: time :: Duration ;
1+ use anyhow :: { Context , Result } ;
22use std:: process:: Stdio ;
3- use tokio:: time:: sleep;
4- use anyhow:: { Result , Context } ;
5- use tracing:: { info, debug, error} ;
3+ use std:: time:: Duration ;
64use tokio:: io:: { AsyncBufReadExt , BufReader } ;
75use tokio:: process:: Command as TokioCommand ;
6+ use tokio:: time:: sleep;
7+ use tracing:: { debug, error, info} ;
88
99pub struct AnvilInstance {
1010 pub rpc_url : String ,
@@ -14,32 +14,31 @@ pub struct AnvilInstance {
1414impl AnvilInstance {
1515 pub async fn start_local ( private_key : & str ) -> Result < Self > {
1616 info ! ( "Starting local Anvil instance" ) ;
17-
17+
1818 let mut cmd = TokioCommand :: new ( "anvil" ) ;
1919 cmd. arg ( "--chain-id" )
20- . arg ( "31337" )
21- . arg ( "--accounts" )
22- . arg ( "10" )
23- . arg ( "--balance" )
24- . arg ( "10000" )
25- . arg ( "--gas-limit" )
26- . arg ( "30000000" )
27- . arg ( "--gas-price" )
28- . arg ( "1000000000" )
29- . arg ( "--block-time" )
30- . arg ( "1" )
31- . stdout ( Stdio :: piped ( ) )
32- . stderr ( Stdio :: piped ( ) ) ;
33-
34- let mut child = cmd. spawn ( )
35- . context ( "Failed to start Anvil" ) ?;
36-
20+ . arg ( "31337" )
21+ . arg ( "--accounts" )
22+ . arg ( "10" )
23+ . arg ( "--balance" )
24+ . arg ( "10000" )
25+ . arg ( "--gas-limit" )
26+ . arg ( "30000000" )
27+ . arg ( "--gas-price" )
28+ . arg ( "1000000000" )
29+ . arg ( "--block-time" )
30+ . arg ( "1" )
31+ . stdout ( Stdio :: piped ( ) )
32+ . stderr ( Stdio :: piped ( ) ) ;
33+
34+ let mut child = cmd. spawn ( ) . context ( "Failed to start Anvil" ) ?;
35+
3736 // Start log streaming for Anvil
3837 Self :: start_log_streaming ( & mut child) . await ;
39-
38+
4039 // Wait a bit for Anvil to start
4140 sleep ( Duration :: from_millis ( 500 ) ) . await ;
42-
41+
4342 // Check if process is still running
4443 match child. try_wait ( ) ? {
4544 Some ( status) => {
@@ -49,60 +48,60 @@ impl AnvilInstance {
4948 info ! ( "Anvil process started successfully" ) ;
5049 }
5150 }
52-
51+
5352 let rpc_url = "http://127.0.0.1:8545" . to_string ( ) ;
54-
53+
5554 // Wait for RPC to be ready
5655 Self :: wait_for_rpc_ready ( & rpc_url) . await ?;
57-
56+
5857 // Fund the test account
5958 Self :: fund_test_account ( & rpc_url, private_key) . await ?;
60-
61- Ok ( Self {
62- rpc_url,
63- process : Some ( child) ,
64- } )
59+
60+ Ok ( Self { rpc_url, process : Some ( child) } )
6561 }
66-
62+
6763 async fn wait_for_rpc_ready ( rpc_url : & str ) -> Result < ( ) > {
6864 let client = reqwest:: Client :: new ( ) ;
6965 let mut attempts = 0 ;
7066 const MAX_ATTEMPTS : u32 = 30 ;
71-
67+
7268 while attempts < MAX_ATTEMPTS {
73- if let Ok ( response) = client. post ( rpc_url)
69+ if let Ok ( response) = client
70+ . post ( rpc_url)
7471 . json ( & serde_json:: json!( {
7572 "jsonrpc" : "2.0" ,
7673 "method" : "eth_blockNumber" ,
7774 "params" : [ ] ,
7875 "id" : 1
7976 } ) )
8077 . send ( )
81- . await {
78+ . await
79+ {
8280 if response. status ( ) . is_success ( ) {
8381 info ! ( "Anvil RPC is ready" ) ;
8482 return Ok ( ( ) ) ;
8583 }
8684 }
87-
85+
8886 attempts += 1 ;
8987 sleep ( Duration :: from_millis ( 200 ) ) . await ;
9088 }
91-
89+
9290 Err ( anyhow:: anyhow!( "Anvil RPC failed to become ready after {} attempts" , MAX_ATTEMPTS ) )
9391 }
94-
92+
9593 async fn fund_test_account ( _rpc_url : & str , _private_key : & str ) -> Result < ( ) > {
9694 //TODO This would typically fund accounts for testing
9795 // For now, we'll use the default funded accounts from Anvil
9896 info ! ( "Using default Anvil funded accounts" ) ;
9997 Ok ( ( ) )
10098 }
101-
99+
102100 pub async fn mine_block ( & self ) -> Result < ( ) > {
103101 let client = reqwest:: Client :: new ( ) ;
104-
105- let response = client. post ( & self . rpc_url )
102+
103+ let response = client
104+ . post ( & self . rpc_url )
106105 . json ( & serde_json:: json!( {
107106 "jsonrpc" : "2.0" ,
108107 "method" : "evm_mine" ,
@@ -111,18 +110,19 @@ impl AnvilInstance {
111110 } ) )
112111 . send ( )
113112 . await ?;
114-
113+
115114 if !response. status ( ) . is_success ( ) {
116115 return Err ( anyhow:: anyhow!( "Failed to mine block" ) ) ;
117116 }
118-
117+
119118 Ok ( ( ) )
120119 }
121-
120+
122121 pub async fn get_block_number ( & self ) -> Result < u64 > {
123122 let client = reqwest:: Client :: new ( ) ;
124-
125- let response = client. post ( & self . rpc_url )
123+
124+ let response = client
125+ . post ( & self . rpc_url )
126126 . json ( & serde_json:: json!( {
127127 "jsonrpc" : "2.0" ,
128128 "method" : "eth_blockNumber" ,
@@ -131,31 +131,31 @@ impl AnvilInstance {
131131 } ) )
132132 . send ( )
133133 . await ?;
134-
134+
135135 let result: serde_json:: Value = response. json ( ) . await ?;
136- let hex_value = result [ "result" ] . as_str ( )
137- . ok_or_else ( || anyhow:: anyhow!( "Invalid response format" ) ) ?;
138-
136+ let hex_value =
137+ result [ "result" ] . as_str ( ) . ok_or_else ( || anyhow:: anyhow!( "Invalid response format" ) ) ?;
138+
139139 let block_number = u64:: from_str_radix ( hex_value. trim_start_matches ( "0x" ) , 16 ) ?;
140140 Ok ( block_number)
141141 }
142-
142+
143143 async fn start_log_streaming ( child : & mut tokio:: process:: Child ) {
144144 if let Some ( stdout) = child. stdout . take ( ) {
145145 let reader = BufReader :: new ( stdout) ;
146146 let mut lines = reader. lines ( ) ;
147-
147+
148148 tokio:: spawn ( async move {
149149 while let Ok ( Some ( line) ) = lines. next_line ( ) . await {
150150 debug ! ( "[ANVIL] {}" , line) ;
151151 }
152152 } ) ;
153153 }
154-
154+
155155 if let Some ( stderr) = child. stderr . take ( ) {
156156 let reader = BufReader :: new ( stderr) ;
157157 let mut lines = reader. lines ( ) ;
158-
158+
159159 tokio:: spawn ( async move {
160160 while let Ok ( Some ( line) ) = lines. next_line ( ) . await {
161161 error ! ( "[ANVIL ERROR] {}" , line) ;
@@ -169,36 +169,40 @@ impl AnvilInstance {
169169 /// Deploy a test contract using forge
170170 pub async fn deploy_test_contract ( & self ) -> Result < String > {
171171 info ! ( "Deploying SimpleERC20 test contract..." ) ;
172-
172+
173173 let e2e_tests_dir = std:: env:: current_dir ( ) ?;
174174 let contract_path = e2e_tests_dir. join ( "contracts/SimpleERC20.sol:SimpleERC20" ) ;
175175 let output = std:: process:: Command :: new ( "forge" )
176176 . args ( [
177177 "create" ,
178- "--rpc-url" , & self . rpc_url ,
179- "--private-key" , "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" ,
178+ "--rpc-url" ,
179+ & self . rpc_url ,
180+ "--private-key" ,
181+ "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" ,
180182 "--broadcast" ,
181- & contract_path. to_string_lossy ( )
183+ & contract_path. to_string_lossy ( ) ,
182184 ] )
183185 . current_dir ( & e2e_tests_dir)
184186 . output ( )
185187 . context ( "Failed to run forge command" ) ?;
186-
188+
187189 if !output. status . success ( ) {
188190 let stderr = String :: from_utf8_lossy ( & output. stderr ) ;
189191 return Err ( anyhow:: anyhow!( "Contract deployment failed: {}" , stderr) ) ;
190192 }
191-
193+
192194 // Parse the contract address from forge output
193195 let stdout = String :: from_utf8_lossy ( & output. stdout ) ;
194- let address_line = stdout. lines ( )
196+ let address_line = stdout
197+ . lines ( )
195198 . find ( |line| line. contains ( "Deployed to:" ) )
196199 . ok_or_else ( || anyhow:: anyhow!( "Could not find contract address in forge output" ) ) ?;
197-
198- let address = address_line. split_whitespace ( )
200+
201+ let address = address_line
202+ . split_whitespace ( )
199203 . last ( )
200204 . ok_or_else ( || anyhow:: anyhow!( "Could not parse contract address" ) ) ?;
201-
205+
202206 info ! ( "Test contract deployed at: {}" , address) ;
203207 Ok ( address. to_string ( ) )
204208 }
@@ -208,8 +212,8 @@ impl Drop for AnvilInstance {
208212 fn drop ( & mut self ) {
209213 if let Some ( mut child) = self . process . take ( ) {
210214 info ! ( "Shutting down Anvil instance" ) ;
211- let _ = child. kill ( ) ;
212- // Note: tokio::process::Child doesn't have wait_timeout,
215+ std :: mem :: drop ( child. kill ( ) ) ;
216+ // Note: tokio::process::Child doesn't have wait_timeout,
213217 // but the process will be cleaned up when the child is dropped
214218 }
215219 }
0 commit comments