Join our community of builders on

Telegram!Telegram

Stellar Sponsored Transactions Guide

Overview

Stellar Sponsored Transactions (also known as gasless transactions) allow users to pay transaction fees using tokens instead of the native XLM currency. This enables a better user experience where users can pay fees in stablecoins like USDC or other tokens they already hold, without needing to maintain XLM balances.

How it works:

  1. Quote: Estimate the fee cost in your preferred token
  2. Build: Prepare a transaction that includes fee payment in the token
  3. Sign: User signs the transaction including fee payment
  4. Send: Submit the transaction (relayer pays XLM fees, user pays token fees)

The relayer handles the complexity of fee conversion, token swaps, and XLM fee payment, while users simply pay in their preferred token.

Prerequisites

  • A running OpenZeppelin Relayer instance
  • A Stellar relayer configured with sponsored transaction support ("fee_payment_strategy" = "user")
  • Trustlines enabled for the fee tokens you want to accept
  • Sufficient XLM balance in the relayer account for network fees

Configuration

1. Basic Relayer Setup

First, configure a Stellar relayer in your config.json:

{
  "relayers": [
    {
      "id": "stellar-sponsored",
      "name": "Stellar Sponsored Transactions",
      "network": "testnet",
      "paused": false,
      "signer_id": "local-signer",
      "network_type": "stellar",
      "policies": {
        "fee_payment_strategy": "user",
        "min_balance": 100000000,
        "max_fee": 100000,
        "allowed_tokens": [
          {
            "asset": "USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5",
            "max_allowed_fee": 1000000000,
            "swap_config": {
              "slippage_percentage": 1.0,
              "min_amount": 1000000,
              "max_amount": 10000000000,
              "retain_min_amount": 5000000
            }
          }
        ],
        "slippage_percentage": 1.0,
        "fee_margin_percentage": 10.0,
        "swap_config": {
          "strategies": ["order-book"],
          "cron_schedule": "0 */6 * * *",
          "min_balance_threshold": 50000000
        }
      }
    }
  ]
}

2. Policy Configuration Explained

fee_payment_strategy

  • relayer: Relayer pays all network fees. Users pay fees in tokens.
  • user: User must include fee payment in the transaction.

allowed_tokens

List of tokens that can be used for fee payment. Each token can have:

  • asset: Asset identifier in format "native" or "CODE:ISSUER" (e.g., "USDC:GA5Z...")
  • max_allowed_fee: Maximum fee amount in token's smallest unit (stroops for native, token decimals for others)
  • swap_config: Token-specific swap configuration:
    • slippage_percentage: Maximum acceptable slippage (default: 1.0%)
    • min_amount: Minimum swap amount
    • max_amount: Maximum swap amount
    • retain_min_amount: Minimum amount to retain after swap

swap_config (Global)

Configuration for converting collected tokens back to XLM:

  • strategies: DEX strategies to use (currently supports order-book)
  • cron_schedule: Schedule for automatic token swaps (e.g., "0 */6 * * *" = every 6 hours)
  • min_balance_threshold: Minimum XLM balance (in stroops) before triggering swaps

slippage_percentage

Default slippage tolerance for token conversions (default: 1.0%)

fee_margin_percentage

Additional fee margin added to estimated fees to account for price fluctuations (default: 10.0%)

3. Enabling Trustlines

Before users can pay fees in tokens, the relayer account must establish trustlines to those tokens. This is a one-time setup per token.

For a complete example of how to create trustlines, see the Relayer SDK example.

Using the API

The sponsored transaction flow consists of four steps: Quote, Build, Sign, and Send.

Step 1: Get Fee Quote

Estimate the transaction fee in your preferred token.

Endpoint: POST /api/v1/relayers/{relayer_id}/transactions/sponsored/quote

Request Body:

{
  "transaction_xdr": "AAAAAgAAAAD...",
  "fee_token": "USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN"
}

Or with operations:

