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. To get started, create a new project with:Installing Dependencies
Install the required packages:Setting Up Kora
You’ll need a running Kora instance before you can use sponsored transactions. You can:- Run Kora locally - Follow the Kora setup guide to run a local instance
- Use a hosted Kora instance - Point your configuration to your hosted Kora server URL
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: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. Createcomponents/gasless-transaction-demo.tsx:
components/gasless-transaction-demo.tsx
Technical Deep Dive
ThehandleGaslessTransaction 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
pipefunction from@solana/kitto 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
getPaymentInstructionendpoint, which calculates the SPL token fee and returns a payment instruction
- 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
- Partially signs the transaction to get a base64-encoded wire format
- Deserializes it to a
VersionedTransactionto 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)
- Sends the user-signed transaction to Kora’s
signTransactionendpoint, 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
How It Works
The sponsored transaction flow follows these steps:- Get Kora signer - Retrieve the address that will pay fees (in SOL)
- Get payment token - Determine which SPL token the user will pay fees with
- Build estimate transaction - Create a transaction with your instructions to estimate fees
- Get payment instruction - Request a payment instruction from Kora that charges the user in SPL tokens
- Build final transaction - Combine your instructions with the payment instruction
- Re-estimate fees - Get an updated payment instruction based on the full transaction
- Sign with user wallet - User signs the transaction (including the payment instruction)
- Get Kora signature - Kora co-signs as the fee payer (paying in SOL)
- Submit to network - Send the fully signed transaction to Solana
Using the Component
Add the component to your main page inapp/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/tokento transfer SPL tokens - SOL transfers - Transfer native SOL
- Program interactions - Call any Solana program
- Multiple instructions - Combine multiple operations in one transaction
Configuration
Compute Budget
The demo uses default compute budget settings: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 validpublicKeyandsignTransaction
”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