npm stats
  • Search
  • About
  • Repo
  • Sponsor
  • more
    • Search
    • About
    • Repo
    • Sponsor

Made by Antonio Ramirez

@tetherto/wdk-wallet-evm-erc-4337

1.0.0-beta.5

@subash.77

npmHomeRepoSnykSocket
Downloads:16418
$ npm install @tetherto/wdk-wallet-evm-erc-4337
DailyWeeklyMonthlyYearly

@tetherto/wdk-wallet-evm-erc-4337

Note: This package is currently in beta. Please test thoroughly in development environments before using in production.

A simple and secure package to manage ERC-4337 compliant wallets for EVM-compatible blockchains. This package provides a clean API for creating, managing, and interacting with account abstraction wallets using BIP-39 seed phrases and EVM-specific derivation paths.

🔍 About WDK

This module is part of the WDK (Wallet Development Kit) project, which empowers developers to build secure, non-custodial wallets with unified blockchain access, stateless architecture, and complete user control.

For detailed documentation about the complete WDK ecosystem, visit docs.wallet.tether.io.

🌟 Features

  • EVM Derivation Paths: Support for BIP-44 standard derivation paths for Ethereum (m/44'/60')
  • Multi-Account Management: Create and manage multiple account abstraction wallets from a single seed phrase
  • ERC-4337 Support: Full implementation of ERC-4337 account abstraction standard
  • UserOperation Management: Create and send UserOperations through bundlers
  • ERC20 Support: Query native token and ERC20 token balances using smart contract interactions

⬇️ Installation

To install the @tetherto/wdk-wallet-evm-erc-4337 package, follow these instructions:

You can install it using npm:

npm install @tetherto/wdk-wallet-evm-erc-4337

🚀 Quick Start

Importing from @tetherto/wdk-wallet-evm-erc-4337

Creating a New Wallet

import WalletManagerEvmErc4337, { 
  WalletAccountEvmErc4337, 
  WalletAccountReadOnlyEvmErc4337 
} from '@tetherto/wdk-wallet-evm-erc-4337'

// Use a BIP-39 seed phrase (replace with your own secure phrase)
const seedPhrase = 'test only example nut use this real life secret phrase must random'

// Create wallet manager with ERC-4337 config (paymaster token mode)
const wallet = new WalletManagerEvmErc4337(seedPhrase, {
  // Common parameters (required for all modes)
  chainId: 1, // Ethereum Mainnet
  provider: 'https://eth-mainnet.g.alchemy.com/v2/your-api-key', // RPC endpoint
  bundlerUrl: 'https://api.stackup.sh/v1/bundler/your-api-key', // ERC-4337 bundler
  entryPointAddress: '0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789', // EntryPoint contract
  safeModulesVersion: '0.3.0',

  // Paymaster token mode parameters
  paymasterUrl: 'https://api.paymaster.com',
  paymasterAddress: '0x8b1f6cb5d062aa2ce8d581942bbb960420d875ba',
  paymasterToken: { address: '0xdAC17F958D2ee523a2206206994597C13D831ec7' }, // USDT
  transferMaxFee: 100000000000000 // Optional: Maximum fee in wei
})

// Or use native coins mode (no paymaster needed)
const nativeWallet = new WalletManagerEvmErc4337(seedPhrase, {
  chainId: 1,
  provider: 'https://eth-mainnet.g.alchemy.com/v2/your-api-key',
  bundlerUrl: 'https://api.stackup.sh/v1/bundler/your-api-key',
  entryPointAddress: '0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789',
  safeModulesVersion: '0.3.0',
  useNativeCoins: true
})

// Get a full access account
const account = await wallet.getAccount(0)

// Convert to a read-only account
const readOnlyAccount = await account.toReadOnlyAccount()

Managing Multiple Accounts

import WalletManagerEvmErc4337 from '@tetherto/wdk-wallet-evm-erc-4337'

// Assume wallet is already created
// Get the first account (index 0)
const account = await wallet.getAccount(0)
const address = await account.getAddress()
console.log('Account 0 address:', address)

// Get the second account (index 1)
const account1 = await wallet.getAccount(1)
const address1 = await account1.getAddress()
console.log('Account 1 address:', address1)

