Solana Gasless Transactions in WDK
In this post, we will cover Gasless transactions on the Solana chain using the WDK wallet module.
Solana is the newest addition to WDK's gasless family. The @tetherto/wdk-wallet-solana-gasless module lets users send SOL and SPL tokens (like USD₮) without holding SOL for fees; a Kora-compatible paymaster becomes the transaction fee payer and charges the fee in a token of your choosing.
Note: as of this writing, the module is published as
1.0.0-beta.1. Test your RPC and paymaster configuration on your target network before production use.
Step 1: Understand How Gasless Works on Solana
On Solana, every transaction has a designated fee payer. Normally, that's the sender, which means holding SOL. The gasless module changes who pays:
- The user signs the transfer with their own key
- The paymaster address is set as the transaction's fee payer, so the paymaster's SOL covers the network fee
- The paymaster quotes and charges its fee in the configured paymaster token (e.g., USD₮), in that token's base units
The @tetherto/wdk-wallet-solana-gasless module wraps the standard WDK Solana wallet (@tetherto/wdk-wallet-solana) and adds paymaster-funded native SOL sends, SPL token transfers, fee quotes, and read-only account support. It works on Mainnet Beta, Devnet, and Testnet, anywhere your paymaster is deployed and funded.
Step 2: Get Your Endpoints and Credentials
The module itself is not enough to enable gasless transactions; Solana's setup is a bit different from the other chains. Instead of signing up for a single "gasless API key", you need two endpoints:
1. A Solana RPC endpoint
For development, the public endpoints work:
- Devnet:
https://api.devnet.solana.com - Mainnet:
https://api.mainnet-beta.solana.com
For production, get a dedicated RPC with an API key from a provider (Helius, QuickNode, Triton, etc.). WDK supports passing an ordered array of RPC URLs for automatic failover, so you can list a paid endpoint first and a public one as backup.
2. A Kora-compatible paymaster endpoint
Kora is an open-source paymaster node standard for Solana. A paymaster node that co-signs transactions as the fee payer and collects the fee in an SPL token. Any Kora-compatible endpoint works with the module. You can either:
- Use a hosted Kora-compatible paymaster from an infrastructure provider, they'll give you a paymaster RPC URL, the paymaster's fee-payer address, and the token it accepts fees in.
Our recommendation: Candide's Solana USDT Paymaster. It's a hosted Kora endpoint on Solana Mainnet with USD₮ as the default fee token, exactly the gasless experience this series is about, with no node to run. It's also the paymaster the WDK module was verified against.
- Run your own Kora node: you deploy and fund the fee-payer account yourself, and configure which SPL tokens it accepts as payment. This is the route if you want to sponsor fees for your own users.
Either way, you'll come out of this step with three values:
Value | Config field |
|---|---|
Paymaster RPC URL |
|
Paymaster fee-payer address |
|
Fee token mint (e.g. USD₮) |
|
Get your Candide Solana USDT Paymaster Key
The process is the same as for any other Candide-supported chain.
- Go to the Candide dashboard and create an account
- Click on "Create API Key" to generate your first APY key.
- Name your API key

- Select the chains you would like to work with, in this case, Solana.

- And your API key is created! You will be able to find it in your dashboard.

