1
1
import { Job , Processor , Worker } from "bullmq" ;
2
+ import { ethers } from "ethers" ;
2
3
import superjson from "superjson" ;
3
4
import { Hex , toSerializableTransaction } from "thirdweb" ;
4
5
import { stringify } from "thirdweb/utils" ;
5
6
import { bundleUserOp } from "thirdweb/wallets/smart" ;
7
+ import type { TransactionSerializable } from "viem" ;
6
8
import { getContractAddress } from "viem" ;
7
9
import { TransactionDB } from "../../db/transactions/db" ;
8
10
import { acquireNonce , releaseNonce } from "../../db/wallets/walletNonce" ;
@@ -12,9 +14,9 @@ import { getChain } from "../../utils/chain";
12
14
import { msSince } from "../../utils/date" ;
13
15
import { env } from "../../utils/env" ;
14
16
import { prettifyError } from "../../utils/error" ;
17
+ import { isEthersErrorCode } from "../../utils/ethers" ;
15
18
import { redis } from "../../utils/redis/redis" ;
16
19
import { thirdwebClient } from "../../utils/sdk" ;
17
- import { simulateQueuedTransaction } from "../../utils/transaction/simulateQueuedTransaction" ;
18
20
import {
19
21
ErroredTransaction ,
20
22
QueuedTransaction ,
@@ -76,15 +78,6 @@ const _handleQueuedTransaction = async (
76
78
const { chainId, from } = queuedTransaction ;
77
79
const chain = await getChain ( chainId ) ;
78
80
79
- const simulateError = await simulateQueuedTransaction ( queuedTransaction ) ;
80
- if ( simulateError ) {
81
- return {
82
- ...queuedTransaction ,
83
- status : "errored" ,
84
- errorMessage : simulateError ,
85
- } ;
86
- }
87
-
88
81
// Handle sending an AA user operation.
89
82
if ( queuedTransaction . isUserOp ) {
90
83
const signedUserOp = await generateSignedUserOperation ( queuedTransaction ) ;
@@ -110,33 +103,45 @@ const _handleQueuedTransaction = async (
110
103
} ;
111
104
}
112
105
113
- // Prepare nonce + gas settings.
114
- const populatedTransaction = await toSerializableTransaction ( {
115
- from,
116
- transaction : {
117
- client : thirdwebClient ,
118
- chain,
106
+ let populatedTransaction : TransactionSerializable ;
107
+ try {
108
+ populatedTransaction = await toSerializableTransaction ( {
109
+ from,
110
+ transaction : {
111
+ client : thirdwebClient ,
112
+ chain,
113
+ ...queuedTransaction ,
114
+ } ,
115
+ } ) ;
116
+ } catch ( error : unknown ) {
117
+ // If the transaction will revert, error.message contains the human-readable error.
118
+ const errorMessage = ( error as Error ) ?. message ?? `${ error } ` ;
119
+ return {
119
120
...queuedTransaction ,
120
- } ,
121
- } ) ;
122
- job . log ( `Populated transaction: ${ stringify ( populatedTransaction ) } ` ) ;
121
+ status : "errored" ,
122
+ errorMessage,
123
+ } ;
124
+ }
123
125
124
126
// Acquire an unused nonce for this transaction.
125
127
const nonce = await acquireNonce ( chainId , from ) ;
128
+ populatedTransaction . nonce = nonce ;
129
+ job . log ( `Populated transaction: ${ stringify ( populatedTransaction ) } ` ) ;
126
130
127
131
// Send transaction to RPC.
128
132
let transactionHash : Hex ;
129
133
try {
130
134
const account = await getAccount ( { chainId, from } ) ;
131
- const sendTransactionResult = await account . sendTransaction ( {
132
- ...populatedTransaction ,
133
- nonce,
134
- } ) ;
135
+ const sendTransactionResult = await account . sendTransaction (
136
+ populatedTransaction ,
137
+ ) ;
135
138
transactionHash = sendTransactionResult . transactionHash ;
136
- } catch ( e ) {
137
- // This transaction was rejected by RPC. Release the nonce to be reused by a future transaction.
138
- await releaseNonce ( chainId , from , nonce ) ;
139
- throw e ;
139
+ } catch ( error : unknown ) {
140
+ // Release the nonce if it has not expired.
141
+ if ( ! isEthersErrorCode ( error , ethers . errors . NONCE_EXPIRED ) ) {
142
+ await releaseNonce ( chainId , from , nonce ) ;
143
+ }
144
+ throw error ;
140
145
}
141
146
142
147
return {
0 commit comments