npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@tetherto/wdk-wallet-evm-7702-gasless

v1.0.0-beta.1

Published

A simple package to manage BIP-32 wallets for evm blockchains, which implement the eip-7702 standard for gasless account abstraction

Readme

@tetherto/wdk-wallet-evm-7702-gasless

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

A simple and secure package to manage gasless EIP-7702 wallets for EVM-compatible blockchains. This package abstracts all EIP-7702 delegation and ERC-4337 UserOperation complexity behind a simple API — call transfer() and delegation, UserOp signing, paymaster sponsorship, and token approvals all happen internally.

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

  • EIP-7702 Delegation: EOA becomes a smart account via delegation — no Safe contract, no address prediction
  • Gasless Transactions: Full paymaster integration for sponsored or ERC-20 token gas payment
  • Auto Approval: Paymaster token allowance is managed automatically, including USDT mainnet reset handling
  • Provider-Agnostic: Works with any ERC-4337 bundler/paymaster (Pimlico, Candide, etc.)
  • Failover Providers: Pass an array of provider URLs or EIP-1193 instances to enable automatic round-robin failover
  • Bare Runtime: Supports both Node.js and Bare runtime
  • EVM Derivation Paths: Support for BIP-44 standard derivation paths for Ethereum (m/44'/60')
  • Multi-Account Management: Create and manage multiple wallets from a single seed phrase
  • ERC20 Support: Query native token and ERC20 token balances, transfers, and approvals
  • Automatic Delegation Lifecycle: Delegation is checked and signed automatically per operation

Installation

npm install @tetherto/wdk-wallet-evm-7702-gasless

Quick Start

Creating a Wallet (Sponsored Mode)

import WalletManagerEvm7702Gasless from '@tetherto/wdk-wallet-evm-7702-gasless'

const wallet = new WalletManagerEvm7702Gasless(seedPhrase, {
  provider: 'https://rpc.mevblocker.io/fast',
  delegationAddress: '0xe6Cae83BdE06E4c305530e199D7217f42808555B',
  bundlerUrl: 'https://api.pimlico.io/v2/1/rpc?apikey=YOUR_KEY',
  isSponsored: true,
  sponsorshipPolicyId: 'sp_my_policy'
})

const account = await wallet.getAccount(0)
const address = await account.getAddress() // Returns the EOA address directly

Creating a Wallet (Paymaster Token Mode)

const wallet = new WalletManagerEvm7702Gasless(seedPhrase, {
  provider: 'https://rpc.mevblocker.io/fast',
  delegationAddress: '0xe6Cae83BdE06E4c305530e199D7217f42808555B',
  bundlerUrl: 'https://api.pimlico.io/v2/1/rpc?apikey=YOUR_KEY',
  paymasterAddress: '0x888888888888Ec68A58AB8094Cc1AD20Ba3D2402',
  paymasterToken: { address: '0xdAC17F958D2ee523a2206206994597C13D831ec7' }, // USDT
  transferMaxFee: 100000000000000n
})

Using Candide (Separate Paymaster Endpoint)

Candide uses separate bundler and paymaster URLs. Use the paymasterUrl field:

const wallet = new WalletManagerEvm7702Gasless(seedPhrase, {
  provider: 'https://rpc.mevblocker.io/fast',
  delegationAddress: '0xe6Cae83BdE06E4c305530e199D7217f42808555B',
  bundlerUrl: 'https://api.candide.dev/bundler/v3/1/YOUR_KEY',
  paymasterUrl: 'https://api.candide.dev/paymaster/v3/1/YOUR_KEY',
  isSponsored: true,
  sponsorshipPolicyId: 'your_policy_id'
})

Wrapping an Existing WalletAccountEvm

import { WalletAccountEvm } from '@tetherto/wdk-wallet-evm'
import { WalletAccountEvm7702Gasless } from '@tetherto/wdk-wallet-evm-7702-gasless'

const evmAccount = new WalletAccountEvm(seed, "0'/0/0", { provider: '...' })

const gaslessAccount = new WalletAccountEvm7702Gasless(evmAccount, {
  provider: '...',
  delegationAddress: '0xe6Cae83BdE06E4c305530e199D7217f42808555B',
  bundlerUrl: '...',
  isSponsored: true
})

