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

@t402/wdk

v2.3.1

Published

T402 Payment Protocol integration with Tether Wallet Development Kit (WDK)

Readme

@t402/wdk

T402 integration with Tether Wallet Development Kit (WDK)

This package provides seamless integration between the T402 payment protocol and Tether's WDK, enabling self-custodial USDT0 payments with EIP-3009 gasless transfers.

Features

  • Multi-chain Wallets - Manage wallets across Ethereum, Arbitrum, Base, and more
  • T402-Compatible Signers - Ready-to-use signers for T402 HTTP payments
  • Balance Aggregation - View balances across all configured chains
  • Cross-chain Bridging - Bridge USDT0 between chains via LayerZero OFT
  • Balance Caching - Reduce RPC calls with configurable TTL caching
  • Comprehensive Error Handling - Typed errors with retry logic

Installation

npm install @t402/wdk
# or
pnpm add @t402/wdk

You'll also need Tether WDK packages:

npm install @tetherto/wdk @tetherto/wdk-wallet-evm
# Optional: for bridging support
npm install @tetherto/wdk-protocol-bridge-usdt0-evm

Quick Start

1. Register WDK Modules

Before using T402WDK, register the Tether WDK modules once at app startup:

import WDK from '@tetherto/wdk'
import WalletManagerEvm from '@tetherto/wdk-wallet-evm'
import BridgeUsdt0Evm from '@tetherto/wdk-protocol-bridge-usdt0-evm' // Optional
import { T402WDK } from '@t402/wdk'

// Register WDK modules (once at app startup)
T402WDK.registerWDK(WDK, WalletManagerEvm, BridgeUsdt0Evm)

2. Create a Wallet

// Generate a new seed phrase
const seedPhrase = T402WDK.generateSeedPhrase()

// Or use an existing seed phrase
const seedPhrase = 'your twelve word seed phrase here ...'

// Create wallet with chain configurations
const wallet = new T402WDK(seedPhrase, {
  arbitrum: 'https://arb1.arbitrum.io/rpc',
  ethereum: 'https://eth.llamarpc.com',
  base: 'https://mainnet.base.org',
})

3. Get a Signer for T402 Payments

import { createT402HTTPClient } from '@t402/core'

// Get signer for Arbitrum
const signer = await wallet.getSigner('arbitrum')

// Use with T402 HTTP client
const client = createT402HTTPClient({
  signers: [{ scheme: 'exact', signer }],
})

// Make paid requests
const response = await client.fetch('https://api.example.com/premium')

API Reference

T402WDK Class

Static Methods

// Register WDK modules (required before creating instances)
T402WDK.registerWDK(WDK, WalletManagerEvm, BridgeUsdt0Evm?);

// Check registration status
T402WDK.isWDKRegistered(): boolean;
T402WDK.isWalletManagerRegistered(): boolean;
T402WDK.isBridgeRegistered(): boolean;

// Generate a new BIP-39 seed phrase
T402WDK.generateSeedPhrase(): string;

Constructor

const wallet = new T402WDK(
  seedPhrase: string,           // BIP-39 mnemonic (12, 15, 18, 21, or 24 words)
  config: T402WDKConfig,        // Chain RPC configurations
  options?: T402WDKOptions      // Optional: cache settings
);

Config Example:

const wallet = new T402WDK(
  seedPhrase,
  {
    // String shorthand (uses default chain settings)
    arbitrum: 'https://arb1.arbitrum.io/rpc',

    // Full config object
    ethereum: {
      provider: 'https://eth.llamarpc.com',
      chainId: 1,
      network: 'eip155:1',
    },
  },
  {
    // Optional cache configuration
    cache: {
      enabled: true,
      tokenBalanceTTL: 30000, // 30 seconds
      nativeBalanceTTL: 15000, // 15 seconds
      aggregatedBalanceTTL: 60000, // 60 seconds
    },
  },
)

Instance Properties

wallet.isInitialized: boolean;           // True if WDK is ready
wallet.initializationError: Error | null; // Error if initialization failed
wallet.isCacheEnabled: boolean;          // True if balance caching is enabled

Chain Management

// Get all configured chain names
wallet.getConfiguredChains(): string[];
// Returns: ['arbitrum', 'ethereum', 'base']

// Get chain configuration
wallet.getChainConfig('arbitrum'): NormalizedChainConfig | undefined;

// Check if chain is configured
wallet.isChainConfigured('arbitrum'): boolean;

// Get chains that support USDT0
wallet.getUsdt0Chains(): string[];

// Get chains that support bridging
wallet.getBridgeableChains(): string[];

