@pimlico/alto 0.0.0-main.20250203T212650 → 0.0.0-main.20250205T142138

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/esm/cli/config/bundler.d.ts +0 -6
  2. package/esm/cli/config/bundler.js +0 -1
  3. package/esm/cli/config/bundler.js.map +1 -1
  4. package/esm/cli/config/options.js +0 -6
  5. package/esm/cli/config/options.js.map +1 -1
  6. package/esm/cli/setupServer.js +5 -5
  7. package/esm/cli/setupServer.js.map +1 -1
  8. package/esm/executor/executor.d.ts +34 -44
  9. package/esm/executor/executor.js +103 -463
  10. package/esm/executor/executor.js.map +1 -1
  11. package/esm/executor/executorManager.d.ts +20 -10
  12. package/esm/executor/executorManager.js +371 -310
  13. package/esm/executor/executorManager.js.map +1 -1
  14. package/esm/executor/filterOpsAndEStimateGas.d.ts +28 -0
  15. package/esm/executor/filterOpsAndEStimateGas.js +191 -0
  16. package/esm/executor/filterOpsAndEStimateGas.js.map +1 -0
  17. package/esm/executor/senderManager.d.ts +2 -0
  18. package/esm/executor/senderManager.js +32 -0
  19. package/esm/executor/senderManager.js.map +1 -1
  20. package/esm/executor/utils.d.ts +21 -20
  21. package/esm/executor/utils.js +46 -185
  22. package/esm/executor/utils.js.map +1 -1
  23. package/esm/handlers/eventManager.d.ts +4 -1
  24. package/esm/handlers/eventManager.js +10 -8
  25. package/esm/handlers/eventManager.js.map +1 -1
  26. package/esm/mempool/mempool.d.ts +15 -11
  27. package/esm/mempool/mempool.js +207 -176
  28. package/esm/mempool/mempool.js.map +1 -1
  29. package/esm/mempool/store.d.ts +7 -7
  30. package/esm/mempool/store.js +13 -12
  31. package/esm/mempool/store.js.map +1 -1
  32. package/esm/rpc/nonceQueuer.js +2 -2
  33. package/esm/rpc/nonceQueuer.js.map +1 -1
  34. package/esm/rpc/rpcHandler.js +27 -31
  35. package/esm/rpc/rpcHandler.js.map +1 -1
  36. package/esm/types/mempool.d.ts +40 -41
  37. package/esm/types/mempool.js.map +1 -1
  38. package/esm/types/schemas.d.ts +0 -4
  39. package/esm/types/schemas.js.map +1 -1
  40. package/esm/utils/userop.d.ts +8 -3
  41. package/esm/utils/userop.js +5 -3
  42. package/esm/utils/userop.js.map +1 -1
  43. package/lib/cli/config/bundler.d.ts +0 -6
  44. package/lib/cli/config/bundler.js +0 -1
  45. package/lib/cli/config/bundler.js.map +1 -1
  46. package/lib/cli/config/options.js +0 -6
  47. package/lib/cli/config/options.js.map +1 -1
  48. package/lib/cli/setupServer.js +5 -5
  49. package/lib/cli/setupServer.js.map +1 -1
  50. package/lib/executor/executor.d.ts +34 -44
  51. package/lib/executor/executor.js +100 -460
  52. package/lib/executor/executor.js.map +1 -1
  53. package/lib/executor/executorManager.d.ts +20 -10
  54. package/lib/executor/executorManager.js +370 -309
  55. package/lib/executor/executorManager.js.map +1 -1
  56. package/lib/executor/filterOpsAndEStimateGas.d.ts +28 -0
  57. package/lib/executor/filterOpsAndEStimateGas.js +218 -0
  58. package/lib/executor/filterOpsAndEStimateGas.js.map +1 -0
  59. package/lib/executor/senderManager.d.ts +2 -0
  60. package/lib/executor/senderManager.js +32 -0
  61. package/lib/executor/senderManager.js.map +1 -1
  62. package/lib/executor/utils.d.ts +21 -20
  63. package/lib/executor/utils.js +49 -186
  64. package/lib/executor/utils.js.map +1 -1
  65. package/lib/handlers/eventManager.d.ts +4 -1
  66. package/lib/handlers/eventManager.js +10 -8
  67. package/lib/handlers/eventManager.js.map +1 -1
  68. package/lib/mempool/mempool.d.ts +15 -11
  69. package/lib/mempool/mempool.js +206 -175
  70. package/lib/mempool/mempool.js.map +1 -1
  71. package/lib/mempool/store.d.ts +7 -7
  72. package/lib/mempool/store.js +13 -12
  73. package/lib/mempool/store.js.map +1 -1
  74. package/lib/rpc/nonceQueuer.js +1 -1
  75. package/lib/rpc/nonceQueuer.js.map +1 -1
  76. package/lib/rpc/rpcHandler.js +26 -30
  77. package/lib/rpc/rpcHandler.js.map +1 -1
  78. package/lib/types/mempool.d.ts +40 -41
  79. package/lib/types/mempool.js.map +1 -1
  80. package/lib/types/schemas.d.ts +0 -4
  81. package/lib/types/schemas.js.map +1 -1
  82. package/lib/utils/userop.d.ts +8 -3
  83. package/lib/utils/userop.js +7 -5
  84. package/lib/utils/userop.js.map +1 -1
  85. package/package.json +1 -1
  86. package/esm/executor/types.d.ts +0 -25
  87. package/esm/executor/types.js +0 -2
  88. package/esm/executor/types.js.map +0 -1
  89. package/lib/executor/types.d.ts +0 -25
  90. package/lib/executor/types.js +0 -3
  91. package/lib/executor/types.js.map +0 -1
