master f07a7a8df7bf cached
12 files
35.7 KB
9.2k tokens
51 symbols
1 requests
Download .txt
Repository: danbayk/solana-spl-token-sniper
Branch: master
Commit: f07a7a8df7bf
Files: 12
Total size: 35.7 KB

Directory structure:
gitextract_y947oijx/

├── .gitignore
├── README.md
├── package.json
├── strategy1/
│   ├── formatAmmKeysById.js
│   ├── start1.js
│   └── swap1.js
├── strategy2/
│   ├── derivePoolKeys.js
│   ├── start2.js
│   └── swap2.js
└── utils/
    ├── config.js
    ├── decoderaylog.js
    └── util.js

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
**/node_modules
.DS_Store

================================================
FILE: README.md
================================================
# Solana SPL-Token Sniper
## Overview
This project is Solana SPL-Token sniper which aims to swap (purchase) new Raydium liquidity pairs within the first price candle by scanning Solana blockchain transactions.
## How the program works
* Scans the Solana blockchain for an "initialize market" transaction which is decoded for all necessary liquidity pool keys and related information.
* Begins sending/retrying the "swap" transaction until the liquidity pool is live, minimizing the time between the transaction and the creation of the LP.
* Upon sending a valid transaction, begins to track the users position with current price/percent gain by scanning and decoding on-chain liquidity pool information in order to get the most accurate data.
## Setup
Use the following instructions to install and run the program (assume node is installed):
1. Create a Solana wallet and obtain public and private keys (Phantom wallet recommended).
2. Obtain a Solana RPC/websocket connection, can use `https://api.mainnet-beta.solana.com` and `wss://api.mainnet-beta.solana.com` for testing purposes.
3. Run `npm install`.
4. Inside the `utils/config.js` file, enter public key, private key, and both RPC connections. Also include amount of SOL to use per swap.
## Running the scripts
This project contains two different strategies for sniping new liquidity pairs:\
### **Strategy #1:**
* The first strategy obtains necessary liquidity pool keys from the transaction which creates the LP. To run this script with `node strategy1/start1.js`. The transaction may fail multiple times before succeeding as Solana transactions can be dropped with certain RPC nodes.
* **NOTE:** this script is slower as it must wait for the "add liquidity" transaction to reach "confirmed" status before obtaining pool data. This results in ~30 seconds between the pool creation and swap transaction.
### **Strategy #2:**
* The second strategy obtains necessary liquidity pool keys from the "initialize market" transaction which typically occurs ~2 minutes before the LP is live on Raydium. This allows for all pool keys to be precomputed. This script also retries the swap transaction multiple times per second which allows for the swap to be sent during the "processed" state of the "add liquidity" transaction instead of "confirmed", greatly reducing the time between the creation of the LP and the swap transaction. This script can be run with `node strategy2/start2.js`.
*Both scripts utilize the same swapping/position management system found in `./swap/swap1.js` and `/swap/swap2.js`.*
### **Additional Notes:**
* Once the program is started, an output will only be displayed once a new market id/pool is found.
* "the amm account owner is not match with this program + error 0x1b": This error occurs when the program sends a swap tx to a pool that doesn't have liquidity yet. The tx spam is part of the sniping strategy and the error will occur until the swap to the pool goes through (moment liquidity is added).
## Future improvements
1. **Automatic position exit strategy:** Implementing an automatic exit strategy based on user-specified parameters. Currently have to manually swap the tokens back to SOL.
2. **Rugpull prevention:** SPL-Token are often rugpulled after launch, prevent the purchase of tokens with low-liquidity and no social media pages.
3. **Reducing tx time:** Reduce time between LP creation and swap tx by utilizing a faster RPC connection and minimizing RPC calls before sending swap tx.


================================================
FILE: package.json
================================================
{
  "dependencies": {
    "@openbook-dex/openbook": "^0.0.9",
    "@raydium-io/raydium-sdk": "^1.3.1-beta.46",
    "@solana/spl-token": "^0.3.11",
    "@solana/web3.js": "^1.87.6",
    "borsh": "^2.0.0",
    "bs64": "^0.1.0",
    "node": "^21.2.0"
  },
  "name": "solana",
  "version": "1.0.0",
  "main": "tx.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "description": ""
}


