1- import { Static , Type } from "@sinclair/typebox" ;
2- import { FastifyInstance } from "fastify" ;
1+ import { Type , type Static } from "@sinclair/typebox" ;
2+ import type { FastifyInstance } from "fastify" ;
33import { StatusCodes } from "http-status-codes" ;
4- import { eth_getTransactionReceipt , getRpcClient } from "thirdweb" ;
54import { TransactionDB } from "../../../db/transactions/db" ;
6- import { getChain } from "../../../utils/chain" ;
7- import { thirdwebClient } from "../../../utils/sdk" ;
5+ import {
6+ getReceiptForEOATransaction ,
7+ getReceiptForUserOp ,
8+ } from "../../../lib/transaction/get-transaction-receipt" ;
9+ import type { QueuedTransaction } from "../../../utils/transaction/types" ;
810import { MineTransactionQueue } from "../../../worker/queues/mineTransactionQueue" ;
911import { SendTransactionQueue } from "../../../worker/queues/sendTransactionQueue" ;
1012import { createCustomError } from "../../middleware/error" ;
@@ -26,13 +28,12 @@ export const responseBodySchema = Type.Object({
2628
2729responseBodySchema . example = {
2830 result : {
29- message :
30- "Transaction queued for retry with queueId: a20ed4ce-301d-4251-a7af-86bd88f6c015" ,
31+ message : "Sent transaction to be retried." ,
3132 status : "success" ,
3233 } ,
3334} ;
3435
35- export async function retryFailedTransaction ( fastify : FastifyInstance ) {
36+ export async function retryFailedTransactionRoute ( fastify : FastifyInstance ) {
3637 fastify . route < {
3738 Body : Static < typeof requestBodySchema > ;
3839 Reply : Static < typeof responseBodySchema > ;
@@ -63,69 +64,48 @@ export async function retryFailedTransaction(fastify: FastifyInstance) {
6364 }
6465 if ( transaction . status !== "errored" ) {
6566 throw createCustomError (
66- `Transaction cannot be retried because status: ${ transaction . status } ` ,
67+ `Cannot retry a transaction with status ${ transaction . status } . ` ,
6768 StatusCodes . BAD_REQUEST ,
6869 "TRANSACTION_CANNOT_BE_RETRIED" ,
6970 ) ;
7071 }
7172
72- if ( transaction . isUserOp ) {
73+ const receipt = transaction . isUserOp
74+ ? await getReceiptForUserOp ( transaction )
75+ : await getReceiptForEOATransaction ( transaction ) ;
76+ if ( receipt ) {
7377 throw createCustomError (
74- "Transaction cannot be retried because it is a userop " ,
78+ "Cannot retry a transaction that is already mined. " ,
7579 StatusCodes . BAD_REQUEST ,
7680 "TRANSACTION_CANNOT_BE_RETRIED" ,
7781 ) ;
7882 }
7983
80- const rpcRequest = getRpcClient ( {
81- client : thirdwebClient ,
82- chain : await getChain ( transaction . chainId ) ,
83- } ) ;
84-
85- // if transaction has sentTransactionHashes, we need to check if any of them are mined
86- if ( "sentTransactionHashes" in transaction ) {
87- const receiptPromises = transaction . sentTransactionHashes . map (
88- ( hash ) => {
89- // if receipt is not found, it will throw an error
90- // so we catch it and return null
91- return eth_getTransactionReceipt ( rpcRequest , {
92- hash,
93- } ) . catch ( ( ) => null ) ;
94- } ,
95- ) ;
96-
97- const receipts = await Promise . all ( receiptPromises ) ;
98-
99- // If any of the transactions are mined, we should not retry.
100- const minedReceipt = receipts . find ( ( receipt ) => ! ! receipt ) ;
101-
102- if ( minedReceipt ) {
103- throw createCustomError (
104- `Transaction cannot be retried because it has already been mined with hash: ${ minedReceipt . transactionHash } ` ,
105- StatusCodes . BAD_REQUEST ,
106- "TRANSACTION_CANNOT_BE_RETRIED" ,
107- ) ;
108- }
109- }
110-
84+ // Remove existing jobs.
11185 const sendJob = await SendTransactionQueue . q . getJob (
11286 SendTransactionQueue . jobId ( {
11387 queueId : transaction . queueId ,
11488 resendCount : 0 ,
11589 } ) ,
11690 ) ;
117- if ( sendJob ) {
118- await sendJob . remove ( ) ;
119- }
91+ await sendJob ?. remove ( ) ;
12092
12193 const mineJob = await MineTransactionQueue . q . getJob (
12294 MineTransactionQueue . jobId ( {
12395 queueId : transaction . queueId ,
12496 } ) ,
12597 ) ;
126- if ( mineJob ) {
127- await mineJob . remove ( ) ;
128- }
98+ await mineJob ?. remove ( ) ;
99+
100+ // Reset the failed job as "queued" and re-enqueue it.
101+ const { errorMessage, ...omitted } = transaction ;
102+ const queuedTransaction : QueuedTransaction = {
103+ ...omitted ,
104+ status : "queued" ,
105+ queuedAt : new Date ( ) ,
106+ resendCount : 0 ,
107+ } ;
108+ await TransactionDB . set ( queuedTransaction ) ;
129109
130110 await SendTransactionQueue . add ( {
131111 queueId : transaction . queueId ,
@@ -134,7 +114,7 @@ export async function retryFailedTransaction(fastify: FastifyInstance) {
134114
135115 reply . status ( StatusCodes . OK ) . send ( {
136116 result : {
137- message : `Transaction queued for retry with queueId: ${ queueId } ` ,
117+ message : "Sent transaction to be retried." ,
138118 status : "success" ,
139119 } ,
140120 } ) ;
0 commit comments