Step 3: Register the Gasless Wallet
Install the module in your project. We support Node.js and Bare runtimes. For more information, check Introduction to WDK: Tether's Wallet Development Kit.
npm install @tetherto/wdk @tetherto/wdk-wallet-solana-gasless
Next, you will have to register the module with WDK. The process and structure are the same as with every other chain in this series; the only thing that changes is the configuration.
Unlike the EVM options, there's no account-model decision to make here: the Solana module derives a regular Solana account from your seed phrase (standard m/44'/501' derivation, the same address a normal Solana wallet would produce). No smart contract, no deployment, no expensive first transaction. What makes it gasless is purely the configuration: the paymaster's address is set as the transaction's fee payer, so its SOL covers the network fee, and the fee is charged to the user in the token you point paymasterToken at, USD₮ here.
import WDK from '@tetherto/wdk'
import WalletManagerSolanaGasless from '@tetherto/wdk-wallet-solana-gasless'
const seedPhrase = 'your seed phrase here' // only for development and testing
const wdk = new WDK(seedPhrase)
.registerWallet('solana-gasless', WalletManagerSolanaGasless, {
provider: 'https://api.mainnet-beta.solana.com',
commitment: 'confirmed',
paymasterUrl: process.env.SOLANA_PAYMASTER_URL, // contains your API key — keep it secret
paymasterAddress: 'YOUR_PAYMASTER_FEE_PAYER_ADDRESS', // provided by your paymaster service
paymasterToken: {
address: 'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB' // USDT on Solana mainnet
},
transferMaxFee: 1000000n, // cap: 1 USDT (6 decimals)
transactionMaxFee: 1000000n
})
console.log('Solana gasless wallet registered')What's happening:
provider: your Solana RPC, a string, or an ordered array of URLs for automatic failover (with optionalretries, default 3).commitment: the confirmation level for RPC reads (processed,confirmed, orfinalized).paymasterUrl: the Kora-compatible paymaster endpoint from Step 2. Also supports failover arrays. With Candide, the URL includes your API key; load it from an environment variable; never hardcode it.paymasterAddress: the address that becomes the transaction's fee payer, its SOL pays the network fee. Your paymaster service provides this address alongside the endpoint; copy it from there.paymasterToken.address: the SPL token mint the paymaster charges fees in (USD₮ here). Fees are quoted and returned in this token's base units. As with the other chains, this single field is what makes the account gasless.transferMaxFee/transactionMaxFee: fee caps in paymaster-token base units.
⚠️ Note the difference from the EVM modules: Solana's module splits the fee cap in two:transferMaxFeeprotectstransfer()calls, whiletransactionMaxFeeprotectssendTransaction()andsignTransaction(). If a quoted fee exceeds its cap, the call throws instead of silently overpaying. Set both in production.
The rest of the flow is the standard WDK pattern: getAccount('solana-gasless', 0), quote, then send. But before moving forward, a few things to keep in mind:
getAddress()returns your normal Solana address, unlike the ERC-4337 Smart Account, there's no separate contract address to fund. Load USD₮ there, and you're ready; the account never needs to hold SOL, not for fees and not for rent. There's even a convenience method for this:account.getPaymasterTokenBalance()returns the balance of the configured fee token directly.- First transfers to a new recipient cost more: if the recipient doesn't have a USD₮ token account yet, the rent for creating it is included in the quoted fee. Subsequent transfers cost a fraction of a cent.
- The paymaster supports the legacy SPL Token program; Token-2022 mints are not supported yet.
- When you no longer need the account or wallet manager, call
account.dispose()andwdk.dispose()to clear private keys from memory.
Step 4: Send Your First Gasless Transaction
Once the wallet is registered, we can send USD₮ from an account that holds zero SOL. As always, we quote first, show users the exact cost before they commit:
const solAccount = await wdk.getAccount('solana-gasless', 0)
const usdtMintSolana = 'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB' // USDT on Solana mainnet
const recipientSol = 'RECIPIENT_SOLANA_ADDRESS'
// Check the balance that matters: the fee token
const balance = await solAccount.getPaymasterTokenBalance()
console.log('USDT balance:', Number(balance) / 1e6, 'USDT')
// Quote the transfer first
const quote = await solAccount.quoteTransfer({
token: usdtMintSolana,
recipient: recipientSol,
amount: 1000000n // 1 USDT (6 decimals)
})
console.log('Paymaster fee:', Number(quote.fee) / 1e6, 'USDT')
console.log('Total deducted:', (1000000 + Number(quote.fee)) / 1e6, 'USDT')
// Send — the paymaster pays the SOL network fee
try {
const result = await solAccount.transfer({
token: usdtMintSolana,
recipient: recipientSol,
amount: 1000000n
}, {
transferMaxFee: 500000n // optional per-call cap: 0.5 USDT
})
console.log('Transaction signature:', result.hash)
console.log('Fee paid:', Number(result.fee) / 1e6, 'USDT')
} catch (error) {
console.error('Transaction failed:', error.message)
}What's happening:
- SPL transfers use
transfer()withtoken,recipient, andamountslightly different parameter names than EVM/TRON'ssendTransaction, reflecting Solana's token model. - The paymaster co-signs the transaction as the fee payer and spends the SOL; the fee is deducted from the user's USD₮ balance in the same transaction. The quoted fee is based on the live SOL cost and, as noted in Step 3, includes the rent for creating the recipient's token account if it doesn't exist yet.
- The second argument is a per-call override: here we cap this specific transfer's fee at 0.5 USD₮. You can also override
paymasterTokenper call to pay one transaction's fee in a different token. - Note that quotes (
quoteTransfer(),quoteSendTransaction()) return estimates but don't enforce the caps; enforcement happens on the actual send/transfer. result.hashis the transaction signature; you can look it up on a Solana explorer.transfer()returns as soon as the transaction is submitted; if you need to wait for on-chain confirmation, pollgetSignatureStatuseson your RPC.
Native SOL sends work through the paymaster too, even the SOL transfer's fee is paid in USD₮:
const result = await solAccount.sendTransaction({
to: recipientSol,
value: 1000000n // lamports
}, {
transactionMaxFee: 500000n // per-call cap for sendTransaction
})Common errors:
- Fee above cap: the quoted paymaster fee exceeds
transferMaxFee/transactionMaxFee. Raise the cap or retry later, and remember first transfers to new recipients quote higher because of token account rent. - "Insufficient balance": the USD₮ balance must cover the amount + fee. Check it with
getPaymasterTokenBalance(). - Fee payer mismatch: if you pass a prebuilt
TransactionMessage, itsfeePayermust be absent or equal topaymasterAddress. - Unsupported mint: the paymaster accepts legacy SPL Token mints; Token-2022 isn't supported yet.
- Underfunded paymaster: transactions fail if the fee payer's SOL runs out. With a hosted paymaster like Candide, this is handled for you; monitor it yourself if you self-host.
One last housekeeping note: call dispose() on owned accounts and wallet managers when you no longer need the private keys in memory.
Key Insights
- Solana gasless = swapping the fee payer: the paymaster's address pays the SOL; you repay in the paymaster token.
- Kora is the standard: any Kora-compatible paymaster works, hosted, or self-run if you want to sponsor your users' fees.
- Two fee caps, not one:
transferMaxFeefor token transfers,transactionMaxFeefor send/sign. Set both in production. - It's a beta: pin the version, test on Devnet, and validate your paymaster config before mainnet.
What's next
Follow WDK's official X account to stay up to date with all the announcements. Join us on Discord, the best place to ask questions, share what you're building, and get help directly from the team.
All four modules are already documented and ready to use. And if you'd enjoy a more guided, step-by-step take with extra tips along the way, we've got you. Coming up:
- TON Gasless Transactions in WDK
- TRON Gasless Transactions in WDK
And after that, more to come. Protocol Integration: Swaps, Bridges, and Lending with WDK, where we layer DeFi functionality on top of these gasless wallets.
Gasless + swaps + bridges + lending = a complete DeFi experience without ever touching native tokens.
Receive the latest WDK news
Click to subscribe to the WDK newsletter and stay updated!
Latest Articles & Guides



