Safe Unified Account
SafeMultiChainSigAccountV1 is the recommended Safe account class for new AbstractionKit examples. It works for normal single-chain Safe transactions and enables chain abstraction for multichain Safe Accounts. It extends the standard Safe Account with multichain signature support via Merkle trees, allowing you to sign UserOperations for multiple chains with a single signature.
Uses EntryPoint v0.9 and EIP-712 typed data with Merkle proofs.
Audits
To learn more about the contracts, visit the Safe 4337 Multi-Chain Signature Module repository.
Import
import { SafeMultiChainSigAccountV1 as SafeAccount } from "abstractionkit";
How to Use
const ownerPublicAddress = "0xBdbc5FBC9cA8C3F514D073eC3de840Ac84FC6D31";
const smartAccount = SafeAccount.initializeNewAccount([ownerPublicAddress]);
console.log("Account address:", smartAccount.accountAddress);
This class inherits all methods from Safe Account. The methods below are specific to multichain operations or override the base class with multichain defaults.
Methods
initializeNewAccount
Initializes a new SafeMultiChainSigAccountV1 from a list of owners. Only needed on the first transaction when the account has not been deployed yet.
- example.ts
- Param Types
- Return Type
import { SafeMultiChainSigAccountV1 as SafeAccount } from "abstractionkit";
const ownerPublicAddress = "0xBdbc5FBC9cA8C3F514D073eC3de840Ac84FC6D31";
const smartAccount = SafeAccount.initializeNewAccount([ownerPublicAddress]);
console.log("Account address (sender): " + smartAccount.accountAddress);
| key | type | description |
|---|---|---|
owners[] | object | Pass the owner(s) address(es) of the account. It can be a single owner account, a multi-sig, or a WebAuthn |
owners[].Signer | ECDSAPublicAddress | WebauthnPublicKey | Signer type which can be either an ECDSA public address or a Webauthn public key |
initCodeOverrides? | object | Override values to change the initialization default values |
initCodeOverrides?.threshold? | number | Signature threshold, defines how many signatures are required. Default is 1. |
initCodeOverrides?.c2Nonce? | bigint | Create2 nonce used to generate different sender addresses from the same owners. Default is 0. |
initCodeOverrides?.entrypointAddress? | string | Address of the entry point for transactions or contracts. |
initCodeOverrides?.safe4337ModuleAddress? | string | Address of the Safe 4337 module. |
initCodeOverrides?.safeModuleSetupAddress? | string | Address used for setting up the Safe module. |
initCodeOverrides?.safeAccountSingleton? | SafeAccountSingleton | Safe contract singleton address. Default is "0x29fcB43b46531BcA003ddC8FCB67FFE91900C762". |
initCodeOverrides?.safeAccountFactoryAddress? | string | Address of the Safe Factory. Default is "0x4e1DCf7AD4e460CfD30791CCC4F9c8a4f820ec67". |
initCodeOverrides?.multisendContractAddress? | string | Address of the Safe 4337 multisend contract. Default is "0xa581c4A4DB7175302464fF3C06380BC3270b4037". |
initCodeOverrides?.webAuthnSharedSigner? | string | Shared signer used for WebAuthn-based authentication. |
initCodeOverrides?.eip7212WebAuthnPrecompileVerifierForSharedSigner? | string | Verifier contract for WebAuthn precompile, related to the shared signer. |
initCodeOverrides?.eip7212WebAuthnContractVerifierForSharedSigner? | string | Contract verifier for WebAuthn-based shared signer, compliant with EIP-7212. |
initCodeOverrides?.onChainIdentifierParams? | OnChainIdentifierParamsType | Parameters for on-chain identifier tracking. |
initCodeOverrides?.onChainIdentifier? | string | Pre-computed on-chain identifier string. |
| key | type | description |
|---|---|---|
ECDSASignature | string | ECDSA signature represented as a string |
WebauthnPublicKey
| key | type | description |
|---|---|---|
authenticatorData | ArrayBuffer | Binary data returned by the authenticator during the Webauthn process |
clientDataFields | string | Fields associated with the client's Webauthn request data |
rs | [bigint, bigint] | Array of two bigints representing the 'r' and 's' values of the signature |
| key | type | description |
|---|---|---|
SafeAccount class | SafeAccountV0_3_0 | An instance of the Safe V3 Account and the initialization parameters |
Source code
createUserOperation
Creates a UserOperation for multichain signing. This overrides the base SafeAccount method to always set isMultiChainSignature: true and use EntryPoint v0.9.
Determines the nonce, fetches gas prices, estimates gas limits, and returns a UserOperation to be signed. You can override any of these values using the overrides parameter.
- example.ts
- Param Types
- Return Type
import {
SafeMultiChainSigAccountV1 as SafeAccount,
MetaTransaction,
} from "abstractionkit";
const smartAccount = SafeAccount.initializeNewAccount([ownerPublicAddress]);
const transaction: MetaTransaction = {
to: "0xTargetAddress",
value: 0n,
data: "0x",
};
const userOperation = await smartAccount.createUserOperation(
[transaction],
"https://rpc-node-url",
"https://bundler-url",
);
| Param Name | Param Type | Description | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Transactions | MetaTransaction[]
| MetaTransaction is the type of a transaction to construct a Safe operation | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Provider RPC | string | The node URL. It is used to fetch the current nonce and fetch gas prices | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Bundler URL | string | The Bundler URL. It is used to fetch the gas limits | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Overrides | CreateUserOperationV9Overrides, optional object
| CreateUserOperationV9Overrides allows you to override any of the default parameters for the user operation. It has the same fields as CreateUserOperationV7Overrides. |
| Param Name | Param Type | Description | |||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| UserOperation | Promise<UserOperationV9>
| Returns an unsigned UserOperation for EntryPoint v0.9. |
Source code
estimateUserOperationGas
Estimates gas limits for a multichain-signature UserOperation against a bundler, matching the estimation surface available on the other account classes. Returns a tuple of [preVerificationGas, verificationGasLimit, callGasLimit].
Usage
import {
SafeMultiChainSigAccountV1 as SafeAccount,
MetaTransaction,
} from "abstractionkit";
const bundlerRpc = "https://api.candide.dev/public/v3/11155111";
const nodeRpcUrl = "https://rpc-node-url";
const smartAccount = SafeAccount.initializeNewAccount([ownerPublicAddress]);
const transaction: MetaTransaction = {
to: "0xTargetAddress",
value: 0n,
data: "0x",
};
const userOperation = await smartAccount.createUserOperation(
[transaction],
nodeRpcUrl,
bundlerRpc,
);
const [preVerificationGas, verificationGasLimit, callGasLimit] =
await smartAccount.estimateUserOperationGas(userOperation, bundlerRpc);
- Param Types
- Return Types
| key | type | description |
|---|---|---|
userOperation | UserOperationV9 | The UserOperation to estimate gas for. |
bundlerRpc | string | Transport | Bundler | The bundler RPC URL used to run eth_estimateUserOperationGas. |
overrides? | object | Optional overrides for the estimation, such as bundler state overrides and the dummy signatures used while estimating. |
overrides?.stateOverrideSet? | StateOverrideSet | State overrides passed to the bundler during estimation. |
overrides?.dummySignerSignaturePairs? | SignerSignaturePair[] | Dummy signer/signature pairs used as placeholders during estimation. |
overrides?.expectedSigners? | Signer[] | The signers whose signatures will be produced at sign time. Used to build dummy signatures when dummySignerSignaturePairs is not provided. |
| key | type | description |
|---|---|---|
[preVerificationGas, verificationGasLimit, callGasLimit] | Promise<[bigint, bigint, bigint]> | Resolves to a tuple of the estimated preVerificationGas, verificationGasLimit, and callGasLimit. |
Source code
signUserOperation
Signs a single UserOperation with one or more private keys. This overrides the base method to always encode the signature with the multichain scheme (isMultiChainSignature: true), which is how this account validates even single-chain UserOperations.
signUserOperation(
userOperation: UserOperationV9,
privateKeys: string[],
chainId: bigint,
options?: SafeSignatureOptions,
): string
Pass one private key per owner required by the threshold. options accepts the usual SafeSignatureOptions timing fields (validAfter, validUntil) and module address override; multiChainMerkleProof is rejected here, use signUserOperations for multi-op Merkle signatures.
userOperation.signature = smartAccount.signUserOperation(
userOperation,
[ownerPrivateKey],
11155111n, // chainId
);
Source code
signUserOperations
Signs multiple UserOperations across different chains with a single signing session. Builds a Merkle tree from the UserOperation hashes, signs the Merkle root, and returns an array of signatures (one per UserOperation) each containing the Merkle proof for its chain.
If only one UserOperation is provided, falls back to single-chain signing.
In AbstractionKit v0.3.5, UserOperationToSignWithOverrides split the previous catch-all overrides field into options for normal signing options and webAuthnSignatureOverrides for WebAuthn-specific signature encoding values.
- example.ts
- Param Types
- Return Type
import { SafeMultiChainSigAccountV1 as SafeAccount } from "abstractionkit";
const smartAccount = SafeAccount.initializeNewAccount([ownerPublicAddress]);
// Create UserOperations for each chain
const userOp1 = await smartAccount.createUserOperation([tx], nodeUrl1, bundlerUrl1);
const userOp2 = await smartAccount.createUserOperation([tx], nodeUrl2, bundlerUrl2);
// Sign all UserOperations with one signing session
const signatures = smartAccount.signUserOperations(
[
{ userOperation: userOp1, chainId: 11155111n },
{ userOperation: userOp2, chainId: 11155420n },
],
[ownerPrivateKey],
);
// Attach signatures
userOp1.signature = signatures[0];
userOp2.signature = signatures[1];
| key | type | description |
|---|---|---|
userOperationsToSign | UserOperationToSign[] | Array of UserOperations with their target chain IDs and optional validity windows |
privateKeys | string[] | Private keys of the Safe owners to sign with |
UserOperationToSign
| key | type | description |
|---|---|---|
chainId | bigint | Target chain ID for this UserOperation |
userOperation | UserOperationV9 | The UserOperation to sign |
validAfter? | bigint | Timestamp the signature will be valid after |
validUntil? | bigint | Timestamp the signature will be valid until |
options? | MultiChainSigningOptions | Per-operation signing options such as the Safe 4337 module address. In v0.3.5 this replaced the generic overrides field for non-WebAuthn options. |
webAuthnSignatureOverrides? | WebAuthnSignatureOverrides | Per-operation WebAuthn signature encoding overrides, such as isInit or WebAuthn verifier addresses. In v0.3.5 this replaced the WebAuthn-specific values previously carried in overrides. |
| key | type | description |
|---|---|---|
signatures | string[] | Array of signatures, one per UserOperation. Each contains the Merkle proof for its respective chain. If only one UserOperation is provided, falls back to single-chain signing. |
Source code
signUserOperationsWithSigners
Multi-op variant that signs a Merkle-rooted bundle of UserOperations using one or more ExternalSigners instead of raw private keys. One signing session covers every chain in the bundle.
Since v0.3.8, this path accepts typed-data-only signers such as fromViemWalletClient as well as raw-hash signers. Pass chainId inside each UserOperationToSign item so the SDK can build the per-chain typed-data payloads.
signUserOperationsWithSigners(
userOperationsToSign: UserOperationToSign[],
signers: ExternalSigner[],
): Promise<string[]>
- example.ts
- Param Types
- Return Type
import { SafeMultiChainSigAccountV1 as SafeAccount, fromViem } from "abstractionkit";
import { privateKeyToAccount } from "viem/accounts";
const smartAccount = SafeAccount.initializeNewAccount([ownerPublicAddress]);
const viemAccount = privateKeyToAccount("0x...");
const signatures = await smartAccount.signUserOperationsWithSigners(
[
{ userOperation: userOp1, chainId: 11155111n },
{ userOperation: userOp2, chainId: 11155420n },
],
[fromViem(viemAccount)],
);
userOp1.signature = signatures[0];
userOp2.signature = signatures[1];
| Param Name | Param Type | Description | |||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| userOperationsToSign | UserOperationToSign[]
| UserOperations with their chain IDs and optional validity windows. | |||||||||||||||||||||
| signers | ExternalSigner[] | One signer per owner required by the threshold, in any order. The built-in adapters |
| Param Name | Param Type | Description |
|---|---|---|
| signatures | Promise<string[]> | One formatted signature per input UserOperation, in the same order. |
See External Signers for the full list of adapters, and the multi-chain add-owner example for a full end-to-end flow.
Source code
getMultiChainSingleSignatureUserOperationsEip712Hash
Static method. Computes the EIP-712 hash of the Merkle tree root for a set of UserOperations. This is the hash that wallet signers (browser extensions, hardware wallets) sign to approve multiple cross-chain operations at once.
- example.ts
- Param Types
- Return Type
import { SafeMultiChainSigAccountV1 as SafeAccount } from "abstractionkit";
const hash = SafeAccount.getMultiChainSingleSignatureUserOperationsEip712Hash([
{ userOperation: userOp1, chainId: 11155111n },
{ userOperation: userOp2, chainId: 11155420n },
]);
| key | type | description |
|---|---|---|
userOperationsToSign | UserOperationToSign[] | Array of UserOperations with their target chain IDs |
overrides? | object | Optional overrides for the Safe 4337 module address |
UserOperationToSign
| key | type | description |
|---|---|---|
chainId | bigint | Target chain ID for this UserOperation |
userOperation | UserOperationV9 | The UserOperation to sign |
validAfter? | bigint | Timestamp the signature will be valid after |
validUntil? | bigint | Timestamp the signature will be valid until |
options? | MultiChainSigningOptions | Per-operation signing options such as the Safe 4337 module address. In v0.3.5 this replaced the generic overrides field for non-WebAuthn options. |
webAuthnSignatureOverrides? | WebAuthnSignatureOverrides | Per-operation WebAuthn signature encoding overrides, such as isInit or WebAuthn verifier addresses. In v0.3.5 this replaced the WebAuthn-specific values previously carried in overrides. |
| key | type | description |
|---|---|---|
hash | string | The EIP-712 hash of the Merkle tree root, ready for signing by wallets |
Source code
getMultiChainSingleSignatureUserOperationsEip712Hash
getMultiChainSingleSignatureUserOperationsEip712Data
Static method. Returns the EIP-712 typed data components (domain, types, message value) for a multi-chain Merkle tree root. Use this for wallet-compatible signing via eth_signTypedData_v4.
- example.ts
- Param Types
- Return Type
import { SafeMultiChainSigAccountV1 as SafeAccount } from "abstractionkit";
const { domain, types, messageValue } =
SafeAccount.getMultiChainSingleSignatureUserOperationsEip712Data([
{ userOperation: userOp1, chainId: 11155111n },
{ userOperation: userOp2, chainId: 11155420n },
]);
// Use with a wallet provider
const signature = await wallet.signTypedData(domain, types, messageValue);
| key | type | description |
|---|---|---|
userOperationsToSign | UserOperationToSign[] | Array of UserOperations with their target chain IDs |
overrides? | object | Optional overrides for module and entrypoint addresses |
UserOperationToSign
| key | type | description |
|---|---|---|
chainId | bigint | Target chain ID for this UserOperation |
userOperation | UserOperationV9 | The UserOperation to sign |
validAfter? | bigint | Timestamp the signature will be valid after |
validUntil? | bigint | Timestamp the signature will be valid until |
options? | MultiChainSigningOptions | Per-operation signing options such as the Safe 4337 module address. In v0.3.5 this replaced the generic overrides field for non-WebAuthn options. |
webAuthnSignatureOverrides? | WebAuthnSignatureOverrides | Per-operation WebAuthn signature encoding overrides, such as isInit or WebAuthn verifier addresses. In v0.3.5 this replaced the WebAuthn-specific values previously carried in overrides. |
| key | type | description |
|---|---|---|
domain | { verifyingContract: string } | The EIP-712 domain with the Safe 4337 module as verifying contract |
types | Record<string, { name: string; type: string }[]> | The EIP-712 type definitions for multi-chain operations |
messageValue | { merkleTreeRoot: string } | The message containing the Merkle tree root of all UserOperation hashes |
Source code
getMultiChainSingleSignatureUserOperationsEip712Data
formatSignaturesToUseroperationsSignatures
Static method. Formats EIP-712 signatures (from wallet signing) into UserOperation signatures with Merkle proofs. Use this after signing with getMultiChainSingleSignatureUserOperationsEip712Hash or getMultiChainSingleSignatureUserOperationsEip712Data.
- example.ts
- Param Types
- Return Type
import { SafeMultiChainSigAccountV1 as SafeAccount } from "abstractionkit";
const userOpsToSign = [
{ userOperation: userOp1, chainId: 11155111n },
// Since v0.3.5, generic signing options and WebAuthn signature overrides
// use separate fields instead of the previous catch-all `overrides` field.
{
userOperation: userOp2,
chainId: 11155420n,
options: { safe4337ModuleAddress: "0x..." },
webAuthnSignatureOverrides: { isInit: userOp2.nonce === 0n },
},
];
const signerSignaturePairs = [
{ signer: ownerAddress, signature: eip712Signature },
];
const signatures = SafeAccount.formatSignaturesToUseroperationsSignatures(
userOpsToSign,
signerSignaturePairs,
);
userOp1.signature = signatures[0];
userOp2.signature = signatures[1];
| key | type | description |
|---|---|---|
userOperationsToSign | UserOperationToSignWithOverrides[] | Array of UserOperations with their target chain IDs and optional per-operation options / WebAuthn signature overrides |
signerSignaturePairs | SignerSignaturePair[] | Array of signer address and signature pairs from EIP-712 signing |
UserOperationToSign
| key | type | description |
|---|---|---|
chainId | bigint | Target chain ID for this UserOperation |
userOperation | UserOperationV9 | The UserOperation to sign |
validAfter? | bigint | Timestamp the signature will be valid after |
validUntil? | bigint | Timestamp the signature will be valid until |
options? | MultiChainSigningOptions | Per-operation signing options such as the Safe 4337 module address. In v0.3.5 this replaced the generic overrides field for non-WebAuthn options. |
webAuthnSignatureOverrides? | WebAuthnSignatureOverrides | Per-operation WebAuthn signature encoding overrides, such as isInit or WebAuthn verifier addresses. In v0.3.5 this replaced the WebAuthn-specific values previously carried in overrides. |
| key | type | description |
|---|---|---|
signatures | string[] | Array of formatted signatures with Merkle proofs, one per UserOperation |
Source code
formatSignaturesToUseroperationsSignatures
Manual EIP-712 signing for a single UserOperation
SafeMultiChainSigAccountV1 also validates single-chain UserOperations through its multichain signature scheme. If you drive signTypedData yourself instead of using signUserOperationWithSigners, format the raw wallet signature with isMultiChainSignature: true.
import {
EIP712_SAFE_OPERATION_PRIMARY_TYPE,
SafeMultiChainSigAccountV1 as SafeAccount,
} from "abstractionkit";
const eip712Data = SafeAccount.getUserOperationEip712Data(
userOperation,
chainId,
);
const signature = await walletClient.signTypedData({
domain: eip712Data.domain,
types: eip712Data.types,
primaryType: EIP712_SAFE_OPERATION_PRIMARY_TYPE,
message: eip712Data.messageValue,
});
userOperation.signature = SafeAccount.formatSignaturesToUseroperationSignature(
[{ signer: ownerAddress, signature }],
{ isMultiChainSignature: true },
);
Full example: eip-712-signing.ts.
sendUserOperation
Inherited from Safe Account. Sends a signed UserOperation to the bundler.
const account = new SafeAccount(userOperation.sender);
const response = await account.sendUserOperation(userOperation, bundlerUrl);
const receipt = await response.included();
Owner Management
SafeMultiChainSigAccountV1 inherits the Safe owner-management helpers from the base SafeAccount class. The two add-owner methods below are the ones used in the guides; the remaining helpers (swap owner, remove owner, module management) are documented on Safe Account V3.
createAddOwnerWithThresholdMetaTransactions
Builds the MetaTransactions needed to add a new owner (an ECDSA address or a WebAuthn public key) and set a new threshold. If the new owner is a WebAuthn signer whose verifier contract is not yet deployed, the returned list also contains a MetaTransaction that deploys the verifier, so it resolves to one or two MetaTransactions.
createAddOwnerWithThresholdMetaTransactions(
newOwner: Signer,
threshold: number,
overrides?: {
nodeRpcUrl?: string;
eip7212WebAuthnPrecompileVerifier?: string;
eip7212WebAuthnContractVerifier?: string;
webAuthnSignerFactory?: string;
webAuthnSignerSingleton?: string;
webAuthnSignerProxyCreationCode?: string;
},
): Promise<MetaTransaction[]>
overrides.nodeRpcUrl is required when adding a WebAuthn owner, so the SDK can check whether the new owner's verifier is already deployed.
- example.ts
- Param Types
- Return Type
const newOwnerPublicAddress = "0xNewOwnerAddress";
const addOwnerMetaTransactions =
await smartAccount.createAddOwnerWithThresholdMetaTransactions(
newOwnerPublicAddress,
2, // new threshold
);
const userOperation = await smartAccount.createUserOperation(
addOwnerMetaTransactions,
nodeRpcUrl,
bundlerUrl,
);
| key | type | description |
|---|---|---|
newOwner | object | The public address of the new owner to be added |
newOwner.Signer | ECDSAPublicAddress | WebauthnPublicKey | Signer type which can be either an ECDSA public address or a Webauthn public key |
threshold | number | The new threshold value for owner confirmations. |
overrides | object | Optional Overrides for the default values used in the transaction. |
overrides.nodeRpcUrl | string? | The JSON-RPC API url for the target chain, to check if the new webauthn owner is deployed or not |
overrides.eip7212WebAuthnPrecompileVerifier | string? | Address for the EIP-7212 WebAuthn precompile verifier. |
overrides.eip7212WebAuthnContractVerifier | string? | Address for the EIP-7212 WebAuthn contract verifier. |
overrides.webAuthnSignerFactory | string? | Address for the WebAuthn signer factory. |
overrides.webAuthnSignerSingleton | string? | Address for the WebAuthn signer singleton. |
| key | type | description |
|---|---|---|
Promise<MetaTransaction[]> | object | The meta-transaction object for adding a new owner with the specified threshold. |
Promise<MetaTransaction[]>.to | string | The target address for the meta-transaction (Safe account address). |
Promise<MetaTransaction[]>.data | string | The encoded function call data for adding an owner. |
Promise<MetaTransaction[]>.value | bigint | The value to be sent with the meta-transaction, set to zero for this function. |
Source code
createAddOwnerWithThresholdMetaTransactions
createStandardAddOwnerWithThresholdMetaTransaction
Builds the single standard addOwnerWithThreshold MetaTransaction for an owner public address. Unlike the plural variant above, it takes a plain address, performs no WebAuthn verifier handling, and returns synchronously.
createStandardAddOwnerWithThresholdMetaTransaction(
newOwner: string,
threshold: number,
): MetaTransaction
const addOwnerMetaTransaction =
smartAccount.createStandardAddOwnerWithThresholdMetaTransaction(
"0xNewOwnerAddress",
2, // new threshold
);
Source code
createStandardAddOwnerWithThresholdMetaTransaction
Gas Sponsorship
Use CandidePaymaster for gas sponsorship with multichain operations. The paymaster uses a two-phase flow: commit (before signing) and finalize (after signing).
import { CandidePaymaster } from "abstractionkit";
// One paymaster instance per chain
const paymaster1 = new CandidePaymaster("https://api.candide.dev/public/v3/11155111");
const paymaster2 = new CandidePaymaster("https://api.candide.dev/public/v3/11155420");
// Phase 1: Commit (before signing)
const commitContext = { signingPhase: "commit" as const };
const commitOverrides = { preVerificationGasPercentageMultiplier: 20 };
const [{ userOperation: commitOp1 }, { userOperation: commitOp2 }] = await Promise.all([
paymaster1.createSponsorPaymasterUserOperation(
smartAccount, userOperation1, bundlerUrl1, undefined, commitContext, commitOverrides
),
paymaster2.createSponsorPaymasterUserOperation(
smartAccount, userOperation2, bundlerUrl2, undefined, commitContext, commitOverrides
),
]);
userOperation1 = commitOp1;
userOperation2 = commitOp2;
// Sign here with signUserOperations...
// Phase 2: Finalize (after signing)
const finalizeContext = { signingPhase: "finalize" as const };
const [{ userOperation: finalOp1 }, { userOperation: finalOp2 }] = await Promise.all([
paymaster1.createSponsorPaymasterUserOperation(
smartAccount, userOperation1, bundlerUrl1, undefined, finalizeContext
),
paymaster2.createSponsorPaymasterUserOperation(
smartAccount, userOperation2, bundlerUrl2, undefined, finalizeContext
),
]);
userOperation1 = finalOp1;
userOperation2 = finalOp2;