abstractionkit 0.3.5 → 0.3.8
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/CHANGELOG.md +96 -0
- package/dist/index.cjs +1844 -1016
- package/dist/index.d.cts +235 -130
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +235 -130
- package/dist/index.d.mts.map +1 -1
- package/dist/index.iife.js +1844 -1016
- package/dist/index.mjs +1840 -1014
- package/dist/index.mjs.map +1 -1
- package/package.json +7 -2
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as __exportAll } from "./chunk-CfYAbeIz.mjs";
|
|
2
|
-
import { AbiCoder,
|
|
2
|
+
import { AbiCoder, TypedDataEncoder, Wallet, encodeRlp, ethers, getAddress, getBytes, hashMessage, hexlify, id, isAddress, keccak256, solidityPacked, solidityPackedKeccak256, toBeArray } from "ethers";
|
|
3
3
|
//#region src/errors.ts
|
|
4
4
|
/**
|
|
5
5
|
* Maps JSON-RPC numeric error codes to human-readable {@link BundlerErrorCode} values.
|
|
@@ -18,6 +18,16 @@ const BundlerErrorCodeDict = {
|
|
|
18
18
|
"-32521": "EXECUTION_REVERTED"
|
|
19
19
|
};
|
|
20
20
|
/**
|
|
21
|
+
* Maps JSON-RPC numeric error codes to human-readable {@link JsonRpcErrorCode} values.
|
|
22
|
+
*/
|
|
23
|
+
const JsonRpcErrorDict = {
|
|
24
|
+
"-32700": "PARSE_ERROR",
|
|
25
|
+
"-32600": "INVALID_REQUEST",
|
|
26
|
+
"-32601": "METHOD_NOT_FOUND",
|
|
27
|
+
"-32602": "INVALID_PARAMS",
|
|
28
|
+
"-32603": "INTERNAL_ERROR"
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
21
31
|
* Custom error class for the AbstractionKit SDK. Wraps bundler, JSON-RPC,
|
|
22
32
|
* and general errors with a structured code, optional numeric errno, and
|
|
23
33
|
* arbitrary JSON-serializable context.
|
|
@@ -72,164 +82,164 @@ function ensureError(value) {
|
|
|
72
82
|
return /* @__PURE__ */ new Error(`This value was thrown as is, not through an Error: ${stringified}`);
|
|
73
83
|
}
|
|
74
84
|
//#endregion
|
|
75
|
-
//#region src/
|
|
76
|
-
/**
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
85
|
+
//#region src/transport/Transport.ts
|
|
86
|
+
/**
|
|
87
|
+
* Default {@link ProviderRpcError} implementation. {@link BaseRpcTransport}
|
|
88
|
+
* throws this automatically when a JSON-RPC envelope returns
|
|
89
|
+
* `{ error: { code, message, data } }`.
|
|
90
|
+
*/
|
|
91
|
+
var TransportRpcError = class extends Error {
|
|
92
|
+
code;
|
|
93
|
+
data;
|
|
94
|
+
/**
|
|
95
|
+
* @param code - Numeric JSON-RPC error code (e.g. -32601 for METHOD_NOT_FOUND)
|
|
96
|
+
* @param message - Human-readable error description
|
|
97
|
+
* @param data - Optional additional data returned by the RPC server
|
|
98
|
+
*/
|
|
99
|
+
constructor(code, message, data) {
|
|
100
|
+
super(message);
|
|
101
|
+
this.name = "TransportRpcError";
|
|
102
|
+
this.code = code;
|
|
103
|
+
this.data = data;
|
|
104
|
+
}
|
|
95
105
|
};
|
|
96
|
-
/**
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
106
|
+
/**
|
|
107
|
+
* Narrowing helper for {@link EventfulTransport}.
|
|
108
|
+
*
|
|
109
|
+
* @param transport - Any transport to test
|
|
110
|
+
* @returns `true` when both `on` and `removeListener` methods are present
|
|
111
|
+
*/
|
|
112
|
+
function isEventfulTransport(transport) {
|
|
113
|
+
const t = transport;
|
|
114
|
+
return typeof t.on === "function" && typeof t.removeListener === "function";
|
|
115
|
+
}
|
|
116
|
+
//#endregion
|
|
117
|
+
//#region src/transport/BaseRpcTransport.ts
|
|
118
|
+
/**
|
|
119
|
+
* Optional convenience base class for users writing new wire-level
|
|
120
|
+
* {@link Transport} backends (WebSocket, IPC, custom HTTP, in-process mock,
|
|
121
|
+
* etc.). Handles JSON-RPC framing, id assignment, bigint serialization, and
|
|
122
|
+
* standard error parsing so subclasses implement only the byte-level
|
|
123
|
+
* `send(envelope, options)` hook.
|
|
124
|
+
*
|
|
125
|
+
* Users who already have a {@link Transport} in hand (window.ethereum, viem
|
|
126
|
+
* `WalletClient`, ethers `Eip1193Provider`-shaped object, etc.) do NOT need
|
|
127
|
+
* this class — they pass their object directly.
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* ```ts
|
|
131
|
+
* class WebSocketTransport extends BaseRpcTransport {
|
|
132
|
+
* protected async send(envelope) {
|
|
133
|
+
* // serialize envelope to JSON, send over the socket, await response
|
|
134
|
+
* return JSON.parse(await this.socket.sendAndAwait(JSON.stringify(envelope)));
|
|
135
|
+
* }
|
|
136
|
+
* }
|
|
137
|
+
* ```
|
|
138
|
+
*/
|
|
139
|
+
var BaseRpcTransport = class BaseRpcTransport {
|
|
140
|
+
nextId = 1;
|
|
141
|
+
/**
|
|
142
|
+
* Build a JSON-RPC envelope, delegate the wire I/O to the subclass's
|
|
143
|
+
* {@link BaseRpcTransport.send} method, and parse the response.
|
|
144
|
+
*
|
|
145
|
+
* @throws {@link TransportRpcError} when the response contains an `error` field
|
|
146
|
+
* @throws {@link TransportRpcError} (code -32603) when the response is malformed
|
|
147
|
+
*/
|
|
148
|
+
async request(args, options) {
|
|
149
|
+
const envelope = {
|
|
150
|
+
jsonrpc: "2.0",
|
|
151
|
+
id: this.nextId++,
|
|
152
|
+
method: args.method,
|
|
153
|
+
params: args.params
|
|
154
|
+
};
|
|
155
|
+
const raw = await this.send(envelope, options);
|
|
156
|
+
return BaseRpcTransport.parseResponse(raw);
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Serialize a JSON-RPC envelope to a string, converting bigint values to
|
|
160
|
+
* `0x`-prefixed hex strings (preserving the historical SDK behavior).
|
|
161
|
+
*/
|
|
162
|
+
static serializeEnvelope(envelope) {
|
|
163
|
+
return JSON.stringify(envelope, (_key, value) => typeof value === "bigint" ? `0x${value.toString(16)}` : value);
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Parse a decoded JSON-RPC response. Returns the `result` field on success
|
|
167
|
+
* or throws a {@link TransportRpcError} on error / malformed response.
|
|
168
|
+
*/
|
|
169
|
+
static parseResponse(raw) {
|
|
170
|
+
if (raw == null || typeof raw !== "object") throw new TransportRpcError(-32603, "malformed JSON-RPC response", raw);
|
|
171
|
+
const response = raw;
|
|
172
|
+
if ("error" in response) {
|
|
173
|
+
const { code, message, data } = response.error;
|
|
174
|
+
throw new TransportRpcError(code, message, data);
|
|
175
|
+
}
|
|
176
|
+
if ("result" in response) return response.result;
|
|
177
|
+
throw new TransportRpcError(-32603, "malformed JSON-RPC response", raw);
|
|
178
|
+
}
|
|
107
179
|
};
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
{
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
{
|
|
141
|
-
type: "uint256",
|
|
142
|
-
name: "maxFeePerGas"
|
|
143
|
-
},
|
|
144
|
-
{
|
|
145
|
-
type: "uint256",
|
|
146
|
-
name: "maxPriorityFeePerGas"
|
|
147
|
-
},
|
|
148
|
-
{
|
|
149
|
-
type: "bytes",
|
|
150
|
-
name: "paymasterAndData"
|
|
151
|
-
},
|
|
152
|
-
{
|
|
153
|
-
type: "uint48",
|
|
154
|
-
name: "validAfter"
|
|
155
|
-
},
|
|
156
|
-
{
|
|
157
|
-
type: "uint48",
|
|
158
|
-
name: "validUntil"
|
|
159
|
-
},
|
|
160
|
-
{
|
|
161
|
-
type: "address",
|
|
162
|
-
name: "entryPoint"
|
|
180
|
+
//#endregion
|
|
181
|
+
//#region src/transport/HttpTransport.ts
|
|
182
|
+
/**
|
|
183
|
+
* Default concrete {@link Transport}: POSTs JSON-RPC envelopes to an HTTP
|
|
184
|
+
* endpoint. Used by every URL-string call site once a string is normalized
|
|
185
|
+
* into a transport.
|
|
186
|
+
*
|
|
187
|
+
* @example
|
|
188
|
+
* ```ts
|
|
189
|
+
* const t = new HttpTransport("https://api.candide.dev/public/v3/11155111");
|
|
190
|
+
* const chainId = await t.request<string>({ method: "eth_chainId" });
|
|
191
|
+
*
|
|
192
|
+
* // With auth headers and a custom fetch:
|
|
193
|
+
* const t2 = new HttpTransport("https://...", {
|
|
194
|
+
* headers: { Authorization: `Bearer ${token}` },
|
|
195
|
+
* fetch: myFetchWithRetry,
|
|
196
|
+
* });
|
|
197
|
+
* ```
|
|
198
|
+
*/
|
|
199
|
+
var HttpTransport = class HttpTransport extends BaseRpcTransport {
|
|
200
|
+
/** Endpoint URL this transport POSTs to. */
|
|
201
|
+
url;
|
|
202
|
+
/** Options passed at construction time. */
|
|
203
|
+
options;
|
|
204
|
+
/**
|
|
205
|
+
* @param url - JSON-RPC endpoint URL (bundler, paymaster, or node)
|
|
206
|
+
* @param options - Optional fetch override and static headers
|
|
207
|
+
*/
|
|
208
|
+
constructor(url, options = {}) {
|
|
209
|
+
super();
|
|
210
|
+
this.url = url;
|
|
211
|
+
this.options = options;
|
|
163
212
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
name: "initCode"
|
|
178
|
-
},
|
|
179
|
-
{
|
|
180
|
-
type: "bytes",
|
|
181
|
-
name: "callData"
|
|
182
|
-
},
|
|
183
|
-
{
|
|
184
|
-
type: "uint128",
|
|
185
|
-
name: "verificationGasLimit"
|
|
186
|
-
},
|
|
187
|
-
{
|
|
188
|
-
type: "uint128",
|
|
189
|
-
name: "callGasLimit"
|
|
190
|
-
},
|
|
191
|
-
{
|
|
192
|
-
type: "uint256",
|
|
193
|
-
name: "preVerificationGas"
|
|
194
|
-
},
|
|
195
|
-
{
|
|
196
|
-
type: "uint128",
|
|
197
|
-
name: "maxPriorityFeePerGas"
|
|
198
|
-
},
|
|
199
|
-
{
|
|
200
|
-
type: "uint128",
|
|
201
|
-
name: "maxFeePerGas"
|
|
202
|
-
},
|
|
203
|
-
{
|
|
204
|
-
type: "bytes",
|
|
205
|
-
name: "paymasterAndData"
|
|
206
|
-
},
|
|
207
|
-
{
|
|
208
|
-
type: "uint48",
|
|
209
|
-
name: "validAfter"
|
|
210
|
-
},
|
|
211
|
-
{
|
|
212
|
-
type: "uint48",
|
|
213
|
-
name: "validUntil"
|
|
214
|
-
},
|
|
215
|
-
{
|
|
216
|
-
type: "address",
|
|
217
|
-
name: "entryPoint"
|
|
213
|
+
async send(envelope, options) {
|
|
214
|
+
const headers = {
|
|
215
|
+
...this.options.headers ?? {},
|
|
216
|
+
"Content-Type": "application/json"
|
|
217
|
+
};
|
|
218
|
+
const body = HttpTransport.serializeEnvelope(envelope);
|
|
219
|
+
return await (await (this.options.fetch ?? globalThis.fetch)(this.url, {
|
|
220
|
+
method: "POST",
|
|
221
|
+
headers,
|
|
222
|
+
body,
|
|
223
|
+
redirect: "follow",
|
|
224
|
+
signal: options?.signal
|
|
225
|
+
})).json();
|
|
218
226
|
}
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
227
|
+
};
|
|
228
|
+
/**
|
|
229
|
+
* Narrowing helper for {@link HttpTransport}. Useful for code that wants to
|
|
230
|
+
* read the underlying URL — e.g. when serializing a configured Bundler for
|
|
231
|
+
* logging or diagnostics.
|
|
232
|
+
*
|
|
233
|
+
* @example
|
|
234
|
+
* ```ts
|
|
235
|
+
* if (isHttpTransport(bundler.transport)) {
|
|
236
|
+
* console.log("Bundler URL:", bundler.transport.url);
|
|
237
|
+
* }
|
|
238
|
+
* ```
|
|
239
|
+
*/
|
|
240
|
+
function isHttpTransport(transport) {
|
|
241
|
+
return transport instanceof HttpTransport;
|
|
242
|
+
}
|
|
233
243
|
//#endregion
|
|
234
244
|
//#region src/types.ts
|
|
235
245
|
/**
|
|
@@ -263,560 +273,456 @@ let PolygonChain = /* @__PURE__ */ function(PolygonChain) {
|
|
|
263
273
|
return PolygonChain;
|
|
264
274
|
}({});
|
|
265
275
|
//#endregion
|
|
266
|
-
//#region src/
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
276
|
+
//#region src/transport/normalize.ts
|
|
277
|
+
/**
|
|
278
|
+
* Recursively convert any `bigint` values inside an RPC param to `0x`-prefixed
|
|
279
|
+
* hex strings, descending into arrays and plain objects. Other values pass
|
|
280
|
+
* through unchanged.
|
|
281
|
+
*
|
|
282
|
+
* Required for user-supplied {@link Transport} implementations that don't go
|
|
283
|
+
* through {@link BaseRpcTransport.serializeEnvelope} (EIP-1193 providers, viem
|
|
284
|
+
* clients, etc.) and would otherwise see raw `bigint`s in `params`. Mirrors the
|
|
285
|
+
* normalization done in {@link sendJsonRpcRequest}.
|
|
286
|
+
*
|
|
287
|
+
* @internal
|
|
288
|
+
*/
|
|
289
|
+
function normalizeRpcValue(value) {
|
|
290
|
+
if (typeof value === "bigint") return `0x${value.toString(16)}`;
|
|
291
|
+
if (Array.isArray(value)) return value.map(normalizeRpcValue);
|
|
292
|
+
if (value !== null && typeof value === "object") {
|
|
293
|
+
const out = {};
|
|
294
|
+
for (const [k, v] of Object.entries(value)) out[k] = normalizeRpcValue(v);
|
|
295
|
+
return out;
|
|
296
|
+
}
|
|
297
|
+
return value;
|
|
275
298
|
}
|
|
276
299
|
/**
|
|
277
|
-
*
|
|
278
|
-
*
|
|
279
|
-
*
|
|
300
|
+
* Wrap a {@link Transport} so every outbound `request` has its `params`
|
|
301
|
+
* normalized (bigints → 0x-hex) before delegation. Idempotent: wrapping an
|
|
302
|
+
* already-normalizing transport is harmless because the second pass sees only
|
|
303
|
+
* strings.
|
|
280
304
|
*
|
|
281
|
-
*
|
|
282
|
-
*
|
|
283
|
-
*
|
|
284
|
-
*
|
|
305
|
+
* Applied once at each service boundary (Bundler, CandidePaymaster,
|
|
306
|
+
* Erc7677Paymaster, JsonRpcNode, …) so normalization is impossible to forget
|
|
307
|
+
* per-method. Required for user-supplied transports (EIP-1193 providers, viem
|
|
308
|
+
* clients, etc.) that don't route through {@link BaseRpcTransport.serializeEnvelope}.
|
|
309
|
+
*
|
|
310
|
+
* @internal
|
|
285
311
|
*/
|
|
286
|
-
function
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
"address",
|
|
295
|
-
"uint256"
|
|
296
|
-
], [
|
|
297
|
-
packedUserOperationHash,
|
|
298
|
-
entrypointAddress,
|
|
299
|
-
chainId
|
|
300
|
-
]));
|
|
301
|
-
} else if (entrypointAddress.toLowerCase() === "0x0000000071727De22E5E9d8BAf0edAc6f37da032".toLowerCase()) {
|
|
302
|
-
packedUserOperationHash = keccak256(createPackedUserOperationV7(useroperation));
|
|
303
|
-
userOperationHash = keccak256(abiCoder.encode([
|
|
304
|
-
"bytes32",
|
|
305
|
-
"address",
|
|
306
|
-
"uint256"
|
|
307
|
-
], [
|
|
308
|
-
packedUserOperationHash,
|
|
309
|
-
entrypointAddress,
|
|
310
|
-
chainId
|
|
311
|
-
]));
|
|
312
|
-
} else if (entrypointAddress.toLowerCase() === "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108".toLowerCase()) {
|
|
313
|
-
packedUserOperationHash = keccak256(createPackedUserOperationV8(useroperation));
|
|
314
|
-
userOperationHash = keccak256(`0x1901${buildDomainSeparator(chainId, entrypointAddress).slice(2)}${packedUserOperationHash.slice(2)}`);
|
|
315
|
-
} else if (entrypointAddress.toLowerCase() === "0x433709009B8330FDa32311DF1C2AFA402eD8D009".toLowerCase()) {
|
|
316
|
-
packedUserOperationHash = keccak256(createPackedUserOperationV9(useroperation));
|
|
317
|
-
userOperationHash = keccak256(`0x1901${buildDomainSeparator(chainId, entrypointAddress).slice(2)}${packedUserOperationHash.slice(2)}`);
|
|
318
|
-
} else throw new RangeError(`unsupported entrypoint address: ${entrypointAddress}`);
|
|
319
|
-
return userOperationHash;
|
|
312
|
+
function normalizingTransport(inner) {
|
|
313
|
+
return { request(args, options) {
|
|
314
|
+
const params = args.params == null ? args.params : normalizeRpcValue(args.params);
|
|
315
|
+
return inner.request({
|
|
316
|
+
method: args.method,
|
|
317
|
+
params
|
|
318
|
+
}, options);
|
|
319
|
+
} };
|
|
320
320
|
}
|
|
321
|
+
//#endregion
|
|
322
|
+
//#region src/transport/JsonRpcNode.ts
|
|
323
|
+
const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
|
|
321
324
|
/**
|
|
322
|
-
*
|
|
323
|
-
*
|
|
324
|
-
*
|
|
325
|
-
*
|
|
326
|
-
*
|
|
325
|
+
* High-level service class for the JSON-RPC node methods abstractionkit reads
|
|
326
|
+
* from. Intentionally NOT a general Ethereum client — only exposes the
|
|
327
|
+
* methods the SDK actually uses. For broader functionality, drop down to
|
|
328
|
+
* `.transport` and call `request({ method, params })` directly, or use a
|
|
329
|
+
* dedicated library like viem / ethers.
|
|
327
330
|
*
|
|
328
|
-
*
|
|
329
|
-
* {@link
|
|
330
|
-
*
|
|
331
|
-
* use within the package and not re-exported from `src/abstractionkit.ts`.
|
|
331
|
+
* Like {@link Bundler} and the paymaster classes, `JsonRpcNode` itself
|
|
332
|
+
* implements {@link Transport} so it can be passed back into any Transport
|
|
333
|
+
* position.
|
|
332
334
|
*
|
|
333
|
-
* @
|
|
334
|
-
*
|
|
335
|
+
* @example
|
|
336
|
+
* ```ts
|
|
337
|
+
* const node = new JsonRpcNode("https://ethereum-sepolia.publicnode.com");
|
|
338
|
+
* const id = await node.chainId();
|
|
339
|
+
* const code = await node.getCode("0x...");
|
|
340
|
+
*
|
|
341
|
+
* // Also a Transport — can be slotted in:
|
|
342
|
+
* const bundler = new Bundler(node); // bundler will speak through this node
|
|
343
|
+
* ```
|
|
335
344
|
*/
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
345
|
+
var JsonRpcNode = class JsonRpcNode {
|
|
346
|
+
/**
|
|
347
|
+
* The raw transport the user passed in (or {@link HttpTransport} when a URL
|
|
348
|
+
* string was passed). Exposed for introspection — reading `.url`,
|
|
349
|
+
* `isHttpTransport(...)` checks, passing it back into another service.
|
|
350
|
+
*
|
|
351
|
+
* Calls made directly on this field (`node.transport.request(...)`) go
|
|
352
|
+
* to the raw transport and skip SDK-level behavior like bigint param
|
|
353
|
+
* normalization. For SDK-pipeline behavior, use {@link JsonRpcNode.request}
|
|
354
|
+
* or the typed methods.
|
|
355
|
+
*/
|
|
356
|
+
transport;
|
|
357
|
+
/** Normalizing wrapper around {@link transport}, used for every SDK-outbound call. */
|
|
358
|
+
outbound;
|
|
359
|
+
/**
|
|
360
|
+
* @param rpc - Node JSON-RPC endpoint URL, or any {@link Transport}
|
|
361
|
+
*/
|
|
362
|
+
constructor(rpc) {
|
|
363
|
+
this.transport = typeof rpc === "string" ? new HttpTransport(rpc) : rpc;
|
|
364
|
+
this.outbound = normalizingTransport(this.transport);
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Normalize any acceptable input into a {@link JsonRpcNode}. Used at every
|
|
368
|
+
* public-API widening site (in account / paymaster classes). When the
|
|
369
|
+
* input is already a `JsonRpcNode`, the same instance is returned by
|
|
370
|
+
* reference, so a user's preconstructed `JsonRpcNode` is never re-wrapped.
|
|
371
|
+
*
|
|
372
|
+
* @param input - URL string, Transport, or existing JsonRpcNode
|
|
373
|
+
*/
|
|
374
|
+
static from(input) {
|
|
375
|
+
return input instanceof JsonRpcNode ? input : new JsonRpcNode(input);
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Transport delegate. Routes through the normalizing wrapper so `bigint`
|
|
379
|
+
* values in `params` are converted to `0x`-hex before reaching
|
|
380
|
+
* user-supplied transports that don't go through
|
|
381
|
+
* {@link BaseRpcTransport.serializeEnvelope}. Lets a `JsonRpcNode` itself
|
|
382
|
+
* slot into any other transport position.
|
|
383
|
+
*/
|
|
384
|
+
request(args, options) {
|
|
385
|
+
return this.outbound.request(args, options);
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* `eth_chainId`. Returns the hex-encoded chain id (e.g. `"0xaa36a7"` for
|
|
389
|
+
* Sepolia).
|
|
390
|
+
*/
|
|
391
|
+
async chainId(options) {
|
|
392
|
+
try {
|
|
393
|
+
const result = await this.outbound.request({ method: "eth_chainId" }, options);
|
|
394
|
+
if (typeof result !== "string") throw new AbstractionKitError("BAD_DATA", "eth_chainId returned ill formed data", { context: JSON.stringify(result) });
|
|
395
|
+
return result;
|
|
396
|
+
} catch (err) {
|
|
397
|
+
throw translateNodeError(err, "eth_chainId");
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* `eth_blockNumber`. Returns the latest block number as a bigint.
|
|
402
|
+
*/
|
|
403
|
+
async blockNumber(options) {
|
|
404
|
+
try {
|
|
405
|
+
const result = await this.outbound.request({ method: "eth_blockNumber" }, options);
|
|
406
|
+
if (typeof result !== "string") throw new AbstractionKitError("BAD_DATA", "eth_blockNumber returned ill formed data", { context: JSON.stringify(result) });
|
|
407
|
+
return BigInt(result);
|
|
408
|
+
} catch (err) {
|
|
409
|
+
throw translateNodeError(err, "eth_blockNumber");
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* `eth_getCode`. Returns the deployed bytecode at `address` at the given
|
|
414
|
+
* block tag (default `"latest"`).
|
|
415
|
+
*/
|
|
416
|
+
async getCode(address, blockTag = "latest", options) {
|
|
417
|
+
try {
|
|
418
|
+
const result = await this.outbound.request({
|
|
419
|
+
method: "eth_getCode",
|
|
420
|
+
params: [address, blockTag]
|
|
421
|
+
}, options);
|
|
422
|
+
if (typeof result !== "string") throw new AbstractionKitError("BAD_DATA", "eth_getCode returned ill formed data", { context: JSON.stringify(result) });
|
|
423
|
+
return result;
|
|
424
|
+
} catch (err) {
|
|
425
|
+
throw translateNodeError(err, "eth_getCode");
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* `eth_call`. Executes a read-only call against `to` and returns the raw
|
|
430
|
+
* return data as a hex string. Supports state overrides via the optional
|
|
431
|
+
* third parameter.
|
|
432
|
+
*/
|
|
433
|
+
async call(tx, blockTag = "latest", stateOverrides, options) {
|
|
434
|
+
const params = stateOverrides == null ? [tx, blockTag] : [
|
|
435
|
+
tx,
|
|
436
|
+
blockTag,
|
|
437
|
+
stateOverrides
|
|
438
|
+
];
|
|
439
|
+
try {
|
|
440
|
+
const result = await this.outbound.request({
|
|
441
|
+
method: "eth_call",
|
|
442
|
+
params
|
|
443
|
+
}, options);
|
|
444
|
+
if (typeof result !== "string") throw new AbstractionKitError("BAD_DATA", "eth_call returned ill formed data", { context: JSON.stringify(result) });
|
|
445
|
+
return result;
|
|
446
|
+
} catch (err) {
|
|
447
|
+
throw translateNodeError(err, "eth_call");
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
451
|
+
* `eth_getTransactionCount`. Returns the transaction count (account nonce
|
|
452
|
+
* at the EOA level — not the EntryPoint nonce; see
|
|
453
|
+
* {@link JsonRpcNode.getEntryPointNonce}) as a bigint.
|
|
454
|
+
*/
|
|
455
|
+
async getTransactionCount(address, blockTag = "latest", options) {
|
|
456
|
+
try {
|
|
457
|
+
const result = await this.outbound.request({
|
|
458
|
+
method: "eth_getTransactionCount",
|
|
459
|
+
params: [address, blockTag]
|
|
460
|
+
}, options);
|
|
461
|
+
if (typeof result !== "string") throw new AbstractionKitError("BAD_DATA", "eth_getTransactionCount returned ill formed data", { context: JSON.stringify(result) });
|
|
462
|
+
return BigInt(result);
|
|
463
|
+
} catch (err) {
|
|
464
|
+
throw translateNodeError(err, "eth_getTransactionCount");
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* Fetch current gas prices and apply a level multiplier.
|
|
469
|
+
*
|
|
470
|
+
* Tries `eth_maxPriorityFeePerGas` + `eth_gasPrice` first (EIP-1559),
|
|
471
|
+
* falling back to `eth_gasPrice` alone if the priority-fee method is
|
|
472
|
+
* unsupported, and finally to a 1 gwei floor multiplied by `gasLevel`.
|
|
473
|
+
*
|
|
474
|
+
* @param gasLevel - {@link GasOption} multiplier (default: Medium = 1.2x)
|
|
475
|
+
* @returns `[maxFeePerGas, maxPriorityFeePerGas]` as bigints
|
|
476
|
+
*/
|
|
477
|
+
async getFeeData(gasLevel = GasOption.Medium, options) {
|
|
478
|
+
try {
|
|
479
|
+
let gasPrice = null;
|
|
480
|
+
let maxPriorityFeePerGas = null;
|
|
481
|
+
try {
|
|
482
|
+
const result = await this.outbound.request({ method: "eth_gasPrice" }, options);
|
|
483
|
+
if (typeof result !== "string") throw new AbstractionKitError("BAD_DATA", "eth_gasPrice returned ill formed data", { context: JSON.stringify(result) });
|
|
484
|
+
gasPrice = BigInt(result);
|
|
485
|
+
} catch (err) {
|
|
486
|
+
if (!isMethodNotSupportedError(err)) throw err;
|
|
487
|
+
}
|
|
488
|
+
try {
|
|
489
|
+
const result = await this.outbound.request({ method: "eth_maxPriorityFeePerGas" }, options);
|
|
490
|
+
if (typeof result !== "string") throw new AbstractionKitError("BAD_DATA", "eth_maxPriorityFeePerGas returned ill formed data", { context: JSON.stringify(result) });
|
|
491
|
+
maxPriorityFeePerGas = BigInt(result);
|
|
492
|
+
} catch (err) {
|
|
493
|
+
if (!isMethodNotSupportedError(err)) throw err;
|
|
494
|
+
}
|
|
495
|
+
let maxFeePerGas;
|
|
496
|
+
let priorityFee;
|
|
497
|
+
if (gasPrice != null && maxPriorityFeePerGas != null) {
|
|
498
|
+
maxFeePerGas = scaleBigIntByGasLevel(gasPrice, gasLevel);
|
|
499
|
+
priorityFee = scaleBigIntByGasLevel(maxPriorityFeePerGas, gasLevel);
|
|
500
|
+
} else if (gasPrice != null) {
|
|
501
|
+
maxFeePerGas = scaleBigIntByGasLevel(gasPrice, gasLevel);
|
|
502
|
+
priorityFee = maxFeePerGas;
|
|
503
|
+
} else {
|
|
504
|
+
maxFeePerGas = scaleBigIntByGasLevel(1000000000n, gasLevel);
|
|
505
|
+
priorityFee = maxFeePerGas;
|
|
506
|
+
}
|
|
507
|
+
if (maxFeePerGas === 0n) maxFeePerGas = 1n;
|
|
508
|
+
if (priorityFee === 0n) priorityFee = 1n;
|
|
509
|
+
if (priorityFee > maxFeePerGas) maxFeePerGas = priorityFee;
|
|
510
|
+
return [maxFeePerGas, priorityFee];
|
|
511
|
+
} catch (err) {
|
|
512
|
+
throw translateNodeError(err, "getFeeData");
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
/**
|
|
516
|
+
* Check whether an address is EIP-7702-delegated and return the delegatee
|
|
517
|
+
* address. EIP-7702-delegated accounts have bytecode of the form
|
|
518
|
+
* `0xef0100<20-byte-delegatee>` per the spec.
|
|
519
|
+
*
|
|
520
|
+
* @returns The checksummed delegatee address, or `null` if not delegated
|
|
521
|
+
*/
|
|
522
|
+
async getDelegatedAddress(accountAddress, options) {
|
|
523
|
+
const code = (await this.getCode(accountAddress, "latest", options)).toLowerCase();
|
|
524
|
+
if (code.length === 48 && code.startsWith("0xef0100")) return getAddress(`0x${code.slice(8)}`);
|
|
525
|
+
return null;
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* Fetch the smart account's nonce from the EntryPoint contract via
|
|
529
|
+
* `eth_call`. This is the 4337 nonce (an EntryPoint-managed counter with
|
|
530
|
+
* 192-bit parallel keys), not the EOA `eth_getTransactionCount`.
|
|
531
|
+
*
|
|
532
|
+
* @param entryPoint - EntryPoint contract address
|
|
533
|
+
* @param account - Smart account address
|
|
534
|
+
* @param key - Nonce key as a `bigint` (default `0n`). Different keys allow
|
|
535
|
+
* parallel nonce channels. `bigint` so the full `uint192` range is
|
|
536
|
+
* representable (a JS `number` would cap at 2^53−1).
|
|
537
|
+
*/
|
|
538
|
+
async getEntryPointNonce(entryPoint, account, key = 0n, options) {
|
|
539
|
+
const data = "0x35567e1a" + AbiCoder.defaultAbiCoder().encode(["address", "uint192"], [account, key]).slice(2);
|
|
540
|
+
const callResult = await this.call({
|
|
541
|
+
from: ZERO_ADDRESS,
|
|
542
|
+
to: entryPoint,
|
|
543
|
+
data
|
|
544
|
+
}, "latest", void 0, options);
|
|
545
|
+
try {
|
|
546
|
+
return BigInt(callResult);
|
|
547
|
+
} catch (err) {
|
|
548
|
+
throw new AbstractionKitError("BAD_DATA", "getNonce returned ill formed data", {
|
|
549
|
+
cause: ensureError(err),
|
|
550
|
+
context: callResult
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
/**
|
|
555
|
+
* Get the EntryPoint deposit balance for an address.
|
|
556
|
+
*
|
|
557
|
+
* @returns The deposit balance in wei as a bigint
|
|
558
|
+
*/
|
|
559
|
+
async getEntryPointDeposit(address, entryPoint, options) {
|
|
560
|
+
return (await this.getEntryPointDepositInfo(address, entryPoint, options)).deposit;
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* Get the full {@link DepositInfo} for an address from the EntryPoint
|
|
564
|
+
* contract.
|
|
565
|
+
*/
|
|
566
|
+
async getEntryPointDepositInfo(address, entryPoint, options) {
|
|
567
|
+
const getDepositInfoSelector = "0x5287ce12";
|
|
568
|
+
const abiCoder = AbiCoder.defaultAbiCoder();
|
|
569
|
+
const data = getDepositInfoSelector + abiCoder.encode(["address"], [address]).slice(2);
|
|
570
|
+
const callResult = await this.call({
|
|
571
|
+
from: ZERO_ADDRESS,
|
|
572
|
+
to: entryPoint,
|
|
573
|
+
data
|
|
574
|
+
}, "latest", void 0, options);
|
|
575
|
+
try {
|
|
576
|
+
const decoded = abiCoder.decode([
|
|
577
|
+
"uint256",
|
|
578
|
+
"bool",
|
|
579
|
+
"uint112",
|
|
580
|
+
"uint32",
|
|
581
|
+
"uint48"
|
|
582
|
+
], callResult);
|
|
583
|
+
if (decoded.length !== 5) throw new AbstractionKitError("BAD_DATA", "getDepositInfo returned ill formed data", { context: JSON.stringify(decoded) });
|
|
584
|
+
return {
|
|
585
|
+
deposit: BigInt(decoded[0]),
|
|
586
|
+
staked: Boolean(decoded[1]),
|
|
587
|
+
stake: BigInt(decoded[2]),
|
|
588
|
+
unstakeDelaySec: BigInt(decoded[3]),
|
|
589
|
+
withdrawTime: BigInt(decoded[4])
|
|
590
|
+
};
|
|
591
|
+
} catch (err) {
|
|
592
|
+
if (err instanceof AbstractionKitError) throw err;
|
|
593
|
+
throw new AbstractionKitError("BAD_DATA", "getDepositInfo returned ill formed data", { cause: ensureError(err) });
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
};
|
|
341
597
|
/**
|
|
342
|
-
*
|
|
343
|
-
*
|
|
344
|
-
* separate paymaster fields. Returns `0x` when no paymaster is set.
|
|
598
|
+
* Apply a fractional `gasLevel` multiplier (e.g. 1.2 = 120%) to a `bigint`
|
|
599
|
+
* gas price without going through JS number precision.
|
|
345
600
|
*
|
|
346
|
-
*
|
|
347
|
-
*
|
|
348
|
-
*
|
|
349
|
-
*
|
|
601
|
+
* The naive `BigInt(Math.ceil(Number(value) * gasLevel))` truncates any
|
|
602
|
+
* `value` above `Number.MAX_SAFE_INTEGER` (2^53 − 1 ≈ 9.0 × 10^15 wei),
|
|
603
|
+
* which is well within the range a chain can legitimately report
|
|
604
|
+
* (especially on testnets, fork networks, or anomalous mainnet spikes).
|
|
350
605
|
*
|
|
351
|
-
*
|
|
352
|
-
*
|
|
353
|
-
*
|
|
606
|
+
* Approach: scale the multiplier into integer space (three-decimal precision
|
|
607
|
+
* — sufficient for `GasOption` and any reasonable custom value), do the
|
|
608
|
+
* multiplication in `BigInt`, then ceiling-divide back down. Preserves the
|
|
609
|
+
* original `Math.ceil(...)` rounding behavior bit-for-bit on small values.
|
|
354
610
|
*
|
|
355
|
-
* @
|
|
356
|
-
* @param stripV9PaymasterSig - Whether to strip the v0.9 paymaster signature suffix
|
|
357
|
-
* @returns Hex-encoded paymasterAndData
|
|
611
|
+
* @internal
|
|
358
612
|
*/
|
|
359
|
-
function
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
let paymasterAndData = useroperation.paymaster;
|
|
363
|
-
if (useroperation.paymasterVerificationGasLimit != null) paymasterAndData += abiCoder.encode(["uint128"], [useroperation.paymasterVerificationGasLimit]).slice(34);
|
|
364
|
-
if (useroperation.paymasterPostOpGasLimit != null) paymasterAndData += abiCoder.encode(["uint128"], [useroperation.paymasterPostOpGasLimit]).slice(34);
|
|
365
|
-
if (useroperation.paymasterData != null) {
|
|
366
|
-
const PAYMASTER_SIG_MAGIC = "22e325a297439656";
|
|
367
|
-
if (stripV9PaymasterSig && useroperation.paymasterData.toLowerCase().endsWith(PAYMASTER_SIG_MAGIC)) {
|
|
368
|
-
const sigLenHex = useroperation.paymasterData.slice(useroperation.paymasterData.length - 16 - 4, useroperation.paymasterData.length - 16);
|
|
369
|
-
const sigLen = parseInt(sigLenHex, 16);
|
|
370
|
-
const prefixEnd = useroperation.paymasterData.length - 16 - 4 - sigLen * 2;
|
|
371
|
-
paymasterAndData += useroperation.paymasterData.slice(0, prefixEnd).replaceAll("0x", "") + PAYMASTER_SIG_MAGIC;
|
|
372
|
-
} else paymasterAndData += useroperation.paymasterData.slice(2);
|
|
373
|
-
}
|
|
374
|
-
return paymasterAndData;
|
|
613
|
+
function scaleBigIntByGasLevel(value, gasLevel) {
|
|
614
|
+
const scale = 1000n;
|
|
615
|
+
return (value * BigInt(Math.round(gasLevel * Number(scale))) + scale - 1n) / scale;
|
|
375
616
|
}
|
|
376
617
|
/**
|
|
377
|
-
*
|
|
378
|
-
*
|
|
618
|
+
* Detect whether an error indicates the JSON-RPC method itself is not
|
|
619
|
+
* implemented by the node. Used by {@link JsonRpcNode.getFeeData} to fall
|
|
620
|
+
* back gracefully on chains that don't expose EIP-1559 fee methods, while
|
|
621
|
+
* still surfacing transport, auth, and parse errors to the caller.
|
|
379
622
|
*
|
|
380
|
-
* @
|
|
381
|
-
* @returns ABI-encoded packed UserOperation as a hex string
|
|
623
|
+
* @internal
|
|
382
624
|
*/
|
|
383
|
-
function
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
keccak256(useroperation.initCode),
|
|
388
|
-
keccak256(useroperation.callData),
|
|
389
|
-
useroperation.callGasLimit,
|
|
390
|
-
useroperation.verificationGasLimit,
|
|
391
|
-
useroperation.preVerificationGas,
|
|
392
|
-
useroperation.maxFeePerGas,
|
|
393
|
-
useroperation.maxPriorityFeePerGas,
|
|
394
|
-
keccak256(useroperation.paymasterAndData)
|
|
395
|
-
];
|
|
396
|
-
return AbiCoder.defaultAbiCoder().encode([
|
|
397
|
-
"address",
|
|
398
|
-
"uint256",
|
|
399
|
-
"bytes32",
|
|
400
|
-
"bytes32",
|
|
401
|
-
"uint256",
|
|
402
|
-
"uint256",
|
|
403
|
-
"uint256",
|
|
404
|
-
"uint256",
|
|
405
|
-
"uint256",
|
|
406
|
-
"bytes32"
|
|
407
|
-
], useroperationValuesArrayWithHashedByteValues);
|
|
625
|
+
function isMethodNotSupportedError(err) {
|
|
626
|
+
if (err?.code === -32601) return true;
|
|
627
|
+
const message = err?.message?.toLowerCase() ?? "";
|
|
628
|
+
return message.includes("method not found") || message.includes("not supported") || message.includes("unsupported");
|
|
408
629
|
}
|
|
409
630
|
/**
|
|
410
|
-
*
|
|
411
|
-
*
|
|
631
|
+
* Translate a transport-level error (or already-wrapped {@link AbstractionKitError})
|
|
632
|
+
* into the `NODE_ERROR` outer / specific inner shape used by {@link JsonRpcNode}.
|
|
412
633
|
*
|
|
413
|
-
*
|
|
414
|
-
* @
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
const abiCoder = AbiCoder.defaultAbiCoder();
|
|
418
|
-
let initCode = "0x";
|
|
419
|
-
if (useroperation.factory != null) {
|
|
420
|
-
initCode = useroperation.factory;
|
|
421
|
-
if (useroperation.factoryData != null) initCode += useroperation.factoryData.slice(2);
|
|
422
|
-
}
|
|
423
|
-
const accountGasLimits = "0x" + abiCoder.encode(["uint128"], [useroperation.verificationGasLimit]).slice(34) + abiCoder.encode(["uint128"], [useroperation.callGasLimit]).slice(34);
|
|
424
|
-
const gasFees = "0x" + abiCoder.encode(["uint128"], [useroperation.maxPriorityFeePerGas]).slice(34) + abiCoder.encode(["uint128"], [useroperation.maxFeePerGas]).slice(34);
|
|
425
|
-
const paymasterAndData = buildPaymasterAndData(useroperation);
|
|
426
|
-
const useroperationValuesArrayWithHashedByteValues = [
|
|
427
|
-
useroperation.sender,
|
|
428
|
-
useroperation.nonce,
|
|
429
|
-
keccak256(initCode),
|
|
430
|
-
keccak256(useroperation.callData),
|
|
431
|
-
accountGasLimits,
|
|
432
|
-
useroperation.preVerificationGas,
|
|
433
|
-
gasFees,
|
|
434
|
-
keccak256(paymasterAndData)
|
|
435
|
-
];
|
|
436
|
-
return abiCoder.encode([
|
|
437
|
-
"address",
|
|
438
|
-
"uint256",
|
|
439
|
-
"bytes32",
|
|
440
|
-
"bytes32",
|
|
441
|
-
"bytes32",
|
|
442
|
-
"uint256",
|
|
443
|
-
"bytes32",
|
|
444
|
-
"bytes32"
|
|
445
|
-
], useroperationValuesArrayWithHashedByteValues);
|
|
446
|
-
}
|
|
447
|
-
/**
|
|
448
|
-
* ABI-encode and pack a UserOperation for hashing (EntryPoint v0.9 format).
|
|
634
|
+
* - `AbstractionKitError` passes through unchanged (already domain-translated).
|
|
635
|
+
* - {@link ProviderRpcError} with a known JSON-RPC code → inner code from
|
|
636
|
+
* {@link JsonRpcErrorDict}.
|
|
637
|
+
* - Anything else → inner `UNKNOWN_ERROR`.
|
|
449
638
|
*
|
|
450
|
-
* @
|
|
451
|
-
* @returns ABI-encoded packed UserOperation as a hex string
|
|
639
|
+
* @internal
|
|
452
640
|
*/
|
|
453
|
-
function
|
|
454
|
-
|
|
641
|
+
function translateNodeError(err, method) {
|
|
642
|
+
if (err instanceof AbstractionKitError) return err;
|
|
643
|
+
const code = err?.code;
|
|
644
|
+
const codeString = code != null ? String(code) : "";
|
|
645
|
+
const innerCode = codeString in JsonRpcErrorDict ? JsonRpcErrorDict[codeString] : "UNKNOWN_ERROR";
|
|
646
|
+
const error = ensureError(err);
|
|
647
|
+
return new AbstractionKitError("NODE_ERROR", `node ${method} rpc call failed`, {
|
|
648
|
+
cause: new AbstractionKitError(innerCode, error.message, { errno: code }),
|
|
649
|
+
errno: code
|
|
650
|
+
});
|
|
455
651
|
}
|
|
652
|
+
//#endregion
|
|
653
|
+
//#region src/Bundler.ts
|
|
456
654
|
/**
|
|
457
|
-
*
|
|
655
|
+
* JSON-RPC client for an ERC-4337 bundler.
|
|
458
656
|
*
|
|
459
|
-
*
|
|
460
|
-
* @
|
|
657
|
+
* Accepts either a URL string (wrapped automatically in {@link HttpTransport})
|
|
658
|
+
* or any {@link Transport} — including a viem client, an EIP-1193 wallet
|
|
659
|
+
* provider, an in-process mock, or a user-composed fallback/retry transport.
|
|
660
|
+
*
|
|
661
|
+
* The class itself implements {@link Transport}, so a `Bundler` can be passed
|
|
662
|
+
* back into any other Transport position.
|
|
663
|
+
*
|
|
664
|
+
* Candide bundler endpoints:
|
|
665
|
+
* - `https://api.candide.dev/api/v3/{chainId}/{apiKey}` (authenticated)
|
|
666
|
+
* - `https://api.candide.dev/public/v3/{chainId}` (public)
|
|
667
|
+
*
|
|
668
|
+
* @example URL string (most common)
|
|
669
|
+
* ```ts
|
|
670
|
+
* const bundler = new Bundler("https://api.candide.dev/public/v3/11155111");
|
|
671
|
+
* const receipt = await bundler.getUserOperationReceipt(userOpHash);
|
|
672
|
+
* ```
|
|
673
|
+
*
|
|
674
|
+
* @example Custom transport (composed retry behavior)
|
|
675
|
+
* ```ts
|
|
676
|
+
* const retryingTransport: Transport = {
|
|
677
|
+
* async request(args, options) {
|
|
678
|
+
* for (let i = 0; i < 3; i++) {
|
|
679
|
+
* try { return await inner.request(args, options); }
|
|
680
|
+
* catch (e) { if (i === 2) throw e; await sleep(2 ** i * 100); }
|
|
681
|
+
* }
|
|
682
|
+
* },
|
|
683
|
+
* };
|
|
684
|
+
* const bundler = new Bundler(retryingTransport);
|
|
685
|
+
* ```
|
|
461
686
|
*/
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
*
|
|
467
|
-
*
|
|
468
|
-
*
|
|
469
|
-
*
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
keccak256(useroperation.callData),
|
|
483
|
-
accountGasLimits,
|
|
484
|
-
useroperation.preVerificationGas,
|
|
485
|
-
gasFees,
|
|
486
|
-
keccak256(paymasterAndData)
|
|
487
|
-
];
|
|
488
|
-
return abiCoder.encode([
|
|
489
|
-
"bytes32",
|
|
490
|
-
"address",
|
|
491
|
-
"uint256",
|
|
492
|
-
"bytes32",
|
|
493
|
-
"bytes32",
|
|
494
|
-
"bytes32",
|
|
495
|
-
"uint256",
|
|
496
|
-
"bytes32",
|
|
497
|
-
"bytes32"
|
|
498
|
-
], useroperationValuesArrayWithHashedByteValues);
|
|
499
|
-
}
|
|
500
|
-
/**
|
|
501
|
-
* Encode a function call into ABI-encoded calldata.
|
|
502
|
-
*
|
|
503
|
-
* @param functionSelector - 4-byte hex function selector (e.g., "0xa9059cbb" for ERC-20 transfer)
|
|
504
|
-
* @param functionInputAbi - Array of ABI type strings (e.g., ["address", "uint256"])
|
|
505
|
-
* @param functionInputParameters - Array of parameter values matching the ABI types
|
|
506
|
-
* @returns ABI-encoded calldata as a hex string (selector + encoded parameters)
|
|
507
|
-
*
|
|
508
|
-
* @example
|
|
509
|
-
* const transferCallData = createCallData(
|
|
510
|
-
* "0xa9059cbb",
|
|
511
|
-
* ["address", "uint256"],
|
|
512
|
-
* ["0xRecipientAddress", 1000000n],
|
|
513
|
-
* );
|
|
514
|
-
*/
|
|
515
|
-
function createCallData(functionSelector, functionInputAbi, functionInputParameters) {
|
|
516
|
-
return functionSelector + AbiCoder.defaultAbiCoder().encode(functionInputAbi, functionInputParameters).slice(2);
|
|
517
|
-
}
|
|
518
|
-
/**
|
|
519
|
-
* Send a JSON-RPC request to the specified endpoint.
|
|
520
|
-
* Automatically converts bigint values to hex strings in the request body.
|
|
521
|
-
*
|
|
522
|
-
* @param rpcUrl - The JSON-RPC endpoint URL (bundler, node, or paymaster)
|
|
523
|
-
* @param method - The JSON-RPC method name (e.g., "eth_call", "eth_sendUserOperation")
|
|
524
|
-
* @param params - The JSON-RPC parameters
|
|
525
|
-
* @param headers - Custom HTTP headers (defaults to Content-Type: application/json)
|
|
526
|
-
* @param paramsKeyName - Key name for the params field (defaults to "params")
|
|
527
|
-
* @returns The result field from the JSON-RPC response
|
|
528
|
-
* @throws AbstractionKitError if the RPC returns an error
|
|
529
|
-
*/
|
|
530
|
-
async function sendJsonRpcRequest(rpcUrl, method, params, headers = { "Content-Type": "application/json" }, paramsKeyName = "params") {
|
|
531
|
-
const requestOptions = {
|
|
532
|
-
method: "POST",
|
|
533
|
-
headers,
|
|
534
|
-
body: JSON.stringify({
|
|
535
|
-
method,
|
|
536
|
-
[paramsKeyName]: params,
|
|
537
|
-
id: Date.now(),
|
|
538
|
-
jsonrpc: "2.0"
|
|
539
|
-
}, (_key, value) => typeof value === "bigint" ? `0x${value.toString(16)}` : value),
|
|
540
|
-
redirect: "follow"
|
|
541
|
-
};
|
|
542
|
-
const response = await (await fetch(rpcUrl, requestOptions)).json();
|
|
543
|
-
if ("result" in response) return response.result;
|
|
544
|
-
else if ("simulation_results" in response) return response.simulation_results;
|
|
545
|
-
else {
|
|
546
|
-
const err = response.error;
|
|
547
|
-
const codeString = String(err.code);
|
|
548
|
-
if (codeString in BundlerErrorCodeDict) throw new AbstractionKitError(BundlerErrorCodeDict[codeString], err.message, {
|
|
549
|
-
errno: err.code,
|
|
550
|
-
context: {
|
|
551
|
-
url: rpcUrl,
|
|
552
|
-
requestOptions: JSON.stringify(requestOptions)
|
|
553
|
-
}
|
|
554
|
-
});
|
|
555
|
-
else throw new AbstractionKitError("UNKNOWN_ERROR", err.message, {
|
|
556
|
-
errno: err.code,
|
|
557
|
-
context: {
|
|
558
|
-
url: rpcUrl,
|
|
559
|
-
requestOptions: JSON.stringify(requestOptions)
|
|
560
|
-
}
|
|
561
|
-
});
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
/**
|
|
565
|
-
* Get a 4-byte function selector from a Solidity-style function signature.
|
|
566
|
-
* Computed as the first 4 bytes of keccak256(signature).
|
|
567
|
-
* @param functionSignature - Solidity-style function signature, e.g. "mint(address)"
|
|
568
|
-
* @returns Function selector as a 0x-prefixed 10-character hex string
|
|
569
|
-
*
|
|
570
|
-
* @example
|
|
571
|
-
* getFunctionSelector("getNonce(address,uint192)"); // "0x35567e1a"
|
|
572
|
-
*/
|
|
573
|
-
function getFunctionSelector(functionSignature) {
|
|
574
|
-
return id(functionSignature).slice(0, 10);
|
|
575
|
-
}
|
|
576
|
-
/**
|
|
577
|
-
* Fetch the account's nonce from the EntryPoint contract.
|
|
578
|
-
*
|
|
579
|
-
* @param rpcUrl - Ethereum JSON-RPC node URL
|
|
580
|
-
* @param entryPoint - EntryPoint contract address
|
|
581
|
-
* @param account - Smart account address to query
|
|
582
|
-
* @param key - Nonce key (default 0). Different keys allow parallel nonce channels.
|
|
583
|
-
* @returns The current nonce as a bigint
|
|
584
|
-
* @throws AbstractionKitError with code "BAD_DATA" if the nonce call fails
|
|
585
|
-
*/
|
|
586
|
-
async function fetchAccountNonce(rpcUrl, entryPoint, account, key = 0) {
|
|
587
|
-
const params = [{
|
|
588
|
-
from: "0x0000000000000000000000000000000000000000",
|
|
589
|
-
to: entryPoint,
|
|
590
|
-
data: createCallData(getFunctionSelector("getNonce(address,uint192)"), ["address", "uint192"], [account, key])
|
|
591
|
-
}, "latest"];
|
|
592
|
-
try {
|
|
593
|
-
const nonce = await sendJsonRpcRequest(rpcUrl, "eth_call", params);
|
|
594
|
-
if (typeof nonce === "string") try {
|
|
595
|
-
return BigInt(nonce);
|
|
596
|
-
} catch (err) {
|
|
597
|
-
throw new AbstractionKitError("BAD_DATA", "getNonce returned ill formed data", { cause: ensureError(err) });
|
|
598
|
-
}
|
|
599
|
-
else throw new AbstractionKitError("BAD_DATA", "getNonce returned ill formed data", { context: JSON.stringify(nonce) });
|
|
600
|
-
} catch (err) {
|
|
601
|
-
throw new AbstractionKitError("BAD_DATA", "getNonce failed", { cause: ensureError(err) });
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
/**
|
|
605
|
-
* Fetch current gas prices from a JSON-RPC node.
|
|
606
|
-
* Applies a gas level multiplier to adjust for faster or cheaper inclusion.
|
|
607
|
-
*
|
|
608
|
-
* @param provideRpc - Ethereum JSON-RPC node URL
|
|
609
|
-
* @param gasLevel - Gas price multiplier (default: GasOption.Medium = 1.2x)
|
|
610
|
-
* @returns A tuple of [maxFeePerGas, maxPriorityFeePerGas] as bigints
|
|
611
|
-
*/
|
|
612
|
-
async function fetchGasPrice(provideRpc, gasLevel = GasOption.Medium) {
|
|
613
|
-
try {
|
|
614
|
-
const feeData = await new JsonRpcProvider(provideRpc).getFeeData();
|
|
615
|
-
let maxFeePerGas;
|
|
616
|
-
let maxPriorityFeePerGas;
|
|
617
|
-
if (feeData.maxFeePerGas != null && feeData.maxPriorityFeePerGas != null) {
|
|
618
|
-
maxFeePerGas = BigInt(Math.ceil(Number(feeData.maxFeePerGas) * gasLevel));
|
|
619
|
-
maxPriorityFeePerGas = BigInt(Math.ceil(Number(feeData.maxPriorityFeePerGas) * gasLevel));
|
|
620
|
-
} else if (feeData.gasPrice != null) {
|
|
621
|
-
maxFeePerGas = BigInt(Math.ceil(Number(feeData.gasPrice) * gasLevel));
|
|
622
|
-
maxPriorityFeePerGas = maxFeePerGas;
|
|
623
|
-
} else {
|
|
624
|
-
maxFeePerGas = BigInt(Math.ceil(1e9 * gasLevel));
|
|
625
|
-
maxPriorityFeePerGas = maxFeePerGas;
|
|
626
|
-
}
|
|
627
|
-
if (maxFeePerGas === 0n) maxFeePerGas = 1n;
|
|
628
|
-
if (maxPriorityFeePerGas === 0n) maxPriorityFeePerGas = 1n;
|
|
629
|
-
return [maxFeePerGas, maxPriorityFeePerGas];
|
|
630
|
-
} catch (err) {
|
|
631
|
-
throw new AbstractionKitError("BAD_DATA", "fetching gas prices from node failed.", { cause: ensureError(err) });
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
/**
|
|
635
|
-
* Fetch current gas prices from the Polygon Gas Station API.
|
|
636
|
-
*
|
|
637
|
-
* @param polygonChain - Target Polygon chain (Mainnet, Amoy, etc.)
|
|
638
|
-
* @param gasLevel - Gas price level (Slow, Medium, Fast)
|
|
639
|
-
* @returns A tuple of [maxFeePerGas, maxPriorityFeePerGas] as bigints
|
|
640
|
-
*/
|
|
641
|
-
async function fetchGasPricePolygon(polygonChain, gasLevel = GasOption.Medium) {
|
|
642
|
-
const gasStationUrl = `https://gasstation.polygon.technology/${polygonChain}`;
|
|
643
|
-
try {
|
|
644
|
-
const response = await (await fetch(gasStationUrl)).json();
|
|
645
|
-
let gasPrice;
|
|
646
|
-
if (gasLevel === GasOption.Slow) gasPrice = response.safeLow;
|
|
647
|
-
else if (gasLevel === GasOption.Medium) gasPrice = response.standard;
|
|
648
|
-
else gasPrice = response.fast;
|
|
649
|
-
let maxFeePerGas = BigInt(Math.ceil(Number(gasPrice.maxFee) * 1e9));
|
|
650
|
-
let maxPriorityFeePerGas = BigInt(Math.ceil(Number(gasPrice.maxPriorityFee) * 1e9));
|
|
651
|
-
if (maxFeePerGas === 0n) maxFeePerGas = 1n;
|
|
652
|
-
if (maxPriorityFeePerGas === 0n) maxPriorityFeePerGas = 1n;
|
|
653
|
-
return [maxFeePerGas, maxPriorityFeePerGas];
|
|
654
|
-
} catch (err) {
|
|
655
|
-
const error = ensureError(err);
|
|
656
|
-
throw new AbstractionKitError("BAD_DATA", `fetching gas prices from ${gasStationUrl} failed.`, { cause: error });
|
|
657
|
-
}
|
|
658
|
-
}
|
|
659
|
-
/**
|
|
660
|
-
* Calculate the maximum gas cost (in wei) that a UserOperation could consume.
|
|
661
|
-
* Uses different formulas for v0.6 (with paymaster multiplier) and v0.7+ UserOperations.
|
|
662
|
-
*
|
|
663
|
-
* @param useroperation - The UserOperation to calculate the max gas cost for
|
|
664
|
-
* @returns Maximum possible gas cost in wei as a bigint
|
|
665
|
-
*/
|
|
666
|
-
function calculateUserOperationMaxGasCost(useroperation) {
|
|
667
|
-
if ("initCode" in useroperation) {
|
|
668
|
-
const mul = useroperation.paymasterAndData !== "0x" && useroperation.paymasterAndData != null ? 3n : 0n;
|
|
669
|
-
return (useroperation.callGasLimit + useroperation.verificationGasLimit * mul + useroperation.preVerificationGas) * useroperation.maxFeePerGas;
|
|
670
|
-
} else return (useroperation.verificationGasLimit + useroperation.callGasLimit + (useroperation.paymasterVerificationGasLimit ?? 0n) + (useroperation.paymasterPostOpGasLimit ?? 0n) + useroperation.preVerificationGas) * useroperation.maxFeePerGas;
|
|
671
|
-
}
|
|
672
|
-
/**
|
|
673
|
-
* Get the deposit balance of an address in the EntryPoint contract.
|
|
674
|
-
*
|
|
675
|
-
* @param nodeRpcUrl - Ethereum JSON-RPC node URL
|
|
676
|
-
* @param address - Address to query the deposit for
|
|
677
|
-
* @param entrypointAddress - EntryPoint contract address
|
|
678
|
-
* @returns The deposit balance as a bigint
|
|
679
|
-
*/
|
|
680
|
-
async function getBalanceOf(nodeRpcUrl, address, entrypointAddress) {
|
|
681
|
-
return (await getDepositInfo(nodeRpcUrl, address, entrypointAddress)).deposit;
|
|
682
|
-
}
|
|
683
|
-
/**
|
|
684
|
-
* Get the full deposit info of an address from the EntryPoint contract.
|
|
685
|
-
*
|
|
686
|
-
* @param nodeRpcUrl - Ethereum JSON-RPC node URL
|
|
687
|
-
* @param address - Address to query
|
|
688
|
-
* @param entrypointAddress - EntryPoint contract address
|
|
689
|
-
* @returns DepositInfo with deposit, staked, stake, unstakeDelaySec, withdrawTime
|
|
690
|
-
*/
|
|
691
|
-
async function getDepositInfo(nodeRpcUrl, address, entrypointAddress) {
|
|
692
|
-
const params = {
|
|
693
|
-
from: "0x0000000000000000000000000000000000000000",
|
|
694
|
-
to: entrypointAddress,
|
|
695
|
-
data: createCallData("0x5287ce12", ["address"], [address])
|
|
696
|
-
};
|
|
697
|
-
try {
|
|
698
|
-
const depositInfoRequestResult = await sendEthCallRequest(nodeRpcUrl, params, "latest");
|
|
699
|
-
const decodedCalldata = AbiCoder.defaultAbiCoder().decode([
|
|
700
|
-
"uint256",
|
|
701
|
-
"bool",
|
|
702
|
-
"uint112",
|
|
703
|
-
"uint32",
|
|
704
|
-
"uint48"
|
|
705
|
-
], depositInfoRequestResult);
|
|
706
|
-
if (decodedCalldata.length === 5) try {
|
|
707
|
-
return {
|
|
708
|
-
deposit: BigInt(decodedCalldata[0]),
|
|
709
|
-
staked: Boolean(decodedCalldata[1]),
|
|
710
|
-
stake: BigInt(decodedCalldata[2]),
|
|
711
|
-
unstakeDelaySec: BigInt(decodedCalldata[3]),
|
|
712
|
-
withdrawTime: BigInt(decodedCalldata[4])
|
|
713
|
-
};
|
|
714
|
-
} catch (err) {
|
|
715
|
-
throw new AbstractionKitError("BAD_DATA", "getDepositInfo returned ill formed data", { cause: ensureError(err) });
|
|
716
|
-
}
|
|
717
|
-
else throw new AbstractionKitError("BAD_DATA", "getDepositInfo returned ill formed data", { context: JSON.stringify(decodedCalldata) });
|
|
718
|
-
} catch (err) {
|
|
719
|
-
throw new AbstractionKitError("BAD_DATA", "getDepositInfo failed", { cause: ensureError(err) });
|
|
720
|
-
}
|
|
721
|
-
}
|
|
722
|
-
/**
|
|
723
|
-
* Send an eth_call JSON-RPC request with optional state overrides.
|
|
724
|
-
*
|
|
725
|
-
* @param nodeRpcUrl - Ethereum JSON-RPC node URL
|
|
726
|
-
* @param ethCallTransaction - The call transaction parameters
|
|
727
|
-
* @param blockNumber - Block number or "latest"
|
|
728
|
-
* @param stateOverrides - Optional state overrides for the call
|
|
729
|
-
* @returns The call result as a hex string
|
|
730
|
-
*/
|
|
731
|
-
async function sendEthCallRequest(nodeRpcUrl, ethCallTransaction, blockNumber, stateOverrides) {
|
|
732
|
-
let params = [];
|
|
733
|
-
if (stateOverrides == null) params = [ethCallTransaction, blockNumber];
|
|
734
|
-
else params = [
|
|
735
|
-
ethCallTransaction,
|
|
736
|
-
blockNumber,
|
|
737
|
-
stateOverrides
|
|
738
|
-
];
|
|
739
|
-
try {
|
|
740
|
-
const data = await sendJsonRpcRequest(nodeRpcUrl, "eth_call", params);
|
|
741
|
-
if (typeof data === "string") try {
|
|
742
|
-
return data;
|
|
743
|
-
} catch (err) {
|
|
744
|
-
throw new AbstractionKitError("BAD_DATA", "eth_call returned ill formed data", { cause: ensureError(err) });
|
|
745
|
-
}
|
|
746
|
-
else throw new AbstractionKitError("BAD_DATA", "eth_call returned ill formed data", { context: JSON.stringify(data) });
|
|
747
|
-
} catch (err) {
|
|
748
|
-
throw new AbstractionKitError("BAD_DATA", "eth_call failed", { cause: ensureError(err) });
|
|
687
|
+
var Bundler = class Bundler {
|
|
688
|
+
/**
|
|
689
|
+
* The raw transport the user passed in (or {@link HttpTransport} when a URL
|
|
690
|
+
* string was passed). Exposed for introspection — reading `.url`,
|
|
691
|
+
* `isHttpTransport(...)` checks, passing it back into another service.
|
|
692
|
+
*
|
|
693
|
+
* Calls made directly on this field (`bundler.transport.request(...)`) go
|
|
694
|
+
* to the raw transport and skip SDK-level behavior like bigint param
|
|
695
|
+
* normalization. For SDK-pipeline behavior, use {@link Bundler.request} or
|
|
696
|
+
* the typed methods.
|
|
697
|
+
*/
|
|
698
|
+
transport;
|
|
699
|
+
/** Normalizing wrapper around {@link transport}, used for every SDK-outbound call. */
|
|
700
|
+
outbound;
|
|
701
|
+
/**
|
|
702
|
+
* @param rpc - Bundler JSON-RPC endpoint URL, or any {@link Transport}.
|
|
703
|
+
*/
|
|
704
|
+
constructor(rpc) {
|
|
705
|
+
this.transport = typeof rpc === "string" ? new HttpTransport(rpc) : rpc;
|
|
706
|
+
this.outbound = normalizingTransport(this.transport);
|
|
749
707
|
}
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
*
|
|
753
|
-
*
|
|
754
|
-
*
|
|
755
|
-
*
|
|
756
|
-
* @param
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
const params = [contractAddress, blockNumber];
|
|
761
|
-
try {
|
|
762
|
-
const data = await sendJsonRpcRequest(nodeRpcUrl, "eth_getCode", params);
|
|
763
|
-
if (typeof data === "string") try {
|
|
764
|
-
return data;
|
|
765
|
-
} catch (err) {
|
|
766
|
-
throw new AbstractionKitError("BAD_DATA", "eth_getCode returned ill formed data", { cause: ensureError(err) });
|
|
767
|
-
}
|
|
768
|
-
else throw new AbstractionKitError("BAD_DATA", "eth_getCode returned ill formed data", { context: JSON.stringify(data) });
|
|
769
|
-
} catch (err) {
|
|
770
|
-
throw new AbstractionKitError("BAD_DATA", "eth_getCode failed", { cause: ensureError(err) });
|
|
708
|
+
/**
|
|
709
|
+
* Normalize any acceptable input into a `Bundler`. When the input is
|
|
710
|
+
* already a `Bundler` instance, it is returned by reference (so a user's
|
|
711
|
+
* pre-constructed Bundler is never re-wrapped and its transport is
|
|
712
|
+
* reused for follow-up calls like {@link SendUseroperationResponse.included}).
|
|
713
|
+
*
|
|
714
|
+
* @param input - URL string, Transport, or existing Bundler
|
|
715
|
+
*/
|
|
716
|
+
static from(input) {
|
|
717
|
+
return input instanceof Bundler ? input : new Bundler(input);
|
|
771
718
|
}
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
*
|
|
775
|
-
*
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
* @returns The checksummed delegatee address, or `null` if not delegated
|
|
780
|
-
*/
|
|
781
|
-
async function getDelegatedAddress(accountAddress, providerRpc) {
|
|
782
|
-
const code = (await sendEthGetCodeRequest(providerRpc, accountAddress, "latest")).toLowerCase();
|
|
783
|
-
if (code.length === 48 && code.startsWith("0xef0100")) return getAddress(`0x${code.slice(8)}`);
|
|
784
|
-
return null;
|
|
785
|
-
}
|
|
786
|
-
/**
|
|
787
|
-
* Fetch gas prices using either the Polygon Gas Station or a standard JSON-RPC node.
|
|
788
|
-
*
|
|
789
|
-
* @param providerRpc - Ethereum JSON-RPC node URL (used if polygonGasStation is null)
|
|
790
|
-
* @param polygonGasStation - Polygon chain to use for gas station (takes priority)
|
|
791
|
-
* @param gasLevel - Gas price multiplier (default: GasOption.Medium)
|
|
792
|
-
* @returns A tuple of [maxFeePerGas, maxPriorityFeePerGas] as bigints
|
|
793
|
-
*/
|
|
794
|
-
async function handlefetchGasPrice(providerRpc, polygonGasStation, gasLevel = GasOption.Medium) {
|
|
795
|
-
let maxFeePerGas;
|
|
796
|
-
let maxPriorityFeePerGas;
|
|
797
|
-
if (polygonGasStation != null) [maxFeePerGas, maxPriorityFeePerGas] = await fetchGasPricePolygon(polygonGasStation, gasLevel);
|
|
798
|
-
else if (providerRpc != null) [maxFeePerGas, maxPriorityFeePerGas] = await fetchGasPrice(providerRpc, gasLevel);
|
|
799
|
-
else throw new AbstractionKitError("BAD_DATA", "providerRpc can't be null if maxFeePerGas and maxPriorityFeePerGas are not overridden");
|
|
800
|
-
return [maxFeePerGas, maxPriorityFeePerGas];
|
|
801
|
-
}
|
|
802
|
-
//#endregion
|
|
803
|
-
//#region src/Bundler.ts
|
|
804
|
-
/**
|
|
805
|
-
* JSON-RPC client for an ERC-4337 bundler.
|
|
806
|
-
*
|
|
807
|
-
* Candide bundler endpoints:
|
|
808
|
-
* - `https://api.candide.dev/api/v3/{chainId}/{apiKey}` (authenticated)
|
|
809
|
-
* - `https://api.candide.dev/public/v3/{chainId}` (public)
|
|
810
|
-
*
|
|
811
|
-
* @example
|
|
812
|
-
* const bundler = new Bundler("https://api.candide.dev/public/v3/11155111");
|
|
813
|
-
* const receipt = await bundler.getUserOperationReceipt(userOpHash);
|
|
814
|
-
*/
|
|
815
|
-
var Bundler = class {
|
|
816
|
-
rpcUrl;
|
|
817
|
-
/** @param rpcUrl - The bundler JSON-RPC endpoint URL */
|
|
818
|
-
constructor(rpcUrl) {
|
|
819
|
-
this.rpcUrl = rpcUrl;
|
|
719
|
+
/**
|
|
720
|
+
* Transport delegate. Forwards directly to the underlying
|
|
721
|
+
* {@link Transport.request}. Lets a `Bundler` itself slot into any other
|
|
722
|
+
* transport position.
|
|
723
|
+
*/
|
|
724
|
+
request(args, options) {
|
|
725
|
+
return this.outbound.request(args, options);
|
|
820
726
|
}
|
|
821
727
|
/**
|
|
822
728
|
* Get the bundler's chain ID.
|
|
@@ -824,11 +730,11 @@ var Bundler = class {
|
|
|
824
730
|
*/
|
|
825
731
|
async chainId() {
|
|
826
732
|
try {
|
|
827
|
-
const chainId = await
|
|
828
|
-
if (typeof chainId
|
|
829
|
-
|
|
733
|
+
const chainId = await this.outbound.request({ method: "eth_chainId" });
|
|
734
|
+
if (typeof chainId !== "string") throw new AbstractionKitError("BAD_DATA", "bundler eth_chainId rpc call failed");
|
|
735
|
+
return chainId;
|
|
830
736
|
} catch (err) {
|
|
831
|
-
throw
|
|
737
|
+
throw translateBundlerError(err, "eth_chainId");
|
|
832
738
|
}
|
|
833
739
|
}
|
|
834
740
|
/**
|
|
@@ -837,9 +743,9 @@ var Bundler = class {
|
|
|
837
743
|
*/
|
|
838
744
|
async supportedEntryPoints() {
|
|
839
745
|
try {
|
|
840
|
-
return await
|
|
746
|
+
return await this.outbound.request({ method: "eth_supportedEntryPoints" });
|
|
841
747
|
} catch (err) {
|
|
842
|
-
throw
|
|
748
|
+
throw translateBundlerError(err, "eth_supportedEntryPoints");
|
|
843
749
|
}
|
|
844
750
|
}
|
|
845
751
|
/**
|
|
@@ -851,14 +757,15 @@ var Bundler = class {
|
|
|
851
757
|
*/
|
|
852
758
|
async estimateUserOperationGas(useroperation, entrypointAddress, state_override_set) {
|
|
853
759
|
try {
|
|
854
|
-
|
|
855
|
-
if (typeof state_override_set === "undefined") jsonRpcResult = await sendJsonRpcRequest(this.rpcUrl, "eth_estimateUserOperationGas", [useroperation, entrypointAddress]);
|
|
856
|
-
else jsonRpcResult = await sendJsonRpcRequest(this.rpcUrl, "eth_estimateUserOperationGas", [
|
|
760
|
+
const params = state_override_set == null ? [useroperation, entrypointAddress] : [
|
|
857
761
|
useroperation,
|
|
858
762
|
entrypointAddress,
|
|
859
763
|
state_override_set
|
|
860
|
-
]
|
|
861
|
-
const res =
|
|
764
|
+
];
|
|
765
|
+
const res = await this.outbound.request({
|
|
766
|
+
method: "eth_estimateUserOperationGas",
|
|
767
|
+
params
|
|
768
|
+
});
|
|
862
769
|
const gasEstimationResult = {
|
|
863
770
|
callGasLimit: BigInt(res.callGasLimit),
|
|
864
771
|
preVerificationGas: BigInt(res.preVerificationGas),
|
|
@@ -868,7 +775,7 @@ var Bundler = class {
|
|
|
868
775
|
if (res.paymasterPostOpGasLimit != null) gasEstimationResult.paymasterPostOpGasLimit = BigInt(res.paymasterPostOpGasLimit);
|
|
869
776
|
return gasEstimationResult;
|
|
870
777
|
} catch (err) {
|
|
871
|
-
throw
|
|
778
|
+
throw translateBundlerError(err, "eth_estimateUserOperationGas");
|
|
872
779
|
}
|
|
873
780
|
}
|
|
874
781
|
/**
|
|
@@ -879,9 +786,12 @@ var Bundler = class {
|
|
|
879
786
|
*/
|
|
880
787
|
async sendUserOperation(useroperation, entrypointAddress) {
|
|
881
788
|
try {
|
|
882
|
-
return await
|
|
789
|
+
return await this.outbound.request({
|
|
790
|
+
method: "eth_sendUserOperation",
|
|
791
|
+
params: [useroperation, entrypointAddress]
|
|
792
|
+
});
|
|
883
793
|
} catch (err) {
|
|
884
|
-
throw
|
|
794
|
+
throw translateBundlerError(err, "eth_sendUserOperation");
|
|
885
795
|
}
|
|
886
796
|
}
|
|
887
797
|
/**
|
|
@@ -891,31 +801,31 @@ var Bundler = class {
|
|
|
891
801
|
*/
|
|
892
802
|
async getUserOperationReceipt(useroperationhash) {
|
|
893
803
|
try {
|
|
894
|
-
const
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
...res.receipt,
|
|
898
|
-
blockNumber: BigInt(res.receipt.blockNumber),
|
|
899
|
-
cumulativeGasUsed: BigInt(res.receipt.cumulativeGasUsed),
|
|
900
|
-
gasUsed: BigInt(res.receipt.gasUsed),
|
|
901
|
-
transactionIndex: BigInt(res.receipt.transactionIndex),
|
|
902
|
-
effectiveGasPrice: res.receipt.effectiveGasPrice == null ? void 0 : BigInt(res.receipt.effectiveGasPrice),
|
|
903
|
-
logs: JSON.stringify(res.receipt.logs)
|
|
904
|
-
};
|
|
905
|
-
return {
|
|
906
|
-
...res,
|
|
907
|
-
nonce: BigInt(res.nonce),
|
|
908
|
-
actualGasCost: BigInt(res.actualGasCost),
|
|
909
|
-
actualGasUsed: BigInt(res.actualGasUsed),
|
|
910
|
-
logs: JSON.stringify(res.logs),
|
|
911
|
-
receipt: userOperationReceipt
|
|
912
|
-
};
|
|
913
|
-
} else return null;
|
|
914
|
-
} catch (err) {
|
|
915
|
-
throw new AbstractionKitError("BUNDLER_ERROR", "bundler eth_getUserOperationReceipt rpc call failed", {
|
|
916
|
-
cause: ensureError(err),
|
|
917
|
-
context: { useroperationhash }
|
|
804
|
+
const jsonRpcResult = await this.outbound.request({
|
|
805
|
+
method: "eth_getUserOperationReceipt",
|
|
806
|
+
params: [useroperationhash]
|
|
918
807
|
});
|
|
808
|
+
if (jsonRpcResult == null) return null;
|
|
809
|
+
const res = jsonRpcResult;
|
|
810
|
+
const userOperationReceipt = {
|
|
811
|
+
...res.receipt,
|
|
812
|
+
blockNumber: BigInt(res.receipt.blockNumber),
|
|
813
|
+
cumulativeGasUsed: BigInt(res.receipt.cumulativeGasUsed),
|
|
814
|
+
gasUsed: BigInt(res.receipt.gasUsed),
|
|
815
|
+
transactionIndex: BigInt(res.receipt.transactionIndex),
|
|
816
|
+
effectiveGasPrice: res.receipt.effectiveGasPrice == null ? void 0 : BigInt(res.receipt.effectiveGasPrice),
|
|
817
|
+
logs: JSON.stringify(res.receipt.logs)
|
|
818
|
+
};
|
|
819
|
+
return {
|
|
820
|
+
...res,
|
|
821
|
+
nonce: BigInt(res.nonce),
|
|
822
|
+
actualGasCost: BigInt(res.actualGasCost),
|
|
823
|
+
actualGasUsed: BigInt(res.actualGasUsed),
|
|
824
|
+
logs: JSON.stringify(res.logs),
|
|
825
|
+
receipt: userOperationReceipt
|
|
826
|
+
};
|
|
827
|
+
} catch (err) {
|
|
828
|
+
throw translateBundlerError(err, "eth_getUserOperationReceipt", { useroperationhash });
|
|
919
829
|
}
|
|
920
830
|
}
|
|
921
831
|
/**
|
|
@@ -925,43 +835,233 @@ var Bundler = class {
|
|
|
925
835
|
*/
|
|
926
836
|
async getUserOperationByHash(useroperationhash) {
|
|
927
837
|
try {
|
|
928
|
-
const
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
838
|
+
const jsonRpcResult = await this.outbound.request({
|
|
839
|
+
method: "eth_getUserOperationByHash",
|
|
840
|
+
params: [useroperationhash]
|
|
841
|
+
});
|
|
842
|
+
if (jsonRpcResult == null) return null;
|
|
843
|
+
return {
|
|
844
|
+
...jsonRpcResult,
|
|
845
|
+
blockNumber: jsonRpcResult.blockNumber == null ? null : BigInt(jsonRpcResult.blockNumber)
|
|
932
846
|
};
|
|
933
|
-
else return null;
|
|
934
847
|
} catch (err) {
|
|
935
|
-
throw
|
|
936
|
-
cause: ensureError(err),
|
|
937
|
-
context: { useroperationhash }
|
|
938
|
-
});
|
|
848
|
+
throw translateBundlerError(err, "eth_getUserOperationByHash", { useroperationhash });
|
|
939
849
|
}
|
|
940
850
|
}
|
|
941
851
|
};
|
|
942
|
-
//#endregion
|
|
943
|
-
//#region src/signer/negotiate.ts
|
|
944
852
|
/**
|
|
945
|
-
*
|
|
946
|
-
*
|
|
947
|
-
*
|
|
853
|
+
* Translate a transport-level error (or already-wrapped
|
|
854
|
+
* {@link AbstractionKitError}) into the `BUNDLER_ERROR` outer / specific
|
|
855
|
+
* 4337-code inner shape used by {@link Bundler}.
|
|
948
856
|
*
|
|
949
|
-
*
|
|
950
|
-
*
|
|
951
|
-
*
|
|
857
|
+
* - `AbstractionKitError` passes through unchanged (already domain-translated).
|
|
858
|
+
* - {@link ProviderRpcError} with a known 4337 code → inner code from
|
|
859
|
+
* {@link BundlerErrorCodeDict}.
|
|
860
|
+
* - Anything else → inner `UNKNOWN_ERROR`.
|
|
861
|
+
*
|
|
862
|
+
* @internal
|
|
952
863
|
*/
|
|
953
|
-
function
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
864
|
+
function translateBundlerError(err, method, context) {
|
|
865
|
+
if (err instanceof AbstractionKitError) {
|
|
866
|
+
if (err.code === "BUNDLER_ERROR") return err;
|
|
867
|
+
return new AbstractionKitError("BUNDLER_ERROR", `bundler ${method} rpc call failed`, {
|
|
868
|
+
cause: err,
|
|
869
|
+
errno: err.errno,
|
|
870
|
+
context
|
|
871
|
+
});
|
|
872
|
+
}
|
|
873
|
+
const code = err?.code;
|
|
874
|
+
const codeString = code != null ? String(code) : "";
|
|
875
|
+
const innerCode = codeString in BundlerErrorCodeDict ? BundlerErrorCodeDict[codeString] : "UNKNOWN_ERROR";
|
|
876
|
+
const error = ensureError(err);
|
|
877
|
+
return new AbstractionKitError("BUNDLER_ERROR", `bundler ${method} rpc call failed`, {
|
|
878
|
+
cause: new AbstractionKitError(innerCode, error.message, { errno: code }),
|
|
879
|
+
errno: code,
|
|
880
|
+
context
|
|
881
|
+
});
|
|
882
|
+
}
|
|
883
|
+
//#endregion
|
|
884
|
+
//#region src/constants.ts
|
|
885
|
+
/** The Ethereum zero address (0x0000...0000), used as a placeholder for empty/null addresses */
|
|
886
|
+
const ZeroAddress = "0x0000000000000000000000000000000000000000";
|
|
887
|
+
/** EntryPoint v0.9 contract address */
|
|
888
|
+
const ENTRYPOINT_V9 = "0x433709009B8330FDa32311DF1C2AFA402eD8D009";
|
|
889
|
+
/** EntryPoint v0.8 contract address */
|
|
890
|
+
const ENTRYPOINT_V8 = "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108";
|
|
891
|
+
/** EntryPoint v0.7 contract address */
|
|
892
|
+
const ENTRYPOINT_V7 = "0x0000000071727De22E5E9d8BAf0edAc6f37da032";
|
|
893
|
+
/** EntryPoint v0.6 contract address */
|
|
894
|
+
const ENTRYPOINT_V6 = "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789";
|
|
895
|
+
/** Safe L2 singleton v1.5.0 address and init hash */
|
|
896
|
+
const Safe_L2_V1_5_0 = {
|
|
897
|
+
singletonAddress: "0xEdd160fEBBD92E350D4D398fb636302fccd67C7e",
|
|
898
|
+
singletonInitHash: "0x1b94aebb5a7df6dff11d93589204a6bbc99b4b8c9014bf1d386d006c2c17a881"
|
|
899
|
+
};
|
|
900
|
+
/** Safe L2 singleton v1.4.1 address and init hash */
|
|
901
|
+
const Safe_L2_V1_4_1 = {
|
|
902
|
+
singletonAddress: "0x29fcB43b46531BcA003ddC8FCB67FFE91900C762",
|
|
903
|
+
singletonInitHash: "0xe298282cefe913ab5d282047161268a8222e4bd4ed106300c547894bbefd31ee"
|
|
904
|
+
};
|
|
905
|
+
/** Default placeholder values for gas estimation before actual values are known */
|
|
906
|
+
const BaseUserOperationDummyValues = {
|
|
907
|
+
sender: ZeroAddress,
|
|
908
|
+
nonce: 0n,
|
|
909
|
+
callData: "0x",
|
|
910
|
+
callGasLimit: 0n,
|
|
911
|
+
verificationGasLimit: 0n,
|
|
912
|
+
preVerificationGas: 0n,
|
|
913
|
+
maxFeePerGas: 0n,
|
|
914
|
+
maxPriorityFeePerGas: 0n,
|
|
915
|
+
signature: "0x"
|
|
916
|
+
};
|
|
917
|
+
/** EIP-712 primary type string used when signing Safe UserOperations. */
|
|
918
|
+
const EIP712_SAFE_OPERATION_PRIMARY_TYPE = "SafeOp";
|
|
919
|
+
/** EIP-712 type definition for Safe UserOperation signing (EntryPoint v0.6) */
|
|
920
|
+
const EIP712_SAFE_OPERATION_V6_TYPE = { SafeOp: [
|
|
921
|
+
{
|
|
922
|
+
type: "address",
|
|
923
|
+
name: "safe"
|
|
924
|
+
},
|
|
925
|
+
{
|
|
926
|
+
type: "uint256",
|
|
927
|
+
name: "nonce"
|
|
928
|
+
},
|
|
929
|
+
{
|
|
930
|
+
type: "bytes",
|
|
931
|
+
name: "initCode"
|
|
932
|
+
},
|
|
933
|
+
{
|
|
934
|
+
type: "bytes",
|
|
935
|
+
name: "callData"
|
|
936
|
+
},
|
|
937
|
+
{
|
|
938
|
+
type: "uint256",
|
|
939
|
+
name: "callGasLimit"
|
|
940
|
+
},
|
|
941
|
+
{
|
|
942
|
+
type: "uint256",
|
|
943
|
+
name: "verificationGasLimit"
|
|
944
|
+
},
|
|
945
|
+
{
|
|
946
|
+
type: "uint256",
|
|
947
|
+
name: "preVerificationGas"
|
|
948
|
+
},
|
|
949
|
+
{
|
|
950
|
+
type: "uint256",
|
|
951
|
+
name: "maxFeePerGas"
|
|
952
|
+
},
|
|
953
|
+
{
|
|
954
|
+
type: "uint256",
|
|
955
|
+
name: "maxPriorityFeePerGas"
|
|
956
|
+
},
|
|
957
|
+
{
|
|
958
|
+
type: "bytes",
|
|
959
|
+
name: "paymasterAndData"
|
|
960
|
+
},
|
|
961
|
+
{
|
|
962
|
+
type: "uint48",
|
|
963
|
+
name: "validAfter"
|
|
964
|
+
},
|
|
965
|
+
{
|
|
966
|
+
type: "uint48",
|
|
967
|
+
name: "validUntil"
|
|
968
|
+
},
|
|
969
|
+
{
|
|
970
|
+
type: "address",
|
|
971
|
+
name: "entryPoint"
|
|
972
|
+
}
|
|
973
|
+
] };
|
|
974
|
+
/** EIP-712 type definition for Safe UserOperation signing (EntryPoint v0.7) */
|
|
975
|
+
const EIP712_SAFE_OPERATION_V7_TYPE = { SafeOp: [
|
|
976
|
+
{
|
|
977
|
+
type: "address",
|
|
978
|
+
name: "safe"
|
|
979
|
+
},
|
|
980
|
+
{
|
|
981
|
+
type: "uint256",
|
|
982
|
+
name: "nonce"
|
|
983
|
+
},
|
|
984
|
+
{
|
|
985
|
+
type: "bytes",
|
|
986
|
+
name: "initCode"
|
|
987
|
+
},
|
|
988
|
+
{
|
|
989
|
+
type: "bytes",
|
|
990
|
+
name: "callData"
|
|
991
|
+
},
|
|
992
|
+
{
|
|
993
|
+
type: "uint128",
|
|
994
|
+
name: "verificationGasLimit"
|
|
995
|
+
},
|
|
996
|
+
{
|
|
997
|
+
type: "uint128",
|
|
998
|
+
name: "callGasLimit"
|
|
999
|
+
},
|
|
1000
|
+
{
|
|
1001
|
+
type: "uint256",
|
|
1002
|
+
name: "preVerificationGas"
|
|
1003
|
+
},
|
|
1004
|
+
{
|
|
1005
|
+
type: "uint128",
|
|
1006
|
+
name: "maxPriorityFeePerGas"
|
|
1007
|
+
},
|
|
1008
|
+
{
|
|
1009
|
+
type: "uint128",
|
|
1010
|
+
name: "maxFeePerGas"
|
|
1011
|
+
},
|
|
1012
|
+
{
|
|
1013
|
+
type: "bytes",
|
|
1014
|
+
name: "paymasterAndData"
|
|
1015
|
+
},
|
|
1016
|
+
{
|
|
1017
|
+
type: "uint48",
|
|
1018
|
+
name: "validAfter"
|
|
1019
|
+
},
|
|
1020
|
+
{
|
|
1021
|
+
type: "uint48",
|
|
1022
|
+
name: "validUntil"
|
|
1023
|
+
},
|
|
1024
|
+
{
|
|
1025
|
+
type: "address",
|
|
1026
|
+
name: "entryPoint"
|
|
1027
|
+
}
|
|
1028
|
+
] };
|
|
1029
|
+
/** EIP-712 primary type string used when signing multi-chain Safe operations. */
|
|
1030
|
+
const EIP712_MULTI_CHAIN_OPERATIONS_PRIMARY_TYPE = "MerkleTreeRoot";
|
|
1031
|
+
/** EIP-712 type definition for multi-chain Safe operations using Merkle tree roots */
|
|
1032
|
+
const EIP712_MULTI_CHAIN_OPERATIONS_TYPE = { MerkleTreeRoot: [{
|
|
1033
|
+
type: "bytes32",
|
|
1034
|
+
name: "merkleTreeRoot"
|
|
1035
|
+
}] };
|
|
1036
|
+
/** Default address for the secp256r1 (P-256) precompile used by WebAuthn verification */
|
|
1037
|
+
const DEFAULT_SECP256R1_PRECOMPILE_ADDRESS = "0x0000000000000000000000000000000000000100";
|
|
1038
|
+
/** Uniswap Calibur singleton v1.0.0 (EntryPoint v0.8) */
|
|
1039
|
+
const CALIBUR_UNISWAP_V1_0_0_SINGLETON_ADDRESS = "0x000000009B1D0aF20D8C6d0A44e162d11F9b8f00";
|
|
1040
|
+
/** Candide Calibur singleton v0.1.0 (EntryPoint v0.9, unaudited) */
|
|
1041
|
+
const CALIBUR_CANDIDE_V0_1_0_SINGLETON_ADDRESS = "0x71032285A847c4311Eb7ec2E7A636aB94A9805Aa";
|
|
1042
|
+
//#endregion
|
|
1043
|
+
//#region src/signer/negotiate.ts
|
|
1044
|
+
/**
|
|
1045
|
+
* Pick the best mutually-supported signing scheme for one signer against an
|
|
1046
|
+
* account's accepted schemes. Later in the `accepted` array = lower preference;
|
|
1047
|
+
* the account ranks by preference.
|
|
1048
|
+
*
|
|
1049
|
+
* Throws a detailed {@link AbstractionKitError} if no scheme overlaps,
|
|
1050
|
+
* citing the signer's address, what the account accepts, and what the
|
|
1051
|
+
* signer can do.
|
|
1052
|
+
*/
|
|
1053
|
+
function pickScheme(signer, accepted, context) {
|
|
1054
|
+
const signerCan = [];
|
|
1055
|
+
if (typeof signer.signTypedData === "function") signerCan.push("typedData");
|
|
1056
|
+
if (typeof signer.signHash === "function") signerCan.push("hash");
|
|
1057
|
+
for (const scheme of accepted) if (signerCan.includes(scheme)) return scheme;
|
|
1058
|
+
throw new AbstractionKitError("BAD_DATA", buildMismatchMessage({
|
|
1059
|
+
accountName: context.accountName,
|
|
1060
|
+
signerIndex: context.signerIndex,
|
|
1061
|
+
signerAddress: signer.address,
|
|
1062
|
+
accepted,
|
|
1063
|
+
signerCan
|
|
1064
|
+
}));
|
|
965
1065
|
}
|
|
966
1066
|
function buildMismatchMessage(params) {
|
|
967
1067
|
const { accountName, signerIndex, signerAddress, accepted, signerCan } = params;
|
|
@@ -971,7 +1071,7 @@ function buildMismatchMessage(params) {
|
|
|
971
1071
|
/**
|
|
972
1072
|
* Invoke a signer for one scheme. Keeps the dispatch in one place so the
|
|
973
1073
|
* account-side code stays linear. `typedData` is optional: accounts that
|
|
974
|
-
* only accept the `"hash"` scheme
|
|
1074
|
+
* only accept the `"hash"` scheme can pass just `hash`.
|
|
975
1075
|
*
|
|
976
1076
|
* `context` is always forwarded to the signer so power-user implementations
|
|
977
1077
|
* can inspect the userOp.
|
|
@@ -1194,45 +1294,540 @@ function encodeAccessList(access_list) {
|
|
|
1194
1294
|
}
|
|
1195
1295
|
return encoded_access_list;
|
|
1196
1296
|
}
|
|
1197
|
-
/** Converts a bigint to a Uint8Array of its big-endian byte representation. */
|
|
1198
|
-
function bigintToBytes(bi) {
|
|
1199
|
-
return getBytes(toBeArray(bi));
|
|
1297
|
+
/** Converts a bigint to a Uint8Array of its big-endian byte representation. */
|
|
1298
|
+
function bigintToBytes(bi) {
|
|
1299
|
+
return getBytes(toBeArray(bi));
|
|
1300
|
+
}
|
|
1301
|
+
/**
|
|
1302
|
+
* Parse a raw ECDSA signature into its components.
|
|
1303
|
+
* Supports standard 65-byte (r + s + v) and EIP-2098 64-byte compact formats.
|
|
1304
|
+
* @param rawSig - Hex string: 128 chars (EIP-2098 compact), or 130/132 chars (standard with 0x prefix)
|
|
1305
|
+
* @returns An object with yParity (0 or 1), r, and s components
|
|
1306
|
+
*/
|
|
1307
|
+
function parseRawSignature(rawSig) {
|
|
1308
|
+
const sig = rawSig.startsWith("0x") ? rawSig.slice(2) : rawSig;
|
|
1309
|
+
if (sig.length !== 128 && sig.length !== 130) throw new RangeError(`invalid signature length: expected 128 (EIP-2098 compact) or 130 (standard) hex chars, got ${sig.length}`);
|
|
1310
|
+
const r = BigInt(`0x${sig.slice(0, 64)}`);
|
|
1311
|
+
if (sig.length === 128) {
|
|
1312
|
+
const yParityAndS = BigInt(`0x${sig.slice(64, 128)}`);
|
|
1313
|
+
return {
|
|
1314
|
+
yParity: Number(yParityAndS >> 255n & 1n),
|
|
1315
|
+
r,
|
|
1316
|
+
s: yParityAndS & (1n << 255n) - 1n
|
|
1317
|
+
};
|
|
1318
|
+
}
|
|
1319
|
+
const s = BigInt(`0x${sig.slice(64, 128)}`);
|
|
1320
|
+
const v = parseInt(sig.slice(128, 130), 16);
|
|
1321
|
+
if (v !== 0 && v !== 1 && v !== 27 && v !== 28) throw new RangeError(`invalid signature v value: ${v}`);
|
|
1322
|
+
return {
|
|
1323
|
+
yParity: v >= 27 ? v - 27 : v,
|
|
1324
|
+
r,
|
|
1325
|
+
s
|
|
1326
|
+
};
|
|
1327
|
+
}
|
|
1328
|
+
/**
|
|
1329
|
+
* Converts a bigint to a 0x-prefixed hex string with even-length padding.
|
|
1330
|
+
* @param value - The bigint value to convert.
|
|
1331
|
+
* @returns The hex string representation (e.g., "0x01", "0xff").
|
|
1332
|
+
*/
|
|
1333
|
+
function bigintToHex(value) {
|
|
1334
|
+
const hex = value.toString(16);
|
|
1335
|
+
return hex.length % 2 ? `0x0${hex}` : `0x${hex}`;
|
|
1336
|
+
}
|
|
1337
|
+
//#endregion
|
|
1338
|
+
//#region src/utils.ts
|
|
1339
|
+
function buildDomainSeparator(chainId, entrypoint) {
|
|
1340
|
+
return keccak256(AbiCoder.defaultAbiCoder().encode(["(bytes32,bytes32,bytes32,uint256,address)"], [[
|
|
1341
|
+
"0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f",
|
|
1342
|
+
"0x364da28a5c92bcc87fe97c8813a6c6b8a3a049b0ea0a328fcb0b4f0e00337586",
|
|
1343
|
+
"0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6",
|
|
1344
|
+
chainId,
|
|
1345
|
+
entrypoint
|
|
1346
|
+
]]));
|
|
1347
|
+
}
|
|
1348
|
+
/**
|
|
1349
|
+
* Compute the UserOperation hash for any supported EntryPoint version.
|
|
1350
|
+
* This hash is what gets signed by the account owner(s).
|
|
1351
|
+
* Automatically selects the correct packing format based on the entrypoint address.
|
|
1352
|
+
*
|
|
1353
|
+
* @param useroperation - UserOperation to hash
|
|
1354
|
+
* @param entrypointAddress - EntryPoint contract address (determines hash format)
|
|
1355
|
+
* @param chainId - Target chain ID
|
|
1356
|
+
* @returns The UserOperation hash as a hex string
|
|
1357
|
+
*/
|
|
1358
|
+
function createUserOperationHash(useroperation, entrypointAddress, chainId) {
|
|
1359
|
+
let packedUserOperationHash;
|
|
1360
|
+
const abiCoder = AbiCoder.defaultAbiCoder();
|
|
1361
|
+
let userOperationHash;
|
|
1362
|
+
if (entrypointAddress.toLowerCase() === "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789".toLowerCase()) {
|
|
1363
|
+
packedUserOperationHash = keccak256(createPackedUserOperationV6(useroperation));
|
|
1364
|
+
userOperationHash = keccak256(abiCoder.encode([
|
|
1365
|
+
"bytes32",
|
|
1366
|
+
"address",
|
|
1367
|
+
"uint256"
|
|
1368
|
+
], [
|
|
1369
|
+
packedUserOperationHash,
|
|
1370
|
+
entrypointAddress,
|
|
1371
|
+
chainId
|
|
1372
|
+
]));
|
|
1373
|
+
} else if (entrypointAddress.toLowerCase() === "0x0000000071727De22E5E9d8BAf0edAc6f37da032".toLowerCase()) {
|
|
1374
|
+
packedUserOperationHash = keccak256(createPackedUserOperationV7(useroperation));
|
|
1375
|
+
userOperationHash = keccak256(abiCoder.encode([
|
|
1376
|
+
"bytes32",
|
|
1377
|
+
"address",
|
|
1378
|
+
"uint256"
|
|
1379
|
+
], [
|
|
1380
|
+
packedUserOperationHash,
|
|
1381
|
+
entrypointAddress,
|
|
1382
|
+
chainId
|
|
1383
|
+
]));
|
|
1384
|
+
} else if (entrypointAddress.toLowerCase() === "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108".toLowerCase()) {
|
|
1385
|
+
packedUserOperationHash = keccak256(createPackedUserOperationV8(useroperation));
|
|
1386
|
+
userOperationHash = keccak256(`0x1901${buildDomainSeparator(chainId, entrypointAddress).slice(2)}${packedUserOperationHash.slice(2)}`);
|
|
1387
|
+
} else if (entrypointAddress.toLowerCase() === "0x433709009B8330FDa32311DF1C2AFA402eD8D009".toLowerCase()) {
|
|
1388
|
+
packedUserOperationHash = keccak256(createPackedUserOperationV9(useroperation));
|
|
1389
|
+
userOperationHash = keccak256(`0x1901${buildDomainSeparator(chainId, entrypointAddress).slice(2)}${packedUserOperationHash.slice(2)}`);
|
|
1390
|
+
} else throw new RangeError(`unsupported entrypoint address: ${entrypointAddress}`);
|
|
1391
|
+
return userOperationHash;
|
|
1392
|
+
}
|
|
1393
|
+
/**
|
|
1394
|
+
* @internal
|
|
1395
|
+
* Reconstruct the packed `initCode` field for an EntryPoint v0.8/v0.9
|
|
1396
|
+
* UserOperation. When `eip7702Auth.address` is set, the EIP-7702 delegatee
|
|
1397
|
+
* address replaces the factory address in initCode (only `factoryData` is
|
|
1398
|
+
* concatenated). Otherwise, behaves like v0.7 (`factory + factoryData`).
|
|
1399
|
+
*
|
|
1400
|
+
* Shared by {@link createPackedUserOperationV8} /
|
|
1401
|
+
* {@link createPackedUserOperationV9} and Simple7702Account's typed-data
|
|
1402
|
+
* builder. Not part of the public API; only `export`ed for cross-module
|
|
1403
|
+
* use within the package and not re-exported from `src/abstractionkit.ts`.
|
|
1404
|
+
*
|
|
1405
|
+
* @param useroperation - V8 or V9 UserOperation to read fields from
|
|
1406
|
+
* @returns Hex-encoded initCode
|
|
1407
|
+
*/
|
|
1408
|
+
function buildPackedInitCodeV8V9(useroperation) {
|
|
1409
|
+
if (useroperation.factory == null) return "0x";
|
|
1410
|
+
const eip7702Auth = useroperation.eip7702Auth;
|
|
1411
|
+
return (eip7702Auth != null && eip7702Auth.address != null ? eip7702Auth.address : useroperation.factory) + (useroperation.factoryData != null ? useroperation.factoryData.slice(2) : "");
|
|
1412
|
+
}
|
|
1413
|
+
/**
|
|
1414
|
+
* @internal
|
|
1415
|
+
* Pack `(verificationGasLimit, callGasLimit)` into a single `bytes32`
|
|
1416
|
+
* (two uint128 values, big-endian). Matches the on-chain
|
|
1417
|
+
* `PackedUserOperation.accountGasLimits` layout used by EntryPoint v0.7+.
|
|
1418
|
+
*/
|
|
1419
|
+
function packAccountGasLimits(verificationGasLimit, callGasLimit) {
|
|
1420
|
+
const abiCoder = AbiCoder.defaultAbiCoder();
|
|
1421
|
+
return "0x" + abiCoder.encode(["uint128"], [verificationGasLimit]).slice(34) + abiCoder.encode(["uint128"], [callGasLimit]).slice(34);
|
|
1422
|
+
}
|
|
1423
|
+
/**
|
|
1424
|
+
* @internal
|
|
1425
|
+
* Pack `(maxPriorityFeePerGas, maxFeePerGas)` into a single `bytes32`
|
|
1426
|
+
* (two uint128 values, big-endian). Matches the on-chain
|
|
1427
|
+
* `PackedUserOperation.gasFees` layout used by EntryPoint v0.7+.
|
|
1428
|
+
*/
|
|
1429
|
+
function packGasFees(maxPriorityFeePerGas, maxFeePerGas) {
|
|
1430
|
+
const abiCoder = AbiCoder.defaultAbiCoder();
|
|
1431
|
+
return "0x" + abiCoder.encode(["uint128"], [maxPriorityFeePerGas]).slice(34) + abiCoder.encode(["uint128"], [maxFeePerGas]).slice(34);
|
|
1432
|
+
}
|
|
1433
|
+
/**
|
|
1434
|
+
* @internal
|
|
1435
|
+
* Reconstruct the packed `paymasterAndData` field from a UserOperation's
|
|
1436
|
+
* separate paymaster fields. Returns `0x` when no paymaster is set.
|
|
1437
|
+
*
|
|
1438
|
+
* For EntryPoint v0.9, when `paymasterData` ends with the parallel-paymaster
|
|
1439
|
+
* signature magic suffix (`0x22e325a297439656`), the embedded signature is
|
|
1440
|
+
* stripped so the userOpHash does not commit to the paymaster's signature
|
|
1441
|
+
* over itself. Pass `stripV9PaymasterSig=false` to preserve the wire format.
|
|
1442
|
+
*
|
|
1443
|
+
* Shared by v7/v8/v9 packers and Simple7702Account's typed-data builder.
|
|
1444
|
+
* Not part of the public API; only `export`ed for cross-module use within
|
|
1445
|
+
* the package and not re-exported from `src/abstractionkit.ts`.
|
|
1446
|
+
*
|
|
1447
|
+
* @param useroperation - V7/V8/V9 UserOperation to read fields from
|
|
1448
|
+
* @param stripV9PaymasterSig - Whether to strip the v0.9 paymaster signature suffix
|
|
1449
|
+
* @returns Hex-encoded paymasterAndData
|
|
1450
|
+
*/
|
|
1451
|
+
function buildPaymasterAndData(useroperation, stripV9PaymasterSig = false) {
|
|
1452
|
+
if (useroperation.paymaster == null) return "0x";
|
|
1453
|
+
const abiCoder = AbiCoder.defaultAbiCoder();
|
|
1454
|
+
let paymasterAndData = useroperation.paymaster;
|
|
1455
|
+
if (useroperation.paymasterVerificationGasLimit != null) paymasterAndData += abiCoder.encode(["uint128"], [useroperation.paymasterVerificationGasLimit]).slice(34);
|
|
1456
|
+
if (useroperation.paymasterPostOpGasLimit != null) paymasterAndData += abiCoder.encode(["uint128"], [useroperation.paymasterPostOpGasLimit]).slice(34);
|
|
1457
|
+
if (useroperation.paymasterData != null) {
|
|
1458
|
+
const PAYMASTER_SIG_MAGIC = "22e325a297439656";
|
|
1459
|
+
if (stripV9PaymasterSig && useroperation.paymasterData.toLowerCase().endsWith(PAYMASTER_SIG_MAGIC)) {
|
|
1460
|
+
const sigLenHex = useroperation.paymasterData.slice(useroperation.paymasterData.length - 16 - 4, useroperation.paymasterData.length - 16);
|
|
1461
|
+
const sigLen = parseInt(sigLenHex, 16);
|
|
1462
|
+
const prefixEnd = useroperation.paymasterData.length - 16 - 4 - sigLen * 2;
|
|
1463
|
+
paymasterAndData += useroperation.paymasterData.slice(0, prefixEnd).replaceAll("0x", "") + PAYMASTER_SIG_MAGIC;
|
|
1464
|
+
} else paymasterAndData += useroperation.paymasterData.slice(2);
|
|
1465
|
+
}
|
|
1466
|
+
return paymasterAndData;
|
|
1467
|
+
}
|
|
1468
|
+
/**
|
|
1469
|
+
* Build the EIP-712 typed data payload for a UserOperation under the
|
|
1470
|
+
* EntryPoint v0.8 / v0.9 domain. The digest of the returned payload equals
|
|
1471
|
+
* the `userOpHash` from {@link createUserOperationHash}, so signing it via
|
|
1472
|
+
* `signTypedData` produces a signature that validates against the same hash
|
|
1473
|
+
* as raw ECDSA over the `userOpHash`.
|
|
1474
|
+
*
|
|
1475
|
+
* Shared by EIP-7702 accounts (Simple7702, Calibur). Earlier EntryPoints
|
|
1476
|
+
* define the userOpHash differently and don't fit this typed-data shape.
|
|
1477
|
+
*
|
|
1478
|
+
* @param userOperation - Unsigned UserOperation to wrap
|
|
1479
|
+
* @param entrypointAddress - The EntryPoint contract address (must be v0.8 or v0.9)
|
|
1480
|
+
* @param chainId - Target chain ID (must match the chain that will validate the signature)
|
|
1481
|
+
* @returns EIP-712 {@link TypedData} payload ready for `signTypedData`
|
|
1482
|
+
* @throws {AbstractionKitError} if `entrypointAddress` is not v0.8 / v0.9
|
|
1483
|
+
*/
|
|
1484
|
+
function getUserOperationEip712DataV8V9(userOperation, entrypointAddress, chainId) {
|
|
1485
|
+
const ep = entrypointAddress.toLowerCase();
|
|
1486
|
+
const isV9 = ep === ENTRYPOINT_V9.toLowerCase();
|
|
1487
|
+
if (ep !== "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108".toLowerCase() && !isV9) throw new AbstractionKitError("BAD_DATA", `getUserOperationEip712Data supports EntryPoint v0.8 / v0.9 only; got ${entrypointAddress}. Earlier EntryPoints define the userOpHash differently and require raw-hash signing.`);
|
|
1488
|
+
const initCode = buildPackedInitCodeV8V9(userOperation);
|
|
1489
|
+
const accountGasLimits = packAccountGasLimits(userOperation.verificationGasLimit, userOperation.callGasLimit);
|
|
1490
|
+
const gasFees = packGasFees(userOperation.maxPriorityFeePerGas, userOperation.maxFeePerGas);
|
|
1491
|
+
const paymasterAndData = buildPaymasterAndData(userOperation, isV9);
|
|
1492
|
+
return {
|
|
1493
|
+
domain: {
|
|
1494
|
+
name: "ERC4337",
|
|
1495
|
+
version: "1",
|
|
1496
|
+
chainId,
|
|
1497
|
+
verifyingContract: entrypointAddress
|
|
1498
|
+
},
|
|
1499
|
+
types: { PackedUserOperation: [
|
|
1500
|
+
{
|
|
1501
|
+
name: "sender",
|
|
1502
|
+
type: "address"
|
|
1503
|
+
},
|
|
1504
|
+
{
|
|
1505
|
+
name: "nonce",
|
|
1506
|
+
type: "uint256"
|
|
1507
|
+
},
|
|
1508
|
+
{
|
|
1509
|
+
name: "initCode",
|
|
1510
|
+
type: "bytes"
|
|
1511
|
+
},
|
|
1512
|
+
{
|
|
1513
|
+
name: "callData",
|
|
1514
|
+
type: "bytes"
|
|
1515
|
+
},
|
|
1516
|
+
{
|
|
1517
|
+
name: "accountGasLimits",
|
|
1518
|
+
type: "bytes32"
|
|
1519
|
+
},
|
|
1520
|
+
{
|
|
1521
|
+
name: "preVerificationGas",
|
|
1522
|
+
type: "uint256"
|
|
1523
|
+
},
|
|
1524
|
+
{
|
|
1525
|
+
name: "gasFees",
|
|
1526
|
+
type: "bytes32"
|
|
1527
|
+
},
|
|
1528
|
+
{
|
|
1529
|
+
name: "paymasterAndData",
|
|
1530
|
+
type: "bytes"
|
|
1531
|
+
}
|
|
1532
|
+
] },
|
|
1533
|
+
primaryType: "PackedUserOperation",
|
|
1534
|
+
message: {
|
|
1535
|
+
sender: userOperation.sender,
|
|
1536
|
+
nonce: userOperation.nonce,
|
|
1537
|
+
initCode,
|
|
1538
|
+
callData: userOperation.callData,
|
|
1539
|
+
accountGasLimits,
|
|
1540
|
+
preVerificationGas: userOperation.preVerificationGas,
|
|
1541
|
+
gasFees,
|
|
1542
|
+
paymasterAndData
|
|
1543
|
+
}
|
|
1544
|
+
};
|
|
1545
|
+
}
|
|
1546
|
+
/**
|
|
1547
|
+
* Compute the EIP-712 digest of a UserOperation under the EntryPoint
|
|
1548
|
+
* v0.8 / v0.9 domain. For these EntryPoints this digest IS the
|
|
1549
|
+
* `userOpHash` ({@link createUserOperationHash}).
|
|
1550
|
+
*
|
|
1551
|
+
* @param userOperation - Unsigned UserOperation to hash
|
|
1552
|
+
* @param entrypointAddress - The EntryPoint contract address (must be v0.8 or v0.9)
|
|
1553
|
+
* @param chainId - Target chain ID
|
|
1554
|
+
* @returns The EIP-712 digest as a hex string
|
|
1555
|
+
* @throws {AbstractionKitError} if `entrypointAddress` is not v0.8 / v0.9
|
|
1556
|
+
*/
|
|
1557
|
+
function getUserOperationEip712HashV8V9(userOperation, entrypointAddress, chainId) {
|
|
1558
|
+
const data = getUserOperationEip712DataV8V9(userOperation, entrypointAddress, chainId);
|
|
1559
|
+
return TypedDataEncoder.hash(data.domain, data.types, data.message);
|
|
1560
|
+
}
|
|
1561
|
+
/**
|
|
1562
|
+
* ABI-encode and pack a UserOperation for hashing (EntryPoint v0.6 format).
|
|
1563
|
+
* Bytes fields (initCode, callData, paymasterAndData) are keccak256-hashed before packing.
|
|
1564
|
+
*
|
|
1565
|
+
* @param useroperation - UserOperation to pack
|
|
1566
|
+
* @returns ABI-encoded packed UserOperation as a hex string
|
|
1567
|
+
*/
|
|
1568
|
+
function createPackedUserOperationV6(useroperation) {
|
|
1569
|
+
const useroperationValuesArrayWithHashedByteValues = [
|
|
1570
|
+
useroperation.sender,
|
|
1571
|
+
useroperation.nonce,
|
|
1572
|
+
keccak256(useroperation.initCode),
|
|
1573
|
+
keccak256(useroperation.callData),
|
|
1574
|
+
useroperation.callGasLimit,
|
|
1575
|
+
useroperation.verificationGasLimit,
|
|
1576
|
+
useroperation.preVerificationGas,
|
|
1577
|
+
useroperation.maxFeePerGas,
|
|
1578
|
+
useroperation.maxPriorityFeePerGas,
|
|
1579
|
+
keccak256(useroperation.paymasterAndData)
|
|
1580
|
+
];
|
|
1581
|
+
return AbiCoder.defaultAbiCoder().encode([
|
|
1582
|
+
"address",
|
|
1583
|
+
"uint256",
|
|
1584
|
+
"bytes32",
|
|
1585
|
+
"bytes32",
|
|
1586
|
+
"uint256",
|
|
1587
|
+
"uint256",
|
|
1588
|
+
"uint256",
|
|
1589
|
+
"uint256",
|
|
1590
|
+
"uint256",
|
|
1591
|
+
"bytes32"
|
|
1592
|
+
], useroperationValuesArrayWithHashedByteValues);
|
|
1593
|
+
}
|
|
1594
|
+
/**
|
|
1595
|
+
* ABI-encode and pack a UserOperation for hashing (EntryPoint v0.7 format).
|
|
1596
|
+
* Reconstructs initCode, accountGasLimits, gasFees, and paymasterAndData from separate fields.
|
|
1597
|
+
*
|
|
1598
|
+
* @param useroperation - UserOperation to pack
|
|
1599
|
+
* @returns ABI-encoded packed UserOperation as a hex string
|
|
1600
|
+
*/
|
|
1601
|
+
function createPackedUserOperationV7(useroperation) {
|
|
1602
|
+
const abiCoder = AbiCoder.defaultAbiCoder();
|
|
1603
|
+
let initCode = "0x";
|
|
1604
|
+
if (useroperation.factory != null) {
|
|
1605
|
+
initCode = useroperation.factory;
|
|
1606
|
+
if (useroperation.factoryData != null) initCode += useroperation.factoryData.slice(2);
|
|
1607
|
+
}
|
|
1608
|
+
const accountGasLimits = packAccountGasLimits(useroperation.verificationGasLimit, useroperation.callGasLimit);
|
|
1609
|
+
const gasFees = packGasFees(useroperation.maxPriorityFeePerGas, useroperation.maxFeePerGas);
|
|
1610
|
+
const paymasterAndData = buildPaymasterAndData(useroperation);
|
|
1611
|
+
const useroperationValuesArrayWithHashedByteValues = [
|
|
1612
|
+
useroperation.sender,
|
|
1613
|
+
useroperation.nonce,
|
|
1614
|
+
keccak256(initCode),
|
|
1615
|
+
keccak256(useroperation.callData),
|
|
1616
|
+
accountGasLimits,
|
|
1617
|
+
useroperation.preVerificationGas,
|
|
1618
|
+
gasFees,
|
|
1619
|
+
keccak256(paymasterAndData)
|
|
1620
|
+
];
|
|
1621
|
+
return abiCoder.encode([
|
|
1622
|
+
"address",
|
|
1623
|
+
"uint256",
|
|
1624
|
+
"bytes32",
|
|
1625
|
+
"bytes32",
|
|
1626
|
+
"bytes32",
|
|
1627
|
+
"uint256",
|
|
1628
|
+
"bytes32",
|
|
1629
|
+
"bytes32"
|
|
1630
|
+
], useroperationValuesArrayWithHashedByteValues);
|
|
1631
|
+
}
|
|
1632
|
+
/**
|
|
1633
|
+
* ABI-encode and pack a UserOperation for hashing (EntryPoint v0.9 format).
|
|
1634
|
+
*
|
|
1635
|
+
* @param useroperation - UserOperation to pack
|
|
1636
|
+
* @returns ABI-encoded packed UserOperation as a hex string
|
|
1637
|
+
*/
|
|
1638
|
+
function createPackedUserOperationV9(useroperation) {
|
|
1639
|
+
return baseCreatePackedUserOperationV8V9(useroperation, true);
|
|
1640
|
+
}
|
|
1641
|
+
/**
|
|
1642
|
+
* ABI-encode and pack a UserOperation for hashing (EntryPoint v0.8 format).
|
|
1643
|
+
*
|
|
1644
|
+
* @param useroperation - UserOperation to pack
|
|
1645
|
+
* @returns ABI-encoded packed UserOperation as a hex string
|
|
1646
|
+
*/
|
|
1647
|
+
function createPackedUserOperationV8(useroperation) {
|
|
1648
|
+
return baseCreatePackedUserOperationV8V9(useroperation, false);
|
|
1649
|
+
}
|
|
1650
|
+
/**
|
|
1651
|
+
* Shared packer for EntryPoint v0.8 and v0.9 UserOperations.
|
|
1652
|
+
* @param useroperation - UserOperation to pack
|
|
1653
|
+
* @param is_v9 - If true, strips the paymaster signature suffix (v0.9-specific)
|
|
1654
|
+
* @returns ABI-encoded packed UserOperation as a hex string
|
|
1655
|
+
*/
|
|
1656
|
+
function baseCreatePackedUserOperationV8V9(useroperation, is_v9) {
|
|
1657
|
+
const abiCoder = AbiCoder.defaultAbiCoder();
|
|
1658
|
+
const initCode = buildPackedInitCodeV8V9(useroperation);
|
|
1659
|
+
const accountGasLimits = packAccountGasLimits(useroperation.verificationGasLimit, useroperation.callGasLimit);
|
|
1660
|
+
const gasFees = packGasFees(useroperation.maxPriorityFeePerGas, useroperation.maxFeePerGas);
|
|
1661
|
+
const paymasterAndData = buildPaymasterAndData(useroperation, is_v9);
|
|
1662
|
+
const useroperationValuesArrayWithHashedByteValues = [
|
|
1663
|
+
"0x29a0bca4af4be3421398da00295e58e6d7de38cb492214754cb6a47507dd6f8e",
|
|
1664
|
+
useroperation.sender,
|
|
1665
|
+
useroperation.nonce,
|
|
1666
|
+
keccak256(initCode),
|
|
1667
|
+
keccak256(useroperation.callData),
|
|
1668
|
+
accountGasLimits,
|
|
1669
|
+
useroperation.preVerificationGas,
|
|
1670
|
+
gasFees,
|
|
1671
|
+
keccak256(paymasterAndData)
|
|
1672
|
+
];
|
|
1673
|
+
return abiCoder.encode([
|
|
1674
|
+
"bytes32",
|
|
1675
|
+
"address",
|
|
1676
|
+
"uint256",
|
|
1677
|
+
"bytes32",
|
|
1678
|
+
"bytes32",
|
|
1679
|
+
"bytes32",
|
|
1680
|
+
"uint256",
|
|
1681
|
+
"bytes32",
|
|
1682
|
+
"bytes32"
|
|
1683
|
+
], useroperationValuesArrayWithHashedByteValues);
|
|
1684
|
+
}
|
|
1685
|
+
/**
|
|
1686
|
+
* Encode a function call into ABI-encoded calldata.
|
|
1687
|
+
*
|
|
1688
|
+
* @param functionSelector - 4-byte hex function selector (e.g., "0xa9059cbb" for ERC-20 transfer)
|
|
1689
|
+
* @param functionInputAbi - Array of ABI type strings (e.g., ["address", "uint256"])
|
|
1690
|
+
* @param functionInputParameters - Array of parameter values matching the ABI types
|
|
1691
|
+
* @returns ABI-encoded calldata as a hex string (selector + encoded parameters)
|
|
1692
|
+
*
|
|
1693
|
+
* @example
|
|
1694
|
+
* const transferCallData = createCallData(
|
|
1695
|
+
* "0xa9059cbb",
|
|
1696
|
+
* ["address", "uint256"],
|
|
1697
|
+
* ["0xRecipientAddress", 1000000n],
|
|
1698
|
+
* );
|
|
1699
|
+
*/
|
|
1700
|
+
function createCallData(functionSelector, functionInputAbi, functionInputParameters) {
|
|
1701
|
+
return functionSelector + AbiCoder.defaultAbiCoder().encode(functionInputAbi, functionInputParameters).slice(2);
|
|
1702
|
+
}
|
|
1703
|
+
/**
|
|
1704
|
+
* Send a JSON-RPC 2.0 request to the specified endpoint.
|
|
1705
|
+
*
|
|
1706
|
+
* **External escape hatch.** Most SDK consumers should use one of the
|
|
1707
|
+
* high-level service classes ({@link Bundler}, {@link CandidePaymaster},
|
|
1708
|
+
* {@link Erc7677Paymaster}, {@link JsonRpcNode}) which translate errors into
|
|
1709
|
+
* the SDK's domain vocabulary. This function is intentionally low-level: it
|
|
1710
|
+
* speaks plain JSON-RPC and throws {@link TransportRpcError}
|
|
1711
|
+
* (an EIP-1193-shaped `ProviderRpcError`) on RPC errors.
|
|
1712
|
+
*
|
|
1713
|
+
* Two execution modes:
|
|
1714
|
+
* - **URL string:** issues a `fetch` POST directly. Supports custom `headers`
|
|
1715
|
+
* and a custom `paramsKeyName` for non-standard servers (e.g. APIs that
|
|
1716
|
+
* expect `"simulations"` instead of `"params"`).
|
|
1717
|
+
* - **Transport / JsonRpcNode:** delegates to `.request({ method, params })`.
|
|
1718
|
+
* `headers` and `paramsKeyName` are ignored (those concerns belong to the
|
|
1719
|
+
* transport implementation).
|
|
1720
|
+
*
|
|
1721
|
+
* Automatically serializes `bigint` values in `params` as `0x`-prefixed hex.
|
|
1722
|
+
*
|
|
1723
|
+
* @param rpc - JSON-RPC endpoint URL, {@link Transport}, or {@link JsonRpcNode}
|
|
1724
|
+
* @param method - The JSON-RPC method name (e.g., `"eth_call"`)
|
|
1725
|
+
* @param params - The JSON-RPC parameters
|
|
1726
|
+
* @param headers - Custom HTTP headers (URL inputs only; default Content-Type: application/json)
|
|
1727
|
+
* @param paramsKeyName - Override the params key in the envelope (URL inputs only; default `"params"`)
|
|
1728
|
+
* @returns The `result` (or non-standard equivalent) field from the JSON-RPC response
|
|
1729
|
+
* @throws {@link TransportRpcError} when the response contains an `error` field
|
|
1730
|
+
*/
|
|
1731
|
+
async function sendJsonRpcRequest(rpc, method, params, headers = { "Content-Type": "application/json" }, paramsKeyName = "params") {
|
|
1732
|
+
const normalizedParams = JSON.parse(JSON.stringify(params, (_key, value) => typeof value === "bigint" ? `0x${value.toString(16)}` : value));
|
|
1733
|
+
if (typeof rpc !== "string") return JsonRpcNode.from(rpc).request({
|
|
1734
|
+
method,
|
|
1735
|
+
params: normalizedParams
|
|
1736
|
+
});
|
|
1737
|
+
const raw = JSON.stringify({
|
|
1738
|
+
method,
|
|
1739
|
+
[paramsKeyName]: normalizedParams,
|
|
1740
|
+
id: Date.now(),
|
|
1741
|
+
jsonrpc: "2.0"
|
|
1742
|
+
});
|
|
1743
|
+
const response = await (await fetch(rpc, {
|
|
1744
|
+
method: "POST",
|
|
1745
|
+
headers,
|
|
1746
|
+
body: raw,
|
|
1747
|
+
redirect: "follow"
|
|
1748
|
+
})).json();
|
|
1749
|
+
if ("result" in response) return response.result;
|
|
1750
|
+
const err = response.error;
|
|
1751
|
+
throw new TransportRpcError(err.code, err.message);
|
|
1752
|
+
}
|
|
1753
|
+
/**
|
|
1754
|
+
* Get a 4-byte function selector from a Solidity-style function signature.
|
|
1755
|
+
* Computed as the first 4 bytes of keccak256(signature).
|
|
1756
|
+
* @param functionSignature - Solidity-style function signature, e.g. "mint(address)"
|
|
1757
|
+
* @returns Function selector as a 0x-prefixed 10-character hex string
|
|
1758
|
+
*
|
|
1759
|
+
* @example
|
|
1760
|
+
* getFunctionSelector("getNonce(address,uint192)"); // "0x35567e1a"
|
|
1761
|
+
*/
|
|
1762
|
+
function getFunctionSelector(functionSignature) {
|
|
1763
|
+
return id(functionSignature).slice(0, 10);
|
|
1764
|
+
}
|
|
1765
|
+
/**
|
|
1766
|
+
* Fetch the account's nonce from the EntryPoint contract.
|
|
1767
|
+
*
|
|
1768
|
+
* Equivalent to `JsonRpcNode.from(rpc).getEntryPointNonce(entryPoint, account, key)`.
|
|
1769
|
+
* Kept as a top-level export for backward compatibility with existing call
|
|
1770
|
+
* sites; the first parameter widens from `string` to
|
|
1771
|
+
* `string | Transport | JsonRpcNode` so users can pass a configured transport
|
|
1772
|
+
* (with custom headers, retries, etc.) without restructuring their code.
|
|
1773
|
+
*
|
|
1774
|
+
* @param rpc - Ethereum JSON-RPC node URL, {@link Transport}, or {@link JsonRpcNode}
|
|
1775
|
+
* @param entryPoint - EntryPoint contract address
|
|
1776
|
+
* @param account - Smart account address to query
|
|
1777
|
+
* @param key - Nonce key (default 0). Different keys allow parallel nonce channels.
|
|
1778
|
+
* @returns The current nonce as a bigint
|
|
1779
|
+
*/
|
|
1780
|
+
async function fetchAccountNonce(rpc, entryPoint, account, key = 0) {
|
|
1781
|
+
return JsonRpcNode.from(rpc).getEntryPointNonce(entryPoint, account, BigInt(key));
|
|
1200
1782
|
}
|
|
1201
1783
|
/**
|
|
1202
|
-
*
|
|
1203
|
-
*
|
|
1204
|
-
* @param
|
|
1205
|
-
* @
|
|
1784
|
+
* Fetch current gas prices from the Polygon Gas Station API.
|
|
1785
|
+
*
|
|
1786
|
+
* @param polygonChain - Target Polygon chain (Mainnet, Amoy, etc.)
|
|
1787
|
+
* @param gasLevel - Gas price level (Slow, Medium, Fast)
|
|
1788
|
+
* @returns A tuple of [maxFeePerGas, maxPriorityFeePerGas] as bigints
|
|
1206
1789
|
*/
|
|
1207
|
-
function
|
|
1208
|
-
const
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1790
|
+
async function fetchGasPricePolygon(polygonChain, gasLevel = GasOption.Medium) {
|
|
1791
|
+
const gasStationUrl = `https://gasstation.polygon.technology/${polygonChain}`;
|
|
1792
|
+
try {
|
|
1793
|
+
const response = await (await fetch(gasStationUrl)).json();
|
|
1794
|
+
let gasPrice;
|
|
1795
|
+
if (gasLevel === GasOption.Slow) gasPrice = response.safeLow;
|
|
1796
|
+
else if (gasLevel === GasOption.Medium) gasPrice = response.standard;
|
|
1797
|
+
else gasPrice = response.fast;
|
|
1798
|
+
let maxFeePerGas = BigInt(Math.ceil(Number(gasPrice.maxFee) * 1e9));
|
|
1799
|
+
let maxPriorityFeePerGas = BigInt(Math.ceil(Number(gasPrice.maxPriorityFee) * 1e9));
|
|
1800
|
+
if (maxFeePerGas === 0n) maxFeePerGas = 1n;
|
|
1801
|
+
if (maxPriorityFeePerGas === 0n) maxPriorityFeePerGas = 1n;
|
|
1802
|
+
return [maxFeePerGas, maxPriorityFeePerGas];
|
|
1803
|
+
} catch (err) {
|
|
1804
|
+
const error = ensureError(err);
|
|
1805
|
+
throw new AbstractionKitError("BAD_DATA", `fetching gas prices from ${gasStationUrl} failed.`, { cause: error });
|
|
1218
1806
|
}
|
|
1219
|
-
const s = BigInt(`0x${sig.slice(64, 128)}`);
|
|
1220
|
-
const v = parseInt(sig.slice(128, 130), 16);
|
|
1221
|
-
if (v !== 0 && v !== 1 && v !== 27 && v !== 28) throw new RangeError(`invalid signature v value: ${v}`);
|
|
1222
|
-
return {
|
|
1223
|
-
yParity: v >= 27 ? v - 27 : v,
|
|
1224
|
-
r,
|
|
1225
|
-
s
|
|
1226
|
-
};
|
|
1227
1807
|
}
|
|
1228
1808
|
/**
|
|
1229
|
-
*
|
|
1230
|
-
*
|
|
1231
|
-
*
|
|
1809
|
+
* Calculate the maximum gas cost (in wei) that a UserOperation could consume.
|
|
1810
|
+
* Uses different formulas for v0.6 (with paymaster multiplier) and v0.7+ UserOperations.
|
|
1811
|
+
*
|
|
1812
|
+
* @param useroperation - The UserOperation to calculate the max gas cost for
|
|
1813
|
+
* @returns Maximum possible gas cost in wei as a bigint
|
|
1232
1814
|
*/
|
|
1233
|
-
function
|
|
1234
|
-
|
|
1235
|
-
|
|
1815
|
+
function calculateUserOperationMaxGasCost(useroperation) {
|
|
1816
|
+
if ("initCode" in useroperation) {
|
|
1817
|
+
const mul = useroperation.paymasterAndData !== "0x" && useroperation.paymasterAndData != null ? 3n : 0n;
|
|
1818
|
+
return (useroperation.callGasLimit + useroperation.verificationGasLimit * mul + useroperation.preVerificationGas) * useroperation.maxFeePerGas;
|
|
1819
|
+
} else return (useroperation.verificationGasLimit + useroperation.callGasLimit + (useroperation.paymasterVerificationGasLimit ?? 0n) + (useroperation.paymasterPostOpGasLimit ?? 0n) + useroperation.preVerificationGas) * useroperation.maxFeePerGas;
|
|
1820
|
+
}
|
|
1821
|
+
/**
|
|
1822
|
+
* @internal
|
|
1823
|
+
* Internal dispatcher: routes to the Polygon Gas Station when a chain is
|
|
1824
|
+
* provided, otherwise delegates to {@link JsonRpcNode.getFeeData}. Used by
|
|
1825
|
+
* the account classes when assembling base UserOperation gas fields.
|
|
1826
|
+
*/
|
|
1827
|
+
async function handlefetchGasPrice(providerRpc, polygonGasStation, gasLevel = GasOption.Medium) {
|
|
1828
|
+
if (polygonGasStation != null) return fetchGasPricePolygon(polygonGasStation, gasLevel);
|
|
1829
|
+
if (providerRpc != null) return JsonRpcNode.from(providerRpc).getFeeData(gasLevel);
|
|
1830
|
+
throw new AbstractionKitError("BAD_DATA", "providerRpc can't be null if maxFeePerGas and maxPriorityFeePerGas are not overridden");
|
|
1236
1831
|
}
|
|
1237
1832
|
//#endregion
|
|
1238
1833
|
//#region src/account/SendUseroperationResponse.ts
|
|
@@ -1440,6 +2035,29 @@ var Calibur7702Account = class Calibur7702Account extends SmartAccount {
|
|
|
1440
2035
|
hookData
|
|
1441
2036
|
]);
|
|
1442
2037
|
}
|
|
2038
|
+
/**
|
|
2039
|
+
* Format a raw EIP-712 signature (from `signTypedData` over the payload
|
|
2040
|
+
* returned by {@link getUserOperationEip712Data}) into Calibur's
|
|
2041
|
+
* `userOp.signature` layout. Provided for API parity with Safe's
|
|
2042
|
+
* `formatEip712SingleSignatureToUseroperationSignature`.
|
|
2043
|
+
*
|
|
2044
|
+
* Under EntryPoint v0.8 / v0.9 the userOpHash IS the EIP-712 digest of
|
|
2045
|
+
* the PackedUserOperation, so a raw-hash signature and a typed-data
|
|
2046
|
+
* signature are byte-identical for deterministic-ECDSA signers; this
|
|
2047
|
+
* method is therefore equivalent to {@link wrapSignature} with the same
|
|
2048
|
+
* `keyHash` / `hookData`. The default `keyHash` is the root-key hash
|
|
2049
|
+
* (`bytes32(0)`), which selects the EOA's own secp256k1 key.
|
|
2050
|
+
*
|
|
2051
|
+
* @param signature - Raw ECDSA signature (65 bytes, hex-encoded) from
|
|
2052
|
+
* `signTypedData` over the payload from {@link getUserOperationEip712Data}
|
|
2053
|
+
* @param overrides - Optional `keyHash` / `hookData` overrides
|
|
2054
|
+
* @returns Hex-encoded wrapped signature ready for `userOp.signature`
|
|
2055
|
+
*/
|
|
2056
|
+
static formatEip712SingleSignatureToUseroperationSignature(signature, overrides = {}) {
|
|
2057
|
+
const keyHash = overrides.keyHash ?? ROOT_KEY_HASH;
|
|
2058
|
+
const hookData = overrides.hookData ?? "0x";
|
|
2059
|
+
return Calibur7702Account.wrapSignature(keyHash, signature, hookData);
|
|
2060
|
+
}
|
|
1443
2061
|
/** The EntryPoint contract address this account targets */
|
|
1444
2062
|
entrypointAddress;
|
|
1445
2063
|
/** The Calibur singleton (delegatee) contract address */
|
|
@@ -1469,6 +2087,46 @@ var Calibur7702Account = class Calibur7702Account extends SmartAccount {
|
|
|
1469
2087
|
return createUserOperationHash(userOperation, this.entrypointAddress, chainId);
|
|
1470
2088
|
}
|
|
1471
2089
|
/**
|
|
2090
|
+
* Build the EIP-712 typed data payload for a UserOperation under the
|
|
2091
|
+
* EntryPoint v0.8 / v0.9 domain. Useful for wallets that can only sign
|
|
2092
|
+
* typed data (`eth_signTypedData_v4`) — the digest of the returned payload
|
|
2093
|
+
* equals the `userOpHash`, so a typed-data signature over it produces a
|
|
2094
|
+
* wrapped Calibur signature that validates against the same hash on-chain.
|
|
2095
|
+
*
|
|
2096
|
+
* Intended for the secp256k1 key path (root key or any registered
|
|
2097
|
+
* secondary secp256k1 key), since `eth_signTypedData_v4` only produces
|
|
2098
|
+
* secp256k1 signatures. P-256 and WebAuthn-P256 keys sign with their
|
|
2099
|
+
* own primitives — for those, use {@link getUserOperationEip712Hash}
|
|
2100
|
+
* (the digest), which is the value-to-be-signed regardless of key type.
|
|
2101
|
+
*
|
|
2102
|
+
* @param userOperation - Unsigned UserOperation to wrap
|
|
2103
|
+
* @param chainId - Target chain ID
|
|
2104
|
+
* @param overrides - Override the entrypoint address (defaults to EntryPoint v0.8)
|
|
2105
|
+
* @returns EIP-712 {@link TypedData} payload ready for `signTypedData`
|
|
2106
|
+
* @throws {AbstractionKitError} if the target EntryPoint is not v0.8 / v0.9.
|
|
2107
|
+
*/
|
|
2108
|
+
static getUserOperationEip712Data(userOperation, chainId, overrides = {}) {
|
|
2109
|
+
return getUserOperationEip712DataV8V9(userOperation, overrides.entrypointAddress ?? "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108", chainId);
|
|
2110
|
+
}
|
|
2111
|
+
/**
|
|
2112
|
+
* Compute the EIP-712 digest of a UserOperation under the EntryPoint
|
|
2113
|
+
* v0.8 / v0.9 domain. For these EntryPoints this digest IS the
|
|
2114
|
+
* `userOpHash`; the wrapped Calibur signature is verified against this
|
|
2115
|
+
* hash on-chain.
|
|
2116
|
+
*
|
|
2117
|
+
* Universal across Calibur key types: secp256k1 and P-256 keys sign
|
|
2118
|
+
* this digest directly, each with their own signing primitive.
|
|
2119
|
+
*
|
|
2120
|
+
* @param userOperation - Unsigned UserOperation to hash
|
|
2121
|
+
* @param chainId - Target chain ID
|
|
2122
|
+
* @param overrides - Override the entrypoint address (defaults to EntryPoint v0.8)
|
|
2123
|
+
* @returns The EIP-712 digest as a hex string
|
|
2124
|
+
* @throws {AbstractionKitError} if the target EntryPoint is not v0.8 / v0.9.
|
|
2125
|
+
*/
|
|
2126
|
+
static getUserOperationEip712Hash(userOperation, chainId, overrides = {}) {
|
|
2127
|
+
return getUserOperationEip712HashV8V9(userOperation, overrides.entrypointAddress ?? "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108", chainId);
|
|
2128
|
+
}
|
|
2129
|
+
/**
|
|
1472
2130
|
* Encode calldata for `executeUserOp(bytes)` with BatchedCall format.
|
|
1473
2131
|
* All transactions (even single ones) go through the same BatchedCall path.
|
|
1474
2132
|
*
|
|
@@ -1520,7 +2178,7 @@ var Calibur7702Account = class Calibur7702Account extends SmartAccount {
|
|
|
1520
2178
|
}
|
|
1521
2179
|
let skipEip7702Auth = false;
|
|
1522
2180
|
let delegationCheckOp = null;
|
|
1523
|
-
if (overrides.eip7702Auth != null && providerRpc != null) delegationCheckOp = getDelegatedAddress(this.accountAddress
|
|
2181
|
+
if (overrides.eip7702Auth != null && providerRpc != null) delegationCheckOp = JsonRpcNode.from(providerRpc).getDelegatedAddress(this.accountAddress).catch(() => null);
|
|
1524
2182
|
if (overrides.eip7702Auth != null && eip7702AuthNonce == null) {
|
|
1525
2183
|
let eip7702AuthNonceOp;
|
|
1526
2184
|
if (providerRpc != null) eip7702AuthNonceOp = sendJsonRpcRequest(providerRpc, "eth_getTransactionCount", [this.accountAddress, "latest"]);
|
|
@@ -1622,7 +2280,7 @@ var Calibur7702Account = class Calibur7702Account extends SmartAccount {
|
|
|
1622
2280
|
userOperation.maxPriorityFeePerGas = 0n;
|
|
1623
2281
|
const userOperationToEstimate = { ...userOperation };
|
|
1624
2282
|
userOperationToEstimate.signature = overrides.dummySignature ?? Calibur7702Account.dummySignature;
|
|
1625
|
-
const estimation = await
|
|
2283
|
+
const estimation = await Bundler.from(bundlerRpc).estimateUserOperationGas(userOperationToEstimate, this.entrypointAddress, overrides.state_override_set);
|
|
1626
2284
|
preVerificationGas = BigInt(estimation.preVerificationGas);
|
|
1627
2285
|
verificationGasLimit = BigInt(estimation.verificationGasLimit);
|
|
1628
2286
|
callGasLimit = BigInt(estimation.callGasLimit);
|
|
@@ -1668,38 +2326,46 @@ var Calibur7702Account = class Calibur7702Account extends SmartAccount {
|
|
|
1668
2326
|
return Calibur7702Account.wrapSignature(keyHash, ecdsaSig, hookData);
|
|
1669
2327
|
}
|
|
1670
2328
|
/**
|
|
1671
|
-
* Schemes Calibur accepts from a Signer.
|
|
1672
|
-
*
|
|
1673
|
-
*
|
|
2329
|
+
* Schemes Calibur accepts from a Signer. EntryPoint v0.8/v0.9 introduced
|
|
2330
|
+
* an EIP-712 domain at the EntryPoint contract, and the userOpHash IS the
|
|
2331
|
+
* EIP-712 digest of the PackedUserOperation under that domain — so signing
|
|
2332
|
+
* the typed data and signing the raw hash produce signatures that verify
|
|
2333
|
+
* against the same `userOpHash` (and recover to the same signer address).
|
|
2334
|
+
* Deterministic-ECDSA signers (ethers, viem, MetaMask) yield byte-identical
|
|
2335
|
+
* bytes; signers that differ in `s` / `v` normalization still validate the
|
|
2336
|
+
* same on-chain.
|
|
2337
|
+
*
|
|
2338
|
+
* `typedData` is listed first so JSON-RPC wallets that can only sign typed
|
|
2339
|
+
* data work without a separate code path; `hash` remains a valid fallback
|
|
2340
|
+
* for local-key signers.
|
|
1674
2341
|
*/
|
|
1675
|
-
static ACCEPTED_SIGNING_SCHEMES = ["hash"];
|
|
2342
|
+
static ACCEPTED_SIGNING_SCHEMES = ["typedData", "hash"];
|
|
1676
2343
|
/**
|
|
1677
|
-
* Sign a UserOperation
|
|
1678
|
-
*
|
|
1679
|
-
*
|
|
1680
|
-
*
|
|
1681
|
-
*
|
|
1682
|
-
*
|
|
1683
|
-
* `fromPrivateKey(pk)`. For secondary (non-root) keys, pass the key
|
|
1684
|
-
* hash via `overrides.keyHash`.
|
|
2344
|
+
* Sign a UserOperation with an {@link AkSigner}. The signer can implement
|
|
2345
|
+
* either `signTypedData` (preferred — JSON-RPC wallets, viem `WalletClient`)
|
|
2346
|
+
* or `signHash` (local keys, hardware wallets). Both schemes produce
|
|
2347
|
+
* signatures that validate against the same `userOpHash` because the
|
|
2348
|
+
* v0.8 / v0.9 userOpHash IS the EIP-712 digest of the PackedUserOperation
|
|
2349
|
+
* (deterministic-ECDSA signers yield byte-identical bytes).
|
|
1685
2350
|
*
|
|
1686
|
-
*
|
|
1687
|
-
*
|
|
1688
|
-
* userOp.signature = await account.signUserOperationWithSigner(
|
|
1689
|
-
* userOp, fromViem(privateKeyToAccount(pk)), chainId,
|
|
1690
|
-
* );
|
|
2351
|
+
* Signers that implement neither method fail offline with an actionable
|
|
2352
|
+
* error.
|
|
1691
2353
|
*/
|
|
1692
2354
|
async signUserOperationWithSigner(userOperation, signer, chainId, overrides = {}) {
|
|
1693
|
-
const
|
|
2355
|
+
const scheme = pickScheme(signer, Calibur7702Account.ACCEPTED_SIGNING_SCHEMES, {
|
|
1694
2356
|
accountName: "Calibur (raw ECDSA over userOpHash)",
|
|
1695
2357
|
signerIndex: 0
|
|
1696
|
-
})
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
2358
|
+
});
|
|
2359
|
+
const hash = createUserOperationHash(userOperation, this.entrypointAddress, chainId);
|
|
2360
|
+
const context = {
|
|
2361
|
+
userOperation,
|
|
2362
|
+
chainId,
|
|
2363
|
+
entryPoint: this.entrypointAddress
|
|
2364
|
+
};
|
|
2365
|
+
const signature = await invokeSigner(signer, scheme, {
|
|
2366
|
+
hash,
|
|
2367
|
+
typedData: scheme === "typedData" ? Calibur7702Account.getUserOperationEip712Data(userOperation, chainId, { entrypointAddress: this.entrypointAddress }) : void 0,
|
|
2368
|
+
context
|
|
1703
2369
|
});
|
|
1704
2370
|
const keyHash = overrides.keyHash ?? ROOT_KEY_HASH;
|
|
1705
2371
|
const hookData = overrides.hookData ?? "0x";
|
|
@@ -1743,7 +2409,7 @@ var Calibur7702Account = class Calibur7702Account extends SmartAccount {
|
|
|
1743
2409
|
* @returns A {@link SendUseroperationResponse} that can be used to wait for inclusion
|
|
1744
2410
|
*/
|
|
1745
2411
|
async sendUserOperation(userOperation, bundlerRpc) {
|
|
1746
|
-
const bundler =
|
|
2412
|
+
const bundler = Bundler.from(bundlerRpc);
|
|
1747
2413
|
return new SendUseroperationResponse(await bundler.sendUserOperation(userOperation, this.entrypointAddress), bundler, this.entrypointAddress);
|
|
1748
2414
|
}
|
|
1749
2415
|
/**
|
|
@@ -1929,7 +2595,7 @@ var Calibur7702Account = class Calibur7702Account extends SmartAccount {
|
|
|
1929
2595
|
*/
|
|
1930
2596
|
async createRevokeDelegationRawTransaction(chainId, eoaPrivateKey, providerRpc, overrides = {}) {
|
|
1931
2597
|
if (new Wallet(eoaPrivateKey).address.toLowerCase() !== this.accountAddress.toLowerCase()) throw new AbstractionKitError("BAD_DATA", `eoaPrivateKey does not match accountAddress (${this.accountAddress})`);
|
|
1932
|
-
const delegatedTo = await getDelegatedAddress(this.accountAddress
|
|
2598
|
+
const delegatedTo = await JsonRpcNode.from(providerRpc).getDelegatedAddress(this.accountAddress);
|
|
1933
2599
|
if (delegatedTo === null) throw new AbstractionKitError("BAD_DATA", "Account is not delegated — nothing to revoke");
|
|
1934
2600
|
if (delegatedTo.toLowerCase() !== this.delegateeAddress.toLowerCase()) throw new AbstractionKitError("BAD_DATA", "Account is delegated to a different address (" + delegatedTo + "), not " + this.delegateeAddress + " — use the correct account class to revoke");
|
|
1935
2601
|
const results = {};
|
|
@@ -1993,14 +2659,14 @@ var Calibur7702Account = class Calibur7702Account extends SmartAccount {
|
|
|
1993
2659
|
/**
|
|
1994
2660
|
* Check if this EOA is delegated to this account's singleton (delegatee).
|
|
1995
2661
|
* Returns `false` if not delegated at all or delegated to a different
|
|
1996
|
-
* singleton. Use
|
|
1997
|
-
*
|
|
2662
|
+
* singleton. Use {@link JsonRpcNode.getDelegatedAddress} to get the raw
|
|
2663
|
+
* delegatee address regardless of which singleton it is.
|
|
1998
2664
|
*
|
|
1999
2665
|
* @param providerRpc - JSON-RPC endpoint
|
|
2000
2666
|
* @returns True if the account is delegated to `this.delegateeAddress`
|
|
2001
2667
|
*/
|
|
2002
2668
|
async isDelegatedToThisAccount(providerRpc) {
|
|
2003
|
-
const address = await getDelegatedAddress(this.accountAddress
|
|
2669
|
+
const address = await JsonRpcNode.from(providerRpc).getDelegatedAddress(this.accountAddress);
|
|
2004
2670
|
if (address === null) return false;
|
|
2005
2671
|
return address.toLowerCase() === this.delegateeAddress.toLowerCase();
|
|
2006
2672
|
}
|
|
@@ -2548,7 +3214,8 @@ async function simulateSenderCallDataWithTenderly(tenderlyAccountSlug, tenderlyP
|
|
|
2548
3214
|
* @returns The simulation results from Tenderly.
|
|
2549
3215
|
*/
|
|
2550
3216
|
async function callTenderlySimulateBundle(tenderlyAccountSlug, tenderlyProjectSlug, tenderlyAccessKey, transactions) {
|
|
2551
|
-
|
|
3217
|
+
const tenderlyUrl = "https://api.tenderly.co/api/v1/account/" + tenderlyAccountSlug + "/project/" + tenderlyProjectSlug + "/simulate-bundle";
|
|
3218
|
+
const simulations = transactions.map((transaction) => {
|
|
2552
3219
|
const transactionObject = {
|
|
2553
3220
|
network_id: transaction.chainId.toString(),
|
|
2554
3221
|
save: transaction.save ?? true,
|
|
@@ -2577,11 +3244,53 @@ async function callTenderlySimulateBundle(tenderlyAccountSlug, tenderlyProjectSl
|
|
|
2577
3244
|
if (transaction.generateAccessList != null) transactionObject.generate_access_list = transaction.generateAccessList;
|
|
2578
3245
|
if (transaction.accessList != null) transactionObject.access_list = transaction.accessList;
|
|
2579
3246
|
return transactionObject;
|
|
2580
|
-
})
|
|
3247
|
+
});
|
|
3248
|
+
const headers = {
|
|
2581
3249
|
Accept: "application/json",
|
|
2582
3250
|
"Content-Type": "application/json",
|
|
2583
3251
|
"X-Access-Key": tenderlyAccessKey
|
|
2584
|
-
}
|
|
3252
|
+
};
|
|
3253
|
+
const body = JSON.stringify({
|
|
3254
|
+
method: "tenderly_simulateBundle",
|
|
3255
|
+
simulations,
|
|
3256
|
+
id: Date.now(),
|
|
3257
|
+
jsonrpc: "2.0"
|
|
3258
|
+
}, (_key, value) => typeof value === "bigint" ? `0x${value.toString(16)}` : value);
|
|
3259
|
+
let response;
|
|
3260
|
+
try {
|
|
3261
|
+
response = await fetch(tenderlyUrl, {
|
|
3262
|
+
method: "POST",
|
|
3263
|
+
headers,
|
|
3264
|
+
body,
|
|
3265
|
+
redirect: "follow"
|
|
3266
|
+
});
|
|
3267
|
+
} catch (err) {
|
|
3268
|
+
const e = ensureError(err);
|
|
3269
|
+
throw new AbstractionKitError("TENDERLY_NETWORK_ERROR", `tenderly_simulateBundle network error: ${e.message}`, { cause: e });
|
|
3270
|
+
}
|
|
3271
|
+
if (!response.ok) throw new AbstractionKitError("TENDERLY_HTTP_ERROR", `tenderly_simulateBundle HTTP ${response.status}: ${response.statusText}`, {
|
|
3272
|
+
errno: response.status,
|
|
3273
|
+
context: {
|
|
3274
|
+
status: response.status,
|
|
3275
|
+
statusText: response.statusText
|
|
3276
|
+
}
|
|
3277
|
+
});
|
|
3278
|
+
const responseText = await response.text();
|
|
3279
|
+
let json;
|
|
3280
|
+
try {
|
|
3281
|
+
json = JSON.parse(responseText);
|
|
3282
|
+
} catch (err) {
|
|
3283
|
+
const e = ensureError(err);
|
|
3284
|
+
throw new AbstractionKitError("TENDERLY_INVALID_JSON", `tenderly_simulateBundle returned invalid JSON: ${e.message}`, {
|
|
3285
|
+
cause: e,
|
|
3286
|
+
context: { responseText }
|
|
3287
|
+
});
|
|
3288
|
+
}
|
|
3289
|
+
if (json.simulation_results != null) return json.simulation_results;
|
|
3290
|
+
throw new AbstractionKitError("TENDERLY_SIMULATION_ERROR", "tenderly_simulateBundle failed", {
|
|
3291
|
+
errno: json.error?.code,
|
|
3292
|
+
context: JSON.stringify(json)
|
|
3293
|
+
});
|
|
2585
3294
|
}
|
|
2586
3295
|
//#endregion
|
|
2587
3296
|
//#region src/account/Safe/multisend.ts
|
|
@@ -2807,7 +3516,7 @@ var SafeAccount = class SafeAccount extends SmartAccount {
|
|
|
2807
3516
|
* ```
|
|
2808
3517
|
*/
|
|
2809
3518
|
static async isDeployed(accountAddress, nodeRpcUrl) {
|
|
2810
|
-
return (await
|
|
3519
|
+
return (await JsonRpcNode.from(nodeRpcUrl).getCode(accountAddress, "latest")).length > 2;
|
|
2811
3520
|
}
|
|
2812
3521
|
/**
|
|
2813
3522
|
* encode calldata for a single MetaTransaction to be executed by Safe account
|
|
@@ -3191,11 +3900,11 @@ var SafeAccount = class SafeAccount extends SmartAccount {
|
|
|
3191
3900
|
/**
|
|
3192
3901
|
* sends a useroperation to a bundler rpc
|
|
3193
3902
|
* @param userOperation - useroperation to send
|
|
3194
|
-
* @param bundlerRpc - bundler rpc
|
|
3903
|
+
* @param bundlerRpc - bundler rpc URL, {@link Transport}, or pre-constructed {@link Bundler}
|
|
3195
3904
|
* @returns promise with SendUseroperationResponse
|
|
3196
3905
|
*/
|
|
3197
3906
|
async sendUserOperation(userOperation, bundlerRpc) {
|
|
3198
|
-
const bundler =
|
|
3907
|
+
const bundler = Bundler.from(bundlerRpc);
|
|
3199
3908
|
return new SendUseroperationResponse(await bundler.sendUserOperation(userOperation, this.entrypointAddress), bundler, this.entrypointAddress);
|
|
3200
3909
|
}
|
|
3201
3910
|
/**
|
|
@@ -3378,7 +4087,7 @@ var SafeAccount = class SafeAccount extends SmartAccount {
|
|
|
3378
4087
|
validUntil,
|
|
3379
4088
|
isMultiChainSignature: overrides.isMultiChainSignature
|
|
3380
4089
|
});
|
|
3381
|
-
const bundler =
|
|
4090
|
+
const bundler = Bundler.from(bundlerRpc);
|
|
3382
4091
|
const inputMaxFeePerGas = userOperation.maxFeePerGas;
|
|
3383
4092
|
const inputMaxPriorityFeePerGas = userOperation.maxPriorityFeePerGas;
|
|
3384
4093
|
userOperation.maxFeePerGas = 0n;
|
|
@@ -3901,7 +4610,7 @@ var SafeAccount = class SafeAccount extends SmartAccount {
|
|
|
3901
4610
|
webAuthnSignerSingleton: overrides.webAuthnSignerSingleton,
|
|
3902
4611
|
webAuthnSignerProxyCreationCode
|
|
3903
4612
|
});
|
|
3904
|
-
if ((await
|
|
4613
|
+
if ((await JsonRpcNode.from(nodeRpcUrl).getCode(newOwnerT, "latest")).length < 3) deployNewOwnerSignerMetaTransaction = SafeAccount.createDeployWebAuthnVerifierMetaTransaction(newOwner.x, newOwner.y, {
|
|
3905
4614
|
eip7212WebAuthnPrecompileVerifier: overrides.eip7212WebAuthnPrecompileVerifier,
|
|
3906
4615
|
eip7212WebAuthnContractVerifier: overrides.eip7212WebAuthnContractVerifier,
|
|
3907
4616
|
webAuthnSignerFactory: overrides.webAuthnSignerFactory
|
|
@@ -3984,7 +4693,7 @@ var SafeAccount = class SafeAccount extends SmartAccount {
|
|
|
3984
4693
|
webAuthnSignerProxyCreationCode
|
|
3985
4694
|
});
|
|
3986
4695
|
if (overrides.nodeRpcUrl == null) throw new RangeError("overrides.nodeRpcUrl can't be null if adding a webauthn owner");
|
|
3987
|
-
if ((await
|
|
4696
|
+
if ((await JsonRpcNode.from(overrides.nodeRpcUrl).getCode(newOwnerT, "latest")).length < 3) deployNewOwnerSignerMetaTransaction = SafeAccount.createDeployWebAuthnVerifierMetaTransaction(newOwner.x, newOwner.y, {
|
|
3988
4697
|
eip7212WebAuthnPrecompileVerifier: overrides.eip7212WebAuthnPrecompileVerifier,
|
|
3989
4698
|
eip7212WebAuthnContractVerifier: overrides.eip7212WebAuthnContractVerifier,
|
|
3990
4699
|
webAuthnSignerFactory: overrides.webAuthnSignerFactory
|
|
@@ -4112,10 +4821,11 @@ var SafeAccount = class SafeAccount extends SmartAccount {
|
|
|
4112
4821
|
*/
|
|
4113
4822
|
async getOwners(nodeRpcUrl) {
|
|
4114
4823
|
const callData = createCallData(getFunctionSelector("getOwners()"), [], []);
|
|
4115
|
-
const
|
|
4824
|
+
const ethCallParams = {
|
|
4116
4825
|
to: this.accountAddress,
|
|
4117
4826
|
data: callData
|
|
4118
|
-
}
|
|
4827
|
+
};
|
|
4828
|
+
const getOwnersResult = await JsonRpcNode.from(nodeRpcUrl).call(ethCallParams, "latest");
|
|
4119
4829
|
return AbiCoder.defaultAbiCoder().decode(["address[]"], getOwnersResult)[0];
|
|
4120
4830
|
}
|
|
4121
4831
|
/**
|
|
@@ -4125,10 +4835,11 @@ var SafeAccount = class SafeAccount extends SmartAccount {
|
|
|
4125
4835
|
*/
|
|
4126
4836
|
async getThreshold(nodeRpcUrl) {
|
|
4127
4837
|
const callData = createCallData("0xe75235b8", [], []);
|
|
4128
|
-
const
|
|
4838
|
+
const ethCallParams = {
|
|
4129
4839
|
to: this.accountAddress,
|
|
4130
4840
|
data: callData
|
|
4131
|
-
}
|
|
4841
|
+
};
|
|
4842
|
+
const getThresholdResult = await JsonRpcNode.from(nodeRpcUrl).call(ethCallParams, "latest");
|
|
4132
4843
|
const decodedCalldata = AbiCoder.defaultAbiCoder().decode(["uint256"], getThresholdResult);
|
|
4133
4844
|
return Number(decodedCalldata[0]);
|
|
4134
4845
|
}
|
|
@@ -4147,10 +4858,11 @@ var SafeAccount = class SafeAccount extends SmartAccount {
|
|
|
4147
4858
|
let pageSize = overrides.pageSize;
|
|
4148
4859
|
if (pageSize == null) pageSize = 10n;
|
|
4149
4860
|
const callData = createCallData("0xcc2f8452", ["address", "uint256"], [start, pageSize]);
|
|
4150
|
-
const
|
|
4861
|
+
const ethCallParams = {
|
|
4151
4862
|
to: this.accountAddress,
|
|
4152
4863
|
data: callData
|
|
4153
|
-
}
|
|
4864
|
+
};
|
|
4865
|
+
const getModulesResult = await JsonRpcNode.from(nodeRpcUrl).call(ethCallParams, "latest");
|
|
4154
4866
|
if (getModulesResult === "0x") throw new AbstractionKitError("BAD_DATA", "getModules returned an empty result, the target account is probably not deployed yet.");
|
|
4155
4867
|
const decodedCalldata = AbiCoder.defaultAbiCoder().decode(["address[]", "address"], getModulesResult);
|
|
4156
4868
|
return [decodedCalldata[0], decodedCalldata[1]];
|
|
@@ -4166,10 +4878,11 @@ var SafeAccount = class SafeAccount extends SmartAccount {
|
|
|
4166
4878
|
*/
|
|
4167
4879
|
async isModuleEnabled(nodeRpcUrl, moduleAddress) {
|
|
4168
4880
|
const callData = createCallData(getFunctionSelector("isModuleEnabled(address)"), ["address"], [moduleAddress]);
|
|
4169
|
-
const
|
|
4881
|
+
const ethCallParams = {
|
|
4170
4882
|
to: this.accountAddress,
|
|
4171
4883
|
data: callData
|
|
4172
|
-
}
|
|
4884
|
+
};
|
|
4885
|
+
const isModuleEnabledResult = await JsonRpcNode.from(nodeRpcUrl).call(ethCallParams, "latest");
|
|
4173
4886
|
return AbiCoder.defaultAbiCoder().decode(["bool"], isModuleEnabledResult)[0];
|
|
4174
4887
|
}
|
|
4175
4888
|
/**
|
|
@@ -4236,7 +4949,7 @@ var SafeAccount = class SafeAccount extends SmartAccount {
|
|
|
4236
4949
|
data: callData
|
|
4237
4950
|
};
|
|
4238
4951
|
const deployedByteCode = SafeAccount.createSafeWebAuthnSignerProxyDeployedByteCode(signer, eip7212WebAuthnPrecompileVerifier, eip7212WebAuthnContractVerifier, webAuthnSignerSingleton);
|
|
4239
|
-
const isModuleEnabledResult = await
|
|
4952
|
+
const isModuleEnabledResult = await JsonRpcNode.from(nodeRpcUrl).call(ethCallParams, "latest", { [arbitraryAddress]: { code: deployedByteCode } });
|
|
4240
4953
|
return AbiCoder.defaultAbiCoder().decode(["bool"], isModuleEnabledResult)[0];
|
|
4241
4954
|
}
|
|
4242
4955
|
static createSafeWebAuthnSignerProxyDeployedByteCode(signer, eip7212WebAuthnPrecompileVerifier, eip7212WebAuthnContractVerifier, webAuthnSignerSingleton) {
|
|
@@ -4356,7 +5069,7 @@ var SafeAccount = class SafeAccount extends SmartAccount {
|
|
|
4356
5069
|
* @param toolVersion - tool version; defaults to the current abstractionkit version
|
|
4357
5070
|
* @returns the on-chain identifier as a hex string (not 0x prefixed)
|
|
4358
5071
|
*/
|
|
4359
|
-
function generateOnChainIdentifier(project, platform = "Web", tool = "abstractionkit", toolVersion = "0.3.
|
|
5072
|
+
function generateOnChainIdentifier(project, platform = "Web", tool = "abstractionkit", toolVersion = "0.3.8") {
|
|
4360
5073
|
const identifierPrefix = "5afe";
|
|
4361
5074
|
const identifierVersion = "00";
|
|
4362
5075
|
const projectHash = keccak256(`0x${Buffer.from(project, "utf8").toString("hex")}`).slice(-20);
|
|
@@ -4619,10 +5332,11 @@ var AllowanceModule = class AllowanceModule extends SafeModule {
|
|
|
4619
5332
|
*/
|
|
4620
5333
|
async getTokens(nodeRpcUrl, safeAddress, delegate) {
|
|
4621
5334
|
const callData = createCallData("0x8d0e8e1d", ["address", "address"], [safeAddress, delegate]);
|
|
4622
|
-
const
|
|
5335
|
+
const ethCallParams = {
|
|
4623
5336
|
to: this.moduleAddress,
|
|
4624
5337
|
data: callData
|
|
4625
|
-
}
|
|
5338
|
+
};
|
|
5339
|
+
const tokens = await JsonRpcNode.from(nodeRpcUrl).call(ethCallParams, "latest");
|
|
4626
5340
|
this.checkForEmptyResultAndRevert(tokens, "getTokens");
|
|
4627
5341
|
return this.abiCoder.decode(["address[]"], tokens)[0];
|
|
4628
5342
|
}
|
|
@@ -4644,10 +5358,11 @@ var AllowanceModule = class AllowanceModule extends SafeModule {
|
|
|
4644
5358
|
delegate,
|
|
4645
5359
|
token
|
|
4646
5360
|
]);
|
|
4647
|
-
const
|
|
5361
|
+
const ethCallParams = {
|
|
4648
5362
|
to: this.moduleAddress,
|
|
4649
5363
|
data: callData
|
|
4650
|
-
}
|
|
5364
|
+
};
|
|
5365
|
+
const tokenAllowance = await JsonRpcNode.from(nodeRpcUrl).call(ethCallParams, "latest");
|
|
4651
5366
|
this.checkForEmptyResultAndRevert(tokenAllowance, "getTokenAllowance");
|
|
4652
5367
|
const allowance = this.abiCoder.decode(["uint256[5]"], tokenAllowance)[0];
|
|
4653
5368
|
return {
|
|
@@ -4699,10 +5414,11 @@ var AllowanceModule = class AllowanceModule extends SafeModule {
|
|
|
4699
5414
|
start,
|
|
4700
5415
|
pageSize
|
|
4701
5416
|
]);
|
|
4702
|
-
const
|
|
5417
|
+
const ethCallParams = {
|
|
4703
5418
|
to: this.moduleAddress,
|
|
4704
5419
|
data: callData
|
|
4705
|
-
}
|
|
5420
|
+
};
|
|
5421
|
+
const delegates = await JsonRpcNode.from(nodeRpcUrl).call(ethCallParams, "latest");
|
|
4706
5422
|
this.checkForEmptyResultAndRevert(delegates, "getDelegates");
|
|
4707
5423
|
const decodedCalldata = this.abiCoder.decode(["address[]", "uint48"], delegates);
|
|
4708
5424
|
return {
|
|
@@ -4787,13 +5503,19 @@ var SocialRecoveryModule = class SocialRecoveryModule extends SafeModule {
|
|
|
4787
5503
|
"address",
|
|
4788
5504
|
"address[]",
|
|
4789
5505
|
"uint256",
|
|
4790
|
-
"(address,bytes)",
|
|
5506
|
+
"(address,bytes)[]",
|
|
4791
5507
|
"bool"
|
|
4792
5508
|
], [
|
|
4793
5509
|
accountAddress,
|
|
4794
5510
|
newOwners,
|
|
4795
5511
|
newThreshold,
|
|
4796
|
-
signaturePairList.
|
|
5512
|
+
[...signaturePairList].sort((a, b) => {
|
|
5513
|
+
const aSigner = BigInt(a.signer);
|
|
5514
|
+
const bSigner = BigInt(b.signer);
|
|
5515
|
+
if (aSigner < bSigner) return -1;
|
|
5516
|
+
if (aSigner > bSigner) return 1;
|
|
5517
|
+
throw new AbstractionKitError("BAD_DATA", `Duplicate signer in recovery signaturePairList: ${a.signer}`);
|
|
5518
|
+
}).map((signaturePair) => [signaturePair.signer, signaturePair.signature]),
|
|
4797
5519
|
execute
|
|
4798
5520
|
]);
|
|
4799
5521
|
return {
|
|
@@ -4945,10 +5667,11 @@ var SocialRecoveryModule = class SocialRecoveryModule extends SafeModule {
|
|
|
4945
5667
|
newThreshold,
|
|
4946
5668
|
nonce
|
|
4947
5669
|
]);
|
|
4948
|
-
const
|
|
5670
|
+
const ethCallParams = {
|
|
4949
5671
|
to: this.moduleAddress,
|
|
4950
5672
|
data: callData
|
|
4951
|
-
}
|
|
5673
|
+
};
|
|
5674
|
+
const recoveryHashResult = await JsonRpcNode.from(nodeRpcUrl).call(ethCallParams, "latest");
|
|
4952
5675
|
this.checkForEmptyResultAndRevert(recoveryHashResult, "getRecoveryHash");
|
|
4953
5676
|
return this.abiCoder.decode(["bytes32"], recoveryHashResult)[0];
|
|
4954
5677
|
}
|
|
@@ -4960,10 +5683,11 @@ var SocialRecoveryModule = class SocialRecoveryModule extends SafeModule {
|
|
|
4960
5683
|
*/
|
|
4961
5684
|
async getRecoveryRequest(nodeRpcUrl, accountAddress) {
|
|
4962
5685
|
const callData = createCallData("0x4f9a28b9", ["address"], [accountAddress]);
|
|
4963
|
-
const
|
|
5686
|
+
const ethCallParams = {
|
|
4964
5687
|
to: this.moduleAddress,
|
|
4965
5688
|
data: callData
|
|
4966
|
-
}
|
|
5689
|
+
};
|
|
5690
|
+
const recoveryRequestResult = await JsonRpcNode.from(nodeRpcUrl).call(ethCallParams, "latest");
|
|
4967
5691
|
this.checkForEmptyResultAndRevert(recoveryRequestResult, "getRecoveryRequest");
|
|
4968
5692
|
const decodedCalldata = this.abiCoder.decode(["(uint256,uint256,uint64,address[])"], recoveryRequestResult);
|
|
4969
5693
|
return {
|
|
@@ -4991,10 +5715,11 @@ var SocialRecoveryModule = class SocialRecoveryModule extends SafeModule {
|
|
|
4991
5715
|
newOwners,
|
|
4992
5716
|
newThreshold
|
|
4993
5717
|
]);
|
|
4994
|
-
const
|
|
5718
|
+
const ethCallParams = {
|
|
4995
5719
|
to: this.moduleAddress,
|
|
4996
5720
|
data: callData
|
|
4997
|
-
}
|
|
5721
|
+
};
|
|
5722
|
+
const recoveryApprovalResult = await JsonRpcNode.from(nodeRpcUrl).call(ethCallParams, "latest");
|
|
4998
5723
|
this.checkForEmptyResultAndRevert(recoveryApprovalResult, "getRecoveryApprovals");
|
|
4999
5724
|
const decodedCalldata = this.abiCoder.decode(["uint256"], recoveryApprovalResult);
|
|
5000
5725
|
return BigInt(decodedCalldata[0]);
|
|
@@ -5020,10 +5745,11 @@ var SocialRecoveryModule = class SocialRecoveryModule extends SafeModule {
|
|
|
5020
5745
|
newOwners,
|
|
5021
5746
|
newThreshold
|
|
5022
5747
|
]);
|
|
5023
|
-
const
|
|
5748
|
+
const ethCallParams = {
|
|
5024
5749
|
to: this.moduleAddress,
|
|
5025
5750
|
data: callData
|
|
5026
|
-
}
|
|
5751
|
+
};
|
|
5752
|
+
const hasGuardianApprovedResult = await JsonRpcNode.from(nodeRpcUrl).call(ethCallParams, "latest");
|
|
5027
5753
|
this.checkForEmptyResultAndRevert(hasGuardianApprovedResult, "hasGuardianApproved");
|
|
5028
5754
|
const decodedCalldata = this.abiCoder.decode(["bool"], hasGuardianApprovedResult);
|
|
5029
5755
|
return Boolean(decodedCalldata[0]);
|
|
@@ -5038,10 +5764,11 @@ var SocialRecoveryModule = class SocialRecoveryModule extends SafeModule {
|
|
|
5038
5764
|
*/
|
|
5039
5765
|
async isGuardian(nodeRpcUrl, accountAddress, guardian) {
|
|
5040
5766
|
const callData = createCallData("0xd4ee9734", ["address", "address"], [accountAddress, guardian]);
|
|
5041
|
-
const
|
|
5767
|
+
const ethCallParams = {
|
|
5042
5768
|
to: this.moduleAddress,
|
|
5043
5769
|
data: callData
|
|
5044
|
-
}
|
|
5770
|
+
};
|
|
5771
|
+
const isGuardianResult = await JsonRpcNode.from(nodeRpcUrl).call(ethCallParams, "latest");
|
|
5045
5772
|
this.checkForEmptyResultAndRevert(isGuardianResult, "isGuardian");
|
|
5046
5773
|
const decodedCalldata = this.abiCoder.decode(["bool"], isGuardianResult);
|
|
5047
5774
|
return Boolean(decodedCalldata[0]);
|
|
@@ -5054,10 +5781,11 @@ var SocialRecoveryModule = class SocialRecoveryModule extends SafeModule {
|
|
|
5054
5781
|
*/
|
|
5055
5782
|
async guardiansCount(nodeRpcUrl, accountAddress) {
|
|
5056
5783
|
const callData = createCallData("0xc026e7ee", ["address"], [accountAddress]);
|
|
5057
|
-
const
|
|
5784
|
+
const ethCallParams = {
|
|
5058
5785
|
to: this.moduleAddress,
|
|
5059
5786
|
data: callData
|
|
5060
|
-
}
|
|
5787
|
+
};
|
|
5788
|
+
const guardiansCountResult = await JsonRpcNode.from(nodeRpcUrl).call(ethCallParams, "latest");
|
|
5061
5789
|
this.checkForEmptyResultAndRevert(guardiansCountResult, "guardiansCount");
|
|
5062
5790
|
const decodedCalldata = this.abiCoder.decode(["uint256"], guardiansCountResult);
|
|
5063
5791
|
return BigInt(decodedCalldata[0]);
|
|
@@ -5070,10 +5798,11 @@ var SocialRecoveryModule = class SocialRecoveryModule extends SafeModule {
|
|
|
5070
5798
|
*/
|
|
5071
5799
|
async threshold(nodeRpcUrl, accountAddress) {
|
|
5072
5800
|
const callData = createCallData("0xc86ec2bf", ["address"], [accountAddress]);
|
|
5073
|
-
const
|
|
5801
|
+
const ethCallParams = {
|
|
5074
5802
|
to: this.moduleAddress,
|
|
5075
5803
|
data: callData
|
|
5076
|
-
}
|
|
5804
|
+
};
|
|
5805
|
+
const thresholdResult = await JsonRpcNode.from(nodeRpcUrl).call(ethCallParams, "latest");
|
|
5077
5806
|
this.checkForEmptyResultAndRevert(thresholdResult, "threshold");
|
|
5078
5807
|
const decodedCalldata = this.abiCoder.decode(["uint256"], thresholdResult);
|
|
5079
5808
|
return BigInt(decodedCalldata[0]);
|
|
@@ -5086,10 +5815,11 @@ var SocialRecoveryModule = class SocialRecoveryModule extends SafeModule {
|
|
|
5086
5815
|
*/
|
|
5087
5816
|
async getGuardians(nodeRpcUrl, accountAddress) {
|
|
5088
5817
|
const callData = createCallData("0xf18858ab", ["address"], [accountAddress]);
|
|
5089
|
-
const
|
|
5818
|
+
const ethCallParams = {
|
|
5090
5819
|
to: this.moduleAddress,
|
|
5091
5820
|
data: callData
|
|
5092
|
-
}
|
|
5821
|
+
};
|
|
5822
|
+
const getGuardiansResult = await JsonRpcNode.from(nodeRpcUrl).call(ethCallParams, "latest");
|
|
5093
5823
|
this.checkForEmptyResultAndRevert(getGuardiansResult, "threshold");
|
|
5094
5824
|
return this.abiCoder.decode(["address[]"], getGuardiansResult)[0];
|
|
5095
5825
|
}
|
|
@@ -5101,10 +5831,11 @@ var SocialRecoveryModule = class SocialRecoveryModule extends SafeModule {
|
|
|
5101
5831
|
*/
|
|
5102
5832
|
async nonce(nodeRpcUrl, accountAddress) {
|
|
5103
5833
|
const callData = createCallData("0x70ae92d2", ["address"], [accountAddress]);
|
|
5104
|
-
const
|
|
5834
|
+
const ethCallParams = {
|
|
5105
5835
|
to: this.moduleAddress,
|
|
5106
5836
|
data: callData
|
|
5107
|
-
}
|
|
5837
|
+
};
|
|
5838
|
+
const nonceResult = await JsonRpcNode.from(nodeRpcUrl).call(ethCallParams, "latest");
|
|
5108
5839
|
this.checkForEmptyResultAndRevert(nonceResult, "threshold");
|
|
5109
5840
|
const decodedCalldata = this.abiCoder.decode(["uint256"], nonceResult);
|
|
5110
5841
|
return BigInt(decodedCalldata[0]);
|
|
@@ -6184,6 +6915,14 @@ var SafeMultiChainSigAccountV1 = class SafeMultiChainSigAccountV1 extends SafeAc
|
|
|
6184
6915
|
* {@link SafeAccountV0_3_0.signUserOperationWithSigners} for the full
|
|
6185
6916
|
* design rationale. Sets the multi-chain flag automatically.
|
|
6186
6917
|
*
|
|
6918
|
+
* Note the chainId plumbing asymmetry vs the multi-op variant:
|
|
6919
|
+
* - **Singular** (this method): `chainId` is a positional argument.
|
|
6920
|
+
* - **Plural** ({@link signUserOperationsWithSigners}): `chainId` lives
|
|
6921
|
+
* inside each `UserOperationToSign` element, since each op may
|
|
6922
|
+
* target a different chain.
|
|
6923
|
+
* Pick the variant that matches your bundle shape; don't pass the same
|
|
6924
|
+
* chainId twice.
|
|
6925
|
+
*
|
|
6187
6926
|
* @param userOperation - UserOperation to sign
|
|
6188
6927
|
* @param signers - one ExternalSigner per owner (any order)
|
|
6189
6928
|
* @param chainId - target chain id
|
|
@@ -6260,17 +6999,26 @@ var SafeMultiChainSigAccountV1 = class SafeMultiChainSigAccountV1 extends SafeAc
|
|
|
6260
6999
|
/**
|
|
6261
7000
|
* Sign a list of UserOperations with a single multi-chain signature. Each
|
|
6262
7001
|
* signer signs the Merkle root of the UserOperation EIP-712 hashes via
|
|
6263
|
-
*
|
|
6264
|
-
*
|
|
7002
|
+
* either `signTypedData` (the root is wrapped in an EIP-712 `MerkleTreeRoot`
|
|
7003
|
+
* message) or raw-hash signing; both schemes produce signatures that
|
|
7004
|
+
* validate against the same on-chain digest.
|
|
6265
7005
|
*
|
|
6266
7006
|
* Signers always receive {@link MultiOpSignContext}. The built-in adapters
|
|
6267
|
-
* `fromPrivateKey`, `fromViem`, and `
|
|
6268
|
-
*
|
|
6269
|
-
*
|
|
6270
|
-
*
|
|
6271
|
-
* (`Signer<SignContext>`) also don't work — they'd receive a context shape
|
|
7007
|
+
* `fromPrivateKey`, `fromViem`, `fromEthersWallet`, and `fromViemWalletClient`
|
|
7008
|
+
* all work here without retyping (`fromViemWalletClient` will sign the
|
|
7009
|
+
* typed-data Merkle wrapper). User-defined single-op signers
|
|
7010
|
+
* (`Signer<SignContext>`) still don't work — they'd receive a context shape
|
|
6272
7011
|
* they didn't declare.
|
|
6273
7012
|
*
|
|
7013
|
+
* Note the chainId plumbing asymmetry vs the single-op variant:
|
|
7014
|
+
* - **Plural** (this method): each `UserOperationToSign` carries its
|
|
7015
|
+
* own `chainId`, since a multichain bundle's ops target different
|
|
7016
|
+
* chains.
|
|
7017
|
+
* - **Singular** ({@link signUserOperationWithSigners}): `chainId` is
|
|
7018
|
+
* a positional argument.
|
|
7019
|
+
* For a length-1 bundle on this method, set `chainId` on the single
|
|
7020
|
+
* element rather than reaching for the singular variant.
|
|
7021
|
+
*
|
|
6274
7022
|
* @param userOperationsToSign - UserOperations + chain IDs + validity windows
|
|
6275
7023
|
* @param signers - one Signer per owner (any order; sorted by address on-chain)
|
|
6276
7024
|
* @returns one signature per input UserOperation, in the same order
|
|
@@ -6299,19 +7047,25 @@ var SafeMultiChainSigAccountV1 = class SafeMultiChainSigAccountV1 extends SafeAc
|
|
|
6299
7047
|
const [root, proofs] = generateMerkleProofs(userOperationsHashes);
|
|
6300
7048
|
const merkleTreeRootHash = TypedDataEncoder.hash({ verifyingContract: this.safe4337ModuleAddress }, EIP712_MULTI_CHAIN_OPERATIONS_TYPE, { merkleTreeRoot: root });
|
|
6301
7049
|
const normalizedAddresses = signers.map((signer) => getAddress(signer.address));
|
|
6302
|
-
|
|
6303
|
-
|
|
6304
|
-
|
|
6305
|
-
|
|
6306
|
-
}
|
|
6307
|
-
}
|
|
6308
|
-
const
|
|
7050
|
+
const typedDataForBundle = {
|
|
7051
|
+
domain: { verifyingContract: this.safe4337ModuleAddress },
|
|
7052
|
+
types: EIP712_MULTI_CHAIN_OPERATIONS_TYPE,
|
|
7053
|
+
primaryType: EIP712_MULTI_CHAIN_OPERATIONS_PRIMARY_TYPE,
|
|
7054
|
+
message: { merkleTreeRoot: root }
|
|
7055
|
+
};
|
|
7056
|
+
const schemes = signers.map((signer, i) => pickScheme(signer, ["typedData", "hash"], {
|
|
7057
|
+
accountName: "SafeMultiChainSigAccountV1 (multi-op Merkle root)",
|
|
7058
|
+
signerIndex: i
|
|
7059
|
+
}));
|
|
7060
|
+
const signatures = await Promise.all(signers.map((signer, i) => invokeSigner(signer, schemes[i], {
|
|
6309
7061
|
hash: merkleTreeRootHash,
|
|
7062
|
+
typedData: typedDataForBundle,
|
|
6310
7063
|
context
|
|
6311
7064
|
})));
|
|
6312
7065
|
const signerSignaturePairs = signers.map((_signer, i) => ({
|
|
6313
7066
|
signer: normalizedAddresses[i],
|
|
6314
|
-
signature: signatures[i]
|
|
7067
|
+
signature: signatures[i],
|
|
7068
|
+
isContractSignature: signers[i].type === "contract"
|
|
6315
7069
|
}));
|
|
6316
7070
|
const userOpSignatures = [];
|
|
6317
7071
|
userOperationsToSign.forEach((uopToSign, index) => {
|
|
@@ -6338,9 +7092,16 @@ var SafeMultiChainSigAccountV1 = class SafeMultiChainSigAccountV1 extends SafeAc
|
|
|
6338
7092
|
}
|
|
6339
7093
|
}
|
|
6340
7094
|
/**
|
|
6341
|
-
* Compute the EIP-712
|
|
6342
|
-
*
|
|
6343
|
-
*
|
|
7095
|
+
* Compute the EIP-712 Merkle root wrapper digest that owners sign once
|
|
7096
|
+
* across all UserOperations in a multi-chain bundle.
|
|
7097
|
+
*
|
|
7098
|
+
* Requires length≥2. For a single UserOperation use
|
|
7099
|
+
* {@link SafeMultiChainSigAccountV1.getUserOperationEip712Hash} (the on-chain
|
|
7100
|
+
* `merkleTreeDepth == 0` branch verifies against the per-op SafeOp digest,
|
|
7101
|
+
* not a Merkle wrapper, so signing the wrapper for length=1 produces bytes
|
|
7102
|
+
* the contract rejects with AA24).
|
|
7103
|
+
*
|
|
7104
|
+
* @param userOperationsToSignsToSign - list of UserOperations with their target chain IDs (length ≥ 2)
|
|
6344
7105
|
* @param overrides - optional overrides for the Safe 4337 module address
|
|
6345
7106
|
* @returns the EIP-712 hash as a hex string
|
|
6346
7107
|
*/
|
|
@@ -6351,12 +7112,25 @@ var SafeMultiChainSigAccountV1 = class SafeMultiChainSigAccountV1 extends SafeAc
|
|
|
6351
7112
|
/**
|
|
6352
7113
|
* Get the EIP-712 typed data components for a multi-chain Merkle tree root.
|
|
6353
7114
|
* Returns the domain, types, and message value needed for signing or hashing.
|
|
6354
|
-
*
|
|
7115
|
+
*
|
|
7116
|
+
* Requires length≥2. For a single UserOperation use
|
|
7117
|
+
* {@link SafeMultiChainSigAccountV1.getUserOperationEip712Data} (or
|
|
7118
|
+
* {@link SafeMultiChainSigAccountV1.getUserOperationEip712Hash}): those
|
|
7119
|
+
* multichain-class overrides default `safe4337ModuleAddress` to
|
|
7120
|
+
* `DEFAULT_SAFE_4337_MODULE_ADDRESS`. The on-chain depth=0 path verifies
|
|
7121
|
+
* against the per-op SafeOp digest, not a Merkle wrapper, so signing the
|
|
7122
|
+
* wrapper for length=1 produces bytes the contract rejects with AA24. The
|
|
7123
|
+
* parent `SafeAccount.getUserOperationEip712Data_V9` /
|
|
7124
|
+
* `getUserOperationEip712Hash_V9` helpers default to a different module and
|
|
7125
|
+
* would hash against the wrong verifying contract unless
|
|
7126
|
+
* `overrides.safe4337ModuleAddress` is supplied explicitly.
|
|
7127
|
+
*
|
|
7128
|
+
* @param userOperationsToSignsToSign - list of UserOperations with their target chain IDs (length ≥ 2)
|
|
6355
7129
|
* @param overrides - optional overrides for the Safe 4337 module address
|
|
6356
7130
|
* @returns an object with domain, types, and messageValue for EIP-712 signing
|
|
6357
7131
|
*/
|
|
6358
7132
|
static getMultiChainSingleSignatureUserOperationsEip712Data(userOperationsToSign, overrides = {}) {
|
|
6359
|
-
if (userOperationsToSign.length <
|
|
7133
|
+
if (userOperationsToSign.length < 2) throw new RangeError("Multi-chain Merkle wrapper helpers require >= 2 userOperations. For a single UserOperation, use SafeMultiChainSigAccountV1.getUserOperationEip712Data or SafeMultiChainSigAccountV1.getUserOperationEip712Hash (these multichain-class overrides default safe4337ModuleAddress to DEFAULT_SAFE_4337_MODULE_ADDRESS, the multi-chain module). The on-chain depth=0 path verifies against the per-op SafeOp digest, not a Merkle wrapper. If calling the parent SafeAccount.getUserOperationEip712Data_V9 / getUserOperationEip712Hash_V9 helpers directly, you must pass overrides.safe4337ModuleAddress = SafeMultiChainSigAccountV1.DEFAULT_SAFE_4337_MODULE_ADDRESS explicitly so signatures hash the correct verifying contract.");
|
|
6360
7134
|
const safe4337ModuleAddress = overrides.safe4337ModuleAddress ?? SafeMultiChainSigAccountV1.DEFAULT_SAFE_4337_MODULE_ADDRESS;
|
|
6361
7135
|
const userOperationsHashes = [];
|
|
6362
7136
|
userOperationsToSign.forEach((userOperationsToSign, _index) => {
|
|
@@ -6509,13 +7283,13 @@ var BaseSimple7702Account = class BaseSimple7702Account extends SmartAccount {
|
|
|
6509
7283
|
/**
|
|
6510
7284
|
* Check if this EOA is delegated to the expected delegatee address via EIP-7702.
|
|
6511
7285
|
* Returns `true` only when delegated to `this.delegateeAddress`.
|
|
6512
|
-
* Use `getDelegatedAddress()` directly to get the raw delegatee address.
|
|
7286
|
+
* Use `JsonRpcNode.getDelegatedAddress()` directly to get the raw delegatee address.
|
|
6513
7287
|
*
|
|
6514
7288
|
* @param providerRpc - Ethereum JSON-RPC node URL
|
|
6515
7289
|
* @returns `true` if delegated to the expected address, `false` otherwise
|
|
6516
7290
|
*/
|
|
6517
7291
|
async isDelegatedToThisAccount(providerRpc) {
|
|
6518
|
-
const address = await getDelegatedAddress(this.accountAddress
|
|
7292
|
+
const address = await JsonRpcNode.from(providerRpc).getDelegatedAddress(this.accountAddress);
|
|
6519
7293
|
if (address === null) return false;
|
|
6520
7294
|
return address.toLowerCase() === this.delegateeAddress.toLowerCase();
|
|
6521
7295
|
}
|
|
@@ -6536,7 +7310,7 @@ var BaseSimple7702Account = class BaseSimple7702Account extends SmartAccount {
|
|
|
6536
7310
|
* @returns Signed raw transaction hex, ready for `eth_sendRawTransaction`
|
|
6537
7311
|
*/
|
|
6538
7312
|
async createRevokeDelegationTransaction(eoaPrivateKey, providerRpc, overrides = {}) {
|
|
6539
|
-
const delegatedTo = await getDelegatedAddress(this.accountAddress
|
|
7313
|
+
const delegatedTo = await JsonRpcNode.from(providerRpc).getDelegatedAddress(this.accountAddress);
|
|
6540
7314
|
if (delegatedTo === null) throw new AbstractionKitError("BAD_DATA", "Account is not delegated — nothing to revoke");
|
|
6541
7315
|
if (delegatedTo.toLowerCase() !== this.delegateeAddress.toLowerCase()) throw new AbstractionKitError("BAD_DATA", "Account is delegated to a different address (" + delegatedTo + "), not " + this.delegateeAddress + " — use the correct account class to revoke");
|
|
6542
7316
|
const results = {};
|
|
@@ -6639,7 +7413,7 @@ var BaseSimple7702Account = class BaseSimple7702Account extends SmartAccount {
|
|
|
6639
7413
|
eip7702AuthNonce = overrides.eip7702Auth.nonce ?? null;
|
|
6640
7414
|
}
|
|
6641
7415
|
let delegationCheckOp = null;
|
|
6642
|
-
if (overrides.eip7702Auth != null && providerRpc != null) delegationCheckOp = getDelegatedAddress(this.accountAddress
|
|
7416
|
+
if (overrides.eip7702Auth != null && providerRpc != null) delegationCheckOp = JsonRpcNode.from(providerRpc).getDelegatedAddress(this.accountAddress).catch(() => null);
|
|
6643
7417
|
if (overrides.eip7702Auth != null && eip7702AuthNonce == null) {
|
|
6644
7418
|
let eip7702AuthNonceOp;
|
|
6645
7419
|
if (providerRpc != null) eip7702AuthNonceOp = sendJsonRpcRequest(providerRpc, "eth_getTransactionCount", [this.accountAddress, "latest"]);
|
|
@@ -6774,7 +7548,7 @@ var BaseSimple7702Account = class BaseSimple7702Account extends SmartAccount {
|
|
|
6774
7548
|
*/
|
|
6775
7549
|
async baseEstimateUserOperationGas(userOperation, bundlerRpc, overrides = {}) {
|
|
6776
7550
|
userOperation.signature = overrides.dummySignature ?? BaseSimple7702Account.dummySignature;
|
|
6777
|
-
const bundler =
|
|
7551
|
+
const bundler = Bundler.from(bundlerRpc);
|
|
6778
7552
|
const inputMaxFeePerGas = userOperation.maxFeePerGas;
|
|
6779
7553
|
const inputMaxPriorityFeePerGas = userOperation.maxPriorityFeePerGas;
|
|
6780
7554
|
userOperation.maxFeePerGas = 0n;
|
|
@@ -6830,83 +7604,35 @@ var BaseSimple7702Account = class BaseSimple7702Account extends SmartAccount {
|
|
|
6830
7604
|
* Deterministic-ECDSA signers yield byte-identical signatures; signers
|
|
6831
7605
|
* that differ in `s` / `v` normalization still validate the same on-chain.
|
|
6832
7606
|
*
|
|
6833
|
-
*
|
|
6834
|
-
*
|
|
6835
|
-
* - Render a custom confirmation UI before delegating to a wallet's
|
|
6836
|
-
* `signTypedData`.
|
|
6837
|
-
* - Drive non-`ExternalSigner`-shaped signers (HSM, MPC service,
|
|
6838
|
-
* backend signing pipelines).
|
|
7607
|
+
* The base class defaults to EntryPoint v0.8; subclasses
|
|
7608
|
+
* ({@link Simple7702AccountV09}) override with their own default.
|
|
6839
7609
|
*
|
|
6840
7610
|
* @param userOperation - Unsigned UserOperation to wrap
|
|
6841
7611
|
* @param chainId - Target chain ID (must match the chain that will validate
|
|
6842
7612
|
* the signature)
|
|
7613
|
+
* @param overrides - Override the entrypoint address
|
|
6843
7614
|
* @returns EIP-712 {@link TypedData} payload ready for `signTypedData`
|
|
6844
|
-
* @throws {AbstractionKitError} if
|
|
6845
|
-
|
|
6846
|
-
|
|
6847
|
-
|
|
6848
|
-
|
|
6849
|
-
|
|
6850
|
-
|
|
6851
|
-
|
|
6852
|
-
|
|
6853
|
-
|
|
6854
|
-
|
|
6855
|
-
|
|
6856
|
-
|
|
6857
|
-
|
|
6858
|
-
|
|
6859
|
-
|
|
6860
|
-
|
|
6861
|
-
|
|
6862
|
-
|
|
6863
|
-
|
|
6864
|
-
|
|
6865
|
-
{
|
|
6866
|
-
name: "sender",
|
|
6867
|
-
type: "address"
|
|
6868
|
-
},
|
|
6869
|
-
{
|
|
6870
|
-
name: "nonce",
|
|
6871
|
-
type: "uint256"
|
|
6872
|
-
},
|
|
6873
|
-
{
|
|
6874
|
-
name: "initCode",
|
|
6875
|
-
type: "bytes"
|
|
6876
|
-
},
|
|
6877
|
-
{
|
|
6878
|
-
name: "callData",
|
|
6879
|
-
type: "bytes"
|
|
6880
|
-
},
|
|
6881
|
-
{
|
|
6882
|
-
name: "accountGasLimits",
|
|
6883
|
-
type: "bytes32"
|
|
6884
|
-
},
|
|
6885
|
-
{
|
|
6886
|
-
name: "preVerificationGas",
|
|
6887
|
-
type: "uint256"
|
|
6888
|
-
},
|
|
6889
|
-
{
|
|
6890
|
-
name: "gasFees",
|
|
6891
|
-
type: "bytes32"
|
|
6892
|
-
},
|
|
6893
|
-
{
|
|
6894
|
-
name: "paymasterAndData",
|
|
6895
|
-
type: "bytes"
|
|
6896
|
-
}
|
|
6897
|
-
] },
|
|
6898
|
-
primaryType: "PackedUserOperation",
|
|
6899
|
-
message: {
|
|
6900
|
-
sender: userOperation.sender,
|
|
6901
|
-
nonce: userOperation.nonce,
|
|
6902
|
-
initCode,
|
|
6903
|
-
callData: userOperation.callData,
|
|
6904
|
-
accountGasLimits,
|
|
6905
|
-
preVerificationGas: userOperation.preVerificationGas,
|
|
6906
|
-
gasFees,
|
|
6907
|
-
paymasterAndData
|
|
6908
|
-
}
|
|
6909
|
-
};
|
|
7615
|
+
* @throws {AbstractionKitError} if the target EntryPoint is not v0.8 / v0.9.
|
|
7616
|
+
*/
|
|
7617
|
+
static getUserOperationEip712Data(userOperation, chainId, overrides = {}) {
|
|
7618
|
+
return getUserOperationEip712DataV8V9(userOperation, overrides.entrypointAddress ?? "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108", chainId);
|
|
7619
|
+
}
|
|
7620
|
+
/**
|
|
7621
|
+
* Compute the EIP-712 digest of a UserOperation under the EntryPoint
|
|
7622
|
+
* v0.8 / v0.9 domain. For these EntryPoints this digest IS the
|
|
7623
|
+
* `userOpHash` ({@link createUserOperationHash}); signing it with raw
|
|
7624
|
+
* ECDSA or via `signTypedData` over the data from
|
|
7625
|
+
* {@link getUserOperationEip712Data} produces a signature that validates
|
|
7626
|
+
* against the same hash on-chain.
|
|
7627
|
+
*
|
|
7628
|
+
* @param userOperation - Unsigned UserOperation to hash
|
|
7629
|
+
* @param chainId - Target chain ID
|
|
7630
|
+
* @param overrides - Override the entrypoint address (defaults to EntryPoint v0.8)
|
|
7631
|
+
* @returns The EIP-712 digest as a hex string
|
|
7632
|
+
* @throws {AbstractionKitError} if the target EntryPoint is not v0.8 / v0.9.
|
|
7633
|
+
*/
|
|
7634
|
+
static getUserOperationEip712Hash(userOperation, chainId, overrides = {}) {
|
|
7635
|
+
return getUserOperationEip712HashV8V9(userOperation, overrides.entrypointAddress ?? "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108", chainId);
|
|
6910
7636
|
}
|
|
6911
7637
|
/**
|
|
6912
7638
|
* Sign a UserOperation with an {@link AkSigner}. The signer can implement
|
|
@@ -6932,7 +7658,7 @@ var BaseSimple7702Account = class BaseSimple7702Account extends SmartAccount {
|
|
|
6932
7658
|
};
|
|
6933
7659
|
return invokeSigner(signer, scheme, {
|
|
6934
7660
|
hash,
|
|
6935
|
-
typedData: scheme === "typedData" ?
|
|
7661
|
+
typedData: scheme === "typedData" ? BaseSimple7702Account.getUserOperationEip712Data(useroperation, chainId, { entrypointAddress: this.entrypointAddress }) : void 0,
|
|
6936
7662
|
context
|
|
6937
7663
|
});
|
|
6938
7664
|
}
|
|
@@ -6943,7 +7669,7 @@ var BaseSimple7702Account = class BaseSimple7702Account extends SmartAccount {
|
|
|
6943
7669
|
* @returns A {@link SendUseroperationResponse} that can be used to wait for inclusion
|
|
6944
7670
|
*/
|
|
6945
7671
|
async baseSendUserOperation(userOperation, bundlerRpc) {
|
|
6946
|
-
const bundler =
|
|
7672
|
+
const bundler = Bundler.from(bundlerRpc);
|
|
6947
7673
|
return new SendUseroperationResponse(await bundler.sendUserOperation(userOperation, this.entrypointAddress), bundler, this.entrypointAddress);
|
|
6948
7674
|
}
|
|
6949
7675
|
/**
|
|
@@ -7071,8 +7797,8 @@ var Simple7702Account = class Simple7702Account extends BaseSimple7702Account {
|
|
|
7071
7797
|
* {@link signUserOperation} method, or wrap explicitly with
|
|
7072
7798
|
* `fromPrivateKey(pk)`.
|
|
7073
7799
|
*
|
|
7074
|
-
* @see {@link BaseSimple7702Account.
|
|
7075
|
-
*
|
|
7800
|
+
* @see {@link BaseSimple7702Account.getUserOperationEip712Data} for the
|
|
7801
|
+
* lower-level escape hatch when you need the typed data outside the
|
|
7076
7802
|
* dispatcher (e.g., to render a custom confirmation UI or feed an HSM
|
|
7077
7803
|
* that doesn't fit the {@link ExternalSigner} shape).
|
|
7078
7804
|
*/
|
|
@@ -7100,6 +7826,22 @@ var Simple7702Account = class Simple7702Account extends BaseSimple7702Account {
|
|
|
7100
7826
|
var Simple7702AccountV09 = class Simple7702AccountV09 extends BaseSimple7702Account {
|
|
7101
7827
|
static DEFAULT_DELEGATEE_ADDRESS = "0xa46cc63eBF4Bd77888AA327837d20b23A63a56B5";
|
|
7102
7828
|
/**
|
|
7829
|
+
* Build the EIP-712 typed data payload for a {@link UserOperationV9}
|
|
7830
|
+
* under the EntryPoint v0.9 domain. See
|
|
7831
|
+
* {@link BaseSimple7702Account.getUserOperationEip712Data} for the
|
|
7832
|
+
* full semantics; this override just defaults `entrypointAddress` to v0.9.
|
|
7833
|
+
*/
|
|
7834
|
+
static getUserOperationEip712Data(userOperation, chainId, overrides = {}) {
|
|
7835
|
+
return BaseSimple7702Account.getUserOperationEip712Data(userOperation, chainId, { entrypointAddress: overrides.entrypointAddress ?? "0x433709009B8330FDa32311DF1C2AFA402eD8D009" });
|
|
7836
|
+
}
|
|
7837
|
+
/**
|
|
7838
|
+
* Compute the EIP-712 digest of a {@link UserOperationV9} under the
|
|
7839
|
+
* EntryPoint v0.9 domain. Defaults `entrypointAddress` to v0.9.
|
|
7840
|
+
*/
|
|
7841
|
+
static getUserOperationEip712Hash(userOperation, chainId, overrides = {}) {
|
|
7842
|
+
return BaseSimple7702Account.getUserOperationEip712Hash(userOperation, chainId, { entrypointAddress: overrides.entrypointAddress ?? "0x433709009B8330FDa32311DF1C2AFA402eD8D009" });
|
|
7843
|
+
}
|
|
7844
|
+
/**
|
|
7103
7845
|
* @param accountAddress - The EOA address that will be delegated via EIP-7702
|
|
7104
7846
|
* @param overrides - Optional overrides for entrypoint and delegatee addresses
|
|
7105
7847
|
* @param overrides.entrypointAddress - Custom EntryPoint address (defaults to EntryPoint v0.9)
|
|
@@ -7169,8 +7911,8 @@ var Simple7702AccountV09 = class Simple7702AccountV09 extends BaseSimple7702Acco
|
|
|
7169
7911
|
* {@link signUserOperation} method, or wrap explicitly with
|
|
7170
7912
|
* `fromPrivateKey(pk)`.
|
|
7171
7913
|
*
|
|
7172
|
-
* @see {@link BaseSimple7702Account.
|
|
7173
|
-
*
|
|
7914
|
+
* @see {@link BaseSimple7702Account.getUserOperationEip712Data} for the
|
|
7915
|
+
* lower-level escape hatch when you need the typed data outside the
|
|
7174
7916
|
* dispatcher.
|
|
7175
7917
|
*/
|
|
7176
7918
|
async signUserOperationWithSigner(useroperation, signer, chainId) {
|
|
@@ -7272,8 +8014,19 @@ const TOKENS_REQUIRING_ALLOWANCE_RESET$1 = ["0xdac17f958d2ee523a2206206994597c13
|
|
|
7272
8014
|
* const { userOperation: sponsoredOp } = await paymaster.createSponsorPaymasterUserOperation(userOp, bundlerRpcUrl);
|
|
7273
8015
|
*/
|
|
7274
8016
|
var CandidePaymaster = class CandidePaymaster extends Paymaster {
|
|
7275
|
-
/**
|
|
7276
|
-
|
|
8017
|
+
/**
|
|
8018
|
+
* The raw transport the user passed in (or {@link HttpTransport} when a URL
|
|
8019
|
+
* string was passed). Exposed for introspection — reading `.url`,
|
|
8020
|
+
* `isHttpTransport(...)` checks, passing it back into another service.
|
|
8021
|
+
*
|
|
8022
|
+
* Calls made directly on this field (`paymaster.transport.request(...)`)
|
|
8023
|
+
* go to the raw transport and skip SDK-level behavior like bigint param
|
|
8024
|
+
* normalization. For SDK-pipeline behavior, use
|
|
8025
|
+
* {@link CandidePaymaster.request} or the typed methods.
|
|
8026
|
+
*/
|
|
8027
|
+
transport;
|
|
8028
|
+
/** Normalizing wrapper around {@link transport}, used for every SDK-outbound call. */
|
|
8029
|
+
outbound;
|
|
7277
8030
|
/** Cached token/metadata per EntryPoint address (lowercase keys) */
|
|
7278
8031
|
entrypointData = /* @__PURE__ */ new Map();
|
|
7279
8032
|
/** Per-entrypoint initialization promises (lowercase keys) */
|
|
@@ -7281,11 +8034,30 @@ var CandidePaymaster = class CandidePaymaster extends Paymaster {
|
|
|
7281
8034
|
/** Cached chain ID (hex string), resolved from URL or pm_chainId RPC */
|
|
7282
8035
|
chainId = null;
|
|
7283
8036
|
chainIdPromise = null;
|
|
7284
|
-
/**
|
|
7285
|
-
|
|
8037
|
+
/**
|
|
8038
|
+
* @param rpc - The Candide paymaster JSON-RPC endpoint URL, or any {@link Transport}.
|
|
8039
|
+
* When a URL string is passed, the chain id is inferred from the URL path
|
|
8040
|
+
* when possible; otherwise it's resolved lazily via `pm_chainId`.
|
|
8041
|
+
*/
|
|
8042
|
+
constructor(rpc) {
|
|
7286
8043
|
super();
|
|
7287
|
-
this.
|
|
7288
|
-
this.
|
|
8044
|
+
this.transport = typeof rpc === "string" ? new HttpTransport(rpc) : rpc;
|
|
8045
|
+
this.outbound = normalizingTransport(this.transport);
|
|
8046
|
+
this.chainId = typeof rpc === "string" ? CandidePaymaster.extractChainIdFromUrl(rpc) : null;
|
|
8047
|
+
}
|
|
8048
|
+
/**
|
|
8049
|
+
* Normalize any acceptable input into a `CandidePaymaster`. Returns the
|
|
8050
|
+
* input by reference when it's already a `CandidePaymaster`.
|
|
8051
|
+
*/
|
|
8052
|
+
static from(input) {
|
|
8053
|
+
return input instanceof CandidePaymaster ? input : new CandidePaymaster(input);
|
|
8054
|
+
}
|
|
8055
|
+
/**
|
|
8056
|
+
* Transport delegate. Forwards directly to {@link Transport.request}, so a
|
|
8057
|
+
* `CandidePaymaster` can be slotted into any other transport position.
|
|
8058
|
+
*/
|
|
8059
|
+
request(args, options) {
|
|
8060
|
+
return this.outbound.request(args, options);
|
|
7289
8061
|
}
|
|
7290
8062
|
/**
|
|
7291
8063
|
* Extract chain ID from a Candide paymaster URL.
|
|
@@ -7313,7 +8085,7 @@ var CandidePaymaster = class CandidePaymaster extends Paymaster {
|
|
|
7313
8085
|
}
|
|
7314
8086
|
async fetchChainId() {
|
|
7315
8087
|
try {
|
|
7316
|
-
return await
|
|
8088
|
+
return await this.outbound.request({ method: "pm_chainId" });
|
|
7317
8089
|
} catch (err) {
|
|
7318
8090
|
throw new AbstractionKitError("PAYMASTER_ERROR", "pm_chainId failed", { cause: ensureError(err) });
|
|
7319
8091
|
}
|
|
@@ -7413,7 +8185,10 @@ var CandidePaymaster = class CandidePaymaster extends Paymaster {
|
|
|
7413
8185
|
}
|
|
7414
8186
|
}
|
|
7415
8187
|
async fetchSupportedTokensRpc(entrypoint) {
|
|
7416
|
-
return await
|
|
8188
|
+
return await this.outbound.request({
|
|
8189
|
+
method: "pm_supportedERC20Tokens",
|
|
8190
|
+
params: [entrypoint]
|
|
8191
|
+
});
|
|
7417
8192
|
}
|
|
7418
8193
|
/**
|
|
7419
8194
|
* Get the EntryPoint addresses supported by this paymaster.
|
|
@@ -7422,7 +8197,7 @@ var CandidePaymaster = class CandidePaymaster extends Paymaster {
|
|
|
7422
8197
|
*/
|
|
7423
8198
|
async getSupportedEntrypoints() {
|
|
7424
8199
|
try {
|
|
7425
|
-
return await
|
|
8200
|
+
return await this.outbound.request({ method: "pm_supportedEntryPoints" });
|
|
7426
8201
|
} catch (err) {
|
|
7427
8202
|
throw new AbstractionKitError("PAYMASTER_ERROR", "pm_supportedEntryPoints failed", { cause: ensureError(err) });
|
|
7428
8203
|
}
|
|
@@ -7489,7 +8264,7 @@ var CandidePaymaster = class CandidePaymaster extends Paymaster {
|
|
|
7489
8264
|
let callGasLimit = userOp.callGasLimit;
|
|
7490
8265
|
if (overrides.preVerificationGas == null || overrides.verificationGasLimit == null || overrides.callGasLimit == null) {
|
|
7491
8266
|
if (bundlerRpc == null) throw new AbstractionKitError("BAD_DATA", "bundlerRpc can't be null if preVerificationGas,verificationGasLimit and callGasLimit are not overridden");
|
|
7492
|
-
const bundler =
|
|
8267
|
+
const bundler = Bundler.from(bundlerRpc);
|
|
7493
8268
|
userOp.callGasLimit = 0n;
|
|
7494
8269
|
userOp.verificationGasLimit = 0n;
|
|
7495
8270
|
userOp.preVerificationGas = 0n;
|
|
@@ -7537,12 +8312,15 @@ var CandidePaymaster = class CandidePaymaster extends Paymaster {
|
|
|
7537
8312
|
try {
|
|
7538
8313
|
const entrypoint = overrides.entrypoint ?? this.resolveEntrypoint(smartAccount, userOperation);
|
|
7539
8314
|
const chainId = await this.getChainId();
|
|
7540
|
-
const jsonRpcResult = await
|
|
7541
|
-
|
|
7542
|
-
|
|
7543
|
-
|
|
7544
|
-
|
|
7545
|
-
|
|
8315
|
+
const jsonRpcResult = await this.outbound.request({
|
|
8316
|
+
method: "pm_getPaymasterData",
|
|
8317
|
+
params: [
|
|
8318
|
+
userOperation,
|
|
8319
|
+
entrypoint,
|
|
8320
|
+
chainId,
|
|
8321
|
+
context
|
|
8322
|
+
]
|
|
8323
|
+
});
|
|
7546
8324
|
return {
|
|
7547
8325
|
userOperation,
|
|
7548
8326
|
sponsorMetadata: this.applyPaymasterResult(userOperation, jsonRpcResult)
|
|
@@ -7726,8 +8504,19 @@ const TOKENS_REQUIRING_ALLOWANCE_RESET = ["0xdac17f958d2ee523a2206206994597c13d8
|
|
|
7726
8504
|
*/
|
|
7727
8505
|
const CANDIDE_TOKEN_QUOTE_TTL_MS = 45e3;
|
|
7728
8506
|
var Erc7677Paymaster = class Erc7677Paymaster extends Paymaster {
|
|
7729
|
-
/**
|
|
7730
|
-
|
|
8507
|
+
/**
|
|
8508
|
+
* The raw transport the user passed in (or {@link HttpTransport} when a URL
|
|
8509
|
+
* string was passed). Exposed for introspection — reading `.url`,
|
|
8510
|
+
* `isHttpTransport(...)` checks, passing it back into another service.
|
|
8511
|
+
*
|
|
8512
|
+
* Calls made directly on this field (`paymaster.transport.request(...)`)
|
|
8513
|
+
* go to the raw transport and skip SDK-level behavior like bigint param
|
|
8514
|
+
* normalization. For SDK-pipeline behavior, use
|
|
8515
|
+
* {@link Erc7677Paymaster.request} or the typed methods.
|
|
8516
|
+
*/
|
|
8517
|
+
transport;
|
|
8518
|
+
/** Normalizing wrapper around {@link transport}, used for every SDK-outbound call. */
|
|
8519
|
+
outbound;
|
|
7731
8520
|
/** Cached chain ID (hex string). Passed via constructor or resolved from the bundler at first use. */
|
|
7732
8521
|
chainId;
|
|
7733
8522
|
/** Detected or explicitly set paymaster provider. `null` means no provider-specific features. */
|
|
@@ -7761,29 +8550,49 @@ var Erc7677Paymaster = class Erc7677Paymaster extends Paymaster {
|
|
|
7761
8550
|
return null;
|
|
7762
8551
|
}
|
|
7763
8552
|
/**
|
|
7764
|
-
* @param
|
|
7765
|
-
* bundler when the provider bundles both
|
|
7766
|
-
* can also be a separate paymaster-only
|
|
8553
|
+
* @param rpc - Paymaster JSON-RPC endpoint URL, or any {@link Transport}.
|
|
8554
|
+
* Can be the same URL as the bundler when the provider bundles both
|
|
8555
|
+
* (Candide, Pimlico, Alchemy); can also be a separate paymaster-only
|
|
8556
|
+
* endpoint, or a fully custom transport.
|
|
7767
8557
|
* @param options
|
|
7768
8558
|
* @param options.chainId - Optional chain id as a bigint (e.g. `1n` for
|
|
7769
8559
|
* mainnet). When provided, avoids a lookup at first use. Otherwise,
|
|
7770
8560
|
* resolved from the bundler via `eth_chainId` on the first call.
|
|
7771
8561
|
* @param options.provider - Paymaster provider. `"auto"` (default) detects
|
|
7772
|
-
* from the RPC URL
|
|
8562
|
+
* from the RPC URL (only when a URL string is passed; non-string inputs
|
|
8563
|
+
* default to `null` unless overridden here). Set explicitly to override,
|
|
8564
|
+
* or `null` to disable provider-specific features.
|
|
7773
8565
|
*/
|
|
7774
|
-
constructor(
|
|
8566
|
+
constructor(rpc, options = {}) {
|
|
7775
8567
|
super();
|
|
7776
|
-
this.
|
|
8568
|
+
this.transport = typeof rpc === "string" ? new HttpTransport(rpc) : rpc;
|
|
8569
|
+
this.outbound = normalizingTransport(this.transport);
|
|
7777
8570
|
this.chainId = options.chainId != null ? `0x${options.chainId.toString(16)}` : null;
|
|
7778
|
-
if (options.provider === void 0 || options.provider === "auto") this.provider = Erc7677Paymaster.detectProvider(
|
|
8571
|
+
if (options.provider === void 0 || options.provider === "auto") this.provider = typeof rpc === "string" ? Erc7677Paymaster.detectProvider(rpc) : null;
|
|
7779
8572
|
else this.provider = options.provider;
|
|
7780
8573
|
}
|
|
7781
8574
|
/**
|
|
8575
|
+
* Normalize any acceptable input into an `Erc7677Paymaster`. Returns the
|
|
8576
|
+
* input by reference when it's already an `Erc7677Paymaster` (in which
|
|
8577
|
+
* case the second `options` argument is ignored — the existing instance's
|
|
8578
|
+
* configuration is preserved).
|
|
8579
|
+
*/
|
|
8580
|
+
static from(input, options) {
|
|
8581
|
+
return input instanceof Erc7677Paymaster ? input : new Erc7677Paymaster(input, options);
|
|
8582
|
+
}
|
|
8583
|
+
/**
|
|
8584
|
+
* Transport delegate. Forwards directly to {@link Transport.request}, so
|
|
8585
|
+
* an `Erc7677Paymaster` can be slotted into any other transport position.
|
|
8586
|
+
*/
|
|
8587
|
+
request(args, options) {
|
|
8588
|
+
return this.outbound.request(args, options);
|
|
8589
|
+
}
|
|
8590
|
+
/**
|
|
7782
8591
|
* Resolve the chain id, querying the bundler if not provided at construction.
|
|
7783
8592
|
*/
|
|
7784
8593
|
async getChainId(bundlerRpc) {
|
|
7785
8594
|
if (this.chainId != null) return this.chainId;
|
|
7786
|
-
const id = await
|
|
8595
|
+
const id = await Bundler.from(bundlerRpc).chainId();
|
|
7787
8596
|
this.chainId = id;
|
|
7788
8597
|
return id;
|
|
7789
8598
|
}
|
|
@@ -7807,12 +8616,15 @@ var Erc7677Paymaster = class Erc7677Paymaster extends Paymaster {
|
|
|
7807
8616
|
*/
|
|
7808
8617
|
async getPaymasterStubData(userOperation, entrypoint, chainIdHex, context = {}) {
|
|
7809
8618
|
try {
|
|
7810
|
-
return await
|
|
7811
|
-
|
|
7812
|
-
|
|
7813
|
-
|
|
7814
|
-
|
|
7815
|
-
|
|
8619
|
+
return await this.outbound.request({
|
|
8620
|
+
method: "pm_getPaymasterStubData",
|
|
8621
|
+
params: [
|
|
8622
|
+
userOperation,
|
|
8623
|
+
entrypoint,
|
|
8624
|
+
chainIdHex,
|
|
8625
|
+
context
|
|
8626
|
+
]
|
|
8627
|
+
});
|
|
7816
8628
|
} catch (err) {
|
|
7817
8629
|
throw new AbstractionKitError("PAYMASTER_ERROR", "pm_getPaymasterStubData failed", { cause: ensureError(err) });
|
|
7818
8630
|
}
|
|
@@ -7823,12 +8635,15 @@ var Erc7677Paymaster = class Erc7677Paymaster extends Paymaster {
|
|
|
7823
8635
|
*/
|
|
7824
8636
|
async getPaymasterData(userOperation, entrypoint, chainIdHex, context = {}) {
|
|
7825
8637
|
try {
|
|
7826
|
-
return await
|
|
7827
|
-
|
|
7828
|
-
|
|
7829
|
-
|
|
7830
|
-
|
|
7831
|
-
|
|
8638
|
+
return await this.outbound.request({
|
|
8639
|
+
method: "pm_getPaymasterData",
|
|
8640
|
+
params: [
|
|
8641
|
+
userOperation,
|
|
8642
|
+
entrypoint,
|
|
8643
|
+
chainIdHex,
|
|
8644
|
+
context
|
|
8645
|
+
]
|
|
8646
|
+
});
|
|
7832
8647
|
} catch (err) {
|
|
7833
8648
|
throw new AbstractionKitError("PAYMASTER_ERROR", "pm_getPaymasterData failed", { cause: ensureError(err) });
|
|
7834
8649
|
}
|
|
@@ -7843,7 +8658,10 @@ var Erc7677Paymaster = class Erc7677Paymaster extends Paymaster {
|
|
|
7843
8658
|
*/
|
|
7844
8659
|
async sendRPCRequest(method, params = []) {
|
|
7845
8660
|
try {
|
|
7846
|
-
return await
|
|
8661
|
+
return await this.outbound.request({
|
|
8662
|
+
method,
|
|
8663
|
+
params
|
|
8664
|
+
});
|
|
7847
8665
|
} catch (err) {
|
|
7848
8666
|
throw new AbstractionKitError("PAYMASTER_ERROR", `sendRPCRequest(${method}) failed`, { cause: ensureError(err) });
|
|
7849
8667
|
}
|
|
@@ -7905,7 +8723,7 @@ var Erc7677Paymaster = class Erc7677Paymaster extends Paymaster {
|
|
|
7905
8723
|
let callGasLimit = userOp.callGasLimit;
|
|
7906
8724
|
if (overrides.preVerificationGas == null || overrides.verificationGasLimit == null || overrides.callGasLimit == null) {
|
|
7907
8725
|
if (bundlerRpc == null) throw new AbstractionKitError("BAD_DATA", "bundlerRpc can't be null if preVerificationGas, verificationGasLimit and callGasLimit are not overridden");
|
|
7908
|
-
const bundler =
|
|
8726
|
+
const bundler = Bundler.from(bundlerRpc);
|
|
7909
8727
|
userOp.callGasLimit = 0n;
|
|
7910
8728
|
userOp.verificationGasLimit = 0n;
|
|
7911
8729
|
userOp.preVerificationGas = 0n;
|
|
@@ -7945,11 +8763,14 @@ var Erc7677Paymaster = class Erc7677Paymaster extends Paymaster {
|
|
|
7945
8763
|
* approval amount via `(exchangeRate * gasCostWei) / 10^18`.
|
|
7946
8764
|
*/
|
|
7947
8765
|
async fetchPimlicoTokenQuote(tokenAddress, entrypoint, chainIdHex) {
|
|
7948
|
-
const quotes = (await
|
|
7949
|
-
|
|
7950
|
-
|
|
7951
|
-
|
|
7952
|
-
|
|
8766
|
+
const quotes = (await this.outbound.request({
|
|
8767
|
+
method: "pimlico_getTokenQuotes",
|
|
8768
|
+
params: [
|
|
8769
|
+
{ tokens: [tokenAddress] },
|
|
8770
|
+
entrypoint,
|
|
8771
|
+
chainIdHex
|
|
8772
|
+
]
|
|
8773
|
+
}))?.quotes;
|
|
7953
8774
|
if (!Array.isArray(quotes) || quotes.length === 0) throw new AbstractionKitError("PAYMASTER_ERROR", `pimlico_getTokenQuotes returned no quotes for token ${tokenAddress}`);
|
|
7954
8775
|
const quote = quotes.find((q) => q.token.toLowerCase() === tokenAddress.toLowerCase());
|
|
7955
8776
|
if (quote == null) throw new AbstractionKitError("PAYMASTER_ERROR", `pimlico_getTokenQuotes did not include token ${tokenAddress}`);
|
|
@@ -7975,7 +8796,10 @@ var Erc7677Paymaster = class Erc7677Paymaster extends Paymaster {
|
|
|
7975
8796
|
const cached = this.candideCache.get(key);
|
|
7976
8797
|
const isStale = cached != null && options.enforceTTL === true && Date.now() - cached.fetchedAt > CANDIDE_TOKEN_QUOTE_TTL_MS;
|
|
7977
8798
|
if (cached != null && !isStale) return cached.data;
|
|
7978
|
-
const result = await
|
|
8799
|
+
const result = await this.outbound.request({
|
|
8800
|
+
method: "pm_supportedERC20Tokens",
|
|
8801
|
+
params: [entrypoint]
|
|
8802
|
+
});
|
|
7979
8803
|
this.candideCache.set(key, {
|
|
7980
8804
|
data: result,
|
|
7981
8805
|
fetchedAt: Date.now()
|
|
@@ -8152,7 +8976,7 @@ var WorldIdPermissionlessPaymaster = class extends Paymaster {
|
|
|
8152
8976
|
let verificationGasLimit = userOperation.verificationGasLimit;
|
|
8153
8977
|
let callGasLimit = userOperation.callGasLimit;
|
|
8154
8978
|
userOperation.preVerificationGas = 0n;
|
|
8155
|
-
const estimation = await
|
|
8979
|
+
const estimation = await Bundler.from(bundlerRpc).estimateUserOperationGas(userOperation, entrypointAddress, overrides.state_override_set);
|
|
8156
8980
|
if (preVerificationGas < estimation.preVerificationGas) preVerificationGas = estimation.preVerificationGas;
|
|
8157
8981
|
if (verificationGasLimit < estimation.verificationGasLimit) verificationGasLimit = estimation.verificationGasLimit;
|
|
8158
8982
|
if (callGasLimit < estimation.callGasLimit) callGasLimit = estimation.callGasLimit;
|
|
@@ -8315,8 +9139,8 @@ function coerceBigint(v, field) {
|
|
|
8315
9139
|
*/
|
|
8316
9140
|
function pubkeyCoordinatesToJson(pubkey) {
|
|
8317
9141
|
return JSON.stringify({
|
|
8318
|
-
x:
|
|
8319
|
-
y:
|
|
9142
|
+
x: `0x${pubkey.x.toString(16)}`,
|
|
9143
|
+
y: `0x${pubkey.y.toString(16)}`
|
|
8320
9144
|
});
|
|
8321
9145
|
}
|
|
8322
9146
|
/**
|
|
@@ -8537,6 +9361,7 @@ var abstractionkit_exports = /* @__PURE__ */ __exportAll({
|
|
|
8537
9361
|
ALLOWANCE_MODULE_V0_1_0_ADDRESS: () => ALLOWANCE_MODULE_V0_1_0_ADDRESS,
|
|
8538
9362
|
AbstractionKitError: () => AbstractionKitError,
|
|
8539
9363
|
AllowanceModule: () => AllowanceModule,
|
|
9364
|
+
BaseRpcTransport: () => BaseRpcTransport,
|
|
8540
9365
|
BaseUserOperationDummyValues: () => BaseUserOperationDummyValues,
|
|
8541
9366
|
Bundler: () => Bundler,
|
|
8542
9367
|
CALIBUR_CANDIDE_V0_1_0_SINGLETON_ADDRESS: () => CALIBUR_CANDIDE_V0_1_0_SINGLETON_ADDRESS,
|
|
@@ -8560,6 +9385,8 @@ var abstractionkit_exports = /* @__PURE__ */ __exportAll({
|
|
|
8560
9385
|
Erc7677Paymaster: () => Erc7677Paymaster,
|
|
8561
9386
|
ExperimentalAllowAllParallelPaymaster: () => ExperimentalAllowAllParallelPaymaster,
|
|
8562
9387
|
GasOption: () => GasOption,
|
|
9388
|
+
HttpTransport: () => HttpTransport,
|
|
9389
|
+
JsonRpcNode: () => JsonRpcNode,
|
|
8563
9390
|
Operation: () => Operation,
|
|
8564
9391
|
PolygonChain: () => PolygonChain,
|
|
8565
9392
|
SAFE_MESSAGE_MODULE_TYPE: () => SAFE_MESSAGE_MODULE_TYPE,
|
|
@@ -8577,6 +9404,7 @@ var abstractionkit_exports = /* @__PURE__ */ __exportAll({
|
|
|
8577
9404
|
SmartAccountFactory: () => SmartAccountFactory,
|
|
8578
9405
|
SocialRecoveryModule: () => SocialRecoveryModule,
|
|
8579
9406
|
SocialRecoveryModuleGracePeriodSelector: () => SocialRecoveryModuleGracePeriodSelector,
|
|
9407
|
+
TransportRpcError: () => TransportRpcError,
|
|
8580
9408
|
WebauthnDummySignerSignaturePair: () => WebauthnDummySignerSignaturePair,
|
|
8581
9409
|
WorldIdPermissionlessPaymaster: () => WorldIdPermissionlessPaymaster,
|
|
8582
9410
|
ZeroAddress: () => ZeroAddress,
|
|
@@ -8591,17 +9419,15 @@ var abstractionkit_exports = /* @__PURE__ */ __exportAll({
|
|
|
8591
9419
|
createUserOperationHash: () => createUserOperationHash,
|
|
8592
9420
|
createWorldIdSignal: () => createWorldIdSignal,
|
|
8593
9421
|
fetchAccountNonce: () => fetchAccountNonce,
|
|
8594
|
-
fetchGasPrice: () => fetchGasPrice,
|
|
8595
9422
|
fromEthersWallet: () => fromEthersWallet,
|
|
8596
9423
|
fromPrivateKey: () => fromPrivateKey,
|
|
8597
9424
|
fromSafeWebauthn: () => fromSafeWebauthn,
|
|
8598
9425
|
fromViem: () => fromViem,
|
|
8599
9426
|
fromViemWalletClient: () => fromViemWalletClient,
|
|
8600
|
-
getBalanceOf: () => getBalanceOf,
|
|
8601
|
-
getDelegatedAddress: () => getDelegatedAddress,
|
|
8602
|
-
getDepositInfo: () => getDepositInfo,
|
|
8603
9427
|
getFunctionSelector: () => getFunctionSelector,
|
|
8604
9428
|
getSafeMessageEip712Data: () => getSafeMessageEip712Data,
|
|
9429
|
+
isEventfulTransport: () => isEventfulTransport,
|
|
9430
|
+
isHttpTransport: () => isHttpTransport,
|
|
8605
9431
|
pubkeyCoordinatesFromJson: () => pubkeyCoordinatesFromJson,
|
|
8606
9432
|
pubkeyCoordinatesToJson: () => pubkeyCoordinatesToJson,
|
|
8607
9433
|
sendJsonRpcRequest: () => sendJsonRpcRequest,
|
|
@@ -8616,6 +9442,6 @@ var abstractionkit_exports = /* @__PURE__ */ __exportAll({
|
|
|
8616
9442
|
webauthnSignatureFromAssertion: () => webauthnSignatureFromAssertion
|
|
8617
9443
|
});
|
|
8618
9444
|
//#endregion
|
|
8619
|
-
export { ALLOWANCE_MODULE_V0_1_0_ADDRESS, AbstractionKitError, AllowanceModule, BaseUserOperationDummyValues, Bundler, CALIBUR_CANDIDE_V0_1_0_SINGLETON_ADDRESS, CALIBUR_UNISWAP_V1_0_0_SINGLETON_ADDRESS, Calibur7702Account, CaliburKeyType, CandidePaymaster, DEFAULT_SECP256R1_PRECOMPILE_ADDRESS, EIP712_MULTI_CHAIN_OPERATIONS_PRIMARY_TYPE, EIP712_MULTI_CHAIN_OPERATIONS_TYPE, EIP712_RECOVERY_MODULE_TYPE, EIP712_SAFE_OPERATION_PRIMARY_TYPE, EIP712_SAFE_OPERATION_V6_TYPE, EIP712_SAFE_OPERATION_V7_TYPE, ENTRYPOINT_V6, ENTRYPOINT_V7, ENTRYPOINT_V8, ENTRYPOINT_V9, EOADummySignerSignaturePair, EXECUTE_RECOVERY_PRIMARY_TYPE, Erc7677Paymaster, ExperimentalAllowAllParallelPaymaster, GasOption, Operation, PolygonChain, SAFE_MESSAGE_MODULE_TYPE, SAFE_MESSAGE_PRIMARY_TYPE, SafeAccountFactory, SafeAccountV0_2_0, SafeAccountV0_3_0, SafeAccountV1_5_0_M_0_3_0, SafeModuleExecutorFunctionSelector, SafeMultiChainSigAccountV1, SendUseroperationResponse, Simple7702Account, Simple7702AccountV09, SmartAccount, SmartAccountFactory, SocialRecoveryModule, SocialRecoveryModuleGracePeriodSelector, WebauthnDummySignerSignaturePair, WorldIdPermissionlessPaymaster, ZeroAddress, abstractionkit_exports as abstractionkit, calculateUserOperationMaxGasCost, callTenderlySimulateBundle, createAndSignEip7702DelegationAuthorization, createAndSignEip7702RawTransaction, createAndSignLegacyRawTransaction, createCallData, createEip7702DelegationAuthorizationHash, createEip7702TransactionHash, createUserOperationHash, createWorldIdSignal, fetchAccountNonce,
|
|
9445
|
+
export { ALLOWANCE_MODULE_V0_1_0_ADDRESS, AbstractionKitError, AllowanceModule, BaseRpcTransport, BaseUserOperationDummyValues, Bundler, CALIBUR_CANDIDE_V0_1_0_SINGLETON_ADDRESS, CALIBUR_UNISWAP_V1_0_0_SINGLETON_ADDRESS, Calibur7702Account, CaliburKeyType, CandidePaymaster, DEFAULT_SECP256R1_PRECOMPILE_ADDRESS, EIP712_MULTI_CHAIN_OPERATIONS_PRIMARY_TYPE, EIP712_MULTI_CHAIN_OPERATIONS_TYPE, EIP712_RECOVERY_MODULE_TYPE, EIP712_SAFE_OPERATION_PRIMARY_TYPE, EIP712_SAFE_OPERATION_V6_TYPE, EIP712_SAFE_OPERATION_V7_TYPE, ENTRYPOINT_V6, ENTRYPOINT_V7, ENTRYPOINT_V8, ENTRYPOINT_V9, EOADummySignerSignaturePair, EXECUTE_RECOVERY_PRIMARY_TYPE, Erc7677Paymaster, ExperimentalAllowAllParallelPaymaster, GasOption, HttpTransport, JsonRpcNode, Operation, PolygonChain, SAFE_MESSAGE_MODULE_TYPE, SAFE_MESSAGE_PRIMARY_TYPE, SafeAccountFactory, SafeAccountV0_2_0, SafeAccountV0_3_0, SafeAccountV1_5_0_M_0_3_0, SafeModuleExecutorFunctionSelector, SafeMultiChainSigAccountV1, SendUseroperationResponse, Simple7702Account, Simple7702AccountV09, SmartAccount, SmartAccountFactory, SocialRecoveryModule, SocialRecoveryModuleGracePeriodSelector, TransportRpcError, WebauthnDummySignerSignaturePair, WorldIdPermissionlessPaymaster, ZeroAddress, abstractionkit_exports as abstractionkit, calculateUserOperationMaxGasCost, callTenderlySimulateBundle, createAndSignEip7702DelegationAuthorization, createAndSignEip7702RawTransaction, createAndSignLegacyRawTransaction, createCallData, createEip7702DelegationAuthorizationHash, createEip7702TransactionHash, createUserOperationHash, createWorldIdSignal, fetchAccountNonce, fromEthersWallet, fromPrivateKey, fromSafeWebauthn, fromViem, fromViemWalletClient, getFunctionSelector, getSafeMessageEip712Data, isEventfulTransport, isHttpTransport, pubkeyCoordinatesFromJson, pubkeyCoordinatesToJson, sendJsonRpcRequest, shareTenderlySimulationAndCreateLink, signHash, simulateSenderCallDataWithTenderly, simulateSenderCallDataWithTenderlyAndCreateShareLink, simulateUserOperationCallDataWithTenderly, simulateUserOperationCallDataWithTenderlyAndCreateShareLink, simulateUserOperationWithTenderly, simulateUserOperationWithTenderlyAndCreateShareLink, webauthnSignatureFromAssertion };
|
|
8620
9446
|
|
|
8621
9447
|
//# sourceMappingURL=index.mjs.map
|