@t402/wdk-bridge
v2.9.0
Published
Cross-chain USDT0 bridging with Tether WDK and LayerZero
Maintainers
Readme
@t402/wdk-bridge
Cross-chain USDT0 bridging with the WDK and LayerZero.
Features
- Multi-Chain Bridging: Bridge USDT0 between Ethereum, Arbitrum, Ink, Berachain, and Unichain
- Auto-Routing: Automatic source chain selection based on balance and fees
- Route Strategies: Choose cheapest, fastest, or preferred chain
- Delivery Tracking: Track bridge status via LayerZero Scan
- Balance Management: Query USDT0 balances across all configured chains
Installation
npm install @t402/wdk-bridge
# or
pnpm add @t402/wdk-bridgePeer Dependencies
npm install @tetherto/wdk @tetherto/wdk-wallet-evmQuick Start
import { WdkBridgeClient } from '@t402/wdk-bridge'
// Create client with WDK accounts for multiple chains
const bridge = new WdkBridgeClient({
accounts: {
ethereum: ethereumWdkAccount,
arbitrum: arbitrumWdkAccount,
},
defaultStrategy: 'cheapest',
})
// Get balances across all chains
const summary = await bridge.getBalances()
console.log(`Total USDT0: ${summary.totalUsdt0}`)
// Auto-bridge from the best source chain
const result = await bridge.autoBridge({
toChain: 'ethereum',
amount: 100_000000n, // 100 USDT0
recipient: '0xRecipientAddress...',
})
// Wait for delivery
const delivery = await result.waitForDelivery()
if (delivery.success) {
console.log(`Delivered! Destination tx: ${delivery.dstTxHash}`)
}API Reference
WdkBridgeClient
Constructor
interface WdkBridgeClientConfig {
accounts: Record<string, WdkAccount> // Chain name -> WDK account
defaultStrategy?: RouteStrategy // 'cheapest' | 'fastest' | 'preferred'
defaultSlippage?: number // Default: 0.5 (0.5%)
}
const bridge = new WdkBridgeClient(config)autoBridge(params): Promise<WdkBridgeResult>
Execute a bridge with automatic source chain selection.
interface AutoBridgeParams {
toChain: string // Destination chain
amount: bigint // Amount in USDT0 decimals (6)
recipient: Address // Recipient address on destination
preferredSourceChain?: string // Optional preferred source
slippageTolerance?: number // Slippage tolerance (default: 0.5%)
}
interface WdkBridgeResult {
txHash: Hex // Source transaction hash
messageGuid: string // LayerZero message GUID
amountSent: bigint // Amount sent
amountToReceive: bigint // Expected amount to receive
fromChain: string // Source chain
toChain: string // Destination chain
estimatedTime: number // Estimated delivery time (seconds)
waitForDelivery(options?: WaitOptions): Promise<DeliveryResult>
}bridge(params): Promise<WdkBridgeResult>
Execute a bridge from a specific chain.
await bridge.bridge({
fromChain: 'arbitrum',
toChain: 'ethereum',
amount: 50_000000n,
recipient: '0x...',
})getRoutes(toChain, amount): Promise<BridgeRoute[]>
Get all available routes to a destination.
interface BridgeRoute {
fromChain: string
toChain: string
nativeFee: bigint // Fee in native token
amountToSend: bigint
minAmountToReceive: bigint
estimatedTime: number // Seconds
available: boolean
unavailableReason?: string
}getBalances(): Promise<BalanceSummary>
Get USDT0 balances across all configured chains.
interface BalanceSummary {
balances: ChainBalance[]
totalUsdt0: bigint
chainsWithBalance: string[]
bridgeableChains: string[] // Chains with enough for bridge minimum
}
interface ChainBalance {
chain: string
chainId: number
usdt0Balance: bigint
nativeBalance: bigint
canBridge: boolean
}getChainBalance(chain): Promise<ChainBalance>
Get balance for a specific chain.
trackMessage(guid): Promise<Message>
Track a LayerZero message by GUID.
waitForDelivery(guid, options): Promise<Message>
Wait for a message to be delivered.
Supported Chains
| Chain | Chain ID | LayerZero EID | | --------- | -------- | ------------- | | Ethereum | 1 | 30101 | | Arbitrum | 42161 | 30110 | | Ink | 57073 | 30291 | | Berachain | 80094 | 30362 | | Unichain | 130 | 30320 |
Route Strategies
| Strategy | Description |
| ----------- | ------------------------------------------------------ |
| cheapest | Select route with lowest native fee (default) |
| fastest | Select route with shortest estimated time |
| preferred | Use preferred chain if available, fallback to cheapest |
Estimated Bridge Times
| Route | Time | | ------------------------------- | ----------- | | L1 -> L2 (Ethereum -> Arbitrum) | ~3 minutes | | L2 -> L1 (Arbitrum -> Ethereum) | ~15 minutes | | L2 -> L2 (Cross-L2) | ~5 minutes |
Constants
import {
BRIDGE_CHAINS,
USDT0_ADDRESSES,
CHAIN_IDS,
MIN_BRIDGE_AMOUNT,
DEFAULT_SLIPPAGE,
supportsBridging,
getUsdt0Address,
getChainId,
getEstimatedBridgeTime,
} from '@t402/wdk-bridge'
// Minimum bridge amount (1 USDT0)
console.log(MIN_BRIDGE_AMOUNT) // 1_000000n
// Check if chain supports bridging
supportsBridging('arbitrum') // true
supportsBridging('polygon') // false
// Get USDT0 address
getUsdt0Address('ethereum')
// '0x6C96dE32CEa08842dcc4058c14d3aaAD7Fa41dee'Examples
Balance Overview
const bridge = new WdkBridgeClient({
accounts: {
ethereum: ethAccount,
arbitrum: arbAccount,
ink: inkAccount,
},
})
const summary = await bridge.getBalances()
console.log(`Total USDT0: ${summary.totalUsdt0 / 1_000000n} USDT0`)
console.log(`Chains with balance: ${summary.chainsWithBalance.join(', ')}`)
console.log(`Bridgeable from: ${summary.bridgeableChains.join(', ')}`)Route Comparison
const routes = await bridge.getRoutes('ethereum', 100_000000n)
for (const route of routes) {
if (route.available) {
console.log(
`${route.fromChain} -> ${route.toChain}: ` +
`fee=${route.nativeFee}, time=${route.estimatedTime}s`,
)
} else {
console.log(`${route.fromChain}: ${route.unavailableReason}`)
}
}Delivery Tracking
const result = await bridge.autoBridge({
toChain: 'ethereum',
amount: 50_000000n,
recipient: '0x...',
})
console.log(`Bridge initiated: ${result.txHash}`)
console.log(`Message GUID: ${result.messageGuid}`)
console.log(`Expected delivery: ${result.estimatedTime}s`)
// Track delivery with status updates
const delivery = await result.waitForDelivery({
timeout: 600_000, // 10 minutes
pollInterval: 10_000, // 10 seconds
onStatusChange: (status) => {
console.log(`Status: ${status}`)
},
})
if (delivery.success) {
console.log(`Delivered in tx: ${delivery.dstTxHash}`)
} else {
console.log(`Failed: ${delivery.error}`)
}Custom RPC URLs
const bridge = new WdkBridgeClient({
accounts: { arbitrum: arbAccount },
})
// Set custom RPC URL
bridge.setRpcUrl('arbitrum', 'https://arb1.arbitrum.io/rpc')Related Packages
@t402/core- Core protocol types and client@t402/wdk- the WDK integration@t402/wdk-gasless- ERC-4337 gasless payments@t402/wdk-multisig- Safe multi-sig wallets@t402/evm- EVM mechanism implementation
License
Apache-2.0
Trademark Notice
T402 is an independent open-source project. It is not affiliated with, endorsed by, sponsored by, or in any way officially connected with Tether Operations Limited, Tether Holdings, or any of their affiliates.
"Tether", "USDT", "USDT0", "USDC", and other token names and trademarks referenced are the property of their respective owners and are used nominatively to describe token interoperability or to reference public open-source products (Apache 2.0). Compatibility with Tether's open-source WDK is at the user's option and does not represent a formal partnership.
