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 multiOwnerLightAccountClientmultiOwnerLightAccountClient } from "./client";
const const owners: anyowners = await import multiOwnerLightAccountClientmultiOwnerLightAccountClient.anyaccount.anygetOwnerAddresses();
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): AlchemyTransportCreates 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: Chainsepolia } 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(): HexgeneratePrivateKey } 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(): HexgeneratePrivateKey()),
chain: {
blockExplorers?: {
[key: string]: ChainBlockExplorer;
default: ChainBlockExplorer;
} | undefined;
... 7 more ...;
testnet?: boolean | undefined;
} & ChainConfig<...>Chain for the client.
chain: const sepolia: Chainsepolia,
transport: AlchemyTransportThe RPC transport
transport: function alchemy(config: AlchemyTransportConfig): AlchemyTransportCreates 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: stringapiKey: "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 multiOwnerLightAccountClientmultiOwnerLightAccountClient } 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: anyopHash = await import multiOwnerLightAccountClientmultiOwnerLightAccountClient.anyupdateOwners({
ownersToAdd: any[]ownersToAdd,
ownersToRemove: any[]ownersToRemove,
});
const const txHash: anytxHash =
await import multiOwnerLightAccountClientmultiOwnerLightAccountClient.anywaitForUserOperationTransaction({
hash: anyhash: const opHash: anyopHash,
});
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): AlchemyTransportCreates 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: Chainsepolia } 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(): HexgeneratePrivateKey } 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(): HexgeneratePrivateKey()),
chain: {
blockExplorers?: {
[key: string]: ChainBlockExplorer;
default: ChainBlockExplorer;
} | undefined;
... 7 more ...;
testnet?: boolean | undefined;
} & ChainConfig<...>Chain for the client.
chain: const sepolia: Chainsepolia,
transport: AlchemyTransportThe RPC transport
transport: function alchemy(config: AlchemyTransportConfig): AlchemyTransportCreates 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: stringapiKey: "YOUR_API_KEY",
}),
});