Epoch SDK

To send transactions, manage smart contract wallet addresses and signing you can use Epoch SDK

Install SDK

yarn add @epoch-protocol/sdk
# or 
npm i @epoch-protocol/sdk

Setting up Bundler Instance

Bundler Instance is needed to connect to the bundler and perform bundler specific actions like send userOps, getUserOps, getUserOperationHash etc.

You need to the bundlerUrl, ENTRY_POINT address and the chain id to setup Bundler Instance

import { HttpRpcClient } from '@epoch-protocol/sdk'

const bundlerInstance = new HttpRpcClient(bundlerUrl, ENTRY_POINT, parseInt(network.chainId.toString()));
const network = await provider.getNetwork();

Setting up Account API

The Account API is essential to manage user's Smart Contract Wallet.

There are 2 Implementations of the Account API in Epoch's SDK

Simple Account API

Account API Implementation for Eth-Infinitism SimpleAccount.sol

You can get the ENTRY_POINT and FACTORY_ADDRESS from the Github here

import { SimpleAccountAPI } from "@epoch-protocol/sdk";

const ENTRY_POINT = "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789";
const FACTORY_ADDRESS = "0x4A4fC0bF39D191b5fcA7d4868C9F742B342a39c1";

const walletAPIInstance = new SimpleAccountAPI({
  entryPointAddress: ENTRY_POINT,
  owner: signer,
  factoryAddress: FACTORY_ADDRESS,

SAFE Account API

Account API Implementation for SAFE Wallet and ERC4337 Module

For SAFE Account API we need the SAFE Config like below, you can use the SAFE config from the SDK itself or you can pass you own.

export interface SafeConfig {
    safeProxyFactory: string;
    singleton: string;
    fallbackModule: string;
    aaModule: string;
    addModuleLib: string;
    salt: BigNumber;

The safeConfig and salt are essential to calculate an address for your SAFE Account. If the salt changes the address will change as well. So make sure you keep a standardised Salt for your application.

import { SafeAccountAPI } from "@epoch-protocol/sdk";
import { safeDefaultConfig } from "@epoch-protocol/sdk/dist/src/SafeDefaultConfig";

const walletAPIInstance = new SafeAccountAPI({
    entryPointAddress: ENTRY_POINT,
    owner: signer,
    safeConfig: safeDefaultConfig[network.chainId],
    salt: safeDefaultConfig[network.chainId].salt,

Note -

  • ERC4337 Module only works for SAFE Version 1.4.1 and above. The wallet deployed using our SDK will have that version

  • Connecting an existing ERC4337 Compatible Address is WIP and we'll soon add support for it. For now if you know the correct safeConfig and salt you can use that address as well.

Generating Valid Nonce for UserOP

Nonce For Normal Transactions

You don't need to do anything for a normal transaction to calculate nonce. The SDK takes care of it or you can manually pass in the nonce if you like.

const userOp = await walletAPI.createSignedUserOp({
        target: someAddress,
        data: "0x",
        value: 100000000n,
        // nonce: BigInt(someNonce),

Nonce For Automated Transactions

Nonce management through Epoch SDK is slightly different when compared to the usual approach for automated transactions, this is to make sure there are no nonce clashes in case you have many user ops in the queue.

let userOp = {...};

const key = await bundler.getValidNonceKey(userOp);
const nonce = await walletAPI.getNonce(key);

userOp.nonce = nonce;

Sending Transactions

Sending transactions is straightforward.

const userOpHash = await bundler.sendUserOpToBundler(userOp);

// In case of non advanced userops
const txid = await walletAPI.getUserOpReceipt(userOpHash);

Getting Queued UserOps

To get a list of all the transactions that are with the bundler simply run these

const queuedUserOpsList = await bundler.getUserOperations(userAddress);

Deleting Queued UserOp

To delete a specific userOp in the automation queue you need to send a new userOp with the same nonce as the userOp you want to delete.

const deleteOp = await walletAPI.createSignedUserOp({
        target: userSCWalletAddress,
        data: "0x",
        value: 0n,
        nonce: BigInt(userOpToDelete.nonce).toString(),

const deleteUserOpHash = await bundler.deleteAdvancedUserOpFromBundler(deleteOp);

Last updated