The @abstraxn/solana-relayer package lets your app build, sign, and relay Solana transactions — gaslessly. The relayer reserves a fee-payer on your behalf so end-users never need SOL for fees. Gas sponsorship works the same way as the Paymaster on EVM chains — you fund a gas pool from the dashboard and the relayer draws from it to cover transaction fees on behalf of your users. What you can do:
  • Send native SOL transfers
  • Send SPL-token transfers (with automatic ATA creation)
  • Submit arbitrary program instructions
  • Track transaction status until finality
  • Sponsor gas for your users (same model as EVM Paymaster)

Installation

npm install @abstraxn/solana-relayer

Setup

Relayer URL Format

The Solana relayer URL follows this pattern:
https://solana-paymaster.abstraxn.com/api/v1/{chainId}/?apikey={your_api_key}
PlaceholderDescriptionExample
{chainId}Solana chain identifier from your dashboard103 (devnet), 101 (mainnet-beta)
{your_api_key}API key from your Abstraxn appak_live_abc123...

Get Your Relayer URL

  1. Visit https://dashboard.abstraxn.com/ and log in or sign up
  2. Go to Apps → create or select an app with Solana as a supported chain
  3. Navigate to Paymaster — if your app has Solana selected, you will see the Solana paymaster link
  4. Copy the Paymaster URL — this is your relayer URL
The Solana Relayer uses the same gas sponsorship model as the EVM Paymaster. Fund your gas pool from the Paymaster section in the dashboard, and the relayer draws from it to cover transaction fees on behalf of your users.
Your relayer URL contains your API key. Store it in environment variables — never hardcode it in client-side code.

Create the Client

import { SolanaRelayer } from "@abstraxn/solana-relayer";

const client = new SolanaRelayer({
  relayerUrl: process.env.SOLANA_RELAYER_URL!,
  // e.g. "https://solana-paymaster.abstraxn.com/api/v1/103/?apikey=your_api_key"
});
That’s it — one config, one client. All four SDK methods use this instance.

Core Flow

Every relayed transaction follows four steps:
buildTx  →  signTx  →  sendTx  →  getTxStatus
StepWhat happens
buildTxSDK fetches an available relayer, reserves it as fee-payer, and builds a VersionedTransaction
signTxUser’s wallet adapter signs the transaction
sendTxSigned transaction is submitted to the relayer hub along with the reservation token
getTxStatusPoll until the transaction reaches confirmed or failed
sendTx must be called after a successful buildTx — the SDK stores the reservation token from the build step internally.

Native SOL Transfer

Send SOL from one wallet to another, with gas paid by the relayer:
import { Connection, PublicKey } from "@solana/web3.js";
import { SolanaRelayer } from "@abstraxn/solana-relayer";

const client = new SolanaRelayer({
  relayerUrl: process.env.SOLANA_RELAYER_URL!,
});

const connection = new Connection(
  "https://api.devnet.solana.com",
  "confirmed",
);

// 1. Build the transaction
const { tx, lastValidBlockHeight } = await client.buildTx({
  connection,
  sender: new PublicKey("<user-wallet-pubkey>"),
  to: new PublicKey("<recipient-pubkey>"),
  lamports: 1_000_000, // 0.001 SOL
});

// 2. Sign with the user's wallet
const serializedUserSignedTx = await client.signTx({
  wallet: walletAdapter, // e.g. from @solana/wallet-adapter-react
  transaction: tx,
});

// 3. Submit to the relayer
const submit = await client.sendTx({
  signedTransaction: serializedUserSignedTx,
  lastValidBlockHeight,
});

// 4. Track status
const finalStatus = await client.getTxStatus({
  txnId: submit.txnId,
});

console.log(finalStatus.status);    // "confirmed" or "failed"
console.log(finalStatus.signature); // on-chain transaction signature

SPL Token Transfer

Transfer any SPL token. If the recipient’s Associated Token Account (ATA) does not exist, the SDK automatically creates it with the relayer as rent payer.
const { tx, lastValidBlockHeight } = await client.buildTx({
  connection,
  sender: new PublicKey("<owner-pubkey>"),
  mint: new PublicKey("<token-mint-address>"),
  recipientOwner: new PublicKey("<recipient-owner-pubkey>"),
  amountUi: "1.25",    // human-readable amount
  decimals: 6,          // fallback — SDK uses on-chain decimals when available
  splTokenProgramId: new PublicKey("<token-program-id>"),
  splAssociatedTokenProgramId: new PublicKey("<ata-program-id>"),
});

// Then sign, send, and track status as above
const signed = await client.signTx({ wallet: walletAdapter, transaction: tx });
const submit = await client.sendTx({ signedTransaction: signed, lastValidBlockHeight });
const status = await client.getTxStatus({ txnId: submit.txnId });

Token transfer parameters

ParameterTypeRequiredDescription
connectionConnectionYesSolana RPC connection
senderPublicKeyYesToken owner’s public key
mintPublicKeyYesSPL token mint address
recipientOwnerPublicKeyYesRecipient’s wallet address (not ATA)
amountUistringYesHuman-readable amount (e.g. "1.25")
decimalsnumberYesFallback decimals if RPC lookup fails
splTokenProgramIdPublicKeyYesToken program (Token or Token-2022)
splAssociatedTokenProgramIdPublicKeyYesAssociated Token Account program
When the mint supports jsonParsed RPC, the SDK reads on-chain decimals and uses TransferChecked. The decimals parameter is only a fallback. If the sender or recipient ATA is missing, the SDK prepends a createAssociatedTokenAccount instruction with the relayer as payer.

Custom Program Transaction

For any instruction set that isn’t a simple transfer — swaps, NFT mints, program calls — pass your own instructions:

