Skip to content

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/42161

Note 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.id

Of 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

  1. Remove the permissionless package:

    npm uninstall permissionless
  2. Ensure you have viem@^2.21.40 version

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 entryPoint to ENTRYPOINT_ADDRESS_V06.
  • If you are launching a new app, set entryPoint to ENTRYPOINT_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, 
	}, 
  // ...
})