Deploy a smart account
You can deploy MetaMask Smart Accounts in two different ways. You can either deploy a smart account automatically when sending the first user operation, or manually deploy the account.
Prerequisites
- Install and set up the Delegation Toolkit.
- Configure the Delegation Toolkit.
- Create a MetaMask smart account.
Deploy with the first user operation
When you send the first user operation from a smart account, the Delegation Toolkit checks whether the account is already deployed. If the account
is not deployed, the toolkit adds the initCode
to the user operation to deploy the account within the
same operation. Internally, the initCode
is encoded using the factory
and factoryData
.
- 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")
});
Deploy manually
To deploy a smart account manually, call the getFactoryArgs
method from the smart account 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
- Learn more about sending user operations.
- To sponsor gas for end users, see how to send a gasless transaction.