WDK logoWDK documentation

Bridge USD₮0 EVM API Reference

Complete API documentation for @tetherto/wdk-protocol-bridge-usdt0-evm

Table of Contents

ClassDescriptionMethods
Usdt0ProtocolEvmMain class for bridging USD₮0 tokens across blockchains. Extends BridgeProtocol from @tetherto/wdk-wallet/protocols.Constructor, Methods

Usdt0ProtocolEvm

The main class for bridging USD₮0 tokens across different blockchains using the LayerZero protocol. Extends BridgeProtocol from @tetherto/wdk-wallet/protocols.

Constructor

new Usdt0ProtocolEvm(account, config?)

Parameters:

  • account (IWalletAccount or IWalletAccountReadOnly from @tetherto/wdk-wallet): The wallet account to use for bridge operations. See account requirements.
  • config (BridgeProtocolConfig, optional): Configuration object
    • bridgeMaxFee (number | bigint, optional): Rejects bridge() when the implementation's combined fee value is at or above this cap

Example:

import Usdt0ProtocolEvm from '@tetherto/wdk-protocol-bridge-usdt0-evm'
import { WalletAccountEvm } from '@tetherto/wdk-wallet-evm'

const account = new WalletAccountEvm(seedPhrase, "0'/0/0", {
  provider: 'https://eth.drpc.org'
})

const bridgeProtocol = new Usdt0ProtocolEvm(account, {
  bridgeMaxFee: 1000000000000000n // Standard Ethereum account: source native base units
})

Account requirements

The constructor accepts the shared IWalletAccount and IWalletAccountReadOnly interfaces in 1.0.0-beta.9. For execution, bridge() checks whether the account has a callable sendTransaction() method instead of requiring a WalletAccountEvm instance.

The account must still implement EVM-compatible getAddress() and quoteSendTransaction() operations. Both bridge methods require an RPC URL or EIP-1193 provider in the wallet configuration. The constructor reads account._config.provider, an internal field absent from the shared interfaces. Implementing the interface alone does not establish runtime compatibility; an account without _config fails construction.

Read-only accounts can use quoteBridge(), subject to the same provider, route, balance, and allowance requirements. They cannot execute bridge() without sendTransaction().

ERC-4337 helper selection and approval batching still depend on the concrete classes resolved by the bridge package: WalletAccountReadOnlyEvmErc4337 for helper construction and quotes, and WalletAccountEvmErc4337 for execution. A custom account or an account from a separate copy of the ERC-4337 package does not receive that path solely because it implements sendTransaction(). The bridge release depends on ERC-4337 wallet 1.0.0-beta.11; verify package deduplication and runtime class identity when combining wallet versions. See Bridge with ERC-4337.

Methods

MethodDescriptionReturnsThrows
bridge(options, config?)Bridges tokens to another blockchainPromise<BridgeResult>If no provider or the combined fee is at or above the cap
quoteBridge(options, config?)Estimates the cost of a bridge operationPromise<Omit<BridgeResult, 'hash'>>If no provider
getSupportedChains()Returns chain descriptors from bundled configurationPromise<SwidgeSupportedChain[]>
getSupportedTokens(options?)Returns configured USD₮0 or XAU₮0 descriptors, with optional filtersPromise<SwidgeSupportedToken[]>

bridge(options, config?)

Bridges tokens to a different blockchain using the USD₮0 protocol.

With a standard EVM account, approve the source-chain bridge spender before calling bridge(). If you pass oftContractAddress, use the same address as the approval spender. Supported ERC-4337 accounts do not need a separate approval call; the protocol bundles an approval and helper call into one UserOperation. On Ethereum mainnet, when the resolved token is USD₮ at 0xdAC17F958D2ee523a2206206994597C13D831ec7, the protocol queries that ERC-4337 account's allowance for the token and the transaction-value helper spender. If the current allowance and the computed approveAmount are both greater than zero, it prepends approve(spender, 0), then approve(spender, approveAmount), then the helper bridge call in the same batch. Standard EVM accounts and every other chain/token path are unchanged.

Parameters:

  • options (BridgeOptions): Bridge operation options
    • targetChain (string): Destination chain name
    • recipient (string): Address that will receive the bridged tokens (EVM hex address, Solana base58 address, TON address, or TRON address)
    • token (string): Token contract address on source chain
    • amount (number | bigint): Amount to bridge in token base units
    • oftContractAddress (string, optional): Custom OFT contract address to use instead of auto-resolving from the source chain
    • dstEid (number, optional): Custom LayerZero destination endpoint ID override
  • config (Erc4337BridgeConfig, optional): ERC-4337 gas-payment overrides plus optional bridgeMaxFee
    • bridgeMaxFee (number | bigint, optional): Override maximum bridge fee