// Get account by custom derivation path
// Full path will be m/44'/60'/0'/0/5
const customAccount = await wallet.getAccountByPath("0'/0/5")
const customAddress = await customAccount.getAddress()
console.log('Custom account address:', customAddress)

// Note: All addresses are checksummed Ethereum addresses (0x...)
// All accounts inherit the provider configuration from the wallet manager

Checking Balances

Owned Account

For accounts where you have the seed phrase and full access:

import WalletManagerEvmErc4337 from '@tetherto/wdk-wallet-evm-erc-4337'

// Assume wallet and account are already created
// Get native token balance (in wei)
const balance = await account.getBalance()
console.log('Native balance:', balance, 'wei') // 1 ETH = 1000000000000000000 wei

// Get ERC20 token balance
const tokenContract = '0x...'; // ERC20 contract address

const tokenBalance = await account.getTokenBalance(tokenContract);
console.log('Token balance:', tokenBalance);

// Note: Provider is required for balance checks
// Make sure wallet was created with a provider configuration

Read-Only Account

For addresses where you don't have the seed phrase:

import { WalletAccountReadOnlyEvmErc4337 } from '@tetherto/wdk-wallet-evm-erc-4337'

// Create a read-only account (native coins mode — no paymaster needed for quoting)
const readOnlyAccount = new WalletAccountReadOnlyEvmErc4337('0x636e9c21f27d9401ac180666bf8DC0D3FcEb0D24', { // Smart contract wallet address
  provider: 'https://eth-mainnet.g.alchemy.com/v2/your-api-key',
  bundlerUrl: 'https://api.stackup.sh/v1/bundler/your-api-key',
  entryPointAddress: '0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789',
  safeModulesVersion: '0.3.0',
  useNativeCoins: true
})

// Check native token balance
const balance = await readOnlyAccount.getBalance()
console.log('Native balance:', balance, 'wei')

// Check ERC20 token balance using contract
const tokenBalance = await readOnlyAccount.getTokenBalance('0xdAC17F958D2ee523a2206206994597C13D831ec7') // USDT contract address
console.log('Token balance:', tokenBalance)

// Note: ERC20 balance checks use the standard balanceOf(address) function
// Make sure the contract address is correct and implements the ERC20 standard

Sending Transactions

Send transactions using UserOperations through the bundler service. All transactions are handled via the EntryPoint contract.

// Send native tokens via UserOperation
const result = await account.sendTransaction({
  to: '0x636e9c21f27d9401ac180666bf8DC0D3FcEb0D24', // Recipient address
  value: 1000000000000000000n, // 1 ETH in wei
  data: '0x', // Optional: transaction data
})
console.log('UserOperation hash:', result.hash)
console.log('Transaction fee:', result.fee, 'wei')

// Get transaction fee estimate
const quote = await account.quoteSendTransaction({
  to: '0x636e9c21f27d9401ac180666bf8DC0D3FcEb0D24',
  value: 1000000000000000000n
});
console.log('Estimated fee:', quote.fee, 'wei');

// Note: Fees are calculated by the bundler and may include paymaster costs

Token Transfers

Transfer ERC20 tokens using UserOperations. Uses standard ERC20 transfer function.

// Transfer ERC20 tokens via UserOperation
const transferResult = await account.transfer({
  token: '0xdAC17F958D2ee523a2206206994597C13D831ec7',      // USDT contract address
  recipient: '0x636e9c21f27d9401ac180666bf8DC0D3FcEb0D24',  // Recipient's address
  amount: 1000000n     // Amount in token's base units (1 USDT = 1000000 for 6 decimals)
})
console.log('UserOperation hash:', transferResult.hash)
console.log('Transfer fee:', transferResult.fee, 'wei')

// Quote token transfer fee
const transferQuote = await account.quoteTransfer({
  token: '0xdAC17F958D2ee523a2206206994597C13D831ec7',      // USDT contract address
  recipient: '0x636e9c21f27d9401ac180666bf8DC0D3FcEb0D24',  // Recipient's address
  amount: 1000000n     // Amount in token's base units
})
console.log('Transfer fee estimate:', transferQuote.fee, 'wei')

