Developer Documentation
Integrate gasless Solana payments
gasless-sol lets a sponsor wallet cover SOL gas while your users pay a small USDC fee — fee and transfer land atomically in one transaction, so the user never needs SOL. Pure TypeScript over @solana/web3.js; no on-chain program to deploy.
Install
Install the SDK alongside its peer dependencies.
bun add gasless-sol @solana/web3.js @solana/spl-token @solana/wallet-adapter-reactShips as ESM with full TypeScript types. Three entry points: gasless-sol (core), gasless-sol/react (hook), gasless-sol/server (API route).
Quickstart
Three files and you have gasless payments: a shared config, two API routes, and a button. Each is covered in detail below.
- 1. Define a config (fee token + amount).
- 2. Add the sponsor API routes.
- 3. Call the React hook from a button.
Configuration
One config object drives everything. Share it between the client and the sponsor route so both agree on the fee.
// lib/gasless.ts
import type { LegionGaslessConfig } from "gasless-sol";
export const gaslessConfig: LegionGaslessConfig = {
gasless: {
fees: [
// Devnet USDC mint. Swap for mainnet USDC on mainnet-beta.
{ token: "USDC", mintAddress: "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU", amount: 0.05 },
],
defaultFeeToken: "USDC",
},
};Sponsor API routes
The sponsor co-signs server-side so the private key never reaches the client. createSponsorRoute validates that every transaction pays the configured fee before signing — without feeConfig the sponsor would pay gas for arbitrary transactions, so always pass it.
// app/api/sponsor/route.ts
import { createSponsorRoute } from "gasless-sol/server";
import { gaslessConfig } from "@/lib/gasless";
export const { GET, POST } = createSponsorRoute({
privateKeyEnvVar: "SPONSOR_PRIVATE_KEY", // server-only, never NEXT_PUBLIC
feeConfig: gaslessConfig, // enforces fee-to-sponsor on every tx
rateLimit: 10, // requests/min/IP
minSolBalance: 0.01, // reject if sponsor underfunded
});// app/api/sponsor-pubkey/route.ts
import { createSponsorPubkeyRoute } from "gasless-sol/server";
export const { GET } = createSponsorPubkeyRoute();React hook
useGaslessTransaction handles the full flow: build → user signs → sponsor co-signs → submit → confirm. It reads the connected wallet from @solana/wallet-adapter-react. Amounts are in raw token units (USDC has 6 decimals, so 1 USDC = 1_000_000).
"use client";
import { useGaslessTransaction } from "gasless-sol/react";
import { gaslessConfig } from "@/lib/gasless";
export function TipButton({ creator }: { creator: string }) {
const { sendGaslessTip, status, signature, error, isLoading } =
useGaslessTransaction(gaslessConfig);
return (
<button
disabled={isLoading}
onClick={() =>
sendGaslessTip({
recipientAddress: creator,
tipAmountRaw: 1_000_000, // 1 USDC
})
}
>
{isLoading ? status : "Send 1 USDC"}
</button>
);
}Returned status cycles through idle → building → signing → sponsoring → submitting → confirming → confirmed (or error). On success, signature holds the tx hash for an Explorer link.
Core builder (advanced)
Need to drive the flow yourself (non-React, custom signing)? Use the builder directly. It returns an unsigned transaction with the sponsor as fee payer — you handle user-sign, POST to your sponsor route, and submit.
import { LegionGasless } from "gasless-sol";
import { Connection, PublicKey } from "@solana/web3.js";
import { gaslessConfig } from "@/lib/gasless";
const legion = new LegionGasless(gaslessConfig);
const { transaction } = await legion.buildGaslessTipTransaction({
connection: new Connection("https://api.devnet.solana.com"),
sponsorPublicKey: new PublicKey(sponsorPubkey),
senderPublicKey: new PublicKey(userPubkey),
recipientPublicKey: new PublicKey(creatorPubkey),
tipAmountRaw: 1_000_000, // 1 USDC
});
// → user partial-signs → POST to /api/sponsor → sponsor co-signs → submitEnvironment
Two variables. The sponsor key is server-only — never prefix it with NEXT_PUBLIC_.
# .env.local
NEXT_PUBLIC_SOLANA_NETWORK=devnet
SPONSOR_PRIVATE_KEY=<base58 secret of a funded devnet wallet>Fund the sponsor with devnet SOL (faucet.solana.com); users need devnet USDC (faucet.circle.com) but zero SOL. Switch NEXT_PUBLIC_SOLANA_NETWORK to mainnet-beta and the config to the mainnet USDC mint for production.
How it works
Each tip is a single atomic transaction with the sponsor as fee payer. No custom program — just the SPL Token and System programs.
feePayer: sponsor ← covers ~5000 lamports SOL gas
ix[0] create ATA (idem.) ← ensure sponsor's USDC account exists
ix[1] USDC transfer ← user pays the fee to sponsor
ix[2] create ATA (idem.) ← ensure recipient's USDC account exists
ix[3] USDC transfer ← user tips the recipient
All-or-nothing: fee + tip succeed together or the tx fails.
User holds 0 SOL the entire time.See it live on the tip jar demo.