From a121730627545bdb64fe410133d82a1cae5383f8 Mon Sep 17 00:00:00 2001 From: Phillip Ho Date: Fri, 8 Nov 2024 12:59:30 +0800 Subject: [PATCH] fix: retry-failed creates undefined nonce value (#766) * fix: retry-failed creates undefined nonce value * type import --- src/server/middleware/error.ts | 33 ++++++++++------- src/server/routes/transaction/retry-failed.ts | 3 +- src/worker/tasks/sendTransactionWorker.ts | 36 ++++++++++--------- 3 files changed, 42 insertions(+), 30 deletions(-) diff --git a/src/server/middleware/error.ts b/src/server/middleware/error.ts index 0287e3f50..1db10c075 100644 --- a/src/server/middleware/error.ts +++ b/src/server/middleware/error.ts @@ -53,7 +53,17 @@ const isZodError = (err: unknown): boolean => { export const withErrorHandler = async (server: FastifyInstance) => { server.setErrorHandler( - (error: Error | CustomError | ZodError, request, reply) => { + (error: string | Error | CustomError | ZodError, request, reply) => { + if (typeof error === "string") { + return reply.status(StatusCodes.INTERNAL_SERVER_ERROR).send({ + error: { + statusCode: 500, + code: "INTERNAL_SERVER_ERROR", + message: error || ReasonPhrases.INTERNAL_SERVER_ERROR, + }, + }); + } + // Ethers Error Codes if (parseEthersError(error)) { return reply.status(StatusCodes.BAD_REQUEST).send({ @@ -103,7 +113,7 @@ export const withErrorHandler = async (server: FastifyInstance) => { StatusCodes.INTERNAL_SERVER_ERROR; const message = error.message ?? ReasonPhrases.INTERNAL_SERVER_ERROR; - reply.status(statusCode).send({ + return reply.status(statusCode).send({ error: { code, message, @@ -111,17 +121,16 @@ export const withErrorHandler = async (server: FastifyInstance) => { stack: env.NODE_ENV !== "production" ? error.stack : undefined, }, }); - } else { - // Handle non-custom errors - reply.status(StatusCodes.INTERNAL_SERVER_ERROR).send({ - error: { - statusCode: 500, - code: "INTERNAL_SERVER_ERROR", - message: error.message || ReasonPhrases.INTERNAL_SERVER_ERROR, - stack: env.NODE_ENV !== "production" ? error.stack : undefined, - }, - }); } + + reply.status(StatusCodes.INTERNAL_SERVER_ERROR).send({ + error: { + statusCode: 500, + code: "INTERNAL_SERVER_ERROR", + message: error.message || ReasonPhrases.INTERNAL_SERVER_ERROR, + stack: env.NODE_ENV !== "production" ? error.stack : undefined, + }, + }); }, ); }; diff --git a/src/server/routes/transaction/retry-failed.ts b/src/server/routes/transaction/retry-failed.ts index 0cb6dd73c..0227f2ca3 100644 --- a/src/server/routes/transaction/retry-failed.ts +++ b/src/server/routes/transaction/retry-failed.ts @@ -69,10 +69,9 @@ export async function retryFailedTransaction(fastify: FastifyInstance) { ); } - // temp do not handle userop if (transaction.isUserOp) { throw createCustomError( - `Transaction cannot be retried because it is a userop`, + "Transaction cannot be retried because it is a userop", StatusCodes.BAD_REQUEST, "TRANSACTION_CANNOT_BE_RETRIED", ); diff --git a/src/worker/tasks/sendTransactionWorker.ts b/src/worker/tasks/sendTransactionWorker.ts index 6b3d091c9..803de187b 100644 --- a/src/worker/tasks/sendTransactionWorker.ts +++ b/src/worker/tasks/sendTransactionWorker.ts @@ -78,17 +78,15 @@ const handler: Processor = async (job: Job) => { // For example, the developer retried all failed transactions during an RPC outage. // An errored queued transaction (resendCount = 0) is safe to retry: the transaction wasn't sent to RPC. if (transaction.status === "errored" && resendCount === 0) { + const { errorMessage, ...omitted } = transaction; transaction = { - ...{ - ...transaction, - nonce: undefined, - errorMessage: undefined, - gas: undefined, - gasPrice: undefined, - maxFeePerGas: undefined, - maxPriorityFeePerGas: undefined, - }, + ...omitted, status: "queued", + resendCount: 0, + queueId: transaction.queueId, + queuedAt: transaction.queuedAt, + value: transaction.value, + data: transaction.data, manuallyResentAt: new Date(), } satisfies QueuedTransaction; } @@ -246,11 +244,14 @@ const _sendUserOp = async ( waitForDeployment: false, })) as UserOperation; // TODO support entrypoint v0.7 accounts } catch (error) { - return { + const errorMessage = wrapError(error, "Bundler").message; + const erroredTransaction: ErroredTransaction = { ...queuedTransaction, status: "errored", - errorMessage: wrapError(error, "Bundler").message, - } satisfies ErroredTransaction; + errorMessage, + }; + job.log(`Failed to populate transaction: ${errorMessage}`); + return erroredTransaction; } job.log(`Populated userOp: ${stringify(signedUserOp)}`); @@ -322,12 +323,15 @@ const _sendTransaction = async ( maxPriorityFeePerGas: overrides?.maxPriorityFeePerGas, }, }); - } catch (e: unknown) { - return { + } catch (error: unknown) { + const errorMessage = wrapError(error, "RPC").message; + const erroredTransaction: ErroredTransaction = { ...queuedTransaction, status: "errored", - errorMessage: wrapError(e, "RPC").message, - } satisfies ErroredTransaction; + errorMessage, + }; + job.log(`Failed to populate transaction: ${errorMessage}`); + return erroredTransaction; } // Handle if `maxFeePerGas` is overridden.