Skip to content

Migration Guide

0.2.0

Using viem 2.20.0

Viem released their own version of account abstraction primitives since version 2.18.0. Since permissionless.js has viem as a peer dependency and are heavily inspired by viem, we will be updating to use the native account abstraction primitives provided by viem. For full documentation on viem's account abstraction primitives, please refer to the viem documentation.

Deprecated createBundlerClient

Viem has default createBundlerClient so we have deprecated the permissionless.js bundler client. This means we have also deprecated the following bundler actions:

  1. chainId
  2. estimateUserOperationGas
  3. getUserOperationByHash
  4. getUserOperationReceipt
  5. sendUserOperation
  6. supportedEntryPoints
  7. waitForUserOperationReceipt

Deprecated getEntryPointVersion

This is no more needed as all the permissionless versions now accepts entryPoint version explicitly.

Deprecated getUserOperationHash

This is part of viem/account-abstraction and now can be directly imported from viem.

Deprecated signUserOperationHashWithECDSA

Deprecated experimental 7677

Changed signerToBiconomySmartAccount to toBiconomySmartAccount

import { toBiconomySmartAccount } from "permissionless/accounts"
import { publicClient } from "./publicClient"
import { entryPoint06Address } from "viem/account-abstraction"
import { privateKeyToAccount, generatePrivateKey } from "viem/accounts"
 
const biconomyAccount = await signerToBiconomySmartAccount(client { 
	signer: privateKeyToAccount(generatePrivateKey()), 
	entryPoint: ENTRYPOINT_ADDRESS_V06, 
	...rest
}) 
const biconomyAccount = await toBiconomySmartAccount({ 
	client: publicClient, 
	owners: [privateKeyToAccount(generatePrivateKey())],
	entryPoint: { 
		address: entryPoint06Address, 
		version: "0.6", 
	} 
	...rest
})	

Changed signerToEcdsaKernelSmartAccount to toEcdsaKernelSmartAccount

import { toEcdsaKernelSmartAccount } from "permissionless/accounts"
import { publicClient } from "./publicClient"
import { entryPoint07Address } from "viem/account-abstraction"
import { privateKeyToAccount, generatePrivateKey } from "viem/accounts"
 
const kernelAccount = await signerToEcdsaKernelSmartAccount(client { 
	signer: privateKeyToAccount(generatePrivateKey()), 
	entryPoint: ENTRYPOINT_ADDRESS_V06, 
	...rest
}) 
const kernelAccount = await toEcdsaKernelSmartAccount({ 
	client: publicClient, 
	owners: [privateKeyToAccount(generatePrivateKey())],
	entryPoint: { // optional, defaults to 0.7
		address: entryPoint07Address, 
		version: "0.7", 
	}, 
	...rest
})	

Changed signerToLightSmartAccount to toLightSmartAccount

import { toLightSmartAccount } from "permissionless/accounts"
import { publicClient } from "./publicClient"
import { entryPoint06Address } from "viem/account-abstraction"
import { privateKeyToAccount, generatePrivateKey } from "viem/accounts"
 
const kernelAccount = await signerToLightSmartAccount(client { 
	signer: privateKeyToAccount(generatePrivateKey()), 
	entryPoint: ENTRYPOINT_ADDRESS_V06, 
	lightAccountVersion: "1.1.0"
	...rest
}) 
const kernelAccount = await toLightSmartAccount({ 
	client: publicClient, 
	owners: [privateKeyToAccount(generatePrivateKey())],
	entryPoint: { // optional, defaults to 0.7
		address: entryPoint06Address, 
		version: "0.6", 
	}, 
	version: "1.1.0" // optional, defaults to "2.0.0"
	...rest
})	

Changed signerToSafeSmartAccount to toSafeSmartAccount

import { toSafeSmartAccount } from "permissionless/accounts"
import { publicClient } from "./publicClient"
import { entryPoint07Address } from "viem/account-abstraction"
import { privateKeyToAccount, generatePrivateKey } from "viem/accounts"
 
