跳转到主要内容

使用 Helius Sender 和 Jupiter Swap API

Helius Sender 是一种低延迟的 Solana 交易发送服务,通过两条路径发送您的交易,最大化包含速度和成交率。 Jupiter 的 Swap API 允许您为交换找到最具价格效率的路径,并为您创建一个可直接使用的交易。 结合这两个工具,交易者可以在竞争对手之前抓住交易机会,并增加交换成功的机会。
Sender 是公开可用的 — 无需计划。

工作原理

  1. 获取交易对的 Jupiter 报价
  2. 使用 Jupiter 的 Swap API 创建交换交易
  3. 修改以兼容 Sender
  4. 通过 Helius Sender 广播以获得最大成功率

要求

  • Node.js ≥ 20(测试版本 v20.9.0)
  • TypeScript ≥ 5
  • 一个 Helius 账户(免费或付费)用于 RPC 访问
  • 一个 Solana 钱包,有足够的 SOL 用于交易费用和小费
全局安装依赖项:npm i -g typescript

实施

1

设置环境

npx tsc --init
npm install @solana/web3.js bs58
npm install --save-dev @types/node
在您的 tsconfig.json 中设置 "types": ["node"]package.json 中设置 "type": "module"
2

创建 Jupiter Swap 客户端

创建一个名为 jupiter-swap-sender.ts 的文件,并包含以下代码:
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.001 * 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

配置

用您的凭据替换硬编码的 API 密钥和私钥。为您的交换设置正确的参数:代币地址、数量、滑点。选择离您的服务器最近的 Sender 端点
4

运行应用程序

执行脚本通过 Helius Sender 进行 Jupiter 交换:
npx tsx jupiter-swap-sender.ts

示例输出

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
示例交易

高级

使用 Jito API 的动态小费

目前,小费金额硬编码为最低 0.001 SOL。然而,这可能不足以赢得竞争激烈账户的 Jito 拍卖。对于这些情况,使用 Jito API 动态计算 Sender 小费:
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.001 SOL
            return Math.max(tip75th, 0.001);
        }

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