githubEdit

Sponsored Transactions (Fee Bump Tx)

⏱️ 4 min read

Introduction

Sponsored transactions allow a sponsor account to pay Stellar transaction fees on behalf of another user. This is essential when working with smart accounts (Soroban contract-based accounts) that cannot pay fees themselves. Using the fee-bump pattern, you can build seamless user experiences where end users never need to hold XLM for gas.

This guide walks you through implementing fee-bump deposits and withdrawals with the DeFindex API.

Why Are Sponsored Transactions Needed?

Smart Accounts and Transaction Fees

On Stellar, there are two types of accounts:

  • Native accounts (G... addresses) — Standard Stellar accounts that hold XLM and can pay transaction fees. They also serve as the source account (sequence number provider) for transactions.

  • Smart accounts (C... addresses) — Soroban contract-based accounts. These accounts cannot be the source account of a transaction and cannot pay transaction fees because Stellar requires fees and sequence numbers from a native G... account.

  • Source = always a native G... account (provides the sequence number). In a fee-bump transaction, the sponsor pays the fee, not the source account.

  • Caller (from) = can be G... or C... (the account that authorizes the vault operation)

When a DeFindex vault is operated by a smart account, the transaction will fail if no one covers the fee.

The Fee-Bump Solution

Stellar's fee-bump transaction wraps an existing (inner) transaction with an outer envelope that specifies a different fee-paying account:

┌──────────────────────────────────────┐
│  Fee-Bump Transaction (outer)        │
│  Fee Source: Sponsor (G...)          │
│                                      │
│  ┌──────────────────────────────┐    │
│  │  Inner Transaction           │    │
│  │  Source: Native account (G..)│    │
│  │  Caller/From: C... or G...   │    │
│  │  Operations: deposit/        │    │
│  │    withdraw, etc.            │    │
│  │  Signed by: Caller           │    │
│  └──────────────────────────────┘    │
│                                      │
│  Signed by: Sponsor                  │
└──────────────────────────────────────┘

The inner transaction's source account is always a native G... account (which provides the sequence number). The caller signs the inner transaction to authorize the vault operation. The sponsor wraps it in a fee-bump and signs the outer transaction to pay the fee. The network processes both as a single unit.

Prerequisites

  • @stellar/stellar-sdk ^14.3.0

  • Two Stellar keypairs:

    • Sponsor — A native G... account funded with XLM to pay fees

    • Caller — The account executing vault operations (can be G... or C...)

  • DeFindex API key (get it at the API Dashboardarrow-up-right)

Environment Configuration

Create a .env file based on the following template:

Note on CALLER_SECRET and smart accounts: CALLER_SECRET expects a Stellar secret key (S...) for native G... accounts. If the caller is a smart account (C...), authorization comes from wallet interactions (Freighter, xBull, etc.), not a raw private key. In that scenario, present the unsigned XDR to the user's wallet for signing instead of using Keypair.fromSecret(...).

Deposit with Fee Bump

Step 1: Initialize API Client and Keypairs

Step 2: Get Unsigned Deposit Transaction

API Reference: POST /vault/{address}/depositarrow-up-rightDepositDto

Step 3: Sign Inner Transaction with Caller

Step 4: Create and Sign Fee-Bump with Sponsor

Step 5: Submit the Transaction

API Reference: POST /sendarrow-up-rightSendXdrDto

Withdraw with Fee Bump

The withdrawal flow is the same pattern, but first queries the user's vault balance to determine how much of the underlying assets to withdraw.

Step 1: Get Vault Balance

API Reference: GET /vault/{address}/balancearrow-up-right

Step 2: Get Unsigned Withdrawal Transaction

API Reference: POST /vault/{address}/withdrawarrow-up-rightWithdrawDto

Step 3: Sign, Wrap, and Submit

The signing and fee-bump steps are identical to the deposit flow:

Fee Considerations

  • buildFeeBumpTransaction takes a per-operation base fee, not a total fee. The SDK internally multiplies by (numOperations + 1) to compute the total fee-bump fee (CAP-0015 rulearrow-up-right).

  • For a typical Soroban transaction with 1 operation, passing parseInt(transaction.fee) as the base fee produces a total fee-bump fee of 2 × innerFee (since 1 op + 1 = 2). This satisfies the protocol's fee-rate check because the per-operation rate equals the inner transaction's rate.

  • The DeFindex API builds the inner transaction with a simulated resource fee and a correct inclusion fee. Passing parseInt(transaction.fee) as the base fee ensures you meet the required minimum.

  • To increase priority during network congestion, pass a higher base fee:

Common Issues

Issue
Cause
Solution

tx_insufficient_fee

Fee-bump per-operation fee rate is lower than the inner transaction's rate

Ensure the base fee passed to buildFeeBumpTransaction is ≥ parseInt(transaction.fee). The SDK handles the (numOps + 1) multiplication internally

tx_bad_auth

Inner transaction not signed by caller

Ensure transaction.sign(callerKeypair) is called before building the fee-bump

Wrong signing order

Fee-bump created before signing inner tx

Always sign the inner transaction first, then build the fee-bump

429 Too Many Requests

API rate limit exceeded

Implement exponential backoff (see Troubleshooting)

tx_bad_seq

Stale sequence number

Re-fetch the unsigned transaction from the API and retry. Note that Soroban resource estimates (footprint, CPU/memory limits) are also ledger-specific, so simply adjusting the sequence number on a stale transaction is not enough — you must request a fresh XDR

tx_too_late

Transaction timebounds expired before submission

The XDR was held too long. Re-fetch a fresh unsigned transaction from the API and sign/submit promptly

Production Notes

  • Channel accounts for concurrent sponsors: If the sponsor account submits multiple fee-bump transactions in parallel, sequence-number collisions will cause failures. Use channel accountsarrow-up-right. a pool of funded G... accounts — so each concurrent submission uses its own sequence number.

  • XDR verification before signing: In production, the sponsor should decode and inspect the inner transaction XDR before signing. Verify that the operations, amounts, and destination contracts match expected values. Never blindly sign arbitrary XDRs.

Complete Example Repository

A fully working example with deposit and withdraw scripts is available at:

paltalabs/examples/defindex-sdk-deposit-withdraw-fee-bumparrow-up-right

To run it:

Additional Resources

Last updated