WDK logoWDK documentation

Configuration

Configuration options and settings for @tetherto/wdk-wallet-evm-erc-4337

Wallet Configuration

The WalletManagerEvmErc4337 requires a complete ERC-4337 configuration object with all required parameters:

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

const config = {
  // Required parameters
  chainId: 1,
  provider: 'https://rpc.mevblocker.io/fast',
  safeModulesVersion: '0.3.0',
  bundlerUrl: `https://api.pimlico.io/v1/ethereum/rpc?apikey=${PIMLICO_API_KEY}`,
  paymasterUrl: `https://api.pimlico.io/v2/ethereum/rpc?apikey=${PIMLICO_API_KEY}`,
  paymasterAddress: '0x777777777777AeC03fd955926DbF81597e66834C',
  paymasterToken: {
    address: '0xdAC17F958D2ee523a2206206994597C13D831ec7'
  },
  transactionMaxFee: 100000,
  transferMaxFee: 100000
}

const wallet = new WalletManagerEvmErc4337(seedPhrase, config)

Account Configuration

Both WalletAccountEvmErc4337 and WalletAccountReadOnlyEvmErc4337 use the same configuration structure:

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

// Full access account
const account = new WalletAccountEvmErc4337(
  seedPhrase,
  "0'/0/0", // BIP-44 derivation path
  config    // Same config as wallet manager
)

// Read-only account (fee caps for sending are not needed)
const readOnlyAccount = new WalletAccountReadOnlyEvmErc4337(
  '0x...', // Owner EOA address; the module predicts the Safe address
  {
    chainId: 1,
    provider: 'https://rpc.mevblocker.io/fast',
    bundlerUrl: 'https://api.candide.dev/public/v3/1',
    paymasterUrl: 'https://api.candide.dev/public/v3/1',
    paymasterAddress: '0x8b1f6cb5d062aa2ce8d581942bbb960420d875ba',
    safeModulesVersion: '0.3.0',
    paymasterToken: {
      address: '0xdAC17F958D2ee523a2206206994597C13D831ec7'
    }
    // Note: transferMaxFee and transactionMaxFee omitted for read-only accounts
  }
)

Configuration Options

Chain ID

The chainId option specifies the expected blockchain network ID. Required for fee estimation and smart account initialization.

Before building a new UserOperation, the account reads eth_chainId from the provider and throws ConfigurationError if it differs from the constructor's chainId. The check happens before smart-account construction and UserOperation signing. This includes non-sponsored quotes that build an operation.

The first successful chain lookup is cached. Keep all failover providers on the configured chain and recreate the wallet or account when switching networks. Per-call fee-mode overrides do not change the chain being checked.

Sponsored quotes return 0n without checking the chain. Quoting or submitting an already-signed UserOperation also bypasses this check. Verify that a signed operation was built for the intended account, chain, EntryPoint, and paymaster before forwarding it.

Type: number Required: Yes

Examples:

// Ethereum Mainnet
const config = { chainId: 1 }

// Polygon Mainnet
const config = { chainId: 137 }

// Arbitrum One
const config = { chainId: 42161 }

// Avalanche C-Chain
const config = { chainId: 43114 }

Provider

The provider option specifies the RPC endpoint or EIP-1193 provider instance for blockchain interactions. Required for all operations. You can also pass an array of endpoints or providers to enable automatic failover: when a request to one provider fails, the wallet retries the next provider in the list.

Type: string | Eip1193Provider | Array<string | Eip1193Provider> Required: Yes

Examples:

// Using RPC URL
const config = {
  provider: 'https://rpc.mevblocker.io/fast'
}

// Using browser provider (MetaMask)
const config = {
  provider: window.ethereum
}

// Using custom ethers provider
import { JsonRpcProvider } from 'ethers'
const config = {
  provider: new JsonRpcProvider('https://rpc.mevblocker.io/fast')
}

// Using multiple providers for automatic failover
const config = {
  provider: [
    'https://rpc.mevblocker.io/fast',
    'https://eth.llamarpc.com'
  ],
  retries: 3 // Optional: additional retry attempts after the initial call fails
}

Retries

The retries option sets the number of additional retry attempts after the initial call fails. It only applies when provider is an array of endpoints or providers. Total attempts equal 1 + retries. If retries exceeds the number of providers, the failover loops back and retries already-failed providers in round-robin order.

Type: number Required: No (optional) Default: 3

const config = {
  provider: [
    'https://rpc.mevblocker.io/fast',
    'https://eth.llamarpc.com'
  ],
  retries: 5
}

Bundler URL

The bundlerUrl option specifies the URL of the ERC-4337 bundler service that handles UserOperation bundling and submission to the mempool. Required for transaction processing.

Type: string Required: Yes

Example:

const config = {
  bundlerUrl: 'https://api.candide.dev/public/v3/1'
}

Paymaster URL

The paymasterUrl option specifies the URL of the paymaster service that sponsors transaction fees using ERC-20 tokens or sponsorship policies.

Type: string Required: Yes, for Paymaster Token mode and Sponsorship Policy mode. Not used in Native Coins mode.

Example:

const config = {
  paymasterUrl: 'https://api.candide.dev/public/v3/1'
}

Paymaster Address

The paymasterAddress option specifies the address of the paymaster smart contract.

Type: string Required: Yes, for Paymaster Token mode only. Not used in Sponsorship Policy or Native Coins modes.

Example:

const config = {
  paymasterAddress: '0x8b1f6cb5d062aa2ce8d581942bbb960420d875ba'
}

Starting in v1.0.0-beta.15, the module validates this address whenever it builds a new token-paymaster UserOperation and the paymaster RPC returns a non-empty paymaster address. The comparison is case-insensitive. If the addresses differ, the module throws ConfigurationError before returning or submitting that newly built operation because its generated ERC-20 approval would target an unexpected paymaster contract.

Treat a mismatch as an invalid paymaster URL and address pairing. Confirm the expected contract with your paymaster provider before changing paymasterAddress; do not automatically trust the address returned by the RPC.

A matching quote can be cached for up to two minutes by transaction data. The cache key does not include per-call fee-mode or paymaster settings, and reusing the cached UserOperation does not call the paymaster RPC or rerun this address check. Pass the same fee-mode and paymaster configuration to a quote and its matching sign or send operation.

On-Chain Identifier

The onChainIdentifier option appends a 50-byte project marker to every UserOperation's call data. Pass a string to use it as the project name, or pass an object for more control over the platform and tool fields.

Type: string | OnChainIdentifier Required: No (optional)

Properties (object form):

  • project (string): The project name included in the marker
  • platform ('Web' | 'Mobile' | 'Safe App' | 'Widget', optional): The platform type (default: 'Web')
  • tool (string, optional): The tool name used to create the UserOperation
  • toolVersion (string, optional): Semver-style tool version string (e.g., '1.0.0')
// String form
const config = {
  onChainIdentifier: 'my-project'
}

// Object form
const config = {
  onChainIdentifier: {
    project: 'my-project',
    platform: 'Mobile',
    tool: 'my-wallet',
    toolVersion: '1.0.0'
  }
}

Parallel Nonce Lanes

ERC-4337 supports two-dimensional nonces: a 192-bit key selects a lane, and each lane has its own sequence. By default this module uses key 0, so do not fire another default-lane operation until the previous one has been included.

Use these optional common configuration fields when the account needs independent lanes:

  • parallel (boolean, default false): Give each send, sign, or transfer operation a fresh random lane at sequence 0. Ordering across those lanes is not guaranteed. Each successfully included UserOperation in a fresh lane creates or advances a distinct EntryPoint nonce slot; signing alone and unincluded submissions do not advance on-chain nonce state.
  • nonceKey (number | bigint | string): Reuse an explicit lane. A string is hashed into a deterministic named lane. A number or bigint is used as the raw uint192 key and must be in 0..2^192-1; use bigint for raw keys above Number.MAX_SAFE_INTEGER.

nonceKey takes precedence over parallel; when neither is set, the module uses the default key-0 lane.

Before enabling either option, configure the account without a lane, submit one default-lane operation, and wait for its receipt so the Safe is deployed. Confirm that the configured bundler accepts nonzero nonce keys, then create the account with the lane setting.

Configure A Named Lane After Safe Deployment
const config = {
  // ...required network and gas-payment fields
  nonceKey: 'scheduled-payments'
}

const wallet = new WalletManagerEvmErc4337(seedPhrase, config)

Reusing one named lane remains sequential: wait for inclusion before submitting its next operation, or batch dependent calls into one UserOperation.

The beta.18 runtime also honors parallel and nonceKey in the per-call config for signTransaction(), sendTransaction(), and transfer(), but the published per-call TypeScript declarations omit both fields. Construction-level configuration is typed; JavaScript per-call usage works at runtime.

Safe Modules Version

The safeModulesVersion option specifies the Safe modules version for smart contract wallet implementation. Beta.18 supports only 0.3.0; other values, including 0.2.0, throw ConfigurationError. Required for smart account initialization.

Type: string Required: Yes

Example:

const config = {
  safeModulesVersion: '0.3.0'
}

Paymaster Token

The paymasterToken option specifies the ERC-20 token used for paying transaction fees through the paymaster.