Managing Multiple Accounts

const account0 = await wallet.getAccount(0)
const account1 = await wallet.getAccount(1)

// Or by custom derivation path (full path: m/44'/60'/0'/0/5)
const customAccount = await wallet.getAccountByPath("0'/0/5")

Checking Balances

// Native token balance (in wei)
const balance = await account.getBalance()

// ERC20 token balance
const tokenBalance = await account.getTokenBalance('0xdAC17F958D2ee523a2206206994597C13D831ec7')

// Multiple token balances
const balances = await account.getTokenBalances([token1, token2])

Token Transfers

// Transfer ERC20 tokens — delegation + UserOp + paymaster handled internally
const result = await account.transfer({
  token: '0xdAC17F958D2ee523a2206206994597C13D831ec7', // USDT
  recipient: '0x742C4265F5Ba4F8E0842e2b9EfE66302F7a13B6F',
  amount: 1000000n // 1 USDT (6 decimals)
})
console.log('UserOperation hash:', result.hash)
console.log('Fee:', result.fee)

// Quote transfer fee before sending
const quote = await account.quoteTransfer({
  token: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
  recipient: '0x742C4265F5Ba4F8E0842e2b9EfE66302F7a13B6F',
  amount: 1000000n
})
console.log('Estimated fee:', quote.fee)

Sending Transactions

// Send a raw transaction via UserOperation
const result = await account.sendTransaction({
  to: '0x742C4265F5Ba4F8E0842e2b9EfE66302F7a13B6F',
  value: 1n, // 1 wei
  data: '0x'
})

// Batch transactions
const batchResult = await account.sendTransaction([
  { to: '0x...', value: 0n, data: '0x...' },
  { to: '0x...', value: 0n, data: '0x...' }
])

// Quote fee
const quote = await account.quoteSendTransaction({
  to: '0x742C4265F5Ba4F8E0842e2b9EfE66302F7a13B6F',
  value: 0n,
  data: '0x'
})

Token Approvals

await account.approve({
  token: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
  spender: '0x742C4265F5Ba4F8E0842e2b9EfE66302F7a13B6F',
  amount: 1000000n
})

Transaction Receipts

// Get full transaction receipt from a UserOp hash
const receipt = await account.getTransactionReceipt(userOpHash)

// Get the raw UserOperation receipt
const userOpReceipt = await account.getUserOperationReceipt(userOpHash)

Message Signing and Verification

// Sign a message
const signature = await account.sign('Hello, EIP-7702!')

// Verify a signature
const isValid = await account.verify('Hello, EIP-7702!', signature)

// Sign typed data (EIP-712)
const typedSig = await account.signTypedData({
  domain: { name: 'MyDApp', version: '1', chainId: 1, verifyingContract: '0x...' },
  types: { Transfer: [{ name: 'to', type: 'address' }, { name: 'amount', type: 'uint256' }] },
  message: { to: '0x...', amount: '1000000' }
})

Fee Management

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

Memory Management

account.dispose() // Clear private keys from memory
wallet.dispose()  // Dispose all accounts

Configuration Reference

Common Fields (always required)

| Field | Type | Description | |-------|------|-------------| | provider | string \| Eip1193Provider \| (string \| Eip1193Provider)[] | RPC endpoint URL, EIP-1193 provider instance, or failover list mixing both formats | | bundlerUrl | string | URL of the ERC-4337 bundler service | | delegationAddress | string | Address of the smart account implementation to delegate to |

Optional Common Fields

| Field | Type | Description | |-------|------|-------------| | paymasterUrl | string | URL of the paymaster service, if different from bundlerUrl (e.g. Candide) | | retries | number | Additional retry attempts for provider failover arrays. Total attempts are 1 + retries. Defaults to 3 |

Sponsored Mode

| Field | Type | Description | |-------|------|-------------| | isSponsored | true | Enables sponsorship | | sponsorshipPolicyId | string (optional) | Sponsorship policy ID for the paymaster provider |

Paymaster Token Mode

