WDK
Solana Gasless Transactions in WDK
Tether's Developer Relations

Solana Gasless Transactions in WDK

Raquel Carrasco Gonzalez
Article byRaquel Carrasco GonzalezDeveloper Relations Services

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:

  1. The user signs the transfer with their own key
  2. The paymaster address is set as the transaction's fee payer, so the paymaster's SOL covers the network fee
  3. 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:

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

paymasterUrl

Paymaster fee-payer address

paymasterAddress

Fee token mint (e.g. USD₮)

paymasterToken.address

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.
Candide empty APY key section
    • Name your API key
Candide name your new API key
    • Select the chains you would like to work with, in this case, Solana.
Candide API key chain selection for Solana.
    • And your API key is created! You will be able to find it in your dashboard.
Candide Dashboard with a Solana API Key

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 optional retries, default 3).
  • commitment: the confirmation level for RPC reads (processed, confirmed, or finalized).
  • 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: transferMaxFee protects transfer() calls, while transactionMaxFee protects sendTransaction() and signTransaction(). 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() and wdk.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() with token, recipient, and amount slightly different parameter names than EVM/TRON's sendTransaction, 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 paymasterToken per 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.hash is 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, poll getSignatureStatuses on 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, its feePayer must be absent or equal to paymasterAddress.
  • 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: transferMaxFee for token transfers, transactionMaxFee for 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:

  1. TON Gasless Transactions in WDK
  2. 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!