⚠️Blast API (blastapi.io) ends Oct 31. Migrate to Dwellir and skip Alchemy's expensive compute units.
Switch Today →
Skip to main content

Sponsored Transactions

Sponsored transactions enable gas fee delegation on Aptos, allowing one account (the sponsor) to pay transaction fees for another account (the user). This feature is crucial for onboarding new users, providing gasless experiences, and building accessible dApps that don't require users to hold native tokens before interacting with applications.

Overview#

Traditional blockchain interactions require users to hold native tokens (APT on Aptos) to pay gas fees, creating a significant onboarding barrier. Sponsored transactions separate the transaction sender from the fee payer, enabling applications, DAOs, or services to subsidize user transactions while maintaining proper authentication and authorization.

How Sponsored Transactions Work#

A sponsored transaction involves three parties:

  1. User (Sender): Signs the transaction to authorize their operations
  2. Sponsor (Fee Payer): Pays gas fees and signs to authorize payment
  3. Network: Validates both signatures and executes the transaction
import { Aptos, Account } from "@aptos-labs/ts-sdk";

const aptos = new Aptos();

// User who wants to perform an operation but has no APT
const user = Account.generate();

// Sponsor who will pay the gas fees
const sponsor = Account.generate(); // Must have APT

// Build sponsored transaction
const transaction = await aptos.transaction.build.simple({
sender: user.accountAddress,
data: {
function: "0x1::aptos_account::transfer",
functionArguments: [recipientAddress, 1000]
},
withFeePayer: true
});

// User signs their part
const userAuth = aptos.transaction.sign({
signer: user,
transaction
});

// Sponsor signs to pay fees
const sponsorAuth = aptos.transaction.signAsFeePayer({
signer: sponsor,
transaction
});

// Submit with both signatures
const committedTxn = await aptos.transaction.submit.simple({
transaction,
senderAuthenticator: userAuth,
feePayerAuthenticator: sponsorAuth
});

await aptos.waitForTransaction({ transactionHash: committedTxn.hash });

Move Implementation#

module 0x1::sponsored_service {
use std::signer;
use aptos_framework::coin;
use aptos_framework::aptos_coin::AptosCoin;

struct SponsorshipConfig has key {
enabled: bool,
daily_limit: u64,
per_user_limit: u64
}

struct UserQuota has key {
used_today: u64,
last_reset: u64
}

// User function that can be sponsored
public entry fun perform_action(user: &signer, data: vector<u8>) {
// User's actual operation
// Gas will be paid by sponsor, not user
}

// Sponsor checks if they should sponsor this user
public entry fun check_sponsorship_eligibility(
user: address,
estimated_gas: u64
): bool acquires UserQuota, SponsorshipConfig {
let config = borrow_global<SponsorshipConfig>(@sponsor);
if (!config.enabled) return false;

if (!exists<UserQuota>(user)) return true;

let quota = borrow_global<UserQuota>(user);
quota.used_today + estimated_gas <= config.per_user_limit
}
}

Real-World Use Cases#

  1. User Onboarding: Let new users interact with your dApp immediately without requiring them to acquire APT first, dramatically reducing onboarding friction.

  2. Gaming Applications: Sponsor in-game transactions so players can play without worrying about gas fees, creating a seamless gaming experience.

  3. Social Media dApps: Enable users to post, like, and interact on blockchain social platforms without paying gas for each action.

  4. Enterprise Applications: Corporations can sponsor transactions for their employees or customers, internalizing blockchain interaction costs.

  5. Loyalty Programs: Reward loyal users by sponsoring their transactions as a benefit, similar to traditional fee waivers.

  6. Micropayments: Enable micro-transaction use cases where gas fees would otherwise be prohibitively expensive relative to transaction value.

Best Practices#

Implement Rate Limiting: Protect sponsors from abuse by implementing per-user quotas, daily limits, and velocity checks.

Verify User Intent: Ensure the sponsored transaction represents genuine user intent and hasn't been manipulated.

Gas Estimation: Accurately estimate gas costs before sponsoring to prevent unexpected costs and set appropriate budgets.

Conditional Sponsorship: Only sponsor transactions that meet specific criteria (new users, specific functions, within limits).

Monitor Costs: Track total sponsorship costs and set alerts for unusual patterns or excessive spending.

Graceful Degradation: Have fallback options if sponsorship limits are reached so users can still complete transactions.

Transparent Terms: Clearly communicate sponsorship terms, limits, and conditions to users.

Advanced Sponsorship Patterns#

Conditional Sponsorship#

async function conditionalSponsor(
user: Account,
transaction: any,
sponsor: Account
): Promise<boolean> {
// Check if user qualifies for sponsorship
const userTxCount = await getUserTransactionCount(user.accountAddress);
const isNewUser = userTxCount < 10;

if (isNewUser) {
// Sponsor new users
return true;
}

// Check if user has loyalty points
const loyaltyPoints = await getLoyaltyPoints(user.accountAddress);
if (loyaltyPoints > 100) {
// Sponsor loyal users
return true;
}

return false; // User must pay own fees
}

Tiered Sponsorship#

struct TieredSponsorship has key {
bronze_limit: u64, // Sponsor up to X gas
silver_limit: u64, // Sponsor up to Y gas
gold_limit: u64, // Sponsor up to Z gas
}

public fun get_user_tier(user: address): u8 {
// Determine user tier based on activity, holdings, etc.
1 // Bronze
}

Pooled Sponsorship#

struct SponsorshipPool has key {
contributors: vector<address>,
total_funds: u64,
used_funds: u64,
}

public fun contribute_to_pool(contributor: &signer, amount: u64) {
// Add funds to sponsorship pool
// Multiple parties share sponsorship costs
}

Cost Management#

// Track sponsorship costs
interface SponsorshipMetrics {
totalSponsored: number;
transactionsSponsored: number;
averageCostPerTx: number;
dailyBudget: number;
remainingBudget: number;
}

async function checkBudget(sponsor: Account): Promise<boolean> {
const metrics = await getSponsorshipMetrics(sponsor.accountAddress);
return metrics.remainingBudget > metrics.averageCostPerTx;
}

Security Considerations#

Sybil Resistance: Implement mechanisms to prevent users from creating multiple accounts to exploit sponsorship limits.

Validation Before Signing: Sponsors must validate transaction contents before signing to prevent sponsoring malicious operations.

Budget Controls: Set strict budget limits and alerts to prevent sponsorship pool depletion.

Abuse Prevention: Monitor for unusual patterns that might indicate coordinated abuse of sponsorship systems.

Emergency Shutdown: Implement ability to quickly disable sponsorship if abuse is detected.

Monitoring and Analytics#

// Track sponsorship metrics
async function trackSponsorship(txHash: string, sponsor: address, user: address, gasPaid: number) {
await database.insert({
timestamp: Date.now(),
txHash,
sponsor,
user,
gasPaid,
type: 'sponsored_transaction'
});

await updateDailyMetrics(sponsor);
await checkAbusePatterns(user);
}