1
1
import { Worker , type Job , type Processor } from "bullmq" ;
2
2
import assert from "node:assert" ;
3
3
import superjson from "superjson" ;
4
- import { getAddress , toSerializableTransaction , type Hex } from "thirdweb" ;
4
+ import {
5
+ getAddress ,
6
+ getContract ,
7
+ readContract ,
8
+ toSerializableTransaction ,
9
+ type Hex ,
10
+ } from "thirdweb" ;
5
11
import { stringify } from "thirdweb/utils" ;
6
- import { bundleUserOp } from "thirdweb/wallets/smart" ;
12
+ import {
13
+ bundleUserOp ,
14
+ createAndSignUserOp ,
15
+ type UserOperation ,
16
+ } from "thirdweb/wallets/smart" ;
7
17
import { getContractAddress } from "viem" ;
8
18
import { TransactionDB } from "../../db/transactions/db" ;
9
19
import {
@@ -33,7 +43,6 @@ import type {
33
43
QueuedTransaction ,
34
44
SentTransaction ,
35
45
} from "../../utils/transaction/types" ;
36
- import { generateSignedUserOperation } from "../../utils/transaction/userOperation" ;
37
46
import { enqueueTransactionWebhook } from "../../utils/transaction/webhook" ;
38
47
import { reportUsage } from "../../utils/usage" ;
39
48
import { MineTransactionQueue } from "../queues/mineTransactionQueue" ;
@@ -130,24 +139,80 @@ const _sendUserOp = async (
130
139
} ;
131
140
}
132
141
133
- const { accountAddress, to, target, chainId } = queuedTransaction ;
142
+ const {
143
+ accountAddress,
144
+ to,
145
+ target,
146
+ chainId,
147
+ from,
148
+ accountFactoryAddress : userProvidedAccountFactoryAddress ,
149
+ overrides,
150
+ } = queuedTransaction ;
134
151
const chain = await getChain ( chainId ) ;
135
152
136
153
assert ( accountAddress , "Invalid userOp parameters: accountAddress" ) ;
137
154
const toAddress = to ?? target ;
138
155
assert ( toAddress , "Invalid transaction parameters: to" ) ;
139
156
140
- let populatedTransaction : PopulatedTransaction ;
157
+ // Resolve Admin-Account for UserOperation Signer
158
+ const adminAccount = await getAccount ( {
159
+ chainId,
160
+ from,
161
+ } ) ;
162
+
163
+ let signedUserOp : UserOperation ;
141
164
try {
142
- populatedTransaction = await toSerializableTransaction ( {
143
- from : getChecksumAddress ( accountAddress ) ,
144
- transaction : {
145
- client : thirdwebClient ,
165
+ // Resolve the user factory from the provided address, or from the `factory()` method if found.
166
+ let accountFactoryAddress = userProvidedAccountFactoryAddress ;
167
+ if ( ! accountFactoryAddress ) {
168
+ // TODO: this is not a good solution since the assumption that the account has a factory function is not guaranteed
169
+ // instead, we should use default account factory address or throw here.
170
+ try {
171
+ const smartAccountContract = getContract ( {
172
+ client : thirdwebClient ,
173
+ chain,
174
+ address : accountAddress ,
175
+ } ) ;
176
+ const onchainAccountFactoryAddress = await readContract ( {
177
+ contract : smartAccountContract ,
178
+ method : "function factory() view returns (address)" ,
179
+ params : [ ] ,
180
+ } ) ;
181
+ accountFactoryAddress = getAddress ( onchainAccountFactoryAddress ) ;
182
+ } catch {
183
+ throw new Error (
184
+ `Failed to find factory address for account '${ accountAddress } ' on chain '${ chainId } '` ,
185
+ ) ;
186
+ }
187
+ }
188
+
189
+ signedUserOp = ( await createAndSignUserOp ( {
190
+ client : thirdwebClient ,
191
+ transactions : [
192
+ {
193
+ client : thirdwebClient ,
194
+ chain,
195
+ ...queuedTransaction ,
196
+ ...overrides ,
197
+ to : getChecksumAddress ( toAddress ) ,
198
+ } ,
199
+ ] ,
200
+ adminAccount,
201
+ smartWalletOptions : {
146
202
chain,
147
- ...queuedTransaction ,
148
- to : getChecksumAddress ( toAddress ) ,
203
+ sponsorGas : true ,
204
+ factoryAddress : accountFactoryAddress ,
205
+ overrides : {
206
+ accountAddress,
207
+ // TODO: let user pass entrypoint address for 0.7 support
208
+ } ,
149
209
} ,
150
- } ) ;
210
+ // don't wait for the account to be deployed between userops
211
+ // making this true will cause issues since it will block this call
212
+ // until the previous userop for the same account is mined
213
+ // we don't want this behavior in the engine context
214
+ waitForDeployment : false ,
215
+ } ) ) as UserOperation ; // TODO support entrypoint v0.7 accounts
151
216
} catch ( e ) {
152
217
const erroredTransaction : ErroredTransaction = {
153
218
...queuedTransaction ,
@@ -160,10 +225,6 @@ const _sendUserOp = async (
160
225
return erroredTransaction ;
161
226
}
162
227
163
- const signedUserOp = await generateSignedUserOperation (
164
- queuedTransaction ,
165
- populatedTransaction ,
166
- ) ;
167
228
job . log ( `Populated userOp: ${ stringify ( signedUserOp ) } ` ) ;
168
229
169
230
const userOpHash = await bundleUserOp ( {
0 commit comments