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
Section titled “How To Sponsor a Transaction”-
Build the transaction with the parameter withFeePayer: true.
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.
- Sign with the sender account using
.sign
. - Sign with the sponsor account using
.signAsFeePayer
.
const senderAuthenticator = aptos.transaction.sign({signer: sender,transaction,});const feePayerAuthenticator = aptos.transaction.signAsFeePayer({signer: feePayer,transaction}) - Sign with the sender account using
-
(Optional) Simulate the sponsoring transaction
You can simulate the sponsoring transaction to preview the result before submitting it as follows:
const [userTransactionResponse] = await aptos.transaction.simulate.simple({signerPublicKey: sender.publicKey,transaction,});By default, the
transaction
’sfeePayerAddress
is set to0x0
, which directs the transaction simulation to skip the gas fee payment. This allows you to simulate the transaction without specifying a fee payer. Note thatsignerPublicKey
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 thetransaction
object as follows: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.
// 4. Submitconst committedTransaction = await aptos.transaction.submit.simple({transaction,senderAuthenticator: senderAuthenticator,feePayerAuthenticator: feePayerAuthenticator,}); -
Wait for the transaction to execute.
// 5. Wait for resultsconst executedTransaction = await aptos.waitForTransaction({ transactionHash: committedTransaction.hash });
TypeScript Sponsor Transaction Code Sample
Section titled “TypeScript Sponsor Transaction Code Sample”/** * 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
Section titled “Common Errors”INSUFFICIENT_BALANCE_FOR_TRANSACTION_FEE
:
- This may be caused by accidentally using
.sign
instead of.signAsFeePayer
when signing the transaction before submitting on-chain. - 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 thegas_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.