Send Transactions
Quote, sign, and send paymaster-funded Solana transactions.
This guide explains how to send native SOL, sign without broadcasting, quote paymaster fees, send an already signed transaction, and use prebuilt transaction messages.
Use BigInt values for token and lamport amounts to avoid precision loss.
Send Native SOL
Use sendTransaction({ to, value }) to send native SOL through the configured paymaster:
const result = await account.sendTransaction({
to: 'Recipient1111111111111111111111111111111',
value: 1000000n
})
console.log('Transaction hash:', result.hash)
console.log('Paymaster fee:', result.fee)The returned fee is denominated in the configured paymaster token's base units, not lamports.
Quote Before Sending
Use quoteSendTransaction() to get the paymaster fee before submitting:
const quote = await account.quoteSendTransaction({
to: 'Recipient1111111111111111111111111111111',
value: 1000000n
})
console.log('Paymaster fee estimate:', quote.fee)Set a Transaction Fee Cap
Set transactionMaxFee in the wallet config or as a per-call override to cancel sendTransaction() or signTransaction() when the paymaster fee is higher than your cap:
const result = await account.sendTransaction({
to: 'Recipient1111111111111111111111111111111',
value: 1000000n
}, {
transactionMaxFee: 500000n
})quoteSendTransaction() returns the estimated fee without enforcing transactionMaxFee. sendTransaction() and signTransaction() reject only when the fee is greater than the cap, so a fee equal to the cap is allowed.
Sign Without Broadcasting
Use signTransaction() when another process will inspect or submit the fully signed transaction:
const signedTransaction = await account.signTransaction({
to: 'Recipient1111111111111111111111111111111',
value: 1000000n
}, {
transactionMaxFee: 500000n
})
console.log('Signed transaction:', signedTransaction)The module adds the payment instruction, checks transactionMaxFee, signs with the owner account, asks the paymaster to sign, and returns a FullySignedTransaction. It does not broadcast from signTransaction().
Quote and Send a Signed Transaction
In 1.0.0-beta.2, owned accounts accept the FullySignedTransaction returned by signTransaction() in both quoteSendTransaction() and sendTransaction():
const signedTransaction = await account.signTransaction({
to: 'Recipient1111111111111111111111111111111',
value: 1000000n
}, {
transactionMaxFee: 500000n
})
const { fee } = await account.quoteSendTransaction(signedTransaction)
console.log('Embedded paymaster fee:', fee)
const result = await account.sendTransaction(signedTransaction, {
transactionMaxFee: 500000n
})
console.log('Transaction hash:', result.hash)For a signed input, quoteSendTransaction() does not request a new paymaster quote or broadcast anything. It decodes the payment amount from the signed message's SPL token payment instruction; the result is a bigint in the configured paymaster token's base units.
sendTransaction(signedTransaction) decodes that same fee, enforces transactionMaxFee, base64-encodes the fully signed wire transaction, and submits it through the configured Solana RPC with encoding: 'base64'. It does not call the paymaster's signAndSendTransaction() again.
A signed transaction is immutable. This flow does not obtain a fresh blockhash, durable nonce, payment instruction, or paymaster signature. Submit the exact signed payload while its existing lifetime is valid, and check its original signature before retrying after an uncertain RPC result.
Use a TransactionMessage
Pass a prebuilt TransactionMessage when your app needs custom instructions.
const quote = await account.quoteSendTransaction(transactionMessage)
console.log('Paymaster fee estimate:', quote.fee)
const result = await account.sendTransaction(transactionMessage)
console.log('Transaction hash:', result.hash)If the message already includes a recent blockhash or durable nonce lifetime, the module preserves it. If it does not, the module adds a blockhash lifetime before it requests payment instructions and signatures. A signed result keeps that lifetime; sendTransaction(signedTransaction) does not refresh it.
If a prebuilt message sets feePayer, it must equal paymasterAddress. The module sets the fee payer to the paymaster address before asking the paymaster for fee instructions.
Read a Transaction Receipt
Use getTransactionReceipt(hash) to check whether a submitted transaction has been included in a block:
const receipt = await account.getTransactionReceipt(result.hash)
if (receipt === null) {
console.log('Transaction is not included in a block yet.')
} else {
console.log('Transaction receipt:', receipt)
}The method returns null while the transaction is still pending.
Override Paymaster Token
You can override the paymaster token for one quote, sign, or send call:
const result = await account.sendTransaction({
to: 'Recipient1111111111111111111111111111111',
value: 1000000n
}, {
paymasterToken: {
address: 'AlternateFeeMint11111111111111111111111111'
}
})For an already signed transaction, the payment token is already embedded in the signed message. Do not use paymasterToken as an override to try to change it; only transactionMaxFee is applied before direct broadcast.
Next Steps
To transfer SPL tokens instead of native SOL, see Transfer SPL Tokens.