WDK logoWDK documentation
EVMStandard EVMGuides

Send Transactions

Send native tokens on EVM chains with EIP-1559 or legacy gas settings.

This guide explains how to send EVM transactions with EIP-1559 gas parameters, send legacy gas transactions, deploy contracts, sign without broadcasting, estimate fees, cap transaction fees, use dynamic fee rates, and wait for finality.

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

Send with EIP-1559 Gas Parameters

You can use account.sendTransaction() to send an EIP-1559 transaction. EIP-1559 transactions provide more predictable gas fees and faster inclusion times.

EIP-1559 Transaction
const result = await account.sendTransaction({
  to: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6',
  value: 1000000000000000000n, // 1 ETH in wei
  maxFeePerGas: 30000000000,
  maxPriorityFeePerGas: 2000000000
})
console.log('Transaction hash:', result.hash)
console.log('Transaction fee:', result.fee, 'wei')

Send with Legacy Gas Parameters

You can also use account.sendTransaction() with legacy gas settings for chains that do not support EIP-1559.

Legacy Transaction
const legacyResult = await account.sendTransaction({
  to: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6',
  value: 1000000000000000000n,
  gasPrice: 20000000000n,
  gasLimit: 21000
})
console.log('Transaction hash:', legacyResult.hash)

Deploy a Contract

For contract-creation transactions, omit to or pass to: null and provide the deployment bytecode in data.

Contract Creation
const result = await account.sendTransaction({
  to: null,
  value: 0n,
  data: contractBytecode,
  maxFeePerGas: 30000000000n,
  maxPriorityFeePerGas: 2000000000n
})

console.log('Deployment transaction:', result.hash)

Sign Without Broadcasting

Use account.signTransaction() when you need a signed raw transaction but want to submit it through a separate relay, service, or review flow.

Sign EVM Transaction
async function signReviewedTransaction(transaction) {
  // Review the recipient, value, data, chain ID, nonce, and fee fields first.
  return await account.signTransaction(transaction)
}

signTransaction() returns signed raw transaction hex and does not broadcast it. In 1.0.0-beta.18, sendTransaction(signedTransaction) quotes the signed transaction, enforces transactionMaxFee, and passes those exact bytes to eth_sendRawTransaction; it does not repopulate or re-sign them. Submit only after an independent review and explicit approval step. Use the object form of sendTransaction() when WDK should populate, sign, and broadcast the transaction.

Submit an Approved Signed Transaction
async function submitApprovedTransaction(signedTransaction, reviewSignedTransaction) {
  const review = await reviewSignedTransaction(signedTransaction)
  if (review?.approved !== true) {
    throw new Error('Signed transaction was not approved')
  }

  return await account.sendTransaction(signedTransaction)
}

reviewSignedTransaction is an application-supplied callback. It must decode the signed transaction, show or otherwise verify its recipient, value, data, chain ID, nonce, and fee fields, and return { approved: true } only after explicit approval. Any other result fails closed without broadcasting.

Estimate Transaction Fees

Use account.quoteSendTransaction() to get a fee estimate before sending.

Quote Transaction Fee
const quote = await account.quoteSendTransaction({
  to: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6',
  value: 1000000000000000000n
})
console.log('Estimated fee:', quote.fee, 'wei')

Cap Transaction Fees

Set transactionMaxFee when you create the wallet to stop native sendTransaction() calls and provider-backed signTransaction() calls if the estimated fee exceeds your limit. Offline signing without a provider cannot estimate fees, so the fee-cap check does not run there.

Cap Native Transaction Fees
const wallet = new WalletManagerEvm(seedPhrase, {
  provider: 'https://eth.drpc.org',
  transactionMaxFee: 100000000000000n
})

Use Dynamic Fee Rates

Retrieve current fee rates using wallet.getFeeRates() and apply them to your transaction.

Dynamic Fee Rates
const feeRates = await wallet.getFeeRates()
console.log('Normal fee rate:', feeRates.normal, 'wei')
console.log('Fast fee rate:', feeRates.fast, 'wei')

const result = await account.sendTransaction({
  to: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6',
  value: 1000000000000000000n,
  data: '0x',
  gasLimit: 21000,
  maxFeePerGas: feeRates.fast,
  maxPriorityFeePerGas: 2000000000n
})
console.log('Transaction sent:', result.hash)
console.log('Fee paid:', result.fee, 'wei')

Gas Estimation: The maxFeePerGas and maxPriorityFeePerGas fields enable EIP-1559 transactions, ensuring more predictable gas fees and faster inclusion times.

Wait for Finality

Use the hash returned by sendTransaction() with waitForTransaction():

Wait for a Finalized Transaction
const result = await account.sendTransaction({
  to: recipient,
  value: 1000000000000000n
})

const receipt = await account.waitForTransaction(result.hash, {
  target: 'final'
})

if (receipt.finality === 'dropped') {
  console.log('The sender nonce was consumed by another transaction')
} else if (receipt.success === false) {
  console.log('The transaction was included but reverted')
}

The EVM default timeout is 120 seconds and the polling interval is four seconds. A node that does not implement the finalized block tag can report confirmed but cannot advance the receipt to final; use a bounded timeout appropriate for that provider. A finality target indicates settlement level, not execution success, so inspect success.

getTransactionReceipt() remains available for the native ethers receipt but is deprecated in favor of getTransaction().

Next Steps

To transfer ERC-20 tokens instead of native tokens, see Transfer ERC-20 Tokens.

On this page