| Field | Type | Description | |-------|------|-------------| | paymasterAddress | string (optional) | Pin on the paymaster smart contract address. When omitted, it is derived from the paymaster RPC (pm_supportedERC20Tokens for Candide, pimlico_getTokenQuotes for Pimlico) | | paymasterToken | { address: string } | ERC-20 token used for gas payment | | transferMaxFee | number \| bigint (optional) | Maximum fee for transfer operations |

API Reference

WalletManagerEvm7702Gasless

The main class for managing EIP-7702 gasless wallets. Extends WalletManager from @tetherto/wdk-wallet.

| Method | Description | Returns | |--------|-------------|---------| | getAccount(index) | Returns a wallet account at the specified index | Promise<WalletAccountEvm7702Gasless> | | getAccountByPath(path) | Returns a wallet account at the specified BIP-44 derivation path | Promise<WalletAccountEvm7702Gasless> | | getFeeRates() | Returns current fee rates | Promise<{normal: bigint, fast: bigint}> | | dispose() | Disposes all wallet accounts, clearing private keys from memory | void |

WalletAccountEvm7702Gasless

Individual gasless wallet account. Extends WalletAccountReadOnlyEvm7702Gasless, implements IWalletAccount.

Constructor overloads:

  • new WalletAccountEvm7702Gasless(seed, path, config) — standard BIP-44 derivation
  • new WalletAccountEvm7702Gasless(walletAccountEvm, config) — wrap an existing WalletAccountEvm

| Method | Description | Returns | |--------|-------------|---------| | getAddress() | Returns the EOA address | Promise<string> | | sign(message) | Signs a message | Promise<string> | | signTypedData(typedData) | Signs typed data (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, fee}> | | quoteSendTransaction(tx, config?) | Estimates the fee for a UserOperation | Promise<{fee}> | | transfer(options, config?) | Transfers ERC20 tokens via UserOperation | Promise<{hash, fee}> | | quoteTransfer(options, config?) | Estimates the fee for an ERC20 transfer | Promise<{fee}> | | approve(options) | Approves a spender for a token amount | Promise<{hash, fee}> | | getBalance() | Returns the native token balance (in wei) | Promise<bigint> | | getTokenBalance(tokenAddress) | Returns the balance of a specific ERC20 token | Promise<bigint> | | getTokenBalances(tokenAddresses) | Returns balances for multiple ERC20 tokens | Promise<Record<string, bigint>> | | getAllowance(token, spender) | Returns the current allowance | Promise<bigint> | | getTransactionReceipt(hash) | Returns a transaction receipt from a UserOp hash | Promise<EvmTransactionReceipt \| null> | | getUserOperationReceipt(hash) | Returns a UserOperation receipt | Promise<UserOperationReceipt \| null> | | toReadOnlyAccount() | Returns a read-only copy of the account | Promise<WalletAccountReadOnlyEvm7702Gasless> | | dispose() | Disposes the wallet account | void |

| Property | Type | Description | |----------|------|-------------| | index | number | The derivation path's index | | path | string | The full derivation path | | keyPair | KeyPair | The account's key pair |

WalletAccountReadOnlyEvm7702Gasless

Read-only EIP-7702 gasless wallet account. Can query balances and estimate fees but cannot sign or send transactions.

| Method | Description | Returns | |--------|-------------|---------| | getAddress() | Returns the EOA address | Promise<string> | | getBalance() | Returns the native token balance (in wei) | Promise<bigint> | | getTokenBalance(tokenAddress) | Returns the balance of a specific ERC20 token | Promise<bigint> | | getTokenBalances(tokenAddresses) | Returns balances for multiple ERC20 tokens | Promise<Record<string, bigint>> | | getPaymasterTokenBalance() | Returns the paymaster token balance | Promise<bigint> | | quoteSendTransaction(tx, config?) | Estimates the fee for a UserOperation | Promise<{fee}> | | quoteTransfer(options, config?) | Estimates the fee for an ERC20 transfer | Promise<{fee}> | | getAllowance(token, spender) | Returns the current allowance | Promise<bigint> | | getTransactionReceipt(hash) | Returns a transaction receipt from a UserOp hash | Promise<EvmTransactionReceipt \| null> | | getUserOperationReceipt(hash) | Returns a UserOperation receipt | Promise<UserOperationReceipt \| null> | | verify(message, signature) | Verifies a message signature | Promise<boolean> | | verifyTypedData(typedData, signature) | Verifies a typed data signature | Promise<boolean> |

