Skip to main content

Using Helius Sender with the Jupiter Swap API

Helius Sender is a low-latency Solana transaction sending service that sends your transactions through two pathways, maximizing inclusion speed and landing rate. Jupiter’s Swap API allows you to find the most price-efficient route for your swaps and creates a ready-to-use transaction for you. Combining these two tools enables traders to seize trading opportunities before the competition and increase the chance that the swap will succeed.
Sender is publicly available — no plan is required.

How it works

  1. Get a Jupiter quote for a trading pair
  2. Use it to create a swap transaction with Jupiter’s Swap API
  3. Modify it for Sender compatibility
  4. Broadcast it through Helius Sender for maximum success rate

Requirements

  • Node.js ≥ 20 (tested with v20.9.0)
  • TypeScript ≥ 5
  • A Helius account (free or paid) for RPC access
  • A Solana wallet with sufficient SOL for transaction fees and tips
Install dependencies globally: npm i -g typescript

Implementation

1

Set up the environment

npx tsc --init
npm install @solana/web3.js bs58
npm install --save-dev @types/node
Set "types": ["node"] in your tsconfig.jsonSet "type": "module" in package.json
2

Create the Jupiter Swap Client

Create a file named jupiter-swap-sender.ts with the following code:
import { 
  Keypair,
  VersionedTransaction,
  Connection,
  TransactionMessage,
  AddressLookupTableAccount,
  SystemProgram,
  PublicKey,
  LAMPORTS_PER_SOL
} from '@solana/web3.js';
import bs58 from 'bs58';

const PRIV_B58 = 'Your private key in base58';
const HELIUS_API_KEY = 'Your Helius API key';

const SENDER_ENDPOINT = 'http://ewr-sender.helius-rpc.com/fast';

const SWAP_AMOUNT = 1000000; // 0.001 SOL
const SWAP_FROM_TOKEN = "So11111111111111111111111111111111111111112";
const SWAP_TO_TOKEN = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
const SWAP_SLIPPAGE_BPS = 50;

const TIP_ACCOUNTS = [
  "4ACfpUFoaSD9bfPdeu6DBt89gB6ENTeHBXCAi87NhDEE",
  "D2L6yPZ2FmmmTKPgzaMKdhu6EWZcTpLy1Vhx8uvZe7NZ",
  "9bnz4RShgq1hAnLnZbP8kbgBg1kEmcJBYQq3gQbmnSta",
  "5VY91ws6B2hMmBFRsXkoAAdsPHBJwRfBht4DXox3xkwn",
  "2nyhqdwKcJZR2vcqCyrYsaPVdAnFoJjiksCXJ7hfEYgD",
  "2q5pghRs6arqVjRvT5gfgWfWcHWmw1ZuCzphgd5KfWGJ",
  "wyvPkWjVZz1M8fHQnMMCDTQDbkManefNNhweYk5WkcF",
  "3KCKozbAaF75qEU33jtzozcJ29yJuaLJTy2jFdzUY8bT",
  "4vieeGHPYPG2MmyPRcYjdiDmmhN3ww7hsFNap8pVN3Ey",
  "4TQLFNWK8AovT1gFvda5jfw2oJeRMKEmw7aH6MGBJ3or"
];

// Use the Jupiter API to get a quote
// https://dev.jup.ag/docs/api/swap-api/quote
async function getQuote(inputMint: string, outputMint: string, amount: number, slippageBps: number) {
  console.log("Request Jupiter quote");
  const url = `https://lite-api.jup.ag/swap/v1/quote?inputMint=${inputMint}&outputMint=${outputMint}&amount=${amount}&slippageBps=${slippageBps}`
  const response = (await fetch(url)).json();
  return response
}

// Use the Jupiter API to get a swap transaction, based on the quote response
// https://dev.jup.ag/docs/api/swap-api/swap
async function getSwap(quote: any) {
  console.log("Request Jupiter swap");

  const user_wallet = Keypair.fromSecretKey(bs58.decode(PRIV_B58));
  const url = "https://lite-api.jup.ag/swap/v1/swap"
  const args = {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      quoteResponse: quote,
      userPublicKey: user_wallet.publicKey,
      dynamicComputeUnitLimit: true,
      prioritizationFeeLamports: {
        priorityLevelWithMaxLamports: {
          maxLamports: 1000000,
          priorityLevel: "veryHigh"
        }
      }
    })
  }
  const response = (await fetch(url, args)).json();
  return response;
}

