$ npm install @tetherto/wdk-wallet-evm-erc-4337Note: 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.
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.
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
@tetherto/wdk-wallet-evm-erc-4337import 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()
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
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
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
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
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)
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)
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
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()
| Class | Description | Methods |
|---|---|---|
| WalletManagerEvmErc4337 | Main class for managing ERC-4337 wallets. Extends WalletManager from @tetherto/wdk-wallet. | Constructor, Methods |
| WalletAccountEvmErc4337 | Individual ERC-4337 wallet account implementation. Extends WalletAccountReadOnlyEvmErc4337 and implements IWalletAccount from @tetherto/wdk-wallet. | Constructor, Methods, Properties |
| WalletAccountReadOnlyEvmErc4337 | Read-only ERC-4337 wallet account. Extends WalletAccountReadOnly from @tetherto/wdk-wallet. | Constructor, Methods |
The main class for managing ERC-4337 compliant wallets.
Extends WalletManager from @tetherto/wdk-wallet.
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 networkprovider (string | Eip1193Provider): RPC endpoint URL or EIP-1193 provider instancebundlerUrl (string): URL of the ERC-4337 bundler serviceentryPointAddress (string): Address of the EntryPoint contractsafeModulesVersion (string): The safe modules versionPaymaster token mode (pay gas fees with an ERC-20 token via a paymaster):
paymasterUrl (string): URL of the paymaster servicepaymasterAddress (string): Address of the paymaster smart contractpaymasterToken (object): Paymaster token configuration
address (string): Token contract addresstransferMaxFee (number | bigint, optional): Maximum fee amount for transfer operationsSponsorship mode (gas fees are sponsored by a paymaster):
isSponsored (true): Enables transaction sponsorshippaymasterUrl (string): URL of the paymaster servicesponsorshipPolicyId (string, optional): Sponsorship policy IDNative coins mode (pay gas fees with native coins, e.g. ETH):
useNativeCoins (true): Enables native coin gas paymenttransferMaxFee (number | bigint, optional): Maximum fee amount for transfer operationsExample:
// 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
})
| Method | Description | Returns |
|---|---|---|
getAccount(index) | Returns a wallet account at the specified index | Promise<WalletAccountEvmErc4337> |
getAccountByPath(path) | Returns a wallet account at the specified BIP-44 derivation path | Promise<WalletAccountEvmErc4337> |
getFeeRates() | Returns current bundler fee rates for UserOperations | Promise<{normal: bigint, fast: bigint}> |
dispose() | Disposes all wallet accounts, clearing private keys from memory | void |
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 processingfast: Higher fee rate for priority UserOperation processingExample:
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
Represents an individual ERC-4337 wallet account. Implements IWalletAccount from @tetherto/wdk-wallet.
new WalletAccountEvmErc4337(seed, path, config)
Parameters:
seed (string | Uint8Array): BIP-39 mnemonic seed phrase or seed bytespath (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).| Method | Description | Returns |
|---|---|---|
getAddress() | Returns the smart contract wallet address | Promise<string> |
sign(message) | Signs a message using the account's private key | Promise<string> |
signTypedData(typedData) | Signs typed data according to EIP-712 | Promise<string> |
verify(message, signature) | Verifies a message signature | Promise<boolean> |
verifyTypedData(typedData, signature) | Verifies a typed data signature | Promise<boolean> |
sendTransaction(tx, config) | Sends a transaction via UserOperation | Promise<{hash: string, fee: bigint}> |
quoteSendTransaction(tx, config) | Estimates the fee for a UserOperation | Promise<{fee: bigint}> |
transfer(options, config) | Transfers ERC20 tokens via UserOperation | Promise<{hash: string, fee: bigint}> |
quoteTransfer(options, config) | Estimates the fee for an ERC20 transfer | Promise<{fee: bigint}> |
getBalance() | Returns the native token balance (in wei) | Promise<bigint> |
getTokenBalance(tokenAddress) | Returns the balance of a specific ERC20 token | Promise<bigint> |
dispose() | Disposes the wallet account, clearing private keys from memory | void |
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 signReturns: 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 separatortypes (object): The type definitionsmessage (object): The structured message to signReturns: 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 messagesignature (string): Signature as hex stringReturns: 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 separatortypes (object): The type definitionsmessage (object): The structured message that was signedsignature (string): The signature to verifyReturns: 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 addressvalue (number | bigint): Amount in weidata (string, optional): Transaction data in hex formatgasLimit (number | bigint, optional): Maximum gas units for the UserOperationmaxFeePerGas (number | bigint, optional): EIP-1559 max fee per gas in weimaxPriorityFeePerGas (number | bigint, optional): EIP-1559 max priority fee per gas in weiconfig (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 settinguseNativeCoins (boolean, optional): Override to use native coins for gaspaymasterUrl (string, optional): Override paymaster service URLpaymasterAddress (string, optional): Override paymaster smart contract addresspaymasterToken (object, optional): Override paymaster tokensponsorshipPolicyId (string, optional): Override sponsorship policy IDReturns: 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 addressrecipient (string): Recipient addressamount (number | bigint): Amount in token's smallest unitconfig (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 addressReturns: 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
| Property | Type | Description |
|---|---|---|
index | number | The derivation path's index of this account |
path | string | The full derivation path of this account |
keyPair | object | The account's key pair (⚠️ Contains sensitive data) |
⚠️ Security Note: The keyPair property contains sensitive cryptographic material. Never log, display, or expose the private key.
Represents a read-only ERC-4337 wallet account.
new WalletAccountReadOnlyEvmErc4337(address, config)
Parameters:
address (string): The smart contract wallet addressconfig (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.| Method | Description | Returns |
|---|---|---|
predictSafeAddress(owner, config) | Static method to predict Safe address for an EOA | string |
getBalance() | Returns the native token balance (in wei) | Promise<bigint> |
getTokenBalance(tokenAddress) | Returns the balance of a specific ERC20 token | Promise<bigint> |
getPaymasterTokenBalance() | Returns the paymaster token balance | Promise<bigint> |
quoteSendTransaction(tx, config) | Estimates the fee for a UserOperation | Promise<{fee: bigint}> |
quoteTransfer(options, config) | Estimates the fee for an ERC20 transfer | Promise<{fee: bigint}> |
verify(message, signature) | Verifies a message signature | Promise<boolean> |
verifyTypedData(typedData, signature) | Verifies a typed data signature | Promise<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 Safeconfig (object): Configuration object
chainId (number): Chain ID of the target networksafeModulesVersion (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 addressReturns: 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 addressvalue (number | bigint): Amount in weidata (string, optional): Transaction data in hex formatconfig (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 addressrecipient (string): Recipient addressamount (number | bigint): Amount in token's smallest unitconfig (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 messagesignature (string): Signature as hex stringReturns: 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 separatortypes (object): The type definitionsmessage (object): The structured message that was signedsignature (string): The signature to verifyReturns: 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)
This package works with any EVM-compatible blockchain that supports ERC-4337, including:
dispose() method to clear private keys from memory when donetransferMaxFee to prevent excessive transaction fees# Install dependencies
npm install
# Build TypeScript definitions
npm run build:types
# Lint code
npm run lint
# Fix linting issues
npm run lint:fix
# Run tests
npm test
# Run tests with coverage
npm run test:coverage
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.
Contributions are welcome! Please feel free to submit a Pull Request.
For support, please open an issue on the GitHub repository.