WDK logoWDK documentation
EVMStandard EVMGuides

Error Handling

Handle errors, manage fees, and dispose of sensitive data in EVM wallets.

This guide covers best practices for handling transaction errors, managing fee limits, and cleaning up sensitive data from memory.

Handle Transaction Errors

Wrap transactions in try/catch blocks to handle common failure scenarios such as insufficient funds or exceeded fee limits.

Transaction Error Handling
import {
  MaximumFeeExceededError,
  ProviderRequiredError,
  ValueError
} from '@tetherto/wdk-wallet'

try {
  const result = await account.sendTransaction({
    to: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6',
    value: 1000000000000000000n
  })
  console.log('Transaction submitted:', result.hash)
} catch (error) {
  if (error instanceof ProviderRequiredError) {
    console.log('Connect the account to a provider before sending')
  } else if (error instanceof MaximumFeeExceededError) {
    console.log('Transaction fee too high')
  } else if (error instanceof ValueError) {
    console.log('Review the transaction type and fee fields')
  } else {
    throw error
  }
}

Handle Token Transfer Errors

Token transfers can fail for additional reasons such as invalid addresses or insufficient token balances.

Token Transfer Error Handling
import {
  MaximumFeeExceededError,
  ProviderRequiredError
} from '@tetherto/wdk-wallet'

try {
  const result = await account.transfer({
    token: '0xdAC17F958D2ee523a2206206994597C13D831ec7', // USDt
    recipient: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6',
    amount: 1000000000000000000n
  })
  console.log('Transfer submitted:', result.hash)
} catch (error) {
  if (error instanceof ProviderRequiredError) {
    console.log('Connect the account to a provider before transferring')
  } else if (error instanceof MaximumFeeExceededError) {
    console.log('Transfer fee too high')
  } else {
    throw error
  }
}

Resolution means the provider accepted the broadcast; it does not prove inclusion or successful EVM execution. Pass the returned hash to waitForTransaction(), then inspect finality and success before releasing goods or updating durable application state.

Manage Fee Limits

Set transactionMaxFee to cap execution gas for sendTransaction() and provider-backed signTransaction(); equality is allowed. Object sends check the populated transaction before signing. Signed raw inputs are checked from their encoded gas and fee fields. signTransaction() checks a quote without populating missing fields; offline signing does not run this check. The cap excludes transferred value. Token approvals use this cap; ERC-20 transfers use transferMaxFee instead.

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

Handle Transaction Status Errors

getTransaction() throws ValueError for a malformed 32-byte hash and NoSuchElementError when neither a transaction nor receipt can be found. waitForTransaction() throws TimeoutError if it does not reach the requested finality within the configured time. A returned dropped receipt is not an exception; it means the sender's mined nonce advanced beyond the pending transaction's nonce.

Do not treat finality as execution success. A mined EVM transaction can be confirmed or final with success: false when it reverted.

Type 3/blob requests fail with ValueError. Authorization fails with AssertionError when it falls back to a provider-reported chain ID of zero. Only an explicit authorization request with chainId: 0 opts into authorization across chains. Delegation and revocation also reject a mismatch between a configured nonzero chain ID and the provider before signing.

Handle Non-derivable Signers

An active PrivateKeySignerEvm throws UnsupportedOperationError from derive(). A disposed signer throws DisposalError before attempting derivation or signing. The manager throws InvalidSignerError when its default signer cannot derive or numeric/path account retrieval selects a non-derivable signer.

Handle a Non-derivable Signer
import { UnsupportedOperationError } from '@tetherto/wdk-wallet'

try {
  await privateKeySigner.derive("0'/0/1")
} catch (error) {
  if (error instanceof UnsupportedOperationError) {
    console.error('Register this private-key signer by name instead of deriving it')
  } else {
    throw error
  }
}

Dispose of Sensitive Data

Call dispose() to initiate wallet cleanup. A disposed manager rejects account retrieval. A disposed account rejects signing and send operations with DisposalError; reads and quotes do not share that guard. A supplied signer can be disposed separately while account.disposed remains false. Manager cleanup skips cached accounts without exposed private-key bytes and can stop when a key getter or disposal throws; dispose derived external accounts explicitly. Default and named signers supplied by your app remain app-owned; dispose them after their final consumer finishes. Accounts preserve a supplied signer unless shouldWipeSignerOnDisposal: true is set. See signer ownership.

Memory Cleanup
account.dispose()

wallet.dispose()

Always call dispose() in a finally block or cleanup handler to initiate cleanup on failure paths. Cleanup does not establish complete erasure of secret copies.

On this page