Sponsoring Transactions
Normally, the account executing a transaction pays for the gas fees. With sponsored (fee payer) transactions, a separate account covers the gas cost on behalf of the sender. This is useful for onboarding new users who do not yet hold APT, subsidizing transaction costs for your application’s users, or managing gas fees from a centralized treasury.
How to Sponsor a Transaction
Section titled “How to Sponsor a Transaction”-
Build the entry function payload.
Create the payload for the transaction the sender wants to execute.
use aptos_sdk::types::EntryFunctionPayload;let payload = EntryFunctionPayload::new("0x1::aptos_account::transfer".parse()?,vec![],vec![bob.address().into(), 10_000_000u64.into()],); -
Build the raw transaction with
TransactionBuilder.Construct a raw transaction from the sender account.
use aptos_sdk::transaction_builder::TransactionBuilder;let raw_txn = TransactionBuilder::new(payload, aptos.get_chain_id().await?).sender(alice.address()).sequence_number(aptos.get_sequence_number(alice.address()).await?).max_gas_amount(10_000).gas_unit_price(100).expiration_timestamp_secs(aptos.get_latest_ledger_info().await?.timestamp() + 60,).build(); -
Create a
FeePayerRawTransactionthat designates the sponsor.Wrap the raw transaction with the fee payer’s address. Use
new_simplewhen there are no additional secondary signers.use aptos_sdk::types::FeePayerRawTransaction;let fee_payer_txn = FeePayerRawTransaction::new_simple(raw_txn,sponsor.address(),); -
Sign the transaction with both the sender and the sponsor.
Use
sign_fee_payer_transactionto produce a signed transaction. The second argument is the sender, the third is an empty slice (used for secondary signers in multi-agent sponsored transactions), and the fourth is the fee payer.let signed_txn = aptos.sign_fee_payer_transaction(&fee_payer_txn,&alice, // Sender&[], // Secondary signers (empty for simple transactions)&sponsor, // Fee payer)?; -
Submit the transaction and wait for confirmation.
let result = aptos.submit_and_wait(signed_txn).await?;let success = result.data.get("success").and_then(|v| v.as_bool()).unwrap_or(false);println!("Transaction success: {}", success);
Sponsored Multi-Agent Transactions
Section titled “Sponsored Multi-Agent Transactions”You can combine fee payer sponsorship with multi-agent transactions. In this case, provide the secondary signers when constructing the FeePayerRawTransaction and when signing.
use aptos_sdk::types::FeePayerRawTransaction;
// Create fee payer transaction with secondary signerslet fee_payer_txn = FeePayerRawTransaction::new( raw_txn, vec![bob.address()], // Secondary signer addresses sponsor.address(), // Fee payer address);
// Sign with all partieslet signed_txn = aptos.sign_fee_payer_transaction( &fee_payer_txn, &alice, // Primary sender &[&bob as &dyn Account], // Secondary signers &sponsor, // Fee payer)?;Common Errors
Section titled “Common Errors”INSUFFICIENT_BALANCE_FOR_TRANSACTION_FEE— The sponsor account does not have enough APT to cover the maximum possible gas fee. The sponsor must hold at leastmax_gas_amount * gas_unit_priceoctas. You can simulate the transaction first to get a tighter gas estimate, then setmax_gas_amountaccordingly. See Simulating Transactions for details.INVALID_AUTH_KEY— The fee payer address specified inFeePayerRawTransactiondoes not match the account that signed as the fee payer. Verify that you pass the correct sponsor account to both the constructor and the signing function.
Full Working Example
Section titled “Full Working Example”/// This example demonstrates a sponsored transaction where a separate/// account (sponsor) pays the gas fees for Alice's transfer to Bob.use aptos_sdk::{Aptos, AptosConfig};use aptos_sdk::account::Ed25519Account;use aptos_sdk::types::{EntryFunctionPayload, FeePayerRawTransaction};use aptos_sdk::transaction_builder::TransactionBuilder;
#[tokio::main]async fn main() -> anyhow::Result<()> { // Connect to testnet let aptos = Aptos::new(AptosConfig::testnet())?;
// Generate accounts let alice = Ed25519Account::generate(); let bob = Ed25519Account::generate(); let sponsor = Ed25519Account::generate();
// Fund accounts -- note that Alice only needs a small amount for the // transfer itself, while the sponsor needs enough to cover gas. aptos.fund_account(alice.address(), 100_000_000).await?; aptos.fund_account(bob.address(), 100_000_000).await?; aptos.fund_account(sponsor.address(), 100_000_000).await?;
println!("Alice: {}", alice.address()); println!("Bob: {}", bob.address()); println!("Sponsor: {}", sponsor.address());
// Check initial balances let alice_balance = aptos.get_balance(alice.address()).await?; let sponsor_balance = aptos.get_balance(sponsor.address()).await?; println!("\n=== Initial Balances ==="); println!("Alice: {} octas", alice_balance); println!("Sponsor: {} octas", sponsor_balance);
// 1. Build the payload let payload = EntryFunctionPayload::new( "0x1::aptos_account::transfer".parse()?, vec![], vec![bob.address().into(), 10_000_000u64.into()], );
// 2. Build the raw transaction let raw_txn = TransactionBuilder::new(payload, aptos.get_chain_id().await?) .sender(alice.address()) .sequence_number(aptos.get_sequence_number(alice.address()).await?) .max_gas_amount(10_000) .gas_unit_price(100) .expiration_timestamp_secs( aptos.get_latest_ledger_info().await?.timestamp() + 60, ) .build();
// 3. Create the fee payer transaction let fee_payer_txn = FeePayerRawTransaction::new_simple( raw_txn, sponsor.address(), );
// 4. Sign with both the sender and the sponsor let signed_txn = aptos.sign_fee_payer_transaction( &fee_payer_txn, &alice, &[], &sponsor, )?;
// 5. Submit and wait let result = aptos.submit_and_wait(signed_txn).await?; let success = result .data .get("success") .and_then(|v| v.as_bool()) .unwrap_or(false); println!("\nTransaction success: {}", success);
// Verify that the sponsor paid the gas, not Alice let new_alice_balance = aptos.get_balance(alice.address()).await?; let new_sponsor_balance = aptos.get_balance(sponsor.address()).await?; println!("\n=== Balances After Transfer ==="); println!("Alice: {} octas (paid only the transfer amount)", new_alice_balance); println!("Sponsor: {} octas (paid the gas fee)", new_sponsor_balance);
Ok(())}