// Transfer using native coins for gas (override constructor config)
const nativeTransfer = await account.transfer({
  token: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
  recipient: '0x636e9c21f27d9401ac180666bf8DC0D3FcEb0D24',
  amount: 1000000n
}, { useNativeCoins: true })
console.log('Native gas transfer hash:', nativeTransfer.hash)

Message Signing and Verification

Sign and verify messages using WalletAccountEvmErc4337.

// Sign a message
const message = 'Hello, Account Abstraction!'
const signature = await account.sign(message)
console.log('Signature:', signature)

// Verify a signature
const isValid = await account.verify(message, signature)
console.log('Signature valid:', isValid)

Fee Management

Retrieve current fee rates using WalletManagerEvmErc4337. Uses bundler service for fee estimation.

// Get current bundler fee rates
const feeRates = await wallet.getFeeRates();
console.log('Normal fee rate:', feeRates.normal, 'wei'); // Base bundler fee
console.log('Fast fee rate:', feeRates.fast, 'wei');     // Priority bundler fee

Memory Management

Clear sensitive data from memory using dispose methods in WalletAccountEvmErc4337 and WalletManagerEvmErc4337.

// Dispose wallet accounts to clear private keys from memory
account.dispose()

// Dispose entire wallet manager
wallet.dispose()

📚 API Reference

Table of Contents

ClassDescriptionMethods
WalletManagerEvmErc4337Main class for managing ERC-4337 wallets. Extends WalletManager from @tetherto/wdk-wallet.Constructor, Methods
WalletAccountEvmErc4337Individual ERC-4337 wallet account implementation. Extends WalletAccountReadOnlyEvmErc4337 and implements IWalletAccount from @tetherto/wdk-wallet.Constructor, Methods, Properties
WalletAccountReadOnlyEvmErc4337Read-only ERC-4337 wallet account. Extends WalletAccountReadOnly from @tetherto/wdk-wallet.Constructor, Methods

WalletManagerEvmErc4337

The main class for managing ERC-4337 compliant wallets.
Extends WalletManager from @tetherto/wdk-wallet.

Constructor

new WalletManagerEvmErc4337(seed, config)

Parameters:

  • seed (string | Uint8Array): BIP-39 mnemonic seed phrase or seed bytes

  • config (object): Configuration object. The configuration is a union of common fields and one of three gas payment modes.

    Common fields (required for all modes):

    • chainId (number): Chain ID of the target network
    • provider (string | Eip1193Provider): RPC endpoint URL or EIP-1193 provider instance
    • bundlerUrl (string): URL of the ERC-4337 bundler service
    • entryPointAddress (string): Address of the EntryPoint contract
    • safeModulesVersion (string): The safe modules version

    Paymaster token mode (pay gas fees with an ERC-20 token via a paymaster):

    • paymasterUrl (string): URL of the paymaster service
    • paymasterAddress (string): Address of the paymaster smart contract
    • paymasterToken (object): Paymaster token configuration
      • address (string): Token contract address
    • transferMaxFee (number | bigint, optional): Maximum fee amount for transfer operations

    Sponsorship mode (gas fees are sponsored by a paymaster):

    • isSponsored (true): Enables transaction sponsorship
    • paymasterUrl (string): URL of the paymaster service
    • sponsorshipPolicyId (string, optional): Sponsorship policy ID

    Native coins mode (pay gas fees with native coins, e.g. ETH):

    • useNativeCoins (true): Enables native coin gas payment
    • transferMaxFee (number | bigint, optional): Maximum fee amount for transfer operations

Example:

// Paymaster token mode
const wallet = new WalletManagerEvmErc4337(seedPhrase, {
  chainId: 1,
  provider: 'https://eth-mainnet.g.alchemy.com/v2/your-api-key',
  bundlerUrl: 'https://api.stackup.sh/v1/bundler/your-api-key',
  entryPointAddress: '0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789',
  safeModulesVersion: '0.3.0',
  paymasterUrl: 'https://api.paymaster.com',
  paymasterAddress: '0x8b1f6cb5d062aa2ce8d581942bbb960420d875ba',
  paymasterToken: { address: '0xdAC17F958D2ee523a2206206994597C13D831ec7' },
  transferMaxFee: 100000000000000n
})

Methods

