Skip to main content

Recommended: JavaScript SDK with React Hooks

For new React apps, we recommend the JavaScript SDK with React Hooks (@dynamic-labs-sdk/react-hooks) instead of the legacy React SDK documented here. The JS SDK comes with many benefits such as a much smaller bundle size and other optimizations. Use the React quickstart (JavaScript SDK) to get started.
This is a React-only guide.

Introduction

In this guide, we’ll show you how to create Solana transactions where users pay transaction fees in SPL tokens (like USDC) instead of SOL. We achieve this using Kora, a fee abstraction service that allows users to pay fees in any supported SPL token. This approach is different from traditional gasless transactions because:
  • Users still pay fees, but in SPL tokens instead of SOL
  • No server-side fee payer wallet is required
  • Kora handles fee estimation and payment instruction creation
  • Kora co-signs transactions as the fee payer

Getting Started

Setting up the Project

We’ll use Next.js for this example. Create the project with create-next-app, then follow the React Quickstart Custom setup path and enable Solana (SVM) only. In the Dynamic dashboard, turn on Solana under Chains & Networks and add your dev origin under SecurityAllowed Origins. If you already have a Next.js app, add the Dynamic React SDK using the same quickstart.

Installing Dependencies

Install the required packages:

Setting Up Kora

You’ll need a running Kora instance before you can use sponsored transactions. You can:
  1. Run Kora locally - Follow the Kora setup guide to run a local instance
  2. Use a hosted Kora instance - Point your configuration to your hosted Kora server URL
For local development, Kora typically runs on http://localhost:8080/. If you’re using devnet instead of localnet, pass the RPC URL to the Kora command:

Configuration

The example uses a configuration object with the following values:
Update koraRpcUrl to point to your Kora instance, and set tokenMintAddress to the SPL token you want to use for fee payments. If you’re using devnet, make sure solanaRpcUrl and solanaWsUrl point to devnet endpoints. For production, consider using environment variables instead of hardcoded values.

Client Implementation

Creating the Transaction Component

Now let’s create a component that handles sponsored transactions using Kora. Create components/gasless-transaction-demo.tsx:
components/gasless-transaction-demo.tsx

Technical Deep Dive

The handleGaslessTransaction function orchestrates a multi-step process to create a transaction where Kora pays fees in SOL while the user pays in SPL tokens. Here’s a technical breakdown: Initial Setup (lines 206-217): Creates a KoraClient instance to communicate with the Kora service, sets up Solana RPC clients for both HTTP and WebSocket connections, and initializes a transaction confirmation promise factory for monitoring transaction status. Kora Configuration (lines 219-225): Retrieves Kora’s fee payer signer address (which will pay fees in SOL) and creates a noop signer placeholder. Fetches Kora’s configuration to determine which SPL token the user will pay fees with. Transaction Building with Fee Estimation (lines 227-275):
  • Creates the user’s instructions (in this case, a memo instruction)
  • Builds an initial transaction estimate using the pipe function from @solana/kit to compose transaction modifications
  • Sets the fee payer to Kora’s signer, adds compute budget instructions (limit and price), and appends user instructions
  • Partially signs the transaction (without user signature) and converts it to base64
  • Sends this estimate to Kora’s getPaymentInstruction endpoint, which calculates the SPL token fee and returns a payment instruction
Final Transaction Construction (lines 277-348):
  • Gets a fresh blockhash (required for transaction validity)
  • Builds the full transaction including both user instructions and the payment instruction
  • Re-estimates fees by sending the complete transaction to Kora again (since adding the payment instruction changes the transaction size and fee calculation)
  • Rebuilds the transaction with the corrected payment instruction
User Signing (lines 350-424):
  • Partially signs the transaction to get a base64-encoded wire format
  • Deserializes it to a VersionedTransaction to access the message structure
  • Identifies the user’s signature index by finding their address in the transaction’s required signers
  • Uses Dynamic’s wallet signer to sign the transaction, extracting only the user’s signature from the signed result
  • Preserves the original transaction structure and injects the user’s signature at the correct index (this is necessary because Dynamic’s signer may modify the transaction structure)
Kora Co-signing and Submission (lines 426-448):
  • Sends the user-signed transaction to Kora’s signTransaction endpoint, which adds Kora’s signature as the fee payer
  • Submits the fully signed transaction to the Solana network via RPC
  • Waits for transaction confirmation using the confirmation promise factory, which monitors both RPC polling and WebSocket subscriptions for efficient confirmation
The two-phase fee estimation (initial estimate → final estimate) is necessary because the payment instruction itself consumes compute units, so the final fee calculation must account for the complete transaction including the payment instruction.

How It Works

The sponsored transaction flow follows these steps:
  1. Get Kora signer - Retrieve the address that will pay fees (in SOL)
  2. Get payment token - Determine which SPL token the user will pay fees with
  3. Build estimate transaction - Create a transaction with your instructions to estimate fees
  4. Get payment instruction - Request a payment instruction from Kora that charges the user in SPL tokens
  5. Build final transaction - Combine your instructions with the payment instruction
  6. Re-estimate fees - Get an updated payment instruction based on the full transaction
  7. Sign with user wallet - User signs the transaction (including the payment instruction)
  8. Get Kora signature - Kora co-signs as the fee payer (paying in SOL)
  9. Submit to network - Send the fully signed transaction to Solana

Using the Component

Add the component to your main page in app/page.tsx:
app/page.tsx

Extending the Demo

The current demo sends a simple memo instruction. You can extend it to:
  • Token transfers - Use @solana-program/token to transfer SPL tokens
  • SOL transfers - Transfer native SOL
  • Program interactions - Call any Solana program
  • Multiple instructions - Combine multiple operations in one transaction
Example: Add a token transfer instruction:

Configuration

Compute Budget

The demo uses default compute budget settings:
Adjust these based on your transaction complexity.

Transaction Version

The demo uses version 0 (legacy) transactions. For versioned transactions:

Troubleshooting

”Wallet not connected”

  • Ensure you’ve connected a Solana wallet through Dynamic
  • Check that useSolanaWallet() returns a valid publicKey and signTransaction

”Kora RPC error”

  • Verify Kora is running and accessible at the configured URL
  • Check network connectivity and CORS settings
  • For local development, ensure Kora is running on http://localhost:8080/

”Transaction failed”

  • Check that your wallet has sufficient SPL tokens for fees
  • Verify the Solana RPC endpoint is accessible
  • Check transaction logs for specific error messages

”Payment instruction failed”

  • Ensure the payment token is configured in Kora
  • Verify the source wallet has sufficient balance of the payment token
  • Check Kora’s validation configuration

Conclusion

Congratulations! You’ve successfully implemented sponsored transactions on Solana using Dynamic’s SDK and Kora. This approach allows users to pay transaction fees in SPL tokens instead of SOL, creating a more flexible payment experience.

Next Steps

  • Configure Kora to support your preferred payment tokens
  • Implement fee estimation UI to show users the cost before signing
  • Add support for multiple payment tokens with user selection
To get the complete source code, check out our GitHub repository.
Last modified on June 25, 2026