Signers

// Get a T402-compatible signer for a chain
const signer = await wallet.getSigner('arbitrum', accountIndex?: number);

// Get wallet address
const address = await wallet.getAddress('arbitrum');

// Clear signer cache (forces re-initialization)
wallet.clearSignerCache();

Balance Operations

// Get USDT0 balance on a chain
const usdt0Balance = await wallet.getUsdt0Balance('arbitrum')

// Get USDC balance on a chain
const usdcBalance = await wallet.getUsdcBalance('base')

// Get all balances for a chain
const chainBalance = await wallet.getChainBalances('arbitrum')
// Returns: { chain, network, native, tokens: [...] }

// Get aggregated balances across all chains
const allBalances = await wallet.getAggregatedBalances()
// Returns: { totalUsdt0, totalUsdc, chains: [...] }

// Find best chain for a payment amount
const best = await wallet.findBestChainForPayment(1000000n, 'USDT0')
// Returns: { chain: 'arbitrum', token: 'USDT0', balance: 5000000n } | null

Bridging

// Check if bridging is possible
wallet.canBridge('arbitrum', 'ethereum'): boolean;

// Get available destinations from a chain
wallet.getBridgeDestinations('arbitrum'): string[];

// Bridge USDT0 between chains
const result = await wallet.bridgeUsdt0({
  fromChain: 'ethereum',
  toChain: 'arbitrum',
  amount: 100_000000n,  // 100 USDT0 (6 decimals)
  recipient?: '0x...',  // Optional, defaults to same wallet
});
// Returns: { txHash: '0x...', estimatedTime: 300 }

Cache Management

// Get cache configuration
wallet.getCacheConfig(): BalanceCacheConfig;

// Get cache statistics
wallet.getCacheStats(): BalanceCacheStats;

// Invalidate all cached balances
wallet.invalidateBalanceCache();

// Invalidate cache for a specific chain
wallet.invalidateChainCache('arbitrum');

// Invalidate cache for a specific address
wallet.invalidateAddressCache('0x1234...');

// Dispose resources (call when done)
wallet.dispose();

WDKSigner Class

The signer returned by wallet.getSigner() implements the T402 ClientEvmSigner interface:

interface WDKSigner {
  readonly address: Address

  // Sign EIP-712 typed data (used by T402 for EIP-3009)
  signTypedData(message: {
    domain: Record<string, unknown>
    types: Record<string, unknown>
    primaryType: string
    message: Record<string, unknown>
  }): Promise<`0x${string}`>

  // Sign a personal message
  signMessage(message: string | Uint8Array): Promise<`0x${string}`>

  // Get native token balance
  getBalance(): Promise<bigint>

  // Get ERC20 token balance
  getTokenBalance(tokenAddress: Address): Promise<bigint>

  // Estimate gas for a transaction
  estimateGas(params: { to: Address; value?: bigint; data?: string }): Promise<bigint>

  // Send a transaction
  sendTransaction(params: {
    to: Address
    value?: bigint
    data?: string
  }): Promise<{ hash: `0x${string}` }>

  // Utility methods
  getChain(): string
  getChainId(): number
  getAccountIndex(): number
  isInitialized: boolean
}

Error Handling

All errors are typed and include context information:

import {
  WDKError,
  WDKInitializationError,
  ChainError,
  SignerError,
  SigningError,
  BalanceError,
  TransactionError,
  BridgeError,
  RPCError,
  WDKErrorCode,
  isWDKError,
  hasErrorCode,
  wrapError,
  withRetry,
  withTimeout,
} from '@t402/wdk'

try {
  const signer = await wallet.getSigner('polygon')
} catch (error) {
  if (isWDKError(error)) {
    console.error(`Error code: ${error.code}`)
    console.error(`Message: ${error.message}`)
    console.error(`Context:`, error.context)

    if (hasErrorCode(error, WDKErrorCode.CHAIN_NOT_CONFIGURED)) {
      console.error('Chain is not configured')
    }

    if (error.isRetryable()) {
      // Can retry this operation
    }
  }
}

Error Codes

| Code Range | Category | | ---------- | -------------------------- | | 1xxx | Initialization errors | | 2xxx | Chain configuration errors | | 3xxx | Signer errors | | 4xxx | Signing errors | | 5xxx | Balance errors | | 6xxx | Transaction errors | | 7xxx | Bridge errors | | 8xxx | RPC errors |

Retry Utilities

import { withRetry, withTimeout } from '@t402/wdk'

// Retry an operation with exponential backoff
const balance = await withRetry(() => signer.getBalance(), { maxRetries: 3, baseDelay: 500 })

