> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dynamic.xyz/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# SVM Gas Sponsorship (Node SDK)

> Sponsor gas for Solana transactions using the Node SDK — via Dynamic's built-in sponsorship or a custom fee payer with delegated access.

## Overview

The Node SDK supports two approaches to SVM gas sponsorship:

1. **Dynamic sponsorship** — pass `sponsor: true` to `signTransaction()`. Dynamic replaces the fee payer with a sponsor wallet. No funded wallet required on your end.
2. **Custom fee payer (delegated access)** — your server holds a funded Solana wallet and signs as the fee payer alongside the user's delegated wallet.

## Dynamic sponsorship (simplest)

Pass `sponsor: true` to `signTransaction()` on `DynamicSvmWalletClient`. Dynamic's API replaces the fee payer before signing.

<Note>
  Gas sponsorship must be enabled for your environment in the Dynamic dashboard.
</Note>

```typescript theme={"system"}
import { DynamicSvmWalletClient } from '@dynamic-labs-wallet/node-svm';
import { decodeBase58, addSignatureToTransaction } from '@dynamic-labs-wallet/node-svm';
import { Connection, Transaction, SystemProgram, PublicKey } from '@solana/web3.js';

const svmClient = await authenticatedSvmClient();
const connection = new Connection(process.env.SOLANA_RPC_URL ?? 'https://api.mainnet-beta.solana.com');

const { blockhash } = await connection.getLatestBlockhash();

const transaction = new Transaction().add(
  SystemProgram.transfer({
    fromPubkey: new PublicKey(walletMetadata.accountAddress),
    toPubkey: new PublicKey(recipientAddress),
    lamports: 1000000,
  })
);
transaction.recentBlockhash = blockhash;
transaction.feePayer = new PublicKey(walletMetadata.accountAddress);

const signatureBase58 = await svmClient.signTransaction({
  walletMetadata,
  externalServerKeyShares,
  transaction,
  sponsor: true,
});

const signatureBytes = decodeBase58(signatureBase58);
const signedTx = addSignatureToTransaction({
  transaction,
  signature: signatureBytes,
  signerPublicKey: new PublicKey(walletMetadata.accountAddress),
});

const txid = await connection.sendRawTransaction(signedTx.serialize());
await connection.confirmTransaction(txid, 'confirmed');
```

For more control, call [`sponsorTransaction()`](/node/reference/svm/sponsor-transaction) directly to inspect or modify the sponsored transaction before signing.

***

## Custom fee payer with delegated access

Use delegated access to sponsor gas fees for user-initiated Solana transactions. Your server signs transactions on behalf of users while paying the transaction fees.

<Note>
  Your server wallet acts as the fee payer for transactions while the user's delegated wallet signs the transaction instructions.
</Note>

## Prerequisites

* A funded Solana wallet for paying gas fees
* Dynamic server API key
* Environment variables configured
* `@dynamic-labs-wallet/node-svm` package installed

```bash theme={"system"}
npm install @dynamic-labs-wallet/node-svm @solana/web3.js @solana/spl-token bs58 tweetnacl
```

## Delegated Access: Sponsor gas for user wallets

Use delegated credentials to sign transactions on behalf of users while your server pays the gas fees.

### 1) Create a delegated client and set up fee payer

```typescript theme={"system"}
import { createDelegatedSvmWalletClient } from '@dynamic-labs-wallet/node-svm';
import { Connection, Keypair } from '@solana/web3.js';
import bs58 from 'bs58';

const ENVIRONMENT_ID = process.env.DYNAMIC_ENVIRONMENT_ID as string;
const SERVER_API_KEY = process.env.DYNAMIC_SERVER_API_KEY as string;
const FEE_PAYER_PRIVATE_KEY = process.env.FEE_PAYER_PRIVATE_KEY as string;
const RPC_URL = process.env.SOLANA_RPC_URL || 'https://api.devnet.solana.com';

const delegatedClient = createDelegatedSvmWalletClient({
  environmentId: ENVIRONMENT_ID,
  apiKey: SERVER_API_KEY,
});

const connection = new Connection(RPC_URL, 'confirmed');
const feePayer = Keypair.fromSecretKey(bs58.decode(FEE_PAYER_PRIVATE_KEY));
```

### 2) Prepare delegation credentials from webhook

When a user delegates access, you receive encrypted credentials via webhook. Decrypt and prepare them:

```typescript theme={"system"}
import { decryptHybridRsaAes256 } from './webhook-handler';

async function prepareDelegationCredentials(webhookPayload: DelegationWebhookPayload) {
  const decryptedShare = await decryptHybridRsaAes256(
    webhookPayload.data.encryptedDelegatedShare
  );

  const walletApiKey = await decryptHybridRsaAes256(
    webhookPayload.data.encryptedWalletApiKey
  );

  const keyShareData = JSON.parse(decryptedShare);
  const pubkeyBytes = bs58.decode(webhookPayload.data.publicKey);

  return {
    walletId: webhookPayload.data.walletId,
    walletApiKey,
    keyShare: {
      pubkey: { pubkey: pubkeyBytes },
      secretShare: keyShareData.secretShare,
    },
    publicKey: webhookPayload.data.publicKey,
  };
}
```

### 3) Sign transaction with delegated wallet and fee payer

