IntroductionConcepts

Smart Account Client

The SmartAccountClient is an extension of viem’s Client which allows you to interact with Smart Contract Accounts. You can use the Smart Account Client to send User Operations, sign messages with your account, sponsor gas, and other account-related actions.

The client is also EIP-1193 compliant, meaning it can easily be swapped out in place of other web3 providers (eg. window.ethereum).

Account hoisting

If you’re using the React or Core packages, the Smart Account Clients already handle hoisting and creation of account instances. The Smart Contracts package exports client definitions for hoisted instances of the Smart Contract Accounts.

When creating a Smart Account Client, you have two options:

  1. You can pass an account instance directly into the client constructor. Doing so will use that account for all of the actions you perform with the client, and you won’t have to pass in the account each time you call a method. This is known as “account hoisting”.
  2. You can create a client without passing in an account. As a result, you’ll have to pass in an account instance to each method you call on the client. This is really useful if you want to manage multiple instances of an account in your app, and want to use one client for all of them.

Let’s see an example:

import {
  
function createAlchemySmartAccountClient<TChain extends Chain = Chain, TAccount extends SmartContractAccount | undefined = SmartContractAccount | undefined, TContext extends UserOperationContext | undefined = UserOperationContext | undefined>(params: AlchemySmartAccountClientConfig<TChain, TAccount, TContext>): AlchemySmartAccountClient<TChain, TAccount, Record<string, never>, TContext>
createAlchemySmartAccountClient
,
const sepolia: Chain
sepolia
,
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
,
} from "@account-kit/infra"; import {
function createLightAccount<TTransport extends Transport = Transport, TSigner extends SmartAccountSigner = SmartAccountSigner<any>, TLightAccountVersion extends LightAccountVersion<"LightAccount"> = "v2.0.0">(config: CreateLightAccountParams<TTransport, TSigner, TLightAccountVersion>): Promise<LightAccount<TSigner, TLightAccountVersion>>
createLightAccount
} from "@account-kit/smart-contracts";
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 http<rpcSchema extends RpcSchema | undefined = undefined, raw extends boolean = false>(url?: string | undefined, config?: HttpTransportConfig<rpcSchema, raw>): HttpTransport<rpcSchema, raw>
http
} from "viem";
import {
function generatePrivateKey(): Hex
generatePrivateKey
} from "viem/accounts";
// with account hoisting const
const transport: AlchemyTransport
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" });
const
const hoistedClient: { [x: string]: never; account: LightAccount<LocalAccountSigner<{ address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; ... 6 more ...; type: "local"; }>, "v2.0.0">; ... 85 more ...; extend: <const client extends { ...; } & ExactPartial<...>>(fn: (client: Client<...>) => client) => Client<...>; }
hoistedClient
=
createAlchemySmartAccountClient<Chain, LightAccount<LocalAccountSigner<{ address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; ... 6 more ...; type: "local"; }>, "v2.0.0">, UserOperationContext | undefined>(params: AlchemySmartAccountClientConfig<...>): { ...; }
createAlchemySmartAccountClient
({
transport: AlchemyTransport

The RPC transport

transport
,
chain?: Chain | undefined

Chain for the client.

chain
:
const sepolia: Chain
sepolia
,
account?: LightAccount<LocalAccountSigner<{ address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; ... 6 more ...; type: "local"; }>, "v2.0.0"> | undefined
account
: await
createLightAccount<AlchemyTransport, LocalAccountSigner<{ address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; ... 6 more ...; type: "local"; }>, "v2.0.0">(config: CreateLightAccountParams<...>): Promise<...>
createLightAccount
({
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: Chain
chain
:
const sepolia: Chain
sepolia
,
transport: AlchemyTransport
transport
,
}), }); const
const signature: `0x${string}`
signature
= await
const hoistedClient: { [x: string]: never; account: LightAccount<LocalAccountSigner<{ address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; ... 6 more ...; type: "local"; }>, "v2.0.0">; ... 85 more ...; extend: <const client extends { ...; } & ExactPartial<...>>(fn: (client: Client<...>) => client) => Client<...>; }
hoistedClient
.
signMessage: (args: SignMessageParameters<LightAccount<LocalAccountSigner<{ address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; ... 6 more ...; type: "local"; }>, "v2.0.0">>) => Promise<Hex>
signMessage
({
message: SignableMessage
message
: "Hello world! " });
// without account hoisting const
const nonHoistedClient: { [x: string]: never; account: SmartContractAccount | undefined; batch?: { multicall?: boolean | Prettify<MulticallBatchOptions> | undefined; } | undefined; ... 84 more ...; extend: <const client extends { ...; } & ExactPartial<...>>(fn: (client: Client<...>) => client) => Client<...>; }
nonHoistedClient
=
createAlchemySmartAccountClient<Chain, SmartContractAccount | undefined, UserOperationContext | undefined>(params: AlchemySmartAccountClientConfig<Chain, SmartContractAccount | undefined, UserOperationContext | undefined>): { ...; }
createAlchemySmartAccountClient
({
transport: AlchemyTransport

The RPC transport

transport
,
chain?: Chain | undefined

Chain for the client.

chain
:
const sepolia: Chain
sepolia
,
}); const
const lightAccount: LightAccount<LocalAccountSigner<{ address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; ... 6 more ...; type: "local"; }>, "v2.0.0">
lightAccount
= await
createLightAccount<AlchemyTransport, LocalAccountSigner<{ address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; ... 6 more ...; type: "local"; }>, "v2.0.0">(config: CreateLightAccountParams<...>): Promise<...>
createLightAccount
({
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: Chain
chain
:
const sepolia: Chain
sepolia
,
transport: AlchemyTransport
transport
,
}); const
const signature2: `0x${string}`
signature2
= await
const nonHoistedClient: { [x: string]: never; account: SmartContractAccount | undefined; batch?: { multicall?: boolean | Prettify<MulticallBatchOptions> | undefined; } | undefined; ... 84 more ...; extend: <const client extends { ...; } & ExactPartial<...>>(fn: (client: Client<...>) => client) => Client<...>; }
nonHoistedClient
.
signMessage: (args: SignMessageParameters<SmartContractAccount | undefined>) => Promise<Hex>
signMessage
({
message: SignableMessage
message
: "Hello world! ",
account: SmartContractAccount<string, keyof EntryPointRegistryBase<unknown>>
account
:
const lightAccount: LightAccount<LocalAccountSigner<{ address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; ... 6 more ...; type: "local"; }>, "v2.0.0">
lightAccount
,
});

Smart Account Client actions

Because the Smart Account Clients are extensions of viem’s clients, they support extensions via the .extend method. The base client already includes a number of actions by default. You can find additional details about these actions in the @aa-sdk/core documentation.