Returns: Promise<BridgeResult> - Bridge operation result

Throws:

  • Error if the account has no callable sendTransaction() method
  • Error if no provider is configured
  • Error if the combined fee value is equal to or greater than bridgeMaxFee

Example:

// Standard EVM account
const standardBridgeProtocol = new Usdt0ProtocolEvm(standardAccount)

await standardAccount.approve({
  token: '0x...', // USDt contract address
  spender: '0x...', // OFT or bridge spender address
  amount: 1000000n
})

const result = await standardBridgeProtocol.bridge({
  targetChain: 'arbitrum',
  recipient: '0x...', // Recipient address
  token: '0x...', // USDt contract address
  amount: 1000000n,
  oftContractAddress: '0x...' // Same address used as approval spender
})

console.log('Bridge hash:', result.hash)
console.log('Account transaction fee:', result.fee)
console.log('Bridge fee:', result.bridgeFee)

// ERC-4337 account: approval is bundled automatically
const erc4337BridgeProtocol = new Usdt0ProtocolEvm(erc4337Account)

const result2 = await erc4337BridgeProtocol.bridge({
  targetChain: 'arbitrum',
  recipient: '0x...', // Recipient address
  token: '0x...', // USDt contract address
  amount: 1000000n,
  oftContractAddress: '0x...' // Optional custom OFT contract
}, {
  paymasterToken: { address: '0x...' } // Paymaster token configuration
})

console.log('Bridge hash:', result2.hash) // Single hash for bundled operations
console.log('Account fee:', result2.fee)
console.log('Bridge fee:', result2.bridgeFee)

quoteBridge(options, config?)

Estimates the cost of a bridge operation without executing it. Read-only accounts are accepted, and this method does not enforce bridgeMaxFee.

For standard EVM accounts, some providers estimate the same bridge transaction that bridge() sends. If that estimate fails because token allowance is missing, approve the source-chain bridge spender before calling quoteBridge(). ERC-4337 quotes include the bundled approval transaction. For WalletAccountReadOnlyEvmErc4337, quoteBridge() uses the same ordered batch as bridge(): on Ethereum mainnet for the resolved USD₮ token at 0xdAC17F958D2ee523a2206206994597C13D831ec7, an existing non-zero allowance and non-zero approveAmount produce approve(spender, 0), approve(spender, approveAmount), then the helper bridge call; otherwise the batch is the approval followed by the helper call.

Parameters:

  • options (BridgeOptions): Bridge operation options (same as bridge method)
  • config (Erc4337QuoteConfig, optional): ERC-4337 gas-payment overrides

Returns: Promise<Omit<BridgeResult, 'hash'>> - Bridge cost estimate

Throws: Error if no provider is configured

Standard-account example:

const quote = await bridgeProtocol.quoteBridge({
  targetChain: 'polygon',
  recipient: '0x...', // Recipient address
  token: '0x...', // USDt contract address
  amount: 1000000n
})

console.log('Estimated transaction fee:', quote.fee)
console.log('Bridge fee:', quote.bridgeFee)

// Check if fees are acceptable
if (quote.fee + quote.bridgeFee >= 1000000000000000n) {
  console.log('Bridge fees too high')
} else {
  // Proceed with bridge
  await account.approve({
    token: '0x...', // USDt contract address
    spender: '0x...', // OFT or bridge spender address
    amount: 1000000n
  })

  const result = await bridgeProtocol.bridge({
    targetChain: 'polygon',
    recipient: '0x...', // Recipient address
    token: '0x...', // USDt contract address
    amount: 1000000n,
    oftContractAddress: '0x...' // Same address used as approval spender
  })
}

getSupportedChains()

Returns the chain descriptors from the package's bundled configuration. This method does not make a network request.

const chains = await bridgeProtocol.getSupportedChains()
// [{ id: 'ethereum', name: 'Ethereum', type: 'evm', nativeToken: 'ETH' }, ...]

getSupportedTokens(options?)

Returns USD₮0 and XAU₮0 descriptors for chains that have a matching bridge contract in bundled configuration. Optional fromChain or toChain filters accept a chain key, chain ID, or endpoint ID. fromToken filters by token symbol.

const allTokens = await bridgeProtocol.getSupportedTokens()
const ethereumTokens = await bridgeProtocol.getSupportedTokens({
  fromChain: 'ethereum'
})
const xaut0Tokens = await bridgeProtocol.getSupportedTokens({
  fromToken: 'XAUT0'
})