MethodDescriptionReturns
getAccount(index)Returns a wallet account at the specified indexPromise<WalletAccountEvmErc4337>
getAccountByPath(path)Returns a wallet account at the specified BIP-44 derivation pathPromise<WalletAccountEvmErc4337>
getFeeRates()Returns current bundler fee rates for UserOperationsPromise<{normal: bigint, fast: bigint}>
dispose()Disposes all wallet accounts, clearing private keys from memoryvoid
getAccount(index)

Returns an ERC-4337 wallet account at the specified index using BIP-44 derivation.

Parameters:

  • index (number, optional): The index of the account to get (default: 0)

Returns: Promise<WalletAccountEvmErc4337> - The ERC-4337 wallet account

Example:

const account = await wallet.getAccount(0)
const address = await account.getAddress()
console.log('Smart account address:', address)
getAccountByPath(path)

Returns an ERC-4337 wallet account at the specified BIP-44 derivation path.

Parameters:

  • path (string): The derivation path (e.g., "0'/0/0", "1'/0/5")

Returns: Promise<WalletAccountEvmErc4337> - The ERC-4337 wallet account

Example:

const account = await wallet.getAccountByPath("0'/0/1")
const address = await account.getAddress()
console.log('Smart account address:', address)
getFeeRates()

Returns current bundler fee rates for ERC-4337 UserOperations.

Returns: Promise<{normal: bigint, fast: bigint}> - Object containing bundler fee rates in wei

  • normal: Standard fee rate for normal UserOperation processing
  • fast: Higher fee rate for priority UserOperation processing

Example:

const feeRates = await wallet.getFeeRates()
console.log('Normal bundler fee:', feeRates.normal, 'wei')
console.log('Fast bundler fee:', feeRates.fast, 'wei')

// Use in UserOperation
const result = await account.sendTransaction({
  to: '0x742C4265F5Ba4F8E0842e2b9EfE66302F7a13B6F',
  value: 1000000000000000000n, // 1 ETH
  maxFeePerGas: feeRates.fast,
  maxPriorityFeePerGas: feeRates.normal
})
dispose()

Disposes all ERC-4337 wallet accounts and clears sensitive data from memory.

Returns: void

Example:

wallet.dispose()
// All smart accounts and private keys are now securely wiped from memory

WalletAccountEvmErc4337

Represents an individual ERC-4337 wallet account. Implements IWalletAccount from @tetherto/wdk-wallet.

Constructor

new WalletAccountEvmErc4337(seed, path, config)

Parameters:

  • seed (string | Uint8Array): BIP-39 mnemonic seed phrase or seed bytes
  • path (string): BIP-44 derivation path (e.g., "0'/0/0")
  • config (object): Configuration object. Same configuration union as WalletManagerEvmErc4337 — see constructor parameters for the full description of common fields and the three gas payment modes (paymaster token, sponsorship, native coins).

Methods

MethodDescriptionReturns
getAddress()Returns the smart contract wallet addressPromise<string>
sign(message)Signs a message using the account's private keyPromise<string>
signTypedData(typedData)Signs typed data according to EIP-712Promise<string>
verify(message, signature)Verifies a message signaturePromise<boolean>
verifyTypedData(typedData, signature)Verifies a typed data signaturePromise<boolean>
sendTransaction(tx, config)Sends a transaction via UserOperationPromise<{hash: string, fee: bigint}>
quoteSendTransaction(tx, config)Estimates the fee for a UserOperationPromise<{fee: bigint}>
transfer(options, config)Transfers ERC20 tokens via UserOperationPromise<{hash: string, fee: bigint}>
quoteTransfer(options, config)Estimates the fee for an ERC20 transferPromise<{fee: bigint}>
getBalance()Returns the native token balance (in wei)Promise<bigint>
getTokenBalance(tokenAddress)Returns the balance of a specific ERC20 tokenPromise<bigint>
dispose()Disposes the wallet account, clearing private keys from memoryvoid
getAddress()

Returns the smart contract wallet address (not the EOA address).

Returns: Promise<string> - The smart contract wallet address

Example:

const smartAccountAddress = await account.getAddress()
console.log('Smart account address:', smartAccountAddress) // 0x123... (contract address)
sign(message)

Signs a message using the account's private key (EOA signing for the smart account).