================================================
FILE: strategy1/formatAmmKeysById.js
================================================
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.formatAmmKeysById = void 0;
const raydium_sdk_1 = require("@raydium-io/raydium-sdk");
const web3_js_1 = require("@solana/web3.js");
const config_1 = require("../utils/config.js");
function formatAmmKeysById(id) {
    return __awaiter(this, void 0, void 0, function* () {
        const account = yield config_1.connection.getAccountInfo(new web3_js_1.PublicKey(id));
        if (account === null)
            throw Error(' get id info error ');
        const info = raydium_sdk_1.LIQUIDITY_STATE_LAYOUT_V4.decode(account.data);
        const marketId = info.marketId;
        const marketAccount = yield config_1.connection.getAccountInfo(marketId);
        if (marketAccount === null)
            throw Error(' get market info error');
        const marketInfo = raydium_sdk_1.MARKET_STATE_LAYOUT_V3.decode(marketAccount.data);
        const lpMint = info.lpMint;
        const lpMintAccount = yield config_1.connection.getAccountInfo(lpMint);
        if (lpMintAccount === null)
            throw Error(' get lp mint info error');
        const lpMintInfo = raydium_sdk_1.SPL_MINT_LAYOUT.decode(lpMintAccount.data);
        return {
            id,
            baseMint: info.baseMint.toString(),
            quoteMint: info.quoteMint.toString(),
            lpMint: info.lpMint.toString(),
            baseDecimals: info.baseDecimal.toNumber(),
            quoteDecimals: info.quoteDecimal.toNumber(),
            // lpDecimals: lpMintInfo.decimals,
            lpDecimals: info.baseDecimal.toNumber(),
            version: 4,
            programId: account.owner.toString(),
            authority: raydium_sdk_1.Liquidity.getAssociatedAuthority({ programId: account.owner }).publicKey.toString(),
            openOrders: info.openOrders.toString(),
            targetOrders: info.targetOrders.toString(),
            baseVault: info.baseVault.toString(),
            quoteVault: info.quoteVault.toString(),
            withdrawQueue: info.withdrawQueue.toString(),
            lpVault: info.lpVault.toString(),
            marketVersion: 3,
            marketProgramId: info.marketProgramId.toString(),
            marketId: info.marketId.toString(),
            marketAuthority: raydium_sdk_1.Market.getAssociatedAuthority({ programId: info.marketProgramId, marketId: info.marketId }).publicKey.toString(),
            marketBaseVault: marketInfo.baseVault.toString(),
            marketQuoteVault: marketInfo.quoteVault.toString(),
            marketBids: marketInfo.bids.toString(),
            marketAsks: marketInfo.asks.toString(),
            marketEventQueue: marketInfo.eventQueue.toString(),
            lookupTableAccount: web3_js_1.PublicKey.default.toString()
        };
    });
}
exports.formatAmmKeysById = formatAmmKeysById;

================================================
FILE: strategy1/start1.js
================================================
const web3 = require('@solana/web3.js')
const WebSocket = require('ws')
const swap = require('./swap1.js')
const raydium_sdk_1 = require("@raydium-io/raydium-sdk");
const config = require('../utils/config.js');

const connection = config.connection;
const ws = new WebSocket(config.websocketConnection)
    ws.onopen = () => {
        ws.send(
            JSON.stringify({
                jsonrpc: '2.0',
                id: 1,
                method: 'blockSubscribe',
                params: [{"mentionsAccountOrProgram": "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8"}, {"commitment": "confirmed", "maxSupportedTransactionVersion": 0, "encoding": "jsonParsed"}]
            })
        )
    }

ws.on('message', (evt) => {
    try {
        const buffer = evt.toString('utf8');
        parseBlock(JSON.parse(buffer));
        return;
    } catch (e) {
        console.log(e)
    }
})

function isStatusError(status){
    if(status.hasOwnProperty('Err')){
        return true;
    }
    else if(status.hasOwnProperty('Ok')){
        return false;
    }
}

async function getTx(tx){
    return await connection.getTransaction(tx, {
        "commitment": "confirmed",
        "maxSupportedTransactionVersion": 0,
        "encoded": "jsonParsed"
    })
}

let swapped = false;
function parseBlock(transaction){
    try{
        const tx = transaction.params.result.value.block.transactions;
        for(let i = 0; i < tx.length; i++){
            if(tx[i].meta.innerInstructions !== undefined && tx[i].meta.innerInstructions.length !== 0){
                if(tx[i].meta.innerInstructions[0].instructions.length === 32 && !isStatusError(tx[i].meta.status)){
                    const signature = tx[i].transaction.signatures[0];
                    console.log(tx[i].transaction.signatures);
                    if(swapped === false){
                        swapped = true;
                        let now = new Date();
                        let utcString = now.toUTCString();
                        console.log(utcString);
                        ws.close();
                    }
                }
            }
    }
    } catch(error){
        console.log("searching...")
    }
}

async function getJsonPoolInfo(tx){
    // market account
    const marketAccount = await connection.getAccountInfo(new web3.PublicKey(tx.transaction.message.accountKeys[19].pubkey));
    const marketInfo = raydium_sdk_1.MARKET_STATE_LAYOUT_V3.decode(marketAccount.data);
    // get decimals
    const getTokenAccount = await connection.getParsedAccountInfo(new web3.PublicKey(tx.transaction.message.accountKeys[17].pubkey));
    const decimals = getTokenAccount.value.data.parsed.info.decimals;
    // create poolKeys object
    const keys = {
        id: tx.transaction.message.accountKeys[2].pubkey,
        baseMint: tx.transaction.message.accountKeys[17].pubkey,
        quoteMint: 'So11111111111111111111111111111111111111112',
        lpMint: tx.transaction.message.accountKeys[4].pubkey,
        baseDecimals: decimals,
        quoteDecimals: 9,
        lpDecimals: decimals,
        version: 4,
        programId: '675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8',
        authority: '5Q544fKrFoe6tsEbD7S8EmxGTJYAKtTVhAW5Q5pge4j1',
        openOrders: tx.transaction.message.accountKeys[3].pubkey,
        targetOrders: tx.transaction.message.accountKeys[7].pubkey,
        baseVault: tx.transaction.message.accountKeys[5].pubkey,
        quoteVault: tx.transaction.message.accountKeys[6].pubkey,
        withdrawQueue: '11111111111111111111111111111111',
        lpVault: '11111111111111111111111111111111',
        marketVersion: 3,
        marketProgramId: tx.transaction.message.accountKeys[22].pubkey,
        marketId: tx.transaction.message.accountKeys[19].pubkey,
        marketAuthority: raydium_sdk_1.Market.getAssociatedAuthority({ programId: new web3.PublicKey(tx.transaction.message.accountKeys[22].pubkey), marketId: new web3.PublicKey(tx.transaction.message.accountKeys[19].pubkey) }).publicKey.toString(),
        marketBaseVault: marketInfo.baseVault.toString(),
        marketQuoteVault: marketInfo.quoteVault.toString(),
        marketBids: marketInfo.bids.toString(),
        marketAsks: marketInfo.asks.toString(),
        marketEventQueue: marketInfo.eventQueue.toString(),
        lookupTableAccount: '11111111111111111111111111111111'
    }
    // convert poolKeys object to JSON
    const jsonPoolKeys = raydium_sdk_1.jsonInfo2PoolKeys(keys);
    swap.swap(jsonPoolKeys, tx.transaction.message.accountKeys[17].pubkey);
}

