WDK logoWDK documentation
Core SDKGuides

Error Handling

Learn about common errors and best practices.

Error Handling & Best Practices

This guide covers recommended patterns for error handling and security when using the WDK.

Handling Common Errors

When interacting with multiple chains and protocols, various runtime issues may occur.

Missing Registration

The most common error is attempting to access a wallet or protocol that hasn't been registered.

Check Registration Pattern
try {
  // This will throw if 'tron' was never registered via .registerWallet()
  const tronAccount = await wdk.getAccount('tron', 0)
} catch (error) {
  console.error('Tron wallet not available:', error.message)
}

Always use try/catch blocks when initializing sessions or accessing dynamic features.

Base Wallet Errors

@tetherto/wdk-wallet v1.0.0-beta.17 exports a shared WdkError hierarchy so applications can distinguish invalid input, provider failures, transaction outcomes, and unsupported capabilities without matching message strings.

Install the base wallet as a direct dependency before importing these contracts; do not rely on a transitive copy supplied by Core or a concrete wallet module:

Install Base Wallet Error Contracts
npm install @tetherto/wdk-wallet@1.0.0-beta.17

These classes define the beta.17 base contract; they do not change a concrete wallet package that still pins an older base version. For example, @tetherto/wdk-wallet-evm v1.0.0-beta.16 pins Base Wallet beta.13 and can still throw plain Error from provider-required paths. Use normalized branching only after the concrete module's release documentation confirms compatible errors, and keep a fallback for unknown errors.

ErrorMeaning
ValueErrorA method argument is invalid
NoSuchElementErrorA requested signer, transaction, proposal, or other identifier was not found
InvalidSignerErrorA signer is incompatible with the requested operation
InvalidTokenErrorAn address does not resolve to a supported token
ProviderRequiredErrorThe operation needs a configured provider
ProviderErrorThe configured provider failed; inspect reason
TransactionErrorA native transaction failed; inspect reason
TransferErrorA token transfer failed; inspect reason
MaximumFeeExceededErrorThe operation exceeds its configured fee ceiling
TimeoutErrorA polling loop, including waitForTransaction(), observed its deadline after a completed lookup or sleep
UnsupportedOperationErrorThe concrete module does not support an optional method
AssertionErrorRequired account state is missing
NotImplementedErrorAn interface/base-class method was not implemented by the concrete module

The package exports reason constants for branching:

ErrorReason constants
ProviderErrorNETWORK_ERROR, UNAUTHORIZED, FORBIDDEN, REQUEST_TIMEOUT, INTERNAL_SERVER_ERROR
TransactionErrorINSUFFICIENT_BALANCE
TransferErrorINSUFFICIENT_BALANCE, INSUFFICIENT_TOKEN_BALANCE
Handle A Provider Failure
import {
  ProviderError,
  ProviderErrorReason,
  WdkError
} from '@tetherto/wdk-wallet'

try {
  await account.getBalance()
} catch (error) {
  if (error instanceof ProviderError &&
      error.reason === ProviderErrorReason.REQUEST_TIMEOUT) {
    // Retry according to your application's provider policy.
  } else if (error instanceof WdkError) {
    // Handle another normalized wallet error.
  } else {
    throw error
  }
}

SignerError is no longer exported in v1.0.0-beta.17. Use InvalidSignerError for an incompatible signer and NoSuchElementError when a default or named signer is missing.

Protocol-specific classes and reason enums are exported from @tetherto/wdk-wallet/protocols:

Error classExported reason values
SwapErrorINSUFFICIENT_BALANCE, INSUFFICIENT_TOKEN_BALANCE, COULD_NOT_MET_THRESHOLD
BridgeErrorINSUFFICIENT_BALANCE, INSUFFICIENT_TOKEN_BALANCE
SupplyError, WithdrawError, BorrowError, RepayErrorINSUFFICIENT_BALANCE, INSUFFICIENT_TOKEN_BALANCE
BuyError, SellErrorINSUFFICIENT_FUNDS
SwidgeErrorINSUFFICIENT_BALANCE, INSUFFICIENT_TOKEN_BALANCE, COULD_NOT_MET_THRESHOLD, SLIPPAGE_TOO_HIGH
SdaErrorROUTE_NOT_SUPPORTED

The protocols subpath also exports AccountRequiredError and ReadOnlyAccountRequiredError. Multisig integrations import AccountNotOwnerError and ThresholdNotMetError from @tetherto/wdk-wallet/multisig. Concrete provider and wallet modules can expose more specific errors in addition to these base contracts.

Memory Management

For security, clear wallet state from memory when a session is complete. The WDK provides dispose() for this purpose.

Seed Lifecycle

WDK does not own the seed you pass to new WDK(seed). The seed comes from your app, so your app is responsible for storing it, decrypting it, and clearing it when it is no longer needed.

Use this lifecycle for sessions that need explicit cleanup:

  1. Decrypt or load the seed into a mutable buffer.
  2. Initialize and use WDK.
  3. Call dispose() on the WDK instance.
  4. Zero the seed buffer when no WDK instance or wallet needs it anymore.
Seed lifecycle cleanup
import WDK from '@tetherto/wdk'
import WalletManagerEvm from '@tetherto/wdk-wallet-evm'

type SeedDecrypter = (encryptedSeed: Uint8Array) => Promise<Uint8Array>

async function runWalletSession(
  encryptedSeed: Uint8Array,
  decryptSeedBytes: SeedDecrypter
) {
  let seedBytes: Uint8Array | undefined
  let wdk: WDK | undefined

  try {
    seedBytes = await decryptSeedBytes(encryptedSeed)

    wdk = new WDK(seedBytes)
      .registerWallet('ethereum', WalletManagerEvm, {
        provider: 'https://eth.drpc.org'
      })

    const account = await wdk.getAccount('ethereum', 0)
    const address = await account.getAddress()
    return address
  } finally {
    wdk?.dispose()
    seedBytes?.fill(0)
  }
}

In this example, decryptSeedBytes() represents your app's secure storage or decryption layer. It should return seed bytes as a Uint8Array.

dispose() clears keys and account state managed by WDK, including private keys held by registered wallets. It does not mutate or zero the seed value you passed to WDK. If your app requires explicit seed cleanup, prefer a mutable Uint8Array; JavaScript strings cannot be reliably zeroed.

Disposing the Instance

You can dispose every registered wallet using dispose():

Dispose WDK
function endSession(wdk) {
  // 1. Dispose registered wallets and account private keys
  wdk.dispose()
  
  // 2. Modify app state to reflect logged-out status
  // ...
  
  console.log('Session ended, wallet data cleared.')
}

Disposing Specific Wallets

You can dispose only the wallets you no longer need using dispose():

Dispose Specific Wallets
// Keep the TON wallet registered, but dispose the Ethereum wallet
wdk.dispose(['ethereum'])

After Disposal: Once a wallet is disposed, any later call that depends on that wallet registration will fail until you register it again. If you call wdk.dispose() without arguments, you must instantiate a new WDK instance or register fresh wallets before resuming operations.

Security Best Practices

Environment Variables

Never hardcode API keys or seed phrases in your source code. Use environment variables (e.g., process.env.TON_API_KEY).

Secure Storage

If you persist a session, never store the raw seed phrase in local storage. Use secure operating system storage (like Keychain on macOS or Keystore on Android).

On this page