{
  "operations": [
    {
      "type": "payment",
      "destination": "GBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
      "amount": 1000000,
      "asset": "native"
    }
  ],
  "source_account": "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF",
  "fee_token": "USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4K4KZVN"
}

Response:

{
  "success": true,
  "data": {
    "fee_in_token_ui": "1.5",
    "fee_in_token": "15000000",
    "conversion_rate": "0.15"
  }
}

For complete code examples, see the Relayer SDK examples.

Step 2: Build Sponsored Transaction

Prepare a transaction that includes fee payment in the specified token.

Endpoint: POST /api/v1/relayers/{relayer_id}/transactions/sponsored/build

Request Body:

{
  "transaction_xdr": "AAAAAgAAAAD...",
  "fee_token": "USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN"
}

Or with operations:

{
  "operations": [
    {
      "type": "payment",
      "destination": "GBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
      "amount": 1000000,
      "asset": "native"
    }
  ],
  "source_account": "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF",
  "fee_token": "USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN"
}

Response:

{
  "success": true,
  "data": {
    "transaction": "AAAAAgAAAAD...",
    "fee_in_token_ui": "1.5",
    "fee_in_token": "15000000",
    "fee_in_stroops": "100000",
    "fee_token": "USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN",
    "valid_until": "2024-01-01T00:01:00Z"
  }
}

For complete code examples, see the Relayer SDK examples.

Step 3: Sign Transaction

The user must sign the transaction XDR returned from the Build endpoint. The transaction includes fee payment operations and is ready for signing. The signing method depends on your wallet setup and relayer configuration:

Option 1: Manual Signing with xBull Wallet

If using the xBull wallet, the transaction can be manually signed:

  1. Open xBull wallet
  2. Navigate to Drawer menu → Lab → Import XDR
  3. Paste the transaction XDR from the Build response
  4. Review and sign the transaction

Note: Other Stellar wallets may support similar XDR import and signing flows. Consult your wallet's documentation for specific instructions.

Option 2: Programmatic Signing via Relayer

If the user's address is connected with another relayer configured in relayer fee payment mode, you can use the Sign Transaction endpoint:

Endpoint: POST /api/v1/relayers/{relayer_id}/sign-transaction


#### Option 3: Custom Signing Solution

You can also use any Stellar SDK or signing library to sign the transaction programmatically with the user's private key.

