abstractionkit 0.3.7 → 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.
@@ -30,6 +30,16 @@ var abstractionkit = (function(exports, ethers) {
30
30
  "-32521": "EXECUTION_REVERTED"
31
31
  };
32
32
  /**
33
+ * Maps JSON-RPC numeric error codes to human-readable {@link JsonRpcErrorCode} values.
34
+ */
35
+ const JsonRpcErrorDict = {
36
+ "-32700": "PARSE_ERROR",
37
+ "-32600": "INVALID_REQUEST",
38
+ "-32601": "METHOD_NOT_FOUND",
39
+ "-32602": "INVALID_PARAMS",
40
+ "-32603": "INTERNAL_ERROR"
41
+ };
42
+ /**
33
43
  * Custom error class for the AbstractionKit SDK. Wraps bundler, JSON-RPC,
34
44
  * and general errors with a structured code, optional numeric errno, and
35
45
  * arbitrary JSON-serializable context.
@@ -84,164 +94,164 @@ var abstractionkit = (function(exports, ethers) {
84
94
  return /* @__PURE__ */ new Error(`This value was thrown as is, not through an Error: ${stringified}`);
85
95
  }
86
96
  //#endregion
87
- //#region src/constants.ts
88
- /** The Ethereum zero address (0x0000...0000), used as a placeholder for empty/null addresses */
89
- const ZeroAddress = "0x0000000000000000000000000000000000000000";
90
- /** EntryPoint v0.9 contract address */
91
- const ENTRYPOINT_V9 = "0x433709009B8330FDa32311DF1C2AFA402eD8D009";
92
- /** EntryPoint v0.8 contract address */
93
- const ENTRYPOINT_V8 = "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108";
94
- /** EntryPoint v0.7 contract address */
95
- const ENTRYPOINT_V7 = "0x0000000071727De22E5E9d8BAf0edAc6f37da032";
96
- /** EntryPoint v0.6 contract address */
97
- const ENTRYPOINT_V6 = "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789";
98
- /** Safe L2 singleton v1.5.0 address and init hash */
99
- const Safe_L2_V1_5_0 = {
100
- singletonAddress: "0xEdd160fEBBD92E350D4D398fb636302fccd67C7e",
101
- singletonInitHash: "0x1b94aebb5a7df6dff11d93589204a6bbc99b4b8c9014bf1d386d006c2c17a881"
102
- };
103
- /** Safe L2 singleton v1.4.1 address and init hash */
104
- const Safe_L2_V1_4_1 = {
105
- singletonAddress: "0x29fcB43b46531BcA003ddC8FCB67FFE91900C762",
106
- singletonInitHash: "0xe298282cefe913ab5d282047161268a8222e4bd4ed106300c547894bbefd31ee"
97
+ //#region src/transport/Transport.ts
98
+ /**
99
+ * Default {@link ProviderRpcError} implementation. {@link BaseRpcTransport}
100
+ * throws this automatically when a JSON-RPC envelope returns
101
+ * `{ error: { code, message, data } }`.
102
+ */
103
+ var TransportRpcError = class extends Error {
104
+ code;
105
+ data;
106
+ /**
107
+ * @param code - Numeric JSON-RPC error code (e.g. -32601 for METHOD_NOT_FOUND)
108
+ * @param message - Human-readable error description
109
+ * @param data - Optional additional data returned by the RPC server
110
+ */
111
+ constructor(code, message, data) {
112
+ super(message);
113
+ this.name = "TransportRpcError";
114
+ this.code = code;
115
+ this.data = data;
116
+ }
107
117
  };
108
- /** Default placeholder values for gas estimation before actual values are known */
109
- const BaseUserOperationDummyValues = {
110
- sender: ZeroAddress,
111
- nonce: 0n,
112
- callData: "0x",
113
- callGasLimit: 0n,
114
- verificationGasLimit: 0n,
115
- preVerificationGas: 0n,
116
- maxFeePerGas: 0n,
117
- maxPriorityFeePerGas: 0n,
118
- signature: "0x"
118
+ /**
119
+ * Narrowing helper for {@link EventfulTransport}.
120
+ *
121
+ * @param transport - Any transport to test
122
+ * @returns `true` when both `on` and `removeListener` methods are present
123
+ */
124
+ function isEventfulTransport(transport) {
125
+ const t = transport;
126
+ return typeof t.on === "function" && typeof t.removeListener === "function";
127
+ }
128
+ //#endregion
129
+ //#region src/transport/BaseRpcTransport.ts
130
+ /**
131
+ * Optional convenience base class for users writing new wire-level
132
+ * {@link Transport} backends (WebSocket, IPC, custom HTTP, in-process mock,
133
+ * etc.). Handles JSON-RPC framing, id assignment, bigint serialization, and
134
+ * standard error parsing so subclasses implement only the byte-level
135
+ * `send(envelope, options)` hook.
136
+ *
137
+ * Users who already have a {@link Transport} in hand (window.ethereum, viem
138
+ * `WalletClient`, ethers `Eip1193Provider`-shaped object, etc.) do NOT need
139
+ * this class — they pass their object directly.
140
+ *
141
+ * @example
142
+ * ```ts
143
+ * class WebSocketTransport extends BaseRpcTransport {
144
+ * protected async send(envelope) {
145
+ * // serialize envelope to JSON, send over the socket, await response
146
+ * return JSON.parse(await this.socket.sendAndAwait(JSON.stringify(envelope)));
147
+ * }
148
+ * }
149
+ * ```
150
+ */
151
+ var BaseRpcTransport = class BaseRpcTransport {
152
+ nextId = 1;
153
+ /**
154
+ * Build a JSON-RPC envelope, delegate the wire I/O to the subclass's
155
+ * {@link BaseRpcTransport.send} method, and parse the response.
156
+ *
157
+ * @throws {@link TransportRpcError} when the response contains an `error` field
158
+ * @throws {@link TransportRpcError} (code -32603) when the response is malformed
159
+ */
160
+ async request(args, options) {
161
+ const envelope = {
162
+ jsonrpc: "2.0",
163
+ id: this.nextId++,
164
+ method: args.method,
165
+ params: args.params
166
+ };
167
+ const raw = await this.send(envelope, options);
168
+ return BaseRpcTransport.parseResponse(raw);
169
+ }
170
+ /**
171
+ * Serialize a JSON-RPC envelope to a string, converting bigint values to
172
+ * `0x`-prefixed hex strings (preserving the historical SDK behavior).
173
+ */
174
+ static serializeEnvelope(envelope) {
175
+ return JSON.stringify(envelope, (_key, value) => typeof value === "bigint" ? `0x${value.toString(16)}` : value);
176
+ }
177
+ /**
178
+ * Parse a decoded JSON-RPC response. Returns the `result` field on success
179
+ * or throws a {@link TransportRpcError} on error / malformed response.
180
+ */
181
+ static parseResponse(raw) {
182
+ if (raw == null || typeof raw !== "object") throw new TransportRpcError(-32603, "malformed JSON-RPC response", raw);
183
+ const response = raw;
184
+ if ("error" in response) {
185
+ const { code, message, data } = response.error;
186
+ throw new TransportRpcError(code, message, data);
187
+ }
188
+ if ("result" in response) return response.result;
189
+ throw new TransportRpcError(-32603, "malformed JSON-RPC response", raw);
190
+ }
119
191
  };
120
- /** EIP-712 primary type string used when signing Safe UserOperations. */
121
- const EIP712_SAFE_OPERATION_PRIMARY_TYPE = "SafeOp";
122
- /** EIP-712 type definition for Safe UserOperation signing (EntryPoint v0.6) */
123
- const EIP712_SAFE_OPERATION_V6_TYPE = { SafeOp: [
124
- {
125
- type: "address",
126
- name: "safe"
127
- },
128
- {
129
- type: "uint256",
130
- name: "nonce"
131
- },
132
- {
133
- type: "bytes",
134
- name: "initCode"
135
- },
136
- {
137
- type: "bytes",
138
- name: "callData"
139
- },
140
- {
141
- type: "uint256",
142
- name: "callGasLimit"
143
- },
144
- {
145
- type: "uint256",
146
- name: "verificationGasLimit"
147
- },
148
- {
149
- type: "uint256",
150
- name: "preVerificationGas"
151
- },
152
- {
153
- type: "uint256",
154
- name: "maxFeePerGas"
155
- },
156
- {
157
- type: "uint256",
158
- name: "maxPriorityFeePerGas"
159
- },
160
- {
161
- type: "bytes",
162
- name: "paymasterAndData"
163
- },
164
- {
165
- type: "uint48",
166
- name: "validAfter"
167
- },
168
- {
169
- type: "uint48",
170
- name: "validUntil"
171
- },
172
- {
173
- type: "address",
174
- name: "entryPoint"
192
+ //#endregion
193
+ //#region src/transport/HttpTransport.ts
194
+ /**
195
+ * Default concrete {@link Transport}: POSTs JSON-RPC envelopes to an HTTP
196
+ * endpoint. Used by every URL-string call site once a string is normalized
197
+ * into a transport.
198
+ *
199
+ * @example
200
+ * ```ts
201
+ * const t = new HttpTransport("https://api.candide.dev/public/v3/11155111");
202
+ * const chainId = await t.request<string>({ method: "eth_chainId" });
203
+ *
204
+ * // With auth headers and a custom fetch:
205
+ * const t2 = new HttpTransport("https://...", {
206
+ * headers: { Authorization: `Bearer ${token}` },
207
+ * fetch: myFetchWithRetry,
208
+ * });
209
+ * ```
210
+ */
211
+ var HttpTransport = class HttpTransport extends BaseRpcTransport {
212
+ /** Endpoint URL this transport POSTs to. */
213
+ url;
214
+ /** Options passed at construction time. */
215
+ options;
216
+ /**
217
+ * @param url - JSON-RPC endpoint URL (bundler, paymaster, or node)
218
+ * @param options - Optional fetch override and static headers
219
+ */
220
+ constructor(url, options = {}) {
221
+ super();
222
+ this.url = url;
223
+ this.options = options;
175
224
  }
176
- ] };
177
- /** EIP-712 type definition for Safe UserOperation signing (EntryPoint v0.7) */
178
- const EIP712_SAFE_OPERATION_V7_TYPE = { SafeOp: [
179
- {
180
- type: "address",
181
- name: "safe"
182
- },
183
- {
184
- type: "uint256",
185
- name: "nonce"
186
- },
187
- {
188
- type: "bytes",
189
- name: "initCode"
190
- },
191
- {
192
- type: "bytes",
193
- name: "callData"
194
- },
195
- {
196
- type: "uint128",
197
- name: "verificationGasLimit"
198
- },
199
- {
200
- type: "uint128",
201
- name: "callGasLimit"
202
- },
203
- {
204
- type: "uint256",
205
- name: "preVerificationGas"
206
- },
207
- {
208
- type: "uint128",
209
- name: "maxPriorityFeePerGas"
210
- },
211
- {
212
- type: "uint128",
213
- name: "maxFeePerGas"
214
- },
215
- {
216
- type: "bytes",
217
- name: "paymasterAndData"
218
- },
219
- {
220
- type: "uint48",
221
- name: "validAfter"
222
- },
223
- {
224
- type: "uint48",
225
- name: "validUntil"
226
- },
227
- {
228
- type: "address",
229
- name: "entryPoint"
225
+ async send(envelope, options) {
226
+ const headers = {
227
+ ...this.options.headers ?? {},
228
+ "Content-Type": "application/json"
229
+ };
230
+ const body = HttpTransport.serializeEnvelope(envelope);
231
+ return await (await (this.options.fetch ?? globalThis.fetch)(this.url, {
232
+ method: "POST",
233
+ headers,
234
+ body,
235
+ redirect: "follow",
236
+ signal: options?.signal
237
+ })).json();
230
238
  }
231
- ] };
232
- /** EIP-712 primary type string used when signing multi-chain Safe operations. */
233
- const EIP712_MULTI_CHAIN_OPERATIONS_PRIMARY_TYPE = "MerkleTreeRoot";
234
- /** EIP-712 type definition for multi-chain Safe operations using Merkle tree roots */
235
- const EIP712_MULTI_CHAIN_OPERATIONS_TYPE = { MerkleTreeRoot: [{
236
- type: "bytes32",
237
- name: "merkleTreeRoot"
238
- }] };
239
- /** Default address for the secp256r1 (P-256) precompile used by WebAuthn verification */
240
- const DEFAULT_SECP256R1_PRECOMPILE_ADDRESS = "0x0000000000000000000000000000000000000100";
241
- /** Uniswap Calibur singleton v1.0.0 (EntryPoint v0.8) */
242
- const CALIBUR_UNISWAP_V1_0_0_SINGLETON_ADDRESS = "0x000000009B1D0aF20D8C6d0A44e162d11F9b8f00";
243
- /** Candide Calibur singleton v0.1.0 (EntryPoint v0.9, unaudited) */
244
- const CALIBUR_CANDIDE_V0_1_0_SINGLETON_ADDRESS = "0x71032285A847c4311Eb7ec2E7A636aB94A9805Aa";
239
+ };
240
+ /**
241
+ * Narrowing helper for {@link HttpTransport}. Useful for code that wants to
242
+ * read the underlying URL e.g. when serializing a configured Bundler for
243
+ * logging or diagnostics.
244
+ *
245
+ * @example
246
+ * ```ts
247
+ * if (isHttpTransport(bundler.transport)) {
248
+ * console.log("Bundler URL:", bundler.transport.url);
249
+ * }
250
+ * ```
251
+ */
252
+ function isHttpTransport(transport) {
253
+ return transport instanceof HttpTransport;
254
+ }
245
255
  //#endregion
246
256
  //#region src/types.ts
247
257
  /**
@@ -275,560 +285,456 @@ var abstractionkit = (function(exports, ethers) {
275
285
  return PolygonChain;
276
286
  }({});
277
287
  //#endregion
278
- //#region src/utils.ts
279
- function buildDomainSeparator(chainId, entrypoint) {
280
- return (0, ethers.keccak256)(ethers.AbiCoder.defaultAbiCoder().encode(["(bytes32,bytes32,bytes32,uint256,address)"], [[
281
- "0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f",
282
- "0x364da28a5c92bcc87fe97c8813a6c6b8a3a049b0ea0a328fcb0b4f0e00337586",
283
- "0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6",
284
- chainId,
285
- entrypoint
286
- ]]));
288
+ //#region src/transport/normalize.ts
289
+ /**
290
+ * Recursively convert any `bigint` values inside an RPC param to `0x`-prefixed
291
+ * hex strings, descending into arrays and plain objects. Other values pass
292
+ * through unchanged.
293
+ *
294
+ * Required for user-supplied {@link Transport} implementations that don't go
295
+ * through {@link BaseRpcTransport.serializeEnvelope} (EIP-1193 providers, viem
296
+ * clients, etc.) and would otherwise see raw `bigint`s in `params`. Mirrors the
297
+ * normalization done in {@link sendJsonRpcRequest}.
298
+ *
299
+ * @internal
300
+ */
301
+ function normalizeRpcValue(value) {
302
+ if (typeof value === "bigint") return `0x${value.toString(16)}`;
303
+ if (Array.isArray(value)) return value.map(normalizeRpcValue);
304
+ if (value !== null && typeof value === "object") {
305
+ const out = {};
306
+ for (const [k, v] of Object.entries(value)) out[k] = normalizeRpcValue(v);
307
+ return out;
308
+ }
309
+ return value;
287
310
  }
288
311
  /**
289
- * Compute the UserOperation hash for any supported EntryPoint version.
290
- * This hash is what gets signed by the account owner(s).
291
- * Automatically selects the correct packing format based on the entrypoint address.
312
+ * Wrap a {@link Transport} so every outbound `request` has its `params`
313
+ * normalized (bigints 0x-hex) before delegation. Idempotent: wrapping an
314
+ * already-normalizing transport is harmless because the second pass sees only
315
+ * strings.
292
316
  *
293
- * @param useroperation - UserOperation to hash
294
- * @param entrypointAddress - EntryPoint contract address (determines hash format)
295
- * @param chainId - Target chain ID
296
- * @returns The UserOperation hash as a hex string
317
+ * Applied once at each service boundary (Bundler, CandidePaymaster,
318
+ * Erc7677Paymaster, JsonRpcNode, …) so normalization is impossible to forget
319
+ * per-method. Required for user-supplied transports (EIP-1193 providers, viem
320
+ * clients, etc.) that don't route through {@link BaseRpcTransport.serializeEnvelope}.
321
+ *
322
+ * @internal
297
323
  */
298
- function createUserOperationHash(useroperation, entrypointAddress, chainId) {
299
- let packedUserOperationHash;
300
- const abiCoder = ethers.AbiCoder.defaultAbiCoder();
301
- let userOperationHash;
302
- if (entrypointAddress.toLowerCase() === "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789".toLowerCase()) {
303
- packedUserOperationHash = (0, ethers.keccak256)(createPackedUserOperationV6(useroperation));
304
- userOperationHash = (0, ethers.keccak256)(abiCoder.encode([
305
- "bytes32",
306
- "address",
307
- "uint256"
308
- ], [
309
- packedUserOperationHash,
310
- entrypointAddress,
311
- chainId
312
- ]));
313
- } else if (entrypointAddress.toLowerCase() === "0x0000000071727De22E5E9d8BAf0edAc6f37da032".toLowerCase()) {
314
- packedUserOperationHash = (0, ethers.keccak256)(createPackedUserOperationV7(useroperation));
315
- userOperationHash = (0, ethers.keccak256)(abiCoder.encode([
316
- "bytes32",
317
- "address",
318
- "uint256"
319
- ], [
320
- packedUserOperationHash,
321
- entrypointAddress,
322
- chainId
323
- ]));
324
- } else if (entrypointAddress.toLowerCase() === "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108".toLowerCase()) {
325
- packedUserOperationHash = (0, ethers.keccak256)(createPackedUserOperationV8(useroperation));
326
- userOperationHash = (0, ethers.keccak256)(`0x1901${buildDomainSeparator(chainId, entrypointAddress).slice(2)}${packedUserOperationHash.slice(2)}`);
327
- } else if (entrypointAddress.toLowerCase() === "0x433709009B8330FDa32311DF1C2AFA402eD8D009".toLowerCase()) {
328
- packedUserOperationHash = (0, ethers.keccak256)(createPackedUserOperationV9(useroperation));
329
- userOperationHash = (0, ethers.keccak256)(`0x1901${buildDomainSeparator(chainId, entrypointAddress).slice(2)}${packedUserOperationHash.slice(2)}`);
330
- } else throw new RangeError(`unsupported entrypoint address: ${entrypointAddress}`);
331
- return userOperationHash;
324
+ function normalizingTransport(inner) {
325
+ return { request(args, options) {
326
+ const params = args.params == null ? args.params : normalizeRpcValue(args.params);
327
+ return inner.request({
328
+ method: args.method,
329
+ params
330
+ }, options);
331
+ } };
332
332
  }
333
+ //#endregion
334
+ //#region src/transport/JsonRpcNode.ts
335
+ const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
333
336
  /**
334
- * @internal
335
- * Reconstruct the packed `initCode` field for an EntryPoint v0.8/v0.9
336
- * UserOperation. When `eip7702Auth.address` is set, the EIP-7702 delegatee
337
- * address replaces the factory address in initCode (only `factoryData` is
338
- * concatenated). Otherwise, behaves like v0.7 (`factory + factoryData`).
337
+ * High-level service class for the JSON-RPC node methods abstractionkit reads
338
+ * from. Intentionally NOT a general Ethereum client only exposes the
339
+ * methods the SDK actually uses. For broader functionality, drop down to
340
+ * `.transport` and call `request({ method, params })` directly, or use a
341
+ * dedicated library like viem / ethers.
339
342
  *
340
- * Shared by {@link createPackedUserOperationV8} /
341
- * {@link createPackedUserOperationV9} and Simple7702Account's typed-data
342
- * builder. Not part of the public API; only `export`ed for cross-module
343
- * use within the package and not re-exported from `src/abstractionkit.ts`.
343
+ * Like {@link Bundler} and the paymaster classes, `JsonRpcNode` itself
344
+ * implements {@link Transport} so it can be passed back into any Transport
345
+ * position.
344
346
  *
345
- * @param useroperation - V8 or V9 UserOperation to read fields from
346
- * @returns Hex-encoded initCode
347
+ * @example
348
+ * ```ts
349
+ * const node = new JsonRpcNode("https://ethereum-sepolia.publicnode.com");
350
+ * const id = await node.chainId();
351
+ * const code = await node.getCode("0x...");
352
+ *
353
+ * // Also a Transport — can be slotted in:
354
+ * const bundler = new Bundler(node); // bundler will speak through this node
355
+ * ```
347
356
  */
348
- function buildPackedInitCodeV8V9(useroperation) {
349
- if (useroperation.factory == null) return "0x";
350
- const eip7702Auth = useroperation.eip7702Auth;
351
- return (eip7702Auth != null && eip7702Auth.address != null ? eip7702Auth.address : useroperation.factory) + (useroperation.factoryData != null ? useroperation.factoryData.slice(2) : "");
352
- }
357
+ var JsonRpcNode = class JsonRpcNode {
358
+ /**
359
+ * The raw transport the user passed in (or {@link HttpTransport} when a URL
360
+ * string was passed). Exposed for introspection reading `.url`,
361
+ * `isHttpTransport(...)` checks, passing it back into another service.
362
+ *
363
+ * Calls made directly on this field (`node.transport.request(...)`) go
364
+ * to the raw transport and skip SDK-level behavior like bigint param
365
+ * normalization. For SDK-pipeline behavior, use {@link JsonRpcNode.request}
366
+ * or the typed methods.
367
+ */
368
+ transport;
369
+ /** Normalizing wrapper around {@link transport}, used for every SDK-outbound call. */
370
+ outbound;
371
+ /**
372
+ * @param rpc - Node JSON-RPC endpoint URL, or any {@link Transport}
373
+ */
374
+ constructor(rpc) {
375
+ this.transport = typeof rpc === "string" ? new HttpTransport(rpc) : rpc;
376
+ this.outbound = normalizingTransport(this.transport);
377
+ }
378
+ /**
379
+ * Normalize any acceptable input into a {@link JsonRpcNode}. Used at every
380
+ * public-API widening site (in account / paymaster classes). When the
381
+ * input is already a `JsonRpcNode`, the same instance is returned by
382
+ * reference, so a user's preconstructed `JsonRpcNode` is never re-wrapped.
383
+ *
384
+ * @param input - URL string, Transport, or existing JsonRpcNode
385
+ */
386
+ static from(input) {
387
+ return input instanceof JsonRpcNode ? input : new JsonRpcNode(input);
388
+ }
389
+ /**
390
+ * Transport delegate. Routes through the normalizing wrapper so `bigint`
391
+ * values in `params` are converted to `0x`-hex before reaching
392
+ * user-supplied transports that don't go through
393
+ * {@link BaseRpcTransport.serializeEnvelope}. Lets a `JsonRpcNode` itself
394
+ * slot into any other transport position.
395
+ */
396
+ request(args, options) {
397
+ return this.outbound.request(args, options);
398
+ }
399
+ /**
400
+ * `eth_chainId`. Returns the hex-encoded chain id (e.g. `"0xaa36a7"` for
401
+ * Sepolia).
402
+ */
403
+ async chainId(options) {
404
+ try {
405
+ const result = await this.outbound.request({ method: "eth_chainId" }, options);
406
+ if (typeof result !== "string") throw new AbstractionKitError("BAD_DATA", "eth_chainId returned ill formed data", { context: JSON.stringify(result) });
407
+ return result;
408
+ } catch (err) {
409
+ throw translateNodeError(err, "eth_chainId");
410
+ }
411
+ }
412
+ /**
413
+ * `eth_blockNumber`. Returns the latest block number as a bigint.
414
+ */
415
+ async blockNumber(options) {
416
+ try {
417
+ const result = await this.outbound.request({ method: "eth_blockNumber" }, options);
418
+ if (typeof result !== "string") throw new AbstractionKitError("BAD_DATA", "eth_blockNumber returned ill formed data", { context: JSON.stringify(result) });
419
+ return BigInt(result);
420
+ } catch (err) {
421
+ throw translateNodeError(err, "eth_blockNumber");
422
+ }
423
+ }
424
+ /**
425
+ * `eth_getCode`. Returns the deployed bytecode at `address` at the given
426
+ * block tag (default `"latest"`).
427
+ */
428
+ async getCode(address, blockTag = "latest", options) {
429
+ try {
430
+ const result = await this.outbound.request({
431
+ method: "eth_getCode",
432
+ params: [address, blockTag]
433
+ }, options);
434
+ if (typeof result !== "string") throw new AbstractionKitError("BAD_DATA", "eth_getCode returned ill formed data", { context: JSON.stringify(result) });
435
+ return result;
436
+ } catch (err) {
437
+ throw translateNodeError(err, "eth_getCode");
438
+ }
439
+ }
440
+ /**
441
+ * `eth_call`. Executes a read-only call against `to` and returns the raw
442
+ * return data as a hex string. Supports state overrides via the optional
443
+ * third parameter.
444
+ */
445
+ async call(tx, blockTag = "latest", stateOverrides, options) {
446
+ const params = stateOverrides == null ? [tx, blockTag] : [
447
+ tx,
448
+ blockTag,
449
+ stateOverrides
450
+ ];
451
+ try {
452
+ const result = await this.outbound.request({
453
+ method: "eth_call",
454
+ params
455
+ }, options);
456
+ if (typeof result !== "string") throw new AbstractionKitError("BAD_DATA", "eth_call returned ill formed data", { context: JSON.stringify(result) });
457
+ return result;
458
+ } catch (err) {
459
+ throw translateNodeError(err, "eth_call");
460
+ }
461
+ }
462
+ /**
463
+ * `eth_getTransactionCount`. Returns the transaction count (account nonce
464
+ * at the EOA level — not the EntryPoint nonce; see
465
+ * {@link JsonRpcNode.getEntryPointNonce}) as a bigint.
466
+ */
467
+ async getTransactionCount(address, blockTag = "latest", options) {
468
+ try {
469
+ const result = await this.outbound.request({
470
+ method: "eth_getTransactionCount",
471
+ params: [address, blockTag]
472
+ }, options);
473
+ if (typeof result !== "string") throw new AbstractionKitError("BAD_DATA", "eth_getTransactionCount returned ill formed data", { context: JSON.stringify(result) });
474
+ return BigInt(result);
475
+ } catch (err) {
476
+ throw translateNodeError(err, "eth_getTransactionCount");
477
+ }
478
+ }
479
+ /**
480
+ * Fetch current gas prices and apply a level multiplier.
481
+ *
482
+ * Tries `eth_maxPriorityFeePerGas` + `eth_gasPrice` first (EIP-1559),
483
+ * falling back to `eth_gasPrice` alone if the priority-fee method is
484
+ * unsupported, and finally to a 1 gwei floor multiplied by `gasLevel`.
485
+ *
486
+ * @param gasLevel - {@link GasOption} multiplier (default: Medium = 1.2x)
487
+ * @returns `[maxFeePerGas, maxPriorityFeePerGas]` as bigints
488
+ */
489
+ async getFeeData(gasLevel = GasOption.Medium, options) {
490
+ try {
491
+ let gasPrice = null;
492
+ let maxPriorityFeePerGas = null;
493
+ try {
494
+ const result = await this.outbound.request({ method: "eth_gasPrice" }, options);
495
+ if (typeof result !== "string") throw new AbstractionKitError("BAD_DATA", "eth_gasPrice returned ill formed data", { context: JSON.stringify(result) });
496
+ gasPrice = BigInt(result);
497
+ } catch (err) {
498
+ if (!isMethodNotSupportedError(err)) throw err;
499
+ }
500
+ try {
501
+ const result = await this.outbound.request({ method: "eth_maxPriorityFeePerGas" }, options);
502
+ if (typeof result !== "string") throw new AbstractionKitError("BAD_DATA", "eth_maxPriorityFeePerGas returned ill formed data", { context: JSON.stringify(result) });
503
+ maxPriorityFeePerGas = BigInt(result);
504
+ } catch (err) {
505
+ if (!isMethodNotSupportedError(err)) throw err;
506
+ }
507
+ let maxFeePerGas;
508
+ let priorityFee;
509
+ if (gasPrice != null && maxPriorityFeePerGas != null) {
510
+ maxFeePerGas = scaleBigIntByGasLevel(gasPrice, gasLevel);
511
+ priorityFee = scaleBigIntByGasLevel(maxPriorityFeePerGas, gasLevel);
512
+ } else if (gasPrice != null) {
513
+ maxFeePerGas = scaleBigIntByGasLevel(gasPrice, gasLevel);
514
+ priorityFee = maxFeePerGas;
515
+ } else {
516
+ maxFeePerGas = scaleBigIntByGasLevel(1000000000n, gasLevel);
517
+ priorityFee = maxFeePerGas;
518
+ }
519
+ if (maxFeePerGas === 0n) maxFeePerGas = 1n;
520
+ if (priorityFee === 0n) priorityFee = 1n;
521
+ if (priorityFee > maxFeePerGas) maxFeePerGas = priorityFee;
522
+ return [maxFeePerGas, priorityFee];
523
+ } catch (err) {
524
+ throw translateNodeError(err, "getFeeData");
525
+ }
526
+ }
527
+ /**
528
+ * Check whether an address is EIP-7702-delegated and return the delegatee
529
+ * address. EIP-7702-delegated accounts have bytecode of the form
530
+ * `0xef0100<20-byte-delegatee>` per the spec.
531
+ *
532
+ * @returns The checksummed delegatee address, or `null` if not delegated
533
+ */
534
+ async getDelegatedAddress(accountAddress, options) {
535
+ const code = (await this.getCode(accountAddress, "latest", options)).toLowerCase();
536
+ if (code.length === 48 && code.startsWith("0xef0100")) return (0, ethers.getAddress)(`0x${code.slice(8)}`);
537
+ return null;
538
+ }
539
+ /**
540
+ * Fetch the smart account's nonce from the EntryPoint contract via
541
+ * `eth_call`. This is the 4337 nonce (an EntryPoint-managed counter with
542
+ * 192-bit parallel keys), not the EOA `eth_getTransactionCount`.
543
+ *
544
+ * @param entryPoint - EntryPoint contract address
545
+ * @param account - Smart account address
546
+ * @param key - Nonce key as a `bigint` (default `0n`). Different keys allow
547
+ * parallel nonce channels. `bigint` so the full `uint192` range is
548
+ * representable (a JS `number` would cap at 2^53−1).
549
+ */
550
+ async getEntryPointNonce(entryPoint, account, key = 0n, options) {
551
+ const data = "0x35567e1a" + ethers.AbiCoder.defaultAbiCoder().encode(["address", "uint192"], [account, key]).slice(2);
552
+ const callResult = await this.call({
553
+ from: ZERO_ADDRESS,
554
+ to: entryPoint,
555
+ data
556
+ }, "latest", void 0, options);
557
+ try {
558
+ return BigInt(callResult);
559
+ } catch (err) {
560
+ throw new AbstractionKitError("BAD_DATA", "getNonce returned ill formed data", {
561
+ cause: ensureError(err),
562
+ context: callResult
563
+ });
564
+ }
565
+ }
566
+ /**
567
+ * Get the EntryPoint deposit balance for an address.
568
+ *
569
+ * @returns The deposit balance in wei as a bigint
570
+ */
571
+ async getEntryPointDeposit(address, entryPoint, options) {
572
+ return (await this.getEntryPointDepositInfo(address, entryPoint, options)).deposit;
573
+ }
574
+ /**
575
+ * Get the full {@link DepositInfo} for an address from the EntryPoint
576
+ * contract.
577
+ */
578
+ async getEntryPointDepositInfo(address, entryPoint, options) {
579
+ const getDepositInfoSelector = "0x5287ce12";
580
+ const abiCoder = ethers.AbiCoder.defaultAbiCoder();
581
+ const data = getDepositInfoSelector + abiCoder.encode(["address"], [address]).slice(2);
582
+ const callResult = await this.call({
583
+ from: ZERO_ADDRESS,
584
+ to: entryPoint,
585
+ data
586
+ }, "latest", void 0, options);
587
+ try {
588
+ const decoded = abiCoder.decode([
589
+ "uint256",
590
+ "bool",
591
+ "uint112",
592
+ "uint32",
593
+ "uint48"
594
+ ], callResult);
595
+ if (decoded.length !== 5) throw new AbstractionKitError("BAD_DATA", "getDepositInfo returned ill formed data", { context: JSON.stringify(decoded) });
596
+ return {
597
+ deposit: BigInt(decoded[0]),
598
+ staked: Boolean(decoded[1]),
599
+ stake: BigInt(decoded[2]),
600
+ unstakeDelaySec: BigInt(decoded[3]),
601
+ withdrawTime: BigInt(decoded[4])
602
+ };
603
+ } catch (err) {
604
+ if (err instanceof AbstractionKitError) throw err;
605
+ throw new AbstractionKitError("BAD_DATA", "getDepositInfo returned ill formed data", { cause: ensureError(err) });
606
+ }
607
+ }
608
+ };
353
609
  /**
354
- * @internal
355
- * Reconstruct the packed `paymasterAndData` field from a UserOperation's
356
- * separate paymaster fields. Returns `0x` when no paymaster is set.
610
+ * Apply a fractional `gasLevel` multiplier (e.g. 1.2 = 120%) to a `bigint`
611
+ * gas price without going through JS number precision.
357
612
  *
358
- * For EntryPoint v0.9, when `paymasterData` ends with the parallel-paymaster
359
- * signature magic suffix (`0x22e325a297439656`), the embedded signature is
360
- * stripped so the userOpHash does not commit to the paymaster's signature
361
- * over itself. Pass `stripV9PaymasterSig=false` to preserve the wire format.
613
+ * The naive `BigInt(Math.ceil(Number(value) * gasLevel))` truncates any
614
+ * `value` above `Number.MAX_SAFE_INTEGER` (2^53 1 ≈ 9.0 × 10^15 wei),
615
+ * which is well within the range a chain can legitimately report
616
+ * (especially on testnets, fork networks, or anomalous mainnet spikes).
362
617
  *
363
- * Shared by v7/v8/v9 packers and Simple7702Account's typed-data builder.
364
- * Not part of the public API; only `export`ed for cross-module use within
365
- * the package and not re-exported from `src/abstractionkit.ts`.
618
+ * Approach: scale the multiplier into integer space (three-decimal precision
619
+ * sufficient for `GasOption` and any reasonable custom value), do the
620
+ * multiplication in `BigInt`, then ceiling-divide back down. Preserves the
621
+ * original `Math.ceil(...)` rounding behavior bit-for-bit on small values.
366
622
  *
367
- * @param useroperation - V7/V8/V9 UserOperation to read fields from
368
- * @param stripV9PaymasterSig - Whether to strip the v0.9 paymaster signature suffix
369
- * @returns Hex-encoded paymasterAndData
623
+ * @internal
370
624
  */
371
- function buildPaymasterAndData(useroperation, stripV9PaymasterSig = false) {
372
- if (useroperation.paymaster == null) return "0x";
373
- const abiCoder = ethers.AbiCoder.defaultAbiCoder();
374
- let paymasterAndData = useroperation.paymaster;
375
- if (useroperation.paymasterVerificationGasLimit != null) paymasterAndData += abiCoder.encode(["uint128"], [useroperation.paymasterVerificationGasLimit]).slice(34);
376
- if (useroperation.paymasterPostOpGasLimit != null) paymasterAndData += abiCoder.encode(["uint128"], [useroperation.paymasterPostOpGasLimit]).slice(34);
377
- if (useroperation.paymasterData != null) {
378
- const PAYMASTER_SIG_MAGIC = "22e325a297439656";
379
- if (stripV9PaymasterSig && useroperation.paymasterData.toLowerCase().endsWith(PAYMASTER_SIG_MAGIC)) {
380
- const sigLenHex = useroperation.paymasterData.slice(useroperation.paymasterData.length - 16 - 4, useroperation.paymasterData.length - 16);
381
- const sigLen = parseInt(sigLenHex, 16);
382
- const prefixEnd = useroperation.paymasterData.length - 16 - 4 - sigLen * 2;
383
- paymasterAndData += useroperation.paymasterData.slice(0, prefixEnd).replaceAll("0x", "") + PAYMASTER_SIG_MAGIC;
384
- } else paymasterAndData += useroperation.paymasterData.slice(2);
385
- }
386
- return paymasterAndData;
625
+ function scaleBigIntByGasLevel(value, gasLevel) {
626
+ const scale = 1000n;
627
+ return (value * BigInt(Math.round(gasLevel * Number(scale))) + scale - 1n) / scale;
387
628
  }
388
629
  /**
389
- * ABI-encode and pack a UserOperation for hashing (EntryPoint v0.6 format).
390
- * Bytes fields (initCode, callData, paymasterAndData) are keccak256-hashed before packing.
630
+ * Detect whether an error indicates the JSON-RPC method itself is not
631
+ * implemented by the node. Used by {@link JsonRpcNode.getFeeData} to fall
632
+ * back gracefully on chains that don't expose EIP-1559 fee methods, while
633
+ * still surfacing transport, auth, and parse errors to the caller.
391
634
  *
392
- * @param useroperation - UserOperation to pack
393
- * @returns ABI-encoded packed UserOperation as a hex string
635
+ * @internal
394
636
  */
395
- function createPackedUserOperationV6(useroperation) {
396
- const useroperationValuesArrayWithHashedByteValues = [
397
- useroperation.sender,
398
- useroperation.nonce,
399
- (0, ethers.keccak256)(useroperation.initCode),
400
- (0, ethers.keccak256)(useroperation.callData),
401
- useroperation.callGasLimit,
402
- useroperation.verificationGasLimit,
403
- useroperation.preVerificationGas,
404
- useroperation.maxFeePerGas,
405
- useroperation.maxPriorityFeePerGas,
406
- (0, ethers.keccak256)(useroperation.paymasterAndData)
407
- ];
408
- return ethers.AbiCoder.defaultAbiCoder().encode([
409
- "address",
410
- "uint256",
411
- "bytes32",
412
- "bytes32",
413
- "uint256",
414
- "uint256",
415
- "uint256",
416
- "uint256",
417
- "uint256",
418
- "bytes32"
419
- ], useroperationValuesArrayWithHashedByteValues);
637
+ function isMethodNotSupportedError(err) {
638
+ if (err?.code === -32601) return true;
639
+ const message = err?.message?.toLowerCase() ?? "";
640
+ return message.includes("method not found") || message.includes("not supported") || message.includes("unsupported");
420
641
  }
421
642
  /**
422
- * ABI-encode and pack a UserOperation for hashing (EntryPoint v0.7 format).
423
- * Reconstructs initCode, accountGasLimits, gasFees, and paymasterAndData from separate fields.
643
+ * Translate a transport-level error (or already-wrapped {@link AbstractionKitError})
644
+ * into the `NODE_ERROR` outer / specific inner shape used by {@link JsonRpcNode}.
424
645
  *
425
- * @param useroperation - UserOperation to pack
426
- * @returns ABI-encoded packed UserOperation as a hex string
427
- */
428
- function createPackedUserOperationV7(useroperation) {
429
- const abiCoder = ethers.AbiCoder.defaultAbiCoder();
430
- let initCode = "0x";
431
- if (useroperation.factory != null) {
432
- initCode = useroperation.factory;
433
- if (useroperation.factoryData != null) initCode += useroperation.factoryData.slice(2);
434
- }
435
- const accountGasLimits = "0x" + abiCoder.encode(["uint128"], [useroperation.verificationGasLimit]).slice(34) + abiCoder.encode(["uint128"], [useroperation.callGasLimit]).slice(34);
436
- const gasFees = "0x" + abiCoder.encode(["uint128"], [useroperation.maxPriorityFeePerGas]).slice(34) + abiCoder.encode(["uint128"], [useroperation.maxFeePerGas]).slice(34);
437
- const paymasterAndData = buildPaymasterAndData(useroperation);
438
- const useroperationValuesArrayWithHashedByteValues = [
439
- useroperation.sender,
440
- useroperation.nonce,
441
- (0, ethers.keccak256)(initCode),
442
- (0, ethers.keccak256)(useroperation.callData),
443
- accountGasLimits,
444
- useroperation.preVerificationGas,
445
- gasFees,
446
- (0, ethers.keccak256)(paymasterAndData)
447
- ];
448
- return abiCoder.encode([
449
- "address",
450
- "uint256",
451
- "bytes32",
452
- "bytes32",
453
- "bytes32",
454
- "uint256",
455
- "bytes32",
456
- "bytes32"
457
- ], useroperationValuesArrayWithHashedByteValues);
458
- }
459
- /**
460
- * ABI-encode and pack a UserOperation for hashing (EntryPoint v0.9 format).
646
+ * - `AbstractionKitError` passes through unchanged (already domain-translated).
647
+ * - {@link ProviderRpcError} with a known JSON-RPC code inner code from
648
+ * {@link JsonRpcErrorDict}.
649
+ * - Anything else → inner `UNKNOWN_ERROR`.
461
650
  *
462
- * @param useroperation - UserOperation to pack
463
- * @returns ABI-encoded packed UserOperation as a hex string
651
+ * @internal
464
652
  */
465
- function createPackedUserOperationV9(useroperation) {
466
- return baseCreatePackedUserOperationV8V9(useroperation, true);
653
+ function translateNodeError(err, method) {
654
+ if (err instanceof AbstractionKitError) return err;
655
+ const code = err?.code;
656
+ const codeString = code != null ? String(code) : "";
657
+ const innerCode = codeString in JsonRpcErrorDict ? JsonRpcErrorDict[codeString] : "UNKNOWN_ERROR";
658
+ const error = ensureError(err);
659
+ return new AbstractionKitError("NODE_ERROR", `node ${method} rpc call failed`, {
660
+ cause: new AbstractionKitError(innerCode, error.message, { errno: code }),
661
+ errno: code
662
+ });
467
663
  }
664
+ //#endregion
665
+ //#region src/Bundler.ts
468
666
  /**
469
- * ABI-encode and pack a UserOperation for hashing (EntryPoint v0.8 format).
667
+ * JSON-RPC client for an ERC-4337 bundler.
470
668
  *
471
- * @param useroperation - UserOperation to pack
472
- * @returns ABI-encoded packed UserOperation as a hex string
669
+ * Accepts either a URL string (wrapped automatically in {@link HttpTransport})
670
+ * or any {@link Transport} including a viem client, an EIP-1193 wallet
671
+ * provider, an in-process mock, or a user-composed fallback/retry transport.
672
+ *
673
+ * The class itself implements {@link Transport}, so a `Bundler` can be passed
674
+ * back into any other Transport position.
675
+ *
676
+ * Candide bundler endpoints:
677
+ * - `https://api.candide.dev/api/v3/{chainId}/{apiKey}` (authenticated)
678
+ * - `https://api.candide.dev/public/v3/{chainId}` (public)
679
+ *
680
+ * @example URL string (most common)
681
+ * ```ts
682
+ * const bundler = new Bundler("https://api.candide.dev/public/v3/11155111");
683
+ * const receipt = await bundler.getUserOperationReceipt(userOpHash);
684
+ * ```
685
+ *
686
+ * @example Custom transport (composed retry behavior)
687
+ * ```ts
688
+ * const retryingTransport: Transport = {
689
+ * async request(args, options) {
690
+ * for (let i = 0; i < 3; i++) {
691
+ * try { return await inner.request(args, options); }
692
+ * catch (e) { if (i === 2) throw e; await sleep(2 ** i * 100); }
693
+ * }
694
+ * },
695
+ * };
696
+ * const bundler = new Bundler(retryingTransport);
697
+ * ```
473
698
  */
474
- function createPackedUserOperationV8(useroperation) {
475
- return baseCreatePackedUserOperationV8V9(useroperation, false);
476
- }
477
- /**
478
- * Shared packer for EntryPoint v0.8 and v0.9 UserOperations.
479
- * @param useroperation - UserOperation to pack
480
- * @param is_v9 - If true, strips the paymaster signature suffix (v0.9-specific)
481
- * @returns ABI-encoded packed UserOperation as a hex string
482
- */
483
- function baseCreatePackedUserOperationV8V9(useroperation, is_v9) {
484
- const abiCoder = ethers.AbiCoder.defaultAbiCoder();
485
- const initCode = buildPackedInitCodeV8V9(useroperation);
486
- const accountGasLimits = "0x" + abiCoder.encode(["uint128"], [useroperation.verificationGasLimit]).slice(34) + abiCoder.encode(["uint128"], [useroperation.callGasLimit]).slice(34);
487
- const gasFees = "0x" + abiCoder.encode(["uint128"], [useroperation.maxPriorityFeePerGas]).slice(34) + abiCoder.encode(["uint128"], [useroperation.maxFeePerGas]).slice(34);
488
- const paymasterAndData = buildPaymasterAndData(useroperation, is_v9);
489
- const useroperationValuesArrayWithHashedByteValues = [
490
- "0x29a0bca4af4be3421398da00295e58e6d7de38cb492214754cb6a47507dd6f8e",
491
- useroperation.sender,
492
- useroperation.nonce,
493
- (0, ethers.keccak256)(initCode),
494
- (0, ethers.keccak256)(useroperation.callData),
495
- accountGasLimits,
496
- useroperation.preVerificationGas,
497
- gasFees,
498
- (0, ethers.keccak256)(paymasterAndData)
499
- ];
500
- return abiCoder.encode([
501
- "bytes32",
502
- "address",
503
- "uint256",
504
- "bytes32",
505
- "bytes32",
506
- "bytes32",
507
- "uint256",
508
- "bytes32",
509
- "bytes32"
510
- ], useroperationValuesArrayWithHashedByteValues);
511
- }
512
- /**
513
- * Encode a function call into ABI-encoded calldata.
514
- *
515
- * @param functionSelector - 4-byte hex function selector (e.g., "0xa9059cbb" for ERC-20 transfer)
516
- * @param functionInputAbi - Array of ABI type strings (e.g., ["address", "uint256"])
517
- * @param functionInputParameters - Array of parameter values matching the ABI types
518
- * @returns ABI-encoded calldata as a hex string (selector + encoded parameters)
519
- *
520
- * @example
521
- * const transferCallData = createCallData(
522
- * "0xa9059cbb",
523
- * ["address", "uint256"],
524
- * ["0xRecipientAddress", 1000000n],
525
- * );
526
- */
527
- function createCallData(functionSelector, functionInputAbi, functionInputParameters) {
528
- return functionSelector + ethers.AbiCoder.defaultAbiCoder().encode(functionInputAbi, functionInputParameters).slice(2);
529
- }
530
- /**
531
- * Send a JSON-RPC request to the specified endpoint.
532
- * Automatically converts bigint values to hex strings in the request body.
533
- *
534
- * @param rpcUrl - The JSON-RPC endpoint URL (bundler, node, or paymaster)
535
- * @param method - The JSON-RPC method name (e.g., "eth_call", "eth_sendUserOperation")
536
- * @param params - The JSON-RPC parameters
537
- * @param headers - Custom HTTP headers (defaults to Content-Type: application/json)
538
- * @param paramsKeyName - Key name for the params field (defaults to "params")
539
- * @returns The result field from the JSON-RPC response
540
- * @throws AbstractionKitError if the RPC returns an error
541
- */
542
- async function sendJsonRpcRequest(rpcUrl, method, params, headers = { "Content-Type": "application/json" }, paramsKeyName = "params") {
543
- const requestOptions = {
544
- method: "POST",
545
- headers,
546
- body: JSON.stringify({
547
- method,
548
- [paramsKeyName]: params,
549
- id: Date.now(),
550
- jsonrpc: "2.0"
551
- }, (_key, value) => typeof value === "bigint" ? `0x${value.toString(16)}` : value),
552
- redirect: "follow"
553
- };
554
- const response = await (await fetch(rpcUrl, requestOptions)).json();
555
- if ("result" in response) return response.result;
556
- else if ("simulation_results" in response) return response.simulation_results;
557
- else {
558
- const err = response.error;
559
- const codeString = String(err.code);
560
- if (codeString in BundlerErrorCodeDict) throw new AbstractionKitError(BundlerErrorCodeDict[codeString], err.message, {
561
- errno: err.code,
562
- context: {
563
- url: rpcUrl,
564
- requestOptions: JSON.stringify(requestOptions)
565
- }
566
- });
567
- else throw new AbstractionKitError("UNKNOWN_ERROR", err.message, {
568
- errno: err.code,
569
- context: {
570
- url: rpcUrl,
571
- requestOptions: JSON.stringify(requestOptions)
572
- }
573
- });
574
- }
575
- }
576
- /**
577
- * Get a 4-byte function selector from a Solidity-style function signature.
578
- * Computed as the first 4 bytes of keccak256(signature).
579
- * @param functionSignature - Solidity-style function signature, e.g. "mint(address)"
580
- * @returns Function selector as a 0x-prefixed 10-character hex string
581
- *
582
- * @example
583
- * getFunctionSelector("getNonce(address,uint192)"); // "0x35567e1a"
584
- */
585
- function getFunctionSelector(functionSignature) {
586
- return (0, ethers.id)(functionSignature).slice(0, 10);
587
- }
588
- /**
589
- * Fetch the account's nonce from the EntryPoint contract.
590
- *
591
- * @param rpcUrl - Ethereum JSON-RPC node URL
592
- * @param entryPoint - EntryPoint contract address
593
- * @param account - Smart account address to query
594
- * @param key - Nonce key (default 0). Different keys allow parallel nonce channels.
595
- * @returns The current nonce as a bigint
596
- * @throws AbstractionKitError with code "BAD_DATA" if the nonce call fails
597
- */
598
- async function fetchAccountNonce(rpcUrl, entryPoint, account, key = 0) {
599
- const params = [{
600
- from: "0x0000000000000000000000000000000000000000",
601
- to: entryPoint,
602
- data: createCallData(getFunctionSelector("getNonce(address,uint192)"), ["address", "uint192"], [account, key])
603
- }, "latest"];
604
- try {
605
- const nonce = await sendJsonRpcRequest(rpcUrl, "eth_call", params);
606
- if (typeof nonce === "string") try {
607
- return BigInt(nonce);
608
- } catch (err) {
609
- throw new AbstractionKitError("BAD_DATA", "getNonce returned ill formed data", { cause: ensureError(err) });
610
- }
611
- else throw new AbstractionKitError("BAD_DATA", "getNonce returned ill formed data", { context: JSON.stringify(nonce) });
612
- } catch (err) {
613
- throw new AbstractionKitError("BAD_DATA", "getNonce failed", { cause: ensureError(err) });
614
- }
615
- }
616
- /**
617
- * Fetch current gas prices from a JSON-RPC node.
618
- * Applies a gas level multiplier to adjust for faster or cheaper inclusion.
619
- *
620
- * @param provideRpc - Ethereum JSON-RPC node URL
621
- * @param gasLevel - Gas price multiplier (default: GasOption.Medium = 1.2x)
622
- * @returns A tuple of [maxFeePerGas, maxPriorityFeePerGas] as bigints
623
- */
624
- async function fetchGasPrice(provideRpc, gasLevel = GasOption.Medium) {
625
- try {
626
- const feeData = await new ethers.JsonRpcProvider(provideRpc).getFeeData();
627
- let maxFeePerGas;
628
- let maxPriorityFeePerGas;
629
- if (feeData.maxFeePerGas != null && feeData.maxPriorityFeePerGas != null) {
630
- maxFeePerGas = BigInt(Math.ceil(Number(feeData.maxFeePerGas) * gasLevel));
631
- maxPriorityFeePerGas = BigInt(Math.ceil(Number(feeData.maxPriorityFeePerGas) * gasLevel));
632
- } else if (feeData.gasPrice != null) {
633
- maxFeePerGas = BigInt(Math.ceil(Number(feeData.gasPrice) * gasLevel));
634
- maxPriorityFeePerGas = maxFeePerGas;
635
- } else {
636
- maxFeePerGas = BigInt(Math.ceil(1e9 * gasLevel));
637
- maxPriorityFeePerGas = maxFeePerGas;
638
- }
639
- if (maxFeePerGas === 0n) maxFeePerGas = 1n;
640
- if (maxPriorityFeePerGas === 0n) maxPriorityFeePerGas = 1n;
641
- return [maxFeePerGas, maxPriorityFeePerGas];
642
- } catch (err) {
643
- throw new AbstractionKitError("BAD_DATA", "fetching gas prices from node failed.", { cause: ensureError(err) });
644
- }
645
- }
646
- /**
647
- * Fetch current gas prices from the Polygon Gas Station API.
648
- *
649
- * @param polygonChain - Target Polygon chain (Mainnet, Amoy, etc.)
650
- * @param gasLevel - Gas price level (Slow, Medium, Fast)
651
- * @returns A tuple of [maxFeePerGas, maxPriorityFeePerGas] as bigints
652
- */
653
- async function fetchGasPricePolygon(polygonChain, gasLevel = GasOption.Medium) {
654
- const gasStationUrl = `https://gasstation.polygon.technology/${polygonChain}`;
655
- try {
656
- const response = await (await fetch(gasStationUrl)).json();
657
- let gasPrice;
658
- if (gasLevel === GasOption.Slow) gasPrice = response.safeLow;
659
- else if (gasLevel === GasOption.Medium) gasPrice = response.standard;
660
- else gasPrice = response.fast;
661
- let maxFeePerGas = BigInt(Math.ceil(Number(gasPrice.maxFee) * 1e9));
662
- let maxPriorityFeePerGas = BigInt(Math.ceil(Number(gasPrice.maxPriorityFee) * 1e9));
663
- if (maxFeePerGas === 0n) maxFeePerGas = 1n;
664
- if (maxPriorityFeePerGas === 0n) maxPriorityFeePerGas = 1n;
665
- return [maxFeePerGas, maxPriorityFeePerGas];
666
- } catch (err) {
667
- const error = ensureError(err);
668
- throw new AbstractionKitError("BAD_DATA", `fetching gas prices from ${gasStationUrl} failed.`, { cause: error });
669
- }
670
- }
671
- /**
672
- * Calculate the maximum gas cost (in wei) that a UserOperation could consume.
673
- * Uses different formulas for v0.6 (with paymaster multiplier) and v0.7+ UserOperations.
674
- *
675
- * @param useroperation - The UserOperation to calculate the max gas cost for
676
- * @returns Maximum possible gas cost in wei as a bigint
677
- */
678
- function calculateUserOperationMaxGasCost(useroperation) {
679
- if ("initCode" in useroperation) {
680
- const mul = useroperation.paymasterAndData !== "0x" && useroperation.paymasterAndData != null ? 3n : 0n;
681
- return (useroperation.callGasLimit + useroperation.verificationGasLimit * mul + useroperation.preVerificationGas) * useroperation.maxFeePerGas;
682
- } else return (useroperation.verificationGasLimit + useroperation.callGasLimit + (useroperation.paymasterVerificationGasLimit ?? 0n) + (useroperation.paymasterPostOpGasLimit ?? 0n) + useroperation.preVerificationGas) * useroperation.maxFeePerGas;
683
- }
684
- /**
685
- * Get the deposit balance of an address in the EntryPoint contract.
686
- *
687
- * @param nodeRpcUrl - Ethereum JSON-RPC node URL
688
- * @param address - Address to query the deposit for
689
- * @param entrypointAddress - EntryPoint contract address
690
- * @returns The deposit balance as a bigint
691
- */
692
- async function getBalanceOf(nodeRpcUrl, address, entrypointAddress) {
693
- return (await getDepositInfo(nodeRpcUrl, address, entrypointAddress)).deposit;
694
- }
695
- /**
696
- * Get the full deposit info of an address from the EntryPoint contract.
697
- *
698
- * @param nodeRpcUrl - Ethereum JSON-RPC node URL
699
- * @param address - Address to query
700
- * @param entrypointAddress - EntryPoint contract address
701
- * @returns DepositInfo with deposit, staked, stake, unstakeDelaySec, withdrawTime
702
- */
703
- async function getDepositInfo(nodeRpcUrl, address, entrypointAddress) {
704
- const params = {
705
- from: "0x0000000000000000000000000000000000000000",
706
- to: entrypointAddress,
707
- data: createCallData("0x5287ce12", ["address"], [address])
708
- };
709
- try {
710
- const depositInfoRequestResult = await sendEthCallRequest(nodeRpcUrl, params, "latest");
711
- const decodedCalldata = ethers.AbiCoder.defaultAbiCoder().decode([
712
- "uint256",
713
- "bool",
714
- "uint112",
715
- "uint32",
716
- "uint48"
717
- ], depositInfoRequestResult);
718
- if (decodedCalldata.length === 5) try {
719
- return {
720
- deposit: BigInt(decodedCalldata[0]),
721
- staked: Boolean(decodedCalldata[1]),
722
- stake: BigInt(decodedCalldata[2]),
723
- unstakeDelaySec: BigInt(decodedCalldata[3]),
724
- withdrawTime: BigInt(decodedCalldata[4])
725
- };
726
- } catch (err) {
727
- throw new AbstractionKitError("BAD_DATA", "getDepositInfo returned ill formed data", { cause: ensureError(err) });
728
- }
729
- else throw new AbstractionKitError("BAD_DATA", "getDepositInfo returned ill formed data", { context: JSON.stringify(decodedCalldata) });
730
- } catch (err) {
731
- throw new AbstractionKitError("BAD_DATA", "getDepositInfo failed", { cause: ensureError(err) });
732
- }
733
- }
734
- /**
735
- * Send an eth_call JSON-RPC request with optional state overrides.
736
- *
737
- * @param nodeRpcUrl - Ethereum JSON-RPC node URL
738
- * @param ethCallTransaction - The call transaction parameters
739
- * @param blockNumber - Block number or "latest"
740
- * @param stateOverrides - Optional state overrides for the call
741
- * @returns The call result as a hex string
742
- */
743
- async function sendEthCallRequest(nodeRpcUrl, ethCallTransaction, blockNumber, stateOverrides) {
744
- let params = [];
745
- if (stateOverrides == null) params = [ethCallTransaction, blockNumber];
746
- else params = [
747
- ethCallTransaction,
748
- blockNumber,
749
- stateOverrides
750
- ];
751
- try {
752
- const data = await sendJsonRpcRequest(nodeRpcUrl, "eth_call", params);
753
- if (typeof data === "string") try {
754
- return data;
755
- } catch (err) {
756
- throw new AbstractionKitError("BAD_DATA", "eth_call returned ill formed data", { cause: ensureError(err) });
757
- }
758
- else throw new AbstractionKitError("BAD_DATA", "eth_call returned ill formed data", { context: JSON.stringify(data) });
759
- } catch (err) {
760
- throw new AbstractionKitError("BAD_DATA", "eth_call failed", { cause: ensureError(err) });
699
+ var Bundler = class Bundler {
700
+ /**
701
+ * The raw transport the user passed in (or {@link HttpTransport} when a URL
702
+ * string was passed). Exposed for introspection — reading `.url`,
703
+ * `isHttpTransport(...)` checks, passing it back into another service.
704
+ *
705
+ * Calls made directly on this field (`bundler.transport.request(...)`) go
706
+ * to the raw transport and skip SDK-level behavior like bigint param
707
+ * normalization. For SDK-pipeline behavior, use {@link Bundler.request} or
708
+ * the typed methods.
709
+ */
710
+ transport;
711
+ /** Normalizing wrapper around {@link transport}, used for every SDK-outbound call. */
712
+ outbound;
713
+ /**
714
+ * @param rpc - Bundler JSON-RPC endpoint URL, or any {@link Transport}.
715
+ */
716
+ constructor(rpc) {
717
+ this.transport = typeof rpc === "string" ? new HttpTransport(rpc) : rpc;
718
+ this.outbound = normalizingTransport(this.transport);
761
719
  }
762
- }
763
- /**
764
- * Send an eth_getCode JSON-RPC request to check deployed bytecode.
765
- *
766
- * @param nodeRpcUrl - Ethereum JSON-RPC node URL
767
- * @param contractAddress - Contract address to query
768
- * @param blockNumber - Block number or "latest"
769
- * @returns The deployed bytecode as a hex string
770
- */
771
- async function sendEthGetCodeRequest(nodeRpcUrl, contractAddress, blockNumber) {
772
- const params = [contractAddress, blockNumber];
773
- try {
774
- const data = await sendJsonRpcRequest(nodeRpcUrl, "eth_getCode", params);
775
- if (typeof data === "string") try {
776
- return data;
777
- } catch (err) {
778
- throw new AbstractionKitError("BAD_DATA", "eth_getCode returned ill formed data", { cause: ensureError(err) });
779
- }
780
- else throw new AbstractionKitError("BAD_DATA", "eth_getCode returned ill formed data", { context: JSON.stringify(data) });
781
- } catch (err) {
782
- throw new AbstractionKitError("BAD_DATA", "eth_getCode failed", { cause: ensureError(err) });
720
+ /**
721
+ * Normalize any acceptable input into a `Bundler`. When the input is
722
+ * already a `Bundler` instance, it is returned by reference (so a user's
723
+ * pre-constructed Bundler is never re-wrapped and its transport is
724
+ * reused for follow-up calls like {@link SendUseroperationResponse.included}).
725
+ *
726
+ * @param input - URL string, Transport, or existing Bundler
727
+ */
728
+ static from(input) {
729
+ return input instanceof Bundler ? input : new Bundler(input);
783
730
  }
784
- }
785
- /**
786
- * Check if an address is delegated via EIP-7702 and return the delegatee address.
787
- * EIP-7702 delegated accounts have bytecode in the format `0xef0100` + 20-byte address.
788
- *
789
- * @param accountAddress - The address to check
790
- * @param providerRpc - Ethereum JSON-RPC node URL
791
- * @returns The checksummed delegatee address, or `null` if not delegated
792
- */
793
- async function getDelegatedAddress(accountAddress, providerRpc) {
794
- const code = (await sendEthGetCodeRequest(providerRpc, accountAddress, "latest")).toLowerCase();
795
- if (code.length === 48 && code.startsWith("0xef0100")) return (0, ethers.getAddress)(`0x${code.slice(8)}`);
796
- return null;
797
- }
798
- /**
799
- * Fetch gas prices using either the Polygon Gas Station or a standard JSON-RPC node.
800
- *
801
- * @param providerRpc - Ethereum JSON-RPC node URL (used if polygonGasStation is null)
802
- * @param polygonGasStation - Polygon chain to use for gas station (takes priority)
803
- * @param gasLevel - Gas price multiplier (default: GasOption.Medium)
804
- * @returns A tuple of [maxFeePerGas, maxPriorityFeePerGas] as bigints
805
- */
806
- async function handlefetchGasPrice(providerRpc, polygonGasStation, gasLevel = GasOption.Medium) {
807
- let maxFeePerGas;
808
- let maxPriorityFeePerGas;
809
- if (polygonGasStation != null) [maxFeePerGas, maxPriorityFeePerGas] = await fetchGasPricePolygon(polygonGasStation, gasLevel);
810
- else if (providerRpc != null) [maxFeePerGas, maxPriorityFeePerGas] = await fetchGasPrice(providerRpc, gasLevel);
811
- else throw new AbstractionKitError("BAD_DATA", "providerRpc can't be null if maxFeePerGas and maxPriorityFeePerGas are not overridden");
812
- return [maxFeePerGas, maxPriorityFeePerGas];
813
- }
814
- //#endregion
815
- //#region src/Bundler.ts
816
- /**
817
- * JSON-RPC client for an ERC-4337 bundler.
818
- *
819
- * Candide bundler endpoints:
820
- * - `https://api.candide.dev/api/v3/{chainId}/{apiKey}` (authenticated)
821
- * - `https://api.candide.dev/public/v3/{chainId}` (public)
822
- *
823
- * @example
824
- * const bundler = new Bundler("https://api.candide.dev/public/v3/11155111");
825
- * const receipt = await bundler.getUserOperationReceipt(userOpHash);
826
- */
827
- var Bundler = class {
828
- rpcUrl;
829
- /** @param rpcUrl - The bundler JSON-RPC endpoint URL */
830
- constructor(rpcUrl) {
831
- this.rpcUrl = rpcUrl;
731
+ /**
732
+ * Transport delegate. Forwards directly to the underlying
733
+ * {@link Transport.request}. Lets a `Bundler` itself slot into any other
734
+ * transport position.
735
+ */
736
+ request(args, options) {
737
+ return this.outbound.request(args, options);
832
738
  }
833
739
  /**
834
740
  * Get the bundler's chain ID.
@@ -836,11 +742,11 @@ var abstractionkit = (function(exports, ethers) {
836
742
  */
837
743
  async chainId() {
838
744
  try {
839
- const chainId = await sendJsonRpcRequest(this.rpcUrl, "eth_chainId", []);
840
- if (typeof chainId === "string") return chainId;
841
- else throw new AbstractionKitError("BAD_DATA", "bundler eth_chainId rpc call failed");
745
+ const chainId = await this.outbound.request({ method: "eth_chainId" });
746
+ if (typeof chainId !== "string") throw new AbstractionKitError("BAD_DATA", "bundler eth_chainId rpc call failed");
747
+ return chainId;
842
748
  } catch (err) {
843
- throw new AbstractionKitError("BUNDLER_ERROR", "bundler eth_chainId rpc call failed", { cause: ensureError(err) });
749
+ throw translateBundlerError(err, "eth_chainId");
844
750
  }
845
751
  }
846
752
  /**
@@ -849,9 +755,9 @@ var abstractionkit = (function(exports, ethers) {
849
755
  */
850
756
  async supportedEntryPoints() {
851
757
  try {
852
- return await sendJsonRpcRequest(this.rpcUrl, "eth_supportedEntryPoints", []);
758
+ return await this.outbound.request({ method: "eth_supportedEntryPoints" });
853
759
  } catch (err) {
854
- throw new AbstractionKitError("BUNDLER_ERROR", "bundler eth_supportedEntryPoints rpc call failed", { cause: ensureError(err) });
760
+ throw translateBundlerError(err, "eth_supportedEntryPoints");
855
761
  }
856
762
  }
857
763
  /**
@@ -863,14 +769,15 @@ var abstractionkit = (function(exports, ethers) {
863
769
  */
864
770
  async estimateUserOperationGas(useroperation, entrypointAddress, state_override_set) {
865
771
  try {
866
- let jsonRpcResult = {};
867
- if (typeof state_override_set === "undefined") jsonRpcResult = await sendJsonRpcRequest(this.rpcUrl, "eth_estimateUserOperationGas", [useroperation, entrypointAddress]);
868
- else jsonRpcResult = await sendJsonRpcRequest(this.rpcUrl, "eth_estimateUserOperationGas", [
772
+ const params = state_override_set == null ? [useroperation, entrypointAddress] : [
869
773
  useroperation,
870
774
  entrypointAddress,
871
775
  state_override_set
872
- ]);
873
- const res = jsonRpcResult;
776
+ ];
777
+ const res = await this.outbound.request({
778
+ method: "eth_estimateUserOperationGas",
779
+ params
780
+ });
874
781
  const gasEstimationResult = {
875
782
  callGasLimit: BigInt(res.callGasLimit),
876
783
  preVerificationGas: BigInt(res.preVerificationGas),
@@ -880,7 +787,7 @@ var abstractionkit = (function(exports, ethers) {
880
787
  if (res.paymasterPostOpGasLimit != null) gasEstimationResult.paymasterPostOpGasLimit = BigInt(res.paymasterPostOpGasLimit);
881
788
  return gasEstimationResult;
882
789
  } catch (err) {
883
- throw new AbstractionKitError("BUNDLER_ERROR", "bundler eth_estimateUserOperationGas rpc call failed", { cause: ensureError(err) });
790
+ throw translateBundlerError(err, "eth_estimateUserOperationGas");
884
791
  }
885
792
  }
886
793
  /**
@@ -891,9 +798,12 @@ var abstractionkit = (function(exports, ethers) {
891
798
  */
892
799
  async sendUserOperation(useroperation, entrypointAddress) {
893
800
  try {
894
- return await sendJsonRpcRequest(this.rpcUrl, "eth_sendUserOperation", [useroperation, entrypointAddress]);
801
+ return await this.outbound.request({
802
+ method: "eth_sendUserOperation",
803
+ params: [useroperation, entrypointAddress]
804
+ });
895
805
  } catch (err) {
896
- throw new AbstractionKitError("BUNDLER_ERROR", "bundler eth_sendUserOperation rpc call failed", { cause: ensureError(err) });
806
+ throw translateBundlerError(err, "eth_sendUserOperation");
897
807
  }
898
808
  }
899
809
  /**
@@ -903,31 +813,31 @@ var abstractionkit = (function(exports, ethers) {
903
813
  */
904
814
  async getUserOperationReceipt(useroperationhash) {
905
815
  try {
906
- const res = await sendJsonRpcRequest(this.rpcUrl, "eth_getUserOperationReceipt", [useroperationhash]);
907
- if (res != null) {
908
- const userOperationReceipt = {
909
- ...res.receipt,
910
- blockNumber: BigInt(res.receipt.blockNumber),
911
- cumulativeGasUsed: BigInt(res.receipt.cumulativeGasUsed),
912
- gasUsed: BigInt(res.receipt.gasUsed),
913
- transactionIndex: BigInt(res.receipt.transactionIndex),
914
- effectiveGasPrice: res.receipt.effectiveGasPrice == null ? void 0 : BigInt(res.receipt.effectiveGasPrice),
915
- logs: JSON.stringify(res.receipt.logs)
916
- };
917
- return {
918
- ...res,
919
- nonce: BigInt(res.nonce),
920
- actualGasCost: BigInt(res.actualGasCost),
921
- actualGasUsed: BigInt(res.actualGasUsed),
922
- logs: JSON.stringify(res.logs),
923
- receipt: userOperationReceipt
924
- };
925
- } else return null;
926
- } catch (err) {
927
- throw new AbstractionKitError("BUNDLER_ERROR", "bundler eth_getUserOperationReceipt rpc call failed", {
928
- cause: ensureError(err),
929
- context: { useroperationhash }
816
+ const jsonRpcResult = await this.outbound.request({
817
+ method: "eth_getUserOperationReceipt",
818
+ params: [useroperationhash]
930
819
  });
820
+ if (jsonRpcResult == null) return null;
821
+ const res = jsonRpcResult;
822
+ const userOperationReceipt = {
823
+ ...res.receipt,
824
+ blockNumber: BigInt(res.receipt.blockNumber),
825
+ cumulativeGasUsed: BigInt(res.receipt.cumulativeGasUsed),
826
+ gasUsed: BigInt(res.receipt.gasUsed),
827
+ transactionIndex: BigInt(res.receipt.transactionIndex),
828
+ effectiveGasPrice: res.receipt.effectiveGasPrice == null ? void 0 : BigInt(res.receipt.effectiveGasPrice),
829
+ logs: JSON.stringify(res.receipt.logs)
830
+ };
831
+ return {
832
+ ...res,
833
+ nonce: BigInt(res.nonce),
834
+ actualGasCost: BigInt(res.actualGasCost),
835
+ actualGasUsed: BigInt(res.actualGasUsed),
836
+ logs: JSON.stringify(res.logs),
837
+ receipt: userOperationReceipt
838
+ };
839
+ } catch (err) {
840
+ throw translateBundlerError(err, "eth_getUserOperationReceipt", { useroperationhash });
931
841
  }
932
842
  }
933
843
  /**
@@ -937,43 +847,233 @@ var abstractionkit = (function(exports, ethers) {
937
847
  */
938
848
  async getUserOperationByHash(useroperationhash) {
939
849
  try {
940
- const res = await sendJsonRpcRequest(this.rpcUrl, "eth_getUserOperationByHash", [useroperationhash]);
941
- if (res != null) return {
942
- ...res,
943
- blockNumber: res.blockNumber == null ? null : BigInt(res.blockNumber)
850
+ const jsonRpcResult = await this.outbound.request({
851
+ method: "eth_getUserOperationByHash",
852
+ params: [useroperationhash]
853
+ });
854
+ if (jsonRpcResult == null) return null;
855
+ return {
856
+ ...jsonRpcResult,
857
+ blockNumber: jsonRpcResult.blockNumber == null ? null : BigInt(jsonRpcResult.blockNumber)
944
858
  };
945
- else return null;
946
859
  } catch (err) {
947
- throw new AbstractionKitError("BUNDLER_ERROR", "bundler eth_getUserOperationByHash rpc call failed", {
948
- cause: ensureError(err),
949
- context: { useroperationhash }
950
- });
860
+ throw translateBundlerError(err, "eth_getUserOperationByHash", { useroperationhash });
951
861
  }
952
862
  }
953
863
  };
954
- //#endregion
955
- //#region src/signer/negotiate.ts
956
864
  /**
957
- * Pick the best mutually-supported signing scheme for one signer against an
958
- * account's accepted schemes. Later in the `accepted` array = lower preference;
959
- * the account ranks by preference.
865
+ * Translate a transport-level error (or already-wrapped
866
+ * {@link AbstractionKitError}) into the `BUNDLER_ERROR` outer / specific
867
+ * 4337-code inner shape used by {@link Bundler}.
960
868
  *
961
- * Throws a detailed {@link AbstractionKitError} if no scheme overlaps,
962
- * citing the signer's address, what the account accepts, and what the
963
- * signer can do.
869
+ * - `AbstractionKitError` passes through unchanged (already domain-translated).
870
+ * - {@link ProviderRpcError} with a known 4337 code inner code from
871
+ * {@link BundlerErrorCodeDict}.
872
+ * - Anything else → inner `UNKNOWN_ERROR`.
873
+ *
874
+ * @internal
964
875
  */
965
- function pickScheme(signer, accepted, context) {
966
- const signerCan = [];
967
- if (typeof signer.signTypedData === "function") signerCan.push("typedData");
968
- if (typeof signer.signHash === "function") signerCan.push("hash");
969
- for (const scheme of accepted) if (signerCan.includes(scheme)) return scheme;
970
- throw new AbstractionKitError("BAD_DATA", buildMismatchMessage({
971
- accountName: context.accountName,
972
- signerIndex: context.signerIndex,
973
- signerAddress: signer.address,
974
- accepted,
975
- signerCan
976
- }));
876
+ function translateBundlerError(err, method, context) {
877
+ if (err instanceof AbstractionKitError) {
878
+ if (err.code === "BUNDLER_ERROR") return err;
879
+ return new AbstractionKitError("BUNDLER_ERROR", `bundler ${method} rpc call failed`, {
880
+ cause: err,
881
+ errno: err.errno,
882
+ context
883
+ });
884
+ }
885
+ const code = err?.code;
886
+ const codeString = code != null ? String(code) : "";
887
+ const innerCode = codeString in BundlerErrorCodeDict ? BundlerErrorCodeDict[codeString] : "UNKNOWN_ERROR";
888
+ const error = ensureError(err);
889
+ return new AbstractionKitError("BUNDLER_ERROR", `bundler ${method} rpc call failed`, {
890
+ cause: new AbstractionKitError(innerCode, error.message, { errno: code }),
891
+ errno: code,
892
+ context
893
+ });
894
+ }
895
+ //#endregion
896
+ //#region src/constants.ts
897
+ /** The Ethereum zero address (0x0000...0000), used as a placeholder for empty/null addresses */
898
+ const ZeroAddress = "0x0000000000000000000000000000000000000000";
899
+ /** EntryPoint v0.9 contract address */
900
+ const ENTRYPOINT_V9 = "0x433709009B8330FDa32311DF1C2AFA402eD8D009";
901
+ /** EntryPoint v0.8 contract address */
902
+ const ENTRYPOINT_V8 = "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108";
903
+ /** EntryPoint v0.7 contract address */
904
+ const ENTRYPOINT_V7 = "0x0000000071727De22E5E9d8BAf0edAc6f37da032";
905
+ /** EntryPoint v0.6 contract address */
906
+ const ENTRYPOINT_V6 = "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789";
907
+ /** Safe L2 singleton v1.5.0 address and init hash */
908
+ const Safe_L2_V1_5_0 = {
909
+ singletonAddress: "0xEdd160fEBBD92E350D4D398fb636302fccd67C7e",
910
+ singletonInitHash: "0x1b94aebb5a7df6dff11d93589204a6bbc99b4b8c9014bf1d386d006c2c17a881"
911
+ };
912
+ /** Safe L2 singleton v1.4.1 address and init hash */
913
+ const Safe_L2_V1_4_1 = {
914
+ singletonAddress: "0x29fcB43b46531BcA003ddC8FCB67FFE91900C762",
915
+ singletonInitHash: "0xe298282cefe913ab5d282047161268a8222e4bd4ed106300c547894bbefd31ee"
916
+ };
917
+ /** Default placeholder values for gas estimation before actual values are known */
918
+ const BaseUserOperationDummyValues = {
919
+ sender: ZeroAddress,
920
+ nonce: 0n,
921
+ callData: "0x",
922
+ callGasLimit: 0n,
923
+ verificationGasLimit: 0n,
924
+ preVerificationGas: 0n,
925
+ maxFeePerGas: 0n,
926
+ maxPriorityFeePerGas: 0n,
927
+ signature: "0x"
928
+ };
929
+ /** EIP-712 primary type string used when signing Safe UserOperations. */
930
+ const EIP712_SAFE_OPERATION_PRIMARY_TYPE = "SafeOp";
931
+ /** EIP-712 type definition for Safe UserOperation signing (EntryPoint v0.6) */
932
+ const EIP712_SAFE_OPERATION_V6_TYPE = { SafeOp: [
933
+ {
934
+ type: "address",
935
+ name: "safe"
936
+ },
937
+ {
938
+ type: "uint256",
939
+ name: "nonce"
940
+ },
941
+ {
942
+ type: "bytes",
943
+ name: "initCode"
944
+ },
945
+ {
946
+ type: "bytes",
947
+ name: "callData"
948
+ },
949
+ {
950
+ type: "uint256",
951
+ name: "callGasLimit"
952
+ },
953
+ {
954
+ type: "uint256",
955
+ name: "verificationGasLimit"
956
+ },
957
+ {
958
+ type: "uint256",
959
+ name: "preVerificationGas"
960
+ },
961
+ {
962
+ type: "uint256",
963
+ name: "maxFeePerGas"
964
+ },
965
+ {
966
+ type: "uint256",
967
+ name: "maxPriorityFeePerGas"
968
+ },
969
+ {
970
+ type: "bytes",
971
+ name: "paymasterAndData"
972
+ },
973
+ {
974
+ type: "uint48",
975
+ name: "validAfter"
976
+ },
977
+ {
978
+ type: "uint48",
979
+ name: "validUntil"
980
+ },
981
+ {
982
+ type: "address",
983
+ name: "entryPoint"
984
+ }
985
+ ] };
986
+ /** EIP-712 type definition for Safe UserOperation signing (EntryPoint v0.7) */
987
+ const EIP712_SAFE_OPERATION_V7_TYPE = { SafeOp: [
988
+ {
989
+ type: "address",
990
+ name: "safe"
991
+ },
992
+ {
993
+ type: "uint256",
994
+ name: "nonce"
995
+ },
996
+ {
997
+ type: "bytes",
998
+ name: "initCode"
999
+ },
1000
+ {
1001
+ type: "bytes",
1002
+ name: "callData"
1003
+ },
1004
+ {
1005
+ type: "uint128",
1006
+ name: "verificationGasLimit"
1007
+ },
1008
+ {
1009
+ type: "uint128",
1010
+ name: "callGasLimit"
1011
+ },
1012
+ {
1013
+ type: "uint256",
1014
+ name: "preVerificationGas"
1015
+ },
1016
+ {
1017
+ type: "uint128",
1018
+ name: "maxPriorityFeePerGas"
1019
+ },
1020
+ {
1021
+ type: "uint128",
1022
+ name: "maxFeePerGas"
1023
+ },
1024
+ {
1025
+ type: "bytes",
1026
+ name: "paymasterAndData"
1027
+ },
1028
+ {
1029
+ type: "uint48",
1030
+ name: "validAfter"
1031
+ },
1032
+ {
1033
+ type: "uint48",
1034
+ name: "validUntil"
1035
+ },
1036
+ {
1037
+ type: "address",
1038
+ name: "entryPoint"
1039
+ }
1040
+ ] };
1041
+ /** EIP-712 primary type string used when signing multi-chain Safe operations. */
1042
+ const EIP712_MULTI_CHAIN_OPERATIONS_PRIMARY_TYPE = "MerkleTreeRoot";
1043
+ /** EIP-712 type definition for multi-chain Safe operations using Merkle tree roots */
1044
+ const EIP712_MULTI_CHAIN_OPERATIONS_TYPE = { MerkleTreeRoot: [{
1045
+ type: "bytes32",
1046
+ name: "merkleTreeRoot"
1047
+ }] };
1048
+ /** Default address for the secp256r1 (P-256) precompile used by WebAuthn verification */
1049
+ const DEFAULT_SECP256R1_PRECOMPILE_ADDRESS = "0x0000000000000000000000000000000000000100";
1050
+ /** Uniswap Calibur singleton v1.0.0 (EntryPoint v0.8) */
1051
+ const CALIBUR_UNISWAP_V1_0_0_SINGLETON_ADDRESS = "0x000000009B1D0aF20D8C6d0A44e162d11F9b8f00";
1052
+ /** Candide Calibur singleton v0.1.0 (EntryPoint v0.9, unaudited) */
1053
+ const CALIBUR_CANDIDE_V0_1_0_SINGLETON_ADDRESS = "0x71032285A847c4311Eb7ec2E7A636aB94A9805Aa";
1054
+ //#endregion
1055
+ //#region src/signer/negotiate.ts
1056
+ /**
1057
+ * Pick the best mutually-supported signing scheme for one signer against an
1058
+ * account's accepted schemes. Later in the `accepted` array = lower preference;
1059
+ * the account ranks by preference.
1060
+ *
1061
+ * Throws a detailed {@link AbstractionKitError} if no scheme overlaps,
1062
+ * citing the signer's address, what the account accepts, and what the
1063
+ * signer can do.
1064
+ */
1065
+ function pickScheme(signer, accepted, context) {
1066
+ const signerCan = [];
1067
+ if (typeof signer.signTypedData === "function") signerCan.push("typedData");
1068
+ if (typeof signer.signHash === "function") signerCan.push("hash");
1069
+ for (const scheme of accepted) if (signerCan.includes(scheme)) return scheme;
1070
+ throw new AbstractionKitError("BAD_DATA", buildMismatchMessage({
1071
+ accountName: context.accountName,
1072
+ signerIndex: context.signerIndex,
1073
+ signerAddress: signer.address,
1074
+ accepted,
1075
+ signerCan
1076
+ }));
977
1077
  }
978
1078
  function buildMismatchMessage(params) {
979
1079
  const { accountName, signerIndex, signerAddress, accepted, signerCan } = params;
@@ -983,7 +1083,7 @@ var abstractionkit = (function(exports, ethers) {
983
1083
  /**
984
1084
  * Invoke a signer for one scheme. Keeps the dispatch in one place so the
985
1085
  * account-side code stays linear. `typedData` is optional: accounts that
986
- * only accept the `"hash"` scheme (Simple7702, Calibur) pass just `hash`.
1086
+ * only accept the `"hash"` scheme can pass just `hash`.
987
1087
  *
988
1088
  * `context` is always forwarded to the signer so power-user implementations
989
1089
  * can inspect the userOp.
@@ -1206,45 +1306,540 @@ var abstractionkit = (function(exports, ethers) {
1206
1306
  }
1207
1307
  return encoded_access_list;
1208
1308
  }
1209
- /** Converts a bigint to a Uint8Array of its big-endian byte representation. */
1210
- function bigintToBytes(bi) {
1211
- return (0, ethers.getBytes)((0, ethers.toBeArray)(bi));
1309
+ /** Converts a bigint to a Uint8Array of its big-endian byte representation. */
1310
+ function bigintToBytes(bi) {
1311
+ return (0, ethers.getBytes)((0, ethers.toBeArray)(bi));
1312
+ }
1313
+ /**
1314
+ * Parse a raw ECDSA signature into its components.
1315
+ * Supports standard 65-byte (r + s + v) and EIP-2098 64-byte compact formats.
1316
+ * @param rawSig - Hex string: 128 chars (EIP-2098 compact), or 130/132 chars (standard with 0x prefix)
1317
+ * @returns An object with yParity (0 or 1), r, and s components
1318
+ */
1319
+ function parseRawSignature(rawSig) {
1320
+ const sig = rawSig.startsWith("0x") ? rawSig.slice(2) : rawSig;
1321
+ 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}`);
1322
+ const r = BigInt(`0x${sig.slice(0, 64)}`);
1323
+ if (sig.length === 128) {
1324
+ const yParityAndS = BigInt(`0x${sig.slice(64, 128)}`);
1325
+ return {
1326
+ yParity: Number(yParityAndS >> 255n & 1n),
1327
+ r,
1328
+ s: yParityAndS & (1n << 255n) - 1n
1329
+ };
1330
+ }
1331
+ const s = BigInt(`0x${sig.slice(64, 128)}`);
1332
+ const v = parseInt(sig.slice(128, 130), 16);
1333
+ if (v !== 0 && v !== 1 && v !== 27 && v !== 28) throw new RangeError(`invalid signature v value: ${v}`);
1334
+ return {
1335
+ yParity: v >= 27 ? v - 27 : v,
1336
+ r,
1337
+ s
1338
+ };
1339
+ }
1340
+ /**
1341
+ * Converts a bigint to a 0x-prefixed hex string with even-length padding.
1342
+ * @param value - The bigint value to convert.
1343
+ * @returns The hex string representation (e.g., "0x01", "0xff").
1344
+ */
1345
+ function bigintToHex(value) {
1346
+ const hex = value.toString(16);
1347
+ return hex.length % 2 ? `0x0${hex}` : `0x${hex}`;
1348
+ }
1349
+ //#endregion
1350
+ //#region src/utils.ts
1351
+ function buildDomainSeparator(chainId, entrypoint) {
1352
+ return (0, ethers.keccak256)(ethers.AbiCoder.defaultAbiCoder().encode(["(bytes32,bytes32,bytes32,uint256,address)"], [[
1353
+ "0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f",
1354
+ "0x364da28a5c92bcc87fe97c8813a6c6b8a3a049b0ea0a328fcb0b4f0e00337586",
1355
+ "0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6",
1356
+ chainId,
1357
+ entrypoint
1358
+ ]]));
1359
+ }
1360
+ /**
1361
+ * Compute the UserOperation hash for any supported EntryPoint version.
1362
+ * This hash is what gets signed by the account owner(s).
1363
+ * Automatically selects the correct packing format based on the entrypoint address.
1364
+ *
1365
+ * @param useroperation - UserOperation to hash
1366
+ * @param entrypointAddress - EntryPoint contract address (determines hash format)
1367
+ * @param chainId - Target chain ID
1368
+ * @returns The UserOperation hash as a hex string
1369
+ */
1370
+ function createUserOperationHash(useroperation, entrypointAddress, chainId) {
1371
+ let packedUserOperationHash;
1372
+ const abiCoder = ethers.AbiCoder.defaultAbiCoder();
1373
+ let userOperationHash;
1374
+ if (entrypointAddress.toLowerCase() === "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789".toLowerCase()) {
1375
+ packedUserOperationHash = (0, ethers.keccak256)(createPackedUserOperationV6(useroperation));
1376
+ userOperationHash = (0, ethers.keccak256)(abiCoder.encode([
1377
+ "bytes32",
1378
+ "address",
1379
+ "uint256"
1380
+ ], [
1381
+ packedUserOperationHash,
1382
+ entrypointAddress,
1383
+ chainId
1384
+ ]));
1385
+ } else if (entrypointAddress.toLowerCase() === "0x0000000071727De22E5E9d8BAf0edAc6f37da032".toLowerCase()) {
1386
+ packedUserOperationHash = (0, ethers.keccak256)(createPackedUserOperationV7(useroperation));
1387
+ userOperationHash = (0, ethers.keccak256)(abiCoder.encode([
1388
+ "bytes32",
1389
+ "address",
1390
+ "uint256"
1391
+ ], [
1392
+ packedUserOperationHash,
1393
+ entrypointAddress,
1394
+ chainId
1395
+ ]));
1396
+ } else if (entrypointAddress.toLowerCase() === "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108".toLowerCase()) {
1397
+ packedUserOperationHash = (0, ethers.keccak256)(createPackedUserOperationV8(useroperation));
1398
+ userOperationHash = (0, ethers.keccak256)(`0x1901${buildDomainSeparator(chainId, entrypointAddress).slice(2)}${packedUserOperationHash.slice(2)}`);
1399
+ } else if (entrypointAddress.toLowerCase() === "0x433709009B8330FDa32311DF1C2AFA402eD8D009".toLowerCase()) {
1400
+ packedUserOperationHash = (0, ethers.keccak256)(createPackedUserOperationV9(useroperation));
1401
+ userOperationHash = (0, ethers.keccak256)(`0x1901${buildDomainSeparator(chainId, entrypointAddress).slice(2)}${packedUserOperationHash.slice(2)}`);
1402
+ } else throw new RangeError(`unsupported entrypoint address: ${entrypointAddress}`);
1403
+ return userOperationHash;
1404
+ }
1405
+ /**
1406
+ * @internal
1407
+ * Reconstruct the packed `initCode` field for an EntryPoint v0.8/v0.9
1408
+ * UserOperation. When `eip7702Auth.address` is set, the EIP-7702 delegatee
1409
+ * address replaces the factory address in initCode (only `factoryData` is
1410
+ * concatenated). Otherwise, behaves like v0.7 (`factory + factoryData`).
1411
+ *
1412
+ * Shared by {@link createPackedUserOperationV8} /
1413
+ * {@link createPackedUserOperationV9} and Simple7702Account's typed-data
1414
+ * builder. Not part of the public API; only `export`ed for cross-module
1415
+ * use within the package and not re-exported from `src/abstractionkit.ts`.
1416
+ *
1417
+ * @param useroperation - V8 or V9 UserOperation to read fields from
1418
+ * @returns Hex-encoded initCode
1419
+ */
1420
+ function buildPackedInitCodeV8V9(useroperation) {
1421
+ if (useroperation.factory == null) return "0x";
1422
+ const eip7702Auth = useroperation.eip7702Auth;
1423
+ return (eip7702Auth != null && eip7702Auth.address != null ? eip7702Auth.address : useroperation.factory) + (useroperation.factoryData != null ? useroperation.factoryData.slice(2) : "");
1424
+ }
1425
+ /**
1426
+ * @internal
1427
+ * Pack `(verificationGasLimit, callGasLimit)` into a single `bytes32`
1428
+ * (two uint128 values, big-endian). Matches the on-chain
1429
+ * `PackedUserOperation.accountGasLimits` layout used by EntryPoint v0.7+.
1430
+ */
1431
+ function packAccountGasLimits(verificationGasLimit, callGasLimit) {
1432
+ const abiCoder = ethers.AbiCoder.defaultAbiCoder();
1433
+ return "0x" + abiCoder.encode(["uint128"], [verificationGasLimit]).slice(34) + abiCoder.encode(["uint128"], [callGasLimit]).slice(34);
1434
+ }
1435
+ /**
1436
+ * @internal
1437
+ * Pack `(maxPriorityFeePerGas, maxFeePerGas)` into a single `bytes32`
1438
+ * (two uint128 values, big-endian). Matches the on-chain
1439
+ * `PackedUserOperation.gasFees` layout used by EntryPoint v0.7+.
1440
+ */
1441
+ function packGasFees(maxPriorityFeePerGas, maxFeePerGas) {
1442
+ const abiCoder = ethers.AbiCoder.defaultAbiCoder();
1443
+ return "0x" + abiCoder.encode(["uint128"], [maxPriorityFeePerGas]).slice(34) + abiCoder.encode(["uint128"], [maxFeePerGas]).slice(34);
1444
+ }
1445
+ /**
1446
+ * @internal
1447
+ * Reconstruct the packed `paymasterAndData` field from a UserOperation's
1448
+ * separate paymaster fields. Returns `0x` when no paymaster is set.
1449
+ *
1450
+ * For EntryPoint v0.9, when `paymasterData` ends with the parallel-paymaster
1451
+ * signature magic suffix (`0x22e325a297439656`), the embedded signature is
1452
+ * stripped so the userOpHash does not commit to the paymaster's signature
1453
+ * over itself. Pass `stripV9PaymasterSig=false` to preserve the wire format.
1454
+ *
1455
+ * Shared by v7/v8/v9 packers and Simple7702Account's typed-data builder.
1456
+ * Not part of the public API; only `export`ed for cross-module use within
1457
+ * the package and not re-exported from `src/abstractionkit.ts`.
1458
+ *
1459
+ * @param useroperation - V7/V8/V9 UserOperation to read fields from
1460
+ * @param stripV9PaymasterSig - Whether to strip the v0.9 paymaster signature suffix
1461
+ * @returns Hex-encoded paymasterAndData
1462
+ */
1463
+ function buildPaymasterAndData(useroperation, stripV9PaymasterSig = false) {
1464
+ if (useroperation.paymaster == null) return "0x";
1465
+ const abiCoder = ethers.AbiCoder.defaultAbiCoder();
1466
+ let paymasterAndData = useroperation.paymaster;
1467
+ if (useroperation.paymasterVerificationGasLimit != null) paymasterAndData += abiCoder.encode(["uint128"], [useroperation.paymasterVerificationGasLimit]).slice(34);
1468
+ if (useroperation.paymasterPostOpGasLimit != null) paymasterAndData += abiCoder.encode(["uint128"], [useroperation.paymasterPostOpGasLimit]).slice(34);
1469
+ if (useroperation.paymasterData != null) {
1470
+ const PAYMASTER_SIG_MAGIC = "22e325a297439656";
1471
+ if (stripV9PaymasterSig && useroperation.paymasterData.toLowerCase().endsWith(PAYMASTER_SIG_MAGIC)) {
1472
+ const sigLenHex = useroperation.paymasterData.slice(useroperation.paymasterData.length - 16 - 4, useroperation.paymasterData.length - 16);
1473
+ const sigLen = parseInt(sigLenHex, 16);
1474
+ const prefixEnd = useroperation.paymasterData.length - 16 - 4 - sigLen * 2;
1475
+ paymasterAndData += useroperation.paymasterData.slice(0, prefixEnd).replaceAll("0x", "") + PAYMASTER_SIG_MAGIC;
1476
+ } else paymasterAndData += useroperation.paymasterData.slice(2);
1477
+ }
1478
+ return paymasterAndData;
1479
+ }
1480
+ /**
1481
+ * Build the EIP-712 typed data payload for a UserOperation under the
1482
+ * EntryPoint v0.8 / v0.9 domain. The digest of the returned payload equals
1483
+ * the `userOpHash` from {@link createUserOperationHash}, so signing it via
1484
+ * `signTypedData` produces a signature that validates against the same hash
1485
+ * as raw ECDSA over the `userOpHash`.
1486
+ *
1487
+ * Shared by EIP-7702 accounts (Simple7702, Calibur). Earlier EntryPoints
1488
+ * define the userOpHash differently and don't fit this typed-data shape.
1489
+ *
1490
+ * @param userOperation - Unsigned UserOperation to wrap
1491
+ * @param entrypointAddress - The EntryPoint contract address (must be v0.8 or v0.9)
1492
+ * @param chainId - Target chain ID (must match the chain that will validate the signature)
1493
+ * @returns EIP-712 {@link TypedData} payload ready for `signTypedData`
1494
+ * @throws {AbstractionKitError} if `entrypointAddress` is not v0.8 / v0.9
1495
+ */
1496
+ function getUserOperationEip712DataV8V9(userOperation, entrypointAddress, chainId) {
1497
+ const ep = entrypointAddress.toLowerCase();
1498
+ const isV9 = ep === ENTRYPOINT_V9.toLowerCase();
1499
+ 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.`);
1500
+ const initCode = buildPackedInitCodeV8V9(userOperation);
1501
+ const accountGasLimits = packAccountGasLimits(userOperation.verificationGasLimit, userOperation.callGasLimit);
1502
+ const gasFees = packGasFees(userOperation.maxPriorityFeePerGas, userOperation.maxFeePerGas);
1503
+ const paymasterAndData = buildPaymasterAndData(userOperation, isV9);
1504
+ return {
1505
+ domain: {
1506
+ name: "ERC4337",
1507
+ version: "1",
1508
+ chainId,
1509
+ verifyingContract: entrypointAddress
1510
+ },
1511
+ types: { PackedUserOperation: [
1512
+ {
1513
+ name: "sender",
1514
+ type: "address"
1515
+ },
1516
+ {
1517
+ name: "nonce",
1518
+ type: "uint256"
1519
+ },
1520
+ {
1521
+ name: "initCode",
1522
+ type: "bytes"
1523
+ },
1524
+ {
1525
+ name: "callData",
1526
+ type: "bytes"
1527
+ },
1528
+ {
1529
+ name: "accountGasLimits",
1530
+ type: "bytes32"
1531
+ },
1532
+ {
1533
+ name: "preVerificationGas",
1534
+ type: "uint256"
1535
+ },
1536
+ {
1537
+ name: "gasFees",
1538
+ type: "bytes32"
1539
+ },
1540
+ {
1541
+ name: "paymasterAndData",
1542
+ type: "bytes"
1543
+ }
1544
+ ] },
1545
+ primaryType: "PackedUserOperation",
1546
+ message: {
1547
+ sender: userOperation.sender,
1548
+ nonce: userOperation.nonce,
1549
+ initCode,
1550
+ callData: userOperation.callData,
1551
+ accountGasLimits,
1552
+ preVerificationGas: userOperation.preVerificationGas,
1553
+ gasFees,
1554
+ paymasterAndData
1555
+ }
1556
+ };
1557
+ }
1558
+ /**
1559
+ * Compute the EIP-712 digest of a UserOperation under the EntryPoint
1560
+ * v0.8 / v0.9 domain. For these EntryPoints this digest IS the
1561
+ * `userOpHash` ({@link createUserOperationHash}).
1562
+ *
1563
+ * @param userOperation - Unsigned UserOperation to hash
1564
+ * @param entrypointAddress - The EntryPoint contract address (must be v0.8 or v0.9)
1565
+ * @param chainId - Target chain ID
1566
+ * @returns The EIP-712 digest as a hex string
1567
+ * @throws {AbstractionKitError} if `entrypointAddress` is not v0.8 / v0.9
1568
+ */
1569
+ function getUserOperationEip712HashV8V9(userOperation, entrypointAddress, chainId) {
1570
+ const data = getUserOperationEip712DataV8V9(userOperation, entrypointAddress, chainId);
1571
+ return ethers.TypedDataEncoder.hash(data.domain, data.types, data.message);
1572
+ }
1573
+ /**
1574
+ * ABI-encode and pack a UserOperation for hashing (EntryPoint v0.6 format).
1575
+ * Bytes fields (initCode, callData, paymasterAndData) are keccak256-hashed before packing.
1576
+ *
1577
+ * @param useroperation - UserOperation to pack
1578
+ * @returns ABI-encoded packed UserOperation as a hex string
1579
+ */
1580
+ function createPackedUserOperationV6(useroperation) {
1581
+ const useroperationValuesArrayWithHashedByteValues = [
1582
+ useroperation.sender,
1583
+ useroperation.nonce,
1584
+ (0, ethers.keccak256)(useroperation.initCode),
1585
+ (0, ethers.keccak256)(useroperation.callData),
1586
+ useroperation.callGasLimit,
1587
+ useroperation.verificationGasLimit,
1588
+ useroperation.preVerificationGas,
1589
+ useroperation.maxFeePerGas,
1590
+ useroperation.maxPriorityFeePerGas,
1591
+ (0, ethers.keccak256)(useroperation.paymasterAndData)
1592
+ ];
1593
+ return ethers.AbiCoder.defaultAbiCoder().encode([
1594
+ "address",
1595
+ "uint256",
1596
+ "bytes32",
1597
+ "bytes32",
1598
+ "uint256",
1599
+ "uint256",
1600
+ "uint256",
1601
+ "uint256",
1602
+ "uint256",
1603
+ "bytes32"
1604
+ ], useroperationValuesArrayWithHashedByteValues);
1605
+ }
1606
+ /**
1607
+ * ABI-encode and pack a UserOperation for hashing (EntryPoint v0.7 format).
1608
+ * Reconstructs initCode, accountGasLimits, gasFees, and paymasterAndData from separate fields.
1609
+ *
1610
+ * @param useroperation - UserOperation to pack
1611
+ * @returns ABI-encoded packed UserOperation as a hex string
1612
+ */
1613
+ function createPackedUserOperationV7(useroperation) {
1614
+ const abiCoder = ethers.AbiCoder.defaultAbiCoder();
1615
+ let initCode = "0x";
1616
+ if (useroperation.factory != null) {
1617
+ initCode = useroperation.factory;
1618
+ if (useroperation.factoryData != null) initCode += useroperation.factoryData.slice(2);
1619
+ }
1620
+ const accountGasLimits = packAccountGasLimits(useroperation.verificationGasLimit, useroperation.callGasLimit);
1621
+ const gasFees = packGasFees(useroperation.maxPriorityFeePerGas, useroperation.maxFeePerGas);
1622
+ const paymasterAndData = buildPaymasterAndData(useroperation);
1623
+ const useroperationValuesArrayWithHashedByteValues = [
1624
+ useroperation.sender,
1625
+ useroperation.nonce,
1626
+ (0, ethers.keccak256)(initCode),
1627
+ (0, ethers.keccak256)(useroperation.callData),
1628
+ accountGasLimits,
1629
+ useroperation.preVerificationGas,
1630
+ gasFees,
1631
+ (0, ethers.keccak256)(paymasterAndData)
1632
+ ];
1633
+ return abiCoder.encode([
1634
+ "address",
1635
+ "uint256",
1636
+ "bytes32",
1637
+ "bytes32",
1638
+ "bytes32",
1639
+ "uint256",
1640
+ "bytes32",
1641
+ "bytes32"
1642
+ ], useroperationValuesArrayWithHashedByteValues);
1643
+ }
1644
+ /**
1645
+ * ABI-encode and pack a UserOperation for hashing (EntryPoint v0.9 format).
1646
+ *
1647
+ * @param useroperation - UserOperation to pack
1648
+ * @returns ABI-encoded packed UserOperation as a hex string
1649
+ */
1650
+ function createPackedUserOperationV9(useroperation) {
1651
+ return baseCreatePackedUserOperationV8V9(useroperation, true);
1652
+ }
1653
+ /**
1654
+ * ABI-encode and pack a UserOperation for hashing (EntryPoint v0.8 format).
1655
+ *
1656
+ * @param useroperation - UserOperation to pack
1657
+ * @returns ABI-encoded packed UserOperation as a hex string
1658
+ */
1659
+ function createPackedUserOperationV8(useroperation) {
1660
+ return baseCreatePackedUserOperationV8V9(useroperation, false);
1661
+ }
1662
+ /**
1663
+ * Shared packer for EntryPoint v0.8 and v0.9 UserOperations.
1664
+ * @param useroperation - UserOperation to pack
1665
+ * @param is_v9 - If true, strips the paymaster signature suffix (v0.9-specific)
1666
+ * @returns ABI-encoded packed UserOperation as a hex string
1667
+ */
1668
+ function baseCreatePackedUserOperationV8V9(useroperation, is_v9) {
1669
+ const abiCoder = ethers.AbiCoder.defaultAbiCoder();
1670
+ const initCode = buildPackedInitCodeV8V9(useroperation);
1671
+ const accountGasLimits = packAccountGasLimits(useroperation.verificationGasLimit, useroperation.callGasLimit);
1672
+ const gasFees = packGasFees(useroperation.maxPriorityFeePerGas, useroperation.maxFeePerGas);
1673
+ const paymasterAndData = buildPaymasterAndData(useroperation, is_v9);
1674
+ const useroperationValuesArrayWithHashedByteValues = [
1675
+ "0x29a0bca4af4be3421398da00295e58e6d7de38cb492214754cb6a47507dd6f8e",
1676
+ useroperation.sender,
1677
+ useroperation.nonce,
1678
+ (0, ethers.keccak256)(initCode),
1679
+ (0, ethers.keccak256)(useroperation.callData),
1680
+ accountGasLimits,
1681
+ useroperation.preVerificationGas,
1682
+ gasFees,
1683
+ (0, ethers.keccak256)(paymasterAndData)
1684
+ ];
1685
+ return abiCoder.encode([
1686
+ "bytes32",
1687
+ "address",
1688
+ "uint256",
1689
+ "bytes32",
1690
+ "bytes32",
1691
+ "bytes32",
1692
+ "uint256",
1693
+ "bytes32",
1694
+ "bytes32"
1695
+ ], useroperationValuesArrayWithHashedByteValues);
1696
+ }
1697
+ /**
1698
+ * Encode a function call into ABI-encoded calldata.
1699
+ *
1700
+ * @param functionSelector - 4-byte hex function selector (e.g., "0xa9059cbb" for ERC-20 transfer)
1701
+ * @param functionInputAbi - Array of ABI type strings (e.g., ["address", "uint256"])
1702
+ * @param functionInputParameters - Array of parameter values matching the ABI types
1703
+ * @returns ABI-encoded calldata as a hex string (selector + encoded parameters)
1704
+ *
1705
+ * @example
1706
+ * const transferCallData = createCallData(
1707
+ * "0xa9059cbb",
1708
+ * ["address", "uint256"],
1709
+ * ["0xRecipientAddress", 1000000n],
1710
+ * );
1711
+ */
1712
+ function createCallData(functionSelector, functionInputAbi, functionInputParameters) {
1713
+ return functionSelector + ethers.AbiCoder.defaultAbiCoder().encode(functionInputAbi, functionInputParameters).slice(2);
1714
+ }
1715
+ /**
1716
+ * Send a JSON-RPC 2.0 request to the specified endpoint.
1717
+ *
1718
+ * **External escape hatch.** Most SDK consumers should use one of the
1719
+ * high-level service classes ({@link Bundler}, {@link CandidePaymaster},
1720
+ * {@link Erc7677Paymaster}, {@link JsonRpcNode}) which translate errors into
1721
+ * the SDK's domain vocabulary. This function is intentionally low-level: it
1722
+ * speaks plain JSON-RPC and throws {@link TransportRpcError}
1723
+ * (an EIP-1193-shaped `ProviderRpcError`) on RPC errors.
1724
+ *
1725
+ * Two execution modes:
1726
+ * - **URL string:** issues a `fetch` POST directly. Supports custom `headers`
1727
+ * and a custom `paramsKeyName` for non-standard servers (e.g. APIs that
1728
+ * expect `"simulations"` instead of `"params"`).
1729
+ * - **Transport / JsonRpcNode:** delegates to `.request({ method, params })`.
1730
+ * `headers` and `paramsKeyName` are ignored (those concerns belong to the
1731
+ * transport implementation).
1732
+ *
1733
+ * Automatically serializes `bigint` values in `params` as `0x`-prefixed hex.
1734
+ *
1735
+ * @param rpc - JSON-RPC endpoint URL, {@link Transport}, or {@link JsonRpcNode}
1736
+ * @param method - The JSON-RPC method name (e.g., `"eth_call"`)
1737
+ * @param params - The JSON-RPC parameters
1738
+ * @param headers - Custom HTTP headers (URL inputs only; default Content-Type: application/json)
1739
+ * @param paramsKeyName - Override the params key in the envelope (URL inputs only; default `"params"`)
1740
+ * @returns The `result` (or non-standard equivalent) field from the JSON-RPC response
1741
+ * @throws {@link TransportRpcError} when the response contains an `error` field
1742
+ */
1743
+ async function sendJsonRpcRequest(rpc, method, params, headers = { "Content-Type": "application/json" }, paramsKeyName = "params") {
1744
+ const normalizedParams = JSON.parse(JSON.stringify(params, (_key, value) => typeof value === "bigint" ? `0x${value.toString(16)}` : value));
1745
+ if (typeof rpc !== "string") return JsonRpcNode.from(rpc).request({
1746
+ method,
1747
+ params: normalizedParams
1748
+ });
1749
+ const raw = JSON.stringify({
1750
+ method,
1751
+ [paramsKeyName]: normalizedParams,
1752
+ id: Date.now(),
1753
+ jsonrpc: "2.0"
1754
+ });
1755
+ const response = await (await fetch(rpc, {
1756
+ method: "POST",
1757
+ headers,
1758
+ body: raw,
1759
+ redirect: "follow"
1760
+ })).json();
1761
+ if ("result" in response) return response.result;
1762
+ const err = response.error;
1763
+ throw new TransportRpcError(err.code, err.message);
1764
+ }
1765
+ /**
1766
+ * Get a 4-byte function selector from a Solidity-style function signature.
1767
+ * Computed as the first 4 bytes of keccak256(signature).
1768
+ * @param functionSignature - Solidity-style function signature, e.g. "mint(address)"
1769
+ * @returns Function selector as a 0x-prefixed 10-character hex string
1770
+ *
1771
+ * @example
1772
+ * getFunctionSelector("getNonce(address,uint192)"); // "0x35567e1a"
1773
+ */
1774
+ function getFunctionSelector(functionSignature) {
1775
+ return (0, ethers.id)(functionSignature).slice(0, 10);
1776
+ }
1777
+ /**
1778
+ * Fetch the account's nonce from the EntryPoint contract.
1779
+ *
1780
+ * Equivalent to `JsonRpcNode.from(rpc).getEntryPointNonce(entryPoint, account, key)`.
1781
+ * Kept as a top-level export for backward compatibility with existing call
1782
+ * sites; the first parameter widens from `string` to
1783
+ * `string | Transport | JsonRpcNode` so users can pass a configured transport
1784
+ * (with custom headers, retries, etc.) without restructuring their code.
1785
+ *
1786
+ * @param rpc - Ethereum JSON-RPC node URL, {@link Transport}, or {@link JsonRpcNode}
1787
+ * @param entryPoint - EntryPoint contract address
1788
+ * @param account - Smart account address to query
1789
+ * @param key - Nonce key (default 0). Different keys allow parallel nonce channels.
1790
+ * @returns The current nonce as a bigint
1791
+ */
1792
+ async function fetchAccountNonce(rpc, entryPoint, account, key = 0) {
1793
+ return JsonRpcNode.from(rpc).getEntryPointNonce(entryPoint, account, BigInt(key));
1212
1794
  }
1213
1795
  /**
1214
- * Parse a raw ECDSA signature into its components.
1215
- * Supports standard 65-byte (r + s + v) and EIP-2098 64-byte compact formats.
1216
- * @param rawSig - Hex string: 128 chars (EIP-2098 compact), or 130/132 chars (standard with 0x prefix)
1217
- * @returns An object with yParity (0 or 1), r, and s components
1796
+ * Fetch current gas prices from the Polygon Gas Station API.
1797
+ *
1798
+ * @param polygonChain - Target Polygon chain (Mainnet, Amoy, etc.)
1799
+ * @param gasLevel - Gas price level (Slow, Medium, Fast)
1800
+ * @returns A tuple of [maxFeePerGas, maxPriorityFeePerGas] as bigints
1218
1801
  */
1219
- function parseRawSignature(rawSig) {
1220
- const sig = rawSig.startsWith("0x") ? rawSig.slice(2) : rawSig;
1221
- 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}`);
1222
- const r = BigInt(`0x${sig.slice(0, 64)}`);
1223
- if (sig.length === 128) {
1224
- const yParityAndS = BigInt(`0x${sig.slice(64, 128)}`);
1225
- return {
1226
- yParity: Number(yParityAndS >> 255n & 1n),
1227
- r,
1228
- s: yParityAndS & (1n << 255n) - 1n
1229
- };
1802
+ async function fetchGasPricePolygon(polygonChain, gasLevel = GasOption.Medium) {
1803
+ const gasStationUrl = `https://gasstation.polygon.technology/${polygonChain}`;
1804
+ try {
1805
+ const response = await (await fetch(gasStationUrl)).json();
1806
+ let gasPrice;
1807
+ if (gasLevel === GasOption.Slow) gasPrice = response.safeLow;
1808
+ else if (gasLevel === GasOption.Medium) gasPrice = response.standard;
1809
+ else gasPrice = response.fast;
1810
+ let maxFeePerGas = BigInt(Math.ceil(Number(gasPrice.maxFee) * 1e9));
1811
+ let maxPriorityFeePerGas = BigInt(Math.ceil(Number(gasPrice.maxPriorityFee) * 1e9));
1812
+ if (maxFeePerGas === 0n) maxFeePerGas = 1n;
1813
+ if (maxPriorityFeePerGas === 0n) maxPriorityFeePerGas = 1n;
1814
+ return [maxFeePerGas, maxPriorityFeePerGas];
1815
+ } catch (err) {
1816
+ const error = ensureError(err);
1817
+ throw new AbstractionKitError("BAD_DATA", `fetching gas prices from ${gasStationUrl} failed.`, { cause: error });
1230
1818
  }
1231
- const s = BigInt(`0x${sig.slice(64, 128)}`);
1232
- const v = parseInt(sig.slice(128, 130), 16);
1233
- if (v !== 0 && v !== 1 && v !== 27 && v !== 28) throw new RangeError(`invalid signature v value: ${v}`);
1234
- return {
1235
- yParity: v >= 27 ? v - 27 : v,
1236
- r,
1237
- s
1238
- };
1239
1819
  }
1240
1820
  /**
1241
- * Converts a bigint to a 0x-prefixed hex string with even-length padding.
1242
- * @param value - The bigint value to convert.
1243
- * @returns The hex string representation (e.g., "0x01", "0xff").
1821
+ * Calculate the maximum gas cost (in wei) that a UserOperation could consume.
1822
+ * Uses different formulas for v0.6 (with paymaster multiplier) and v0.7+ UserOperations.
1823
+ *
1824
+ * @param useroperation - The UserOperation to calculate the max gas cost for
1825
+ * @returns Maximum possible gas cost in wei as a bigint
1244
1826
  */
1245
- function bigintToHex(value) {
1246
- const hex = value.toString(16);
1247
- return hex.length % 2 ? `0x0${hex}` : `0x${hex}`;
1827
+ function calculateUserOperationMaxGasCost(useroperation) {
1828
+ if ("initCode" in useroperation) {
1829
+ const mul = useroperation.paymasterAndData !== "0x" && useroperation.paymasterAndData != null ? 3n : 0n;
1830
+ return (useroperation.callGasLimit + useroperation.verificationGasLimit * mul + useroperation.preVerificationGas) * useroperation.maxFeePerGas;
1831
+ } else return (useroperation.verificationGasLimit + useroperation.callGasLimit + (useroperation.paymasterVerificationGasLimit ?? 0n) + (useroperation.paymasterPostOpGasLimit ?? 0n) + useroperation.preVerificationGas) * useroperation.maxFeePerGas;
1832
+ }
1833
+ /**
1834
+ * @internal
1835
+ * Internal dispatcher: routes to the Polygon Gas Station when a chain is
1836
+ * provided, otherwise delegates to {@link JsonRpcNode.getFeeData}. Used by
1837
+ * the account classes when assembling base UserOperation gas fields.
1838
+ */
1839
+ async function handlefetchGasPrice(providerRpc, polygonGasStation, gasLevel = GasOption.Medium) {
1840
+ if (polygonGasStation != null) return fetchGasPricePolygon(polygonGasStation, gasLevel);
1841
+ if (providerRpc != null) return JsonRpcNode.from(providerRpc).getFeeData(gasLevel);
1842
+ throw new AbstractionKitError("BAD_DATA", "providerRpc can't be null if maxFeePerGas and maxPriorityFeePerGas are not overridden");
1248
1843
  }
1249
1844
  //#endregion
1250
1845
  //#region src/account/SendUseroperationResponse.ts
@@ -1452,6 +2047,29 @@ var abstractionkit = (function(exports, ethers) {
1452
2047
  hookData
1453
2048
  ]);
1454
2049
  }
2050
+ /**
2051
+ * Format a raw EIP-712 signature (from `signTypedData` over the payload
2052
+ * returned by {@link getUserOperationEip712Data}) into Calibur's
2053
+ * `userOp.signature` layout. Provided for API parity with Safe's
2054
+ * `formatEip712SingleSignatureToUseroperationSignature`.
2055
+ *
2056
+ * Under EntryPoint v0.8 / v0.9 the userOpHash IS the EIP-712 digest of
2057
+ * the PackedUserOperation, so a raw-hash signature and a typed-data
2058
+ * signature are byte-identical for deterministic-ECDSA signers; this
2059
+ * method is therefore equivalent to {@link wrapSignature} with the same
2060
+ * `keyHash` / `hookData`. The default `keyHash` is the root-key hash
2061
+ * (`bytes32(0)`), which selects the EOA's own secp256k1 key.
2062
+ *
2063
+ * @param signature - Raw ECDSA signature (65 bytes, hex-encoded) from
2064
+ * `signTypedData` over the payload from {@link getUserOperationEip712Data}
2065
+ * @param overrides - Optional `keyHash` / `hookData` overrides
2066
+ * @returns Hex-encoded wrapped signature ready for `userOp.signature`
2067
+ */
2068
+ static formatEip712SingleSignatureToUseroperationSignature(signature, overrides = {}) {
2069
+ const keyHash = overrides.keyHash ?? ROOT_KEY_HASH;
2070
+ const hookData = overrides.hookData ?? "0x";
2071
+ return Calibur7702Account.wrapSignature(keyHash, signature, hookData);
2072
+ }
1455
2073
  /** The EntryPoint contract address this account targets */
1456
2074
  entrypointAddress;
1457
2075
  /** The Calibur singleton (delegatee) contract address */
@@ -1481,6 +2099,46 @@ var abstractionkit = (function(exports, ethers) {
1481
2099
  return createUserOperationHash(userOperation, this.entrypointAddress, chainId);
1482
2100
  }
1483
2101
  /**
2102
+ * Build the EIP-712 typed data payload for a UserOperation under the
2103
+ * EntryPoint v0.8 / v0.9 domain. Useful for wallets that can only sign
2104
+ * typed data (`eth_signTypedData_v4`) — the digest of the returned payload
2105
+ * equals the `userOpHash`, so a typed-data signature over it produces a
2106
+ * wrapped Calibur signature that validates against the same hash on-chain.
2107
+ *
2108
+ * Intended for the secp256k1 key path (root key or any registered
2109
+ * secondary secp256k1 key), since `eth_signTypedData_v4` only produces
2110
+ * secp256k1 signatures. P-256 and WebAuthn-P256 keys sign with their
2111
+ * own primitives — for those, use {@link getUserOperationEip712Hash}
2112
+ * (the digest), which is the value-to-be-signed regardless of key type.
2113
+ *
2114
+ * @param userOperation - Unsigned UserOperation to wrap
2115
+ * @param chainId - Target chain ID
2116
+ * @param overrides - Override the entrypoint address (defaults to EntryPoint v0.8)
2117
+ * @returns EIP-712 {@link TypedData} payload ready for `signTypedData`
2118
+ * @throws {AbstractionKitError} if the target EntryPoint is not v0.8 / v0.9.
2119
+ */
2120
+ static getUserOperationEip712Data(userOperation, chainId, overrides = {}) {
2121
+ return getUserOperationEip712DataV8V9(userOperation, overrides.entrypointAddress ?? "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108", chainId);
2122
+ }
2123
+ /**
2124
+ * Compute the EIP-712 digest of a UserOperation under the EntryPoint
2125
+ * v0.8 / v0.9 domain. For these EntryPoints this digest IS the
2126
+ * `userOpHash`; the wrapped Calibur signature is verified against this
2127
+ * hash on-chain.
2128
+ *
2129
+ * Universal across Calibur key types: secp256k1 and P-256 keys sign
2130
+ * this digest directly, each with their own signing primitive.
2131
+ *
2132
+ * @param userOperation - Unsigned UserOperation to hash
2133
+ * @param chainId - Target chain ID
2134
+ * @param overrides - Override the entrypoint address (defaults to EntryPoint v0.8)
2135
+ * @returns The EIP-712 digest as a hex string
2136
+ * @throws {AbstractionKitError} if the target EntryPoint is not v0.8 / v0.9.
2137
+ */
2138
+ static getUserOperationEip712Hash(userOperation, chainId, overrides = {}) {
2139
+ return getUserOperationEip712HashV8V9(userOperation, overrides.entrypointAddress ?? "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108", chainId);
2140
+ }
2141
+ /**
1484
2142
  * Encode calldata for `executeUserOp(bytes)` with BatchedCall format.
1485
2143
  * All transactions (even single ones) go through the same BatchedCall path.
1486
2144
  *
@@ -1532,7 +2190,7 @@ var abstractionkit = (function(exports, ethers) {
1532
2190
  }
1533
2191
  let skipEip7702Auth = false;
1534
2192
  let delegationCheckOp = null;
1535
- if (overrides.eip7702Auth != null && providerRpc != null) delegationCheckOp = getDelegatedAddress(this.accountAddress, providerRpc).catch(() => null);
2193
+ if (overrides.eip7702Auth != null && providerRpc != null) delegationCheckOp = JsonRpcNode.from(providerRpc).getDelegatedAddress(this.accountAddress).catch(() => null);
1536
2194
  if (overrides.eip7702Auth != null && eip7702AuthNonce == null) {
1537
2195
  let eip7702AuthNonceOp;
1538
2196
  if (providerRpc != null) eip7702AuthNonceOp = sendJsonRpcRequest(providerRpc, "eth_getTransactionCount", [this.accountAddress, "latest"]);
@@ -1634,7 +2292,7 @@ var abstractionkit = (function(exports, ethers) {
1634
2292
  userOperation.maxPriorityFeePerGas = 0n;
1635
2293
  const userOperationToEstimate = { ...userOperation };
1636
2294
  userOperationToEstimate.signature = overrides.dummySignature ?? Calibur7702Account.dummySignature;
1637
- const estimation = await new Bundler(bundlerRpc).estimateUserOperationGas(userOperationToEstimate, this.entrypointAddress, overrides.state_override_set);
2295
+ const estimation = await Bundler.from(bundlerRpc).estimateUserOperationGas(userOperationToEstimate, this.entrypointAddress, overrides.state_override_set);
1638
2296
  preVerificationGas = BigInt(estimation.preVerificationGas);
1639
2297
  verificationGasLimit = BigInt(estimation.verificationGasLimit);
1640
2298
  callGasLimit = BigInt(estimation.callGasLimit);
@@ -1680,38 +2338,46 @@ var abstractionkit = (function(exports, ethers) {
1680
2338
  return Calibur7702Account.wrapSignature(keyHash, ecdsaSig, hookData);
1681
2339
  }
1682
2340
  /**
1683
- * Schemes Calibur accepts from a Signer. Only raw-hash ECDSA, since
1684
- * the account verifies a plain signature over the userOp hash, then
1685
- * wraps with `(keyHash, signature, hookData)`.
2341
+ * Schemes Calibur accepts from a Signer. EntryPoint v0.8/v0.9 introduced
2342
+ * an EIP-712 domain at the EntryPoint contract, and the userOpHash IS the
2343
+ * EIP-712 digest of the PackedUserOperation under that domain — so signing
2344
+ * the typed data and signing the raw hash produce signatures that verify
2345
+ * against the same `userOpHash` (and recover to the same signer address).
2346
+ * Deterministic-ECDSA signers (ethers, viem, MetaMask) yield byte-identical
2347
+ * bytes; signers that differ in `s` / `v` normalization still validate the
2348
+ * same on-chain.
2349
+ *
2350
+ * `typedData` is listed first so JSON-RPC wallets that can only sign typed
2351
+ * data work without a separate code path; `hash` remains a valid fallback
2352
+ * for local-key signers.
1686
2353
  */
1687
- static ACCEPTED_SIGNING_SCHEMES = ["hash"];
2354
+ static ACCEPTED_SIGNING_SCHEMES = ["typedData", "hash"];
1688
2355
  /**
1689
- * Sign a UserOperation using an {@link ExternalSigner}. Calibur only
1690
- * accepts raw-hash ECDSA; signers without `signHash` fail offline with
1691
- * an actionable error.
1692
- *
1693
- * For signing with a raw private-key string, use the sync
1694
- * {@link signUserOperation} method, or wrap explicitly with
1695
- * `fromPrivateKey(pk)`. For secondary (non-root) keys, pass the key
1696
- * hash via `overrides.keyHash`.
2356
+ * Sign a UserOperation with an {@link AkSigner}. The signer can implement
2357
+ * either `signTypedData` (preferred — JSON-RPC wallets, viem `WalletClient`)
2358
+ * or `signHash` (local keys, hardware wallets). Both schemes produce
2359
+ * signatures that validate against the same `userOpHash` because the
2360
+ * v0.8 / v0.9 userOpHash IS the EIP-712 digest of the PackedUserOperation
2361
+ * (deterministic-ECDSA signers yield byte-identical bytes).
1697
2362
  *
1698
- * @example
1699
- * import { fromViem, fromEthersWallet } from "abstractionkit";
1700
- * userOp.signature = await account.signUserOperationWithSigner(
1701
- * userOp, fromViem(privateKeyToAccount(pk)), chainId,
1702
- * );
2363
+ * Signers that implement neither method fail offline with an actionable
2364
+ * error.
1703
2365
  */
1704
2366
  async signUserOperationWithSigner(userOperation, signer, chainId, overrides = {}) {
1705
- const signature = await invokeSigner(signer, pickScheme(signer, Calibur7702Account.ACCEPTED_SIGNING_SCHEMES, {
2367
+ const scheme = pickScheme(signer, Calibur7702Account.ACCEPTED_SIGNING_SCHEMES, {
1706
2368
  accountName: "Calibur (raw ECDSA over userOpHash)",
1707
2369
  signerIndex: 0
1708
- }), {
1709
- hash: createUserOperationHash(userOperation, this.entrypointAddress, chainId),
1710
- context: {
1711
- userOperation,
1712
- chainId,
1713
- entryPoint: this.entrypointAddress
1714
- }
2370
+ });
2371
+ const hash = createUserOperationHash(userOperation, this.entrypointAddress, chainId);
2372
+ const context = {
2373
+ userOperation,
2374
+ chainId,
2375
+ entryPoint: this.entrypointAddress
2376
+ };
2377
+ const signature = await invokeSigner(signer, scheme, {
2378
+ hash,
2379
+ typedData: scheme === "typedData" ? Calibur7702Account.getUserOperationEip712Data(userOperation, chainId, { entrypointAddress: this.entrypointAddress }) : void 0,
2380
+ context
1715
2381
  });
1716
2382
  const keyHash = overrides.keyHash ?? ROOT_KEY_HASH;
1717
2383
  const hookData = overrides.hookData ?? "0x";
@@ -1755,7 +2421,7 @@ var abstractionkit = (function(exports, ethers) {
1755
2421
  * @returns A {@link SendUseroperationResponse} that can be used to wait for inclusion
1756
2422
  */
1757
2423
  async sendUserOperation(userOperation, bundlerRpc) {
1758
- const bundler = new Bundler(bundlerRpc);
2424
+ const bundler = Bundler.from(bundlerRpc);
1759
2425
  return new SendUseroperationResponse(await bundler.sendUserOperation(userOperation, this.entrypointAddress), bundler, this.entrypointAddress);
1760
2426
  }
1761
2427
  /**
@@ -1941,7 +2607,7 @@ var abstractionkit = (function(exports, ethers) {
1941
2607
  */
1942
2608
  async createRevokeDelegationRawTransaction(chainId, eoaPrivateKey, providerRpc, overrides = {}) {
1943
2609
  if (new ethers.Wallet(eoaPrivateKey).address.toLowerCase() !== this.accountAddress.toLowerCase()) throw new AbstractionKitError("BAD_DATA", `eoaPrivateKey does not match accountAddress (${this.accountAddress})`);
1944
- const delegatedTo = await getDelegatedAddress(this.accountAddress, providerRpc);
2610
+ const delegatedTo = await JsonRpcNode.from(providerRpc).getDelegatedAddress(this.accountAddress);
1945
2611
  if (delegatedTo === null) throw new AbstractionKitError("BAD_DATA", "Account is not delegated — nothing to revoke");
1946
2612
  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");
1947
2613
  const results = {};
@@ -2005,14 +2671,14 @@ var abstractionkit = (function(exports, ethers) {
2005
2671
  /**
2006
2672
  * Check if this EOA is delegated to this account's singleton (delegatee).
2007
2673
  * Returns `false` if not delegated at all or delegated to a different
2008
- * singleton. Use the standalone {@link getDelegatedAddress} utility to
2009
- * get the raw delegatee address regardless of which singleton it is.
2674
+ * singleton. Use {@link JsonRpcNode.getDelegatedAddress} to get the raw
2675
+ * delegatee address regardless of which singleton it is.
2010
2676
  *
2011
2677
  * @param providerRpc - JSON-RPC endpoint
2012
2678
  * @returns True if the account is delegated to `this.delegateeAddress`
2013
2679
  */
2014
2680
  async isDelegatedToThisAccount(providerRpc) {
2015
- const address = await getDelegatedAddress(this.accountAddress, providerRpc);
2681
+ const address = await JsonRpcNode.from(providerRpc).getDelegatedAddress(this.accountAddress);
2016
2682
  if (address === null) return false;
2017
2683
  return address.toLowerCase() === this.delegateeAddress.toLowerCase();
2018
2684
  }
@@ -2560,7 +3226,8 @@ var abstractionkit = (function(exports, ethers) {
2560
3226
  * @returns The simulation results from Tenderly.
2561
3227
  */
2562
3228
  async function callTenderlySimulateBundle(tenderlyAccountSlug, tenderlyProjectSlug, tenderlyAccessKey, transactions) {
2563
- return await sendJsonRpcRequest("https://api.tenderly.co/api/v1/account/" + tenderlyAccountSlug + "/project/" + tenderlyProjectSlug + "/simulate-bundle", "tenderly_simulateBundle", transactions.map((transaction) => {
3229
+ const tenderlyUrl = "https://api.tenderly.co/api/v1/account/" + tenderlyAccountSlug + "/project/" + tenderlyProjectSlug + "/simulate-bundle";
3230
+ const simulations = transactions.map((transaction) => {
2564
3231
  const transactionObject = {
2565
3232
  network_id: transaction.chainId.toString(),
2566
3233
  save: transaction.save ?? true,
@@ -2589,11 +3256,53 @@ var abstractionkit = (function(exports, ethers) {
2589
3256
  if (transaction.generateAccessList != null) transactionObject.generate_access_list = transaction.generateAccessList;
2590
3257
  if (transaction.accessList != null) transactionObject.access_list = transaction.accessList;
2591
3258
  return transactionObject;
2592
- }), {
3259
+ });
3260
+ const headers = {
2593
3261
  Accept: "application/json",
2594
3262
  "Content-Type": "application/json",
2595
3263
  "X-Access-Key": tenderlyAccessKey
2596
- }, "simulations");
3264
+ };
3265
+ const body = JSON.stringify({
3266
+ method: "tenderly_simulateBundle",
3267
+ simulations,
3268
+ id: Date.now(),
3269
+ jsonrpc: "2.0"
3270
+ }, (_key, value) => typeof value === "bigint" ? `0x${value.toString(16)}` : value);
3271
+ let response;
3272
+ try {
3273
+ response = await fetch(tenderlyUrl, {
3274
+ method: "POST",
3275
+ headers,
3276
+ body,
3277
+ redirect: "follow"
3278
+ });
3279
+ } catch (err) {
3280
+ const e = ensureError(err);
3281
+ throw new AbstractionKitError("TENDERLY_NETWORK_ERROR", `tenderly_simulateBundle network error: ${e.message}`, { cause: e });
3282
+ }
3283
+ if (!response.ok) throw new AbstractionKitError("TENDERLY_HTTP_ERROR", `tenderly_simulateBundle HTTP ${response.status}: ${response.statusText}`, {
3284
+ errno: response.status,
3285
+ context: {
3286
+ status: response.status,
3287
+ statusText: response.statusText
3288
+ }
3289
+ });
3290
+ const responseText = await response.text();
3291
+ let json;
3292
+ try {
3293
+ json = JSON.parse(responseText);
3294
+ } catch (err) {
3295
+ const e = ensureError(err);
3296
+ throw new AbstractionKitError("TENDERLY_INVALID_JSON", `tenderly_simulateBundle returned invalid JSON: ${e.message}`, {
3297
+ cause: e,
3298
+ context: { responseText }
3299
+ });
3300
+ }
3301
+ if (json.simulation_results != null) return json.simulation_results;
3302
+ throw new AbstractionKitError("TENDERLY_SIMULATION_ERROR", "tenderly_simulateBundle failed", {
3303
+ errno: json.error?.code,
3304
+ context: JSON.stringify(json)
3305
+ });
2597
3306
  }
2598
3307
  //#endregion
2599
3308
  //#region src/account/Safe/multisend.ts
@@ -2819,7 +3528,7 @@ var abstractionkit = (function(exports, ethers) {
2819
3528
  * ```
2820
3529
  */
2821
3530
  static async isDeployed(accountAddress, nodeRpcUrl) {
2822
- return (await sendEthGetCodeRequest(nodeRpcUrl, accountAddress, "latest")).length > 2;
3531
+ return (await JsonRpcNode.from(nodeRpcUrl).getCode(accountAddress, "latest")).length > 2;
2823
3532
  }
2824
3533
  /**
2825
3534
  * encode calldata for a single MetaTransaction to be executed by Safe account
@@ -3203,11 +3912,11 @@ var abstractionkit = (function(exports, ethers) {
3203
3912
  /**
3204
3913
  * sends a useroperation to a bundler rpc
3205
3914
  * @param userOperation - useroperation to send
3206
- * @param bundlerRpc - bundler rpc to send useroperation
3915
+ * @param bundlerRpc - bundler rpc URL, {@link Transport}, or pre-constructed {@link Bundler}
3207
3916
  * @returns promise with SendUseroperationResponse
3208
3917
  */
3209
3918
  async sendUserOperation(userOperation, bundlerRpc) {
3210
- const bundler = new Bundler(bundlerRpc);
3919
+ const bundler = Bundler.from(bundlerRpc);
3211
3920
  return new SendUseroperationResponse(await bundler.sendUserOperation(userOperation, this.entrypointAddress), bundler, this.entrypointAddress);
3212
3921
  }
3213
3922
  /**
@@ -3390,7 +4099,7 @@ var abstractionkit = (function(exports, ethers) {
3390
4099
  validUntil,
3391
4100
  isMultiChainSignature: overrides.isMultiChainSignature
3392
4101
  });
3393
- const bundler = new Bundler(bundlerRpc);
4102
+ const bundler = Bundler.from(bundlerRpc);
3394
4103
  const inputMaxFeePerGas = userOperation.maxFeePerGas;
3395
4104
  const inputMaxPriorityFeePerGas = userOperation.maxPriorityFeePerGas;
3396
4105
  userOperation.maxFeePerGas = 0n;
@@ -3913,7 +4622,7 @@ var abstractionkit = (function(exports, ethers) {
3913
4622
  webAuthnSignerSingleton: overrides.webAuthnSignerSingleton,
3914
4623
  webAuthnSignerProxyCreationCode
3915
4624
  });
3916
- if ((await sendEthGetCodeRequest(nodeRpcUrl, newOwnerT, "latest")).length < 3) deployNewOwnerSignerMetaTransaction = SafeAccount.createDeployWebAuthnVerifierMetaTransaction(newOwner.x, newOwner.y, {
4625
+ if ((await JsonRpcNode.from(nodeRpcUrl).getCode(newOwnerT, "latest")).length < 3) deployNewOwnerSignerMetaTransaction = SafeAccount.createDeployWebAuthnVerifierMetaTransaction(newOwner.x, newOwner.y, {
3917
4626
  eip7212WebAuthnPrecompileVerifier: overrides.eip7212WebAuthnPrecompileVerifier,
3918
4627
  eip7212WebAuthnContractVerifier: overrides.eip7212WebAuthnContractVerifier,
3919
4628
  webAuthnSignerFactory: overrides.webAuthnSignerFactory
@@ -3996,7 +4705,7 @@ var abstractionkit = (function(exports, ethers) {
3996
4705
  webAuthnSignerProxyCreationCode
3997
4706
  });
3998
4707
  if (overrides.nodeRpcUrl == null) throw new RangeError("overrides.nodeRpcUrl can't be null if adding a webauthn owner");
3999
- if ((await sendEthGetCodeRequest(overrides.nodeRpcUrl, newOwnerT, "latest")).length < 3) deployNewOwnerSignerMetaTransaction = SafeAccount.createDeployWebAuthnVerifierMetaTransaction(newOwner.x, newOwner.y, {
4708
+ if ((await JsonRpcNode.from(overrides.nodeRpcUrl).getCode(newOwnerT, "latest")).length < 3) deployNewOwnerSignerMetaTransaction = SafeAccount.createDeployWebAuthnVerifierMetaTransaction(newOwner.x, newOwner.y, {
4000
4709
  eip7212WebAuthnPrecompileVerifier: overrides.eip7212WebAuthnPrecompileVerifier,
4001
4710
  eip7212WebAuthnContractVerifier: overrides.eip7212WebAuthnContractVerifier,
4002
4711
  webAuthnSignerFactory: overrides.webAuthnSignerFactory
@@ -4124,10 +4833,11 @@ var abstractionkit = (function(exports, ethers) {
4124
4833
  */
4125
4834
  async getOwners(nodeRpcUrl) {
4126
4835
  const callData = createCallData(getFunctionSelector("getOwners()"), [], []);
4127
- const getOwnersResult = await sendEthCallRequest(nodeRpcUrl, {
4836
+ const ethCallParams = {
4128
4837
  to: this.accountAddress,
4129
4838
  data: callData
4130
- }, "latest");
4839
+ };
4840
+ const getOwnersResult = await JsonRpcNode.from(nodeRpcUrl).call(ethCallParams, "latest");
4131
4841
  return ethers.AbiCoder.defaultAbiCoder().decode(["address[]"], getOwnersResult)[0];
4132
4842
  }
4133
4843
  /**
@@ -4137,10 +4847,11 @@ var abstractionkit = (function(exports, ethers) {
4137
4847
  */
4138
4848
  async getThreshold(nodeRpcUrl) {
4139
4849
  const callData = createCallData("0xe75235b8", [], []);
4140
- const getThresholdResult = await sendEthCallRequest(nodeRpcUrl, {
4850
+ const ethCallParams = {
4141
4851
  to: this.accountAddress,
4142
4852
  data: callData
4143
- }, "latest");
4853
+ };
4854
+ const getThresholdResult = await JsonRpcNode.from(nodeRpcUrl).call(ethCallParams, "latest");
4144
4855
  const decodedCalldata = ethers.AbiCoder.defaultAbiCoder().decode(["uint256"], getThresholdResult);
4145
4856
  return Number(decodedCalldata[0]);
4146
4857
  }
@@ -4159,10 +4870,11 @@ var abstractionkit = (function(exports, ethers) {
4159
4870
  let pageSize = overrides.pageSize;
4160
4871
  if (pageSize == null) pageSize = 10n;
4161
4872
  const callData = createCallData("0xcc2f8452", ["address", "uint256"], [start, pageSize]);
4162
- const getModulesResult = await sendEthCallRequest(nodeRpcUrl, {
4873
+ const ethCallParams = {
4163
4874
  to: this.accountAddress,
4164
4875
  data: callData
4165
- }, "latest");
4876
+ };
4877
+ const getModulesResult = await JsonRpcNode.from(nodeRpcUrl).call(ethCallParams, "latest");
4166
4878
  if (getModulesResult === "0x") throw new AbstractionKitError("BAD_DATA", "getModules returned an empty result, the target account is probably not deployed yet.");
4167
4879
  const decodedCalldata = ethers.AbiCoder.defaultAbiCoder().decode(["address[]", "address"], getModulesResult);
4168
4880
  return [decodedCalldata[0], decodedCalldata[1]];
@@ -4178,10 +4890,11 @@ var abstractionkit = (function(exports, ethers) {
4178
4890
  */
4179
4891
  async isModuleEnabled(nodeRpcUrl, moduleAddress) {
4180
4892
  const callData = createCallData(getFunctionSelector("isModuleEnabled(address)"), ["address"], [moduleAddress]);
4181
- const isModuleEnabledResult = await sendEthCallRequest(nodeRpcUrl, {
4893
+ const ethCallParams = {
4182
4894
  to: this.accountAddress,
4183
4895
  data: callData
4184
- }, "latest");
4896
+ };
4897
+ const isModuleEnabledResult = await JsonRpcNode.from(nodeRpcUrl).call(ethCallParams, "latest");
4185
4898
  return ethers.AbiCoder.defaultAbiCoder().decode(["bool"], isModuleEnabledResult)[0];
4186
4899
  }
4187
4900
  /**
@@ -4248,7 +4961,7 @@ var abstractionkit = (function(exports, ethers) {
4248
4961
  data: callData
4249
4962
  };
4250
4963
  const deployedByteCode = SafeAccount.createSafeWebAuthnSignerProxyDeployedByteCode(signer, eip7212WebAuthnPrecompileVerifier, eip7212WebAuthnContractVerifier, webAuthnSignerSingleton);
4251
- const isModuleEnabledResult = await sendEthCallRequest(nodeRpcUrl, ethCallParams, "latest", { [arbitraryAddress]: { code: deployedByteCode } });
4964
+ const isModuleEnabledResult = await JsonRpcNode.from(nodeRpcUrl).call(ethCallParams, "latest", { [arbitraryAddress]: { code: deployedByteCode } });
4252
4965
  return ethers.AbiCoder.defaultAbiCoder().decode(["bool"], isModuleEnabledResult)[0];
4253
4966
  }
4254
4967
  static createSafeWebAuthnSignerProxyDeployedByteCode(signer, eip7212WebAuthnPrecompileVerifier, eip7212WebAuthnContractVerifier, webAuthnSignerSingleton) {
@@ -4368,7 +5081,7 @@ var abstractionkit = (function(exports, ethers) {
4368
5081
  * @param toolVersion - tool version; defaults to the current abstractionkit version
4369
5082
  * @returns the on-chain identifier as a hex string (not 0x prefixed)
4370
5083
  */
4371
- function generateOnChainIdentifier(project, platform = "Web", tool = "abstractionkit", toolVersion = "0.3.7") {
5084
+ function generateOnChainIdentifier(project, platform = "Web", tool = "abstractionkit", toolVersion = "0.3.8") {
4372
5085
  const identifierPrefix = "5afe";
4373
5086
  const identifierVersion = "00";
4374
5087
  const projectHash = (0, ethers.keccak256)(`0x${Buffer.from(project, "utf8").toString("hex")}`).slice(-20);
@@ -4631,10 +5344,11 @@ var abstractionkit = (function(exports, ethers) {
4631
5344
  */
4632
5345
  async getTokens(nodeRpcUrl, safeAddress, delegate) {
4633
5346
  const callData = createCallData("0x8d0e8e1d", ["address", "address"], [safeAddress, delegate]);
4634
- const tokens = await sendEthCallRequest(nodeRpcUrl, {
5347
+ const ethCallParams = {
4635
5348
  to: this.moduleAddress,
4636
5349
  data: callData
4637
- }, "latest");
5350
+ };
5351
+ const tokens = await JsonRpcNode.from(nodeRpcUrl).call(ethCallParams, "latest");
4638
5352
  this.checkForEmptyResultAndRevert(tokens, "getTokens");
4639
5353
  return this.abiCoder.decode(["address[]"], tokens)[0];
4640
5354
  }
@@ -4656,10 +5370,11 @@ var abstractionkit = (function(exports, ethers) {
4656
5370
  delegate,
4657
5371
  token
4658
5372
  ]);
4659
- const tokenAllowance = await sendEthCallRequest(nodeRpcUrl, {
5373
+ const ethCallParams = {
4660
5374
  to: this.moduleAddress,
4661
5375
  data: callData
4662
- }, "latest");
5376
+ };
5377
+ const tokenAllowance = await JsonRpcNode.from(nodeRpcUrl).call(ethCallParams, "latest");
4663
5378
  this.checkForEmptyResultAndRevert(tokenAllowance, "getTokenAllowance");
4664
5379
  const allowance = this.abiCoder.decode(["uint256[5]"], tokenAllowance)[0];
4665
5380
  return {
@@ -4711,10 +5426,11 @@ var abstractionkit = (function(exports, ethers) {
4711
5426
  start,
4712
5427
  pageSize
4713
5428
  ]);
4714
- const delegates = await sendEthCallRequest(nodeRpcUrl, {
5429
+ const ethCallParams = {
4715
5430
  to: this.moduleAddress,
4716
5431
  data: callData
4717
- }, "latest");
5432
+ };
5433
+ const delegates = await JsonRpcNode.from(nodeRpcUrl).call(ethCallParams, "latest");
4718
5434
  this.checkForEmptyResultAndRevert(delegates, "getDelegates");
4719
5435
  const decodedCalldata = this.abiCoder.decode(["address[]", "uint48"], delegates);
4720
5436
  return {
@@ -4799,13 +5515,19 @@ var abstractionkit = (function(exports, ethers) {
4799
5515
  "address",
4800
5516
  "address[]",
4801
5517
  "uint256",
4802
- "(address,bytes)",
5518
+ "(address,bytes)[]",
4803
5519
  "bool"
4804
5520
  ], [
4805
5521
  accountAddress,
4806
5522
  newOwners,
4807
5523
  newThreshold,
4808
- signaturePairList.map((signaturePair) => [signaturePair.signer, signaturePair.signature]),
5524
+ [...signaturePairList].sort((a, b) => {
5525
+ const aSigner = BigInt(a.signer);
5526
+ const bSigner = BigInt(b.signer);
5527
+ if (aSigner < bSigner) return -1;
5528
+ if (aSigner > bSigner) return 1;
5529
+ throw new AbstractionKitError("BAD_DATA", `Duplicate signer in recovery signaturePairList: ${a.signer}`);
5530
+ }).map((signaturePair) => [signaturePair.signer, signaturePair.signature]),
4809
5531
  execute
4810
5532
  ]);
4811
5533
  return {
@@ -4957,10 +5679,11 @@ var abstractionkit = (function(exports, ethers) {
4957
5679
  newThreshold,
4958
5680
  nonce
4959
5681
  ]);
4960
- const recoveryHashResult = await sendEthCallRequest(nodeRpcUrl, {
5682
+ const ethCallParams = {
4961
5683
  to: this.moduleAddress,
4962
5684
  data: callData
4963
- }, "latest");
5685
+ };
5686
+ const recoveryHashResult = await JsonRpcNode.from(nodeRpcUrl).call(ethCallParams, "latest");
4964
5687
  this.checkForEmptyResultAndRevert(recoveryHashResult, "getRecoveryHash");
4965
5688
  return this.abiCoder.decode(["bytes32"], recoveryHashResult)[0];
4966
5689
  }
@@ -4972,10 +5695,11 @@ var abstractionkit = (function(exports, ethers) {
4972
5695
  */
4973
5696
  async getRecoveryRequest(nodeRpcUrl, accountAddress) {
4974
5697
  const callData = createCallData("0x4f9a28b9", ["address"], [accountAddress]);
4975
- const recoveryRequestResult = await sendEthCallRequest(nodeRpcUrl, {
5698
+ const ethCallParams = {
4976
5699
  to: this.moduleAddress,
4977
5700
  data: callData
4978
- }, "latest");
5701
+ };
5702
+ const recoveryRequestResult = await JsonRpcNode.from(nodeRpcUrl).call(ethCallParams, "latest");
4979
5703
  this.checkForEmptyResultAndRevert(recoveryRequestResult, "getRecoveryRequest");
4980
5704
  const decodedCalldata = this.abiCoder.decode(["(uint256,uint256,uint64,address[])"], recoveryRequestResult);
4981
5705
  return {
@@ -5003,10 +5727,11 @@ var abstractionkit = (function(exports, ethers) {
5003
5727
  newOwners,
5004
5728
  newThreshold
5005
5729
  ]);
5006
- const recoveryApprovalResult = await sendEthCallRequest(nodeRpcUrl, {
5730
+ const ethCallParams = {
5007
5731
  to: this.moduleAddress,
5008
5732
  data: callData
5009
- }, "latest");
5733
+ };
5734
+ const recoveryApprovalResult = await JsonRpcNode.from(nodeRpcUrl).call(ethCallParams, "latest");
5010
5735
  this.checkForEmptyResultAndRevert(recoveryApprovalResult, "getRecoveryApprovals");
5011
5736
  const decodedCalldata = this.abiCoder.decode(["uint256"], recoveryApprovalResult);
5012
5737
  return BigInt(decodedCalldata[0]);
@@ -5032,10 +5757,11 @@ var abstractionkit = (function(exports, ethers) {
5032
5757
  newOwners,
5033
5758
  newThreshold
5034
5759
  ]);
5035
- const hasGuardianApprovedResult = await sendEthCallRequest(nodeRpcUrl, {
5760
+ const ethCallParams = {
5036
5761
  to: this.moduleAddress,
5037
5762
  data: callData
5038
- }, "latest");
5763
+ };
5764
+ const hasGuardianApprovedResult = await JsonRpcNode.from(nodeRpcUrl).call(ethCallParams, "latest");
5039
5765
  this.checkForEmptyResultAndRevert(hasGuardianApprovedResult, "hasGuardianApproved");
5040
5766
  const decodedCalldata = this.abiCoder.decode(["bool"], hasGuardianApprovedResult);
5041
5767
  return Boolean(decodedCalldata[0]);
@@ -5050,10 +5776,11 @@ var abstractionkit = (function(exports, ethers) {
5050
5776
  */
5051
5777
  async isGuardian(nodeRpcUrl, accountAddress, guardian) {
5052
5778
  const callData = createCallData("0xd4ee9734", ["address", "address"], [accountAddress, guardian]);
5053
- const isGuardianResult = await sendEthCallRequest(nodeRpcUrl, {
5779
+ const ethCallParams = {
5054
5780
  to: this.moduleAddress,
5055
5781
  data: callData
5056
- }, "latest");
5782
+ };
5783
+ const isGuardianResult = await JsonRpcNode.from(nodeRpcUrl).call(ethCallParams, "latest");
5057
5784
  this.checkForEmptyResultAndRevert(isGuardianResult, "isGuardian");
5058
5785
  const decodedCalldata = this.abiCoder.decode(["bool"], isGuardianResult);
5059
5786
  return Boolean(decodedCalldata[0]);
@@ -5066,10 +5793,11 @@ var abstractionkit = (function(exports, ethers) {
5066
5793
  */
5067
5794
  async guardiansCount(nodeRpcUrl, accountAddress) {
5068
5795
  const callData = createCallData("0xc026e7ee", ["address"], [accountAddress]);
5069
- const guardiansCountResult = await sendEthCallRequest(nodeRpcUrl, {
5796
+ const ethCallParams = {
5070
5797
  to: this.moduleAddress,
5071
5798
  data: callData
5072
- }, "latest");
5799
+ };
5800
+ const guardiansCountResult = await JsonRpcNode.from(nodeRpcUrl).call(ethCallParams, "latest");
5073
5801
  this.checkForEmptyResultAndRevert(guardiansCountResult, "guardiansCount");
5074
5802
  const decodedCalldata = this.abiCoder.decode(["uint256"], guardiansCountResult);
5075
5803
  return BigInt(decodedCalldata[0]);
@@ -5082,10 +5810,11 @@ var abstractionkit = (function(exports, ethers) {
5082
5810
  */
5083
5811
  async threshold(nodeRpcUrl, accountAddress) {
5084
5812
  const callData = createCallData("0xc86ec2bf", ["address"], [accountAddress]);
5085
- const thresholdResult = await sendEthCallRequest(nodeRpcUrl, {
5813
+ const ethCallParams = {
5086
5814
  to: this.moduleAddress,
5087
5815
  data: callData
5088
- }, "latest");
5816
+ };
5817
+ const thresholdResult = await JsonRpcNode.from(nodeRpcUrl).call(ethCallParams, "latest");
5089
5818
  this.checkForEmptyResultAndRevert(thresholdResult, "threshold");
5090
5819
  const decodedCalldata = this.abiCoder.decode(["uint256"], thresholdResult);
5091
5820
  return BigInt(decodedCalldata[0]);
@@ -5098,10 +5827,11 @@ var abstractionkit = (function(exports, ethers) {
5098
5827
  */
5099
5828
  async getGuardians(nodeRpcUrl, accountAddress) {
5100
5829
  const callData = createCallData("0xf18858ab", ["address"], [accountAddress]);
5101
- const getGuardiansResult = await sendEthCallRequest(nodeRpcUrl, {
5830
+ const ethCallParams = {
5102
5831
  to: this.moduleAddress,
5103
5832
  data: callData
5104
- }, "latest");
5833
+ };
5834
+ const getGuardiansResult = await JsonRpcNode.from(nodeRpcUrl).call(ethCallParams, "latest");
5105
5835
  this.checkForEmptyResultAndRevert(getGuardiansResult, "threshold");
5106
5836
  return this.abiCoder.decode(["address[]"], getGuardiansResult)[0];
5107
5837
  }
@@ -5113,10 +5843,11 @@ var abstractionkit = (function(exports, ethers) {
5113
5843
  */
5114
5844
  async nonce(nodeRpcUrl, accountAddress) {
5115
5845
  const callData = createCallData("0x70ae92d2", ["address"], [accountAddress]);
5116
- const nonceResult = await sendEthCallRequest(nodeRpcUrl, {
5846
+ const ethCallParams = {
5117
5847
  to: this.moduleAddress,
5118
5848
  data: callData
5119
- }, "latest");
5849
+ };
5850
+ const nonceResult = await JsonRpcNode.from(nodeRpcUrl).call(ethCallParams, "latest");
5120
5851
  this.checkForEmptyResultAndRevert(nonceResult, "threshold");
5121
5852
  const decodedCalldata = this.abiCoder.decode(["uint256"], nonceResult);
5122
5853
  return BigInt(decodedCalldata[0]);
@@ -6196,6 +6927,14 @@ var abstractionkit = (function(exports, ethers) {
6196
6927
  * {@link SafeAccountV0_3_0.signUserOperationWithSigners} for the full
6197
6928
  * design rationale. Sets the multi-chain flag automatically.
6198
6929
  *
6930
+ * Note the chainId plumbing asymmetry vs the multi-op variant:
6931
+ * - **Singular** (this method): `chainId` is a positional argument.
6932
+ * - **Plural** ({@link signUserOperationsWithSigners}): `chainId` lives
6933
+ * inside each `UserOperationToSign` element, since each op may
6934
+ * target a different chain.
6935
+ * Pick the variant that matches your bundle shape; don't pass the same
6936
+ * chainId twice.
6937
+ *
6199
6938
  * @param userOperation - UserOperation to sign
6200
6939
  * @param signers - one ExternalSigner per owner (any order)
6201
6940
  * @param chainId - target chain id
@@ -6272,17 +7011,26 @@ var abstractionkit = (function(exports, ethers) {
6272
7011
  /**
6273
7012
  * Sign a list of UserOperations with a single multi-chain signature. Each
6274
7013
  * signer signs the Merkle root of the UserOperation EIP-712 hashes via
6275
- * raw-hash signing; `signTypedData` isn't exposed because the Merkle root
6276
- * is opaque and has no meaningful typed-data display.
7014
+ * either `signTypedData` (the root is wrapped in an EIP-712 `MerkleTreeRoot`
7015
+ * message) or raw-hash signing; both schemes produce signatures that
7016
+ * validate against the same on-chain digest.
6277
7017
  *
6278
7018
  * Signers always receive {@link MultiOpSignContext}. The built-in adapters
6279
- * `fromPrivateKey`, `fromViem`, and `fromEthersWallet` return
6280
- * `Signer<unknown>` and work here without retyping; `fromViemWalletClient`
6281
- * does **not** it only exposes `signTypedData`, so {@link pickScheme}
6282
- * rejects it offline. User-defined single-op signers
6283
- * (`Signer<SignContext>`) also don't work — they'd receive a context shape
7019
+ * `fromPrivateKey`, `fromViem`, `fromEthersWallet`, and `fromViemWalletClient`
7020
+ * all work here without retyping (`fromViemWalletClient` will sign the
7021
+ * typed-data Merkle wrapper). User-defined single-op signers
7022
+ * (`Signer<SignContext>`) still don't work they'd receive a context shape
6284
7023
  * they didn't declare.
6285
7024
  *
7025
+ * Note the chainId plumbing asymmetry vs the single-op variant:
7026
+ * - **Plural** (this method): each `UserOperationToSign` carries its
7027
+ * own `chainId`, since a multichain bundle's ops target different
7028
+ * chains.
7029
+ * - **Singular** ({@link signUserOperationWithSigners}): `chainId` is
7030
+ * a positional argument.
7031
+ * For a length-1 bundle on this method, set `chainId` on the single
7032
+ * element rather than reaching for the singular variant.
7033
+ *
6286
7034
  * @param userOperationsToSign - UserOperations + chain IDs + validity windows
6287
7035
  * @param signers - one Signer per owner (any order; sorted by address on-chain)
6288
7036
  * @returns one signature per input UserOperation, in the same order
@@ -6311,14 +7059,19 @@ var abstractionkit = (function(exports, ethers) {
6311
7059
  const [root, proofs] = generateMerkleProofs(userOperationsHashes);
6312
7060
  const merkleTreeRootHash = ethers.TypedDataEncoder.hash({ verifyingContract: this.safe4337ModuleAddress }, EIP712_MULTI_CHAIN_OPERATIONS_TYPE, { merkleTreeRoot: root });
6313
7061
  const normalizedAddresses = signers.map((signer) => (0, ethers.getAddress)(signer.address));
6314
- signers.forEach((signer, i) => {
6315
- pickScheme(signer, ["hash"], {
6316
- accountName: "SafeMultiChainSigAccountV1 (multi-op Merkle root)",
6317
- signerIndex: i
6318
- });
6319
- });
6320
- const signatures = await Promise.all(signers.map((signer) => invokeSigner(signer, "hash", {
7062
+ const typedDataForBundle = {
7063
+ domain: { verifyingContract: this.safe4337ModuleAddress },
7064
+ types: EIP712_MULTI_CHAIN_OPERATIONS_TYPE,
7065
+ primaryType: EIP712_MULTI_CHAIN_OPERATIONS_PRIMARY_TYPE,
7066
+ message: { merkleTreeRoot: root }
7067
+ };
7068
+ const schemes = signers.map((signer, i) => pickScheme(signer, ["typedData", "hash"], {
7069
+ accountName: "SafeMultiChainSigAccountV1 (multi-op Merkle root)",
7070
+ signerIndex: i
7071
+ }));
7072
+ const signatures = await Promise.all(signers.map((signer, i) => invokeSigner(signer, schemes[i], {
6321
7073
  hash: merkleTreeRootHash,
7074
+ typedData: typedDataForBundle,
6322
7075
  context
6323
7076
  })));
6324
7077
  const signerSignaturePairs = signers.map((_signer, i) => ({
@@ -6542,13 +7295,13 @@ var abstractionkit = (function(exports, ethers) {
6542
7295
  /**
6543
7296
  * Check if this EOA is delegated to the expected delegatee address via EIP-7702.
6544
7297
  * Returns `true` only when delegated to `this.delegateeAddress`.
6545
- * Use `getDelegatedAddress()` directly to get the raw delegatee address.
7298
+ * Use `JsonRpcNode.getDelegatedAddress()` directly to get the raw delegatee address.
6546
7299
  *
6547
7300
  * @param providerRpc - Ethereum JSON-RPC node URL
6548
7301
  * @returns `true` if delegated to the expected address, `false` otherwise
6549
7302
  */
6550
7303
  async isDelegatedToThisAccount(providerRpc) {
6551
- const address = await getDelegatedAddress(this.accountAddress, providerRpc);
7304
+ const address = await JsonRpcNode.from(providerRpc).getDelegatedAddress(this.accountAddress);
6552
7305
  if (address === null) return false;
6553
7306
  return address.toLowerCase() === this.delegateeAddress.toLowerCase();
6554
7307
  }
@@ -6569,7 +7322,7 @@ var abstractionkit = (function(exports, ethers) {
6569
7322
  * @returns Signed raw transaction hex, ready for `eth_sendRawTransaction`
6570
7323
  */
6571
7324
  async createRevokeDelegationTransaction(eoaPrivateKey, providerRpc, overrides = {}) {
6572
- const delegatedTo = await getDelegatedAddress(this.accountAddress, providerRpc);
7325
+ const delegatedTo = await JsonRpcNode.from(providerRpc).getDelegatedAddress(this.accountAddress);
6573
7326
  if (delegatedTo === null) throw new AbstractionKitError("BAD_DATA", "Account is not delegated — nothing to revoke");
6574
7327
  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");
6575
7328
  const results = {};
@@ -6672,7 +7425,7 @@ var abstractionkit = (function(exports, ethers) {
6672
7425
  eip7702AuthNonce = overrides.eip7702Auth.nonce ?? null;
6673
7426
  }
6674
7427
  let delegationCheckOp = null;
6675
- if (overrides.eip7702Auth != null && providerRpc != null) delegationCheckOp = getDelegatedAddress(this.accountAddress, providerRpc).catch(() => null);
7428
+ if (overrides.eip7702Auth != null && providerRpc != null) delegationCheckOp = JsonRpcNode.from(providerRpc).getDelegatedAddress(this.accountAddress).catch(() => null);
6676
7429
  if (overrides.eip7702Auth != null && eip7702AuthNonce == null) {
6677
7430
  let eip7702AuthNonceOp;
6678
7431
  if (providerRpc != null) eip7702AuthNonceOp = sendJsonRpcRequest(providerRpc, "eth_getTransactionCount", [this.accountAddress, "latest"]);
@@ -6807,7 +7560,7 @@ var abstractionkit = (function(exports, ethers) {
6807
7560
  */
6808
7561
  async baseEstimateUserOperationGas(userOperation, bundlerRpc, overrides = {}) {
6809
7562
  userOperation.signature = overrides.dummySignature ?? BaseSimple7702Account.dummySignature;
6810
- const bundler = new Bundler(bundlerRpc);
7563
+ const bundler = Bundler.from(bundlerRpc);
6811
7564
  const inputMaxFeePerGas = userOperation.maxFeePerGas;
6812
7565
  const inputMaxPriorityFeePerGas = userOperation.maxPriorityFeePerGas;
6813
7566
  userOperation.maxFeePerGas = 0n;
@@ -6863,83 +7616,35 @@ var abstractionkit = (function(exports, ethers) {
6863
7616
  * Deterministic-ECDSA signers yield byte-identical signatures; signers
6864
7617
  * that differ in `s` / `v` normalization still validate the same on-chain.
6865
7618
  *
6866
- * Common use cases beyond direct signing:
6867
- * - Inspect / log the typed data the wallet will display.
6868
- * - Render a custom confirmation UI before delegating to a wallet's
6869
- * `signTypedData`.
6870
- * - Drive non-`ExternalSigner`-shaped signers (HSM, MPC service,
6871
- * backend signing pipelines).
7619
+ * The base class defaults to EntryPoint v0.8; subclasses
7620
+ * ({@link Simple7702AccountV09}) override with their own default.
6872
7621
  *
6873
7622
  * @param userOperation - Unsigned UserOperation to wrap
6874
7623
  * @param chainId - Target chain ID (must match the chain that will validate
6875
7624
  * the signature)
7625
+ * @param overrides - Override the entrypoint address
6876
7626
  * @returns EIP-712 {@link TypedData} payload ready for `signTypedData`
6877
- * @throws {AbstractionKitError} if this account targets an EntryPoint
6878
- * version other than v0.8 / v0.9. Earlier EntryPoints define the
6879
- * userOpHash differently and require raw-hash signing.
6880
- */
6881
- getUserOperationEip712TypedData(userOperation, chainId) {
6882
- const ep = this.entrypointAddress.toLowerCase();
6883
- const isV9 = ep === ENTRYPOINT_V9.toLowerCase();
6884
- if (ep !== "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108".toLowerCase() && !isV9) throw new AbstractionKitError("BAD_DATA", `getUserOperationEip712TypedData supports EntryPoint v0.8 / v0.9 only; this account targets ${this.entrypointAddress}. Use signUserOperation (raw-hash ECDSA) for earlier EntryPoint versions.`);
6885
- const abiCoder = ethers.AbiCoder.defaultAbiCoder();
6886
- const initCode = buildPackedInitCodeV8V9(userOperation);
6887
- const accountGasLimits = "0x" + abiCoder.encode(["uint128"], [userOperation.verificationGasLimit]).slice(34) + abiCoder.encode(["uint128"], [userOperation.callGasLimit]).slice(34);
6888
- const gasFees = "0x" + abiCoder.encode(["uint128"], [userOperation.maxPriorityFeePerGas]).slice(34) + abiCoder.encode(["uint128"], [userOperation.maxFeePerGas]).slice(34);
6889
- const paymasterAndData = buildPaymasterAndData(userOperation, isV9);
6890
- return {
6891
- domain: {
6892
- name: "ERC4337",
6893
- version: "1",
6894
- chainId,
6895
- verifyingContract: this.entrypointAddress
6896
- },
6897
- types: { PackedUserOperation: [
6898
- {
6899
- name: "sender",
6900
- type: "address"
6901
- },
6902
- {
6903
- name: "nonce",
6904
- type: "uint256"
6905
- },
6906
- {
6907
- name: "initCode",
6908
- type: "bytes"
6909
- },
6910
- {
6911
- name: "callData",
6912
- type: "bytes"
6913
- },
6914
- {
6915
- name: "accountGasLimits",
6916
- type: "bytes32"
6917
- },
6918
- {
6919
- name: "preVerificationGas",
6920
- type: "uint256"
6921
- },
6922
- {
6923
- name: "gasFees",
6924
- type: "bytes32"
6925
- },
6926
- {
6927
- name: "paymasterAndData",
6928
- type: "bytes"
6929
- }
6930
- ] },
6931
- primaryType: "PackedUserOperation",
6932
- message: {
6933
- sender: userOperation.sender,
6934
- nonce: userOperation.nonce,
6935
- initCode,
6936
- callData: userOperation.callData,
6937
- accountGasLimits,
6938
- preVerificationGas: userOperation.preVerificationGas,
6939
- gasFees,
6940
- paymasterAndData
6941
- }
6942
- };
7627
+ * @throws {AbstractionKitError} if the target EntryPoint is not v0.8 / v0.9.
7628
+ */
7629
+ static getUserOperationEip712Data(userOperation, chainId, overrides = {}) {
7630
+ return getUserOperationEip712DataV8V9(userOperation, overrides.entrypointAddress ?? "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108", chainId);
7631
+ }
7632
+ /**
7633
+ * Compute the EIP-712 digest of a UserOperation under the EntryPoint
7634
+ * v0.8 / v0.9 domain. For these EntryPoints this digest IS the
7635
+ * `userOpHash` ({@link createUserOperationHash}); signing it with raw
7636
+ * ECDSA or via `signTypedData` over the data from
7637
+ * {@link getUserOperationEip712Data} produces a signature that validates
7638
+ * against the same hash on-chain.
7639
+ *
7640
+ * @param userOperation - Unsigned UserOperation to hash
7641
+ * @param chainId - Target chain ID
7642
+ * @param overrides - Override the entrypoint address (defaults to EntryPoint v0.8)
7643
+ * @returns The EIP-712 digest as a hex string
7644
+ * @throws {AbstractionKitError} if the target EntryPoint is not v0.8 / v0.9.
7645
+ */
7646
+ static getUserOperationEip712Hash(userOperation, chainId, overrides = {}) {
7647
+ return getUserOperationEip712HashV8V9(userOperation, overrides.entrypointAddress ?? "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108", chainId);
6943
7648
  }
6944
7649
  /**
6945
7650
  * Sign a UserOperation with an {@link AkSigner}. The signer can implement
@@ -6965,7 +7670,7 @@ var abstractionkit = (function(exports, ethers) {
6965
7670
  };
6966
7671
  return invokeSigner(signer, scheme, {
6967
7672
  hash,
6968
- typedData: scheme === "typedData" ? this.getUserOperationEip712TypedData(useroperation, chainId) : void 0,
7673
+ typedData: scheme === "typedData" ? BaseSimple7702Account.getUserOperationEip712Data(useroperation, chainId, { entrypointAddress: this.entrypointAddress }) : void 0,
6969
7674
  context
6970
7675
  });
6971
7676
  }
@@ -6976,7 +7681,7 @@ var abstractionkit = (function(exports, ethers) {
6976
7681
  * @returns A {@link SendUseroperationResponse} that can be used to wait for inclusion
6977
7682
  */
6978
7683
  async baseSendUserOperation(userOperation, bundlerRpc) {
6979
- const bundler = new Bundler(bundlerRpc);
7684
+ const bundler = Bundler.from(bundlerRpc);
6980
7685
  return new SendUseroperationResponse(await bundler.sendUserOperation(userOperation, this.entrypointAddress), bundler, this.entrypointAddress);
6981
7686
  }
6982
7687
  /**
@@ -7104,8 +7809,8 @@ var abstractionkit = (function(exports, ethers) {
7104
7809
  * {@link signUserOperation} method, or wrap explicitly with
7105
7810
  * `fromPrivateKey(pk)`.
7106
7811
  *
7107
- * @see {@link BaseSimple7702Account.getUserOperationEip712TypedData} for
7108
- * the lower-level escape hatch when you need the typed data outside the
7812
+ * @see {@link BaseSimple7702Account.getUserOperationEip712Data} for the
7813
+ * lower-level escape hatch when you need the typed data outside the
7109
7814
  * dispatcher (e.g., to render a custom confirmation UI or feed an HSM
7110
7815
  * that doesn't fit the {@link ExternalSigner} shape).
7111
7816
  */
@@ -7133,6 +7838,22 @@ var abstractionkit = (function(exports, ethers) {
7133
7838
  var Simple7702AccountV09 = class Simple7702AccountV09 extends BaseSimple7702Account {
7134
7839
  static DEFAULT_DELEGATEE_ADDRESS = "0xa46cc63eBF4Bd77888AA327837d20b23A63a56B5";
7135
7840
  /**
7841
+ * Build the EIP-712 typed data payload for a {@link UserOperationV9}
7842
+ * under the EntryPoint v0.9 domain. See
7843
+ * {@link BaseSimple7702Account.getUserOperationEip712Data} for the
7844
+ * full semantics; this override just defaults `entrypointAddress` to v0.9.
7845
+ */
7846
+ static getUserOperationEip712Data(userOperation, chainId, overrides = {}) {
7847
+ return BaseSimple7702Account.getUserOperationEip712Data(userOperation, chainId, { entrypointAddress: overrides.entrypointAddress ?? "0x433709009B8330FDa32311DF1C2AFA402eD8D009" });
7848
+ }
7849
+ /**
7850
+ * Compute the EIP-712 digest of a {@link UserOperationV9} under the
7851
+ * EntryPoint v0.9 domain. Defaults `entrypointAddress` to v0.9.
7852
+ */
7853
+ static getUserOperationEip712Hash(userOperation, chainId, overrides = {}) {
7854
+ return BaseSimple7702Account.getUserOperationEip712Hash(userOperation, chainId, { entrypointAddress: overrides.entrypointAddress ?? "0x433709009B8330FDa32311DF1C2AFA402eD8D009" });
7855
+ }
7856
+ /**
7136
7857
  * @param accountAddress - The EOA address that will be delegated via EIP-7702
7137
7858
  * @param overrides - Optional overrides for entrypoint and delegatee addresses
7138
7859
  * @param overrides.entrypointAddress - Custom EntryPoint address (defaults to EntryPoint v0.9)
@@ -7202,8 +7923,8 @@ var abstractionkit = (function(exports, ethers) {
7202
7923
  * {@link signUserOperation} method, or wrap explicitly with
7203
7924
  * `fromPrivateKey(pk)`.
7204
7925
  *
7205
- * @see {@link BaseSimple7702Account.getUserOperationEip712TypedData} for
7206
- * the lower-level escape hatch when you need the typed data outside the
7926
+ * @see {@link BaseSimple7702Account.getUserOperationEip712Data} for the
7927
+ * lower-level escape hatch when you need the typed data outside the
7207
7928
  * dispatcher.
7208
7929
  */
7209
7930
  async signUserOperationWithSigner(useroperation, signer, chainId) {
@@ -7305,8 +8026,19 @@ var abstractionkit = (function(exports, ethers) {
7305
8026
  * const { userOperation: sponsoredOp } = await paymaster.createSponsorPaymasterUserOperation(userOp, bundlerRpcUrl);
7306
8027
  */
7307
8028
  var CandidePaymaster = class CandidePaymaster extends Paymaster {
7308
- /** The paymaster JSON-RPC endpoint URL */
7309
- rpcUrl;
8029
+ /**
8030
+ * The raw transport the user passed in (or {@link HttpTransport} when a URL
8031
+ * string was passed). Exposed for introspection — reading `.url`,
8032
+ * `isHttpTransport(...)` checks, passing it back into another service.
8033
+ *
8034
+ * Calls made directly on this field (`paymaster.transport.request(...)`)
8035
+ * go to the raw transport and skip SDK-level behavior like bigint param
8036
+ * normalization. For SDK-pipeline behavior, use
8037
+ * {@link CandidePaymaster.request} or the typed methods.
8038
+ */
8039
+ transport;
8040
+ /** Normalizing wrapper around {@link transport}, used for every SDK-outbound call. */
8041
+ outbound;
7310
8042
  /** Cached token/metadata per EntryPoint address (lowercase keys) */
7311
8043
  entrypointData = /* @__PURE__ */ new Map();
7312
8044
  /** Per-entrypoint initialization promises (lowercase keys) */
@@ -7314,11 +8046,30 @@ var abstractionkit = (function(exports, ethers) {
7314
8046
  /** Cached chain ID (hex string), resolved from URL or pm_chainId RPC */
7315
8047
  chainId = null;
7316
8048
  chainIdPromise = null;
7317
- /** @param rpcUrl - The Candide paymaster JSON-RPC endpoint URL */
7318
- constructor(rpcUrl) {
8049
+ /**
8050
+ * @param rpc - The Candide paymaster JSON-RPC endpoint URL, or any {@link Transport}.
8051
+ * When a URL string is passed, the chain id is inferred from the URL path
8052
+ * when possible; otherwise it's resolved lazily via `pm_chainId`.
8053
+ */
8054
+ constructor(rpc) {
7319
8055
  super();
7320
- this.rpcUrl = rpcUrl;
7321
- this.chainId = CandidePaymaster.extractChainIdFromUrl(rpcUrl);
8056
+ this.transport = typeof rpc === "string" ? new HttpTransport(rpc) : rpc;
8057
+ this.outbound = normalizingTransport(this.transport);
8058
+ this.chainId = typeof rpc === "string" ? CandidePaymaster.extractChainIdFromUrl(rpc) : null;
8059
+ }
8060
+ /**
8061
+ * Normalize any acceptable input into a `CandidePaymaster`. Returns the
8062
+ * input by reference when it's already a `CandidePaymaster`.
8063
+ */
8064
+ static from(input) {
8065
+ return input instanceof CandidePaymaster ? input : new CandidePaymaster(input);
8066
+ }
8067
+ /**
8068
+ * Transport delegate. Forwards directly to {@link Transport.request}, so a
8069
+ * `CandidePaymaster` can be slotted into any other transport position.
8070
+ */
8071
+ request(args, options) {
8072
+ return this.outbound.request(args, options);
7322
8073
  }
7323
8074
  /**
7324
8075
  * Extract chain ID from a Candide paymaster URL.
@@ -7346,7 +8097,7 @@ var abstractionkit = (function(exports, ethers) {
7346
8097
  }
7347
8098
  async fetchChainId() {
7348
8099
  try {
7349
- return await sendJsonRpcRequest(this.rpcUrl, "pm_chainId", []);
8100
+ return await this.outbound.request({ method: "pm_chainId" });
7350
8101
  } catch (err) {
7351
8102
  throw new AbstractionKitError("PAYMASTER_ERROR", "pm_chainId failed", { cause: ensureError(err) });
7352
8103
  }
@@ -7446,7 +8197,10 @@ var abstractionkit = (function(exports, ethers) {
7446
8197
  }
7447
8198
  }
7448
8199
  async fetchSupportedTokensRpc(entrypoint) {
7449
- return await sendJsonRpcRequest(this.rpcUrl, "pm_supportedERC20Tokens", [entrypoint]);
8200
+ return await this.outbound.request({
8201
+ method: "pm_supportedERC20Tokens",
8202
+ params: [entrypoint]
8203
+ });
7450
8204
  }
7451
8205
  /**
7452
8206
  * Get the EntryPoint addresses supported by this paymaster.
@@ -7455,7 +8209,7 @@ var abstractionkit = (function(exports, ethers) {
7455
8209
  */
7456
8210
  async getSupportedEntrypoints() {
7457
8211
  try {
7458
- return await sendJsonRpcRequest(this.rpcUrl, "pm_supportedEntryPoints", []);
8212
+ return await this.outbound.request({ method: "pm_supportedEntryPoints" });
7459
8213
  } catch (err) {
7460
8214
  throw new AbstractionKitError("PAYMASTER_ERROR", "pm_supportedEntryPoints failed", { cause: ensureError(err) });
7461
8215
  }
@@ -7522,7 +8276,7 @@ var abstractionkit = (function(exports, ethers) {
7522
8276
  let callGasLimit = userOp.callGasLimit;
7523
8277
  if (overrides.preVerificationGas == null || overrides.verificationGasLimit == null || overrides.callGasLimit == null) {
7524
8278
  if (bundlerRpc == null) throw new AbstractionKitError("BAD_DATA", "bundlerRpc can't be null if preVerificationGas,verificationGasLimit and callGasLimit are not overridden");
7525
- const bundler = new Bundler(bundlerRpc);
8279
+ const bundler = Bundler.from(bundlerRpc);
7526
8280
  userOp.callGasLimit = 0n;
7527
8281
  userOp.verificationGasLimit = 0n;
7528
8282
  userOp.preVerificationGas = 0n;
@@ -7570,12 +8324,15 @@ var abstractionkit = (function(exports, ethers) {
7570
8324
  try {
7571
8325
  const entrypoint = overrides.entrypoint ?? this.resolveEntrypoint(smartAccount, userOperation);
7572
8326
  const chainId = await this.getChainId();
7573
- const jsonRpcResult = await sendJsonRpcRequest(this.rpcUrl, "pm_getPaymasterData", [
7574
- userOperation,
7575
- entrypoint,
7576
- chainId,
7577
- context
7578
- ]);
8327
+ const jsonRpcResult = await this.outbound.request({
8328
+ method: "pm_getPaymasterData",
8329
+ params: [
8330
+ userOperation,
8331
+ entrypoint,
8332
+ chainId,
8333
+ context
8334
+ ]
8335
+ });
7579
8336
  return {
7580
8337
  userOperation,
7581
8338
  sponsorMetadata: this.applyPaymasterResult(userOperation, jsonRpcResult)
@@ -7759,8 +8516,19 @@ var abstractionkit = (function(exports, ethers) {
7759
8516
  */
7760
8517
  const CANDIDE_TOKEN_QUOTE_TTL_MS = 45e3;
7761
8518
  var Erc7677Paymaster = class Erc7677Paymaster extends Paymaster {
7762
- /** The paymaster JSON-RPC endpoint URL */
7763
- rpcUrl;
8519
+ /**
8520
+ * The raw transport the user passed in (or {@link HttpTransport} when a URL
8521
+ * string was passed). Exposed for introspection — reading `.url`,
8522
+ * `isHttpTransport(...)` checks, passing it back into another service.
8523
+ *
8524
+ * Calls made directly on this field (`paymaster.transport.request(...)`)
8525
+ * go to the raw transport and skip SDK-level behavior like bigint param
8526
+ * normalization. For SDK-pipeline behavior, use
8527
+ * {@link Erc7677Paymaster.request} or the typed methods.
8528
+ */
8529
+ transport;
8530
+ /** Normalizing wrapper around {@link transport}, used for every SDK-outbound call. */
8531
+ outbound;
7764
8532
  /** Cached chain ID (hex string). Passed via constructor or resolved from the bundler at first use. */
7765
8533
  chainId;
7766
8534
  /** Detected or explicitly set paymaster provider. `null` means no provider-specific features. */
@@ -7794,29 +8562,49 @@ var abstractionkit = (function(exports, ethers) {
7794
8562
  return null;
7795
8563
  }
7796
8564
  /**
7797
- * @param rpcUrl - Paymaster JSON-RPC endpoint. Can be the same URL as the
7798
- * bundler when the provider bundles both (Candide, Pimlico, Alchemy);
7799
- * can also be a separate paymaster-only endpoint.
8565
+ * @param rpc - Paymaster JSON-RPC endpoint URL, or any {@link Transport}.
8566
+ * Can be the same URL as the bundler when the provider bundles both
8567
+ * (Candide, Pimlico, Alchemy); can also be a separate paymaster-only
8568
+ * endpoint, or a fully custom transport.
7800
8569
  * @param options
7801
8570
  * @param options.chainId - Optional chain id as a bigint (e.g. `1n` for
7802
8571
  * mainnet). When provided, avoids a lookup at first use. Otherwise,
7803
8572
  * resolved from the bundler via `eth_chainId` on the first call.
7804
8573
  * @param options.provider - Paymaster provider. `"auto"` (default) detects
7805
- * from the RPC URL. Set explicitly to override, or `null` to disable.
8574
+ * from the RPC URL (only when a URL string is passed; non-string inputs
8575
+ * default to `null` unless overridden here). Set explicitly to override,
8576
+ * or `null` to disable provider-specific features.
7806
8577
  */
7807
- constructor(rpcUrl, options = {}) {
8578
+ constructor(rpc, options = {}) {
7808
8579
  super();
7809
- this.rpcUrl = rpcUrl;
8580
+ this.transport = typeof rpc === "string" ? new HttpTransport(rpc) : rpc;
8581
+ this.outbound = normalizingTransport(this.transport);
7810
8582
  this.chainId = options.chainId != null ? `0x${options.chainId.toString(16)}` : null;
7811
- if (options.provider === void 0 || options.provider === "auto") this.provider = Erc7677Paymaster.detectProvider(rpcUrl);
8583
+ if (options.provider === void 0 || options.provider === "auto") this.provider = typeof rpc === "string" ? Erc7677Paymaster.detectProvider(rpc) : null;
7812
8584
  else this.provider = options.provider;
7813
8585
  }
7814
8586
  /**
8587
+ * Normalize any acceptable input into an `Erc7677Paymaster`. Returns the
8588
+ * input by reference when it's already an `Erc7677Paymaster` (in which
8589
+ * case the second `options` argument is ignored — the existing instance's
8590
+ * configuration is preserved).
8591
+ */
8592
+ static from(input, options) {
8593
+ return input instanceof Erc7677Paymaster ? input : new Erc7677Paymaster(input, options);
8594
+ }
8595
+ /**
8596
+ * Transport delegate. Forwards directly to {@link Transport.request}, so
8597
+ * an `Erc7677Paymaster` can be slotted into any other transport position.
8598
+ */
8599
+ request(args, options) {
8600
+ return this.outbound.request(args, options);
8601
+ }
8602
+ /**
7815
8603
  * Resolve the chain id, querying the bundler if not provided at construction.
7816
8604
  */
7817
8605
  async getChainId(bundlerRpc) {
7818
8606
  if (this.chainId != null) return this.chainId;
7819
- const id = await new Bundler(bundlerRpc).chainId();
8607
+ const id = await Bundler.from(bundlerRpc).chainId();
7820
8608
  this.chainId = id;
7821
8609
  return id;
7822
8610
  }
@@ -7840,12 +8628,15 @@ var abstractionkit = (function(exports, ethers) {
7840
8628
  */
7841
8629
  async getPaymasterStubData(userOperation, entrypoint, chainIdHex, context = {}) {
7842
8630
  try {
7843
- return await sendJsonRpcRequest(this.rpcUrl, "pm_getPaymasterStubData", [
7844
- userOperation,
7845
- entrypoint,
7846
- chainIdHex,
7847
- context
7848
- ]);
8631
+ return await this.outbound.request({
8632
+ method: "pm_getPaymasterStubData",
8633
+ params: [
8634
+ userOperation,
8635
+ entrypoint,
8636
+ chainIdHex,
8637
+ context
8638
+ ]
8639
+ });
7849
8640
  } catch (err) {
7850
8641
  throw new AbstractionKitError("PAYMASTER_ERROR", "pm_getPaymasterStubData failed", { cause: ensureError(err) });
7851
8642
  }
@@ -7856,12 +8647,15 @@ var abstractionkit = (function(exports, ethers) {
7856
8647
  */
7857
8648
  async getPaymasterData(userOperation, entrypoint, chainIdHex, context = {}) {
7858
8649
  try {
7859
- return await sendJsonRpcRequest(this.rpcUrl, "pm_getPaymasterData", [
7860
- userOperation,
7861
- entrypoint,
7862
- chainIdHex,
7863
- context
7864
- ]);
8650
+ return await this.outbound.request({
8651
+ method: "pm_getPaymasterData",
8652
+ params: [
8653
+ userOperation,
8654
+ entrypoint,
8655
+ chainIdHex,
8656
+ context
8657
+ ]
8658
+ });
7865
8659
  } catch (err) {
7866
8660
  throw new AbstractionKitError("PAYMASTER_ERROR", "pm_getPaymasterData failed", { cause: ensureError(err) });
7867
8661
  }
@@ -7876,7 +8670,10 @@ var abstractionkit = (function(exports, ethers) {
7876
8670
  */
7877
8671
  async sendRPCRequest(method, params = []) {
7878
8672
  try {
7879
- return await sendJsonRpcRequest(this.rpcUrl, method, params);
8673
+ return await this.outbound.request({
8674
+ method,
8675
+ params
8676
+ });
7880
8677
  } catch (err) {
7881
8678
  throw new AbstractionKitError("PAYMASTER_ERROR", `sendRPCRequest(${method}) failed`, { cause: ensureError(err) });
7882
8679
  }
@@ -7938,7 +8735,7 @@ var abstractionkit = (function(exports, ethers) {
7938
8735
  let callGasLimit = userOp.callGasLimit;
7939
8736
  if (overrides.preVerificationGas == null || overrides.verificationGasLimit == null || overrides.callGasLimit == null) {
7940
8737
  if (bundlerRpc == null) throw new AbstractionKitError("BAD_DATA", "bundlerRpc can't be null if preVerificationGas, verificationGasLimit and callGasLimit are not overridden");
7941
- const bundler = new Bundler(bundlerRpc);
8738
+ const bundler = Bundler.from(bundlerRpc);
7942
8739
  userOp.callGasLimit = 0n;
7943
8740
  userOp.verificationGasLimit = 0n;
7944
8741
  userOp.preVerificationGas = 0n;
@@ -7978,11 +8775,14 @@ var abstractionkit = (function(exports, ethers) {
7978
8775
  * approval amount via `(exchangeRate * gasCostWei) / 10^18`.
7979
8776
  */
7980
8777
  async fetchPimlicoTokenQuote(tokenAddress, entrypoint, chainIdHex) {
7981
- const quotes = (await sendJsonRpcRequest(this.rpcUrl, "pimlico_getTokenQuotes", [
7982
- { tokens: [tokenAddress] },
7983
- entrypoint,
7984
- chainIdHex
7985
- ]))?.quotes;
8778
+ const quotes = (await this.outbound.request({
8779
+ method: "pimlico_getTokenQuotes",
8780
+ params: [
8781
+ { tokens: [tokenAddress] },
8782
+ entrypoint,
8783
+ chainIdHex
8784
+ ]
8785
+ }))?.quotes;
7986
8786
  if (!Array.isArray(quotes) || quotes.length === 0) throw new AbstractionKitError("PAYMASTER_ERROR", `pimlico_getTokenQuotes returned no quotes for token ${tokenAddress}`);
7987
8787
  const quote = quotes.find((q) => q.token.toLowerCase() === tokenAddress.toLowerCase());
7988
8788
  if (quote == null) throw new AbstractionKitError("PAYMASTER_ERROR", `pimlico_getTokenQuotes did not include token ${tokenAddress}`);
@@ -8008,7 +8808,10 @@ var abstractionkit = (function(exports, ethers) {
8008
8808
  const cached = this.candideCache.get(key);
8009
8809
  const isStale = cached != null && options.enforceTTL === true && Date.now() - cached.fetchedAt > CANDIDE_TOKEN_QUOTE_TTL_MS;
8010
8810
  if (cached != null && !isStale) return cached.data;
8011
- const result = await sendJsonRpcRequest(this.rpcUrl, "pm_supportedERC20Tokens", [entrypoint]);
8811
+ const result = await this.outbound.request({
8812
+ method: "pm_supportedERC20Tokens",
8813
+ params: [entrypoint]
8814
+ });
8012
8815
  this.candideCache.set(key, {
8013
8816
  data: result,
8014
8817
  fetchedAt: Date.now()
@@ -8185,7 +8988,7 @@ var abstractionkit = (function(exports, ethers) {
8185
8988
  let verificationGasLimit = userOperation.verificationGasLimit;
8186
8989
  let callGasLimit = userOperation.callGasLimit;
8187
8990
  userOperation.preVerificationGas = 0n;
8188
- const estimation = await new Bundler(bundlerRpc).estimateUserOperationGas(userOperation, entrypointAddress, overrides.state_override_set);
8991
+ const estimation = await Bundler.from(bundlerRpc).estimateUserOperationGas(userOperation, entrypointAddress, overrides.state_override_set);
8189
8992
  if (preVerificationGas < estimation.preVerificationGas) preVerificationGas = estimation.preVerificationGas;
8190
8993
  if (verificationGasLimit < estimation.verificationGasLimit) verificationGasLimit = estimation.verificationGasLimit;
8191
8994
  if (callGasLimit < estimation.callGasLimit) callGasLimit = estimation.callGasLimit;
@@ -8570,6 +9373,7 @@ var abstractionkit = (function(exports, ethers) {
8570
9373
  ALLOWANCE_MODULE_V0_1_0_ADDRESS: () => ALLOWANCE_MODULE_V0_1_0_ADDRESS,
8571
9374
  AbstractionKitError: () => AbstractionKitError,
8572
9375
  AllowanceModule: () => AllowanceModule,
9376
+ BaseRpcTransport: () => BaseRpcTransport,
8573
9377
  BaseUserOperationDummyValues: () => BaseUserOperationDummyValues,
8574
9378
  Bundler: () => Bundler,
8575
9379
  CALIBUR_CANDIDE_V0_1_0_SINGLETON_ADDRESS: () => CALIBUR_CANDIDE_V0_1_0_SINGLETON_ADDRESS,
@@ -8593,6 +9397,8 @@ var abstractionkit = (function(exports, ethers) {
8593
9397
  Erc7677Paymaster: () => Erc7677Paymaster,
8594
9398
  ExperimentalAllowAllParallelPaymaster: () => ExperimentalAllowAllParallelPaymaster,
8595
9399
  GasOption: () => GasOption,
9400
+ HttpTransport: () => HttpTransport,
9401
+ JsonRpcNode: () => JsonRpcNode,
8596
9402
  Operation: () => Operation,
8597
9403
  PolygonChain: () => PolygonChain,
8598
9404
  SAFE_MESSAGE_MODULE_TYPE: () => SAFE_MESSAGE_MODULE_TYPE,
@@ -8610,6 +9416,7 @@ var abstractionkit = (function(exports, ethers) {
8610
9416
  SmartAccountFactory: () => SmartAccountFactory,
8611
9417
  SocialRecoveryModule: () => SocialRecoveryModule,
8612
9418
  SocialRecoveryModuleGracePeriodSelector: () => SocialRecoveryModuleGracePeriodSelector,
9419
+ TransportRpcError: () => TransportRpcError,
8613
9420
  WebauthnDummySignerSignaturePair: () => WebauthnDummySignerSignaturePair,
8614
9421
  WorldIdPermissionlessPaymaster: () => WorldIdPermissionlessPaymaster,
8615
9422
  ZeroAddress: () => ZeroAddress,
@@ -8624,17 +9431,15 @@ var abstractionkit = (function(exports, ethers) {
8624
9431
  createUserOperationHash: () => createUserOperationHash,
8625
9432
  createWorldIdSignal: () => createWorldIdSignal,
8626
9433
  fetchAccountNonce: () => fetchAccountNonce,
8627
- fetchGasPrice: () => fetchGasPrice,
8628
9434
  fromEthersWallet: () => fromEthersWallet,
8629
9435
  fromPrivateKey: () => fromPrivateKey,
8630
9436
  fromSafeWebauthn: () => fromSafeWebauthn,
8631
9437
  fromViem: () => fromViem,
8632
9438
  fromViemWalletClient: () => fromViemWalletClient,
8633
- getBalanceOf: () => getBalanceOf,
8634
- getDelegatedAddress: () => getDelegatedAddress,
8635
- getDepositInfo: () => getDepositInfo,
8636
9439
  getFunctionSelector: () => getFunctionSelector,
8637
9440
  getSafeMessageEip712Data: () => getSafeMessageEip712Data,
9441
+ isEventfulTransport: () => isEventfulTransport,
9442
+ isHttpTransport: () => isHttpTransport,
8638
9443
  pubkeyCoordinatesFromJson: () => pubkeyCoordinatesFromJson,
8639
9444
  pubkeyCoordinatesToJson: () => pubkeyCoordinatesToJson,
8640
9445
  sendJsonRpcRequest: () => sendJsonRpcRequest,
@@ -8652,6 +9457,7 @@ var abstractionkit = (function(exports, ethers) {
8652
9457
  exports.ALLOWANCE_MODULE_V0_1_0_ADDRESS = ALLOWANCE_MODULE_V0_1_0_ADDRESS;
8653
9458
  exports.AbstractionKitError = AbstractionKitError;
8654
9459
  exports.AllowanceModule = AllowanceModule;
9460
+ exports.BaseRpcTransport = BaseRpcTransport;
8655
9461
  exports.BaseUserOperationDummyValues = BaseUserOperationDummyValues;
8656
9462
  exports.Bundler = Bundler;
8657
9463
  exports.CALIBUR_CANDIDE_V0_1_0_SINGLETON_ADDRESS = CALIBUR_CANDIDE_V0_1_0_SINGLETON_ADDRESS;
@@ -8675,6 +9481,8 @@ var abstractionkit = (function(exports, ethers) {
8675
9481
  exports.Erc7677Paymaster = Erc7677Paymaster;
8676
9482
  exports.ExperimentalAllowAllParallelPaymaster = ExperimentalAllowAllParallelPaymaster;
8677
9483
  exports.GasOption = GasOption;
9484
+ exports.HttpTransport = HttpTransport;
9485
+ exports.JsonRpcNode = JsonRpcNode;
8678
9486
  exports.Operation = Operation;
8679
9487
  exports.PolygonChain = PolygonChain;
8680
9488
  exports.SAFE_MESSAGE_MODULE_TYPE = SAFE_MESSAGE_MODULE_TYPE;
@@ -8692,6 +9500,7 @@ var abstractionkit = (function(exports, ethers) {
8692
9500
  exports.SmartAccountFactory = SmartAccountFactory;
8693
9501
  exports.SocialRecoveryModule = SocialRecoveryModule;
8694
9502
  exports.SocialRecoveryModuleGracePeriodSelector = SocialRecoveryModuleGracePeriodSelector;
9503
+ exports.TransportRpcError = TransportRpcError;
8695
9504
  exports.WebauthnDummySignerSignaturePair = WebauthnDummySignerSignaturePair;
8696
9505
  exports.WorldIdPermissionlessPaymaster = WorldIdPermissionlessPaymaster;
8697
9506
  exports.ZeroAddress = ZeroAddress;
@@ -8712,17 +9521,15 @@ var abstractionkit = (function(exports, ethers) {
8712
9521
  exports.createUserOperationHash = createUserOperationHash;
8713
9522
  exports.createWorldIdSignal = createWorldIdSignal;
8714
9523
  exports.fetchAccountNonce = fetchAccountNonce;
8715
- exports.fetchGasPrice = fetchGasPrice;
8716
9524
  exports.fromEthersWallet = fromEthersWallet;
8717
9525
  exports.fromPrivateKey = fromPrivateKey;
8718
9526
  exports.fromSafeWebauthn = fromSafeWebauthn;
8719
9527
  exports.fromViem = fromViem;
8720
9528
  exports.fromViemWalletClient = fromViemWalletClient;
8721
- exports.getBalanceOf = getBalanceOf;
8722
- exports.getDelegatedAddress = getDelegatedAddress;
8723
- exports.getDepositInfo = getDepositInfo;
8724
9529
  exports.getFunctionSelector = getFunctionSelector;
8725
9530
  exports.getSafeMessageEip712Data = getSafeMessageEip712Data;
9531
+ exports.isEventfulTransport = isEventfulTransport;
9532
+ exports.isHttpTransport = isHttpTransport;
8726
9533
  exports.pubkeyCoordinatesFromJson = pubkeyCoordinatesFromJson;
8727
9534
  exports.pubkeyCoordinatesToJson = pubkeyCoordinatesToJson;
8728
9535
  exports.sendJsonRpcRequest = sendJsonRpcRequest;