WDK
Wallet Fundamentals: Sending USDt in WDK
Tether's Developer Relations

Wallet Fundamentals: Sending USDt in WDK

10 minutes read

Raquel Carrasco Gonzalez
Article byRaquel Carrasco GonzalezDeveloper Relations Services

In the previous post, Wallet Fundamentals: Build, Sign, and Send Your First Transaction in WDK, we learned how to create wallets, derive accounts, and send native assets across multiple blockchains using the same API.

By the end of this post, you’ll understand how token transfers work across different blockchain models and how WDK unifies them under a single developer experience. We’ll cover both EVM-compatible chains and TRON, two of the most widely used networks for USDt transfers.

Now it’s time to take the next step: sending USDt.

We’ll move from concept to code in four steps: registering wallet modules, retrieving token balances, building and sending USDt transfers on EVM chains, and finally sending USDt on TRON. Like the previous tutorial, this post is entirely hands-on.


Step 1: Install and Register Wallet Modules

Before sending USDt, we first need to register the blockchain wallet modules we want to use. If you have followed the previous post, Wallet Fundamentals: Build, Sign, and Send Your First Transaction in WDK, you will only need to install the Tron chain module and add it’s configuration. 

Set your Bare or Node.js projects (check previous post), and install the required packages:

npm install @tetherto/wdk @tetherto/wdk-wallet-evm @tetherto/wdk-wallet-tron

Now, create your wallet setup:

import WDK from '@tetherto/wdk'
import WalletManagerEvm from '@tetherto/wdk-wallet-evm'
import WalletManagerTron from '@tetherto/wdk-wallet-tron'

const seedPhrase = 'your seed phrase here' // development only

const wdk = new WDK(seedPhrase)
 .registerWallet('ethereum', WalletManagerEvm, {
   provider: 'https://eth.drpc.org'
 })
 .registerWallet('tron', WalletManagerTron, {
   provider: 'https://api.trongrid.io'
 })

console.log('Wallet modules registered')

What's happening here

Just like in the previous tutorial, WDK uses a modular architecture.

Each blockchain is added through .registerWallet(), which injects the blockchain-specific logic into the WDK Core Module while preserving the same developer interface. All through a consistent API regardless of the underlying blockchain.

Step 2: Retrieve Accounts and Token Balances

Now let’s derive accounts and check their USDt balances. We have checked the native assets before, but now we want to check our USDt balance. To do so, we need the token address in the network. And use the .getTokenBalance method. 

Ethereum (EVM Chains)

const ethAccount = await wdk.getAccount('ethereum', 0)

const ethAddress = await ethAccount.getAddress()

console.log('EVM address:', ethAddress)

// Retrieve USDt balance
const usdtBalance = await ethAccount.getTokenBalance({
 contractAddress: '0xdAC17F958D2ee523a2206206994597C13D831ec7'
})

console.log('USDt balance:', usdtBalance)

TRON

const tronAccount = await wdk.getAccount('tron', 0)

const tronAddress = await tronAccount.getAddress()

console.log('TRON address:', tronAddress)

// Retrieve TRC-20 USDt balance
const usdtBalance = await tronAccount.getTokenBalance({
 contractAddress: 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t'
})

console.log('USDt balance:', usdtBalance)

What's happening here

Unlike native assets like ETH or BTC, USDt is implemented as a smart contract token. That means:

  • On Ethereum and EVM chains, USDt is an ERC-20 token
  • On TRON, USDt is a TRC-20 token

Even though the token standards differ internally, WDK exposes the same mental model:

  • Specify the token contract
  • retrieve balances
  • send transfers

The contractAddress identifies which token contract you want to interact with.

For Ethereum mainnet:

  • USDt ERC-20 contract:
    0xdAC17F958D2ee523a2206206994597C13D831ec7

For TRON mainnet:

  • USDt TRC-20 contract:
    TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t

Step 3: Send USDt on EVM Chains

Now let’s send USDt on Ethereum or any EVM-compatible network.

// Estimate transfer fee first
const quote = await ethAccount.quoteTransfer({
 contractAddress: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
 to: '0x350188fB7EB1A49d43398b610a3714844b361FC3',
 value: 1000000n // 1 USDt (6 decimals)
})

console.log('Estimated fee:', quote.fee)

// Send USDt
try {
 const result = await ethAccount.transfer({
   contractAddress: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
   to: '0x350188fB7EB1A49d43398b610a3714844b361FC3',
   value: 1000000n
 })

 console.log('Transaction hash:', result.hash)
 console.log('Fee paid:', result.fee)
} catch (error) {
 console.error('Transfer failed:', error.message)
}

What's happening here

Token transfers on EVM chains are, in fact, smart contract interactions. Instead of transferring ETH directly:

  1. The wallet calls the token contract
  2. The token contract updates balances internally
  3. The blockchain records the transaction

But with WDK, the process still follows the same pattern:

  1. Estimate the fee
  2. Send the transaction
  3. Handle errors

So developers can focus on application logic instead of blockchain internals. Also, the value field uses the token’s smallest denomination. USDt uses 6 decimals (other tokens use 10, 18, or other decimals):

1000000n  // 1 USDt = 1,000,000 units

Step 4: Send USDt on TRON

Now let’s send USDt on TRON.

// Estimate transfer fee
const quote = await tronAccount.quoteTransfer({
 contractAddress: 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t', //USDt in Tron
 to: 'TVjsyZ7fYF3qLF6BQgPmTEZy1xrNNyVAAA', //Tron destination address
 value: 1000000n // 1 USDt 
})

console.log('Estimated fee:', quote.fee)

// Send USDt
try {
 const result = await tronAccount.transfer({
   contractAddress: 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t',
   to: 'TVjsyZ7fYF3qLF6BQgPmTEZy1xrNNyVAAA',
   value: 1000000n
 })

 console.log('Transaction hash:', result.hash)
 console.log('Fee paid:', result.fee)
} catch (error) {
 console.error('Transfer failed:', error.message)
}

What's happening here

TRON uses a different architecture from Ethereum, but WDK keeps the developer workflow nearly identical.

Under the hood:

  1. Ethereum uses gas
  2. TRON uses energy and bandwidth
  3. Address formats are different
  4. Transaction serialization differs completely

But from the application developer's perspective, with WDK, the interface remains the same, the process still follows the same pattern:

  1. Estimate the fee
  2. Send the transaction
  3. Handle errors

This is one of WDK’s main design goals: abstract blockchain-specific complexity while preserving self-custody and low-level control.

Next Steps

In this post, we learned how to send USDt across EVM chains and TRON using WDK’s unified wallet interface.

In the next post, Wallet Fundamentals: Build, Sign, and Send Your First Transaction in WDK, we’ll explore gas abstraction and account abstraction flows, including how to enable gas sponsorship and stablecoin-powered transactions with ERC-4337, Ton gasless, and Tron Gassfree modules.

Receive the latest WDK news

Click to subscribe to the WDK newsletter and stay updated!

Latest Articles & Guides

Wallet Fundamentals: Sending USDt in WDK | WDK by Tether