@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.
- package/esm/cli/config/bundler.d.ts +0 -6
- package/esm/cli/config/bundler.js +0 -1
- package/esm/cli/config/bundler.js.map +1 -1
- package/esm/cli/config/options.js +0 -6
- package/esm/cli/config/options.js.map +1 -1
- package/esm/cli/setupServer.js +5 -5
- package/esm/cli/setupServer.js.map +1 -1
- package/esm/executor/executor.d.ts +34 -44
- package/esm/executor/executor.js +103 -463
- package/esm/executor/executor.js.map +1 -1
- package/esm/executor/executorManager.d.ts +20 -10
- package/esm/executor/executorManager.js +371 -310
- package/esm/executor/executorManager.js.map +1 -1
- package/esm/executor/filterOpsAndEStimateGas.d.ts +28 -0
- package/esm/executor/filterOpsAndEStimateGas.js +191 -0
- package/esm/executor/filterOpsAndEStimateGas.js.map +1 -0
- package/esm/executor/senderManager.d.ts +2 -0
- package/esm/executor/senderManager.js +32 -0
- package/esm/executor/senderManager.js.map +1 -1
- package/esm/executor/utils.d.ts +21 -20
- package/esm/executor/utils.js +46 -185
- package/esm/executor/utils.js.map +1 -1
- package/esm/handlers/eventManager.d.ts +4 -1
- package/esm/handlers/eventManager.js +10 -8
- package/esm/handlers/eventManager.js.map +1 -1
- package/esm/mempool/mempool.d.ts +15 -11
- package/esm/mempool/mempool.js +207 -176
- package/esm/mempool/mempool.js.map +1 -1
- package/esm/mempool/store.d.ts +7 -7
- package/esm/mempool/store.js +13 -12
- package/esm/mempool/store.js.map +1 -1
- package/esm/rpc/nonceQueuer.js +2 -2
- package/esm/rpc/nonceQueuer.js.map +1 -1
- package/esm/rpc/rpcHandler.js +27 -31
- package/esm/rpc/rpcHandler.js.map +1 -1
- package/esm/types/mempool.d.ts +40 -41
- package/esm/types/mempool.js.map +1 -1
- package/esm/types/schemas.d.ts +0 -4
- package/esm/types/schemas.js.map +1 -1
- package/esm/utils/userop.d.ts +8 -3
- package/esm/utils/userop.js +5 -3
- package/esm/utils/userop.js.map +1 -1
- package/lib/cli/config/bundler.d.ts +0 -6
- package/lib/cli/config/bundler.js +0 -1
- package/lib/cli/config/bundler.js.map +1 -1
- package/lib/cli/config/options.js +0 -6
- package/lib/cli/config/options.js.map +1 -1
- package/lib/cli/setupServer.js +5 -5
- package/lib/cli/setupServer.js.map +1 -1
- package/lib/executor/executor.d.ts +34 -44
- package/lib/executor/executor.js +100 -460
- package/lib/executor/executor.js.map +1 -1
- package/lib/executor/executorManager.d.ts +20 -10
- package/lib/executor/executorManager.js +370 -309
- package/lib/executor/executorManager.js.map +1 -1
- package/lib/executor/filterOpsAndEStimateGas.d.ts +28 -0
- package/lib/executor/filterOpsAndEStimateGas.js +218 -0
- package/lib/executor/filterOpsAndEStimateGas.js.map +1 -0
- package/lib/executor/senderManager.d.ts +2 -0
- package/lib/executor/senderManager.js +32 -0
- package/lib/executor/senderManager.js.map +1 -1
- package/lib/executor/utils.d.ts +21 -20
- package/lib/executor/utils.js +49 -186
- package/lib/executor/utils.js.map +1 -1
- package/lib/handlers/eventManager.d.ts +4 -1
- package/lib/handlers/eventManager.js +10 -8
- package/lib/handlers/eventManager.js.map +1 -1
- package/lib/mempool/mempool.d.ts +15 -11
- package/lib/mempool/mempool.js +206 -175
- package/lib/mempool/mempool.js.map +1 -1
- package/lib/mempool/store.d.ts +7 -7
- package/lib/mempool/store.js +13 -12
- package/lib/mempool/store.js.map +1 -1
- package/lib/rpc/nonceQueuer.js +1 -1
- package/lib/rpc/nonceQueuer.js.map +1 -1
- package/lib/rpc/rpcHandler.js +26 -30
- package/lib/rpc/rpcHandler.js.map +1 -1
- package/lib/types/mempool.d.ts +40 -41
- package/lib/types/mempool.js.map +1 -1
- package/lib/types/schemas.d.ts +0 -4
- package/lib/types/schemas.js.map +1 -1
- package/lib/utils/userop.d.ts +8 -3
- package/lib/utils/userop.js +7 -5
- package/lib/utils/userop.js.map +1 -1
- package/package.json +1 -1
- package/esm/executor/types.d.ts +0 -25
- package/esm/executor/types.js +0 -2
- package/esm/executor/types.js.map +0 -1
- package/lib/executor/types.d.ts +0 -25
- package/lib/executor/types.js +0 -3
- package/lib/executor/types.js.map +0 -1
package/esm/mempool/mempool.js
CHANGED
|
@@ -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,
|
|
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(
|
|
31
|
-
const
|
|
27
|
+
replaceSubmitted(userOpInfo, transactionInfo) {
|
|
28
|
+
const { userOpHash } = userOpInfo;
|
|
29
|
+
const existingUserOpToReplace = this.store
|
|
32
30
|
.dumpSubmitted()
|
|
33
|
-
.find((
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
35
|
+
...userOpInfo,
|
|
39
36
|
transactionInfo
|
|
40
37
|
});
|
|
41
|
-
this.monitor.setUserOperationStatus(
|
|
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
|
|
45
|
+
const processingUserOp = this.store
|
|
49
46
|
.dumpProcessing()
|
|
50
|
-
.find((
|
|
51
|
-
if (
|
|
47
|
+
.find((userOpInfo) => userOpInfo.userOpHash === userOpHash);
|
|
48
|
+
if (processingUserOp) {
|
|
52
49
|
this.store.removeProcessing(userOpHash);
|
|
53
50
|
this.store.addSubmitted({
|
|
54
|
-
|
|
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
|
|
113
|
-
const
|
|
114
|
-
entities.sender.add(
|
|
115
|
-
const isUserOpV06 = isVersion06(
|
|
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(
|
|
118
|
-
:
|
|
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(
|
|
124
|
-
:
|
|
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(
|
|
134
|
-
const
|
|
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((
|
|
141
|
+
].find((userOpInfo) => userOpInfo.userOpHash === userOpHash);
|
|
147
142
|
if (existingUserOperation) {
|
|
148
143
|
return [false, "Already known"];
|
|
149
144
|
}
|
|
150
|
-
if (processedOrSubmittedOps.find((
|
|
151
|
-
const mempoolUserOp =
|
|
152
|
-
return (mempoolUserOp.sender ===
|
|
153
|
-
mempoolUserOp.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(
|
|
161
|
-
const
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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(
|
|
171
|
-
|
|
172
|
-
|
|
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(
|
|
180
|
-
|
|
181
|
-
|
|
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((
|
|
189
|
-
if (
|
|
190
|
-
const
|
|
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 (
|
|
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
|
|
201
|
-
const
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
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(
|
|
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
|
|
218
|
-
return
|
|
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] =
|
|
223
|
+
const [nonceKey] = getNonceKeyAndSequence(userOp.nonce);
|
|
228
224
|
const queuedUserOperationsCount = this.store
|
|
229
225
|
.dumpOutstanding()
|
|
230
226
|
.filter((userOpInfo) => {
|
|
231
|
-
const userOp = userOpInfo
|
|
232
|
-
const [opNonceKey] =
|
|
233
|
-
return (
|
|
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
|
-
|
|
239
|
+
userOp,
|
|
244
240
|
entryPoint,
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
referencedContracts
|
|
241
|
+
userOpHash: userOpHash,
|
|
242
|
+
referencedContracts,
|
|
243
|
+
addedToMempool: Date.now()
|
|
249
244
|
});
|
|
250
|
-
this.monitor.setUserOperationStatus(
|
|
245
|
+
this.monitor.setUserOperationStatus(userOpHash, {
|
|
251
246
|
status: "not_submitted",
|
|
252
247
|
transactionHash: null
|
|
253
248
|
});
|
|
254
|
-
this.eventManager.emitAddedToMempool(
|
|
249
|
+
this.eventManager.emitAddedToMempool(userOpHash);
|
|
255
250
|
return [true, ""];
|
|
256
251
|
}
|
|
257
252
|
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: <explanation>
|
|
258
|
-
async shouldSkip(
|
|
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
|
|
264
|
+
const { userOp, entryPoint, userOpHash, referencedContracts } = userOpInfo;
|
|
265
|
+
const isUserOpV06 = isVersion06(userOp);
|
|
271
266
|
const paymaster = isUserOpV06
|
|
272
|
-
? getAddressFromInitCodeOrPaymasterAndData(
|
|
273
|
-
:
|
|
267
|
+
? getAddressFromInitCodeOrPaymasterAndData(userOp.paymasterAndData)
|
|
268
|
+
: userOp.paymaster;
|
|
274
269
|
const factory = isUserOpV06
|
|
275
|
-
? getAddressFromInitCodeOrPaymasterAndData(
|
|
276
|
-
:
|
|
277
|
-
const paymasterStatus = this.reputationManager.getStatus(
|
|
278
|
-
const factoryStatus = this.reputationManager.getStatus(
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
318
|
+
if (senders.has(userOp.sender) &&
|
|
324
319
|
this.config.enforceUniqueSendersPerBundle) {
|
|
325
320
|
this.logger.trace({
|
|
326
|
-
sender:
|
|
327
|
-
|
|
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(
|
|
337
|
+
queuedUserOperations = await this.getQueuedUserOperations(userOp, entryPoint);
|
|
343
338
|
}
|
|
344
339
|
validationResult = await this.validator.validateUserOperation({
|
|
345
340
|
shouldCheckPrefund: false,
|
|
346
|
-
userOperation:
|
|
341
|
+
userOperation: userOp,
|
|
347
342
|
queuedUserOperations,
|
|
348
|
-
entryPoint
|
|
349
|
-
referencedContracts
|
|
343
|
+
entryPoint,
|
|
344
|
+
referencedContracts
|
|
350
345
|
});
|
|
351
346
|
}
|
|
352
347
|
catch (e) {
|
|
353
348
|
this.logger.error({
|
|
354
|
-
|
|
349
|
+
userOpHash,
|
|
355
350
|
error: JSON.stringify(e)
|
|
356
351
|
}, "2nd Validation error");
|
|
357
|
-
this.store.removeOutstanding(
|
|
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 !==
|
|
364
|
+
if (address !== userOp.sender &&
|
|
365
|
+
knownEntities.sender.has(address)) {
|
|
370
366
|
this.logger.trace({
|
|
371
367
|
storageAddress,
|
|
372
|
-
|
|
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:
|
|
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
|
-
|
|
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(
|
|
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
|
|
429
|
-
const
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
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] =
|
|
438
|
-
const [bNonceKey, bNonceValue] =
|
|
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
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
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
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
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
|
|
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(
|
|
534
|
+
async getQueuedUserOperations(userOp, entryPoint, _currentNonceValue) {
|
|
504
535
|
const entryPointContract = getContract({
|
|
505
536
|
address: entryPoint,
|
|
506
|
-
abi: isVersion06(
|
|
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,
|
|
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([
|
|
548
|
+
const getNonceResult = await entryPointContract.read.getNonce([userOp.sender, nonceKey], {
|
|
520
549
|
blockTag: "latest"
|
|
521
550
|
});
|
|
522
|
-
currentNonceValue =
|
|
551
|
+
currentNonceValue = getNonceKeyAndSequence(getNonceResult)[1];
|
|
523
552
|
}
|
|
524
553
|
const outstanding = this.store
|
|
525
554
|
.dumpOutstanding()
|
|
526
|
-
.
|
|
527
|
-
|
|
528
|
-
const [
|
|
529
|
-
return (mempoolUserOp.sender ===
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
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
|
|
536
|
-
const
|
|
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");
|