Parameters:

  • message (string): Message to sign

Returns: Promise<string> - Signature as hex string

Example:

const signature = await account.sign('Hello ERC-4337!')
console.log('Signature:', signature)
signTypedData(typedData)

Signs typed data according to EIP-712.

Parameters:

  • typedData (object):
    • domain (object): The EIP-712 domain separator
    • types (object): The type definitions
    • message (object): The structured message to sign

Returns: Promise<string> - The typed data signature

Example:

const signature = await account.signTypedData({
  domain: {
    name: 'MyDApp',
    version: '1',
    chainId: 1,
    verifyingContract: '0x...'
  },
  types: {
    Transfer: [
      { name: 'to', type: 'address' },
      { name: 'amount', type: 'uint256' }
    ]
  },
  message: {
    to: '0x1234567890abcdef1234567890abcdef12345678',
    amount: '1000000'
  }
})
console.log('Typed data signature:', signature)
verify(message, signature)

Verifies a message signature using the account's public key.

Parameters:

  • message (string): Original message
  • signature (string): Signature as hex string

Returns: Promise<boolean> - True if signature is valid

Example:

const isValid = await account.verify('Hello ERC-4337!', signature)
console.log('Signature valid:', isValid)
verifyTypedData(typedData, signature)

Verifies a typed data signature against the account's address.

Parameters:

  • typedData (object):
    • domain (object): The EIP-712 domain separator
    • types (object): The type definitions
    • message (object): The structured message that was signed
  • signature (string): The signature to verify

Returns: Promise<boolean> - True if the signature is valid

Example:

const isValid = await account.verifyTypedData(
  {
    domain: {
      name: 'MyDApp',
      version: '1',
      chainId: 1,
      verifyingContract: '0x...'
    },
    types: {
      Transfer: [
        { name: 'to', type: 'address' },
        { name: 'amount', type: 'uint256' }
      ]
    },
    message: {
      to: '0x1234567890abcdef1234567890abcdef12345678',
      amount: '1000000'
    }
  },
  signature
)
console.log('Typed data signature valid:', isValid)
sendTransaction(tx)

Sends a transaction via UserOperation through the ERC-4337 bundler.

Parameters:

  • tx (object | object[]): The transaction object, or an array of multiple transactions to send in batch.
    • to (string): Recipient address
    • value (number | bigint): Amount in wei
    • data (string, optional): Transaction data in hex format
    • gasLimit (number | bigint, optional): Maximum gas units for the UserOperation
    • maxFeePerGas (number | bigint, optional): EIP-1559 max fee per gas in wei
    • maxPriorityFeePerGas (number | bigint, optional): EIP-1559 max priority fee per gas in wei
  • config (object, optional): If set, overrides the given configuration options. The provided fields are merged with the constructor configuration (i.e. only the specified properties are overridden). The merged configuration is validated to ensure all required fields for the resulting gas payment mode are present. Accepts any combination of fields from EvmErc4337WalletPaymasterTokenConfig, EvmErc4337WalletSponsorshipPolicyConfig, or EvmErc4337WalletNativeCoinsConfig.
    • isSponsored (boolean, optional): Override sponsorship setting
    • useNativeCoins (boolean, optional): Override to use native coins for gas
    • paymasterUrl (string, optional): Override paymaster service URL
    • paymasterAddress (string, optional): Override paymaster smart contract address
    • paymasterToken (object, optional): Override paymaster token
    • sponsorshipPolicyId (string, optional): Override sponsorship policy ID

Returns: Promise<{hash: string, fee: bigint}> - Object containing UserOperation hash and total fee (in wei)

Example:

const result = await account.sendTransaction({
  to: '0x742C4265F5Ba4F8E0842e2b9EfE66302F7a13B6F',
  value: 1000000000000000000n, // 1 ETH in wei
  maxFeePerGas: 20000000000n, // 20 gwei
  maxPriorityFeePerGas: 2000000000n // 2 gwei
})
console.log('UserOperation hash:', result.hash)
console.log('Total fee paid:', result.fee, 'wei')

