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