For complete code examples, see the [Relayer SDK examples](https://github.com/OpenZeppelin/openzeppelin-relayer-sdk/tree/main/examples/relayers/stellar/src/sponsored).

### Step 4: Submit to Relayer

Submit the signed transaction to the relayer. The relayer will:
1. Process it as a fee-bump transaction (wrap it in a fee-bump envelope)
2. Sign the fee-bump transaction with the relayer's key
3. Submit it to the Stellar network

**Endpoint:** `POST /api/v1/relayers/{relayer_id}/transactions`

**Request Body:**

```json
{
  "transaction_xdr": "AAAAAgAAAAD...",
  "network": "testnet",
  "fee_bump": true
}

Response:

{
  "success": true,
  "data": {
    "id": "tx-123456",
    "status": "pending",
    "network_data": {
      "signature": "abc123...",
      "transaction": "AAAAAgAAAAD..."
    }
  }
}

For complete code examples, see the Relayer SDK examples.

Request/Response Formats

Fee Token Format

Fee tokens are specified using Stellar asset identifiers:

  • Native XLM: "native"
  • Token: "CODE:ISSUER" (e.g., "USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN")

Operations Format

When building transactions from operations, use the following format:

{
  "operations": [
    {
      "type": "payment",
      "destination": "G...",
      "amount": 1000000,
      "asset": "native"
    },
    {
      "type": "payment",
      "destination": "G...",
      "amount": 5000000,
      "asset": "USDC:GA5Z..."
    }
  ]
}

Supported operation types:

  • payment: Standard payment operation
  • createAccount: Account creation
  • changeTrust: Trustline management
  • manageData: Data entry management
  • bumpSequence: Sequence number bump
  • setOptions: Account options
  • allowTrust: Trustline authorization
  • accountMerge: Account merge
  • manageBuyOffer: Create buy offer
  • manageSellOffer: Create sell offer
  • createPassiveSellOffer: Create passive offer
  • pathPaymentStrictSend: Path payment strict send
  • pathPaymentStrictReceive: Path payment strict receive
  • createClaimableBalance: Create claimable balance
  • claimClaimableBalance: Claim claimable balance
  • beginSponsoringFutureReserves: Begin sponsoring reserves
  • endSponsoringFutureReserves: End sponsoring reserves
  • revokeSponsorship: Revoke sponsorship
  • clawback: Clawback operation
  • clawbackClaimableBalance: Clawback claimable balance
  • setTrustLineFlags: Set trustline flags
  • liquidityPoolDeposit: Deposit to liquidity pool
  • liquidityPoolWithdraw: Withdraw from liquidity pool
  • invokeHostFunction: Soroban contract invocation
  • extendFootprintTtl: Extend footprint TTL
  • restoreFootprint: Restore footprint

Best Practices

  1. Always Quote First: Get a fee quote before building to show users the expected cost
  2. Handle Expiration: Transactions expire after 1 minute. Rebuild if needed
  3. Monitor Relayer Balance: Ensure the relayer has sufficient XLM for network fees
  4. Configure Swap Schedule: Set up automatic token swaps to maintain XLM balance
  5. Set Appropriate Limits: Configure max_allowed_fee to prevent excessive fees
  6. Use Fee Margins: The fee_margin_percentage helps account for price fluctuations
  7. Monitor Trustlines: Ensure trustlines are established before users attempt transactions

Soroban Gas Abstraction

Overview

Soroban Gas Abstraction extends the sponsored transaction concept to Soroban smart contracts. Instead of requiring users to hold XLM for contract invocation fees, users can pay in any Soroban token (e.g., a USDC Soroban token contract). This is powered by a FeeForwarder smart contract that wraps the user's contract call with fee collection logic.

How it differs from Classic Stellar Sponsored Transactions:

AspectClassic StellarSoroban Gas Abstraction
Transaction typeClassic operations (payments, trustlines, etc.)Soroban InvokeHostFunction operations
Fee mechanismFee-bump transaction wrapperFeeForwarder smart contract
Fee token formatCredit assets (CODE:ISSUER)Soroban token contracts (C... address)
AuthorizationStandard transaction signingUser signs a SorobanAuthorizationEntry
SubmissionRelayer wraps in fee-bump envelopeRelayer injects signed auth entries and submits directly

Prerequisites

  • A running OpenZeppelin Relayer instance
  • A Stellar relayer configured with "fee_payment_strategy": "user"
  • A deployed FeeForwarder contract on the target network. Check the OpenZeppelin contract here
  • The STELLAR_TESTNET_FEE_FORWARDER_ADDRESS or STELLAR_MAINNET_FEE_FORWARDER_ADDRESS environment variable set to the FeeForwarder contract address
  • Allowed tokens configured with Soroban token contract addresses (C... format)
  • Sufficient XLM balance in the relayer account for network fees

Configuration

Configure the relayer with Soroban token contracts in the allowed_tokens list. Note that Soroban tokens use contract addresses (C... format) rather than the CODE:ISSUER format used for classic Stellar assets.

{
  "relayers": [
    {
      "id": "stellar-soroban-gas",
      "name": "Stellar Soroban Gas Abstraction",
      "network": "testnet",
      "paused": false,
      "signer_id": "local-signer",
      "network_type": "stellar",
      "policies": {
        "fee_payment_strategy": "user",
        "fee_margin_percentage": 5,
        "allowed_tokens": [
          {
            "asset": "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC",
            "max_allowed_fee": 1000000000,
            "swap_config": {
              "slippage_percentage": 0.5,
              "min_amount": 10000000,
              "max_amount": 1000000000,
              "retain_min_amount": 10000000
            }
          }
        ],
        "swap_config": {
          "strategies": ["soroswap"],
          "cron_schedule": "0 */6 * * *",
          "min_balance_threshold": 50000000
        }
      }
    }
  ]
}

Set the required environment variables:

For testnet, you must provide all environment variables. For mainnet, the code already contains default addresses, but you can override them using environment variables.

# Stellar FeeForwarder Contract Addresses
# STELLAR_MAINNET_FEE_FORWARDER_ADDRESS=
STELLAR_TESTNET_FEE_FORWARDER_ADDRESS=

# Stellar Soroswap Router Contract Addresses
STELLAR_MAINNET_SOROSWAP_ROUTER_ADDRESS=
STELLAR_TESTNET_SOROSWAP_ROUTER_ADDRESS=

# Stellar Soroswap Factory Contract Addresses
STELLAR_MAINNET_SOROSWAP_FACTORY_ADDRESS=
STELLAR_TESTNET_SOROSWAP_FACTORY_ADDRESS=

# Stellar Soroswap Native Wrapper (XLM) Contract Addresses
STELLAR_MAINNET_SOROSWAP_NATIVE_WRAPPER_ADDRESS=
STELLAR_TESTNET_SOROSWAP_NATIVE_WRAPPER_ADDRESS=

Using the API

The Soroban gas abstraction flow uses the same endpoints as classic sponsored transactions but with key differences in the request/response payloads and an additional authorization signing step.

Step 1: Get Fee Quote

Estimate the fee for a Soroban contract invocation in your preferred token.

Endpoint: POST /api/v1/relayers/{relayer_id}/transactions/sponsored/quote

Request Body:

The transaction_xdr should contain the Soroban InvokeHostFunction operation, and the fee_token should be a Soroban token contract address (C... format).

{
  "transaction_xdr": "AAAAAgAAAAD...",
  "fee_token": "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC"
}

Response:

The response includes max_fee_in_token and max_fee_in_token_ui fields that account for slippage buffer. These represent the maximum amount the user authorizes.

{
  "success": true,
  "data": {
    "fee_in_token_ui": "1.5",
    "fee_in_token": "15000000",
    "conversion_rate": "0.15",
    "max_fee_in_token": "15750000",
    "max_fee_in_token_ui": "1.575"
  }
}

Step 2: Build Sponsored Transaction

Prepare a Soroban transaction that wraps your contract call with the FeeForwarder contract.

Endpoint: POST /api/v1/relayers/{relayer_id}/transactions/sponsored/build

Request Body:

{
  "transaction_xdr": "AAAAAgAAAAD...",
  "fee_token": "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC"
}

Response:

The response includes a user_auth_entry field containing the Soroban authorization entry that the user must sign. This authorization entry authorizes the FeeForwarder contract to collect fees in the specified token on the user's behalf.

{
  "success": true,
  "data": {
    "transaction": "AAAAAgAAAAD...",
    "fee_in_token_ui": "1.5",
    "fee_in_token": "15000000",
    "fee_in_stroops": "100000",
    "fee_token": "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC",
    "valid_until": "2024-01-01T00:01:00Z",
    "user_auth_entry": "AAAABgAAAAA...",
    "max_fee_in_token": "15750000",
    "max_fee_in_token_ui": "1.575"
  }
}

Step 3: Sign the Authorization Entry

Unlike classic sponsored transactions where the user signs the full transaction XDR, for Soroban gas abstraction the user must sign the user_auth_entry returned from the Build response. This is a SorobanAuthorizationEntry XDR that authorizes:

  1. The FeeForwarder contract to transfer fee tokens from the user to the relayer
  2. The user's original contract invocation (wrapped inside the FeeForwarder call)

The signing method depends on your setup:

  • Programmatic signing: Use the Stellar SDK to sign the authorization entry with the user's private key
  • Wallet signing: Use a Soroban-compatible wallet that supports signing authorization entries
  • CLI helper tool: A ready-to-use signing tool is provided in the repository at helpers/sign_soroban_auth_entry.rs
Using the CLI Helper Tool

The repository includes a helper tool that signs Soroban authorization entries directly from the command line. This is useful for testing and development:

cargo run --example sign_soroban_auth_entry -- \
  --secret-key "S..." \
  --auth-entry "<base64 XDR from build response>" \
  --network testnet

To generate the full JSON payload ready for the /transactions endpoint, include the --transaction-xdr flag:

cargo run --example sign_soroban_auth_entry -- \
  --secret-key "S..." \
  --auth-entry "<base64 XDR from build response>" \
  --network testnet \
  --transaction-xdr "<transaction XDR from build response>"

This outputs a JSON object with network, transaction_xdr, and signed_auth_entry fields that can be sent directly to the Submit endpoint.

For complete code examples of signing authorization entries and testing the full gas abstraction flow (quote, build, sign, and send), see the Relayer SDK examples. The example demonstrates the end-to-end flow including fee estimation, transaction building, authorization signing, and submission.

Step 4: Submit to Relayer

Submit the transaction XDR from the Build response along with the user's signed authorization entry. The relayer will:

  1. Inject the user's signed authorization entry into the transaction
  2. Add the relayer's own authorization entry (as the transaction source account)
  3. Re-simulate the transaction with the signed auth entries for accurate resource calculation
  4. Apply a resource buffer (15% safety margin) to prevent execution failures
  5. Sign the transaction with the relayer's key
  6. Submit it to the Stellar network

Endpoint: POST /api/v1/relayers/{relayer_id}/transactions

Request Body:

{
  "transaction_xdr": "AAAAAgAAAAD...",
  "network": "testnet",
  "signed_auth_entry": "AAAABgAAAAA..."
}

Note: The signed_auth_entry field is used instead of fee_bump for Soroban gas abstraction. These two fields are mutually exclusive.

Response:

{
  "success": true,
  "data": {
    "id": "tx-123456",
    "status": "pending",
    "network_data": {
      "signature": "abc123...",
      "transaction": "AAAAAgAAAAD..."
    }
  }
}

Fee Token Format

For Soroban gas abstraction, fee tokens are specified using Soroban contract addresses:

  • Soroban token: "C..." (e.g., "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC")

This differs from classic sponsored transactions which use the "CODE:ISSUER" format. The relayer automatically detects whether to use the classic or Soroban flow based on the fee_token format and the presence of InvokeHostFunction operations in the transaction XDR.

How the FeeForwarder Contract Works

The FeeForwarder contract acts as a proxy that:

  1. Collects the fee: Transfers the fee token amount from the user to the relayer
  2. Executes the user's call: Invokes the target contract function on behalf of the user
  3. Manages authorization: Bundles fee approval and contract invocation into a single authorization entry

The contract's forward() function takes these parameters:

  • fee_token - Soroban token contract address for fee payment
  • fee_amount - Actual fee to charge
  • max_fee_amount - Maximum fee authorized by the user (includes slippage buffer)
  • expiration_ledger - Ledger number when the authorization expires
  • target_contract - The contract the user wants to invoke
  • target_fn - The function name to call
  • target_args - The function arguments
  • user - The user's account address
  • relayer - The relayer's account address (fee recipient)

Best Practices for Soroban Gas Abstraction

  1. Use the Quote endpoint first: Always get a fee estimate before building to show users the expected cost, including the max fee with slippage
  2. Handle authorization expiration: Authorization entries expire at a specific ledger. Rebuild if the authorization has expired
  3. Validate token contract addresses: Ensure fee tokens use valid Soroban contract addresses (C... format, 56 characters)
  4. Monitor resource fees: Soroban transactions have dynamic resource fees based on CPU instructions, storage reads/writes, and ledger entry access. The relayer applies a 15% buffer, but complex contract calls may need higher limits
  5. Set appropriate max fees: Configure max_allowed_fee per token to prevent excessive fees during network congestion
  6. Configure the FeeForwarder address: Ensure the correct FeeForwarder contract address is set via environment variables for each network

Additional Resources