How to manage ownership of a Multi-Owner Light Account

A MultiOwnerLightAccount has one or more ECDSA or SCA owners. This lets your account integrate with multiple signers at once, and supports recovering your account if one signer is lost.

The MultiOwnerLightAccount is able to:

  • Update (add or remove) owners for an account.
  • Show all owners of an account.
  • Validate signed signatures of ERC-4337 enabled user operations as well as regular transactions.

When you connect your MultiOwnerLightAccount to SmartAccountClient you can extend the client with multiOwnerLightAccountClientActions, which exposes a set of methods available to call the MultiOwnerLightAccount with the client connected to the account.

When using createMultiOwnerLightAccountAlchemyClient in @account-kit/smart-contracts, the SmartAccountClient comes automatically extended with multiOwnerLightAccountClientActions as defaults available for use.

1. Get all current owners of a MultiOwnerLightAccount

You can use the getOwnerAddresses method on the MultiOwnerLightAccount object, which can be accessed from a connected client.

import { 
import multiOwnerLightAccountClient
multiOwnerLightAccountClient
} from "./client";
const
const owners: any
owners
= await
import multiOwnerLightAccountClient
multiOwnerLightAccountClient
.
any
account
.
any
getOwnerAddresses
();
import { 
class LocalAccountSigner<T extends HDAccount | PrivateKeyAccount | LocalAccount>

Represents a local account signer and provides methods to sign messages and transactions, as well as static methods to create the signer from mnemonic or private key.

LocalAccountSigner
} from "@aa-sdk/core";
import {
function alchemy(config: AlchemyTransportConfig): AlchemyTransport

Creates an Alchemy transport with the specified configuration options. When sending all traffic to Alchemy, you must pass in one of rpcUrl, apiKey, or jwt. If you want to send Bundler and Paymaster traffic to Alchemy and Node traffic to a different RPC, you must pass in alchemyConnection and nodeRpcUrl.

alchemy
,
const sepolia: Chain
sepolia
} from "@account-kit/infra";
import {
function createMultiOwnerLightAccountAlchemyClient<TSigner extends SmartAccountSigner = SmartAccountSigner<any>>(params: AlchemyMultiOwnerLightAccountClientConfig<TSigner>): Promise<AlchemySmartAccountClient<Chain | undefined, MultiOwnerLightAccount<TSigner>, MultiOwnerLightAccountClientActions<TSigner>>>
createMultiOwnerLightAccountAlchemyClient
} from "@account-kit/smart-contracts";
import {
function generatePrivateKey(): Hex
generatePrivateKey
} from "viem/accounts";
export const
const multiOwnerLightAccountClient: { account: MultiOwnerLightAccount<LocalAccountSigner<{ address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; ... 6 more ...; type: "local"; }>>; ... 86 more ...; extend: <const client extends { ...; } & ExactPartial<...>>(fn: (client: Client<...>) => client) => Client<...>; }
multiOwnerLightAccountClient
=
await
createMultiOwnerLightAccountAlchemyClient<LocalAccountSigner<{ address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; ... 6 more ...; type: "local"; }>>(params: AlchemyMultiOwnerLightAccountClientConfig<...>): Promise<...>
createMultiOwnerLightAccountAlchemyClient
({
signer: LocalAccountSigner<{ address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; ... 6 more ...; type: "local"; }>
signer
:
class LocalAccountSigner<T extends HDAccount | PrivateKeyAccount | LocalAccount>

Represents a local account signer and provides methods to sign messages and transactions, as well as static methods to create the signer from mnemonic or private key.

LocalAccountSigner
.
LocalAccountSigner<T extends HDAccount | PrivateKeyAccount | LocalAccount>.privateKeyToAccountSigner(key: Hex): LocalAccountSigner<PrivateKeyAccount>

Creates a LocalAccountSigner instance using the provided private key.

privateKeyToAccountSigner
(
function generatePrivateKey(): Hex
generatePrivateKey
()),
chain: { blockExplorers?: { [key: string]: ChainBlockExplorer; default: ChainBlockExplorer; } | undefined; ... 7 more ...; testnet?: boolean | undefined; } & ChainConfig<...>

Chain for the client.

chain
:
const sepolia: Chain
sepolia
,
transport: AlchemyTransport

The RPC transport

transport
:
function alchemy(config: AlchemyTransportConfig): AlchemyTransport

Creates an Alchemy transport with the specified configuration options. When sending all traffic to Alchemy, you must pass in one of rpcUrl, apiKey, or jwt. If you want to send Bundler and Paymaster traffic to Alchemy and Node traffic to a different RPC, you must pass in alchemyConnection and nodeRpcUrl.

alchemy
({
apiKey: string
apiKey
: "YOUR_API_KEY",
}), });

