@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
@@ -1,8 +1,5 @@
1
- // import { MongoClient, Collection, Filter } from "mongodb"
2
- // import { PublicClient, getContract } from "viem"
3
- // import { EntryPointAbi } from "../types/EntryPoint"
4
1
  import { EntryPointV06Abi, EntryPointV07Abi, RpcError, ValidationErrors } from "../types/index.js";
5
- import { getAddressFromInitCodeOrPaymasterAndData, getNonceKeyAndValue, getUserOperationHash, isVersion06, isVersion07 } from "../utils/index.js";
2
+ import { getAddressFromInitCodeOrPaymasterAndData, getNonceKeyAndSequence, getUserOperationHash, isVersion06, isVersion07, scaleBigIntByPercent } from "../utils/index.js";
6
3
  import { getAddress, getContract } from "viem";
7
4
  import { ReputationStatuses } from "./reputationManager.js";
8
5
  import { MemoryStore } from "./store.js";
@@ -27,31 +24,31 @@ export class MemoryMempool {
27
24
  this.throttledEntityBundleCount = 4; // we don't have any config for this as of now
28
25
  this.eventManager = eventManager;
29
26
  }
30
- replaceSubmitted(userOperation, transactionInfo) {
31
- const op = this.store
27
+ replaceSubmitted(userOpInfo, transactionInfo) {
28
+ const { userOpHash } = userOpInfo;
29
+ const existingUserOpToReplace = this.store
32
30
  .dumpSubmitted()
33
- .find((op) => op.userOperation.userOperationHash ===
34
- userOperation.userOperationHash);
35
- if (op) {
36
- this.store.removeSubmitted(userOperation.userOperationHash);
31
+ .find((userOpInfo) => userOpInfo.userOpHash === userOpHash);
32
+ if (existingUserOpToReplace) {
33
+ this.store.removeSubmitted(userOpHash);
37
34
  this.store.addSubmitted({
38
- userOperation,
35
+ ...userOpInfo,
39
36
  transactionInfo
40
37
  });
41
- this.monitor.setUserOperationStatus(userOperation.userOperationHash, {
38
+ this.monitor.setUserOperationStatus(userOpHash, {
42
39
  status: "submitted",
43
40
  transactionHash: transactionInfo.transactionHash
44
41
  });
45
42
  }
46
43
  }
47
44
  markSubmitted(userOpHash, transactionInfo) {
48
- const op = this.store
45
+ const processingUserOp = this.store
49
46
  .dumpProcessing()
50
- .find((op) => op.userOperationHash === userOpHash);
51
- if (op) {
47
+ .find((userOpInfo) => userOpInfo.userOpHash === userOpHash);
48
+ if (processingUserOp) {
52
49
  this.store.removeProcessing(userOpHash);
53
50
  this.store.addSubmitted({
54
- userOperation: op,
51
+ ...processingUserOp,
55
52
  transactionInfo
56
53
  });
57
54
  this.monitor.setUserOperationStatus(userOpHash, {
@@ -61,7 +58,7 @@ export class MemoryMempool {
61
58
  }
62
59
  }
63
60
  dumpOutstanding() {
64
- return this.store.dumpOutstanding();
61
+ return this.store.dumpOutstanding().map(({ userOp }) => userOp);
65
62
  }
66
63
  dumpProcessing() {
67
64
  return this.store.dumpProcessing();
@@ -109,19 +106,19 @@ export class MemoryMempool {
109
106
  paymasters: new Set(),
110
107
  factories: new Set()
111
108
  };
112
- for (const mempoolOp of allOps) {
113
- const op = mempoolOp.userOperation;
114
- entities.sender.add(op.sender);
115
- const isUserOpV06 = isVersion06(op);
109
+ for (const userOpInfo of allOps) {
110
+ const { userOp } = userOpInfo;
111
+ entities.sender.add(userOp.sender);
112
+ const isUserOpV06 = isVersion06(userOp);
116
113
  const paymaster = isUserOpV06
117
- ? getAddressFromInitCodeOrPaymasterAndData(op.paymasterAndData)
118
- : op.paymaster;
114
+ ? getAddressFromInitCodeOrPaymasterAndData(userOp.paymasterAndData)
115
+ : userOp.paymaster;
119
116
  if (paymaster) {
120
117
  entities.paymasters.add(paymaster);
121
118
  }
122
119
  const factory = isUserOpV06
123
- ? getAddressFromInitCodeOrPaymasterAndData(op.initCode)
124
- : op.factory;
120
+ ? getAddressFromInitCodeOrPaymasterAndData(userOp.initCode)
121
+ : userOp.factory;
125
122
  if (factory) {
126
123
  entities.factories.add(factory);
127
124
  }
@@ -130,66 +127,66 @@ export class MemoryMempool {
130
127
  }
131
128
  // TODO: add check for adding a userop with conflicting nonce
132
129
  // In case of concurrent requests
133
- add(userOperation, entryPoint, referencedContracts) {
134
- const opHash = getUserOperationHash(userOperation, entryPoint, this.config.publicClient.chain.id);
130
+ add(userOp, entryPoint, referencedContracts) {
131
+ const userOpHash = getUserOperationHash(userOp, entryPoint, this.config.publicClient.chain.id);
135
132
  const outstandingOps = [...this.store.dumpOutstanding()];
136
133
  const processedOrSubmittedOps = [
137
134
  ...this.store.dumpProcessing(),
138
- ...this.store
139
- .dumpSubmitted()
140
- .map(({ userOperation }) => userOperation)
135
+ ...this.store.dumpSubmitted()
141
136
  ];
142
137
  // Check if the exact same userOperation is already in the mempool.
143
138
  const existingUserOperation = [
144
139
  ...outstandingOps,
145
140
  ...processedOrSubmittedOps
146
- ].find(({ userOperationHash }) => userOperationHash === opHash);
141
+ ].find((userOpInfo) => userOpInfo.userOpHash === userOpHash);
147
142
  if (existingUserOperation) {
148
143
  return [false, "Already known"];
149
144
  }
150
- if (processedOrSubmittedOps.find((opInfo) => {
151
- const mempoolUserOp = opInfo.userOperation;
152
- return (mempoolUserOp.sender === userOperation.sender &&
153
- mempoolUserOp.nonce === userOperation.nonce);
145
+ if (processedOrSubmittedOps.find((userOpInfo) => {
146
+ const { userOp: mempoolUserOp } = userOpInfo;
147
+ return (mempoolUserOp.sender === userOp.sender &&
148
+ mempoolUserOp.nonce === userOp.nonce);
154
149
  })) {
155
150
  return [
156
151
  false,
157
152
  "AA25 invalid account nonce: User operation is already in mempool and getting processed with same nonce and sender"
158
153
  ];
159
154
  }
160
- this.reputationManager.updateUserOperationSeenStatus(userOperation, entryPoint);
161
- const oldUserOp = [...outstandingOps, ...processedOrSubmittedOps].find((opInfo) => {
162
- const mempoolUserOp = opInfo.userOperation;
163
- const isSameSender = mempoolUserOp.sender === userOperation.sender;
164
- if (isSameSender &&
165
- mempoolUserOp.nonce === userOperation.nonce) {
155
+ this.reputationManager.updateUserOperationSeenStatus(userOp, entryPoint);
156
+ const oldUserOpInfo = [
157
+ ...outstandingOps,
158
+ ...processedOrSubmittedOps
159
+ ].find((userOpInfo) => {
160
+ const { userOp: mempoolUserOp } = userOpInfo;
161
+ const isSameSender = mempoolUserOp.sender === userOp.sender;
162
+ if (isSameSender && mempoolUserOp.nonce === userOp.nonce) {
166
163
  return true;
167
164
  }
168
165
  // Check if there is already a userOperation with initCode + same sender (stops rejected ops due to AA10).
169
166
  if (isVersion06(mempoolUserOp) &&
170
- isVersion06(userOperation) &&
171
- userOperation.initCode &&
172
- userOperation.initCode !== "0x") {
167
+ isVersion06(userOp) &&
168
+ userOp.initCode &&
169
+ userOp.initCode !== "0x") {
173
170
  return (isSameSender &&
174
171
  mempoolUserOp.initCode &&
175
172
  mempoolUserOp.initCode !== "0x");
176
173
  }
177
174
  // Check if there is already a userOperation with factory + same sender (stops rejected ops due to AA10).
178
175
  if (isVersion07(mempoolUserOp) &&
179
- isVersion07(userOperation) &&
180
- userOperation.factory &&
181
- userOperation.factory !== "0x") {
176
+ isVersion07(userOp) &&
177
+ userOp.factory &&
178
+ userOp.factory !== "0x") {
182
179
  return (isSameSender &&
183
180
  mempoolUserOp.factory &&
184
181
  mempoolUserOp.factory !== "0x");
185
182
  }
186
183
  return false;
187
184
  });
188
- const isOldUserOpProcessingOrSubmitted = processedOrSubmittedOps.some((submittedOp) => submittedOp.userOperationHash === oldUserOp?.userOperationHash);
189
- if (oldUserOp) {
190
- const oldOp = oldUserOp.userOperation;
185
+ const isOldUserOpProcessingOrSubmitted = processedOrSubmittedOps.some((userOpInfo) => userOpInfo.userOpHash === oldUserOpInfo?.userOpHash);
186
+ if (oldUserOpInfo) {
187
+ const { userOp: oldUserOp } = oldUserOpInfo;
191
188
  let reason = "AA10 sender already constructed: A conflicting userOperation with initCode for this sender is already in the mempool. bump the gas price by minimum 10%";
192
- if (oldOp.nonce === userOperation.nonce) {
189
+ if (oldUserOp.nonce === userOp.nonce) {
193
190
  reason =
194
191
  "AA25 invalid account nonce: User operation already present in mempool, bump the gas price by minimum 10%";
195
192
  }
@@ -197,25 +194,24 @@ export class MemoryMempool {
197
194
  if (isOldUserOpProcessingOrSubmitted) {
198
195
  return [false, reason];
199
196
  }
200
- const oldMaxPriorityFeePerGas = oldOp.maxPriorityFeePerGas;
201
- const newMaxPriorityFeePerGas = userOperation.maxPriorityFeePerGas;
202
- const oldMaxFeePerGas = oldOp.maxFeePerGas;
203
- const newMaxFeePerGas = userOperation.maxFeePerGas;
204
- const incrementMaxPriorityFeePerGas = (oldMaxPriorityFeePerGas * BigInt(10)) / BigInt(100);
205
- const incrementMaxFeePerGas = (oldMaxFeePerGas * BigInt(10)) / BigInt(100);
206
- if (newMaxPriorityFeePerGas <
207
- oldMaxPriorityFeePerGas + incrementMaxPriorityFeePerGas ||
208
- newMaxFeePerGas < oldMaxFeePerGas + incrementMaxFeePerGas) {
197
+ const oldOp = oldUserOp;
198
+ const newOp = userOp;
199
+ const hasHigherPriorityFee = newOp.maxPriorityFeePerGas >=
200
+ scaleBigIntByPercent(oldOp.maxPriorityFeePerGas, 110n);
201
+ const hasHigherMaxFee = newOp.maxFeePerGas >=
202
+ scaleBigIntByPercent(oldOp.maxFeePerGas, 110n);
203
+ const hasHigherFees = hasHigherPriorityFee || hasHigherMaxFee;
204
+ if (!hasHigherFees) {
209
205
  return [false, reason];
210
206
  }
211
- this.store.removeOutstanding(oldUserOp.userOperationHash);
207
+ this.store.removeOutstanding(oldUserOpInfo.userOpHash);
212
208
  }
213
209
  // Check if mempool already includes max amount of parallel user operations
214
210
  const parallelUserOperationsCount = this.store
215
211
  .dumpOutstanding()
216
212
  .filter((userOpInfo) => {
217
- const userOp = userOpInfo.userOperation;
218
- return userOp.sender === userOperation.sender;
213
+ const { userOp: mempoolUserOp } = userOpInfo;
214
+ return mempoolUserOp.sender === userOp.sender;
219
215
  }).length;
220
216
  if (parallelUserOperationsCount > this.config.mempoolMaxParallelOps) {
221
217
  return [
@@ -224,13 +220,13 @@ export class MemoryMempool {
224
220
  ];
225
221
  }
226
222
  // Check if mempool already includes max amount of queued user operations
227
- const [nonceKey] = getNonceKeyAndValue(userOperation.nonce);
223
+ const [nonceKey] = getNonceKeyAndSequence(userOp.nonce);
228
224
  const queuedUserOperationsCount = this.store
229
225
  .dumpOutstanding()
230
226
  .filter((userOpInfo) => {
231
- const userOp = userOpInfo.userOperation;
232
- const [opNonceKey] = getNonceKeyAndValue(userOp.nonce);
233
- return (userOp.sender === userOperation.sender &&
227
+ const { userOp: mempoolUserOp } = userOpInfo;
228
+ const [opNonceKey] = getNonceKeyAndSequence(mempoolUserOp.nonce);
229
+ return (mempoolUserOp.sender === userOp.sender &&
234
230
  opNonceKey === nonceKey);
235
231
  }).length;
236
232
  if (queuedUserOperationsCount > this.config.mempoolMaxQueuedOps) {
@@ -240,23 +236,21 @@ export class MemoryMempool {
240
236
  ];
241
237
  }
242
238
  this.store.addOutstanding({
243
- userOperation,
239
+ userOp,
244
240
  entryPoint,
245
- userOperationHash: opHash,
246
- firstSubmitted: oldUserOp ? oldUserOp.firstSubmitted : Date.now(),
247
- lastReplaced: Date.now(),
248
- referencedContracts
241
+ userOpHash: userOpHash,
242
+ referencedContracts,
243
+ addedToMempool: Date.now()
249
244
  });
250
- this.monitor.setUserOperationStatus(opHash, {
245
+ this.monitor.setUserOperationStatus(userOpHash, {
251
246
  status: "not_submitted",
252
247
  transactionHash: null
253
248
  });
254
- this.eventManager.emitAddedToMempool(opHash);
249
+ this.eventManager.emitAddedToMempool(userOpHash);
255
250
  return [true, ""];
256
251
  }
257
252
  // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: <explanation>
258
- async shouldSkip(opInfo, paymasterDeposit, stakedEntityCount, knownEntities, senders, storageMap) {
259
- const op = opInfo.userOperation;
253
+ async shouldSkip(userOpInfo, paymasterDeposit, stakedEntityCount, knownEntities, senders, storageMap) {
260
254
  if (!this.config.safeMode) {
261
255
  return {
262
256
  skip: false,
@@ -267,18 +261,19 @@ export class MemoryMempool {
267
261
  storageMap
268
262
  };
269
263
  }
270
- const isUserOpV06 = isVersion06(op);
264
+ const { userOp, entryPoint, userOpHash, referencedContracts } = userOpInfo;
265
+ const isUserOpV06 = isVersion06(userOp);
271
266
  const paymaster = isUserOpV06
272
- ? getAddressFromInitCodeOrPaymasterAndData(op.paymasterAndData)
273
- : op.paymaster;
267
+ ? getAddressFromInitCodeOrPaymasterAndData(userOp.paymasterAndData)
268
+ : userOp.paymaster;
274
269
  const factory = isUserOpV06
275
- ? getAddressFromInitCodeOrPaymasterAndData(op.initCode)
276
- : op.factory;
277
- const paymasterStatus = this.reputationManager.getStatus(opInfo.entryPoint, paymaster);
278
- const factoryStatus = this.reputationManager.getStatus(opInfo.entryPoint, factory);
270
+ ? getAddressFromInitCodeOrPaymasterAndData(userOp.initCode)
271
+ : userOp.factory;
272
+ const paymasterStatus = this.reputationManager.getStatus(entryPoint, paymaster);
273
+ const factoryStatus = this.reputationManager.getStatus(entryPoint, factory);
279
274
  if (paymasterStatus === ReputationStatuses.banned ||
280
275
  factoryStatus === ReputationStatuses.banned) {
281
- this.store.removeOutstanding(opInfo.userOperationHash);
276
+ this.store.removeOutstanding(userOpHash);
282
277
  return {
283
278
  skip: true,
284
279
  paymasterDeposit,
@@ -293,7 +288,7 @@ export class MemoryMempool {
293
288
  stakedEntityCount[paymaster] >= this.throttledEntityBundleCount) {
294
289
  this.logger.trace({
295
290
  paymaster,
296
- opHash: opInfo.userOperationHash
291
+ userOpHash
297
292
  }, "Throttled paymaster skipped");
298
293
  return {
299
294
  skip: true,
@@ -309,7 +304,7 @@ export class MemoryMempool {
309
304
  stakedEntityCount[factory] >= this.throttledEntityBundleCount) {
310
305
  this.logger.trace({
311
306
  factory,
312
- opHash: opInfo.userOperationHash
307
+ userOpHash
313
308
  }, "Throttled factory skipped");
314
309
  return {
315
310
  skip: true,
@@ -320,11 +315,11 @@ export class MemoryMempool {
320
315
  storageMap
321
316
  };
322
317
  }
323
- if (senders.has(op.sender) &&
318
+ if (senders.has(userOp.sender) &&
324
319
  this.config.enforceUniqueSendersPerBundle) {
325
320
  this.logger.trace({
326
- sender: op.sender,
327
- opHash: opInfo.userOperationHash
321
+ sender: userOp.sender,
322
+ userOpHash
328
323
  }, "Sender skipped because already included in bundle");
329
324
  return {
330
325
  skip: true,
@@ -339,22 +334,22 @@ export class MemoryMempool {
339
334
  try {
340
335
  let queuedUserOperations = [];
341
336
  if (!isUserOpV06) {
342
- queuedUserOperations = await this.getQueuedUserOperations(op, opInfo.entryPoint);
337
+ queuedUserOperations = await this.getQueuedUserOperations(userOp, entryPoint);
343
338
  }
344
339
  validationResult = await this.validator.validateUserOperation({
345
340
  shouldCheckPrefund: false,
346
- userOperation: op,
341
+ userOperation: userOp,
347
342
  queuedUserOperations,
348
- entryPoint: opInfo.entryPoint,
349
- referencedContracts: opInfo.referencedContracts
343
+ entryPoint,
344
+ referencedContracts
350
345
  });
351
346
  }
352
347
  catch (e) {
353
348
  this.logger.error({
354
- opHash: opInfo.userOperationHash,
349
+ userOpHash,
355
350
  error: JSON.stringify(e)
356
351
  }, "2nd Validation error");
357
- this.store.removeOutstanding(opInfo.userOperationHash);
352
+ this.store.removeOutstanding(userOpHash);
358
353
  return {
359
354
  skip: true,
360
355
  paymasterDeposit,
@@ -366,10 +361,11 @@ export class MemoryMempool {
366
361
  }
367
362
  for (const storageAddress of Object.keys(validationResult.storageMap)) {
368
363
  const address = getAddress(storageAddress);
369
- if (address !== op.sender && knownEntities.sender.has(address)) {
364
+ if (address !== userOp.sender &&
365
+ knownEntities.sender.has(address)) {
370
366
  this.logger.trace({
371
367
  storageAddress,
372
- opHash: opInfo.userOperationHash
368
+ userOpHash
373
369
  }, "Storage address skipped");
374
370
  return {
375
371
  skip: true,
@@ -385,7 +381,7 @@ export class MemoryMempool {
385
381
  if (paymasterDeposit[paymaster] === undefined) {
386
382
  const entryPointContract = getContract({
387
383
  abi: isUserOpV06 ? EntryPointV06Abi : EntryPointV07Abi,
388
- address: opInfo.entryPoint,
384
+ address: entryPoint,
389
385
  client: {
390
386
  public: this.config.publicClient
391
387
  }
@@ -397,7 +393,7 @@ export class MemoryMempool {
397
393
  validationResult.returnInfo.prefund) {
398
394
  this.logger.trace({
399
395
  paymaster,
400
- opHash: opInfo.userOperationHash
396
+ userOpHash
401
397
  }, "Paymaster skipped because of insufficient balance left to sponsor all user ops in the bundle");
402
398
  return {
403
399
  skip: true,
@@ -415,7 +411,7 @@ export class MemoryMempool {
415
411
  if (factory) {
416
412
  stakedEntityCount[factory] = (stakedEntityCount[factory] ?? 0) + 1;
417
413
  }
418
- senders.add(op.sender);
414
+ senders.add(userOp.sender);
419
415
  return {
420
416
  skip: false,
421
417
  paymasterDeposit,
@@ -425,118 +421,153 @@ export class MemoryMempool {
425
421
  storageMap
426
422
  };
427
423
  }
428
- async process(maxGasLimit, minOps) {
429
- const outstandingUserOperations = this.store.dumpOutstanding().slice();
430
- // Sort userops before the execution
431
- // Decide the order of the userops based on the sender and nonce
432
- // If sender is the same, sort by nonce key
433
- outstandingUserOperations.sort((a, b) => {
434
- const aUserOp = a.userOperation;
435
- const bUserOp = b.userOperation;
424
+ async getBundles(maxBundleCount) {
425
+ const bundlePromises = this.config.entrypoints.map(async (entryPoint) => {
426
+ return await this.process({
427
+ entryPoint,
428
+ maxGasLimit: this.config.maxGasPerBundle,
429
+ minOpsPerBundle: 1,
430
+ maxBundleCount
431
+ });
432
+ });
433
+ const bundlesNested = await Promise.all(bundlePromises);
434
+ const bundles = bundlesNested.flat();
435
+ return bundles;
436
+ }
437
+ // Returns a bundle of userOperations in array format.
438
+ async process({ maxGasLimit, entryPoint, minOpsPerBundle, maxBundleCount }) {
439
+ let outstandingUserOps = this.store
440
+ .dumpOutstanding()
441
+ .filter((op) => op.entryPoint === entryPoint)
442
+ .sort((aUserOpInfo, bUserOpInfo) => {
443
+ // Sort userops before the execution
444
+ // Decide the order of the userops based on the sender and nonce
445
+ // If sender is the same, sort by nonce key
446
+ const aUserOp = aUserOpInfo.userOp;
447
+ const bUserOp = bUserOpInfo.userOp;
436
448
  if (aUserOp.sender === bUserOp.sender) {
437
- const [aNonceKey, aNonceValue] = getNonceKeyAndValue(aUserOp.nonce);
438
- const [bNonceKey, bNonceValue] = getNonceKeyAndValue(bUserOp.nonce);
449
+ const [aNonceKey, aNonceValue] = getNonceKeyAndSequence(aUserOp.nonce);
450
+ const [bNonceKey, bNonceValue] = getNonceKeyAndSequence(bUserOp.nonce);
439
451
  if (aNonceKey === bNonceKey) {
440
452
  return Number(aNonceValue - bNonceValue);
441
453
  }
442
454
  return Number(aNonceKey - bNonceKey);
443
455
  }
444
456
  return 0;
457
+ })
458
+ .slice();
459
+ if (outstandingUserOps.length === 0)
460
+ return [];
461
+ // Get EntryPoint version. (Ideally version should be derived from CLI flags)
462
+ const isV6 = isVersion06(outstandingUserOps[0].userOp);
463
+ const allSameVersion = outstandingUserOps.every((userOpInfo) => {
464
+ const { userOp } = userOpInfo;
465
+ return isVersion06(userOp) === isV6;
445
466
  });
446
- let opsTaken = 0;
447
- let gasUsed = 0n;
448
- const result = [];
449
- // paymaster deposit should be enough for all UserOps in the bundle.
450
- let paymasterDeposit = {};
451
- // throttled paymasters and factories are allowed only small UserOps per bundle.
452
- let stakedEntityCount = {};
453
- // each sender is allowed only once per bundle
454
- let senders = new Set();
455
- let knownEntities = this.getKnownEntities();
456
- let storageMap = {};
457
- for (const opInfo of outstandingUserOperations) {
458
- const op = opInfo.userOperation;
459
- gasUsed += op.callGasLimit + op.verificationGasLimit;
460
- if (isVersion07(op)) {
461
- gasUsed +=
462
- (op.paymasterPostOpGasLimit ?? 0n) +
463
- (op.paymasterVerificationGasLimit ?? 0n);
464
- }
465
- if (gasUsed > maxGasLimit && opsTaken >= (minOps || 0)) {
467
+ if (!allSameVersion) {
468
+ throw new Error("All user operations from same EntryPoint must be of the same version");
469
+ }
470
+ const bundles = [];
471
+ // Process all outstanding ops.
472
+ while (outstandingUserOps.length > 0) {
473
+ // If maxBundles is set and we reached the limit, break.
474
+ if (maxBundleCount && bundles.length >= maxBundleCount) {
466
475
  break;
467
476
  }
468
- const skipResult = await this.shouldSkip(opInfo, paymasterDeposit, stakedEntityCount, knownEntities, senders, storageMap);
469
- paymasterDeposit = skipResult.paymasterDeposit;
470
- stakedEntityCount = skipResult.stakedEntityCount;
471
- knownEntities = skipResult.knownEntities;
472
- senders = skipResult.senders;
473
- storageMap = skipResult.storageMap;
474
- if (skipResult.skip) {
475
- continue;
477
+ // Setup for next bundle.
478
+ const currentBundle = {
479
+ entryPoint,
480
+ version: isV6 ? "0.6" : "0.7",
481
+ userOps: []
482
+ };
483
+ let gasUsed = 0n;
484
+ let paymasterDeposit = {}; // paymaster deposit should be enough for all UserOps in the bundle.
485
+ let stakedEntityCount = {}; // throttled paymasters and factories are allowed only small UserOps per bundle.
486
+ let senders = new Set(); // each sender is allowed only once per bundle
487
+ let knownEntities = this.getKnownEntities();
488
+ let storageMap = {};
489
+ // Keep adding ops to current bundle.
490
+ while (outstandingUserOps.length > 0) {
491
+ const userOpInfo = outstandingUserOps.shift();
492
+ if (!userOpInfo)
493
+ break;
494
+ const { userOp, userOpHash } = userOpInfo;
495
+ // NOTE: currently if a userOp is skipped due to sender enforceUniqueSendersPerBundle it will be picked up
496
+ // again the next time mempool.process is called.
497
+ const skipResult = await this.shouldSkip(userOpInfo, paymasterDeposit, stakedEntityCount, knownEntities, senders, storageMap);
498
+ if (skipResult.skip)
499
+ continue;
500
+ gasUsed +=
501
+ userOp.callGasLimit +
502
+ userOp.verificationGasLimit +
503
+ (isVersion07(userOp)
504
+ ? (userOp.paymasterPostOpGasLimit || 0n) +
505
+ (userOp.paymasterVerificationGasLimit || 0n)
506
+ : 0n);
507
+ // Only break on gas limit if we've hit minOpsPerBundle.
508
+ if (gasUsed > maxGasLimit &&
509
+ currentBundle.userOps.length >= minOpsPerBundle) {
510
+ outstandingUserOps.unshift(userOpInfo); // re-add op to front of queue
511
+ break;
512
+ }
513
+ // Update state based on skip result
514
+ paymasterDeposit = skipResult.paymasterDeposit;
515
+ stakedEntityCount = skipResult.stakedEntityCount;
516
+ knownEntities = skipResult.knownEntities;
517
+ senders = skipResult.senders;
518
+ storageMap = skipResult.storageMap;
519
+ this.reputationManager.decreaseUserOperationCount(userOp);
520
+ this.store.removeOutstanding(userOpHash);
521
+ this.store.addProcessing(userOpInfo);
522
+ // Add op to current bundle
523
+ currentBundle.userOps.push(userOpInfo);
524
+ }
525
+ if (currentBundle.userOps.length > 0) {
526
+ bundles.push(currentBundle);
476
527
  }
477
- this.reputationManager.decreaseUserOperationCount(op);
478
- this.store.removeOutstanding(opInfo.userOperationHash);
479
- this.store.addProcessing(opInfo);
480
- result.push(opInfo);
481
- opsTaken++;
482
- }
483
- return result;
484
- }
485
- get(opHash) {
486
- const outstanding = this.store
487
- .dumpOutstanding()
488
- .find((op) => op.userOperationHash === opHash);
489
- if (outstanding) {
490
- return outstanding.userOperation;
491
- }
492
- const submitted = this.store
493
- .dumpSubmitted()
494
- .find((op) => op.userOperation.userOperationHash === opHash);
495
- if (submitted) {
496
- return submitted.userOperation.userOperation;
497
528
  }
498
- return null;
529
+ return bundles;
499
530
  }
500
531
  // For a specfic user operation, get all the queued user operations
501
532
  // They should be executed first, ordered by nonce value
502
533
  // If cuurentNonceValue is not provided, it will be fetched from the chain
503
- async getQueuedUserOperations(userOperation, entryPoint, _currentNonceValue) {
534
+ async getQueuedUserOperations(userOp, entryPoint, _currentNonceValue) {
504
535
  const entryPointContract = getContract({
505
536
  address: entryPoint,
506
- abi: isVersion06(userOperation)
507
- ? EntryPointV06Abi
508
- : EntryPointV07Abi,
537
+ abi: isVersion06(userOp) ? EntryPointV06Abi : EntryPointV07Abi,
509
538
  client: {
510
539
  public: this.config.publicClient
511
540
  }
512
541
  });
513
- const [nonceKey, userOperationNonceValue] = getNonceKeyAndValue(userOperation.nonce);
542
+ const [nonceKey, nonceSequence] = getNonceKeyAndSequence(userOp.nonce);
514
543
  let currentNonceValue = BigInt(0);
515
544
  if (_currentNonceValue) {
516
545
  currentNonceValue = _currentNonceValue;
517
546
  }
518
547
  else {
519
- const getNonceResult = await entryPointContract.read.getNonce([userOperation.sender, nonceKey], {
548
+ const getNonceResult = await entryPointContract.read.getNonce([userOp.sender, nonceKey], {
520
549
  blockTag: "latest"
521
550
  });
522
- currentNonceValue = getNonceKeyAndValue(getNonceResult)[1];
551
+ currentNonceValue = getNonceKeyAndSequence(getNonceResult)[1];
523
552
  }
524
553
  const outstanding = this.store
525
554
  .dumpOutstanding()
526
- .map(({ userOperation }) => userOperation)
527
- .filter((mempoolUserOp) => {
528
- const [opNonceKey, opNonceValue] = getNonceKeyAndValue(mempoolUserOp.nonce);
529
- return (mempoolUserOp.sender === userOperation.sender &&
530
- opNonceKey === nonceKey &&
531
- opNonceValue >= currentNonceValue &&
532
- opNonceValue < userOperationNonceValue);
555
+ .filter((userOpInfo) => {
556
+ const { userOp: mempoolUserOp } = userOpInfo;
557
+ const [mempoolNonceKey, mempoolNonceSequence] = getNonceKeyAndSequence(mempoolUserOp.nonce);
558
+ return (mempoolUserOp.sender === userOp.sender &&
559
+ mempoolNonceKey === nonceKey &&
560
+ mempoolNonceSequence >= currentNonceValue &&
561
+ mempoolNonceSequence < nonceSequence);
533
562
  });
534
563
  outstanding.sort((a, b) => {
535
- const [, aNonceValue] = getNonceKeyAndValue(a.nonce);
536
- const [, bNonceValue] = getNonceKeyAndValue(b.nonce);
564
+ const aUserOp = a.userOp;
565
+ const bUserOp = b.userOp;
566
+ const [, aNonceValue] = getNonceKeyAndSequence(aUserOp.nonce);
567
+ const [, bNonceValue] = getNonceKeyAndSequence(bUserOp.nonce);
537
568
  return Number(aNonceValue - bNonceValue);
538
569
  });
539
- return outstanding;
570
+ return outstanding.map((userOpInfo) => userOpInfo.userOp);
540
571
  }
541
572
  clear() {
542
573
  this.store.clear("outstanding");