Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 18 additions & 4 deletions executors/src/eip7702_executor/confirm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"
);
}
}
Expand Down
23 changes: 18 additions & 5 deletions executors/src/eip7702_executor/send.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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)),
Expand Down
28 changes: 21 additions & 7 deletions executors/src/external_bundler/confirm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
}
}

Expand Down Expand Up @@ -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"
);
}

Expand Down