abstractionkit 0.3.5 → 0.3.8

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