11use {
2+ alloy:: { primitives:: Address , providers:: Provider } ,
23 configs:: test_util:: TestDefault ,
34 e2e:: setup:: { API_HOST , OnchainComponents , Services , run_test} ,
45 ethrpc:: { Web3 , alloy:: CallBuilderExt } ,
@@ -18,6 +19,12 @@ async fn local_node_order_simulation() {
1819 run_test ( order_simulation) . await ;
1920}
2021
22+ #[ tokio:: test]
23+ #[ ignore]
24+ async fn local_node_order_simulation_block_number ( ) {
25+ run_test ( order_simulation_block_number) . await ;
26+ }
27+
2128async fn order_simulation ( web3 : Web3 ) {
2229 let mut onchain = OnchainComponents :: deploy ( web3. clone ( ) ) . await ;
2330
@@ -87,3 +94,115 @@ async fn order_simulation(web3: Web3) {
8794 assert_eq ! ( tenderly. simulation_type, Some ( SimulationType :: Full ) ) ;
8895 assert_eq ! ( tenderly. value, None ) ;
8996}
97+
98+ async fn order_simulation_block_number ( web3 : Web3 ) {
99+ let mut onchain = OnchainComponents :: deploy ( web3. clone ( ) ) . await ;
100+
101+ let [ solver] = onchain. make_solvers ( 10u64 . eth ( ) ) . await ;
102+ let [ trader] = onchain. make_accounts ( 10u64 . eth ( ) ) . await ;
103+ let [ token] = onchain
104+ . deploy_tokens_with_weth_uni_v2_pools ( 1_000u64 . eth ( ) , 1_000u64 . eth ( ) )
105+ . await ;
106+
107+ // Fund trader so the order passes balance validation at submission time.
108+ onchain
109+ . contracts ( )
110+ . weth
111+ . deposit ( )
112+ . from ( trader. address ( ) )
113+ . value ( 3u64 . eth ( ) )
114+ . send_and_watch ( )
115+ . await
116+ . unwrap ( ) ;
117+ onchain
118+ . contracts ( )
119+ . weth
120+ . approve ( onchain. contracts ( ) . allowance , 3u64 . eth ( ) )
121+ . from ( trader. address ( ) )
122+ . send_and_watch ( )
123+ . await
124+ . unwrap ( ) ;
125+
126+ let services = Services :: new ( & onchain) . await ;
127+ services
128+ . start_protocol_with_args (
129+ configs:: autopilot:: Configuration :: test ( "test_solver" , solver. address ( ) ) ,
130+ configs:: orderbook:: Configuration :: test_default ( ) ,
131+ solver,
132+ )
133+ . await ;
134+
135+ let order = OrderCreation {
136+ sell_token : * onchain. contracts ( ) . weth . address ( ) ,
137+ sell_amount : 2u64 . eth ( ) ,
138+ buy_token : * token. address ( ) ,
139+ buy_amount : 1u64 . eth ( ) ,
140+ valid_to : model:: time:: now_in_epoch_seconds ( ) + 300 ,
141+ kind : OrderKind :: Buy ,
142+ ..Default :: default ( )
143+ }
144+ . sign (
145+ EcdsaSigningScheme :: Eip712 ,
146+ & onchain. contracts ( ) . domain_separator ,
147+ & trader. signer ,
148+ ) ;
149+ let uid = services. create_order ( & order) . await . unwrap ( ) ;
150+
151+ // Transfer all WETH away from the trader — now they have no sell-token
152+ // balance. The current block becomes the "no funds" snapshot.
153+ let burn = Address :: from ( [ 0x42u8 ; 20 ] ) ;
154+ onchain
155+ . contracts ( )
156+ . weth
157+ . transfer ( burn, 3u64 . eth ( ) )
158+ . from ( trader. address ( ) )
159+ . send_and_watch ( )
160+ . await
161+ . unwrap ( ) ;
162+ let block_no_funds = web3. provider . get_block_number ( ) . await . unwrap ( ) ;
163+
164+ // Re-deposit WETH. The current block now has the trader fully funded again.
165+ onchain
166+ . contracts ( )
167+ . weth
168+ . deposit ( )
169+ . from ( trader. address ( ) )
170+ . value ( 3u64 . eth ( ) )
171+ . send_and_watch ( )
172+ . await
173+ . unwrap ( ) ;
174+ let block_with_funds = web3. provider . get_block_number ( ) . await . unwrap ( ) ;
175+
176+ let client = services. client ( ) ;
177+
178+ // Simulation at the block where the trader had no WETH must fail.
179+ let response = client
180+ . get ( format ! (
181+ "{API_HOST}/api/v1/debug/simulation/{uid}?block_number={block_no_funds}"
182+ ) )
183+ . send ( )
184+ . await
185+ . unwrap ( ) ;
186+ assert_eq ! ( response. status( ) , StatusCode :: OK ) ;
187+ let result = response. json :: < OrderSimulationResult > ( ) . await . unwrap ( ) ;
188+ assert ! (
189+ result. error. is_some( ) ,
190+ "expected simulation failure at block {block_no_funds} (no funds), got success"
191+ ) ;
192+
193+ // Simulation at the block where the trader has WETH must succeed.
194+ let response = client
195+ . get ( format ! (
196+ "{API_HOST}/api/v1/debug/simulation/{uid}?block_number={block_with_funds}"
197+ ) )
198+ . send ( )
199+ . await
200+ . unwrap ( ) ;
201+ assert_eq ! ( response. status( ) , StatusCode :: OK ) ;
202+ let result = response. json :: < OrderSimulationResult > ( ) . await . unwrap ( ) ;
203+ assert_eq ! (
204+ result. error, None ,
205+ "expected simulation success at block {block_with_funds} (funded), got error: {:?}" ,
206+ result. error
207+ ) ;
208+ }
0 commit comments