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:
import com.dynamic.sdk.android.DynamicSDK
import com.dynamic.sdk.android.Models.BaseWallet
import com.solanaweb3.*
import org.sol4k.PublicKey

val sdk = DynamicSDK.getInstance()

suspend fun sendSOLGasless(
    wallet: BaseWallet,
    recipient: String,
    amountInSOL: Double
): String {
    val connection = Connection(Cluster.DEVNET)
    val fromPubkey = PublicKey(wallet.address)
    val toPubkey = PublicKey(recipient)
    val lamports = (amountInSOL * 1_000_000_000).toLong()
    val blockhash = connection.getLatestBlockhash()

    // Create transaction - sponsorship is handled automatically
    val instruction = SystemProgram.transfer(fromPubkey, toPubkey, lamports)
    val transaction = Transaction.v0(fromPubkey, listOf(instruction), blockhash.blockhash)
    val base64Transaction = transaction.serializeUnsignedToBase64()

    // Sign and send - gas fees are sponsored automatically
    return sdk.solana.signAndSendTransaction(base64Transaction, wallet)
}

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.

Checking Sponsorship Status

You can check if gas sponsorship is enabled in your project settings:
val sdk = DynamicSDK.getInstance()

fun checkSponsorshipEnabled(): Boolean {
    return sdk.projectSettings?.sdk?.embeddedWallets?.svmGasSponsorshipEnabled ?: false
}

Error Handling

sealed class SolanaTransactionResult {
    data class Success(val signature: String) : SolanaTransactionResult()
    data class InsufficientBalance(val message: String) : SolanaTransactionResult()
    data class SponsorshipFailed(val message: String) : SolanaTransactionResult()
    data class Error(val message: String) : SolanaTransactionResult()
}

suspend fun sendTransactionWithFallback(
    wallet: BaseWallet,
    recipient: String,
    amountInSOL: Double
): SolanaTransactionResult {
    return try {
        // Attempt transaction - sponsorship applied automatically if enabled
        val signature = sendSOLGasless(wallet, recipient, amountInSOL)
        SolanaTransactionResult.Success(signature)
    } catch (e: Exception) {
        val errorMessage = e.message?.lowercase() ?: ""

        when {
            errorMessage.contains("insufficient") || errorMessage.contains("balance") ->
                SolanaTransactionResult.InsufficientBalance(
                    "Insufficient balance for transaction"
                )
            errorMessage.contains("sponsor") ->
                SolanaTransactionResult.SponsorshipFailed(
                    "Gas sponsorship failed, SOL required for fees"
                )
            else ->
                SolanaTransactionResult.Error(e.message ?: "Unknown error")
        }
    }
}

// Usage in ViewModel
viewModelScope.launch {
    when (val result = sendTransactionWithFallback(wallet, recipient, amount)) {
        is SolanaTransactionResult.Success -> {
            _transactionSignature.value = result.signature
        }
        is SolanaTransactionResult.InsufficientBalance -> {
            _errorMessage.value = result.message
        }
        is SolanaTransactionResult.SponsorshipFailed -> {
            _errorMessage.value = result.message
        }
        is SolanaTransactionResult.Error -> {
            _errorMessage.value = result.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

What’s Next