Skip to content

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.

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::MultiAgentWithFeePayer structure
    • 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.

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.

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: