Skip to main content
SVM Gas Sponsorship is an enterprise-only feature. Contact us to learn more about upgrading your plan.
On Solana, every transaction needs a small amount of SOL to pay the network fee. Gas sponsorship lets your app cover that fee instead, so users can transact without ever holding any SOL. This is one of the most common ways to remove friction for people new to crypto. Dynamic has gas sponsorship built in. For the basic case you don’t need to change any code — flip a switch in the dashboard and your existing Solana transactions get sponsored automatically.
SVM Gas Sponsorship works only with V3 MPC embedded wallets (the wallets Dynamic creates for your users). It does not work with external wallets like Phantom.

Quick start

1

Turn on gas sponsorship in the dashboard

  1. Go to the Dynamic Dashboard
  2. Navigate to SettingsEmbedded Wallets
  3. Make sure Solana (SOL) is enabled in your chain configurations
  4. Toggle on SVM Gas Sponsorship
2

Send a transaction as usual

Once sponsorship is on, the signAndSendTransaction you already use sponsors embedded-wallet transactions automatically. No code changes required.
import { signAndSendTransaction } from '@dynamic-labs-sdk/solana';

const sendTx = async (walletAccount, transaction) => {
  // Gas is sponsored automatically when the project setting is on
  // and the wallet supports it.
  const { signature } = await signAndSendTransaction({
    transaction,
    walletAccount,
  });

  console.log('Transaction sent:', signature);
};
If you’re sponsoring because your users hold no SOL at all, use signAndSendSponsoredTransaction instead. It guarantees the transaction is sponsored and throws if sponsorship isn’t available — rather than silently falling back to a regular (unsponsored) transaction that a user with no SOL couldn’t pay for. This is the safest choice for most “the user has an empty wallet” flows.
import { signAndSendSponsoredTransaction } from '@dynamic-labs-sdk/solana';

const { signature } = await signAndSendSponsoredTransaction({
  transaction,
  walletAccount,
});

React example

In React, use useWalletAccounts to get the user’s embedded wallet, then call signAndSendSponsoredTransaction from a button handler. The try/catch shows the user a friendly message if sponsorship fails (see Error handling).
import { signAndSendSponsoredTransaction, isSolanaWalletAccount, SponsorTransactionError } from '@dynamic-labs-sdk/solana';
import { useWalletAccounts } from '@dynamic-labs-sdk/react-hooks';
import { useState } from 'react';
import { Transaction, SystemProgram, PublicKey } from '@solana/web3.js';

function SponsoredSendButton({ recipientAddress }) {
  const walletAccounts = useWalletAccounts();
  const walletAccount = walletAccounts.find(isSolanaWalletAccount);
  const [signature, setSignature] = useState('');
  const [error, setError] = useState('');

  const handleSend = async () => {
    if (!walletAccount) return;
    setError('');
    try {
      const transaction = new Transaction().add(
        SystemProgram.transfer({
          fromPubkey: new PublicKey(walletAccount.address),
          toPubkey: new PublicKey(recipientAddress),
          lamports: 1_000_000,
        }),
      );
      const { signature } = await signAndSendSponsoredTransaction({ transaction, walletAccount });
      setSignature(signature);
    } catch (err) {
      if (err instanceof SponsorTransactionError) {
        setError('Gas sponsorship failed');
      }
    }
  };

  return (
    <div>
      <button onClick={handleSend} disabled={!walletAccount}>Send (Sponsored)</button>
      {signature && <p>Signature: {signature.slice(0, 20)}...</p>}
      {error && <p style={{ color: 'red' }}>{error}</p>}
    </div>
  );
}

Error handling

When you require sponsorship with signAndSendSponsoredTransaction and it can’t go through, it throws a SponsorTransactionError. Wrap the call in a try/catch so you can show the user a message and decide what to do next.
import {
  signAndSendSponsoredTransaction,
  SponsorTransactionError,
} from '@dynamic-labs-sdk/solana';

const sendTransaction = async (walletAccount, transaction) => {
  try {
    const { signature } = await signAndSendSponsoredTransaction({
      transaction,
      walletAccount,
    });
    return { success: true, signature };
  } catch (error) {
    if (error instanceof SponsorTransactionError) {
      return { success: false, error: 'Gas sponsorship failed' };
    }
    return { success: false, error: error.message };
  }
};
A SponsorTransactionError is thrown when:
  • The sponsorship API cannot sponsor the transaction (sponsorship not enabled, or a limit was hit)
  • The wallet doesn’t support sponsored transactions (e.g. an external wallet rather than a V3 MPC embedded wallet)

Advanced usage

Everything below is optional. Reach for it only when you need more control than the quick start gives you.

How it works under the hood

When a transaction is sponsored, the SDK:
  1. Sends the transaction to Dynamic’s backend for sponsorship
  2. Replaces the fee payer with Dynamic’s sponsored account
  3. Signs the sponsored transaction with the user’s wallet
  4. Broadcasts to the network with skipPreflight: true by default

Automatic vs. required sponsorship

There are two ways to send a sponsored transaction, and the difference matters when sponsorship might not be available:
FunctionsponsorshipModeBehavior
signAndSendTransaction'auto' (default)Sponsors when project settings and the wallet support it; otherwise sends a regular transaction.
signAndSendSponsoredTransactionalways requiredAlways requires sponsorship; throws SponsorTransactionError if it isn’t available.
Use signAndSendSponsoredTransaction whenever a failed sponsorship should stop the transaction — for example when the user has no SOL to fall back on.

Opting out per call

Pass sponsorshipMode: 'off' to skip sponsorship for a specific transaction even when the project setting is on:
const { signature } = await signAndSendTransaction({
  sponsorshipMode: 'off',
  transaction,
  walletAccount,
});

Send options

You can pass SendOptions to override defaults. Sponsored transactions use skipPreflight: true by default, but you can override this:
const { signature } = await signAndSendSponsoredTransaction({
  transaction,
  walletAccount,
  options: { skipPreflight: false },
});

Limitations

LimitationDetails
Wallet typeEmbedded wallets only (V3 MPC)
Transaction sizeMaximum 2KB base64-encoded
Already-signedTransactions with signatures are not sponsored
BatchingEach transaction sponsored individually