Skip to main content

Overview

SVM Gas Sponsorship is Dynamic’s built-in feature that automatically sponsors Solana (SVM) transaction fees for your users. When enabled, Dynamic handles all the complexity of fee sponsorship behind the scenes, allowing your users to transact without needing SOL for gas fees. When enabled, Dynamic automatically replaces the fee payer in Solana transactions with a sponsored account, so your users don’t need SOL for gas.
SVM Gas Sponsorship is available exclusively for V3 MPC embedded wallets. It works automatically once enabled - no code changes required.

Prerequisites

Enabling SVM Gas Sponsorship

  1. Go to the Dynamic Dashboard
  2. Navigate to Settings > Embedded Wallets
  3. Ensure Solana (SOL) is enabled in your chain configurations
  4. Toggle on SVM Gas Sponsorship
The SVM Gas Sponsorship toggle only appears when Solana is enabled in your chain configurations and you’re using V3 MPC wallets.

How it works

Once enabled, gas sponsorship is applied automatically to all transactions from your users’ embedded wallets. Your code remains unchanged:
using DynamicSDK.Core;
using Solana.Unity.Programs;
using Solana.Unity.Rpc.Models;
using Solana.Unity.Wallet;
using System.Collections.Generic;

public async void SendSOLGasless(BaseWallet wallet, string recipient, double amountInSOL)
{
    var solana = DynamicSDK.Instance.Networks.Solana;
    var rpcClient = solana.CreateConnection();

    // Get recent blockhash
    var blockHashResult = await rpcClient.GetLatestBlockHashAsync();
    var blockHash = blockHashResult.Result.Value.Blockhash;

    var fromKey = new PublicKey(wallet.Address);
    var toKey = new PublicKey(recipient);
    var lamports = (ulong)(amountInSOL * 1_000_000_000);

    // Create transaction - sponsorship is handled automatically
    var tx = new VersionedTransaction
    {
        RecentBlockHash = blockHash,
        FeePayer = fromKey,
        Instructions = new List<TransactionInstruction>
        {
            SystemProgram.Transfer(fromKey, toKey, lamports)
        },
        AddressTableLookups = new List<MessageAddressTableLookup>()
    };

    var message = tx.CompileMessage();
    var txBytes = new byte[1 + 64 + message.Length];
    txBytes[0] = 1;
    Buffer.BlockCopy(message, 0, txBytes, 65, message.Length);

    // Sign and send - gas fees are sponsored automatically
    string txHash = await solana.SignAndSendTransaction(
        wallet.Id, txBytes, SolanaTransactionType.Versioned);

    Debug.Log($"Transaction sent: {txHash}");
}

Transaction flow

When your application sends a transaction:
  1. Transaction Creation: Your app creates a standard Solana transaction
  2. Automatic Sponsorship: The SDK intercepts the transaction before signing
  3. Backend Processing: The transaction is sent to Dynamic’s backend
  4. Sponsorship Processing: Dynamic sponsors the transaction
  5. Fee Payer Replacement: The transaction’s fee payer is replaced with Dynamic’s sponsored account
  6. User Signing: The sponsored transaction is returned for the user to sign
  7. Broadcast: The fully signed transaction is sent to the Solana network

Limitations

Wallet requirements

  • Embedded wallets only: Sponsorship only works with Dynamic’s MPC embedded wallets
  • V3 wallets required: Must be using V3 MPC wallet configuration
  • Not for external wallets: External wallets (Phantom, Solflare, etc.) are not supported

Transaction constraints

  • Transaction size: Maximum 2KB for the base64-encoded transaction
  • Already-signed transactions: Transactions that are already signed will not be sponsored
  • Single transaction: Each transaction is sponsored individually (no batching)

Fallback behavior

If sponsorship fails, the SDK will fall back to using the original transaction, which requires the user to have SOL for gas fees.
When sponsorship fails, the transaction will proceed without sponsorship. Ensure your UX handles cases where users may need SOL for gas fees as a fallback.

Error handling

using DynamicSDK.Core;
using UnityEngine;
using System;

public enum SolanaTransactionResult
{
    Success,
    InsufficientBalance,
    SponsorshipFailed,
    Error
}

public class TransactionResponse
{
    public SolanaTransactionResult Result { get; set; }
    public string Signature { get; set; }
    public string ErrorMessage { get; set; }
}

public async void SendTransactionWithFallback(
    BaseWallet wallet,
    string recipient,
    double amountInSOL,
    Action<TransactionResponse> callback)
{
    try
    {
        // Attempt transaction - sponsorship applied automatically if enabled
        var solana = DynamicSDK.Instance.Networks.Solana;
        var rpcClient = solana.CreateConnection();

        var blockHashResult = await rpcClient.GetLatestBlockHashAsync();
        var blockHash = blockHashResult.Result.Value.Blockhash;

        var fromKey = new PublicKey(wallet.Address);
        var toKey = new PublicKey(recipient);
        var lamports = (ulong)(amountInSOL * 1_000_000_000);

        var tx = new VersionedTransaction
        {
            RecentBlockHash = blockHash,
            FeePayer = fromKey,
            Instructions = new List<TransactionInstruction>
            {
                SystemProgram.Transfer(fromKey, toKey, lamports)
            },
            AddressTableLookups = new List<MessageAddressTableLookup>()
        };

        var message = tx.CompileMessage();
        var txBytes = new byte[1 + 64 + message.Length];
        txBytes[0] = 1;
        Buffer.BlockCopy(message, 0, txBytes, 65, message.Length);

        string signature = await solana.SignAndSendTransaction(
            wallet.Id, txBytes, SolanaTransactionType.Versioned);

        callback(new TransactionResponse
        {
            Result = SolanaTransactionResult.Success,
            Signature = signature
        });
    }
    catch (Exception e)
    {
        var errorMessage = e.Message?.ToLower() ?? "";

        if (errorMessage.Contains("insufficient") || errorMessage.Contains("balance"))
        {
            callback(new TransactionResponse
            {
                Result = SolanaTransactionResult.InsufficientBalance,
                ErrorMessage = "Insufficient balance for transaction"
            });
        }
        else if (errorMessage.Contains("sponsor"))
        {
            callback(new TransactionResponse
            {
                Result = SolanaTransactionResult.SponsorshipFailed,
                ErrorMessage = "Gas sponsorship failed, SOL required for fees"
            });
        }
        else
        {
            callback(new TransactionResponse
            {
                Result = SolanaTransactionResult.Error,
                ErrorMessage = e.Message
            });
        }
    }
}

Best practices

  1. Don’t assume sponsorship: Build your UI to handle cases where sponsorship might not be available
  2. Show transaction status: Provide feedback during the sponsorship and signing process
  3. Test in Sandbox first: Verify sponsorship works in your Sandbox environment before going live
  4. Monitor usage: Keep an eye on sponsorship usage in your dashboard

Next steps