permissionless.js: ERC-4337 Account Abstraction Utilities

0.3.5 · active · verified Sun Apr 19

permissionless.js is a robust, TypeScript-first utility library designed to simplify interaction with Ethereum's ERC-4337 Account Abstraction standard. It provides a comprehensive set of client interfaces and helper functions for integrating with ERC-4337 Bundlers and Paymasters, as well as tools for creating and managing Smart Accounts. This enables developers to build dApps with advanced features like gasless transactions, multi-signature accounts, and custom validation logic. The library leverages `viem` for core Ethereum interactions and `ox` for cryptographic operations, offering a type-safe and modular approach to account abstraction development. The current stable version is 0.3.5, with frequent patch releases addressing bug fixes and minor feature enhancements, alongside occasional minor releases for larger feature sets or dependency upgrades.

Common errors

Warnings

Install

Imports

Quickstart

Demonstrates setting up `viem` and `permissionless` clients, creating a simple smart account, preparing a user operation with paymaster sponsorship, and logging the prepared UserOperation.

import { createPublicClient, http, privateKeyToAccount, Address } from 'viem';
import { optimismSepolia } from 'viem/chains';
import { createBundlerClient, createSmartAccountClient, createPimlicoPaymasterClient, ENTRYPOINT_ADDRESS_V07, signerToSimpleSmartAccount } from 'permissionless';
import { type UserOperation } from 'permissionless/types';

const privateKey = (process.env.PRIVATE_KEY as `0x${string}`) || '0x...'; // Replace with your private key
const pimlicoRpcUrl = process.env.PIMLICO_RPC_URL || 'https://api.pimlico.io/v2/optimism-sepolia/rpc?apikey=YOUR_PIMLICO_API_KEY';

const chain = optimismSepolia;
const publicClient = createPublicClient({
  chain,
  transport: http(chain.rpcUrls.default.http[0]),
});

const bundlerClient = createBundlerClient({
  chain,
  transport: http(pimlicoRpcUrl),
  entryPoint: ENTRYPOINT_ADDRESS_V07
});

const paymasterClient = createPimlicoPaymasterClient({
  chain,
  transport: http(pimlicoRpcUrl),
  entryPoint: ENTRYPOINT_ADDRESS_V07
});

async function sendSimpleUserOperation() {
  const signer = privateKeyToAccount(privateKey);

  const simpleSmartAccountClient = await signerToSimpleSmartAccount(publicClient, {
    signer,
    entryPoint: ENTRYPOINT_ADDRESS_V07,
    factoryAddress: '0x9406Cc6185a346906296840746125a0E447645E4' // Example SimpleAccountFactory
  });

  const smartAccountClient = createSmartAccountClient({
    account: simpleSmartAccountClient,
    chain,
    transport: http(pimlicoRpcUrl),
    entryPoint: ENTRYPOINT_ADDRESS_V07
  });

  console.log(`Smart account address: ${smartAccountClient.account.address}`);

  const userOperation = await smartAccountClient.prepareUserOperation({
    userOperation: {
      callData: await smartAccountClient.account.encodeCallData({
        to: smartAccountClient.account.address,
        value: 0n,
        data: '0x'
      })
    },
    sponsorUserOperation: async ({ userOperation }) => {
      return paymasterClient.sponsorUserOperation({
        userOperation,
        entryPoint: ENTRYPOINT_ADDRESS_V07
      });
    }
  });

  // For demonstration, not actually sending a transaction
  console.log('Prepared UserOperation:', userOperation);

  // To send, uncomment:
  // const userOpHash = await smartAccountClient.sendUserOperation({
  //   userOperation: userOperation,
  //   entryPoint: ENTRYPOINT_ADDRESS_V07
  // });
  // console.log(`UserOperation sent, hash: ${userOpHash}`);
  // const receipt = await bundlerClient.waitForUserOperationReceipt({ hash: userOpHash });
  // console.log('UserOperation receipt:', receipt);
}

sendSimpleUserOperation().catch(console.error);

view raw JSON →