```typescript theme={"system"}
import { delegatedSignTransaction } from '@dynamic-labs-wallet/node-svm';
import { Transaction, SystemProgram, PublicKey, VersionedTransaction } from '@solana/web3.js';
import nacl from 'tweetnacl';

const credentials = await prepareDelegationCredentials(webhookPayload);
const sender = new PublicKey(credentials.publicKey);

const transaction = new Transaction();
const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash('confirmed');

transaction.recentBlockhash = blockhash;
transaction.lastValidBlockHeight = lastValidBlockHeight;
transaction.feePayer = feePayer.publicKey;

transaction.add(
  SystemProgram.transfer({
    fromPubkey: sender,
    toPubkey: new PublicKey(recipientAddress),
    lamports: 1000000,
  })
);

let signedTransaction = await delegatedSignTransaction(delegatedClient, {
  walletId: credentials.walletId,
  walletApiKey: credentials.walletApiKey,
  keyShare: credentials.keyShare,
  transaction,
  signerAddress: sender.toString(),
});

if (signedTransaction instanceof VersionedTransaction) {
  signedTransaction.sign([feePayer]);
} else {
  const message = signedTransaction.serializeMessage();
  const feePayerSignature = nacl.sign.detached(message, feePayer.secretKey);
  signedTransaction.addSignature(feePayer.publicKey, Buffer.from(feePayerSignature));
}

const signature = await connection.sendRawTransaction(
  signedTransaction.serialize(),
  { skipPreflight: false, maxRetries: 3 }
);

await connection.confirmTransaction({ signature, blockhash, lastValidBlockHeight });

console.log('Transaction signature:', signature);
```

## SPL Token Transfers with Gas Sponsorship

```typescript theme={"system"}
import {
  createTransferCheckedInstruction,
  getAssociatedTokenAddress,
  createAssociatedTokenAccountInstruction,
} from '@solana/spl-token';

const USDC_MINT = new PublicKey('Gh9ZwEmdLJ8DscKNTkTqPbNwLNNBjuSzaG9Vp2KGtKJr');
const sender = new PublicKey(credentials.publicKey);
const recipient = new PublicKey(recipientAddress);

const senderTokenAccount = await getAssociatedTokenAddress(USDC_MINT, sender);
const recipientTokenAccount = await getAssociatedTokenAddress(USDC_MINT, recipient);

const instructions = [];

const recipientTokenInfo = await connection.getAccountInfo(recipientTokenAccount);
if (!recipientTokenInfo) {
  instructions.push(
    createAssociatedTokenAccountInstruction(
      feePayer.publicKey,
      recipientTokenAccount,
      recipient,
      USDC_MINT
    )
  );
}

instructions.push(
  createTransferCheckedInstruction(
    senderTokenAccount,
    USDC_MINT,
    recipientTokenAccount,
    sender,
    BigInt(1000000),
    6
  )
);

const transaction = new Transaction();
const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash('confirmed');

transaction.recentBlockhash = blockhash;
transaction.lastValidBlockHeight = lastValidBlockHeight;
transaction.feePayer = feePayer.publicKey;
instructions.forEach(instruction => transaction.add(instruction));

let signedTransaction = await delegatedSignTransaction(delegatedClient, {
  walletId: credentials.walletId,
  walletApiKey: credentials.walletApiKey,
  keyShare: credentials.keyShare,
  transaction,
  signerAddress: sender.toString(),
});

if (!(signedTransaction instanceof VersionedTransaction)) {
  const message = signedTransaction.serializeMessage();
  const feePayerSignature = nacl.sign.detached(message, feePayer.secretKey);
  signedTransaction.addSignature(feePayer.publicKey, Buffer.from(feePayerSignature));
}

const signature = await connection.sendRawTransaction(signedTransaction.serialize());
await connection.confirmTransaction({ signature, blockhash, lastValidBlockHeight });
```

## Signature Verification

When using both delegated signing and fee payer signatures, verify all required signatures are present:

```typescript theme={"system"}
if (!(signedTransaction instanceof VersionedTransaction)) {
  const allSigned = signedTransaction.signatures.every(sig => sig.signature !== null);

  if (!allSigned) {
    const unsigned = signedTransaction.signatures
      .filter(sig => sig.signature === null)
      .map(sig => sig.publicKey.toString());
    throw new Error(`Missing signatures for: ${unsigned.join(', ')}`);
  }

  console.log('All required signatures present');
}
```

## Error Handling

```typescript theme={"system"}
try {
  const signature = await connection.sendRawTransaction(signedTransaction.serialize());
  await connection.confirmTransaction({ signature, blockhash, lastValidBlockHeight });
} catch (error) {
  if (error.message.includes('insufficient funds')) {
    console.error('Fee payer wallet has insufficient SOL');
  } else if (error.message.includes('key share')) {
    console.error('Invalid or expired delegation credentials');
  } else {
    console.error('Transaction failed:', error);
  }
}
```

## Related

* [`signTransaction()`](/node/reference/svm/sign-transaction) - Sign with `sponsor: true` for Dynamic-managed sponsorship
* [`sponsorTransaction()`](/node/reference/svm/sponsor-transaction) - Sponsor a transaction separately for manual control
* [`delegatedSignTransaction()`](/node/reference/svm/delegated-sign-transaction) - Sign with delegated access and `signerAddress`
* [Delegated Access Overview](/overview/wallets/embedded-wallets/mpc/delegated-access/overview)
