Sponsored Transactions
As outlined in AIP-39, sponsored transactions allow one account to pay the fees associated with executing a transaction for another account, essentially setting up a fee payer. Sponsored transactions simplify the process for onboarding users into applications by allowing the application to cover all associated fees for interacting with the Aptos blockchain. Here are two examples:
- MerkleTrade offers low cost trading to those with Ethereum wallets by creating an Aptos wallet for users and covering all transaction fees so that the user does not need to acquire utility tokens for Aptos.
- Community engagement applications like Graffio offered to cover transaction fees for custodial accounts to support the collaborative drawing application for those without wallets.
Process Overview
Section titled “Process Overview”The process for sending a sponsored transaction follows:
- The sender of the transaction determines upon an operation, as defined by
a
RawTransaction. - The sender generates a
RawTransactionWithData::MultiAgentWithFeePayerstructure- Prior to the framework 1.8 release, this must contain the fee payer’s address.
- After framework release 1.8, this can optionally be set to
0x0.
- (Optionally) the sender aggregates signatures from other signers.
- The sender can forward the signed transaction to the fee payer to sign and forward it to the blockchain.
- Upon execution of the transaction, gas fees are deducted from the fee payer and refunds return to the fee payer. Sequence-number transactions also increment the sender’s sequence number; orderless sponsored transactions use a nonce instead.
Alternatively, if the fee payer knows the operation and all signers involved, the fee payer could generate and sign the transaction and send it back to the other signers to sign.
Technical Details
Section titled “Technical Details”In Aptos, a sponsored transaction reuses the same SignedTransaction as any other user transaction:
pub struct SignedTransaction { /// The raw transaction raw_txn: RawTransaction,
/// Public key and signature to authenticate authenticator: TransactionAuthenticator,}The difference is in the TransactionAuthenticator, which stores the
authorization from the fee payer of the transaction to extract utility fees from
their account:
pub enum TransactionAuthenticator {... /// Optional Multi-agent transaction with a fee payer. FeePayer { sender: AccountAuthenticator, secondary_signer_addresses: Vec<AccountAddress>, secondary_signers: Vec<AccountAuthenticator>, fee_payer_address: AccountAddress, fee_payer_signer: AccountAuthenticator, },...}As of framework 1.8 and AIP-115 stateless accounts, the
sender does not need a pre-created 0x1::account::Account resource. The first
transaction that creates that resource still needs enough gas to cover creation
in addition to execution.
During signing of the transaction, all parties sign the following:
pub enum RawTransactionWithData {... MultiAgentWithFeePayer { raw_txn: RawTransaction, secondary_signer_addresses: Vec<AccountAddress>, fee_payer_address: AccountAddress, },}Prior to framework release 1.8, all signers were required to know the actual fee
payer address prior to signing. As of framework release 1.8, signers can
optionally set the address to 0x0 and only the fee payer must sign with their
address set.
SDK Support
Section titled “SDK Support”Build sponsored transactions with withFeePayer: true, then have the sender
.sign and the sponsor .signAsFeePayer. See the
TypeScript SDK sponsoring guide
for a full walkthrough.
Each official SDK documents sponsored transactions and ships a tested example: