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" ;
3
3
import { StatusCodes } from "http-status-codes" ;
4
- import { eth_getTransactionReceipt , getRpcClient } from "thirdweb" ;
5
4
import { 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" ;
8
10
import { MineTransactionQueue } from "../../../worker/queues/mineTransactionQueue" ;
9
11
import { SendTransactionQueue } from "../../../worker/queues/sendTransactionQueue" ;
10
12
import { createCustomError } from "../../middleware/error" ;
@@ -26,13 +28,12 @@ export const responseBodySchema = Type.Object({
26
28
27
29
responseBodySchema . example = {
28
30
result : {
29
- message :
30
- "Transaction queued for retry with queueId: a20ed4ce-301d-4251-a7af-86bd88f6c015" ,
31
+ message : "Sent transaction to be retried." ,
31
32
status : "success" ,
32
33
} ,
33
34
} ;
34
35
35
- export async function retryFailedTransaction ( fastify : FastifyInstance ) {
36
+ export async function retryFailedTransactionRoute ( fastify : FastifyInstance ) {
36
37
fastify . route < {
37
38
Body : Static < typeof requestBodySchema > ;
38
39
Reply : Static < typeof responseBodySchema > ;
@@ -63,69 +64,48 @@ export async function retryFailedTransaction(fastify: FastifyInstance) {
63
64
}
64
65
if ( transaction . status !== "errored" ) {
65
66
throw createCustomError (
66
- `Transaction cannot be retried because status: ${ transaction . status } ` ,
67
+ `Cannot retry a transaction with status ${ transaction . status } . ` ,
67
68
StatusCodes . BAD_REQUEST ,
68
69
"TRANSACTION_CANNOT_BE_RETRIED" ,
69
70
) ;
70
71
}
71
72
72
- if ( transaction . isUserOp ) {
73
+ const receipt = transaction . isUserOp
74
+ ? await getReceiptForUserOp ( transaction )
75
+ : await getReceiptForEOATransaction ( transaction ) ;
76
+ if ( receipt ) {
73
77
throw createCustomError (
74
- "Transaction cannot be retried because it is a userop " ,
78
+ "Cannot retry a transaction that is already mined. " ,
75
79
StatusCodes . BAD_REQUEST ,
76
80
"TRANSACTION_CANNOT_BE_RETRIED" ,
77
81
) ;
78
82
}
79
83
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.
111
85
const sendJob = await SendTransactionQueue . q . getJob (
112
86
SendTransactionQueue . jobId ( {
113
87
queueId : transaction . queueId ,
114
88
resendCount : 0 ,
115
89
} ) ,
116
90
) ;
117
- if ( sendJob ) {
118
- await sendJob . remove ( ) ;
119
- }
91
+ await sendJob ?. remove ( ) ;
120
92
121
93
const mineJob = await MineTransactionQueue . q . getJob (
122
94
MineTransactionQueue . jobId ( {
123
95
queueId : transaction . queueId ,
124
96
} ) ,
125
97
) ;
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 ) ;
129
109
130
110
await SendTransactionQueue . add ( {
131
111
queueId : transaction . queueId ,
@@ -134,7 +114,7 @@ export async function retryFailedTransaction(fastify: FastifyInstance) {
134
114
135
115
reply . status ( StatusCodes . OK ) . send ( {
136
116
result : {
137
- message : `Transaction queued for retry with queueId: ${ queueId } ` ,
117
+ message : "Sent transaction to be retried." ,
138
118
status : "success" ,
139
119
} ,
140
120
} ) ;
0 commit comments