@@ -1,25 +1,20 @@
1
- import { EntryPointV06Abi, EntryPointV07Abi } from "../types/index.js";
2
- import { getRequiredPrefund, getUserOperationHash, isVersion06, maxBigInt, parseViemError, scaleBigIntByPercent, toPackedUserOperation } from "../utils/index.js";
1
+ import { maxBigInt, parseViemError, scaleBigIntByPercent } from "../utils/index.js";
3
2
  import * as sentry from "@sentry/node";
4
- import { Mutex } from "async-mutex";
5
- import { FeeCapTooLowError, InsufficientFundsError, IntrinsicGasTooLowError, NonceTooLowError, TransactionExecutionError, encodeFunctionData, getContract, BaseError, NonceTooHighError } from "viem";
6
- import { filterOpsAndEstimateGas, flushStuckTransaction, simulatedOpsToResults, isTransactionUnderpricedError, getAuthorizationList } from "./utils.js";
3
+ import { IntrinsicGasTooLowError, NonceTooLowError, TransactionExecutionError, NonceTooHighError, BaseError } from "viem";
4
+ import { calculateAA95GasFloor, encodeHandleOpsCalldata, getAuthorizationList, getUserOpHashes, isTransactionUnderpricedError } from "./utils.js";
7
5
  import { sendPflConditional } from "./fastlane.js";