================================================
FILE: strategy1/swap1.js
================================================
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const assert_1 = __importDefault(require("assert"));
const raydium_sdk_1 = require("@raydium-io/raydium-sdk");
const config_1 = require("../utils/config.js");
const formatAmmKeysById_1 = require("./formatAmmKeysById.js");
const util_1 = require("../utils/util.js");
const { LAMPORTS_PER_SOL } = require("@solana/web3.js");
const web3 = require("@solana/web3.js");
const connection = config_1.connection;
function swapOnlyAmm(input) {
    return __awaiter(this, void 0, void 0, function* () {
        const outputToken = new raydium_sdk_1.Token(raydium_sdk_1.TOKEN_PROGRAM_ID, new web3.PublicKey(input.tokenAddress), input.poolKeys.lpDecimals);
        const { innerTransactions } = yield raydium_sdk_1.Liquidity.makeSwapInstructionSimple({
            connection: config_1.connection,
            poolKeys: input.poolKeys,
            userKeys: {
                tokenAccounts: input.walletTokenAccounts,
                owner: input.wallet.publicKey,
            },
            amountIn: input.inputTokenAmount,
            amountOut: new raydium_sdk_1.TokenAmount(outputToken, 1),
            fixedSide: 'in',
            makeTxVersion: config_1.makeTxVersion,
        });
        return { txids: yield (0, util_1.buildAndSendTx)(innerTransactions) };
    });
}

const buyAmtSol = config_1.amtBuySol;
function swap(poolKeys, tokenAddress) {
    return __awaiter(this, void 0, void 0, function* () {
        const ownerAddress = config_1.ownerAddress;
        const inputToken = new raydium_sdk_1.Token(raydium_sdk_1.TOKEN_PROGRAM_ID, new web3_js_1.PublicKey('So11111111111111111111111111111111111111112'), 9, 'WSOL', 'WSOL'); // WSOL
        const inputTokenAmount = new raydium_sdk_1.TokenAmount(inputToken, LAMPORTS_PER_SOL * buyAmtSol);
        const slippage = new raydium_sdk_1.Percent(1, 100);
        const walletTokenAccounts = yield (0, util_1.getWalletTokenAccount)(config_1.connection, config_1.wallet.publicKey);
        swapOnlyAmm({
            poolKeys,
            tokenAddress, 
            inputTokenAmount,
            slippage,
            walletTokenAccounts,
            wallet: config_1.wallet,
        }).then(({ txids }) => {
            /** continue with txids */
            console.log('txids', txids);
            if(txids.length === 1){
                monitorTokenSell(txids[0], tokenAddress, ownerAddress, poolKeys.baseVault.toString(), poolKeys.quoteVault.toString());
            }
        }).catch(error => {
            console.log(error);
            swap(poolKeys, tokenAddress);
        })
    });
}
exports.swap = swap

async function getTx(tx){
    return await connection.getTransaction(tx, {
        maxSupportedTransactionVersion: 0,
        commitment: "confirmed"
    })
}

async function getBalances(tx, tokenAddress, ownerAddress){
    let validTx = await getTx(tx);
    while(validTx === null){
        validTx = await getTx(tx);
        if(validTx !== null){
            for(const account of validTx.meta.postTokenBalances){
                if(account.mint === tokenAddress && account.owner === ownerAddress){
                    return account.uiTokenAmount.uiAmount;
                }
            }
        }
    }
}

async function getTokenPriceInSol(baseVault, quoteVault){
    const baseVaultAccount = await connection.getTokenAccountBalance(new web3.PublicKey(baseVault));
    const quoteVaultAccount = await connection.getTokenAccountBalance(new web3.PublicKey(quoteVault));
    const baseVaultAccountAmount = baseVaultAccount.value.uiAmount;
    const quoteVaultAccountAmount = quoteVaultAccount.value.uiAmount;
    return (quoteVaultAccountAmount / baseVaultAccountAmount);
}

async function monitorTokenSell(tx, tokenAddress, ownerAddress, baseVault, quoteVault){
    const tokenBalance = await getBalances(tx, tokenAddress, ownerAddress);
    const buyPrice = (buyAmtSol / tokenBalance);
    monitorToken(buyPrice, baseVault, quoteVault);
}

async function monitorToken(buyPrice, baseVault, quoteVault){
    let interval = setInterval(async () => {
        const currentPrice = await getTokenPriceInSol(baseVault, quoteVault);
        console.log("buy price: " + buyPrice + " current price: " + currentPrice);
        const percentIncrease = ((buyPrice - currentPrice) / buyPrice) * 100;
        console.log("percent increase: " + percentIncrease);
    }, 500)
}