Static instructions

import { TransactionInstruction, PublicKey } from "@solana/web3.js";

const instructions: TransactionInstruction[] = [
  // Your custom instructions here
];

const { tx, lastValidBlockHeight } = await client.buildTx({
  connection,
  sender: new PublicKey("<signer-pubkey>"),
  instructions,
});

Dynamic instructions with buildInstructions

When your instructions need the relayer’s public key before they can be built (e.g. ATA creation where the relayer pays rent), use the buildInstructions callback:
const { tx, lastValidBlockHeight } = await client.buildTx({
  connection,
  sender: new PublicKey("<signer-pubkey>"),
  buildInstructions: async ({ feePayer }) => {
    // feePayer is the reserved relayer's public key
    return [
      createAssociatedTokenAccountInstruction(
        feePayer,        // payer (relayer)
        ata,             // ATA to create
        recipient,       // owner of the new ATA
        mint,            // token mint
      ),
      createTransferCheckedInstruction(
        senderAta,       // source
        mint,            // mint
        ata,             // destination
        senderPubkey,    // owner
        amount,          // raw amount
        decimals,        // token decimals
      ),
    ];
  },
});
instructions, buildInstructions, and transaction are mutually exclusive — pass exactly one. Passing more than one will throw an error.

buildTx Input Variants

buildTx accepts exactly one of three transaction types:
TypeRequired fieldsUse case
Native transferto + lamportsSend SOL
Token transfermint + recipientOwner + amountUi + decimals + splTokenProgramId + splAssociatedTokenProgramIdSend SPL tokens
Custominstructions, buildInstructions, or transaction (pick one)Any program call
All variants require connection and sender.

API Reference

new SolanaRelayer(config)

OptionTypeDescription
relayerUrlstringFull relayer endpoint URL (includes API key)

buildTx(params){ tx, lastValidBlockHeight }

Fetches an available relayer, reserves it, and builds a VersionedTransaction. Returns the transaction and block height for expiry tracking.

signTx(params)serializedTransaction

ParameterTypeDescription
walletWallet AdapterSolana wallet adapter instance
transactionVersionedTransactionTransaction from buildTx

sendTx(params){ txnId }

ParameterTypeDescription
signedTransactionstringSerialized signed transaction from signTx
lastValidBlockHeightnumberBlock height from buildTx

getTxStatus(params){ status, signature }

ParameterTypeDescription
txnIdstringTransaction ID from sendTx
Returns:
FieldTypeValues
statusstring"confirmed" or "failed"
signaturestringOn-chain transaction signature

How It Works Under the Hood

The SDK communicates with sol-relayer-hub using a JSON-RPC style protocol over a single POST endpoint:
POST <relayerUrl>
Content-Type: application/json

{ "method": "<method>", "params": [...], "id": 1, "jsonrpc": "2.0" }
RPC MethodSDK MethodPurpose
sol_getAvailableRelayerbuildTxReserve a fee-payer relayer
sol_sendTx / sol_sendProgrammeTxsendTxSubmit signed transaction
sol_getTxStatusgetTxStatusPoll transaction status
You never call these directly — the SDK handles serialization, reservation tokens, and retries.

Integrating with Wallet Adapters

The signTx method accepts any standard Solana wallet adapter. Here’s a React example using @solana/wallet-adapter-react:
import { useWallet, useConnection } from "@solana/wallet-adapter-react";
import { PublicKey } from "@solana/web3.js";
import { SolanaRelayer } from "@abstraxn/solana-relayer";

function SendButton() {
  const { publicKey, wallet } = useWallet();
  const { connection } = useConnection();

  const handleSend = async () => {
    if (!publicKey || !wallet) return;

    const client = new SolanaRelayer({
      relayerUrl: process.env.NEXT_PUBLIC_SOLANA_RELAYER_URL!,
    });

    const { tx, lastValidBlockHeight } = await client.buildTx({
      connection,
      sender: publicKey,
      to: new PublicKey("11111111111111111111111111111112"),
      lamports: 1_000_000,
    });

    const signed = await client.signTx({
      wallet: wallet.adapter,
      transaction: tx,
    });

    const { txnId } = await client.sendTx({
      signedTransaction: signed,
      lastValidBlockHeight,
    });

    // Poll until confirmed
    let status;
    do {
      await new Promise((r) => setTimeout(r, 2000));
      status = await client.getTxStatus({ txnId });
    } while (status.status !== "confirmed" && status.status !== "failed");

    console.log("Final:", status.status, status.signature);
  };

  return (
    <button onClick={handleSend} disabled={!publicKey}>
      Send 0.001 SOL (Gasless)
    </button>
  );
}

Troubleshooting

Q: buildTx fails with “no relayer available” All relayers are currently reserved. Wait a moment and retry — relayers are released after submission or timeout. Q: sendTx returns an error
  • Ensure you call sendTx after a successful buildTx (the reservation token is stored internally).
  • Check that the lastValidBlockHeight has not expired — if the block height has passed, rebuild the transaction.
Q: Token transfer fails with “account not found”
  • Verify the mint address is correct on the target network (devnet vs mainnet).
  • Ensure splTokenProgramId matches the token standard (Token vs Token-2022).
Q: Transaction stuck in pending
  • Check Solana network status for congestion.
  • Verify the relayer hub is healthy via its health endpoint.

Security Notes

  • Store the relayer URL (which contains your API key) in server-side environment variables.
  • Validate all user inputs (addresses, amounts) before building transactions.
  • The relayer only pays gas from your sponsored pool — it cannot move user funds. Users must still sign with their own wallet.
  • Monitor your gas pool balance in the dashboard to avoid failed sponsorships.