abstractionkit 0.3.1 → 0.3.2

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.
@@ -805,6 +805,52 @@ var abstractionkit = (function(exports, ethers) {
805
805
  return [maxFeePerGas, maxPriorityFeePerGas];
806
806
  }
807
807
  //#endregion
808
+ //#region src/signer/negotiate.ts
809
+ /**
810
+ * Pick the best mutually-supported signing scheme for one signer against an
811
+ * account's accepted schemes. Later in the `accepted` array = lower preference;
812
+ * the account ranks by preference.
813
+ *
814
+ * Throws a detailed {@link AbstractionKitError} if no scheme overlaps,
815
+ * citing the signer's address, what the account accepts, and what the
816
+ * signer can do.
817
+ */
818
+ function pickScheme(signer, accepted, context) {
819
+ const signerCan = [];
820
+ if (typeof signer.signTypedData === "function") signerCan.push("typedData");
821
+ if (typeof signer.signHash === "function") signerCan.push("hash");
822
+ for (const scheme of accepted) if (signerCan.includes(scheme)) return scheme;
823
+ throw new AbstractionKitError("BAD_DATA", buildMismatchMessage({
824
+ accountName: context.accountName,
825
+ signerIndex: context.signerIndex,
826
+ signerAddress: signer.address,
827
+ accepted,
828
+ signerCan
829
+ }));
830
+ }
831
+ function buildMismatchMessage(params) {
832
+ const { accountName, signerIndex, signerAddress, accepted, signerCan } = params;
833
+ const canStr = signerCan.length > 0 ? signerCan.join(", ") : "none";
834
+ return `No compatible signing scheme for signer[${signerIndex}] ${signerAddress}. ${accountName} accepts: [${accepted.join(", ")}]; signer provides: [${canStr}]. ` + (signerCan.length === 0 ? "Signer must implement at least one of `signHash` or `signTypedData`. " : "") + "Hint: `fromViem` / `fromEthersWallet` give both; `fromViemWalletClient` gives only `typedData` (use Safe for JSON-RPC wallets).";
835
+ }
836
+ /**
837
+ * Invoke a signer for one scheme. Keeps the dispatch in one place so the
838
+ * account-side code stays linear. `typedData` is optional: accounts that
839
+ * only accept the `"hash"` scheme (Simple7702, Calibur) pass just `hash`.
840
+ *
841
+ * `context` is always forwarded to the signer so power-user implementations
842
+ * can inspect the userOp.
843
+ */
844
+ async function invokeSigner(signer, scheme, payload) {
845
+ if (scheme === "typedData") {
846
+ if (typeof signer.signTypedData !== "function") throw new AbstractionKitError("BAD_DATA", `signer ${signer.address} is missing signTypedData`);
847
+ if (!payload.typedData) throw new AbstractionKitError("BAD_DATA", `scheme "typedData" selected but no typedData payload provided`);
848
+ return signer.signTypedData(payload.typedData, payload.context);
849
+ }
850
+ if (typeof signer.signHash !== "function") throw new AbstractionKitError("BAD_DATA", `signer ${signer.address} is missing signHash`);
851
+ return signer.signHash(payload.hash, payload.context);
852
+ }
853
+ //#endregion
808
854
  //#region src/utils7702.ts
809
855
  const SET_CODE_TX_TYPE = "0x04";
810
856
  /**
@@ -1582,6 +1628,30 @@ var abstractionkit = (function(exports, ethers) {
1582
1628
  return new ethers.Wallet(privateKey).signingKey.sign(userOperationHash).serialized;
1583
1629
  }
1584
1630
  /**
1631
+ * Schemes Simple7702 accepts from a Signer. Only raw-hash ECDSA, since
1632
+ * the delegatee verifies a plain signature over the userOp hash.
1633
+ */
1634
+ static ACCEPTED_SIGNING_SCHEMES = ["hash"];
1635
+ /**
1636
+ * Sign a UserOperation with an {@link AkSigner}. Signer must implement
1637
+ * `signHash`, since Simple7702 only verifies raw ECDSA over the userOp
1638
+ * hash. JSON-RPC wallets and anything that only provides `signTypedData`
1639
+ * fail offline with a specific error.
1640
+ */
1641
+ async baseSignUserOperationWithSigner(useroperation, signer, chainId) {
1642
+ return invokeSigner(signer, pickScheme(signer, BaseSimple7702Account.ACCEPTED_SIGNING_SCHEMES, {
1643
+ accountName: "Simple7702 (raw ECDSA over userOpHash)",
1644
+ signerIndex: 0
1645
+ }), {
1646
+ hash: createUserOperationHash(useroperation, this.entrypointAddress, chainId),
1647
+ context: {
1648
+ userOperation: useroperation,
1649
+ chainId,
1650
+ entryPoint: this.entrypointAddress
1651
+ }
1652
+ });
1653
+ }
1654
+ /**
1585
1655
  * Submit a signed UserOperation to a bundler for on-chain inclusion.
1586
1656
  * @param userOperation - The signed UserOperation to submit
1587
1657
  * @param bundlerRpc - Bundler RPC endpoint
@@ -1692,6 +1762,18 @@ var abstractionkit = (function(exports, ethers) {
1692
1762
  return this.baseSignUserOperation(useroperation, privateKey, chainId);
1693
1763
  }
1694
1764
  /**
1765
+ * Sign a {@link UserOperationV8} using an {@link ExternalSigner}.
1766
+ * Simple7702 only accepts raw-hash ECDSA; signers without `signHash`
1767
+ * fail offline with an actionable error.
1768
+ *
1769
+ * For signing with a raw private-key string, use the sync
1770
+ * {@link signUserOperation} method, or wrap explicitly with
1771
+ * `fromPrivateKey(pk)`.
1772
+ */
1773
+ async signUserOperationWithSigner(useroperation, signer, chainId) {
1774
+ return this.baseSignUserOperationWithSigner(useroperation, signer, chainId);
1775
+ }
1776
+ /**
1695
1777
  * Send a signed {@link UserOperationV8} to a bundler for on-chain inclusion.
1696
1778
  * @param userOperation - The signed UserOperation to submit
1697
1779
  * @param bundlerRpc - Bundler RPC endpoint
@@ -1757,6 +1839,15 @@ var abstractionkit = (function(exports, ethers) {
1757
1839
  return this.baseSignUserOperation(useroperation, privateKey, chainId);
1758
1840
  }
1759
1841
  /**
1842
+ * Sign a {@link UserOperationV9} using an {@link ExternalSigner}.
1843
+ * Simple7702 only accepts raw-hash ECDSA; signers without `signHash`
1844
+ * fail offline with an actionable error. For a raw pk string, use the
1845
+ * sync {@link signUserOperation} method or wrap with `fromPrivateKey`.
1846
+ */
1847
+ async signUserOperationWithSigner(useroperation, signer, chainId) {
1848
+ return this.baseSignUserOperationWithSigner(useroperation, signer, chainId);
1849
+ }
1850
+ /**
1760
1851
  * Send a signed {@link UserOperationV9} to a bundler for on-chain inclusion.
1761
1852
  * @param userOperation - The signed UserOperation to submit
1762
1853
  * @param bundlerRpc - Bundler RPC endpoint
@@ -3161,6 +3252,68 @@ var abstractionkit = (function(exports, ethers) {
3161
3252
  });
3162
3253
  }
3163
3254
  /**
3255
+ * Schemes Safe accepts from a {@link Signer}, in preference order.
3256
+ * `typedData` is preferred because wallets can display structured fields
3257
+ * rather than a hex blob; `hash` is accepted as a fallback for signers
3258
+ * that only support raw ECDSA.
3259
+ */
3260
+ static ACCEPTED_SIGNING_SCHEMES = ["typedData", "hash"];
3261
+ /**
3262
+ * Sign a UserOperation using one or more {@link Signer}s. This is the
3263
+ * capability-oriented signing path: each signer declares what it can do
3264
+ * (`signHash`, `signTypedData`, both) and the account picks the best
3265
+ * match per signer. Incompatible signers fail offline with an actionable
3266
+ * error rather than a silent bundler rejection.
3267
+ *
3268
+ * Signers are invoked in parallel. For interactive wallets that share a
3269
+ * popup session, sequence the prompts inside your Signer implementation.
3270
+ *
3271
+ * @param useroperation - UserOperation to sign
3272
+ * @param signers - Signer instances (`fromViem(account)`, `fromEthersWallet(wallet)`, etc.)
3273
+ * @param chainId - target chain id
3274
+ * @param entrypointAddress - target EntryPoint
3275
+ * @param safe4337ModuleAddress - Safe 4337 module
3276
+ * @param overrides - optional validAfter / validUntil / multi-chain flag
3277
+ * @returns formatted signature
3278
+ */
3279
+ static async baseSignUserOperationWithSigners(useroperation, signers, chainId, entrypointAddress, safe4337ModuleAddress, context, overrides = {}) {
3280
+ const validAfter = overrides.validAfter ?? 0n;
3281
+ const validUntil = overrides.validUntil ?? 0n;
3282
+ if (signers.length < 1) throw new RangeError("There should be at least one signer");
3283
+ const typedDataRaw = SafeAccount.getUserOperationEip712Data(useroperation, chainId, {
3284
+ validAfter,
3285
+ validUntil,
3286
+ entrypointAddress,
3287
+ safe4337ModuleAddress
3288
+ });
3289
+ const userOpHash = ethers.TypedDataEncoder.hash(typedDataRaw.domain, typedDataRaw.types, typedDataRaw.messageValue);
3290
+ const { EIP712Domain: _drop, ...primaryTypes } = typedDataRaw.types;
3291
+ const typedData = {
3292
+ domain: typedDataRaw.domain,
3293
+ types: primaryTypes,
3294
+ primaryType: EIP712_SAFE_OPERATION_PRIMARY_TYPE,
3295
+ message: typedDataRaw.messageValue
3296
+ };
3297
+ const normalizedAddresses = signers.map((signer) => (0, ethers.getAddress)(signer.address));
3298
+ const schemes = signers.map((signer, signerIndex) => pickScheme(signer, SafeAccount.ACCEPTED_SIGNING_SCHEMES, {
3299
+ accountName: "Safe (EIP-712 or raw hash over SafeOp digest)",
3300
+ signerIndex
3301
+ }));
3302
+ const signerSignaturePairs = (await Promise.all(signers.map((signer, i) => invokeSigner(signer, schemes[i], {
3303
+ hash: userOpHash,
3304
+ typedData,
3305
+ context
3306
+ })))).map((signature, i) => ({
3307
+ signer: normalizedAddresses[i],
3308
+ signature
3309
+ }));
3310
+ return SafeAccount.formatSignaturesToUseroperationSignature(signerSignaturePairs, {
3311
+ validAfter,
3312
+ validUntil,
3313
+ isMultiChainSignature: overrides.isMultiChainSignature
3314
+ });
3315
+ }
3316
+ /**
3164
3317
  * compute the deterministic address for a webauthn proxy verifier based on a
3165
3318
  * webauthn public key(x, y)
3166
3319
  * @param x - webauthn public key x parameter
@@ -3807,7 +3960,7 @@ var abstractionkit = (function(exports, ethers) {
3807
3960
  * @param toolVersion - tool version, defaults to current abstractionkit version
3808
3961
  * @returns the onchain idenetifier as a hex string (not 0x prefixed)
3809
3962
  */
3810
- function generateOnChainIdentifier(project, platform = "Web", tool = "abstractionkit", toolVersion = "0.3.1") {
3963
+ function generateOnChainIdentifier(project, platform = "Web", tool = "abstractionkit", toolVersion = "0.3.2") {
3811
3964
  const identifierPrefix = "5afe";
3812
3965
  const identifierVersion = "00";
3813
3966
  const projectHash = (0, ethers.keccak256)("0x" + Buffer.from(project, "utf8").toString("hex")).slice(-20);
@@ -3884,6 +4037,17 @@ var abstractionkit = (function(exports, ethers) {
3884
4037
  * with the Daimo P256 verifier instead of the FCL P256 verifier
3885
4038
  * used by the base SafeAccount class.
3886
4039
  * @see {@link https://github.com/safe-fndn/safe-modules/blob/04e65efbce634e776cc8c1fbe90061f09e09a71b/modules/passkey/CHANGELOG.md?plain=1#L23}
4040
+ *
4041
+ * @remarks Signer typing on this class is asymmetric:
4042
+ * - {@link signUserOperationWithSigners} (singular Operation) signs one op
4043
+ * and uses {@link SignContext} like every other account.
4044
+ * - {@link signUserOperationsWithSigners} (plural Operations) signs a bundle
4045
+ * under one signature and uses {@link MultiOpSignContext}.
4046
+ *
4047
+ * To author one signer that works on both methods, type it as
4048
+ * `ExternalSigner<unknown>` (the shape returned by the built-in adapters).
4049
+ * The two narrow context types exist so signers that DO read the context
4050
+ * get accurate, non-optional fields per path.
3887
4051
  */
3888
4052
  var SafeMultiChainSigAccountV1 = class SafeMultiChainSigAccountV1 extends SafeAccount {
3889
4053
  static DEFAULT_ENTRYPOINT_ADDRESS = ENTRYPOINT_V9;
@@ -4102,6 +4266,23 @@ var abstractionkit = (function(exports, ethers) {
4102
4266
  });
4103
4267
  }
4104
4268
  /**
4269
+ * Sign a single UserOperation for multi-chain using one or more
4270
+ * {@link AkSigner} instances. See
4271
+ * {@link SafeAccountV0_3_0.signUserOperationWithSigners} for the full
4272
+ * design rationale. Sets the multi-chain flag automatically.
4273
+ */
4274
+ signUserOperationWithSigners(userOperation, signers, chainId, overrides = {}) {
4275
+ const context = {
4276
+ userOperation,
4277
+ chainId,
4278
+ entryPoint: this.entrypointAddress
4279
+ };
4280
+ return SafeAccount.baseSignUserOperationWithSigners(userOperation, signers, chainId, this.entrypointAddress, this.safe4337ModuleAddress, context, {
4281
+ ...overrides,
4282
+ isMultiChainSignature: true
4283
+ });
4284
+ }
4285
+ /**
4105
4286
  * sign a list of useroperations - multi chain signature
4106
4287
  * @param useroperation - useroperation to sign
4107
4288
  * @param privateKeys - for the signers
@@ -4152,6 +4333,86 @@ var abstractionkit = (function(exports, ethers) {
4152
4333
  })];
4153
4334
  }
4154
4335
  /**
4336
+ * Sign a list of UserOperations with a single multi-chain signature,
4337
+ * using {@link AkSigner} instances typed for {@link MultiOpSignContext}
4338
+ * (viem, ethers, hardware wallet, HSM, MPC, Uint8Array-only). Each
4339
+ * signer signs the Merkle root of the UserOperation EIP-712 hashes via
4340
+ * raw-hash signing. `signTypedData` isn't exposed here because the
4341
+ * Merkle root is opaque and has no meaningful typed-data display.
4342
+ *
4343
+ * Signers always receive {@link MultiOpSignContext} regardless of bundle
4344
+ * length, so multi-op-typed signers can rely on `ctx.userOperations`
4345
+ * being defined. Pre-built adapters `fromPrivateKey`, `fromViem`, and
4346
+ * `fromEthersWallet` return a universal `Signer<unknown>` and work
4347
+ * here without retyping; user-defined single-op signers
4348
+ * (`Signer<SignContext>`) do not — they would receive a context shape
4349
+ * they didn't declare. `fromViemWalletClient` is **not** usable on the
4350
+ * multi-op Merkle path: it only exposes `signTypedData`, and the
4351
+ * Merkle root has no meaningful typed-data display. {@link pickScheme}
4352
+ * rejects it offline with an actionable error.
4353
+ *
4354
+ * @param userOperationsToSign - UserOperations + chain IDs + validity windows
4355
+ * @param signers - one Signer per owner (any order; sorted by address on-chain)
4356
+ * @returns one signature per input UserOperation, in the same order
4357
+ */
4358
+ async signUserOperationsWithSigners(userOperationsToSign, signers) {
4359
+ if (userOperationsToSign.length < 1) throw new RangeError("There should be at least one userOperationsToSign");
4360
+ if (signers.length < 1) throw new RangeError("There should be at least one signer");
4361
+ const context = {
4362
+ userOperations: userOperationsToSign.map((u) => ({
4363
+ userOperation: u.userOperation,
4364
+ chainId: u.chainId
4365
+ })),
4366
+ entryPoint: this.entrypointAddress
4367
+ };
4368
+ if (userOperationsToSign.length > 1) {
4369
+ const userOperationsHashes = [];
4370
+ userOperationsToSign.forEach((uopToSign) => {
4371
+ const userOperationHash = SafeAccount.getUserOperationEip712Hash_V9(uopToSign.userOperation, uopToSign.chainId, {
4372
+ validAfter: uopToSign.validAfter,
4373
+ validUntil: uopToSign.validUntil,
4374
+ safe4337ModuleAddress: this.safe4337ModuleAddress,
4375
+ entrypointAddress: this.entrypointAddress
4376
+ });
4377
+ userOperationsHashes.push(userOperationHash);
4378
+ });
4379
+ const [root, proofs] = generateMerkleProofs(userOperationsHashes);
4380
+ const merkleTreeRootHash = ethers.TypedDataEncoder.hash({ verifyingContract: this.safe4337ModuleAddress }, EIP712_MULTI_CHAIN_OPERATIONS_TYPE, { merkleTreeRoot: root });
4381
+ const normalizedAddresses = signers.map((signer) => (0, ethers.getAddress)(signer.address));
4382
+ signers.forEach((signer, i) => {
4383
+ pickScheme(signer, ["hash"], {
4384
+ accountName: "SafeMultiChainSigAccountV1 (multi-op Merkle root)",
4385
+ signerIndex: i
4386
+ });
4387
+ });
4388
+ const signatures = await Promise.all(signers.map((signer) => invokeSigner(signer, "hash", {
4389
+ hash: merkleTreeRootHash,
4390
+ context
4391
+ })));
4392
+ const signerSignaturePairs = signers.map((_signer, i) => ({
4393
+ signer: normalizedAddresses[i],
4394
+ signature: signatures[i]
4395
+ }));
4396
+ const userOpSignatures = [];
4397
+ userOperationsToSign.forEach((uopToSign, index) => {
4398
+ userOpSignatures.push(SafeAccount.formatSignaturesToUseroperationSignature(signerSignaturePairs, {
4399
+ validAfter: uopToSign.validAfter,
4400
+ validUntil: uopToSign.validUntil,
4401
+ isMultiChainSignature: true,
4402
+ multiChainMerkleProof: proofs[index]
4403
+ }));
4404
+ });
4405
+ return userOpSignatures;
4406
+ } else {
4407
+ const u = userOperationsToSign[0];
4408
+ return [await SafeAccount.baseSignUserOperationWithSigners(u.userOperation, signers, u.chainId, this.entrypointAddress, this.safe4337ModuleAddress, context, {
4409
+ validAfter: u.validAfter,
4410
+ validUntil: u.validUntil,
4411
+ isMultiChainSignature: true
4412
+ })];
4413
+ }
4414
+ }
4415
+ /**
4155
4416
  * Compute the EIP-712 hash of a multi-chain Merkle tree root for a set of UserOperations.
4156
4417
  * This hash is what signers sign to approve multiple cross-chain operations at once.
4157
4418
  * @param userOperationsToSignsToSign - list of UserOperations with their target chain IDs
@@ -4628,34 +4889,42 @@ var abstractionkit = (function(exports, ethers) {
4628
4889
  return Calibur7702Account.wrapSignature(keyHash, ecdsaSig, hookData);
4629
4890
  }
4630
4891
  /**
4631
- * Sign a UserOperation with an external signer (viem, ethers Signer,
4632
- * hardware wallet, MPC signer, etc.).
4633
- * Computes the UserOperation hash and wraps the returned signature in
4634
- * Calibur's format: `abi.encode(keyHash, ecdsaSig, hookData)`.
4635
- *
4636
- * By default signs with the root key. To sign with a registered
4637
- * secondary key, pass its key hash via `overrides.keyHash`.
4892
+ * Schemes Calibur accepts from a Signer. Only raw-hash ECDSA, since
4893
+ * the account verifies a plain signature over the userOp hash, then
4894
+ * wraps with `(keyHash, signature, hookData)`.
4895
+ */
4896
+ static ACCEPTED_SIGNING_SCHEMES = ["hash"];
4897
+ /**
4898
+ * Sign a UserOperation using an {@link ExternalSigner}. Calibur only
4899
+ * accepts raw-hash ECDSA; signers without `signHash` fail offline with
4900
+ * an actionable error.
4638
4901
  *
4639
- * @param userOperation - The UserOperation to sign
4640
- * @param signer - Async signing function: `(hash: string) => Promise<string>`
4641
- * @param chainId - Target chain ID
4642
- * @param overrides - Optional overrides (keyHash for secondary keys, hookData)
4643
- * @returns Promise resolving to the hex-encoded wrapped signature
4902
+ * For signing with a raw private-key string, use the sync
4903
+ * {@link signUserOperation} method, or wrap explicitly with
4904
+ * `fromPrivateKey(pk)`. For secondary (non-root) keys, pass the key
4905
+ * hash via `overrides.keyHash`.
4644
4906
  *
4645
4907
  * @example
4646
- * // Sign with a viem wallet client
4908
+ * import { fromViem, fromEthersWallet } from "abstractionkit";
4647
4909
  * userOp.signature = await account.signUserOperationWithSigner(
4648
- * userOp,
4649
- * (hash) => walletClient.signMessage({ message: { raw: hash } }),
4650
- * chainId,
4910
+ * userOp, fromViem(privateKeyToAccount(pk)), chainId,
4651
4911
  * );
4652
4912
  */
4653
4913
  async signUserOperationWithSigner(userOperation, signer, chainId, overrides = {}) {
4654
- const userOperationHash = createUserOperationHash(userOperation, this.entrypointAddress, chainId);
4914
+ const signature = await invokeSigner(signer, pickScheme(signer, Calibur7702Account.ACCEPTED_SIGNING_SCHEMES, {
4915
+ accountName: "Calibur (raw ECDSA over userOpHash)",
4916
+ signerIndex: 0
4917
+ }), {
4918
+ hash: createUserOperationHash(userOperation, this.entrypointAddress, chainId),
4919
+ context: {
4920
+ userOperation,
4921
+ chainId,
4922
+ entryPoint: this.entrypointAddress
4923
+ }
4924
+ });
4655
4925
  const keyHash = overrides.keyHash ?? ROOT_KEY_HASH;
4656
4926
  const hookData = overrides.hookData ?? "0x";
4657
- const ecdsaSig = await signer(userOperationHash);
4658
- return Calibur7702Account.wrapSignature(keyHash, ecdsaSig, hookData);
4927
+ return Calibur7702Account.wrapSignature(keyHash, signature, hookData);
4659
4928
  }
4660
4929
  /**
4661
4930
  * Format a WebAuthn (passkey) assertion into Calibur's signature format.
@@ -5126,6 +5395,88 @@ var abstractionkit = (function(exports, ethers) {
5126
5395
  }
5127
5396
  };
5128
5397
  //#endregion
5398
+ //#region src/signer/adapters.ts
5399
+ /**
5400
+ * Build a Signer from a raw private key. Uses the library's existing
5401
+ * ethers dependency internally, so no additional packages are required on
5402
+ * the caller side. Supports both raw-hash and typed-data signing.
5403
+ *
5404
+ * Prefer this when all you have is a private key (test suites, server-side
5405
+ * scripts, scripts with env-injected keys, etc.). If you already hold a
5406
+ * viem Account or ethers Wallet from elsewhere in your app, pass it to
5407
+ * {@link fromViem} or {@link fromEthersWallet} instead.
5408
+ *
5409
+ * @example
5410
+ * import { fromPrivateKey } from "abstractionkit";
5411
+ * const signer = fromPrivateKey(process.env.PRIVATE_KEY!);
5412
+ * userOp.signature = await safe.signUserOperationWithSigners(userOp, [signer], chainId);
5413
+ */
5414
+ function fromPrivateKey(privateKey) {
5415
+ const wallet = new ethers.Wallet(privateKey);
5416
+ return {
5417
+ address: (0, ethers.getAddress)(wallet.address),
5418
+ signHash: async (hash) => wallet.signingKey.sign(hash).serialized,
5419
+ signTypedData: async (td) => await wallet.signTypedData(td.domain, td.types, td.message)
5420
+ };
5421
+ }
5422
+ /**
5423
+ * Adapt a viem Local Account (e.g. `privateKeyToAccount(pk)`) to a Signer.
5424
+ * Supports both raw-hash and typed-data signing.
5425
+ *
5426
+ * @remarks Requires viem &gt;= 2.0.
5427
+ */
5428
+ function fromViem(account) {
5429
+ return {
5430
+ address: account.address,
5431
+ signHash: (hash) => account.sign({ hash }),
5432
+ signTypedData: (td) => account.signTypedData({
5433
+ domain: td.domain,
5434
+ types: td.types,
5435
+ primaryType: td.primaryType,
5436
+ message: td.message
5437
+ })
5438
+ };
5439
+ }
5440
+ /**
5441
+ * Adapt a viem WalletClient to a Signer. WalletClient is the client-style
5442
+ * API dApps use to drive browser / JSON-RPC wallets, so only typed-data
5443
+ * signing is exposed (JSON-RPC wallets can't sign raw hashes).
5444
+ *
5445
+ * Requires the client to have been constructed with an `account` (local or
5446
+ * JSON-RPC). For local accounts, pass that directly to `fromViem` instead
5447
+ * if you want raw-hash fallback.
5448
+ *
5449
+ * @remarks Requires viem &gt;= 2.0.
5450
+ */
5451
+ function fromViemWalletClient(client) {
5452
+ if (!client.account) throw new Error("fromViemWalletClient: client has no `account` configured. Construct with `createWalletClient({ account, transport, chain })`.");
5453
+ const account = client.account;
5454
+ const signTypedData = client.signTypedData;
5455
+ return {
5456
+ address: account.address,
5457
+ signTypedData: (td) => signTypedData({
5458
+ account,
5459
+ domain: td.domain,
5460
+ types: td.types,
5461
+ primaryType: td.primaryType,
5462
+ message: td.message
5463
+ })
5464
+ };
5465
+ }
5466
+ /**
5467
+ * Adapt an ethers `Wallet` / `HDNodeWallet` to a Signer. Supports both
5468
+ * raw-hash and typed-data signing.
5469
+ *
5470
+ * @remarks Requires ethers &gt;= 6.0.
5471
+ */
5472
+ function fromEthersWallet(wallet) {
5473
+ return {
5474
+ address: wallet.address,
5475
+ signHash: async (hash) => wallet.signingKey.sign(hash).serialized,
5476
+ signTypedData: async (td) => await wallet.signTypedData(td.domain, td.types, td.message)
5477
+ };
5478
+ }
5479
+ //#endregion
5129
5480
  //#region src/account/Safe/modules/SafeModule.ts
5130
5481
  /**
5131
5482
  * Abstract base class for Safe modules. Provides shared utilities for
@@ -6129,6 +6480,45 @@ var abstractionkit = (function(exports, ethers) {
6129
6480
  signUserOperation(useroperation, privateKeys, chainId, overrides = {}) {
6130
6481
  return SafeAccount.baseSignSingleUserOperation(useroperation, privateKeys, chainId, this.entrypointAddress, this.safe4337ModuleAddress, overrides);
6131
6482
  }
6483
+ /**
6484
+ * Sign a UserOperation using one or more {@link ExternalSigner} instances.
6485
+ * Capability-oriented entry point: each Signer declares what it can do
6486
+ * (`signHash`, `signTypedData`, both) and the account picks the best
6487
+ * match per signer. Incompatible signers fail offline with an actionable
6488
+ * error — no silent bundler rejections.
6489
+ *
6490
+ * This method is for external signers only (viem, ethers, hardware
6491
+ * wallets, MPC, HSMs, Uint8Array-only signers). If you just have a raw
6492
+ * private-key string, use the sync {@link signUserOperation} method
6493
+ * instead, or wrap explicitly with `fromPrivateKey(pk)`.
6494
+ *
6495
+ * Prebuilt adapters: `fromViem`, `fromEthersWallet`,
6496
+ * `fromViemWalletClient`, `fromPrivateKey`. Custom signers just need to
6497
+ * match the `ExternalSigner` shape.
6498
+ *
6499
+ * @example
6500
+ * import { fromViem } from "abstractionkit";
6501
+ * import { privateKeyToAccount } from "viem/accounts";
6502
+ *
6503
+ * const signer = fromViem(privateKeyToAccount(pk));
6504
+ * userOp.signature = await account.signUserOperationWithSigners(
6505
+ * userOp, [signer], chainId,
6506
+ * );
6507
+ *
6508
+ * @param useroperation - UserOperation to sign
6509
+ * @param signers - one ExternalSigner per owner (any order)
6510
+ * @param chainId - target chain ID
6511
+ * @param overrides - optional validAfter / validUntil / multi-chain flag
6512
+ * @returns Promise resolving to the formatted signature string
6513
+ */
6514
+ signUserOperationWithSigners(useroperation, signers, chainId, overrides = {}) {
6515
+ const context = {
6516
+ userOperation: useroperation,
6517
+ chainId,
6518
+ entryPoint: this.entrypointAddress
6519
+ };
6520
+ return SafeAccount.baseSignUserOperationWithSigners(useroperation, signers, chainId, this.entrypointAddress, this.safe4337ModuleAddress, context, overrides);
6521
+ }
6132
6522
  };
6133
6523
  //#endregion
6134
6524
  //#region src/account/Safe/SafeAccountV0_2_0.ts
@@ -6371,6 +6761,19 @@ var abstractionkit = (function(exports, ethers) {
6371
6761
  signUserOperation(useroperation, privateKeys, chainId, overrides = {}) {
6372
6762
  return SafeAccount.baseSignSingleUserOperation(useroperation, privateKeys, chainId, this.entrypointAddress, this.safe4337ModuleAddress, overrides);
6373
6763
  }
6764
+ /**
6765
+ * Sign a UserOperation using one or more {@link AkSigner} instances.
6766
+ * See {@link SafeAccountV0_3_0.signUserOperationWithSigners} for full
6767
+ * design rationale and examples.
6768
+ */
6769
+ signUserOperationWithSigners(useroperation, signers, chainId, overrides = {}) {
6770
+ const context = {
6771
+ userOperation: useroperation,
6772
+ chainId,
6773
+ entryPoint: this.entrypointAddress
6774
+ };
6775
+ return SafeAccount.baseSignUserOperationWithSigners(useroperation, signers, chainId, this.entrypointAddress, this.safe4337ModuleAddress, context, overrides);
6776
+ }
6374
6777
  };
6375
6778
  //#endregion
6376
6779
  //#region src/account/Safe/SafeAccountV1_5_0_M_0_3_0.ts
@@ -7536,6 +7939,10 @@ var abstractionkit = (function(exports, ethers) {
7536
7939
  createWorldIdSignal: () => createWorldIdSignal,
7537
7940
  fetchAccountNonce: () => fetchAccountNonce,
7538
7941
  fetchGasPrice: () => fetchGasPrice,
7942
+ fromEthersWallet: () => fromEthersWallet,
7943
+ fromPrivateKey: () => fromPrivateKey,
7944
+ fromViem: () => fromViem,
7945
+ fromViemWalletClient: () => fromViemWalletClient,
7539
7946
  getBalanceOf: () => getBalanceOf,
7540
7947
  getDelegatedAddress: () => getDelegatedAddress,
7541
7948
  getDepositInfo: () => getDepositInfo,
@@ -7616,6 +8023,10 @@ var abstractionkit = (function(exports, ethers) {
7616
8023
  exports.createWorldIdSignal = createWorldIdSignal;
7617
8024
  exports.fetchAccountNonce = fetchAccountNonce;
7618
8025
  exports.fetchGasPrice = fetchGasPrice;
8026
+ exports.fromEthersWallet = fromEthersWallet;
8027
+ exports.fromPrivateKey = fromPrivateKey;
8028
+ exports.fromViem = fromViem;
8029
+ exports.fromViemWalletClient = fromViemWalletClient;
7619
8030
  exports.getBalanceOf = getBalanceOf;
7620
8031
  exports.getDelegatedAddress = getDelegatedAddress;
7621
8032
  exports.getDepositInfo = getDepositInfo;