Deploy a smart account
You can deploy MetaMask Smart Accounts in two different ways. You can either deploy the smart account automatically when sending the first user operation, or use a manual approach.
Prerequisites
- Install and set up the Delegation Toolkit.
- Configure the Delegation Toolkit.
- Create a MetaMask smart account
Sending first user operation
Whenever you send the first user operation, it checks whether the smart account is already deployed. If the account
is not deployed, the initCode
is added to the user operation to ensure the smart account is deployed within the
same operation. Internally, the initCode
is encoded using the factory and factory data.
- example.ts
- config.ts
import { bundlerClient, smartAccount } from "./config.ts";
import { parseEther } from "viem";
// Appropriate fee per gas must be determined for the specific bundler being used.
const maxFeePerGas = 1n;
const maxPriorityFeePerGas = 1n;
const userOperationHash = await bundlerClient.sendUserOperation({
account: smartAccount,
calls: [
{
to: "0x1234567890123456789012345678901234567890",
value: parseEther("1")
}
],
maxFeePerGas,
maxPriorityFeePerGas
});
import { createPublicClient, http } from "viem";
import { createBundlerClient } from "viem/account-abstraction";
import { sepolia as chain } from "viem/chains";
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
import {
Implementation,
toMetaMaskSmartAccount,
} from "@metamask/delegation-toolkit";
const publicClient = createPublicClient({
chain,
transport: http()
});
const privateKey = generatePrivateKey();
const account = privateKeyToAccount(privateKey);
export const smartAccount = await toMetaMaskSmartAccount({
client: publicClient,
implementation: Implementation.Hybrid,
deployParams: [account.address, [], [], []],
deploySalt: "0x",
signatory: { account },
});
export const bundlerClient = createBundlerClient({
client: publicClient,
transport: http("https://public.pimlico.io/v2/11155111/rpc")
});
Manual approach
To use the manual approach, you can call the MetaMask Smart Account getFactoryArgs
method to retrieve the factory
and factoryData
. This allows you to use a relay account to sponsor the deployment without needing a paymaster.
The factory
represents the contract address responsible for deploying the smart account, while factoryData
contains the
calldata that will be executed by the factory
to deploy the smart account.
The relay account can be either an externally owned account (EOA) or another smart account. This example uses an EOA.
- example.ts
- config.ts
import { walletClient, smartAccount } from "./config.ts";
const { factory, factoryData } = await smartAccount.getFactoryArgs();
// Deploy smart account using relay account.
const hash = await walletClient.sendTransaction({
to: factory,
data: factoryData,
})
import { createPublicClient, createWalletClient, http } from "viem";
import { sepolia as chain } from "viem/chains";
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
import {
Implementation,
toMetaMaskSmartAccount,
} from "@metamask/delegation-toolkit";
const publicClient = createPublicClient({
chain,
transport: http()
});
const privateKey = generatePrivateKey();
const account = privateKeyToAccount(privateKey);
export const smartAccount = await toMetaMaskSmartAccount({
client: publicClient,
implementation: Implementation.Hybrid,
deployParams: [account.address, [], [], []],
deploySalt: "0x",
signatory: { account },
});
const relayAccountPrivateKey = "0x121..";
const relayAccount = privateKeyToAccount(relayAccountPrivateKey)
export const walletClient = createWalletClient({
account: relayAccount,
chain,
transport: http()
})
Next steps
- See Send a user operation to learn how to send user operations.
- See Send a gasless transaction to learn how to use gasless paymaster.