// Add timeout to a promise
const result = await withTimeout(
  someAsyncOperation(),
  30000, // 30 second timeout
  'Operation description',
)

Supported Chains

| Chain | Chain ID | USDT0 | Bridging | | --------- | -------- | ----- | -------- | | Ethereum | 1 | ✅ | ✅ | | Arbitrum | 42161 | ✅ | ✅ | | Base | 8453 | ✅ | ✅ | | Ink | 57073 | ✅ | ✅ | | Berachain | 80094 | ✅ | ✅ | | Unichain | 130 | ✅ | ✅ |

Examples

Complete Payment Flow

import WDK from '@tetherto/wdk'
import WalletManagerEvm from '@tetherto/wdk-wallet-evm'
import { T402WDK } from '@t402/wdk'
import { createT402HTTPClient } from '@t402/core'

// 1. Setup (once at app startup)
T402WDK.registerWDK(WDK, WalletManagerEvm)

// 2. Create wallet
const wallet = new T402WDK(seedPhrase, {
  arbitrum: 'https://arb1.arbitrum.io/rpc',
})

// 3. Check balance before payment
const balance = await wallet.getUsdt0Balance('arbitrum')
console.log(`USDT0 Balance: ${balance / 1000000n} USDT0`)

// 4. Get signer and create client
const signer = await wallet.getSigner('arbitrum')
const client = createT402HTTPClient({
  signers: [{ scheme: 'exact', signer }],
})

// 5. Make paid request
const response = await client.fetch('https://api.example.com/premium')

// 6. Invalidate cache after payment
wallet.invalidateChainCache('arbitrum')

Multi-Chain Balance Check

const wallet = new T402WDK(seedPhrase, {
  arbitrum: 'https://arb1.arbitrum.io/rpc',
  ethereum: 'https://eth.llamarpc.com',
  base: 'https://mainnet.base.org',
})

// Get all balances
const balances = await wallet.getAggregatedBalances()

console.log(`Total USDT0: ${balances.totalUsdt0 / 1000000n}`)
console.log(`Total USDC: ${balances.totalUsdc / 1000000n}`)

for (const chain of balances.chains) {
  console.log(`\n${chain.chain}:`)
  console.log(`  Native: ${chain.native}`)
  for (const token of chain.tokens) {
    console.log(`  ${token.symbol}: ${token.formatted}`)
  }
}

Auto-Select Best Chain

const amount = 50_000000n // 50 USDT0

// Find the best chain with sufficient balance
const best = await wallet.findBestChainForPayment(amount)

if (best) {
  console.log(`Use ${best.token} on ${best.chain}`)
  const signer = await wallet.getSigner(best.chain)
  // Use signer for payment...
} else {
  console.log('Insufficient balance on all chains')
}

Cross-Chain Bridge

import BridgeUsdt0Evm from '@tetherto/wdk-protocol-bridge-usdt0-evm'

// Register with bridge support
T402WDK.registerWDK(WDK, WalletManagerEvm, BridgeUsdt0Evm)

const wallet = new T402WDK(seedPhrase, {
  ethereum: 'https://eth.llamarpc.com',
  arbitrum: 'https://arb1.arbitrum.io/rpc',
})

// Check if bridging is possible
if (wallet.canBridge('ethereum', 'arbitrum')) {
  // Bridge 100 USDT0 from Ethereum to Arbitrum
  const result = await wallet.bridgeUsdt0({
    fromChain: 'ethereum',
    toChain: 'arbitrum',
    amount: 100_000000n,
  })

  console.log(`Bridge tx: ${result.txHash}`)
  console.log(`Estimated time: ${result.estimatedTime}s`)
}

Testing

For testing without actual WDK, use the MockWDKSigner:

import { MockWDKSigner } from '@t402/wdk';

const mockSigner = new MockWDKSigner(
  '0x1234567890123456789012345678901234567890',
  '0xprivatekey...'
);

// Use in tests
const signature = await mockSigner.signTypedData({...});

TypeScript Support

Full TypeScript support with exported types:

import type {
  T402WDKConfig,
  T402WDKOptions,
  T402BalanceCacheConfig,
  NormalizedChainConfig,
  TokenBalance,
  ChainBalance,
  AggregatedBalance,
  BridgeParams,
  BridgeResult,
  T402WDKSigner,
  WDKAccount,
  WDKInstance,
  WDKConstructor,
  CacheConfig,
  CacheStats,
  BalanceCacheConfig,
  BalanceCacheStats,
  RetryConfig,
} from '@t402/wdk'

License

Apache 2.0