// Send sponsored transaction (overrides constructor config)
const sponsoredResult = await account.sendTransaction({
  to: '0x742C4265F5Ba4F8E0842e2b9EfE66302F7a13B6F',
  value: 1000000000000000000n
}, {
  isSponsored: true,
  sponsorshipPolicyId: 'POLICY_ID'
})
console.log('Sponsored hash:', sponsoredResult.hash)

// Send transaction using native coins for gas
const nativeResult = await account.sendTransaction({
  to: '0x742C4265F5Ba4F8E0842e2b9EfE66302F7a13B6F',
  value: 1000000000000000000n
}, {
  useNativeCoins: true
})
console.log('Native gas hash:', nativeResult.hash)
quoteSendTransaction(tx, config)

Estimates the fee for a UserOperation without submitting it to the bundler.

Parameters:

  • tx (object | object[]): Same as sendTransaction tx parameter.
  • config (object, optional): Same as sendTransaction config parameter — overrides the given configuration options by merging with the constructor configuration.

Returns: Promise<{fee: bigint}> - Object containing estimated total fee (in wei)

Example:

const quote = await account.quoteSendTransaction({
  to: '0x742C4265F5Ba4F8E0842e2b9EfE66302F7a13B6F',
  value: 1000000000000000000n // 1 ETH in wei
})
console.log('Estimated UserOperation fee:', quote.fee, 'wei')
console.log('Estimated fee in ETH:', Number(quote.fee) / 1e18)

// Quote with native coins gas payment
const nativeQuote = await account.quoteSendTransaction({
  to: '0x742C4265F5Ba4F8E0842e2b9EfE66302F7a13B6F',
  value: 1000000000000000000n
}, { useNativeCoins: true })
console.log('Estimated native gas fee:', nativeQuote.fee, 'wei')
transfer(options, config)

Transfers ERC20 tokens via UserOperation through the bundler.

Parameters:

  • options (object): Transfer options
    • token (string): ERC20 token contract address
    • recipient (string): Recipient address
    • amount (number | bigint): Amount in token's smallest unit
  • config (object, optional): Same as sendTransaction config parameter — overrides the given configuration options by merging with the constructor configuration.

Returns: Promise<{hash: string, fee: bigint}> - Object containing UserOperation hash and fee (in wei)

Example:

const result = await account.transfer({
  token: '0xdAC17F958D2ee523a2206206994597C13D831ec7', // USDT
  recipient: '0x742C4265F5Ba4F8E0842e2b9EfE66302F7a13B6F',
  amount: 1000000n // 1 USDT (6 decimals)
})
console.log('UserOperation hash:', result.hash)
console.log('Gas fee paid:', result.fee, 'wei')

// Send sponsored token transfer
const sponsoredTransfer = await account.transfer({
  token: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
  recipient: '0x742C4265F5Ba4F8E0842e2b9EfE66302F7a13B6F',
  amount: 1000000n
}, {
  isSponsored: true,
  sponsorshipPolicyId: 'POLICY_ID'
})
console.log('Sponsored transfer hash:', sponsoredTransfer.hash)

// Transfer using native coins for gas
const nativeTransfer = await account.transfer({
  token: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
  recipient: '0x742C4265F5Ba4F8E0842e2b9EfE66302F7a13B6F',
  amount: 1000000n
}, {
  useNativeCoins: true
})
console.log('Native gas transfer hash:', nativeTransfer.hash)
quoteTransfer(options, config)

Estimates the fee for an ERC20 token transfer UserOperation without submitting it.

Parameters:

  • options (object): Same as transfer options parameter.
  • config (object, optional): Same as sendTransaction config parameter — overrides the given configuration options by merging with the constructor configuration.

Returns: Promise<{fee: bigint}> - Object containing estimated fee (in wei)

Example:

const quote = await account.quoteTransfer({
  token: '0xdAC17F958D2ee523a2206206994597C13D831ec7', // USDT
  recipient: '0x742C4265F5Ba4F8E0842e2b9EfE66302F7a13B6F',
  amount: 1000000n // 1 USDT (6 decimals)
})
console.log('Estimated transfer fee:', quote.fee, 'wei')
getBalance()

Returns the smart contract wallet's native token balance (e.g., ETH, MATIC, etc.) in wei.

Returns: Promise<bigint> - Balance in wei

Example:

