From d5845b7e7a8b5728f23f3e59efcd4276e96cb9c2 Mon Sep 17 00:00:00 2001 From: Prithvish Baidya Date: Thu, 9 Oct 2025 00:52:18 +0530 Subject: [PATCH] Refactor webhook handling and error management in EIP-7702 and external bundler executors - Updated webhook queuing logic to skip notifications for "waiting for receipt" states, improving efficiency. - Enhanced error handling in the delegation contract checks to differentiate between retryable and non-retryable errors. - Increased maximum confirmation attempts and reduced confirmation retry delay for external bundler to optimize transaction processing. --- executors/src/eip7702_executor/confirm.rs | 22 ++++++++++++++---- executors/src/eip7702_executor/send.rs | 23 +++++++++++++++---- executors/src/external_bundler/confirm.rs | 28 +++++++++++++++++------ 3 files changed, 57 insertions(+), 16 deletions(-) diff --git a/executors/src/eip7702_executor/confirm.rs b/executors/src/eip7702_executor/confirm.rs index 3ebe97b..0195cb9 100644 --- a/executors/src/eip7702_executor/confirm.rs +++ b/executors/src/eip7702_executor/confirm.rs @@ -332,11 +332,25 @@ where tx: &mut TransactionContext<'_>, ) { // Don't modify transaction registry on NACK - job will be retried - if let Err(e) = self.queue_nack_webhook(job, nack_data, tx) { - tracing::error!( + + // Only queue webhook for actual errors, not for "waiting for receipt" states + let should_queue_webhook = !matches!( + nack_data.error, + Eip7702ConfirmationError::ReceiptNotAvailable { .. } + ); + + if should_queue_webhook { + if let Err(e) = self.queue_nack_webhook(job, nack_data, tx) { + tracing::error!( + transaction_id = job.job.data.transaction_id, + error = ?e, + "Failed to queue nack webhook" + ); + } + } else { + tracing::debug!( transaction_id = job.job.data.transaction_id, - error = ?e, - "Failed to queue nack webhook" + "Skipping webhook for receipt not available - transaction still mining" ); } } diff --git a/executors/src/eip7702_executor/send.rs b/executors/src/eip7702_executor/send.rs index 2a6e706..6e1f160 100644 --- a/executors/src/eip7702_executor/send.rs +++ b/executors/src/eip7702_executor/send.rs @@ -226,11 +226,22 @@ where .delegation_contract_cache .get_delegation_contract(transactions.account().chain()) .await - .map_err(|e| Eip7702SendError::DelegationCheckFailed { inner_error: e }) - .map_err_nack( - Some(Duration::from_secs(2)), - twmq::job::RequeuePosition::Last, - )?; + .map_err(|e| { + let mapped_error = Eip7702SendError::DelegationCheckFailed { + inner_error: e.clone(), + }; + + // Retry only if the error is retryable (network issues, 5xx, etc.) + // Client errors (4xx) like "Invalid chain" or auth errors fail immediately + if is_build_error_retryable(&e) { + mapped_error.nack( + Some(Duration::from_secs(2)), + twmq::job::RequeuePosition::Last, + ) + } else { + mapped_error.fail() + } + })?; let transactions = transactions .add_authorization_if_needed( @@ -249,6 +260,8 @@ where }, }; + // Retry only if the error is retryable (network issues, 5xx, etc.) + // Client errors (4xx) like "Invalid chain" or auth errors fail immediately if is_build_error_retryable(&e) { mapped_error.nack_with_max_retries( Some(Duration::from_secs(2)), diff --git a/executors/src/external_bundler/confirm.rs b/executors/src/external_bundler/confirm.rs index 53f0a03..bc273be 100644 --- a/executors/src/external_bundler/confirm.rs +++ b/executors/src/external_bundler/confirm.rs @@ -124,8 +124,8 @@ where deployment_lock, webhook_queue, transaction_registry, - max_confirmation_attempts: 20, // ~5 minutes with 15 second delays - confirmation_retry_delay: Duration::from_secs(5), + max_confirmation_attempts: 50, // ~100 seconds with 2 second delays + confirmation_retry_delay: Duration::from_secs(2), } } @@ -274,12 +274,26 @@ where tx: &mut TransactionContext<'_>, ) { // NEVER release lock on NACK - job will be retried with the same lock - // Just queue webhook with current status - if let Err(e) = self.queue_nack_webhook(job, nack_data, tx) { - tracing::error!( + + // Only queue webhook for actual errors, not for "waiting for receipt" states + let should_queue_webhook = !matches!( + nack_data.error, + UserOpConfirmationError::ReceiptNotAvailable { .. } + ); + + if should_queue_webhook { + if let Err(e) = self.queue_nack_webhook(job, nack_data, tx) { + tracing::error!( + transaction_id = job.job.data.transaction_id, + error = ?e, + "Failed to queue nack webhook" + ); + } + } else { + tracing::debug!( transaction_id = job.job.data.transaction_id, - error = ?e, - "Failed to queue nack webhook" + attempt = job.job.attempts, + "Skipping webhook for receipt not available - transaction still mining" ); }