const safeAccount = await signerToSafeSmartAccount(client { 
	signer: privateKeyToAccount(generatePrivateKey()), 
	entryPoint: ENTRYPOINT_ADDRESS_V07, 
	safeVersion: "1.4.1"
	...rest
}) 
const safeAccount = await toSafeSmartAccount({ 
	client: publicClient, 
	owners: [privateKeyToAccount(generatePrivateKey())],
	entryPoint: { // optional, defaults to 0.7
		address: entryPoint07Address, 
		version: "0.7", 
	}, 
	version: "1.4.1"
	...rest
})

Changed signerToSimpleSmartAccount to toSimpleSmartAccount

import { toSimpleSmartAccount } from "permissionless/accounts"
import { publicClient } from "./publicClient"
import { entryPoint07Address } from "viem/account-abstraction"
import { privateKeyToAccount, generatePrivateKey } from "viem/accounts"
 
const simpleAccount = await signerToSimpleSmartAccount(client { 
	signer: privateKeyToAccount(generatePrivateKey()), 
	entryPoint: ENTRYPOINT_ADDRESS_V06, 
	...rest
}) 
const simpleAccount = await toSimpleSmartAccount({ 
	client: publicClient, 
	owner: privateKeyToAccount(generatePrivateKey()),
	entryPoint: { // optional, defaults to 0.7
		address: entryPoint07Address, 
		version: "0.7", 
	}, 
	...rest
})

Changed signerToTrustSmartAccount to toTrustSmartAccount

import { toTrustSmartAccount } from "permissionless/accounts"
import { publicClient } from "./publicClient"
import { entryPoint06Address } from "viem/account-abstraction"
import { privateKeyToAccount, generatePrivateKey } from "viem/accounts"
 
const trustAccount = await signerToTrustSmartAccount(client { 
	signer: privateKeyToAccount(generatePrivateKey()), 
	entryPoint: ENTRYPOINT_ADDRESS_V06, 
	...rest
}) 
const trustAccount = await toTrustSmartAccount({ 
	client: publicClient, 
	owner: privateKeyToAccount(generatePrivateKey()),
	entryPoint: { 
		address: entryPoint06Address, 
		version: "0.6", 
	}, 
	...rest
})

Deprecated `privateKeyTo<account-name>SmartAccount

All the private key to account conversion functions are now deprecated. You can use to<account-name>SmartAccount to create a smart account instead.

Example:

import { toSafeSmartAccount } from "permissionless/accounts"
import { publicClient } from "./publicClient"
import { entryPoint07Address } from "viem/account-abstraction"
import { privateKeyToAccount } from "viem/accounts"
 
const safeAccount = await toSafeSmartAccount({
	client: publicClient,
	owners: [privateKeyToAccount(privateKey)],
	entryPoint: {
		address: entryPoint07Address,
		version: "0.7"
	}, // global entrypoint
	version: "1.4.1",
})

Merged createPimlicoPaymasterClient and createPimlicoBundlerClient to createPimlicoClient

import { http } from "viem";
import { entryPoint07Address } from "viem/account-abstraction"
import { createPimlicoClient } from "permissionless/clients/pimlico"
 
const pimlicoUrl = `https://api.pimlico.io/v2/sepolia/rpc?apikey=<PIMLICO_API_KEY>`
 
 
const pimlicoPaymasterClient = createPimlicoPaymasterClient({ 
	chain: foundry, 
	transport: http(paymasterRpc), 
	entryPoint: ENTRYPOINT_ADDRESS_V07
}) 
 
const pimlicoPaymasterClient = createPimlicoPaymasterClient({ 
	chain: foundry, 
	transport: http(paymasterRpc), 
	entryPoint: ENTRYPOINT_ADDRESS_V07
}) 
 
const pimlicoClient = createPimlicoClient({ 
	transport: http(pimlicoUrl), 
	entryPoint: { // Optional, defaults to 0.7
		address: entryPoint07Address, 
		version: "0.7", 
	} 
}) 