Key Differences from ERC-4337 Module

| Aspect | ERC-4337 (wdk-wallet-evm-erc-4337) | ERC-7702 Gasless (this module) | |--------|---------------------------------------|-------------------------------| | Smart Account | Safe contract (predicted address) | EOA delegated via EIP-7702 | | getAddress() | Returns Safe contract address | Returns EOA address directly | | Underlying Library | @tetherto/wdk-safe-relay-kit | abstractionkit | | Address Prediction | predictSafeAddress() required | No prediction needed | | Gas Payment | Native coins, paymaster token, sponsored | Sponsored or paymaster token | | Token Approval | Manual amountToApprove | Automatic (including USDT reset) | | Chain Requirement | Any EVM with ERC-4337 | Requires Pectra-activated chains (EIP-7702) |

Supported Networks

This package works with EVM-compatible blockchains that support both EIP-7702 (Pectra upgrade) and ERC-4337:

  • Ethereum Mainnet (post-Pectra)
  • Ethereum Sepolia (testnet)
  • Other Pectra-activated EVM chains

Bundler/Paymaster Compatibility

This module uses standard ERC-4337 bundler RPCs (eth_sendUserOperation, eth_estimateUserOperationGas) and ERC-7677 paymaster RPCs (pm_getPaymasterStubData, pm_getPaymasterData). Not all providers support generic EIP-7702 SimpleAccount delegation — many lock you to their own smart account implementation.

Tested Providers

| Provider | Sponsored | Paymaster Token | Status | |----------|-----------|-----------------|--------| | Pimlico | Yes | Yes | Fully working — all flows tested on mainnet and Sepolia | | Candide | Yes | Yes | Working with known limitations (see below) |

Not Compatible

| Provider | Reason | |----------|--------| | Alchemy | Locked to Modular Account v2 — rejects all other delegation addresses | | ZeroDev | Locked to Kernel smart account — requires createKernelAccount, not standard toSimpleSmartAccount | | Gelato | Proprietary Smart Wallet SDK — no standard bundler RPCs exposed |

Known Candide Limitations

Candide's Voltaire bundler has known gas estimation issues affecting EIP-7702 accounts. These are bundler-side bugs, not module issues — the same operations work correctly on Pimlico.

1. callGasLimit underestimation for USDT on Ethereum mainnet

The bundler returns ~27k callGasLimit for USDT transfers, but USDT's non-standard implementation needs ~40k+. The UserOp inner call reverts with empty reason data (out of gas). Standard ERC-20 tokens (USDC, DAI) are not affected.

2. verificationGasLimit underestimation for ERC-20 paymaster

The bundler's gas estimate doesn't meet its own validation margin. Error: "verificationGas should have extra 2000 gas, has only -15791". Observed on Sepolia with the ERC-20 token paymaster flow.

Related Candide GitHub issues:

  • abstractionkit #57: Gas estimation ran before approval was prepended, producing wrong estimates
  • abstractionkit #78 (open): Simple7702Account ignoring paymaster fields in overrides
  • voltaire #33: Gas estimation failed for IAccountExecute accounts on EntryPoint v0.8

Recommendation: Use Pimlico for production deployments until Candide resolves these estimation issues. If using Candide, the paymaster-token flow on Ethereum mainnet works reliably; the sponsored flow works for ETH transfers and standard ERC-20 tokens but may fail for USDT.

Security Considerations

  • Seed Phrase Security: Always store your seed phrase securely and never share it
  • Private Key Management: The package handles private keys internally via memory-safe buffers (Uint8Array) that are zeroed on dispose()
  • Memory Cleanup: Use the dispose() method to clear private keys from memory when done
  • Fee Limits: Set transferMaxFee to prevent excessive transaction fees
  • Delegation Awareness: The EOA delegates execution to a smart account implementation — verify the delegationAddress is trusted and audited
  • Bundler Security: Use trusted bundler services and validate UserOperation responses
  • Contract Interactions: Verify contract addresses and token decimals before transfers

Development

# Install dependencies
npm install

# Lint code
npm run lint

# Fix linting issues
npm run lint:fix

# Build TypeScript definitions
npm run build:types

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.