WDK logoWDK documentation
SolanaStandard SolanaGuides

Send SOL

Send native SOL and estimate transaction fees on Solana.

This guide explains how to send native SOL, wait for finality, sign a transaction without broadcasting, quote and send a signed transaction, estimate transaction fees, cap native transaction fees, quote or send a TransactionMessage, use a serialized transaction, use dynamic fee rates, and run a complete SOL transfer flow.

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

On Solana, values are expressed in lamports (1 SOL = 10^9 lamports). Fees are calculated based on the recent blockhash and instruction count.

Send Native SOL

Use account.sendTransaction() to transfer SOL to a recipient address.

Send SOL
const result = await account.sendTransaction({
  to: 'publicKey', // Recipient's base58-encoded public key
  value: 1000000000n // 1 SOL in lamports
})
console.log('Transaction hash:', result.hash)
console.log('Transaction fee:', result.fee, 'lamports')

Wait for Finality

Pass the transaction signature returned in result.hash to waitForTransaction():

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

console.log('Finalized in slot:', receipt.block)
if (receipt.success === false) {
  console.log('The transaction was finalized but execution failed')
}

Solana processed, confirmed, and finalized status map to WDK pending, confirmed, and final. The default wait interval is four seconds and timeout is 60 seconds. The module does not classify evicted or never-landed signatures as dropped; they time out. Always inspect success after settlement.

getTransactionReceipt() remains available for native transaction data but is deprecated in favor of getTransaction().

Sign a Transaction Without Broadcasting

Use account.signTransaction() when you need a fully signed transaction but want another process to review, relay, or submit it.

Sign SOL Transaction
const signedTransaction = await account.signTransaction({
  to: '11111111111111111111111111111112',
  value: 1000000000n
})
console.log('Signed transaction:', signedTransaction)

Quote and Send a Signed Transaction

Pass the FullySignedTransaction returned by signTransaction() to the quote and send methods when review and submission are separate steps.

Quote and Send Signed Bytes
const signedTransaction = await account.signTransaction({
  to: '11111111111111111111111111111112',
  value: 1000000000n
})

const quote = await account.quoteSendTransaction(signedTransaction)
console.log('Estimated fee:', quote.fee, 'lamports')

const result = await account.sendTransaction(signedTransaction)
console.log('Transaction signature:', result.hash)

Signing seals the recent blockhash or durable nonce into the message. WDK broadcasts the signed bytes unchanged and does not refresh the transaction lifetime or re-sign it. Submit the transaction before that lifetime becomes invalid. sendTransaction() quotes it again and enforces transactionMaxFee.

Estimate Transaction Fees

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

Quote Transaction Fee
const quote = await account.quoteSendTransaction({
  to: 'publicKey',
  value: 1000000000n
})
console.log('Estimated fee:', quote.fee, 'lamports')

Cap Native Transaction Fees

Set transactionMaxFee when you create the wallet to stop native sendTransaction() and signTransaction() calls if the estimated fee is greater than your limit. A fee equal to the configured cap is allowed. Use transferMaxFee separately for SPL token transfers.

Set a Native Transaction Fee Cap
const wallet = new WalletManagerSolana(seedPhrase, {
  provider: 'https://api.mainnet-beta.solana.com',
  transactionMaxFee: 10000000n // 0.01 SOL in lamports
})

Quote or Send a TransactionMessage

Use a prebuilt TransactionMessage when you need custom instructions or a durable nonce flow.

If the transaction message already includes a recent blockhash or durable nonce lifetime, WDK preserves it. If it does not, WDK fetches the latest blockhash before quoting or sending. When you set feePayer, it must match the wallet address.

Quote and Send a TransactionMessage
const quote = await account.quoteSendTransaction(txMessage)
console.log('Estimated fee:', quote.fee, 'lamports')

const result = await account.sendTransaction(txMessage)
console.log('Transaction hash:', result.hash)

Use a Serialized Transaction

Pass a base64-encoded serialized Solana transaction when another service builds the transaction, such as a swap or bridge API. An owned account signs this form before sending it; the serialized transaction's fee payer must be the account address.

Treat serialized transactions from another service as untrusted. Before signing or broadcasting one, decode and independently review every instruction, account, amount, and transaction lifetime. WDK checks the fee payer and required signatures, but it does not validate the transaction's intended effects.

Quote and Sign a Reviewed Serialized Transaction
async function quoteAndSignReviewedTransaction(serializedTransaction) {
  const quote = await account.quoteSendTransaction(serializedTransaction)
  console.log('Estimated fee:', quote.fee, 'lamports')

  const signedTransaction = await account.signTransaction(serializedTransaction)
  return { quote, signedTransaction }
}

A swap or bridge service normally supplies serializedTransaction. Call this helper only after your application completes the independent review above. quoteSendTransaction(serializedTransaction) only decodes and quotes the compiled message, and signTransaction(serializedTransaction) signs without broadcasting. After the same review and an explicit approval step, call sendTransaction(serializedTransaction) to sign and broadcast directly, or pass the returned signedTransaction to sendTransaction() to broadcast the reviewed signed transaction. After this account adds its signature, the transaction must be fully signed; pre-existing signatures may be retained. A malformed serialized transaction causes these operations to throw. signTransaction() and sendTransaction() also throw when the serialized transaction's fee payer does not match the account address; quoteSendTransaction() does not check the fee payer.

Use Dynamic Fee Rates

Retrieve current fee rates using wallet.getFeeRates(). Rates are calculated based on the recent blockhash and compute unit prices.

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

Complete Example

Full SOL Transfer Flow
async function sendSOLTransfer(account, wallet) {
  const solBalance = await account.getBalance()
  const transferAmount = 1000000000n // 1 SOL

  if (solBalance < transferAmount) {
    throw new Error('Insufficient SOL balance')
  }

  const quote = await account.quoteSendTransaction({
    to: '11111111111111111111111111111112',
    value: transferAmount
  })
  console.log('Estimated fee:', quote.fee, 'lamports')

  const result = await account.sendTransaction({
    to: '11111111111111111111111111111112',
    value: transferAmount
  })

  console.log('Transaction hash:', result.hash)
  console.log('Fee paid:', result.fee, 'lamports')

  return result
}

Next Steps

To transfer SPL tokens instead of native SOL, see Transfer SPL Tokens.

On this page