How to create and use a Kernel account with permissionless.js
Kernel is a modular smart account that supports plugins, which are smart contracts that extend the account's functionalities. You can use Kernel with popular plugins such as session keys and account recovery, and even write your own plugins. Kernel is compatible with ERC-7579.
Picking an EntryPoint
Kernel is compatible with EntryPoint versions v0.6 and v0.7.
If you are building a new application, we recommend using EntryPoint v0.7, which gives you the latest and greatest features and optimizations. If you already have an application using Kernel on EntryPoint v0.6, just stick with it -- it will be supported indefinitely.
In this guide, we will use EntryPoint v0.7.
Steps
Import the required packages
import { ENTRYPOINT_ADDRESS_V07, createSmartAccountClient } from "permissionless"
import { signerToEcdsaKernelSmartAccount } from "permissionless/accounts"
import {
createPimlicoBundlerClient,
createPimlicoPaymasterClient,
} from "permissionless/clients/pimlico"
import { createPublicClient, getContract, http, parseEther } from "viem"
import { sepolia } from "viem/chains"
Create the clients
First we must create the public, bundler, and (optionally) paymaster clients that will be used to interact with the Kernel account.
export const publicClient = createPublicClient({
transport: http("https://rpc.ankr.com/eth_sepolia"),
})
export const paymasterClient = createPimlicoPaymasterClient({
transport: http("https://api.pimlico.io/v2/sepolia/rpc?apikey=API_KEY"),
entryPoint: ENTRYPOINT_ADDRESS_V07,
})
export const pimlicoBundlerClient = createPimlicoBundlerClient({
transport: http("https://api.pimlico.io/v2/sepolia/rpc?apikey=API_KEY"),
entryPoint: ENTRYPOINT_ADDRESS_V07,
})
Create the signer
Kernel accounts can work with a variety of signing algorithms such as ECDSA, passkeys, and multisig. In permissionless.js, the default Kernel account validates ECDSA signatures. Any signer can be used as a signer for the Kernel account.
For example, to create a signer based on a private key:
import { privateKeyToAccount } from "viem/accounts"
const signer = privateKeyToAccount("0xPRIVATE_KEY")
Create the Kernel account
With a signer, you can create a Kernel account as such:
const kernelAccount = await signerToEcdsaKernelSmartAccount(publicClient, {
entryPoint: ENTRYPOINT_ADDRESS_V07,
signer: signer,
index: 0n, // optional
address: "0x...", // optional, only if you are using an already created account
})
The Kernel address is computed deterministically from the signer, but you can optionally pass an index
to create any number of different accounts using the same signer. You can also pass an address
to use an already created Kernel account.
Create the smart account client
The smart account client is a permissionless.js client that is meant to serve as an almost drop-in replacement for viem's walletClient.
const smartAccountClient = createSmartAccountClient({
account: kernelAccount,
entryPoint: ENTRYPOINT_ADDRESS_V07,
chain: sepolia,
bundlerTransport: http("https://api.pimlico.io/v2/sepolia/rpc?apikey=API_KEY"),
middleware: {
sponsorUserOperation: paymasterClient.sponsorUserOperation, // optional
gasPrice: async () => (await pimlicoBundlerClient.getUserOperationGasPrice()).fast, // use pimlico bundler to get gas prices
},
})
Send a transaction
Transactions using permissionless.js simply wrap around user operations. This means you can switch to permissionless.js from your existing viem EOA codebase with minimal-to-no changes.
const txHash = await smartAccountClient.sendTransaction({
to: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
value: parseEther("0.1"),
})
This also means you can also use viem Contract instances to transact without any modifications.
const nftContract = getContract({
address: "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2",
abi: nftAbi,
client: {
public: publicClient,
wallet: smartAccountClient,
},
})
const txHash = await nftContract.write.mint()
You can also send an array of transactions in a single batch.
const txHash = await smartAccountClient.sendTransactions({
transactions: [
{
to: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
value: parseEther("0.1"),
data: "0x",
},
{
to: "0x1440ec793aE50fA046B95bFeCa5aF475b6003f9e",
value: parseEther("0.1"),
data: "0x1234",
},
],
})