1- use alloy:: {
2- primitives:: { Address , Bytes , U256 } ,
3- rpc:: types:: UserOperationReceipt ,
4- } ;
1+ use alloy:: primitives:: { Address , Bytes , U256 } ;
52use engine_core:: {
6- chain:: { Chain , ChainService } ,
3+ chain:: { Chain , ChainService , RpcCredentials } ,
74 error:: AlloyRpcErrorToEngineError ,
5+ execution_options:: WebhookOptions ,
6+ rpc_clients:: UserOperationReceipt ,
87} ;
98use serde:: { Deserialize , Serialize } ;
109use std:: { sync:: Arc , time:: Duration } ;
1110use twmq:: {
1211 DurableExecution , FailHookData , NackHookData , Queue , SuccessHookData ,
12+ error:: TwmqError ,
1313 hooks:: TransactionContext ,
1414 job:: { Job , JobResult , RequeuePosition , ToJobResult } ,
1515} ;
@@ -19,7 +19,7 @@ use crate::webhook::{
1919 envelope:: { ExecutorStage , HasWebhookOptions , WebhookCapable } ,
2020} ;
2121
22- use super :: { deployment:: RedisDeploymentLock , send :: WebhookOptions } ;
22+ use super :: deployment:: RedisDeploymentLock ;
2323
2424// --- Job Payload ---
2525#[ derive( Serialize , Deserialize , Debug , Clone ) ]
@@ -32,8 +32,8 @@ pub struct UserOpConfirmationJobData {
3232 pub nonce : U256 ,
3333 pub deployment_lock_acquired : bool ,
3434 pub webhook_options : Option < WebhookOptions > ,
35- pub thirdweb_client_id : Option < String > ,
36- pub thirdweb_service_key : Option < String > ,
35+
36+ pub rpc_credentials : RpcCredentials ,
3737}
3838
3939// --- Success Result ---
@@ -47,7 +47,7 @@ pub struct UserOpConfirmationResult {
4747
4848// --- Error Types ---
4949#[ derive( Serialize , Deserialize , Debug , Clone , thiserror:: Error ) ]
50- #[ serde( rename_all = "camelCase " , tag = "errorCode" ) ]
50+ #[ serde( rename_all = "SCREAMING_SNAKE_CASE " , tag = "errorCode" ) ]
5151pub enum UserOpConfirmationError {
5252 #[ error( "Chain service error for chainId {chain_id}: {message}" ) ]
5353 ChainServiceError { chain_id : u64 , message : String } ,
@@ -63,13 +63,21 @@ pub enum UserOpConfirmationError {
6363 user_op_hash : Bytes ,
6464 message : String ,
6565 #[ serde( skip_serializing_if = "Option::is_none" ) ]
66- technical_details : Option < String > ,
66+ inner_error : Option < serde_json :: Value > ,
6767 } ,
6868
6969 #[ error( "Internal error: {message}" ) ]
7070 InternalError { message : String } ,
7171}
7272
73+ impl From < TwmqError > for UserOpConfirmationError {
74+ fn from ( error : TwmqError ) -> Self {
75+ UserOpConfirmationError :: InternalError {
76+ message : format ! ( "Deserialization error for job data: {}" , error. to_string( ) ) ,
77+ }
78+ }
79+ }
80+
7381// --- Handler ---
7482pub struct UserOpConfirmationHandler < CS >
7583where
@@ -115,6 +123,7 @@ where
115123 type ErrorData = UserOpConfirmationError ;
116124 type JobData = UserOpConfirmationJobData ;
117125
126+ #[ tracing:: instrument( skip( self , job) , fields( transaction_id = job. id, stage = Self :: stage_name( ) , executor = Self :: executor_name( ) ) ) ]
118127 async fn process ( & self , job : & Job < Self :: JobData > ) -> JobResult < Self :: Output , Self :: ErrorData > {
119128 let job_data = & job. data ;
120129
@@ -126,28 +135,29 @@ where
126135 chain_id : job_data. chain_id ,
127136 message : format ! ( "Failed to get chain instance: {}" , e) ,
128137 } )
129- . fail_err ( ) ?;
138+ . map_err_fail ( ) ?;
139+
140+ let chain = chain. with_new_default_headers (
141+ job. data
142+ . rpc_credentials
143+ . to_header_map ( )
144+ . map_err ( |e| UserOpConfirmationError :: InternalError {
145+ message : format ! ( "Bad RPC Credential values, unserialisable into headers: {e}" ) ,
146+ } )
147+ . map_err_fail ( ) ?,
148+ ) ;
130149
131150 // 2. Query for User Operation Receipt
132- let receipt_result = chain
151+ let receipt_option = chain
133152 . bundler_client ( )
134153 . get_user_op_receipt ( job_data. user_op_hash . clone ( ) )
135154 . await
136155 . map_err ( |e| UserOpConfirmationError :: ReceiptQueryFailed {
137156 user_op_hash : job_data. user_op_hash . clone ( ) ,
138157 message : e. to_string ( ) ,
139- technical_details : Some (
140- serde_json:: to_string ( & e. to_engine_bundler_error ( & chain) ) . unwrap_or_default ( ) ,
141- ) ,
142- } ) ;
143-
144- let receipt_option = match receipt_result {
145- Ok ( opt) => opt,
146- Err ( e) => {
147- // Network/RPC errors might be temporary, retry
148- return Err ( e) . nack_err ( Some ( self . confirmation_retry_delay ) , RequeuePosition :: Last ) ;
149- }
150- } ;
158+ inner_error : serde_json:: to_value ( & e. to_engine_bundler_error ( & chain) ) . ok ( ) ,
159+ } )
160+ . map_err_nack ( Some ( self . confirmation_retry_delay ) , RequeuePosition :: Last ) ?;
151161
152162 let receipt = match receipt_option {
153163 Some ( receipt) => receipt,
@@ -158,14 +168,14 @@ where
158168 user_op_hash : job_data. user_op_hash . clone ( ) ,
159169 attempt_number : job. attempts ,
160170 } )
161- . fail_err ( ) ; // FAIL - triggers on_fail hook which will release lock
171+ . map_err_fail ( ) ; // FAIL - triggers on_fail hook which will release lock
162172 }
163173
164174 return Err ( UserOpConfirmationError :: ReceiptNotAvailable {
165175 user_op_hash : job_data. user_op_hash . clone ( ) ,
166176 attempt_number : job. attempts ,
167177 } )
168- . nack_err ( Some ( self . confirmation_retry_delay ) , RequeuePosition :: Last ) ;
178+ . map_err_nack ( Some ( self . confirmation_retry_delay ) , RequeuePosition :: Last ) ;
169179 // NACK - triggers on_nack hook which keeps lock for retry
170180 }
171181 } ;
@@ -299,13 +309,7 @@ where
299309
300310impl HasWebhookOptions for UserOpConfirmationJobData {
301311 fn webhook_url ( & self ) -> Option < String > {
302- self . webhook_options
303- . as_ref ( )
304- . map ( |opts| opts. webhook_url . clone ( ) )
305- }
306-
307- fn transaction_id ( & self ) -> String {
308- self . transaction_id . clone ( )
312+ self . webhook_options . as_ref ( ) . map ( |opts| opts. url . clone ( ) )
309313 }
310314}
311315
0 commit comments