2. Add or remove owners for a MultiOwnerLightAccount

You can use the updateOwners method on the multiOwnerLightAccountClientActions extended smart account client to add or remove owners from the MultiOwnerLightAccount.

import { 
import multiOwnerLightAccountClient
multiOwnerLightAccountClient
} from "./client";
const
const ownersToAdd: any[]
ownersToAdd
= []; // the addresses of owners to be added
const
const ownersToRemove: any[]
ownersToRemove
= []; // the addresses of owners to be removed
const
const opHash: any
opHash
= await
import multiOwnerLightAccountClient
multiOwnerLightAccountClient
.
any
updateOwners
({
ownersToAdd: any[]
ownersToAdd
,
ownersToRemove: any[]
ownersToRemove
,
}); const
const txHash: any
txHash
=
await
import multiOwnerLightAccountClient
multiOwnerLightAccountClient
.
any
waitForUserOperationTransaction
({
hash: any
hash
:
const opHash: any
opHash
,
});
import { 
class LocalAccountSigner<T extends HDAccount | PrivateKeyAccount | LocalAccount>

Represents a local account signer and provides methods to sign messages and transactions, as well as static methods to create the signer from mnemonic or private key.

LocalAccountSigner
} from "@aa-sdk/core";
import {
function alchemy(config: AlchemyTransportConfig): AlchemyTransport

Creates an Alchemy transport with the specified configuration options. When sending all traffic to Alchemy, you must pass in one of rpcUrl, apiKey, or jwt. If you want to send Bundler and Paymaster traffic to Alchemy and Node traffic to a different RPC, you must pass in alchemyConnection and nodeRpcUrl.

alchemy
,
const sepolia: Chain
sepolia
} from "@account-kit/infra";
import {
function createMultiOwnerLightAccountAlchemyClient<TSigner extends SmartAccountSigner = SmartAccountSigner<any>>(params: AlchemyMultiOwnerLightAccountClientConfig<TSigner>): Promise<AlchemySmartAccountClient<Chain | undefined, MultiOwnerLightAccount<TSigner>, MultiOwnerLightAccountClientActions<TSigner>>>
createMultiOwnerLightAccountAlchemyClient
} from "@account-kit/smart-contracts";
import {
function generatePrivateKey(): Hex
generatePrivateKey
} from "viem/accounts";
export const
const multiOwnerLightAccountClient: { account: MultiOwnerLightAccount<LocalAccountSigner<{ address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; ... 6 more ...; type: "local"; }>>; ... 86 more ...; extend: <const client extends { ...; } & ExactPartial<...>>(fn: (client: Client<...>) => client) => Client<...>; }
multiOwnerLightAccountClient
=
await
createMultiOwnerLightAccountAlchemyClient<LocalAccountSigner<{ address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; ... 6 more ...; type: "local"; }>>(params: AlchemyMultiOwnerLightAccountClientConfig<...>): Promise<...>
createMultiOwnerLightAccountAlchemyClient
({
signer: LocalAccountSigner<{ address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; ... 6 more ...; type: "local"; }>
signer
:
class LocalAccountSigner<T extends HDAccount | PrivateKeyAccount | LocalAccount>

Represents a local account signer and provides methods to sign messages and transactions, as well as static methods to create the signer from mnemonic or private key.

LocalAccountSigner
.
LocalAccountSigner<T extends HDAccount | PrivateKeyAccount | LocalAccount>.privateKeyToAccountSigner(key: Hex): LocalAccountSigner<PrivateKeyAccount>

Creates a LocalAccountSigner instance using the provided private key.

privateKeyToAccountSigner
(
function generatePrivateKey(): Hex
generatePrivateKey
()),
chain: { blockExplorers?: { [key: string]: ChainBlockExplorer; default: ChainBlockExplorer; } | undefined; ... 7 more ...; testnet?: boolean | undefined; } & ChainConfig<...>

Chain for the client.

chain
:
const sepolia: Chain
sepolia
,
transport: AlchemyTransport

The RPC transport

transport
:
function alchemy(config: AlchemyTransportConfig): AlchemyTransport

Creates an Alchemy transport with the specified configuration options. When sending all traffic to Alchemy, you must pass in one of rpcUrl, apiKey, or jwt. If you want to send Bundler and Paymaster traffic to Alchemy and Node traffic to a different RPC, you must pass in alchemyConnection and nodeRpcUrl.

alchemy
({
apiKey: string
apiKey
: "YOUR_API_KEY",
}), });