# Safe Unified Account

**SafeMultiChainSigAccountV1** enables chain abstraction for 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[​](#audits "Direct link to Audits")

* [Audit Report by Nethermind (NM-0874)](https://github.com/candidelabs/safe-4337-multi-chain-signature-module/blob/main/audit/NM_0874_Candide_safe.pdf)

To learn more about the contracts, visit the [Safe 4337 Multi-Chain Signature Module repository](https://github.com/candidelabs/safe-4337-multi-chain-signature-module).

## Import[​](#import "Direct link to Import")

```
import { SafeMultiChainSigAccountV1 as SafeAccount } from "abstractionkit";
```

## How to Use[​](#how-to-use "Direct link to 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](https://docs.candide.dev/wallet/abstractionkit/safe-account.md). The methods below are specific to multichain operations or override the base class with multichain defaults.

## Methods[​](#methods "Direct link to Methods")

### initializeNewAccount[​](#initializenewaccount "Direct link to 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

example.ts

```
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[]`           | `key	type	descriptionSigner	ECDSAPublicAddress \| WebauthnPublicKey	Signer type which can be either an ECDSA public address or a Webauthn public key`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           | Pass the owner(s) address(es) of the account. It can be a single owner account, a multi-sig, or a WebAuthn |
| `initCodeOverrides?` | `key	type	descriptionthreshold?	number	Signature threshold, defines how many signatures are required. Default is 1.
c2Nonce?	bigint	Create2 nonce used to generate different sender addresses from the same owners. Default is 0.
entrypointAddress?	string	Address of the entry point for transactions or contracts.
safe4337ModuleAddress?	string	Address of the Safe 4337 module.
safeModuleSetupAddress?	string	Address used for setting up the Safe module.
safeAccountSingleton?	SafeAccountSingleton	Safe contract singleton address. Default is "0x29fcB43b46531BcA003ddC8FCB67FFE91900C762".
safeAccountFactoryAddress?	string	Address of the Safe Factory. Default is "0x4e1DCf7AD4e460CfD30791CCC4F9c8a4f820ec67".
multisendContractAddress?	string	Address of the Safe 4337 multisend contract. Default is "0xa581c4A4DB7175302464fF3C06380BC3270b4037".
webAuthnSharedSigner?	string	Shared signer used for WebAuthn-based authentication.
eip7212WebAuthnPrecompileVerifierForSharedSigner?	string	Verifier contract for WebAuthn precompile, related to the shared signer.
eip7212WebAuthnContractVerifierForSharedSigner?	string	Contract verifier for WebAuthn-based shared signer, compliant with EIP-7212.
onChainIdentifierParams?	OnChainIdentifierParamsType	Parameters for on-chain identifier tracking.
onChainIdentifier?	string	Pre-computed on-chain identifier string.` | Override values to change the initialization default values                                                |

| 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[​](#source-code "Direct link to Source code")

[initializeNewAccount](https://github.com/candidelabs/abstractionkit/blob/main/src/account/Safe/SafeMultiChainSigAccount.ts)

### createUserOperation[​](#createuseroperation "Direct link to 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

example.ts

```
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",
);
```

| key               | type                                                                                                                                                                                                                                                                                                                                                                               | description                                                               |
| :---------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------ |
| `MetaTransaction` | `key	type	descriptionto	string	To address, or the the target contract address for the transaction
value	BigNumberish	Value transfered if making a native token transfer. (usually 0n for contract interaction with non-native tokens like erc-20 tokens)
data	BytesLike	The call data for the transaction
operation	Operation: enum	Default to 0 for a Call. 1 for a Delegate Call. (Optional)` | MetaTransaction is the type of transaction to construct a Safe operation. |

#### Source code[​](#source-code-1 "Direct link to Source code")

[createUserOperation](https://github.com/candidelabs/abstractionkit/blob/main/src/account/Safe/SafeMultiChainSigAccount.ts)

### signUserOperations[​](#signuseroperations "Direct link to 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.

* example.ts
* Param Types
* Return Type

example.ts

```
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 |

| 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[​](#source-code-2 "Direct link to Source code")

[signUserOperations](https://github.com/candidelabs/abstractionkit/blob/main/src/account/Safe/SafeMultiChainSigAccount.ts)

### getMultiChainSingleSignatureUserOperationsEip712Hash[​](#getmultichainsinglesignatureuseroperationseip712hash "Direct link to 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

example.ts

```
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 |

| key    | type     | description                                                            |
| :----- | :------- | :--------------------------------------------------------------------- |
| `hash` | `string` | The EIP-712 hash of the Merkle tree root, ready for signing by wallets |

#### Source code[​](#source-code-3 "Direct link to Source code")

[getMultiChainSingleSignatureUserOperationsEip712Hash](https://github.com/candidelabs/abstractionkit/blob/main/src/account/Safe/SafeMultiChainSigAccount.ts)

### getMultiChainSingleSignatureUserOperationsEip712Data[​](#getmultichainsinglesignatureuseroperationseip712data "Direct link to 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

example.ts

```
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 |

| 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[​](#source-code-4 "Direct link to Source code")

[getMultiChainSingleSignatureUserOperationsEip712Data](https://github.com/candidelabs/abstractionkit/blob/main/src/account/Safe/SafeMultiChainSigAccount.ts)

### formatSignaturesToUseroperationsSignatures[​](#formatsignaturestouseroperationssignatures "Direct link to 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

example.ts

```
import { SafeMultiChainSigAccountV1 as SafeAccount } from "abstractionkit";

const userOpsToSign = [
    { userOperation: userOp1, chainId: 11155111n },
    { userOperation: userOp2, chainId: 11155420n },
];

const signerSignaturePairs = [
    { signer: ownerAddress, signature: eip712Signature },
];

const signatures = SafeAccount.formatSignaturesToUseroperationsSignatures(
    userOpsToSign,
    signerSignaturePairs,
);

userOp1.signature = signatures[0];
userOp2.signature = signatures[1];
```

| key                    | type                         | description                                                      |
| :--------------------- | :--------------------------- | :--------------------------------------------------------------- |
| `userOperationsToSign` | `UserOperationToSign[]`      | Array of UserOperations with their target chain IDs              |
| `signerSignaturePairs` | `SignerSignaturePair[]`      | Array of signer address and signature pairs from EIP-712 signing |
| `overrides?`           | `WebAuthnSignatureOverrides` | Optional overrides for WebAuthn verifier 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 |

| key          | type       | description                                                             |
| :----------- | :--------- | :---------------------------------------------------------------------- |
| `signatures` | `string[]` | Array of formatted signatures with Merkle proofs, one per UserOperation |

#### Source code[​](#source-code-5 "Direct link to Source code")

[formatSignaturesToUseroperationsSignatures](https://github.com/candidelabs/abstractionkit/blob/main/src/account/Safe/SafeMultiChainSigAccount.ts)

### sendUserOperation[​](#senduseroperation "Direct link to sendUserOperation")

Inherited from [Safe Account](https://docs.candide.dev/wallet/abstractionkit/safe-account.md). Sends a signed UserOperation to the bundler.

example.ts

```
const account = new SafeAccount(userOperation.sender);
const response = await account.sendUserOperation(userOperation, bundlerUrl);
const receipt = await response.included();
```

## Gas Sponsorship[​](#gas-sponsorship "Direct link to Gas Sponsorship")

Use `CandidePaymaster` for gas sponsorship with multichain operations. The paymaster uses a two-phase flow: **commit** (before signing) and **finalize** (after signing).

example.ts

```
import { CandidePaymaster } from "abstractionkit";

// One paymaster instance per chain
const paymaster1 = new CandidePaymaster("https://api.candide.dev/public/v3/sepolia");
const paymaster2 = new CandidePaymaster("https://api.candide.dev/public/v3/optimism-sepolia");

// Phase 1: Commit (before signing)
const commitOverrides = {
    preVerificationGasPercentageMultiplier: 20,
    context: { signingPhase: "commit" as const },
};
const [[commitOp1], [commitOp2]] = await Promise.all([
    paymaster1.createSponsorPaymasterUserOperation(
        smartAccount, userOperation1, bundlerUrl1, undefined, commitOverrides
    ),
    paymaster2.createSponsorPaymasterUserOperation(
        smartAccount, userOperation2, bundlerUrl2, undefined, commitOverrides
    ),
]);
userOperation1 = commitOp1;
userOperation2 = commitOp2;

// Sign here with signUserOperations...

// Phase 2: Finalize (after signing)
const finalizeOverrides = { context: { signingPhase: "finalize" as const } };
const [[finalOp1], [finalOp2]] = await Promise.all([
    paymaster1.createSponsorPaymasterUserOperation(
        smartAccount, userOperation1, bundlerUrl1, undefined, finalizeOverrides
    ),
    paymaster2.createSponsorPaymasterUserOperation(
        smartAccount, userOperation2, bundlerUrl2, undefined, finalizeOverrides
    ),
]);
userOperation1 = finalOp1;
userOperation2 = finalOp2;
```
