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 = ''; exports.wallet = web3_js_1.Keypair.fromSecretKey(bs58.decode('')); exports.connection = new web3_js_1.Connection(''); exports.websocketConnection = ''; exports.amtBuySol = ''; 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