Getting started with Modular Account V2 is straightforward. Below, you'll create a Modular Account V2 from a signer and send a transaction through viem's bundler client. Your MAv2 deploys onchain on the first user operation.
@alchemy/wallet-apis already uses Modular Account V2 as its default smart account — createSmartWalletClient gives you MAv2 plus account creation, gas estimation, and the bundler client behind a single call. This page covers the lower-level path: instantiating MAv2 directly from @alchemy/smart-accounts and pairing it with viem's bundler client. Use it when you need lower-level control over custom wiring.
Prerequisites
- minimum Typescript version of 5
Installation
yarn add @alchemy/smart-accounts @alchemy/aa-infra viemFor Modular Account V2, the address is derived from the owner and an optional salt (default 0n). Same owner + same salt → same address. Pass accountAddress to connect to an existing account whose contract-derived address doesn't match the signer (e.g. the original owner is no longer in your control).
import { createClient } from "viem";
import { privateKeyToAccount, generatePrivateKey } from "viem/accounts";
import { sepolia } from "viem/chains";
import { alchemyTransport } from "@alchemy/common";
import { toModularAccountV2 } from "@alchemy/smart-accounts";
const ALCHEMY_API_KEY = "your-api-key"; // https://dashboard.alchemy.com/apps
const transport = alchemyTransport({ apiKey: ALCHEMY_API_KEY });
const rpcClient = createClient({ chain: sepolia, transport });
const account = await toModularAccountV2({
client: rpcClient,
owner: privateKeyToAccount(generatePrivateKey()),
// mode: "7702", // optional — defaults to "default"
});Choosing which mode to use
Two variants of Modular Account V2 are available: default and 7702.
- (Recommended)
defaultprovides the cheapest, most flexible smart wallet. 7702if you want EIP-7702 support — see the EIP-7702 guide.
Pair the account with viem's bundler client (and @alchemy/aa-infra's fee estimator) to submit user operations.
import { createBundlerClient } from "viem/account-abstraction";
import { createClient, parseEther } from "viem";
import { privateKeyToAccount, generatePrivateKey } from "viem/accounts";
import { sepolia } from "viem/chains";
import { alchemyTransport } from "@alchemy/common";
import { toModularAccountV2 } from "@alchemy/smart-accounts";
import { estimateFeesPerGas } from "@alchemy/aa-infra";
const ALCHEMY_API_KEY = "your-api-key";
const transport = alchemyTransport({ apiKey: ALCHEMY_API_KEY });
const rpcClient = createClient({ chain: sepolia, transport });
const account = await toModularAccountV2({
client: rpcClient,
owner: privateKeyToAccount(generatePrivateKey()),
});
const bundlerClient = createBundlerClient({
account,
client: rpcClient,
chain: sepolia,
transport,
userOperation: { estimateFeesPerGas },
});
const hash = await bundlerClient.sendUserOperation({
calls: [{
to: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", // vitalik
data: "0x",
value: parseEther("0"),
}],
});
console.log("UO hash:", hash, "\nMAv2 address:", account.address);