Skip to content

How to use an ERC-7579 compatible smart account with permissionless.js

ERC-7579 defines a standard for modular smart account interfaces. It also defines behavior for interoperability with minimal restrictions for accounts and modules.

Currently Safe and Kernel are the only smart accounts that implements ERC-7579.

For this guide, we will use the Safe smart accounts as an example. If you would like to find out more about the Safe smart account, you can check out the Safe-specific guide.

This guide will show you how to create and use a ERC-7579 compatible smart account with permissionless.js.

Steps

Import the required packages

import { signerToSafeSmartAccount } from "permissionless/accounts"
import { signer } from "../signer"
import { ENTRYPOINT_ADDRESS_V07, createSmartAccountClient } from "permissionless"
import { sepolia } from "viem/chains"
import { encodePacked, http } from "viem"
import { createPimlicoBundlerClient } from "permissionless/clients/pimlico"
import { pimlicoPaymasterActions } from "permissionless/actions/pimlico"
import { erc7579Actions } from "permissionless/actions/erc7579"
import { createPublicClient } from "viem"

Create the clients

First we must create the public, bundler, and (optionally) paymaster clients that will be used to interact with the SafeAccount.

const apiKey = "YOUR_PIMLICO_API_KEY"
const bundlerUrl = `https://api.pimlico.io/v2/sepolia/rpc?apikey=${apiKey}`
 
const publicClient = createPublicClient({
	transport: http("https://rpc.ankr.com/eth_sepolia"),
})
 
const pimlicoBundlerClient = createPimlicoBundlerClient({
	transport: http(bundlerUrl),
	entryPoint: ENTRYPOINT_ADDRESS_V07,
}).extend(pimlicoPaymasterActions(ENTRYPOINT_ADDRESS_V07))

Create the SafeAccount

You can also pass an address to use an already created SafeAccount.

const safeAccount = await signerToSafeSmartAccount(publicClient, {
	signer,
	safeVersion: "1.4.1",
	entryPoint: ENTRYPOINT_ADDRESS_V07,
	safe4337ModuleAddress: "0x7579EE8307284F293B1927136486880611F20002",
	erc7579LaunchpadAddress: "0x7579011aB74c46090561ea277Ba79D510c6C00ff",
	attesters: ["0x000000333034E9f539ce08819E12c1b8Cb29084d"],
	attestersThreshold: 1,
})

Create the smart account client and extend it with the ERC7579 actions

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: safeAccount,
	entryPoint: ENTRYPOINT_ADDRESS_V07,
	chain: sepolia,
	bundlerTransport: http(bundlerUrl),
	middleware: {
		gasPrice: async () => {
			return (await pimlicoBundlerClient.getUserOperationGasPrice()).fast
		},
		sponsorUserOperation: pimlicoBundlerClient.sponsorUserOperation,
	},
}).extend(erc7579Actions({ entryPoint: ENTRYPOINT_ADDRESS_V07 }))

Interact with the 7579 actions

You can install a module on the Safe account using the installModule action.

const ownableExecutorModule = "0x4Fd8d57b94966982B62e9588C27B4171B55E8354"
const moduleData = encodePacked(["address"], ["0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"])
const userOpHash = await smartAccountClient.installModule({
	type: "executor",
	address: ownableExecutorModule,
	context: moduleData,
})
 
const receipt = await pimlicoBundlerClient.waitForUserOperationReceipt({ hash: userOpHash })

You can also call all other ERC7579 actions, example supportsExecutionMode.

const isExecutionModeSupported = await smartAccountClient.supportsExecutionMode({
	type: "delegatecall",
	revertOnError: true,
	selector: "0x",
	context: "0x",
})