Type: object Required: Yes, for Paymaster Token mode only. Not used in Sponsorship Policy or Native Coins modes.

Properties:

  • address (string): The ERC-20 token contract address

Example:

const config = {
  paymasterToken: {
    address: '0xdAC17F958D2ee523a2206206994597C13D831ec7' // USDt
  }
}

Sponsorship Policy ID

The sponsorshipPolicyId option specifies the sponsorship policy identifier for sponsored transactions.

Type: string Required: No (optional), only used in Sponsorship Policy mode.

Example:

const config = {
  isSponsored: true,
  sponsorshipPolicyId: 'sp_my_policy_id'
}

Gas Payment Mode Flags

These boolean flags control which gas payment mode is used. Only one mode should be active at a time.

isSponsored

Enables Sponsorship Policy mode, where a sponsor covers transaction fees.

Type: boolean Default: false

const config = {
  isSponsored: true,
  paymasterUrl: 'https://api.candide.dev/public/v3/1'
}

useNativeCoins

Enables Native Coins mode, where the user pays fees in the chain's native currency (ETH, MATIC, etc.).

Type: boolean Default: false

const config = {
  useNativeCoins: true,
  transactionMaxFee: 100000000000000n, // Optional: max send/sign fee in wei
  transferMaxFee: 100000000000000n // Optional: max transfer fee in wei
}

Transaction Max Fee

The transactionMaxFee option sets the maximum fee amount for non-sponsored sendTransaction() and signTransaction() operations. It applies in Paymaster Token and Native Coins modes and is separate from transferMaxFee, which only caps token transfer operations. Optional parameter.

Type: number | bigint Required: No (optional) Unit: Paymaster token base units in Paymaster Token mode; native token base units in Native Coins mode

Example:

import { MaximumFeeExceededError } from '@tetherto/wdk-wallet'

const config = {
  transactionMaxFee: 100000, // 100,000 paymaster token units
  transferMaxFee: 100000
}

try {
  const result = await account.sendTransaction({
    to: '0x...',
    value: 1000000000000000n
  })
} catch (error) {
  if (error instanceof MaximumFeeExceededError) {
    console.error('Transaction canceled: Fee too high')
  } else {
    throw error
  }
}

Transfer Max Fee

The transferMaxFee option sets the maximum fee amount for transfer operations. In Paymaster Token mode, the cap uses paymaster token base units. In Native Coins mode, the cap uses native token base units. This prevents transactions with unexpectedly high fees. Optional parameter.

Type: number | bigint Required: No (optional) Unit: Paymaster token base units in Paymaster Token mode; native token base units in Native Coins mode

Example:

import { MaximumFeeExceededError } from '@tetherto/wdk-wallet'

const config = {
  transferMaxFee: 100000 // 100,000 paymaster token units (e.g., 0.1 USDt if 6 decimals)
}

// Usage with error handling
try {
  const result = await account.transfer({
    token: '0x...',
    recipient: '0x...',
    amount: 1000000
  })
} catch (error) {
  if (error instanceof MaximumFeeExceededError) {
    console.error('Transfer canceled: Fee too high')
  } else {
    throw error
  }
}

Network-Specific Configurations

Ethereum Mainnet

Supported Paymaster Tokens

The following tokens are supported for gas payments on Ethereum Mainnet:

  • USD₮: 0xdAC17F958D2ee523a2206206994597C13D831ec7
  • USA₮: 0x07041776f5007aca2a54844f50503a18a72a8b68
  • XAU₮: 0x68749665ff8d2d112fa859aa293f07a622782f38
const ethereumConfig = {
  chainId: 1,
  provider: 'https://rpc.mevblocker.io/fast',
  bundlerUrl: 'https://api.candide.dev/public/v3/1',
  paymasterUrl: 'https://api.candide.dev/public/v3/1',
  paymasterAddress: '0x8b1f6cb5d062aa2ce8d581942bbb960420d875ba',
  safeModulesVersion: '0.3.0',
  paymasterToken: {
    address: '0xdAC17F958D2ee523a2206206994597C13D831ec7' // USDt
  },
  transferMaxFee: 100000 // 100,000 paymaster token units (e.g., 0.1 USDt if 6 decimals)
}

Polygon Mainnet

const polygonConfig = {
  chainId: 137,
  provider: 'https://polygon-rpc.com',
  bundlerUrl: 'https://api.candide.dev/public/v3/137',
  paymasterUrl: 'https://api.candide.dev/public/v3/137',
  paymasterAddress: '0x8b1f6cb5d062aa2ce8d581942bbb960420d875ba',
  safeModulesVersion: '0.3.0',
  paymasterToken: {
    address: '0xc2132D05D31c914a87C6611C10748AEb04B58e8F' // USDt0 on Polygon
  },
  transferMaxFee: 100000
}

