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:
- Node.js and npm installed on your system (follow instructions here).
- Bitquery Free Developer Account with OAuth token (follow instructions here).
- Solana Wallet with some SOL for transaction fees.
Step 1: Setting Up the Environment
Initialize a new Node.js project:
mkdir solana-sniper-bot
cd solana-sniper-bot
npm init -yInstall the necessary dependencies:
npm install @solana/web3.js cross-fetch lodash @project-serum/anchor bs58
Step 2: Creating the Bot
Create a new file
index.js
:touch index.js
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");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
}
}
}
}
}
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));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();
}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: "" };
}
}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);
}
}
- 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
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.
- Replace
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.