WDK logoWDK documentation
Core SDKGuides

Send Transactions

Learn how to send native tokens on different blockchains.

You can send native tokens, sign a transaction without broadcasting it, track transaction finality, and orchestrate multi-chain payments from WDK wallet accounts.

Get Testnet Funds: To test these transactions without spending real money, ensure you are on a testnet and have obtained funds. See Testnet Funds & Faucets for a list of available faucets.

BigInt Usage: Always use BigInt (the n suffix) for monetary values to avoid precision loss with large numbers.

Send Native Tokens

The sendTransaction method allows you to transfer value. It accepts a unified configuration object, though specific parameters (like value formatting) may vary slightly depending on the blockchain.

Ethereum Example

On EVM chains, values are typically expressed in Wei (1 ETH = 10^18 Wei).

The following example will:

  1. Retrieve the first Ethereum account (see Manage Accounts)
  2. Send 0.001 ETH (1000000000000000 wei) to an account using sendTransaction.
Send ETH
const ethAccount = await wdk.getAccount('ethereum', 0)

const result = await ethAccount.sendTransaction({
  to: '0x71C7656EC7ab88b098defB751B7401B5f6d8976F',
  value: 1000000000000000n // 0.001 ETH (in Wei)
})

console.log('Transaction sent! Hash:', result.hash)

TON Example

On TON, values are expressed in Nanotons (1 TON = 10^9 Nanotons).

The following example will:

  1. Retrieve the first TON account
  2. Send 1 TON (1000000000 nton) to an account using sendTransaction.
Send TON
// Send TON transaction
const tonAccount = await wdk.getAccount('ton', 0)
const tonResult = await tonAccount.sendTransaction({
  to: 'UQCz5ON7jjK32HnqPushubsHxgsXgeSZDZPvh8P__oqol90r',
  value: 1000000000n // 1 TON (in nanotons)
})
console.log('TON transaction:', tonResult.hash)

Sign Without Broadcasting

Use account.signTransaction() when your app needs a signed transaction payload but does not want WDK to broadcast it immediately. Wallet modules accept their own transaction shape and may return a module-specific signed payload.

Sign An EVM Transaction
const ethAccount = await wdk.getAccount('ethereum', 0)

const signedTransaction = await ethAccount.signTransaction({
  to: '0x71C7656EC7ab88b098defB751B7401B5f6d8976F',
  value: 1000000000000000n
})

console.log('Signed transaction:', signedTransaction)

signTransaction() only signs. Use sendTransaction() when you want WDK to sign, broadcast, and return the transaction hash.

Apply Local Transaction Policies

Use wdk.registerPolicy() to evaluate local ALLOW and DENY rules before account or protocol write methods run. Policies can target a full wallet identifier or selected account indices and derivation paths.

When a policy governs an account, wrapped write operations are default-denied unless a matching ALLOW permits them. For approval limits, start with an explicit ALLOW baseline and add narrower DENY rules for blocked cases.

See Transaction Policies for policy scope, evaluation order, simulation, and error-handling examples.

Handling Responses

The sendTransaction method returns a transaction result object. The most important field is typically hash, which represents the transaction ID on the blockchain. You can use this hash to track the status of your payment on a block explorer.

Track Transaction Finality

Base Wallet v1.0.0-beta.17 defines getTransaction(hash) for one normalized lookup and waitForTransaction(hash, options) for polling until the requested finality. The common receipt reports pending, confirmed, final, or dropped; concrete wallet modules can add native chain fields.

These are base contracts, not universally available chain APIs yet. The default getTransaction() throws NotImplementedError; for example, the currently released @tetherto/wdk-wallet-evm v1.0.0-beta.16 still exposes its legacy receipt lookup instead. Use the following pattern only after the concrete module's release documentation confirms a normalized getTransaction() implementation.

Wait For A Transaction
// `account` must implement the Base Wallet v1.0.0-beta.17 contract.
const receipt = await account.waitForTransaction(transactionHash, {
  target: 'confirmed',
  timeout: 120000,
  interval: 4000,
  maxPollErrors: 3
})

if (receipt.finality === 'dropped') {
  console.error('Transaction dropped before confirmation')
} else if (receipt.success === false) {
  console.error('Transaction confirmed but reverted')
} else {
  console.log('Transaction confirmed in block:', receipt.block)
}

waitForTransaction() defaults to the confirmed target. The base account uses a 60-second polling deadline and four-second interval, but chain modules can override both defaults. A transaction reported as dropped must remain dropped for two consecutive polls before the method returns it.

An unseen transaction raises NoSuchElementError from getTransaction(). The wait helper treats that as transient and keeps polling until its deadline. It also tolerates three consecutive ProviderError results by default and rethrows the next one. Other lookup errors are rethrown immediately; an observed expired deadline throws TimeoutError.

The timeout is checked only after awaited lookups and between polls. It does not cancel getTransaction() or shorten the final sleep to the remaining time, and a target receipt returned by a completed lookup is accepted before the deadline is checked again. A slow or hung provider call can therefore overrun the configured timeout indefinitely. Configure request timeouts or cancellation in the concrete provider separately.

A resolved wait does not guarantee successful execution. It also resolves for reverted transactions and stable dropped receipts, so always inspect finality and success before updating application state.

getTransactionReceipt() is deprecated in @tetherto/wdk-wallet v1.0.0-beta.17. Migrate to getTransaction() only when the concrete module implements it; until then, keep using that module's documented receipt lookup without assuming the normalized finality shape.

Multi-Chain Transactions

You can orchestrate payments across different chains in a single function by acting on multiple account objects sequentially.

The following example will:

  1. Retrieve an ETH and ton account using the getAccount() method.
  2. Send ETH and await the transaction.
  3. Send TON and await the transaction.
Multi-Chain Payment
async function sendCrossChainPayments(wdk) {
  const ethAccount = await wdk.getAccount('ethereum', 0)
  const tonAccount = await wdk.getAccount('ton', 0)

  // 1. Send ETH
  await ethAccount.sendTransaction({
    to: '0x...',
    value: 1000000000000000000n
  })

  // 2. Send TON
  await tonAccount.sendTransaction({
    to: 'EQ...',
    value: 1000000000n
  })
}

Next Steps

For more complex interactions like swapping tokens or bridging assets, learn how to integrate protocols. To guard writes before they execute, add local transaction policies.

On this page