Skip to main content

Tutorial for Building a Solana Sniper Bot Using Bitquery Real-Time Solana Subscriptions and Jupiter Swap API

This tutorial will guide you through building a Solana sniper bot using Bitquery for real-time Solana subscriptions and the Jupiter Swap API for executing swaps. By the end of this guide, you'll have a bot that listens for specific on-chain instructions and performs token swaps based on the detected instructions.

Tutorial Video

Tutorial

Prerequisites

Before you begin, ensure you have the following:

  1. Node.js and npm installed on your system (follow instructions here).
  2. Bitquery Free Developer Account with OAuth token (follow instructions here).
  3. Solana Wallet with some SOL for transaction fees.

Step 1: Setting Up the Environment

  1. Initialize a new Node.js project:

    mkdir solana-sniper-bot
    cd solana-sniper-bot
    npm init -y
  2. Install the necessary dependencies:

    npm install @solana/web3.js cross-fetch lodash @project-serum/anchor bs58

Step 2: Creating the Bot

  1. Create a new file index.js:

    touch index.js
  2. Add the necessary imports:

    const {
    Connection,
    PublicKey,
    VersionedTransaction,
    Keypair,
    } = require("@solana/web3.js");
    const fetch = require("cross-fetch");
    const lodash = require("lodash");
    const { Wallet } = require("@project-serum/anchor");
    const bs58 = require("bs58");
  3. Define the Solana Instructions GraphQL query for Bitquery:

This query fetches new liquidity pools created on the Solana Raydium DEX along with token information. For detailed information on the Raydium API, check examples here

    const gql = (strings, ...values) =>
strings.reduce((final, str, i) => final + str + (values[i] || ""), "");

const query = gql`
{
Solana {
Instructions(
where: {
Transaction: { Result: { Success: true } }
Instruction: {
Program: {
Method: { is: "initializeUserWithNonce" }
Address: { is: "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8" }
}
}
}
limit: { count: 1 }
orderBy: { ascending: Block_Date }
) {
Instruction {
Accounts {
Address
}
}
}
}
}
`;

Note: For real-time tracking of new tokens, use the subscription query below:

 subscription {
Solana {
Instructions(
where: {Transaction: {Result: {Success: true}}, Instruction: {Program: {Method: {is: "initializeUserWithNonce"}, Address: {is: "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8"}}}}
) {
Instruction {
Accounts {
Address
}
}
}
}
}
  1. Set up the Solana connection and wallet:

    const connection = new Connection("https://api.mainnet-beta.solana.com");
    const walletPublicKey = new PublicKey("YOUR_PUBLIC_KEY");
    const secretKeyUint8Array = new Uint8Array([/* YOUR_SECRET_KEY_ARRAY */]);
    const wallet = new Wallet(Keypair.fromSecretKey(secretKeyUint8Array));
  2. Define a function to fetch data from Bitquery:

    async function fetchGraphQL(query) {
    const response = await fetch("https://streaming.bitquery.io/eap", {
    method: "POST",
    headers: {
    "Content-Type": "application/json",
    Authorization: "Bearer YOUR_BITQUERY_OAUTH_TOKEN",
    },
    body: JSON.stringify({ query }),
    });

    if (!response.ok) {
    throw new Error(`HTTP error! status: ${response.status}`);
    }
    return await response.json();
    }
  3. Fetch pool addresses from Bitquery:

    async function getPoolAddresses() {
    try {
    const data = await fetchGraphQL(query);
    const instructions = lodash.get(data, "data.Solana.Instructions", []);

    return instructions.map(({ Instruction: { Accounts } }) => ({
    poolAddress: Accounts.length > 4 ? Accounts[4].Address : undefined,
    tokenA: Accounts.length > 8 ? Accounts[8].Address : undefined,
    tokenB: Accounts.length > 9 ? Accounts[9].Address : undefined,
    }))[0];
    } catch (error) {
    console.error("Error fetching data:", error);
    return { poolAddress: "", tokenA: "", tokenB: "" };
    }
    }
  4. Execute a token swap using Jupiter API:

This code has been derived from sample mentioned in the official docs here. We add checks for "TOKEN_NOT_TRADABLE" and "COULD_NOT_FIND_ANY_ROUTE" to accommodate delays in the Jupiter Swap API.

    async function swapTokens(tokenA, tokenB) {
try {
const quoteUrl = `https://quote-api.jup.ag/v6/quote?inputMint=${tokenB}&outputMint=${tokenA}&amount=10000&slippageBps=150`;
console.log("quote url ", quoteUrl);
const quoteResponse = await fetch(quoteUrl);
const quoteData = await quoteResponse.json();
if (
quoteData["errorCode"] != "TOKEN_NOT_TRADABLE" &&
quoteData["errorCode"] != "COULD_NOT_FIND_ANY_ROUTE"
) {
const swapTransactionResponse = await fetch(
"https://quote-api.jup.ag/v6/swap",
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
quoteResponse: quoteData,
userPublicKey: wallet.publicKey.toString(),
wrapAndUnwrapSol: true,
}),
}
);

const { swapTransaction } = await swapTransactionResponse.json();

const swapTransactionBuf = Buffer.from(swapTransaction, "base64");
console.log("swapTransactionBuf ", swapTransactionBuf);
const transaction = VersionedTransaction.deserialize(swapTransactionBuf);
transaction.sign([wallet.payer]);
const rawTransaction = transaction.serialize();
const txid = await connection.sendRawTransaction(rawTransaction, {
skipPreflight: false,
maxRetries: 4,
preflightCommitment: "confirmed",
commitment: "confirmed",
});

const confirmation = await connection.confirmTransaction(
txid,
"confirmed"
);
console.log(
`Transaction confirmed: ${confirmation.value.err ? "Error" : "Success"}`
);
console.log(`Transaction successful: https://solscan.io/tx/${txid}`);
}
} catch (error) {
console.error("Error during token swap:", error);
}
}
  1. Define the main function to coordinate the steps:
    async function main() {
const { tokenA, tokenB } = await getPoolAddresses();
await swapTokens(tokenA, tokenB);
}

main();

Step 3: Running the Bot

  1. Replace placeholders:

    • Replace YOUR_PUBLIC_KEY with your actual Solana wallet public key.
    • Replace YOUR_SECRET_KEY_ARRAY with your wallet's secret key array.
    • Replace YOUR_BITQUERY_OAUTH_TOKEN with your actual Bitquery OAuth token.
  2. Run the bot:

    node index.js

Conclusion

Congratulations! You've successfully built a Solana sniper bot similar to SOL Sniper Bot using Bitquery for real-time Solana subscriptions and the Jupiter Swap API for executing swaps. This bot actively listens for specific on-chain instructions and performs swaps based on the detected activities. Remember to monitor and manage your bot diligently, especially when operating on the mainnet with real funds.

Disclaimer

This tutorial is for educational purposes only. Trading cryptocurrencies involves significant risk, and you should consult with a professional advisor before making any investment decisions.