All the previous actions that were part of createPimlicoBundlerClient and createPimlicoPaymasterClient are now part of createPimlicoClient and can also be acccessed individually.

Pimlico client also extends actions of Paymaster client by viem. It uses ERC-7677 as the default standard to communicate with Pimlico paymasters.

Deprecated smartAccountClient.deployContract

Changed smartAccountClient.prepareUserOperationRequest to smartAccountClient.prepareUserOperation

import { smartAccountClient } from './config'
 
const userOperation = await smartAccountClient.prepareUserOperationRequest({ 
	userOperation: { 
		callData: await smartAccountClient.account.encodeCallData({ 
			to: CONTRACT_ADDRESS, 
			data: "0x", 
			value: parseEther('1') 
		}), 
	} 
}) 
 
const userOperation = await smartAccountClient.prepareUserOperation({ 
	calls: [{ 
		to: CONTRACT_ADDRESS, 
		data: "0x", 
		value: parseEther('1') 
	}] 
}) 

Changed smartAccountClient.sendUserOperation

import { smartAccountClient } from './config'
 
const userOperationHash = await smartAccountClient.sendUserOperation({
    userOperation: { 
        sender: "0x6152348912fb1e78c9037D83f9d4524D4a2988Ed",
        nonce: "0x8104e3ad430ea6d354d013a6789fdfc71e671c4300000000000000000000",
        factory: "0xd703aaE79538628d27099B8c4f621bE4CCd142d5",
        factoryData: "0xc5265d5d0000000000000000000000006723b44abeec4e71ebe3232bd5b455805badd22f0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e412af322c018104e3ad430ea6d354d013a6789fdfc71e671c4300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000014ec787ae5c34157ce61e751e54dff3612d4117663000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
        callData: "0xe9ae5c530100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000594bc666500faed35dc741f45a35c571399560d80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e4e688a440000000000000000000000000c67e4838d4e6682e3a5f9ec27f133e76cb3855f30000000000000000000000006152348912fb1e78c9037d83f9d4524d4a2988ed00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000041aebdfb90adba067d9304980a300636506c3c9081b01f64b04f108407a890602377625ef9096946cc028743123646881c7e31a1c8d6698132c188cb4c33a3f9151b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
        maxFeePerGas: 99985502n,
        maxPriorityFeePerGas: 1221000n,
        preVerificationGas: 66875n,
        verificationGasLimit: 373705n,
        callGasLimit: 170447n,
        paymaster: "0x9d0021A869f1Ed3a661Ffe8C9B41Ec6244261d98",
        paymasterData: "0x000000000000000000000000000000000000000000000000000000006638ab470000000000000000000000000000000000000000000000000000000000000000e9311d1945317dc6a1c750e8e6d0641a598beb59edc5652ed3807ca57338a7a107123e1a479386b2c91f242d2dff367c18e0ad9d1021acfe47afc890e252644e1c",
        paymasterVerificationGasLimit: 20274n,
        paymasterPostOpGasLimit: 1n,
        signature: "0xa58d445f26b126fcd644975f0c66bd45f3e6e5b9c1acec2eeee490aa9341cfc312988231c228f84e12eac2d90ed9cd8825546d70d73b2e0fabdd6c8089ab29581b",
    } 
})

It also allows you to submit user operations with using calls object like with sendTransaction. You can read more about it here.

Deprecated smartAccountClient.sendTransactions

Use smartAccountClient.sendTransaction instead. It now accepts an array of transactions. Read more about it here.

Example:

import { account, smartAccountClient } from './config'
import { parseEther } from 'viem'
const hash = await smartAccountClient.sendTransaction({
  account,
  calls: [{ 
    to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', 
    value: parseEther('1') 
  }, { 
    abi: wagmiAbi, 
    functionName: 'mint', 
    to: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2', 
  }] 
})

Error handling

We have deprecated permissionless.js error handling and now use viem's error handling.