From b1b79d6ab9908fef4e3ffde58741413d70969f84 Mon Sep 17 00:00:00 2001 From: Prithvish Baidya Date: Fri, 12 Sep 2025 09:06:29 +0530 Subject: [PATCH] feat: add experimental retry for prepareUserOp errors in transaction processing Introduced a new environment variable EXPERIMENTAL__RETRY_PREPARE_USEROP_ERRORS to enable retrying on prepareUserOp errors instead of failing the transaction immediately. Updated transaction handling logic to support this feature. --- src/shared/utils/env.ts | 4 +++ src/worker/tasks/send-transaction-worker.ts | 27 ++++++++++++++++++--- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/shared/utils/env.ts b/src/shared/utils/env.ts index 3db48dcb..85aaf3b9 100644 --- a/src/shared/utils/env.ts +++ b/src/shared/utils/env.ts @@ -115,6 +115,8 @@ export const env = createEnv({ .number() .gt(0.0, "scaling factor must be greater than 0") .default(1.0), + // Retry prepareUserOp errors instead of immediately failing the transaction + EXPERIMENTAL__RETRY_PREPARE_USEROP_ERRORS: boolEnvSchema(false), }, clientPrefix: "NEVER_USED", client: {}, @@ -169,6 +171,8 @@ export const env = createEnv({ process.env.EXPERIMENTAL__MINE_WORKER_MAX_POLL_INTERVAL_SECONDS, EXPERIMENTAL__MINE_WORKER_POLL_INTERVAL_SCALING_FACTOR: process.env.EXPERIMENTAL__MINE_WORKER_POLL_INTERVAL_SCALING_FACTOR, + EXPERIMENTAL__RETRY_PREPARE_USEROP_ERRORS: + process.env.EXPERIMENTAL__RETRY_PREPARE_USEROP_ERRORS, SEND_WEBHOOK_QUEUE_CONCURRENCY: process.env.SEND_WEBHOOK_QUEUE_CONCURRENCY, }, onValidationError: (error: ZodError) => { diff --git a/src/worker/tasks/send-transaction-worker.ts b/src/worker/tasks/send-transaction-worker.ts index 21615b5a..2530eea4 100644 --- a/src/worker/tasks/send-transaction-worker.ts +++ b/src/worker/tasks/send-transaction-worker.ts @@ -295,12 +295,19 @@ const _sendUserOp = async ( }); } catch (error) { const errorMessage = wrapError(error, "Bundler").message; + job.log(`Failed to populate transaction: ${errorMessage}`); + + // If retry is enabled for prepareUserOp errors, throw to trigger job retry + if (env.EXPERIMENTAL__RETRY_PREPARE_USEROP_ERRORS) { + throw error; + } + + // Otherwise, return errored transaction as before const erroredTransaction: ErroredTransaction = { ...queuedTransaction, status: "errored", errorMessage, }; - job.log(`Failed to populate transaction: ${errorMessage}`); return erroredTransaction; } @@ -356,12 +363,19 @@ const _sendUserOp = async ( const errorMessage = `${ wrapError(error, "Bundler").message } Failed to sign prepared userop`; + job.log(`Failed to sign userop: ${errorMessage}`); + + // If retry is enabled for prepareUserOp errors, throw to trigger job retry + if (env.EXPERIMENTAL__RETRY_PREPARE_USEROP_ERRORS) { + throw error; + } + + // Otherwise, return errored transaction as before const erroredTransaction: ErroredTransaction = { ...queuedTransaction, status: "errored", errorMessage, }; - job.log(`Failed to sign userop: ${errorMessage}`); return erroredTransaction; } @@ -383,12 +397,19 @@ const _sendUserOp = async ( const errorMessage = `${ wrapError(error, "Bundler").message } Failed to bundle userop`; + job.log(`Failed to bundle userop: ${errorMessage}`); + + // If retry is enabled for prepareUserOp errors, throw to trigger job retry + if (env.EXPERIMENTAL__RETRY_PREPARE_USEROP_ERRORS) { + throw error; + } + + // Otherwise, return errored transaction as before const erroredTransaction: ErroredTransaction = { ...queuedTransaction, status: "errored", errorMessage, }; - job.log(`Failed to bundle userop: ${errorMessage}`); return erroredTransaction; }