================================================
FILE: strategy2/derivePoolKeys.js
================================================
const web3 = require('@solana/web3.js');
const raydium_sdk_1 = require("@raydium-io/raydium-sdk");
const spl = require('@solana/spl-token');
const {Market} = require('@openbook-dex/openbook');
const config = require('../utils/config.js');

const connection = config.connection;

const wsolAddress = 'So11111111111111111111111111111111111111112';
const openbookProgramId = new web3.PublicKey('srmqPvymJeFKQ4zGQed1GFppgkRHL9kaELCbyksJtPX');

const rayProgram = new web3.PublicKey('675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8');
const myAccount = new web3.PublicKey(config.ownerAddress);

async function derivePoolKeys(id){
    console.log(id);
    const marketId = new web3.PublicKey(id);

    const marketInfo = await getMarketInfo(marketId);
    const marketDeco = await getDecodedData(marketInfo);

    const baseMint = marketDeco.baseMint;
    const baseMintData = await getMintData(baseMint);
    const baseDecimals = await getDecimals(baseMintData);
    const ownerBaseAta = await getOwnerAta(baseMint, myAccount);
    
    const quoteMint = marketDeco.quoteMint;
    const quoteMintData = await getMintData(quoteMint);
    const quoteDecimals = await getDecimals(quoteMintData);
    const ownerQuoteAta = await getOwnerAta(quoteMint, myAccount);

    const authority = (raydium_sdk_1.findProgramAddress([Buffer.from([97, 109, 109, 32, 97, 117, 116, 104, 111, 114, 105, 116, 121])], rayProgram))['publicKey'];
    // const marketAuthority = getVaultSigner(marketId, marketDeco);

    const poolKeys = {
        id: raydium_sdk_1.findProgramAddress([rayProgram.toBuffer(), marketId.toBuffer(), Buffer.from('amm_associated_seed', 'utf-8')], rayProgram)['publicKey'],
        baseMint: baseMint,
        quoteMint, quoteMint,
        lpMint: raydium_sdk_1.findProgramAddress([rayProgram.toBuffer(), marketId.toBuffer(), Buffer.from('lp_mint_associated_seed', 'utf-8')], rayProgram)['publicKey'],
        baseDecimals: baseDecimals,
        quoteDecimals: quoteDecimals,
        lpDecimals: baseDecimals,
        version: 4,
        programId: rayProgram,
        authority: authority,
        openOrders: raydium_sdk_1.findProgramAddress([rayProgram.toBuffer(), marketId.toBuffer(), Buffer.from('open_order_associated_seed', 'utf-8')], rayProgram)['publicKey'],
        targetOrders: raydium_sdk_1.findProgramAddress([rayProgram.toBuffer(), marketId.toBuffer(), Buffer.from('target_associated_seed', 'utf-8')], rayProgram)['publicKey'],
        baseVault: raydium_sdk_1.findProgramAddress([rayProgram.toBuffer(), marketId.toBuffer(), Buffer.from('coin_vault_associated_seed', 'utf-8')], rayProgram)['publicKey'],
        quoteVault: raydium_sdk_1.findProgramAddress([rayProgram.toBuffer(), marketId.toBuffer(), Buffer.from('pc_vault_associated_seed', 'utf-8')], rayProgram)['publicKey'],
        // withdrawQueue: raydium_sdk_1.findProgramAddress([rayProgram.toBuffer(), marketId.toBuffer(), Buffer.from('withdraw_associated_seed', 'utf-8')], rayProgram)['publicKey'],
        // lpVault: raydium_sdk_1.findProgramAddress([rayProgram.toBuffer(), marketId.toBuffer(), Buffer.from('temp_lp_token_associated_seed', 'utf-8')], rayProgram)['publicKey'],
        withdrawQueue: new web3.PublicKey('11111111111111111111111111111111'),
        lpVault: new web3.PublicKey('11111111111111111111111111111111'),
        marketVersion: 3,
        marketProgramId: openbookProgramId,
        // marketProgramId: new web3.PublicKey('9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin'),
        marketId: marketId,
        marketAuthority: raydium_sdk_1.Market.getAssociatedAuthority({ programId: new web3.PublicKey('9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin'), marketId: marketId }).publicKey,
        marketBaseVault: marketDeco.baseVault,
        marketQuoteVault: marketDeco.quoteVault,
        marketBids: marketDeco.bids,
        marketAsks: marketDeco.asks,
        marketEventQueue: marketDeco.eventQueue,
        // ownerBaseAta: ownerBaseAta,
        // ownerQuoteAta: ownerQuoteAta,
        // marketAuthority: marketAuthority,
        // coinVault: raydium_sdk_1.findProgramAddress([rayProgram.toBuffer(), marketId.toBuffer(), Buffer.from('pc_vault_associated_seed', 'utf-8')], rayProgram)['publicKey'],
        lookupTableAccount: web3.PublicKey.default
    };
    return poolKeys;
}
exports.derivePoolKeys = derivePoolKeys;

async function getMarketInfo(marketId){
    const marketInfo = await connection.getAccountInfo(marketId);
    return marketInfo;
}

async function getDecodedData(marketInfo){
    return await Market.getLayout(openbookProgramId).decode(marketInfo.data);
}

async function getMintData(mint){
    return await connection.getAccountInfo(mint);
}

async function getDecimals(mintData){
    return raydium_sdk_1.SPL_MINT_LAYOUT.decode(mintData.data).decimals;
}

