Creating a Smart Account
Impatient? Check out a complete example here.
At the core of account abstraction is the smart account -- an account powered by a smart contract. ZeroDev is built on Kernel, a modular smart account that can be customized with plugins.
When you create a Kernel account, you set it up with a validator, which is a type of plugin that handles how the account validates UserOps. In this tutorial, we will be using the ECDSA validator, which works like a normal EOA by validating signatures from a ECDSA private key. ZeroDev supports other validators such as passkeys and multisig.
We will be using a local private key, but the ECDSA validator also works with third-party auth providers.
Installation
npm i @zerodev/sdk @zerodev/ecdsa-validatoryarn add @zerodev/sdk @zerodev/ecdsa-validatorpnpm i @zerodev/sdk @zerodev/ecdsa-validatorbun add @zerodev/sdk @zerodev/ecdsa-validatorAPI
Picking an EntryPoint
Currently there are two versions of ERC-4337 that are used in production. They are referred to as "EntryPoint 0.6" and "EntryPoint 0.7", where "EntryPoint" refers to the singleton ERC-4337 contract.
If you are building a new application, we recommend using EntryPoint 0.7 (Kernel v3), which gives you the latest and greatest features and optimizations. If you already have an application using EntryPoint 0.6 (Kernel v2), just stick with it -- it will be supported indefinitely.
In this tutorial, we will use EntryPoint 0.7. Start by selecting an EntryPoint:
const entryPoint = getEntryPoint("0.7")Picking a Kernel version
Kernel is the smart account that ZeroDev builds on. ZeroDev SDK used to implicitly use the latest version of Kernel, which has caused some compatibility issues when people upgrade the SDK. Therefore, starting from ZeroDev SDK v5.3, we require that you explicitly specify the Kernel version. This is how you generally should choose:
- If you had already been in production with ZeroDev SDK v4 or lower, use Kernel version 2.4 with EntryPoint 0.6.
- If you had already been in production with ZeroDev SDK v5, use Kernel version 3.0 with EntryPoint 0.7.
- If you are still in development or starting a new project, use Kernel version 3.1 with EntryPoint 0.7.
import { KERNEL_V3_1 } from "@zerodev/sdk/constants"
const kernelVersion = KERNEL_V3_1Creating a public client
In Viem, a public client is an interface to a JSON-RPC API such as Infura or Alchemy.
import { createPublicClient, http } from "viem"
import { base } from 'viem/chains'
const publicClient = createPublicClient({
// In production, you will want to set your RPC provider here (e.g. Infura/Alchemy).
transport: http(),
chain: base,
})Creating a signer
As aforementioned, a Kernel account using a ECDSA validator is "owned" by a signer, which is anything that can sign messages with a private key.
Since Kernel is built on top of Viem, we can use any Viem account as the signer. In this example, we create a signer from a private key:
import { Hex } from "viem"
import { privateKeyToAccount } from "viem/accounts"
const signer = privateKeyToAccount("PRIVATE_KEY" as Hex)Replace PRIVATE_KEY with an actual private key. You can generate a random one here.
Creating a ECDSA validator
Then create a ECDSA validator from the signer:
import { signerToEcdsaValidator } from "@zerodev/ecdsa-validator"
const ecdsaValidator = await signerToEcdsaValidator(publicClient, {
signer,
entryPoint,
kernelVersion
})Create a Kernel account
Next, create a Kernel account with the ECDSA validator:
import { createKernelAccount } from "@zerodev/sdk"
const account = await createKernelAccount(publicClient, {
plugins: {
sudo: ecdsaValidator,
},
entryPoint,
kernelVersion
})Create an account client
Now that we have an account, we can finally construct an "account client," which is the equivalent of a wallet client in Viem that allows you to send UserOps to bundlers.
import { createKernelAccountClient } from "@zerodev/sdk"
import { http } from "viem"
import { base } from 'viem/chains'
const kernelClient = createKernelAccountClient({
account,
// Replace with your chain
chain: base,
// Find the RPC in your ZeroDev dashboard
bundlerTransport: http('ZERODEV_RPC'),
// Required - the public client
client: publicClient,
// Optional -- only if you want to use a paymaster
paymaster: {
getPaymasterData(userOperation) {
return paymasterClient.sponsorUserOperation({userOperation})
}
},
})Note that:
- You need to replace the
ZERODEV_RPCwith the RPC found on your ZeroDev dashboard. - You need to make sure to set the right
chain. paymasteronly needs to be specified if you want to use a paymaster.
Now you are ready to do things with your smart account, like sending UserOps!