async function createSenderTransactionFromSwapResponse(swapResponse: any) {
  console.log("Create transaction for Sender");

  // Deserialize the Jupiter transaction
  const transactionBase64 = swapResponse.swapTransaction
  const jupiterTransaction = VersionedTransaction.deserialize(Buffer.from(transactionBase64, 'base64'));

  // Get the Address Lookup Tables
  const connection = new Connection(
    'https://mainnet.helius-rpc.com/?api-key=' + HELIUS_API_KEY
  );

  let altAccountResponses = await Promise.all(
    jupiterTransaction.message.addressTableLookups.map(l => connection.getAddressLookupTable(l.accountKey))
  );

  let altAccounts: AddressLookupTableAccount[] = altAccountResponses.map(item => {
    if (item.value == null) throw new Error("ALT is null");
    return item.value;
  });

  // Use the Address Lookup Tables to decompile the transaction
  let decompiledMessage = TransactionMessage.decompile(jupiterTransaction.message, {
    addressLookupTableAccounts: altAccounts,
  });

  // Note that Jupiter has already added the Compute Budget instructions
  // Nothing needs to be done here

  // Add a tip instruction
  const user_wallet = Keypair.fromSecretKey(bs58.decode(PRIV_B58));
  const transferIx = SystemProgram.transfer({
    fromPubkey: user_wallet.publicKey,
    toPubkey: new PublicKey(TIP_ACCOUNTS[Math.floor(Math.random() * TIP_ACCOUNTS.length)]!),
    lamports: 0.0002 * LAMPORTS_PER_SOL,
  })

  decompiledMessage.instructions.push(transferIx);

  // Compile the full transaction
  const transaction = new VersionedTransaction(decompiledMessage.compileToV0Message(altAccounts));

  // Sign the transaction
  transaction.sign([user_wallet]);

  return transaction;
}

async function broadcastTransactionWithSender(transaction: VersionedTransaction) {
  console.log("Send to Sender");

  const connection = new Connection(
    'https://mainnet.helius-rpc.com/?api-key=' + HELIUS_API_KEY
  );

  const response = await fetch(SENDER_ENDPOINT, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      jsonrpc: '2.0',
      id: Date.now().toString(),
      method: 'sendTransaction',
      params: [
        Buffer.from(transaction.serialize()).toString('base64'),
        {
          encoding: 'base64',
          skipPreflight: true, // Required for Sender
          maxRetries: 0
        }
      ]
    })
  });

  const json = await response.json();

  if (json.error) {
    throw new Error(json.error.message);
  }

  console.log("Sender Response:");
  console.log(json);

  console.log("Wait for confirmation");
  const {blockhash, lastValidBlockHeight} = await connection.getLatestBlockhash();
  const result = await connection.confirmTransaction({
    blockhash,
    lastValidBlockHeight,
    signature: bs58.encode(transaction.signatures[0]!)
  }, 'confirmed');
  console.log("Confirmed");
}

async function main() {
  const amount = SWAP_AMOUNT.toString();
  const quote = await getQuote(SWAP_FROM_TOKEN, SWAP_TO_TOKEN, SWAP_AMOUNT, SWAP_SLIPPAGE_BPS);
  const swap = await getSwap(quote);
  const senderTransaction = await createSenderTransactionFromSwapResponse(swap);
  console.log("Signature:", bs58.encode(senderTransaction.signatures[0]!));
  await broadcastTransactionWithSender(senderTransaction);
}

main().catch(console.error);
3

Configuration

Replace the hardcoded API key and private key with your credentials. Set the right parameters for your swap: token addresses, amount, slippage. Choose the Sender endpoint that’s closest to your server.
4

Run the Application

Execute the script to perform a Jupiter swap via Helius Sender:
npx tsx jupiter-swap-sender.ts

Sample output

Request Jupiter quote
Request Jupiter swap
Create transaction for Sender
Signature: 2weWhhYyGA9xPqFaXVfR9FdBDAjM3AEbebYzEER2bLQn6JJ7tJ8wwtncbvD9qi2xFJuDxHdDGQWQWQ7m1cUCg74S
Send to Sender
Sender Response:
{
  jsonrpc: '2.0',
  id: '1758913856910',
  result: '2weWhhYyGA9xPqFaXVfR9FdBDAjM3AEbebYzEER2bLQn6JJ7tJ8wwtncbvD9qi2xFJuDxHdDGQWQWQ7m1cUCg74S'
}
Wait for confirmation
Confirmed
Example transaction

Advanced

Dynamic tipping using the Jito API

Right now, the tip amount is hardcoded at the minimum 0.0002 SOL. However, this may not be enough to win the Jito auction for highly contested accounts. For these cases, use the Jito API to dynamically compute the Sender tip:
async function getDynamicTipAmount(): Promise<number> {
    try {
        const response = await fetch('https://bundles.jito.wtf/api/v1/bundles/tip_floor');
        const data = await response.json();

        if (data && data[0] && typeof data[0].landed_tips_75th_percentile === 'number') {
            const tip75th = data[0].landed_tips_75th_percentile;
            // Use 75th percentile but minimum 0.0002 SOL
            return Math.max(tip75th, 0.0002);
        }

        // Fallback if API fails or data is invalid
        return 0.0002;
    } catch (error) {
        console.warn('Failed to fetch dynamic tip amount, using fallback:', error);
        return 0.0002; // Fallback to minimum
    }
}