JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 5
  • Score
    100M100P100Q44903F
  • License Apache-2.0

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

Package Exports

  • @t402/wdk
  • @t402/wdk/adapters
  • @t402/wdk/adapters/svm
  • @t402/wdk/adapters/ton
  • @t402/wdk/adapters/tron

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'
  • @t402/core - Core protocol types and client
  • @t402/evm - EVM mechanism implementation
  • @t402/wdk-gasless - ERC-4337 gasless payments
  • @t402/wdk-bridge - Cross-chain LayerZero bridging
  • @t402/wdk-multisig - Safe multi-sig wallets

License

Apache 2.0