async function getOwnerAta(mint, publicKey){
    const foundAta = web3.PublicKey.findProgramAddressSync([publicKey.toBuffer(), spl.TOKEN_PROGRAM_ID.toBuffer(), mint.toBuffer()], spl.ASSOCIATED_TOKEN_PROGRAM_ID)[0];
    return foundAta;
}

function getVaultSigner(marketId, marketDeco){
    const seeds = [marketId.toBuffer()];
    const seedsWithNonce = seeds.concat(Buffer.from([Number(marketDeco.vaultSignerNonce.toString())]), Buffer.alloc(7));
    return web3.PublicKey.createProgramAddressSync(seedsWithNonce, openbookProgramId);
}

================================================
FILE: strategy2/start2.js
================================================
const web3 = require('@solana/web3.js');
const raydium_sdk_1 = require("@raydium-io/raydium-sdk");
const WebSocket = require('ws');
const derivePoolKeys = require('./derivePoolKeys.js');
const swap = require('./swap2.js');
const config = require('../utils/config.js');

const connection = config.connection;

const ws = new WebSocket(config.websocketConnection)
    ws.onopen = () => {
        ws.send(
            JSON.stringify({
                jsonrpc: '2.0',
                id: 1,
                method: 'blockSubscribe',
                params: [{"mentionsAccountOrProgram": "srmqPvymJeFKQ4zGQed1GFppgkRHL9kaELCbyksJtPX"}, {"commitment": "confirmed", "maxSupportedTransactionVersion": 0, "encoding": "jsonParsed"}]
            })
        )
    }

ws.on('message', (evt) => {
    try {
        const buffer = evt.toString('utf8');
        parseTxs(JSON.parse(buffer));
        return;
    } catch (e) {
        console.log(e)
    }
})

function parseTxs(txsFromBlock){
    if(txsFromBlock.params === undefined){
        return;
    }
    const allTx = txsFromBlock.params.result.value.block.transactions;
    for(const tx of allTx){
        if(parseLogs(tx.meta.logMessages) && tx.transaction.message.accountKeys.length === 13 && tx.transaction.message.instructions.length === 6){
            ws.close();
            console.log(tx.transaction.signatures)
            parseAccountKeys(tx.transaction.message.accountKeys, tx.transaction.signatures);
        }
    }
}

function parseLogs(logs){
    let invoke = 0;
    let consumed = 0;
    let success = 0;
    for(const log of logs){
        if(log.includes("Program srmqPvymJeFKQ4zGQed1GFppgkRHL9kaELCbyksJtPX invoke")){
            invoke += 1;
        }
        if(log.includes("Program srmqPvymJeFKQ4zGQed1GFppgkRHL9kaELCbyksJtPX consumed")){
            consumed += 1;
        }
        if(log.includes("Program srmqPvymJeFKQ4zGQed1GFppgkRHL9kaELCbyksJtPX success")){
            success += 1;
        }
    }
    if(invoke === 1 && consumed === 1 && success === 1){
        return true;
    } else{
        return false;
    }
}

async function parseAccountKeys(keys, signature){
    let marketId = null;
    for(const key of keys){
        console.log(key);
        const keyData = await connection.getAccountInfo(new web3.PublicKey(key.pubkey));
        if(keyData !== null && keyData.data.length === 388){
            marketId = key.pubkey;
        }
    }
    if(marketId === null){
        parseAccountKeys(keys);
    } else{
        const poolKeys = await derivePoolKeys.derivePoolKeys(marketId);
        swap.swap(poolKeys, signature);
    }
}

================================================
FILE: strategy2/swap2.js
================================================
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const assert_1 = __importDefault(require("assert"));
const raydium_sdk_1 = require("@raydium-io/raydium-sdk");
const config_1 = require("../utils/config.js");
const util_1 = require("../utils/util.js");
const { LAMPORTS_PER_SOL } = require("@solana/web3.js");
const web3 = require("@solana/web3.js");
const connection = config_1.connection;
function swapOnlyAmm(input) {
    return __awaiter(this, void 0, void 0, function* () {
        const outputToken = new raydium_sdk_1.Token(raydium_sdk_1.TOKEN_PROGRAM_ID, new web3.PublicKey(input.tokenAddress), input.poolKeys.lpDecimals);
        const { innerTransactions } = yield raydium_sdk_1.Liquidity.makeSwapInstructionSimple({
            connection: config_1.connection,
            poolKeys: input.poolKeys,
            userKeys: {
                tokenAccounts: input.walletTokenAccounts,
                owner: input.wallet.publicKey,
            },
            amountIn: input.inputTokenAmount,
            amountOut: new raydium_sdk_1.TokenAmount(outputToken, 1),
            fixedSide: 'in',
            makeTxVersion: config_1.makeTxVersion,
        });
        return { txids: yield (0, util_1.buildAndSendTx)(innerTransactions) };
    });
}