const balance = await account.getBalance()
console.log('Smart account balance:', balance, 'wei')
console.log('Balance in ETH:', Number(balance) / 1e18)
getTokenBalance(tokenAddress)

Returns the smart contract wallet's balance of a specific ERC20 token.

Parameters:

  • tokenAddress (string): The ERC20 token contract address

Returns: Promise<bigint> - Token balance in token's smallest unit

Example:

// Get USDT balance (6 decimals)
const usdtBalance = await account.getTokenBalance('0xdAC17F958D2ee523a2206206994597C13D831ec7')
console.log('Smart account USDT balance:', Number(usdtBalance) / 1e6)
dispose()

Disposes the wallet account, securely erasing the private key from memory.

Returns: void

Example:

account.dispose()
// Private key is now securely wiped from memory

Properties

PropertyTypeDescription
indexnumberThe derivation path's index of this account
pathstringThe full derivation path of this account
keyPairobjectThe account's key pair (⚠️ Contains sensitive data)

⚠️ Security Note: The keyPair property contains sensitive cryptographic material. Never log, display, or expose the private key.

WalletAccountReadOnlyEvmErc4337

Represents a read-only ERC-4337 wallet account.

Constructor

new WalletAccountReadOnlyEvmErc4337(address, config)

Parameters:

  • address (string): The smart contract wallet address
  • config (object): Configuration object. Same configuration union as WalletManagerEvmErc4337 (excluding transferMaxFee) — see constructor parameters for the full description of common fields and the three gas payment modes.

Methods

MethodDescriptionReturns
predictSafeAddress(owner, config)Static method to predict Safe address for an EOAstring
getBalance()Returns the native token balance (in wei)Promise<bigint>
getTokenBalance(tokenAddress)Returns the balance of a specific ERC20 tokenPromise<bigint>
getPaymasterTokenBalance()Returns the paymaster token balancePromise<bigint>
quoteSendTransaction(tx, config)Estimates the fee for a UserOperationPromise<{fee: bigint}>
quoteTransfer(options, config)Estimates the fee for an ERC20 transferPromise<{fee: bigint}>
verify(message, signature)Verifies a message signaturePromise<boolean>
verifyTypedData(typedData, signature)Verifies a typed data signaturePromise<boolean>
predictSafeAddress(owner, config) (static)

Predicts the Safe smart contract address for a given EOA owner without requiring network calls.

Parameters:

  • owner (string): The EOA address that will own the Safe
  • config (object): Configuration object
    • chainId (number): Chain ID of the target network
    • safeModulesVersion (string, optional): Safe modules version (e.g., "0.3.0")

Returns: string - The predicted Safe address

Example:

const safeAddress = WalletAccountReadOnlyEvmErc4337.predictSafeAddress(
  '0x742C4265F5Ba4F8E0842e2b9EfE66302F7a13B6F',
  { chainId: 1, safeModulesVersion: '0.3.0' }
)
console.log('Predicted Safe address:', safeAddress)
getBalance()

Returns the smart contract wallet's native token balance (e.g., ETH, MATIC, etc.) in wei.

Returns: Promise<bigint> - Balance in wei

Example:

const balance = await readOnlyAccount.getBalance()
console.log('Smart account balance:', balance, 'wei')
console.log('Balance in ETH:', Number(balance) / 1e18)
getTokenBalance(tokenAddress)

Returns the smart contract wallet's balance of a specific ERC20 token.

Parameters:

  • tokenAddress (string): The ERC20 token contract address

Returns: Promise<bigint> - Token balance in token's smallest unit

Example:

// Get USDT balance (6 decimals) for the smart account
const usdtBalance = await readOnlyAccount.getTokenBalance('0xdAC17F958D2ee523a2206206994597C13D831ec7')
console.log('Smart account USDT balance:', Number(usdtBalance) / 1e6)
quoteSendTransaction(tx, config)

Estimates the fee for a UserOperation without submitting it to the bundler.

Parameters:

  • tx (object | object[]): The transaction object, or an array of multiple transactions to send in batch.
    • to (string): Recipient address
    • value (number | bigint): Amount in wei
    • data (string, optional): Transaction data in hex format
  • config (object, optional): If set, overrides the given configuration options by merging with the constructor configuration. See sendTransaction config for the full list of overridable fields.