Arbitrum One

const arbitrumConfig = {
  chainId: 42161,
  provider: 'https://arb1.arbitrum.io/rpc',
  bundlerUrl: 'https://public.pimlico.io/v2/42161/rpc',
  paymasterUrl: 'https://public.pimlico.io/v2/42161/rpc',
  paymasterAddress: '0x777777777777AeC03fd955926DbF81597e66834C',
  safeModulesVersion: '0.3.0',
  paymasterToken: {
    address: '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9' // USDt0 on Arbitrum
  },
  transferMaxFee: 100000
}

Avalanche C-Chain

const avalancheConfig = {
  chainId: 43114,
  provider: 'https://avalanche-c-chain-rpc.publicnode.com',
  bundlerUrl: "https://public.pimlico.io/v2/43114/rpc",
  paymasterUrl: "https://public.pimlico.io/v2/43114/rpc",
  paymasterAddress: '0x8b1f6cb5d062aa2ce8d581942bbb960420d875ba',
  safeModulesVersion: '0.3.0',
  paymasterToken: {
    address: '0x9702230a8ea53601f5cd2dc00fdbc13d4df4a8c7' // USDt
  },
  transferMaxFee: 100000 // 100,000 paymaster token units (e.g., 0.1 USDt if 6 decimals)
}

Plasma

// Plasma (example Layer 2)
const plasmaConfig = {
  chainId: 9745,
  provider: 'https://plasma.drpc.org',
  // For ERC-4337 support, optional fields:
  bundlerUrl: 'https://api.candide.dev/public/v3/9745',
  paymasterUrl: 'https://api.candide.dev/public/v3/9745',
  paymasterAddress: '0x8b1f6cb5d062aa2ce8d581942bbb960420d875ba',
  safeModulesVersion: '0.3.0',
  paymasterToken: {
    address: '0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb' // USDt0
  },
  transferMaxFee: 100000 // 100,000 paymaster token units (e.g., 0.1 USDt0 if 6 decimals)
}

Sepolia Testnet (USD₮ ERC-20 mock/testnet only)

Choose a six-decimal USD₮ test token supported by each paymaster on chain 11155111. Get its address from that provider's current faucet or supported-token response, verify its contract and decimals on Sepolia, and set the corresponding environment variable below. The two providers may support different test-token contracts.


const pimlicoTestTokenAddress = process.env.PIMLICO_SEPOLIA_TEST_TOKEN_ADDRESS
const candideTestTokenAddress = process.env.CANDIDE_SEPOLIA_TEST_TOKEN_ADDRESS
for (const address of [pimlicoTestTokenAddress, candideTestTokenAddress]) {
  if (!/^0x[0-9a-fA-F]{40}$/.test(address ?? '')) {
    throw new Error('Set both verified Sepolia test-token addresses')
  }
}

// Pimlico
const sepoliaConfigPimlico = {
  chainId: 11155111,
  provider: 'https://sepolia.drpc.org',
  bundlerUrl: 'https://public.pimlico.io/v2/11155111/rpc',
  paymasterUrl: 'https://public.pimlico.io/v2/11155111/rpc',
  paymasterAddress: '0x777777777777AeC03fd955926DbF81597e66834C',
  safeModulesVersion: '0.3.0',
  paymasterToken: {
    address: pimlicoTestTokenAddress
  },
  transferMaxFee: 100000 // 0.1 USDt (6 decimals)
}

// Candide
const sepoliaConfigCandide = {
  chainId: 11155111,
  provider: 'https://sepolia.drpc.org',
  bundlerUrl: 'https://api.candide.dev/public/v3/11155111',
  paymasterUrl: 'https://api.candide.dev/public/v3/11155111',
  paymasterAddress: '0x8b1f6cb5d062aa2ce8d581942bbb960420d875ba',
  safeModulesVersion: '0.3.0',
  paymasterToken: {
    address: candideTestTokenAddress
  },
  transferMaxFee: 100000
}

Important Ethereum Sepolia is a testnet. The USD₮ tokens available at the links below are not real and do not entitle the holder to anything. In particular, they cannot be redeemed with Tether International, S.A. de C.V. ("Tether International") and are not Tether Tokens as described in Tether International's Terms of Service. The USD₮ tokens available at the links below on this testnet are intended for testing WDK on Ethereum Sepolia. The links below are links to third-party websites and are Third-Party Information as described in Tether Operations, S.A. de C.V.'s Website Terms

Get test USD₮:


Need Help?

On this page