const buyAmtSol = config_1.amtBuySol;
function swap(poolKeys, signature) {
    return __awaiter(this, void 0, void 0, function* () {
        const ownerAddress = config_1.ownerAddress;
        const inputToken = new raydium_sdk_1.Token(raydium_sdk_1.TOKEN_PROGRAM_ID, new web3_js_1.PublicKey('So11111111111111111111111111111111111111112'), 9, 'WSOL', 'WSOL'); // WSOL
        const inputTokenAmount = new raydium_sdk_1.TokenAmount(inputToken, LAMPORTS_PER_SOL * buyAmtSol);
        const slippage = new raydium_sdk_1.Percent(1, 100);
        const walletTokenAccounts = yield (0, util_1.getWalletTokenAccount)(config_1.connection, config_1.wallet.publicKey);
        swapOnlyAmm({
            poolKeys,
            tokenAddress: poolKeys.baseMint.toString(), 
            inputTokenAmount,
            slippage,
            walletTokenAccounts,
            wallet: config_1.wallet,
        }).then(({ txids }) => {
            /** continue with txids */
            console.log('txids', txids);
            if(txids.length === 1){
                monitorTokenSell(txids[0], poolKeys.baseMint.toString(), ownerAddress, poolKeys.baseVault.toString(), poolKeys.quoteVault.toString());
            }
        }).catch(error => {
            console.log(signature);
            console.log(error);
            swap(poolKeys, poolKeys.baseMint.toString());
        })
    });
}
exports.swap = swap

async function getTx(tx){
    return await connection.getTransaction(tx, {
        maxSupportedTransactionVersion: 0,
        commitment: "confirmed"
    })
}

async function getBalances(tx, tokenAddress, ownerAddress){
    let validTx = await getTx(tx);
    while(validTx === null){
        validTx = await getTx(tx);
        if(validTx !== null){
            for(const account of validTx.meta.postTokenBalances){
                if(account.mint === tokenAddress && account.owner === ownerAddress){
                    return account.uiTokenAmount.uiAmount;
                }
            }
        }
    }
}

async function getTokenPriceInSol(baseVault, quoteVault){
    const baseVaultAccount = await connection.getTokenAccountBalance(new web3.PublicKey(baseVault));
    const quoteVaultAccount = await connection.getTokenAccountBalance(new web3.PublicKey(quoteVault));
    const baseVaultAccountAmount = baseVaultAccount.value.uiAmount;
    const quoteVaultAccountAmount = quoteVaultAccount.value.uiAmount;
    return (quoteVaultAccountAmount / baseVaultAccountAmount);
}

async function monitorTokenSell(tx, tokenAddress, ownerAddress, baseVault, quoteVault){
    const tokenBalance = await getBalances(tx, tokenAddress, ownerAddress);
    const buyPrice = (buyAmtSol / tokenBalance);
    monitorToken(buyPrice, baseVault, quoteVault);
}

async function monitorToken(buyPrice, baseVault, quoteVault){
    let interval = setInterval(async () => {
        const currentPrice = await getTokenPriceInSol(baseVault, quoteVault);
        console.log("buy price: " + buyPrice + " current price: " + currentPrice);
        const percentIncrease = ((buyPrice - currentPrice) / buyPrice) * 100;
        console.log("percent increase: " + percentIncrease);
    }, 500)
}

================================================
FILE: utils/config.js
================================================
const bs58 = require('bs58');
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DEFAULT_TOKEN = exports.addLookupTableInfo = exports.makeTxVersion = exports.RAYDIUM_MAINNET_API = exports.ENDPOINT = exports.PROGRAMIDS = exports.connection = exports.wallet = exports.rpcToken = exports.rpcUrl = void 0;
const raydium_sdk_1 = require("@raydium-io/raydium-sdk");
const web3_js_1 = require("@solana/web3.js");
exports.ownerAddress = '<YOUR WALLET PUBLIC KEY>';
exports.wallet = web3_js_1.Keypair.fromSecretKey(bs58.decode('<YOUR PRIVATE KEY>'));
exports.connection = new web3_js_1.Connection('<YOUR RPC CONNECTION URL>');
exports.websocketConnection = '<YOUR WEBSOCKET CONNECTION URL>';
exports.amtBuySol = '<INTEGER AMOUNT OF SOL TO USER PER SWAP, EX. 0.001>';
exports.PROGRAMIDS = raydium_sdk_1.MAINNET_PROGRAM_ID;
exports.ENDPOINT = raydium_sdk_1.ENDPOINT;
exports.RAYDIUM_MAINNET_API = raydium_sdk_1.RAYDIUM_MAINNET;
exports.makeTxVersion = raydium_sdk_1.TxVersion.V0; // LEGACY
exports.addLookupTableInfo = raydium_sdk_1.LOOKUP_TABLE_CACHE;


================================================
FILE: utils/decoderaylog.js
================================================
const web3 = require('@solana/web3.js');
const WebSocket = require('ws');
const raydium_sdk_1 = require("@raydium-io/raydium-sdk");
const bs64 = require('bs64');
const config = require('./config');

const connection = config.connection;

const feeAddress = '7YttLkHDoNj9wyDur5pM1ejNaAvT9X4eqaYcHQqtj2G5'
const rayProgram = new web3.PublicKey('675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8');

const ws = new WebSocket(config.websocketConnection)
    ws.onopen = () => {
        ws.send(
            JSON.stringify({
                jsonrpc: '2.0',
                id: 1,
                method: 'logsSubscribe',
                params: [{"mentions": [feeAddress]}, {"commitment": "processed"}]
            })
        )
    }

ws.on('message', (evt) => {
    try {
        const buffer = evt.toString('utf8');
        parseLogs(JSON.parse(buffer));
        return;
    } catch (e) {
        console.log(e)
    }
})