6
+ import { filterOpsAndEstimateGas } from "./filterOpsAndEStimateGas.js";
8
7
  export class Executor {
9
- // private unWatch: WatchBlocksReturnType | undefined
10
8
  config;
11
- senderManager;
12
9
  logger;
13
10
  metrics;
14
11
  reputationManager;
15
12
  gasPriceManager;
16
- mutex;
17
13
  mempool;
18
14
  eventManager;
19
- constructor({ config, mempool, senderManager, reputationManager, metrics, gasPriceManager, eventManager }) {
15
+ constructor({ config, mempool, reputationManager, metrics, gasPriceManager, eventManager }) {
20
16
  this.config = config;
21
17
  this.mempool = mempool;
22
- this.senderManager = senderManager;
23
18
  this.reputationManager = reputationManager;
24
19
  this.logger = config.getLogger({ module: "executor" }, {
25
20
  level: config.executorLogLevel || config.logLevel
@@ -27,243 +22,23 @@ export class Executor {
27
22
  this.metrics = metrics;
28
23
  this.gasPriceManager = gasPriceManager;
29
24
  this.eventManager = eventManager;
30
- this.mutex = new Mutex();
31
25
  }
32
26
  cancelOps(_entryPoint, _ops) {
33
27
  throw new Error("Method not implemented.");
34
28
  }
35
- markWalletProcessed(executor) {
36
- if (!this.senderManager.availableWallets.includes(executor)) {
37
- this.senderManager.pushWallet(executor);
38
- }
39
- return Promise.resolve();
40
- }
41
- async replaceTransaction(transactionInfo) {
42
- const newRequest = { ...transactionInfo.transactionRequest };
43
- let gasPriceParameters;
44
- try {
45
- gasPriceParameters =
46
- await this.gasPriceManager.tryGetNetworkGasPrice();
47
- }
48
- catch (err) {
49
- this.logger.error({ error: err }, "Failed to get network gas price");
50
- this.markWalletProcessed(transactionInfo.executor);
51
- return { status: "failed" };
52
- }
53
- newRequest.maxFeePerGas = scaleBigIntByPercent(gasPriceParameters.maxFeePerGas, 115n);
54
- newRequest.maxPriorityFeePerGas = scaleBigIntByPercent(gasPriceParameters.maxPriorityFeePerGas, 115n);
55
- newRequest.account = transactionInfo.executor;
56
- const opsWithHashes = transactionInfo.userOperationInfos.map((opInfo) => {
57
- const op = opInfo.userOperation;
58
- return {
59
- userOperation: opInfo.userOperation,
60
- userOperationHash: getUserOperationHash(op, transactionInfo.entryPoint, this.config.walletClient.chain.id),
61
- entryPoint: opInfo.entryPoint
62
- };
29
+ async sendHandleOpsTransaction({ txParam, gasOpts }) {
30
+ const { isUserOpV06, entryPoint, userOps } = txParam;
31
+ const handleOpsCalldata = encodeHandleOpsCalldata({
32
+ userOps,
33
+ beneficiary: txParam.account.address
63
34
  });
64
- const [isUserOpVersion06, entryPoint] = opsWithHashes.reduce((acc, owh) => {
65
- if (acc[0] !== isVersion06(owh.userOperation) ||
66
- acc[1] !== owh.entryPoint) {
67
- throw new Error("All user operations must be of the same version");
68
- }
69
- return acc;
70
- }, [
71
- isVersion06(opsWithHashes[0].userOperation),
72
- opsWithHashes[0].entryPoint
73
- ]);
74
- const ep = getContract({
75
- abi: isUserOpVersion06 ? EntryPointV06Abi : EntryPointV07Abi,
76
- address: entryPoint,
77
- client: {
78
- public: this.config.publicClient,
79
- wallet: this.config.walletClient
80
- }
81
- });
82
- let { simulatedOps, gasLimit } = await filterOpsAndEstimateGas(transactionInfo.entryPoint, ep, transactionInfo.executor, opsWithHashes, newRequest.nonce, newRequest.maxFeePerGas, newRequest.maxPriorityFeePerGas, this.config.blockTagSupport ? "latest" : undefined, this.config.legacyTransactions, this.config.fixedGasLimitForEstimation, this.reputationManager, this.logger);
83
- const childLogger = this.logger.child({
84
- transactionHash: transactionInfo.transactionHash,
85
- executor: transactionInfo.executor.address
86
- });
87
- if (simulatedOps.length === 0) {
88
- childLogger.warn("no ops to bundle");
89
- this.markWalletProcessed(transactionInfo.executor);
90
- return { status: "failed" };
91
- }
92
- if (simulatedOps.every((op) => op.reason === "AA25 invalid account nonce" ||
93
- op.reason === "AA10 sender already constructed")) {
94
- childLogger.trace({ reasons: simulatedOps.map((sop) => sop.reason) }, "all ops failed simulation with nonce error");
95
- return { status: "potentially_already_included" };
96
- }
97
- if (simulatedOps.every((op) => op.reason !== undefined)) {
98
- childLogger.warn("all ops failed simulation");
99
- this.markWalletProcessed(transactionInfo.executor);
100
- return { status: "failed" };
101
- }
102
- const opsToBundle = simulatedOps
103
- .filter((op) => op.reason === undefined)
104
- .map((op) => {
105
- const opInfo = transactionInfo.userOperationInfos.find((info) => info.userOperationHash === op.owh.userOperationHash);
106
- if (!opInfo) {
107
- throw new Error("opInfo not found");
108
- }
109
- return opInfo;
110
- });
111
- if (this.config.localGasLimitCalculation) {
112
- gasLimit = opsToBundle.reduce((acc, opInfo) => {
113
- const userOperation = opInfo.userOperation;
114
- return (acc +
115
- userOperation.preVerificationGas +
116
- 3n * userOperation.verificationGasLimit +
117
- userOperation.callGasLimit);
118
- }, 0n);
119
- }
120
- // https://github.com/eth-infinitism/account-abstraction/blob/fa61290d37d079e928d92d53a122efcc63822214/contracts/core/EntryPoint.sol#L236
121
- let innerHandleOpFloor = 0n;
122
- for (const owh of opsToBundle) {
123
- const op = owh.userOperation;
124
- innerHandleOpFloor +=
125
- op.callGasLimit + op.verificationGasLimit + 5000n;
126
- }
127
- if (gasLimit < innerHandleOpFloor) {
128
- gasLimit += innerHandleOpFloor;
129
- }
130
- // sometimes the estimation rounds down, adding a fixed constant accounts for this
131
- gasLimit += 10000n;
132
- // ensures that we don't submit again with too low of a gas value
133
- newRequest.gas = maxBigInt(newRequest.gas, gasLimit);
134
- // update calldata to include only ops that pass simulation
135
- let txParam;
136
- const userOps = opsToBundle.map((op) => isUserOpVersion06
137
- ? op.userOperation
138
- : toPackedUserOperation(op.userOperation));
139
- txParam = {
140
- isUserOpVersion06,
141
- isReplacementTx: true,
142
- ops: userOps,
143
- entryPoint: transactionInfo.entryPoint
144
- };
145
- try {
146
- childLogger.info({
147
- newRequest: {
148
- ...newRequest,
149
- abi: undefined,
150
- chain: undefined
151
- },
152
- executor: newRequest.account.address,
153
- opsToBundle: opsToBundle.map((opInfo) => opInfo.userOperationHash)
154
- }, "replacing transaction");
155
- const txHash = await this.sendHandleOpsTransaction({
156
- txParam,
157
- opts: this.config.legacyTransactions
158
- ? {
159
- account: newRequest.account,
160
- gasPrice: newRequest.maxFeePerGas,
161
- gas: newRequest.gas,
162
- nonce: newRequest.nonce
163
- }
164
- : {
165
- account: newRequest.account,
166
- maxFeePerGas: newRequest.maxFeePerGas,
167
- maxPriorityFeePerGas: newRequest.maxPriorityFeePerGas,
168
- gas: newRequest.gas,
169
- nonce: newRequest.nonce
170
- }
171
- });
172
- opsToBundle.map(({ entryPoint, userOperation }) => {
173
- const chainId = this.config.publicClient.chain?.id;
174
- const opHash = getUserOperationHash(userOperation, entryPoint, chainId);
175
- this.eventManager.emitSubmitted(opHash, txHash);
176
- });
177
- const newTxInfo = {
178
- ...transactionInfo,
179
- transactionRequest: newRequest,
180
- transactionHash: txHash,
181
- previousTransactionHashes: [
182
- transactionInfo.transactionHash,
183
- ...transactionInfo.previousTransactionHashes
184
- ],
185
- lastReplaced: Date.now(),
186
- userOperationInfos: opsToBundle.map((opInfo) => {
187
- return {
188
- entryPoint: opInfo.entryPoint,
189
- userOperation: opInfo.userOperation,
190
- userOperationHash: opInfo.userOperationHash,
191
- lastReplaced: Date.now(),
192
- firstSubmitted: opInfo.firstSubmitted
193
- };
194
- })
195
- };
196
- return {
197
- status: "replaced",
198
- transactionInfo: newTxInfo
199
- };
200
- }
201
- catch (err) {
202
- const e = parseViemError(err);
203
- if (!e) {
204
- sentry.captureException(err);
205
- childLogger.error({ error: err }, "unknown error replacing transaction");
206
- }
207
- if (e instanceof NonceTooLowError) {
208
- childLogger.trace({ error: e }, "nonce too low, potentially already included");
209
- return { status: "potentially_already_included" };
210
- }
211
- if (e instanceof FeeCapTooLowError) {
212
- childLogger.warn({ error: e }, "fee cap too low, not replacing");
213
- }
214
- if (e instanceof InsufficientFundsError) {
215
- childLogger.warn({ error: e }, "insufficient funds, not replacing");
216
- }
217
- if (e instanceof IntrinsicGasTooLowError) {
218
- childLogger.warn({ error: e }, "intrinsic gas too low, not replacing");
219
- }
220
- childLogger.warn({ error: e }, "error replacing transaction");
221
- this.markWalletProcessed(transactionInfo.executor);
222
- return { status: "failed" };
223
- }
224
- }
225
- async flushStuckTransactions() {
226
- const allWallets = new Set(this.senderManager.wallets);
227
- const utilityWallet = this.senderManager.utilityAccount;
228
- if (utilityWallet) {
229
- allWallets.add(utilityWallet);
230
- }
231
- const wallets = Array.from(allWallets);
232
- let gasPrice;
233
- try {
234
- gasPrice = await this.gasPriceManager.tryGetNetworkGasPrice();
235
- }
236
- catch (e) {
237
- this.logger.error({ error: e }, "error flushing stuck transaction");
238
- return;
239
- }
240
- const promises = wallets.map((wallet) => {
241
- try {
242
- flushStuckTransaction(this.config.publicClient, this.config.walletClient, wallet, gasPrice.maxFeePerGas * 5n, this.logger);
243
- }
244
- catch (e) {
245
- this.logger.error({ error: e }, "error flushing stuck transaction");
246
- }
247
- });
248
- await Promise.all(promises);
249
- }
250
- async sendHandleOpsTransaction({ txParam, opts }) {
251
- let data;
252
- let to;
253
- const { isUserOpVersion06, ops, entryPoint } = txParam;
254
- data = encodeFunctionData({
255
- abi: isUserOpVersion06 ? EntryPointV06Abi : EntryPointV07Abi,
256
- functionName: "handleOps",
257
- args: [ops, opts.account.address]
258
- });
259
- to = entryPoint;
260
35
  const request = await this.config.walletClient.prepareTransactionRequest({
261
- to,
262
- data,
263
- ...opts
36
+ to: entryPoint,
37
+ data: handleOpsCalldata,
38
+ ...txParam,
39
+ ...gasOpts
264
40
  });
265
41
  request.gas = scaleBigIntByPercent(request.gas, this.config.executorGasMultiplier);
266
- let isTransactionUnderPriced = false;
267
42
  let attempts = 0;
268
43
  let transactionHash;
269
44
  const maxAttempts = 3;
@@ -271,7 +46,7 @@ export class Executor {
271
46
  while (attempts < maxAttempts) {
272
47
  try {
273
48
  if (this.config.enableFastlane &&
274
- isUserOpVersion06 &&
49
+ isUserOpV06 &&
275
50
  !txParam.isReplacementTx &&
276
51
  attempts === 0) {
277
52
  const serializedTransaction = await this.config.walletClient.signTransaction(request);
@@ -288,13 +63,11 @@ export class Executor {
288
63
  break;
289
64
  }
290
65
  catch (e) {
291
- isTransactionUnderPriced = false;
292
66
  if (e instanceof BaseError) {
293
67
  if (isTransactionUnderpricedError(e)) {
294
68
  this.logger.warn("Transaction underpriced, retrying");
295
69
  request.maxFeePerGas = scaleBigIntByPercent(request.maxFeePerGas, 150n);
296
70
  request.maxPriorityFeePerGas = scaleBigIntByPercent(request.maxPriorityFeePerGas, 150n);
297
- isTransactionUnderPriced = true;
298
71
  }
299
72
  }
300
73
  const error = e;
@@ -320,286 +93,153 @@ export class Executor {
320
93
  await new Promise((resolve) => setTimeout(resolve, 500));
321
94
  }
322
95
  }
96
+ attempts++;
323
97
  if (attempts === maxAttempts) {
324
98
  throw error;
325
99
  }
326
- attempts++;
327
100
  }
328
101
  }
329
- if (isTransactionUnderPriced) {
330
- await this.handleTransactionUnderPriced({
331
- nonce: request.nonce,
332
- executor: request.account
333
- });
334
- }
335
102
  // needed for TS
336
103
  if (!transactionHash) {
337
104
  throw new Error("Transaction hash not assigned");
338
105
  }
339
106
  return transactionHash;
340
107
  }
341
- // Occurs when tx was sent with conflicting nonce, we want to resubmit all conflicting ops
342
- async handleTransactionUnderPriced({ nonce, executor }) {
343
- const submitted = this.mempool.dumpSubmittedOps();
344
- const conflictingOps = submitted
345
- .filter((submitted) => {
346
- const tx = submitted.transactionInfo;
347
- return (tx.executor.address === executor.address &&
348
- tx.transactionRequest.nonce === nonce);
349
- })
350
- .map(({ userOperation }) => userOperation);
351
- conflictingOps.map((op) => {
352
- this.logger.info(`Resubmitting ${op.userOperationHash} due to transaction underpriced`);
353
- this.mempool.removeSubmitted(op.userOperationHash);
354
- this.mempool.add(op.userOperation, op.entryPoint);
355
- });
356
- if (conflictingOps.length > 0) {
357
- this.markWalletProcessed(executor);
358
- }
359
- }
360
- async bundle(entryPoint, ops) {
361
- const wallet = await this.senderManager.getWallet();
362
- const opsWithHashes = ops.map((userOperation) => {
363
- return {
364
- userOperation,
365
- userOperationHash: getUserOperationHash(userOperation, entryPoint, this.config.walletClient.chain.id)
366
- };
367
- });
368
- const isUserOpVersion06 = opsWithHashes.reduce((acc, op) => {
369
- if (acc !== isVersion06(op.userOperation)) {
370
- throw new Error("All user operations must be of the same version");
371
- }
372
- return acc;
373
- }, isVersion06(opsWithHashes[0].userOperation));
374
- const ep = getContract({
375
- abi: isUserOpVersion06 ? EntryPointV06Abi : EntryPointV07Abi,
376
- address: entryPoint,
377
- client: {
378
- public: this.config.publicClient,
379
- wallet: this.config.walletClient
380
- }
381
- });
108
+ async bundle({ executor, userOpBundle, nonce, gasPriceParams, gasLimitSuggestion, isReplacementTx }) {
109
+ const { entryPoint, userOps, version } = userOpBundle;
110
+ const { maxFeePerGas, maxPriorityFeePerGas } = gasPriceParams;
111
+ const isUserOpV06 = version === "0.6";
382
112
  let childLogger = this.logger.child({
383
- userOperations: opsWithHashes.map((oh) => oh.userOperationHash),
113
+ isReplacementTx,
114
+ userOperations: getUserOpHashes(userOps),
384
115
  entryPoint
385
116
  });
386
- childLogger.debug("bundling user operation");
387
- // These calls can throw, so we try/catch them to mark wallet as processed in event of error.
388
- let nonce;
389
- let gasPriceParameters;
390
- try {
391
- ;
392
- [gasPriceParameters, nonce] = await Promise.all([
393
- this.gasPriceManager.tryGetNetworkGasPrice(),
394
- this.config.publicClient.getTransactionCount({
395
- address: wallet.address,
396
- blockTag: "pending"
397
- })
398
- ]);
399
- }
400
- catch (err) {
401
- childLogger.error({ error: err }, "Failed to get parameters for bundling");
402
- this.markWalletProcessed(wallet);
403
- return opsWithHashes.map((owh) => {
404
- return {
405
- status: "resubmit",
406
- info: {
407
- entryPoint,
408
- userOpHash: owh.userOperationHash,
409
- userOperation: owh.userOperation,
410
- reason: "Failed to get parameters for bundling"
411
- }
412
- };
413
- });
414
- }
415
- let { gasLimit, simulatedOps } = await filterOpsAndEstimateGas(entryPoint, ep, wallet, opsWithHashes, nonce, gasPriceParameters.maxFeePerGas, gasPriceParameters.maxPriorityFeePerGas, this.config.blockTagSupport ? "pending" : undefined, this.config.legacyTransactions, this.config.fixedGasLimitForEstimation, this.reputationManager, childLogger, getAuthorizationList(opsWithHashes.map(({ userOperation }) => userOperation)));
416
- if (simulatedOps.length === 0) {
117
+ let estimateResult = await filterOpsAndEstimateGas({
118
+ userOpBundle,
119
+ executor,
120
+ nonce,
121
+ maxFeePerGas,
122
+ maxPriorityFeePerGas,
123
+ reputationManager: this.reputationManager,
124
+ config: this.config,
125
+ logger: childLogger
126
+ });
127
+ if (estimateResult.status === "unhandled_failure") {
417
128
  childLogger.error("gas limit simulation encountered unexpected failure");
418
- this.markWalletProcessed(wallet);
419
- return opsWithHashes.map((owh) => {
420
- return {
421
- status: "failure",
422
- error: {
423
- entryPoint,
424
- userOpHash: owh.userOperationHash,
425
- userOperation: owh.userOperation,
426
- reason: "INTERNAL FAILURE"
427
- }
428
- };
429
- });
129
+ return {
130
+ status: "unhandled_simulation_failure",
131
+ rejectedUserOps: estimateResult.rejectedUserOps,
132
+ reason: "INTERNAL FAILURE"
133
+ };
430
134
  }
431
- if (simulatedOps.every((op) => op.reason !== undefined)) {
135
+ if (estimateResult.status === "all_ops_failed_simulation") {
432
136
  childLogger.warn("all ops failed simulation");
433
- this.markWalletProcessed(wallet);
434
- return simulatedOps.map(({ reason, owh }) => {
435
- return {
436
- status: "failure",
437
- error: {
438
- entryPoint,
439
- userOpHash: owh.userOperationHash,
440
- userOperation: owh.userOperation,
441
- reason: reason
442
- }
443
- };
444
- });
137
+ return {
138
+ status: "all_ops_failed_simulation",
139
+ rejectedUserOps: estimateResult.rejectedUserOps
140
+ };
445
141
  }
446
- const opsWithHashToBundle = simulatedOps
447
- .filter((op) => op.reason === undefined)
448
- .map((op) => op.owh);
142
+ let { gasLimit, userOpsToBundle, rejectedUserOps } = estimateResult;
143
+ // Update child logger with userOperations being sent for bundling.
449
144
  childLogger = this.logger.child({
450
- userOperations: opsWithHashToBundle.map((owh) => owh.userOperationHash),
145
+ isReplacementTx,
146
+ userOperations: getUserOpHashes(userOpsToBundle),
451
147
  entryPoint
452
148
  });
453
- // https://github.com/eth-infinitism/account-abstraction/blob/fa61290d37d079e928d92d53a122efcc63822214/contracts/core/EntryPoint.sol#L236
454
- let innerHandleOpFloor = 0n;
455
- let totalBeneficiaryFees = 0n;
456
- for (const owh of opsWithHashToBundle) {
457
- const op = owh.userOperation;
458
- innerHandleOpFloor +=
459
- op.callGasLimit + op.verificationGasLimit + 5000n;
460
- totalBeneficiaryFees += getRequiredPrefund(op);
461
- }
462
- if (gasLimit < innerHandleOpFloor) {
463
- gasLimit += innerHandleOpFloor;
149
+ // Ensure that we don't submit with gas too low leading to AA95.
150
+ const aa95GasFloor = calculateAA95GasFloor(userOpsToBundle);
151
+ if (gasLimit < aa95GasFloor) {
152
+ gasLimit += aa95GasFloor;
464
153
  }
465
154
  // sometimes the estimation rounds down, adding a fixed constant accounts for this
466
155
  gasLimit += 10000n;
467
- childLogger.debug({ gasLimit }, "got gas limit");
156
+ gasLimit = gasLimitSuggestion
157
+ ? maxBigInt(gasLimit, gasLimitSuggestion)
158
+ : gasLimit;
468
159
  let transactionHash;
469
160
  try {
470
161
  const isLegacyTransaction = this.config.legacyTransactions;
471
- if (this.config.noProfitBundling) {
472
- const gasPrice = totalBeneficiaryFees / gasLimit;
473
- if (isLegacyTransaction) {
474
- gasPriceParameters.maxFeePerGas = gasPrice;
475
- gasPriceParameters.maxPriorityFeePerGas = gasPrice;
476
- }
477
- else {
478
- gasPriceParameters.maxFeePerGas = maxBigInt(gasPrice, gasPriceParameters.maxFeePerGas || 0n);
479
- }
480
- }
481
- const authorizationList = getAuthorizationList(opsWithHashToBundle.map(({ userOperation }) => userOperation));
482
- let opts;
162
+ const authorizationList = getAuthorizationList(userOpsToBundle);
163
+ const { maxFeePerGas, maxPriorityFeePerGas } = gasPriceParams;
164
+ let gasOpts;
483
165
  if (isLegacyTransaction) {
484
- opts = {
166
+ gasOpts = {
485
167
  type: "legacy",
486
- gasPrice: gasPriceParameters.maxFeePerGas,
487
- account: wallet,
488
- gas: gasLimit,
489
- nonce
168
+ gasPrice: maxFeePerGas
490
169
  };
491
170
  }
492
171
  else if (authorizationList) {
493
- opts = {
172
+ gasOpts = {
494
173
  type: "eip7702",
495
- maxFeePerGas: gasPriceParameters.maxFeePerGas,
496
- maxPriorityFeePerGas: gasPriceParameters.maxPriorityFeePerGas,
497
- account: wallet,
498
- gas: gasLimit,
499
- nonce,
174
+ maxFeePerGas,
175
+ maxPriorityFeePerGas,
500
176
  authorizationList
501
177
  };
502
178
  }
503
179
  else {
504
- opts = {
180
+ gasOpts = {
505
181
  type: "eip1559",
506
- maxFeePerGas: gasPriceParameters.maxFeePerGas,
507
- maxPriorityFeePerGas: gasPriceParameters.maxPriorityFeePerGas,
508
- account: wallet,
509
- gas: gasLimit,
510
- nonce
182
+ maxFeePerGas,
183
+ maxPriorityFeePerGas
511
184
  };
512
185
  }
513
- const userOps = opsWithHashToBundle.map(({ userOperation }) => {
514
- if (isUserOpVersion06) {
515
- return userOperation;
516
- }
517
- return toPackedUserOperation(userOperation);
518
- });
519
186
  transactionHash = await this.sendHandleOpsTransaction({
520
187
  txParam: {
521
- ops: userOps,
522
- isReplacementTx: false,
523
- isUserOpVersion06,
188
+ account: executor,
189
+ nonce,
190
+ gas: gasLimit,
191
+ userOps: userOpsToBundle,
192
+ isReplacementTx,
193
+ isUserOpV06,
524
194
  entryPoint
525
195
  },
526
- opts
196
+ gasOpts
527
197
  });
528
- opsWithHashToBundle.map(({ userOperationHash }) => {
529
- this.eventManager.emitSubmitted(userOperationHash, transactionHash);
198
+ this.eventManager.emitSubmitted({
199
+ userOpHashes: getUserOpHashes(userOpsToBundle),
200
+ transactionHash
530
201
  });
531
202
  }
532
203
  catch (err) {
533
204
  const e = parseViemError(err);
534
- if (e instanceof InsufficientFundsError) {
535
- childLogger.error({ error: e }, "insufficient funds, not submitting transaction");
536
- this.markWalletProcessed(wallet);
537
- return opsWithHashToBundle.map((owh) => {
538
- return {
539
- status: "resubmit",
540
- info: {
541
- entryPoint,
542
- userOpHash: owh.userOperationHash,
543
- userOperation: owh.userOperation,
544
- reason: InsufficientFundsError.name
545
- }
546
- };
547
- });
548
- }
549
- sentry.captureException(err);
550
- childLogger.error({ error: JSON.stringify(err) }, "error submitting bundle transaction");
551
- this.markWalletProcessed(wallet);
552
- return opsWithHashes.map((owh) => {
205
+ const { rejectedUserOps, userOpsToBundle } = estimateResult;
206
+ // if unknown error, return INTERNAL FAILURE
207
+ if (!e) {
208
+ sentry.captureException(err);
209
+ childLogger.error({ error: JSON.stringify(err) }, "error submitting bundle transaction");
553
210
  return {
554
- status: "failure",
555
- error: {
556
- entryPoint,
557
- userOpHash: owh.userOperationHash,
558
- userOperation: owh.userOperation,
559
- reason: "INTERNAL FAILURE"
560
- }
211
+ rejectedUserOps,
212
+ userOpsToBundle,
213
+ status: "bundle_submission_failure",
214
+ reason: "INTERNAL FAILURE"
561
215
  };
562
- });
563
- }
564
- const userOperationInfos = opsWithHashToBundle.map((op) => {
216
+ }
565
217
  return {
566
- entryPoint,
567
- userOperation: op.userOperation,
568
- userOperationHash: op.userOperationHash,
569
- lastReplaced: Date.now(),
570
- firstSubmitted: Date.now()
218
+ rejectedUserOps,
219
+ userOpsToBundle,
220
+ status: "bundle_submission_failure",
221
+ reason: e
571
222
  };
572
- });
573
- const transactionInfo = {
574
- entryPoint,
575
- isVersion06: isUserOpVersion06,
576
- transactionHash: transactionHash,
577
- previousTransactionHashes: [],
223
+ }
224
+ const userOpsBundled = userOpsToBundle;
225
+ const bundleResult = {
226
+ status: "bundle_success",
227
+ userOpsBundled,
228
+ rejectedUserOps,
229
+ transactionHash,
578
230
  transactionRequest: {
579
- account: wallet,
580
- to: ep.address,
581
231
  gas: gasLimit,
582
- chain: this.config.walletClient.chain,
583
- maxFeePerGas: gasPriceParameters.maxFeePerGas,
584
- maxPriorityFeePerGas: gasPriceParameters.maxPriorityFeePerGas,
585
- nonce: nonce
586
- },
587
- executor: wallet,
588
- userOperationInfos,
589
- lastReplaced: Date.now(),
590
- firstSubmitted: Date.now(),
591
- timesPotentiallyIncluded: 0
232
+ maxFeePerGas: gasPriceParams.maxFeePerGas,
233
+ maxPriorityFeePerGas: gasPriceParams.maxPriorityFeePerGas,
234
+ nonce
235
+ }
592
236
  };
593
- const userOperationResults = simulatedOpsToResults(simulatedOps, transactionInfo);
594
237
  childLogger.info({
595
- transactionRequest: {
596
- ...transactionInfo.transactionRequest,
597
- abi: undefined
598
- },
238
+ transactionRequest: bundleResult.transactionRequest,
599
239
  txHash: transactionHash,
600
- opHashes: opsWithHashToBundle.map((owh) => owh.userOperationHash)
240
+ opHashes: getUserOpHashes(userOpsBundled)
601
241
  }, "submitted bundle transaction");
602
- return userOperationResults;
242
+ return bundleResult;
603
243
  }
604
244
  }
605
245
  //# sourceMappingURL=executor.js.map