Returns: Promise<{fee: bigint}> - Object containing estimated total UserOperation fee (in wei)

Example:

const quote = await readOnlyAccount.quoteSendTransaction({
  to: '0x742C4265F5Ba4F8E0842e2b9EfE66302F7a13B6F',
  value: 1000000000000000000n // 1 ETH in wei
})
console.log('Estimated UserOperation fee:', quote.fee, 'wei')
console.log('Estimated fee in ETH:', Number(quote.fee) / 1e18)
quoteTransfer(options, config)

Estimates the fee for an ERC20 token transfer UserOperation without submitting it to the bundler.

Parameters:

  • options (object): Transfer options
    • token (string): ERC20 token contract address
    • recipient (string): Recipient address
    • amount (number | bigint): Amount in token's smallest unit
  • config (object, optional): Same as quoteSendTransaction config parameter.

Returns: Promise<{fee: bigint}> - Object containing estimated UserOperation fee (in wei)

Example:

const quote = await readOnlyAccount.quoteTransfer({
  token: '0xdAC17F958D2ee523a2206206994597C13D831ec7', // USDT
  recipient: '0x742C4265F5Ba4F8E0842e2b9EfE66302F7a13B6F',
  amount: 1000000n // 1 USDT (6 decimals)
})
console.log('Estimated transfer UserOperation fee:', quote.fee, 'wei')
console.log('Estimated fee in ETH:', Number(quote.fee) / 1e18)
verify(message, signature)

Verifies a message signature using the account's public key.

Parameters:

  • message (string): Original message
  • signature (string): Signature as hex string

Returns: Promise<boolean> - True if signature is valid

Example:

const isValid = await readOnlyAccount.verify('Hello ERC-4337!', signature)
console.log('Signature valid:', isValid)
verifyTypedData(typedData, signature)

Verifies a typed data signature against the account's address.

Parameters:

  • typedData (object):
    • domain (object): The EIP-712 domain separator
    • types (object): The type definitions
    • message (object): The structured message that was signed
  • signature (string): The signature to verify

Returns: Promise<boolean> - True if the signature is valid

Example:

const isValid = await account.verifyTypedData(
  {
    domain: {
      name: 'MyDApp',
      version: '1',
      chainId: 1,
      verifyingContract: '0x...'
    },
    types: {
      Transfer: [
        { name: 'to', type: 'address' },
        { name: 'amount', type: 'uint256' }
      ]
    },
    message: {
      to: '0x1234567890abcdef1234567890abcdef12345678',
      amount: '1000000'
    }
  },
  signature
)
console.log('Typed data signature valid:', isValid)

🌐 Supported Networks

This package works with any EVM-compatible blockchain that supports ERC-4337, including:

  • Ethereum Mainnet
  • Ethereum Testnets (Sepolia, etc.)
  • Layer 2 Networks (Arbitrum, Optimism, etc.)
  • Other EVM Chains (Polygon, Avalanche C-Chain, etc.)

🔒 Security Considerations

  • Seed Phrase Security: Always store your seed phrase securely and never share it
  • Private Key Management: The package handles private keys internally with memory safety features
  • Smart Contract Security:
    • Verify EntryPoint contract addresses
    • Use audited account implementation contracts
    • Validate factory contract addresses
  • Bundler Security:
    • Use trusted bundler services
    • Validate UserOperation contents before signing
    • Monitor bundler responses for unexpected behavior
  • Memory Cleanup: Use the dispose() method to clear private keys from memory when done
  • Fee Limits: Set transferMaxFee to prevent excessive transaction fees
  • Gas Estimation: Always estimate gas before sending UserOperations
  • Contract Interactions:
    • Verify contract addresses and token decimals before transfers
    • Review UserOperation calldata before signing
  • Network Validation: Ensure correct EntryPoint and factory addresses for each network

🛠️ Development

Building

# Install dependencies
npm install

# Build TypeScript definitions
npm run build:types

# Lint code
npm run lint

# Fix linting issues
npm run lint:fix

Testing

# Run tests
npm test

# Run tests with coverage
npm run test:coverage

📜 License

This project is licensed under the Apache License 2.0 - see the LICENSE file for details.

🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

🆘 Support

For support, please open an issue on the GitHub repository.