function parseLogs(buffer){
    if(buffer.params === undefined){
        return;
    }
    let now = new Date();
    let utcString = now.toUTCString();
    console.log(utcString);
    const allLogs = buffer.params.result.value.logs;
    for(const log of allLogs){
        if(log.includes("ray_log")){
            const rayLogSplit = log.split(" ");
            const rayLog = rayLogSplit[3];
            const logData = Buffer.from(rayLog, "base64");
            const market = new web3.PublicKey(logData.subarray(75 - 32), 75);
            console.log(market)
            const pool = raydium_sdk_1.findProgramAddress([rayProgram.toBuffer(), market.toBuffer(), Buffer.from('amm_associated_seed', 'utf-8')], rayProgram)['publicKey'];
            console.log(pool);
        }
    }
}

================================================
FILE: utils/util.js
================================================
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.sleepTime = exports.getATAAddress = exports.buildAndSendTx = exports.getWalletTokenAccount = exports.sendTx = void 0;
const raydium_sdk_1 = require("@raydium-io/raydium-sdk");
const web3_js_1 = require("@solana/web3.js");
const config_1 = require("./config");
function sendTx(connection, payer, txs, options) {
    return __awaiter(this, void 0, void 0, function* () {
        const txids = [];
        for (const iTx of txs) {
            if (iTx instanceof web3_js_1.VersionedTransaction) {
                iTx.sign([payer]);
                txids.push(yield connection.sendTransaction(iTx, options));
            }
            else {
                txids.push(yield connection.sendTransaction(iTx, [payer], options));
            }
        }
        return txids;
    });
}
exports.sendTx = sendTx;
function getWalletTokenAccount(connection, wallet) {
    return __awaiter(this, void 0, void 0, function* () {
        const walletTokenAccount = yield connection.getTokenAccountsByOwner(wallet, {
            programId: raydium_sdk_1.TOKEN_PROGRAM_ID,
        });
        return walletTokenAccount.value.map((i) => ({
            pubkey: i.pubkey,
            programId: i.account.owner,
            accountInfo: raydium_sdk_1.SPL_ACCOUNT_LAYOUT.decode(i.account.data),
        }));
    });
}
exports.getWalletTokenAccount = getWalletTokenAccount;
function buildAndSendTx(innerSimpleV0Transaction, options) {
    return __awaiter(this, void 0, void 0, function* () {
        const willSendTx = yield (0, raydium_sdk_1.buildSimpleTransaction)({
            connection: config_1.connection,
            makeTxVersion: config_1.makeTxVersion,
            payer: config_1.wallet.publicKey,
            innerTransactions: innerSimpleV0Transaction,
            addLookupTableInfo: config_1.addLookupTableInfo,
        });
        return yield sendTx(config_1.connection, config_1.wallet, willSendTx, options);
    });
}
exports.buildAndSendTx = buildAndSendTx;
function getATAAddress(programId, owner, mint) {
    const { publicKey, nonce } = (0, raydium_sdk_1.findProgramAddress)([owner.toBuffer(), programId.toBuffer(), mint.toBuffer()], new web3_js_1.PublicKey("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"));
    return { publicKey, nonce };
}
exports.getATAAddress = getATAAddress;
function sleepTime(ms) {
    return __awaiter(this, void 0, void 0, function* () {
        console.log((new Date()).toLocaleString(), 'sleepTime', ms);
        return new Promise(resolve => setTimeout(resolve, ms));
    });
}
exports.sleepTime = sleepTime;
//# sourceMappingURL=util.js.map
Download .txt
gitextract_y947oijx/

├── .gitignore
├── README.md
├── package.json
├── strategy1/
│   ├── formatAmmKeysById.js
│   ├── start1.js
│   └── swap1.js
├── strategy2/
│   ├── derivePoolKeys.js
│   ├── start2.js
│   └── swap2.js
└── utils/
    ├── config.js
    ├── decoderaylog.js
    └── util.js
Download .txt
SYMBOL INDEX (51 symbols across 8 files)

