13
13
serde:: { Deserialize , Serialize } ,
14
14
std:: { collections:: HashMap , sync:: Arc , time:: SystemTime } ,
15
15
tracing:: error,
16
+ yttrium:: chain_abstraction:: api:: Transaction ,
16
17
} ;
17
18
18
19
/// Gas estimation caching TTL paramters
@@ -26,7 +27,12 @@ pub struct SimulationRequest {
26
27
pub input : Bytes ,
27
28
pub estimate_gas : bool ,
28
29
pub state_objects : HashMap < Address , StateStorage > ,
29
- pub save : bool ,
30
+ pub save : bool , // Save the simulation to the dashboard
31
+ }
32
+
33
+ #[ derive( Debug , Deserialize , Serialize , Clone ) ]
34
+ pub struct BundledSimulationRequests {
35
+ pub simulations : Vec < SimulationRequest > ,
30
36
}
31
37
32
38
#[ derive( Debug , Deserialize , Serialize , Clone ) ]
@@ -39,10 +45,18 @@ pub struct SimulationResponse {
39
45
pub transaction : ResponseTransaction ,
40
46
}
41
47
48
+ #[ derive( Debug , Deserialize , Serialize , Clone ) ]
49
+ pub struct BundledSimulationResponse {
50
+ pub simulation_results : Vec < SimulationResponse > ,
51
+ }
52
+
42
53
#[ derive( Debug , Deserialize , Serialize , Clone ) ]
43
54
pub struct ResponseTransaction {
55
+ pub hash : String ,
44
56
pub gas_used : u64 ,
45
57
pub transaction_info : ResponseTransactionInfo ,
58
+ pub status : bool , // Was simulating transaction successful
59
+ pub nonce : u64 ,
46
60
}
47
61
48
62
#[ derive( Debug , Deserialize , Serialize , Clone ) ]
@@ -55,7 +69,7 @@ pub struct AssetChange {
55
69
#[ serde( rename = "type" ) ]
56
70
pub asset_type : AssetChangeType ,
57
71
pub from : Address ,
58
- pub to : Address ,
72
+ pub to : Option < Address > ,
59
73
pub raw_amount : U256 ,
60
74
pub token_info : TokenInfo ,
61
75
}
@@ -228,10 +242,91 @@ impl SimulationProvider for TenderlyProvider {
228
242
"Failed to get the transaction simulation response from Tenderly with status: {}" ,
229
243
response. status( )
230
244
) ;
231
- return Err ( RpcError :: ConversionProviderError ) ;
245
+ return Err ( RpcError :: SimulationProviderUnavailable ) ;
232
246
}
233
247
let response = response. json :: < SimulationResponse > ( ) . await ?;
234
248
249
+ // The transaction failed if the `status` field is false
250
+ if !response. transaction . status {
251
+ return Err ( RpcError :: SimulationFailed ( format ! (
252
+ "Failed to simulate the transaction with Tenderly. Transaction hash: {}" ,
253
+ response. transaction. hash
254
+ ) ) ) ;
255
+ }
256
+
257
+ Ok ( response)
258
+ }
259
+
260
+ #[ tracing:: instrument( skip( self ) , fields( provider = "Tenderly" ) , level = "debug" ) ]
261
+ async fn simulate_bundled_transactions (
262
+ & self ,
263
+ transactions : Vec < Transaction > ,
264
+ state_overrides : HashMap < Address , HashMap < B256 , B256 > > ,
265
+ metrics : Arc < Metrics > ,
266
+ ) -> Result < BundledSimulationResponse , RpcError > {
267
+ let url = Url :: parse ( format ! ( "{}/simulate-bundle" , & self . base_api_url) . as_str ( ) )
268
+ . map_err ( |_| RpcError :: ConversionParseURLError ) ?;
269
+
270
+ let mut bundled_simulations = BundledSimulationRequests {
271
+ simulations : vec ! [ ] ,
272
+ } ;
273
+
274
+ for transaction in transactions {
275
+ let ( _, evm_chain_id) = disassemble_caip2 ( & transaction. chain_id ) ?;
276
+
277
+ // fill the state_objects with the state_overrides
278
+ let mut state_objects: HashMap < Address , StateStorage > = HashMap :: new ( ) ;
279
+ for ( address, state) in state_overrides. clone ( ) {
280
+ let mut account_state = StateStorage {
281
+ storage : HashMap :: new ( ) ,
282
+ } ;
283
+ for ( key, value) in state {
284
+ account_state. storage . insert ( key, value) ;
285
+ }
286
+ state_objects. insert ( address, account_state) ;
287
+ }
288
+
289
+ bundled_simulations. simulations . push ( SimulationRequest {
290
+ network_id : evm_chain_id,
291
+ from : transaction. from ,
292
+ to : transaction. to ,
293
+ input : transaction. input ,
294
+ estimate_gas : true ,
295
+ state_objects,
296
+ save : true ,
297
+ } ) ;
298
+ }
299
+
300
+ let latency_start = SystemTime :: now ( ) ;
301
+ let response = self . send_post_request ( url, & bundled_simulations) . await ?;
302
+
303
+ metrics. add_latency_and_status_code_for_provider (
304
+ self . provider_kind ,
305
+ response. status ( ) . into ( ) ,
306
+ latency_start,
307
+ None ,
308
+ Some ( "simulate_bundled" . to_string ( ) ) ,
309
+ ) ;
310
+
311
+ if !response. status ( ) . is_success ( ) {
312
+ error ! (
313
+ "Failed to get the transactions bundled simulation response from Tenderly with status: {}" ,
314
+ response. status( )
315
+ ) ;
316
+ return Err ( RpcError :: SimulationProviderUnavailable ) ;
317
+ }
318
+ let response = response. json :: < BundledSimulationResponse > ( ) . await ?;
319
+
320
+ // Check for the status of each transaction
321
+ for simulation in response. simulation_results . iter ( ) {
322
+ if !simulation. transaction . status {
323
+ return Err ( RpcError :: SimulationFailed ( format ! (
324
+ "Failed to simulate bundled transactions with Tenderly. Failed transaction hash: {}" ,
325
+ simulation. transaction. hash
326
+ ) ) ) ;
327
+ }
328
+ }
329
+
235
330
Ok ( response)
236
331
}
237
332
0 commit comments