The returned descriptors omit on-chain token addresses because this discovery method does not resolve them. Solana, TON, and TRON are destination-only entries and are not returned by getSupportedTokens().

These discovery methods expose separate static chain and token lists. They do not prove that a source token has an on-chain peer for a destination. Confirm the exact pair with quoteBridge() before execution.

Types

BridgeOptions

interface BridgeOptions {
  targetChain: string;              // Destination chain name
  recipient: string;                // Address that will receive bridged tokens
  token: string;                    // Token contract address on source chain
  amount: number | bigint;          // Amount to bridge in token base units
  oftContractAddress?: string;      // Optional custom OFT contract address
  dstEid?: number;                  // Optional destination endpoint ID override
}

BridgeResult

interface BridgeResult {
  hash: string;                     // Main bridge transaction hash
  fee: bigint;                      // Account quote fee; unit depends on account gas-payment mode
  bridgeFee: bigint;                // Standard: source native unit; ERC-4337 helper: bridged-token base units
}

BridgeProtocolConfig

interface BridgeProtocolConfig {
  bridgeMaxFee?: number | bigint;    // Reject when the implementation's fee + bridgeFee is at or above this value
}
type Erc4337QuoteConfig = Partial<
  | EvmErc4337WalletPaymasterTokenConfig
  | EvmErc4337WalletSponsorshipPolicyConfig
  | EvmErc4337WalletNativeCoinsConfig
>

type Erc4337BridgeConfig = Erc4337QuoteConfig & {
  bridgeMaxFee?: number | bigint
}

Fee units and bridgeMaxFee

Account flowfeebridgeFee
WalletAccountEvmSource-chain native base unitsSource-chain native base units
Other compatible accounts using the single-transaction pathAccount-specific quoteSendTransaction() fee unitsSource-chain native base units
ERC-4337 with native gasSource-chain native base unitsBridged-token base units
ERC-4337 with token-paid gasPaymaster-token base unitsBridged-token base units
ERC-4337 with sponsored gas0Bridged-token base units

In 1.0.0-beta.9, bridge() numerically adds fee + bridgeFee when checking bridgeMaxFee, even when the fields have different denominations. This affects ERC-4337 helper flows and any compatible account whose quoted fee is not in source-chain native base units. Do not interpret that sum as a total monetary cost. Set a cap only after confirming that the selected account payment mode produces compatible units. The equality boundary is rejected.

Erc4337QuoteConfig is a partial operation-level override of the wallet's token-paid, sponsored, or native-gas configuration. Sponsored gas uses isSponsored: true and can include sponsorshipPolicyId; native gas uses useNativeCoins: true. See the ERC-4337 wallet configuration for the complete account requirements.

Discovery types

type SwidgeSupportedChain = {
  id: string | number
  name: string
  type: string
  nativeToken: string
}

type SwidgeSupportedToken = {
  token: string
  chain: string | number
  symbol: string
  decimals: number
  address?: string
  name?: string
}

type SwidgeSupportedTokensOptions = {
  fromChain?: string | number
  fromToken?: string
  toChain?: string | number
}

Supported Chains

The bridge protocol supports the following chains:

Source Chains (EVM):

  • 'ethereum' (Chain ID: 1) - ERC-4337 helper support
  • 'arbitrum' (Chain ID: 42161) - ERC-4337 helper support
  • 'optimism' (Chain ID: 10)
  • 'polygon' (Chain ID: 137) - ERC-4337 helper support
  • 'berachain' (Chain ID: 80094)
  • 'ink' (Chain ID: 57073)
  • 'plasma' (Chain ID: 9745) - ERC-4337 helper support
  • 'conflux' (Chain ID: 1030)
  • 'corn' (Chain ID: 21000000)
  • 'avalanche' (Chain ID: 43114)
  • 'celo' (Chain ID: 42220)
  • 'flare' (Chain ID: 14)
  • 'hyperevm' (Chain ID: 999)
  • 'mantle' (Chain ID: 5000)
  • 'megaeth' (Chain ID: 4326)
  • 'monad' (Chain ID: 143)
  • 'morph' (Chain ID: 2818)
  • 'rootstock' (Chain ID: 30)
  • 'sei' (Chain ID: 1329)
  • 'stable' (Chain ID: 988)
  • 'unichain' (Chain ID: 130)
  • 'xlayer' (Chain ID: 196)

Configured destination keys:

  • EVM destinations: same as source-chain set above
  • 'solana' (EID: 30168)
  • 'ton' (EID: 30343)
  • 'tron' (EID: 30420)

The configured keys are not a Cartesian route matrix. Route execution still requires a matching source contract and a destination peer configured on-chain.

Error Handling

The bridge protocol throws specific errors for different failure cases:

try {
  await account.approve({
    token: '0x...', // USDt contract address
    spender: '0x...', // OFT or bridge spender address
    amount: 1000000n
  })

  const result = await bridgeProtocol.bridge({
    targetChain: 'arbitrum',
    recipient: '0x...', // Recipient address
    token: '0x...', // USDt contract address
    amount: 1000000n,
    oftContractAddress: '0x...' // Same address used as approval spender
  })
} catch (error) {
  if (error.message.includes('not supported')) {
    console.error('Chain or token not supported')
  }
  if (error.message.includes('Exceeded maximum fee')) {
    console.error('Bridge fee too high')
  }
  if (error.message.includes('must be connected to a provider')) {
    console.error('Wallet not connected to blockchain')
  }
  if (error.message.includes('requires the protocol to be initialized with a non read-only account')) {
    console.error('Cannot bridge with read-only account')
  }
  if (error.message.includes('cannot be equal to the source chain')) {
    console.error('Cannot bridge to the same chain')
  }
}

Usage Examples

Basic Bridge Operation

import Usdt0ProtocolEvm from '@tetherto/wdk-protocol-bridge-usdt0-evm'
import { WalletAccountEvm } from '@tetherto/wdk-wallet-evm'

async function bridgeTokens() {
  // Create wallet account
  const account = new WalletAccountEvm(seedPhrase, "0'/0/0", {
    provider: 'https://eth.drpc.org'
  })

  // Create bridge protocol
  const bridgeProtocol = new Usdt0ProtocolEvm(account)

  // Get quote first
  const quote = await bridgeProtocol.quoteBridge({
    targetChain: 'arbitrum',
    recipient: '0x...', // Recipient address
    token: '0x...', // USDt contract address
    amount: 1000000n
  })

  console.log('Bridge quote:', quote)

  // Execute bridge
  await account.approve({
    token: '0x...', // USDt contract address
    spender: '0x...', // OFT or bridge spender address
    amount: 1000000n
  })

  const result = await bridgeProtocol.bridge({
    targetChain: 'arbitrum',
    recipient: '0x...', // Recipient address
    token: '0x...', // USDt contract address
    amount: 1000000n,
    oftContractAddress: '0x...' // Optional custom OFT contract
  })

  console.log('Bridge result:', result)

  return result
}

Multi-Chain Bridge

async function bridgeToMultipleChains(account, bridgeProtocol) {
  const chains = ['arbitrum', 'polygon', 'ethereum']
  const token = '0x...' // USDt contract address
  const amount = 1000000n
  const recipient = '0x...' // Recipient address

  const results = []

  for (const chain of chains) {
    try {
      // Get quote
      const quote = await bridgeProtocol.quoteBridge({
        targetChain: chain,
        recipient,
        token,
        amount
      })

      console.log(`Bridge to ${chain}:`, quote)

      // Execute bridge
      await account.approve({
        token,
        spender: '0x...', // OFT or bridge spender address for the source route
        amount
      })

      const result = await bridgeProtocol.bridge({
        targetChain: chain,
        recipient,
        token,
        amount,
        oftContractAddress: '0x...' // Same address used as approval spender
      })

      results.push({ chain, result })
      console.log(`Bridge to ${chain} successful:`, result.hash)

    } catch (error) {
      console.error(`Bridge to ${chain} failed:`, error.message)
    }
  }

  return results
}

ERC-4337 Gasless Bridge

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

async function gaslessBridge() {
  // Create ERC-4337 account
  const account = new WalletAccountEvmErc4337(seedPhrase, "0'/0/0", {
    chainId: 42161,
    provider: 'https://arb1.arbitrum.io/rpc',
    bundlerUrl: 'https://api.candide.dev/public/v3/42161',
    safeModulesVersion: '0.3.0',
    paymasterUrl: 'https://api.candide.dev/public/v3/42161',
    paymasterAddress: '0x8b1f6cb5d062aa2ce8d581942bbb960420d875ba',
    paymasterToken: { address: '0x...' } // Paymaster token configuration
  })

  // Create bridge protocol
  const bridgeProtocol = new Usdt0ProtocolEvm(account)

  // The protocol bundles approval and the helper call in one UserOperation.
  const result = await bridgeProtocol.bridge({
    targetChain: 'polygon',
    recipient: '0x...', // Recipient address
    token: '0x...', // USDt contract address
    amount: 1000000n,
    oftContractAddress: '0x...' // Optional custom OFT contract
  }, {
    paymasterToken: { address: '0x...' } // Paymaster token configuration
  })

  console.log('ERC-4337 bridge result:', result)
  return result
}

Need Help?

On this page