FILE: strategy1/formatAmmKeysById.js
  function adopt (line 3) | function adopt(value) { return value instanceof P ? value : new P(functi...
  function fulfilled (line 5) | function fulfilled(value) { try { step(generator.next(value)); } catch (...
  function rejected (line 6) | function rejected(value) { try { step(generator["throw"](value)); } catc...
  function step (line 7) | function step(result) { result.done ? resolve(result.value) : adopt(resu...
  function formatAmmKeysById (line 16) | function formatAmmKeysById(id) {

FILE: strategy1/start1.js
  function isStatusError (line 30) | function isStatusError(status){
  function getTx (line 39) | async function getTx(tx){
  function parseBlock (line 48) | function parseBlock(transaction){
  function getJsonPoolInfo (line 71) | async function getJsonPoolInfo(tx){

FILE: strategy1/swap1.js
  function adopt (line 3) | function adopt(value) { return value instanceof P ? value : new P(functi...
  function fulfilled (line 5) | function fulfilled(value) { try { step(generator.next(value)); } catch (...
  function rejected (line 6) | function rejected(value) { try { step(generator["throw"](value)); } catc...
  function step (line 7) | function step(result) { result.done ? resolve(result.value) : adopt(resu...
  function swapOnlyAmm (line 23) | function swapOnlyAmm(input) {
  function swap (line 43) | function swap(poolKeys, tokenAddress) {
  function getTx (line 71) | async function getTx(tx){
  function getBalances (line 78) | async function getBalances(tx, tokenAddress, ownerAddress){
  function getTokenPriceInSol (line 92) | async function getTokenPriceInSol(baseVault, quoteVault){
  function monitorTokenSell (line 100) | async function monitorTokenSell(tx, tokenAddress, ownerAddress, baseVaul...
  function monitorToken (line 106) | async function monitorToken(buyPrice, baseVault, quoteVault){

FILE: strategy2/derivePoolKeys.js
  function derivePoolKeys (line 15) | async function derivePoolKeys(id){
  function getMarketInfo (line 74) | async function getMarketInfo(marketId){
  function getDecodedData (line 79) | async function getDecodedData(marketInfo){
  function getMintData (line 83) | async function getMintData(mint){
  function getDecimals (line 87) | async function getDecimals(mintData){
  function getOwnerAta (line 91) | async function getOwnerAta(mint, publicKey){
  function getVaultSigner (line 96) | function getVaultSigner(marketId, marketDeco){

FILE: strategy2/start2.js
  function parseTxs (line 32) | function parseTxs(txsFromBlock){
  function parseLogs (line 46) | function parseLogs(logs){
  function parseAccountKeys (line 68) | async function parseAccountKeys(keys, signature){

FILE: strategy2/swap2.js
  function adopt (line 3) | function adopt(value) { return value instanceof P ? value : new P(functi...
  function fulfilled (line 5) | function fulfilled(value) { try { step(generator.next(value)); } catch (...
  function rejected (line 6) | function rejected(value) { try { step(generator["throw"](value)); } catc...
  function step (line 7) | function step(result) { result.done ? resolve(result.value) : adopt(resu...
  function swapOnlyAmm (line 22) | function swapOnlyAmm(input) {
  function swap (line 42) | function swap(poolKeys, signature) {
  function getTx (line 71) | async function getTx(tx){
  function getBalances (line 78) | async function getBalances(tx, tokenAddress, ownerAddress){
  function getTokenPriceInSol (line 92) | async function getTokenPriceInSol(baseVault, quoteVault){
  function monitorTokenSell (line 100) | async function monitorTokenSell(tx, tokenAddress, ownerAddress, baseVaul...
  function monitorToken (line 106) | async function monitorToken(buyPrice, baseVault, quoteVault){

FILE: utils/decoderaylog.js
  function parseLogs (line 34) | function parseLogs(buffer){

FILE: utils/util.js
  function adopt (line 3) | function adopt(value) { return value instanceof P ? value : new P(functi...
  function fulfilled (line 5) | function fulfilled(value) { try { step(generator.next(value)); } catch (...
  function rejected (line 6) | function rejected(value) { try { step(generator["throw"](value)); } catc...
  function step (line 7) | function step(result) { result.done ? resolve(result.value) : adopt(resu...
  function sendTx (line 16) | function sendTx(connection, payer, txs, options) {
  function getWalletTokenAccount (line 32) | function getWalletTokenAccount(connection, wallet) {
  function buildAndSendTx (line 45) | function buildAndSendTx(innerSimpleV0Transaction, options) {
  function getATAAddress (line 58) | function getATAAddress(programId, owner, mint) {
  function sleepTime (line 63) | function sleepTime(ms) {
Condensed preview — 12 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (38K chars).
[
  {
    "path": ".gitignore",
    "chars": 25,
    "preview": "**/node_modules\n.DS_Store"
  },
  {
    "path": "README.md",
    "chars": 3484,
    "preview": "# Solana SPL-Token Sniper\n## Overview\nThis project is Solana SPL-Token sniper which aims to swap (purchase) new Raydium "
  },
  {
    "path": "package.json",
    "chars": 450,
    "preview": "{\n  \"dependencies\": {\n    \"@openbook-dex/openbook\": \"^0.0.9\",\n    \"@raydium-io/raydium-sdk\": \"^1.3.1-beta.46\",\n    \"@sol"
  },
  {
    "path": "strategy1/formatAmmKeysById.js",
    "chars": 3509,
    "preview": "\"use strict\";\nvar __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {\n    function ad"
  },
  {
    "path": "strategy1/start1.js",
    "chars": 4537,
    "preview": "const web3 = require('@solana/web3.js')\nconst WebSocket = require('ws')\nconst swap = require('./swap1.js')\nconst raydium"
  },
  {
    "path": "strategy1/swap1.js",
    "chars": 5222,
    "preview": "\"use strict\";\nvar __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {\n    function ad"
  },
  {
    "path": "strategy2/derivePoolKeys.js",
    "chars": 5317,
    "preview": "const web3 = require('@solana/web3.js');\nconst raydium_sdk_1 = require(\"@raydium-io/raydium-sdk\");\nconst spl = require('"
  },
  {
    "path": "strategy2/start2.js",
    "chars": 2614,
    "preview": "const web3 = require('@solana/web3.js');\nconst raydium_sdk_1 = require(\"@raydium-io/raydium-sdk\");\nconst WebSocket = req"
  },
  {
    "path": "strategy2/swap2.js",
    "chars": 5254,
    "preview": "\"use strict\";\nvar __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {\n    function ad"
  },
  {
    "path": "utils/config.js",
    "chars": 1076,
    "preview": "const bs58 = require('bs58');\n\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.DEFAU"
  },
  {
    "path": "utils/decoderaylog.js",
    "chars": 1696,
    "preview": "const web3 = require('@solana/web3.js');\nconst WebSocket = require('ws');\nconst raydium_sdk_1 = require(\"@raydium-io/ray"
  },
  {
    "path": "utils/util.js",
    "chars": 3340,
    "preview": "\"use strict\";\nvar __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {\n    function ad"
  }
]

About this extraction

This page contains the full source code of the danbayk/solana-spl-token-sniper GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 12 files (35.7 KB), approximately 9.2k tokens, and a symbol index with 51 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!