Migration Guide
API v2 => API v3
With API v3, the notable changes are:
- A ZeroDev project can now support multiple networks.
- The same RPC can be used for both bundler and paymaster.
Here's what a v3 RPC looks like:
https://rpc.zerodev.app/api/v3/55569f2d-5bbf-43e5-90cc-75d43c26e007/chain/42161Note that the last part 42161 is the chain ID. Therefore, if you'd like to programmatically use the RPC for different chains, you can do something like:
// replace the prefix with your own RPC prefix
const rpcPrefix = `https://rpc.zerodev.app/api/v3/55569f2d-5bbf-43e5-90cc-75d43c26e007/chain/`
const rpc = rpcPrefix + chain.idOf course, you can also simply copy the RPC for different chains from the dashboard.

The same RPC can then be used as both the bundler RPC and the paymaster RPC. See the tutorial for an example.
SDK 5.3.x => 5.4.x
In version 5.4.x of the @zerodev/sdk, we've migrated to using viem@2.18.x with native Account Abstraction (AA) modules instead of the permissionless package. This change brings significant updates to types, imports, function signatures, and overall API usage.
This guide will help you migrate your codebase to be compatible with the new version.
Update dependencies
-
Remove the
permissionlesspackage:npm uninstall permissionless -
Ensure you have
viem@^2.21.40version
Update permissionless Account Abstractions
Replace any imports from permissionless with the equivalent from viem/account-abstraction or @zerodev/sdk if applicable.
Update Type Definitions
Replace EntryPoint Types
import type { EntryPoint } from 'permissionless/types';
import type { EntryPointVersion } from 'viem/account-abstraction'; Replace UserOperation Types
import type { UserOperation } from 'permissionless/types';
import type { UserOperation } from 'viem/account-abstraction'; Replaced entryPoint: Address with entryPoint: { address: Address; version: EntryPointVersion}
For createKernelAccount and signerToEcdsaValidator among other plugins, replace the entryPoint parameter as shown:
import { getEntryPoint } from "@zerodev/sdk/constants";
createKernelAccount(publicClient, {
// ...
entryPoint: ENTRYPOINT_ADDRESS_V07,
entryPoint: getEntryPoint("0.7"),
})signerToEcdsaValidator(publicClient, {
// ...
entryPoint: ENTRYPOINT_ADDRESS_V07,
entryPoint: getEntryPoint("0.7"),
})Removed entryPoint from createKernelAccountClient
const kernelClient = createKernelAccountClient({
entryPoint,
// ...
});Replaced middleware.sponsorUserOperation from createKernelAccountClient with paymaster.getPaymasterData
const kernelClient = createKernelAccountClient({
middleware: {
sponsorUserOperation: paymasterClient.sponsorUserOperation,
},
paymaster: {
getPaymasterData(userOperation) {
return paymasterClient.sponsorUserOperation({ userOperation })
}
}
// ...
});Added client to createKernelAccountClient
client is now required in createKernelAccountClient.
const kernelClient = createKernelAccountClient({
client: publicClient,
// ...
});Added estimateFeesPerGas to userOperation in createKernelAccountClient
estimateFeesPerGas is now required in userOperation in createKernelAccountClient to estimate the gas price for the user operation.
The default gas prices might be too high, so it's recommended to use this function to estimate the gas price.
const kernelClient = createKernelAccountClient({
userOperation: {
estimateFeesPerGas: async ({bundlerClient}) => {
return getUserOperationGasPrice(bundlerClient)
}
},
// ...
});kernelClient.sendUserOperation and kernelClient.signUserOperation now take userOperation properties directly
await kernelClient.sendUserOperation({
userOperation: { sender, callData, nonce, ...rest },
sender,
callData,
nonce,
...rest
});
await kernelClient.signUserOperation({
userOperation: { sender, callData, nonce, ...rest },
sender,
callData,
nonce,
...rest
});Replaced account.encodeCallData with account.encodeCalls
await account.encodeCallData(
{
to: zeroAddress,
value: BigInt(0),
data: "0x",
callType
},
),
await account.encodeCalls([
{
to: zeroAddress,
value: BigInt(0),
data: "0x",
},
], callType), Replaced kernelClient.sendTransactions with kernelClient.sendTransaction
await kernelClient.sendTransactions({
transactions: [
// ...
],
});
await kernelClient.sendTransaction({
calls: [
// ...
],
}); KernelAccountClient extends bundlerActions by default
For example:
const bundlerClient = kernelClient.extend(bundlerActions(entryPoint));
await bundlerClient.waitForUserOperationReceipt({
hash: userOpHash,
});
await kernelClient.waitForUserOperationReceipt({ hash }) Merged bundlerClient.sendUserOperation and kernelClient.sendUserOperation
kernelClient.sendUserOperation now prepares the userOperation if needed and directly calls eth_sendUserOperation.
SDK 5.1.x => 5.2.x
Most functions now take an entryPoint param
EntryPoint 0.7 is the most recent update to ERC-4337, but we will still be supporting EntryPoint 0.6.
The SDK will automatically use Kernel v3 for EntryPoint 0.7, and Kernel v2 for EntryPoint 0.6.
You will need to specify an entryPoint parameter to many functions, including:
- Functions for creating validators, such as
signerToEcdsaValidator - Functions for creating Kernel accounts, such as
createKernelAccount - Function for creating Kernel client:
createKernelAccountClient
For example:
import { ENTRYPOINT_ADDRESS_V06, ENTRYPOINT_ADDRESS_V07 } from "permissionless"
// If migrating a live app
const entryPoint = ENTRYPOINT_ADDRESS_V06
// If launching a new app
const entryPoint = ENTRYPOINT_ADDRESS_V07
const account = await createKernelAccount(publicClient, {
plugins: {
sudo: ecdsaValidator,
},
entryPoint,
})- If you are migrating a live app that is using EntryPoint 0.6 (Kernel v2), set
entryPointtoENTRYPOINT_ADDRESS_V06. - If you are launching a new app, set
entryPointtoENTRYPOINT_ADDRESS_V07.
Replaced transport with bundlerTransport inside createKernelAccountClient
const kernelClient = createKernelAccountClient({
transport: http(bundlerUrl),
bundlerTransport: http(bundlerUrl),
// ...
})Replaced sponsorUserOperation with middleware.sponsorUserOperation
Instead of accepting just a sponsorUserOperation middleware, createSmartAccountClient now accepts a middleware function that can specify a sponsorUserOperation function internally, as well as a gasPrice function.
const kernelClient = createKernelAccountClient({
sponsorUserOperation: paymasterClient.sponsorUserOperation,
middleware: {
sponsorUserOperation: paymasterClient.sponsorUserOperation,
},
// ...
})