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

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