Skip to content
🎉 Welcome to the new Aptos Docs! Click here to submit feedback!

Sponsoring Transactions

Normally, the account that is executing a transaction pays for the gas fees. You can allow another account to cover those charges by sponsoring a transaction.

This can be used to help manage fees from a central account when working with complicated smart contracts.

How To Sponsor a Transaction

Build the transaction with the parameter withFeePayer: true.

sponsor.ts
const transaction = await aptos.transaction.build.simple({
    sender: sender.accountAddress,
    withFeePayer: true,
    data: {
        // All transactions on Aptos are implemented via smart contracts.
        function: "0x1::aptos_account::transfer",
        functionArguments: [destination.accountAddress, 100],
    },
});

Sign the transaction with BOTH the sender and the feePayer.

  1. Sign with the sender account using .sign.
  2. Sign with the sponsor account using .signAsFeePayer.
⚠️

The sponsor uses a different function (.signAsFeePayer) than the sender to sign!

sponsor.ts
const senderAuthenticator = aptos.transaction.sign({
    signer: sender,
    transaction,
});
const feePayerAuthenticator = aptos.transaction.signAsFeePayer({
    signer: feePayer,
    transaction
})

(Optional) Simulate the sponsoring transaction

You can simulate the sponsoring transaction to preview the result before submitting it as follows:

sponsor.ts
const [userTransactionResponse] = await aptos.transaction.simulate.simple({
    signerPublicKey: sender.publicKey,
    transaction,
});

By default, the transaction’s feePayerAddress is set to 0x0, which directs the transaction simulation to skip the gas fee payment. This allows you to simulate the transaction without specifying a fee payer. Note that signerPublicKey is optional and can be omitted if you want to skip the authentication key check for the sender.

You can also simulate the transaction with a specific fee payer by setting the feePayerAddress in the transaction object as follows:

sponsor.ts
transaction.feePayerAddress = feePayer.accountAddress;
const [userTransactionResponse] = await aptos.transaction.simulate.simple({
    signerPublicKey: sender.publicKey,
    feePayerPublicKey: feePayer.publicKey,
    transaction,
});

This setup will verify that feePayer has sufficient balance to cover the gas fee for the transaction. Similarly, feePayerPublicKey is optional and can be omitted if you wish to bypass the authentication key check for the fee payer.

Submit the transaction by combining both signatures.

sponsor.ts
// 4. Submit
const committedTransaction = await aptos.transaction.submit.simple({
    transaction,
    senderAuthenticator: senderAuthenticator,
    feePayerAuthenticator: feePayerAuthenticator,
});

Wait for the transaction to execute.

sponsor.ts
// 5. Wait for results
const executedTransaction = await aptos.waitForTransaction({ transactionHash: committedTransaction.hash });

TypeScript Sponsor Transaction Code Sample

sponsor.ts
/**
 * This example shows how to use the Aptos SDK to send a transaction with a sponsor.
 */
 
import {
    Account,
    Aptos,
    AptosConfig,
    Network,
} from "@aptos-labs/ts-sdk";
 
async function example() {
    console.log("This example will send a sponsored transaction from Alice to Carol.");
 
    // 0. Setup the client and test accounts
    const config = new AptosConfig({ network: Network.DEVNET });
    const aptos = new Aptos(config);
 
    let alice = Account.generate();
    let bob = Account.generate();
    let carol = Account.generate();
 
    console.log("=== Addresses ===\n");
    console.log(`Alice's address is: ${alice.accountAddress}`);
    console.log(`Bob's address is: ${bob.accountAddress}`);
    console.log(`Carol's address is: ${carol.accountAddress}`);
 
    console.log("\n=== Funding accounts ===\n");
    await aptos.fundAccount({
        accountAddress: alice.accountAddress,
        amount: 500_000_000,
    });
    await aptos.fundAccount({
        accountAddress: bob.accountAddress,
        amount: 500_000_000,
    });
    await aptos.fundAccount({
        accountAddress: carol.accountAddress,
        amount: 100,
    });
    console.log("Funded the accounts!")
 
    // 1. Build
    console.log("\n=== 1. Building the transaction ===\n");
    const transaction = await aptos.transaction.build.simple({
        sender: alice.accountAddress,
        withFeePayer: true,
        data: {
            // All transactions on Aptos are implemented via smart contracts.
            function: "0x1::aptos_account::transfer",
            functionArguments: [carol.accountAddress, 100],
        },
    });
    console.log("Built the transaction!")
 
    // 2. Sign
    console.log("\n=== 2. Signing transaction ===\n");
    const aliceSenderAuthenticator = aptos.transaction.sign({
        signer: alice,
        transaction,
    });
    const bobSenderAuthenticator = aptos.transaction.signAsFeePayer({
        signer: bob,
        transaction
    })
    console.log("Signed the transaction!")
 
    // 3. Simulate (Optional)
    console.log("\n === 3. Simulating Response (Optional) === \n")
    const [userTransactionResponse] = await aptos.transaction.simulate.simple({
        signerPublicKey: alice.publicKey,
        feePayerPublicKey: bob.publicKey,
        transaction,
    });
    console.log(userTransactionResponse)
 
    // 4. Submit
    console.log("\n=== 4. Submitting transaction ===\n");
    const committedTransaction = await aptos.transaction.submit.simple({
        transaction,
        senderAuthenticator: aliceSenderAuthenticator,
        feePayerAuthenticator: bobSenderAuthenticator,
    });
    console.log("Submitted transaction hash:", committedTransaction.hash);
 
    // 5. Wait for results
    console.log("\n=== 5. Waiting for result of transaction ===\n");
    const executedTransaction = await aptos.waitForTransaction({ transactionHash: committedTransaction.hash });
    console.log(executedTransaction)
};
 
example();

Common Errors

INSUFFICIENT_BALANCE_FOR_TRANSACTION_FEE :

  1. This may be caused by accidentally using .sign instead of .signAsFeePayer when signing the transaction before submitting on-chain.
  2. Sponsoring a transaction requires that the sponsoring account have enough funds to cover the max possible gas fee. This is often orders of magnitude larger than the expected or actual gas fees required for a transaction to execute. In this case, increase the funds in the account above the max_gas_amount multiplied by the gas_unit_price in the simulated transaction. These must be multiplied because gas is unitless, and so must be multiplied by the conversion rate from gas to octas. You can learn more about gas here.