mirror of
https://github.com/fdundjer/solana-sniper-bot.git
synced 2025-11-09 20:12:06 +10:00
232 lines
8.0 KiB
TypeScript
232 lines
8.0 KiB
TypeScript
import { MarketCache, PoolCache } from './cache';
|
|
import { Listeners } from './listeners';
|
|
import { Connection, KeyedAccountInfo, Keypair } from '@solana/web3.js';
|
|
import { LIQUIDITY_STATE_LAYOUT_V4, MARKET_STATE_LAYOUT_V3, Token, TokenAmount } from '@raydium-io/raydium-sdk';
|
|
import { AccountLayout, getAssociatedTokenAddressSync } from '@solana/spl-token';
|
|
import { Bot, BotConfig } from './bot';
|
|
import { DefaultTransactionExecutor, TransactionExecutor } from './transactions';
|
|
import {
|
|
getToken,
|
|
getWallet,
|
|
logger,
|
|
COMMITMENT_LEVEL,
|
|
RPC_ENDPOINT,
|
|
RPC_WEBSOCKET_ENDPOINT,
|
|
PRE_LOAD_EXISTING_MARKETS,
|
|
LOG_LEVEL,
|
|
CHECK_IF_MUTABLE,
|
|
CHECK_IF_MINT_IS_RENOUNCED,
|
|
CHECK_IF_FREEZABLE,
|
|
CHECK_IF_BURNED,
|
|
QUOTE_MINT,
|
|
MAX_POOL_SIZE,
|
|
MIN_POOL_SIZE,
|
|
QUOTE_AMOUNT,
|
|
PRIVATE_KEY,
|
|
USE_SNIPE_LIST,
|
|
ONE_TOKEN_AT_A_TIME,
|
|
AUTO_SELL_DELAY,
|
|
MAX_SELL_RETRIES,
|
|
AUTO_SELL,
|
|
MAX_BUY_RETRIES,
|
|
AUTO_BUY_DELAY,
|
|
COMPUTE_UNIT_LIMIT,
|
|
COMPUTE_UNIT_PRICE,
|
|
CACHE_NEW_MARKETS,
|
|
TAKE_PROFIT,
|
|
STOP_LOSS,
|
|
BUY_SLIPPAGE,
|
|
SELL_SLIPPAGE,
|
|
PRICE_CHECK_DURATION,
|
|
PRICE_CHECK_INTERVAL,
|
|
SNIPE_LIST_REFRESH_INTERVAL,
|
|
TRANSACTION_EXECUTOR,
|
|
WARP_FEE,
|
|
FILTER_CHECK_INTERVAL,
|
|
FILTER_CHECK_DURATION,
|
|
CONSECUTIVE_FILTER_MATCHES,
|
|
} from './helpers';
|
|
import { version } from './package.json';
|
|
import { WarpTransactionExecutor } from './transactions/warp-transaction-executor';
|
|
|
|
const connection = new Connection(RPC_ENDPOINT, {
|
|
wsEndpoint: RPC_WEBSOCKET_ENDPOINT,
|
|
commitment: COMMITMENT_LEVEL,
|
|
});
|
|
|
|
function printDetails(wallet: Keypair, quoteToken: Token, bot: Bot) {
|
|
logger.info(`
|
|
.. :-===++++-
|
|
.-==+++++++- =+++++++++-
|
|
..:::--===+=.=: .+++++++++++:=+++++++++:
|
|
.==+++++++++++++++=:+++: .+++++++++++.=++++++++-.
|
|
.-+++++++++++++++=:=++++- .+++++++++=:.=+++++-::-.
|
|
-:+++++++++++++=:+++++++- .++++++++-:- =+++++=-:
|
|
-:++++++=++++=:++++=++++= .++++++++++- =+++++:
|
|
-:++++-:=++=:++++=:-+++++:+++++====--:::::::.
|
|
::=+-:::==:=+++=::-:--::::::::::---------::.
|
|
::-: .::::::::. --------:::..
|
|
:- .:.-:::.
|
|
|
|
WARP DRIVE ACTIVATED 🚀🐟
|
|
Made with ❤️ by humans.
|
|
Version: ${version}
|
|
`);
|
|
|
|
const botConfig = bot.config;
|
|
|
|
logger.info('------- CONFIGURATION START -------');
|
|
logger.info(`Wallet: ${wallet.publicKey.toString()}`);
|
|
|
|
logger.info('- Bot -');
|
|
|
|
logger.info(`Using warp: ${bot.isWarp}`);
|
|
if (bot.isWarp) {
|
|
logger.info(`Warp fee: ${WARP_FEE}`);
|
|
} else {
|
|
logger.info(`Compute Unit limit: ${botConfig.unitLimit}`);
|
|
logger.info(`Compute Unit price (micro lamports): ${botConfig.unitPrice}`);
|
|
}
|
|
|
|
logger.info(`Single token at the time: ${botConfig.oneTokenAtATime}`);
|
|
logger.info(`Pre load existing markets: ${PRE_LOAD_EXISTING_MARKETS}`);
|
|
logger.info(`Cache new markets: ${CACHE_NEW_MARKETS}`);
|
|
logger.info(`Log level: ${LOG_LEVEL}`);
|
|
|
|
logger.info('- Buy -');
|
|
logger.info(`Buy amount: ${botConfig.quoteAmount.toFixed()} ${botConfig.quoteToken.name}`);
|
|
logger.info(`Auto buy delay: ${botConfig.autoBuyDelay} ms`);
|
|
logger.info(`Max buy retries: ${botConfig.maxBuyRetries}`);
|
|
logger.info(`Buy amount (${quoteToken.symbol}): ${botConfig.quoteAmount.toFixed()}`);
|
|
logger.info(`Buy slippage: ${botConfig.buySlippage}%`);
|
|
|
|
logger.info('- Sell -');
|
|
logger.info(`Auto sell: ${AUTO_SELL}`);
|
|
logger.info(`Auto sell delay: ${botConfig.autoSellDelay} ms`);
|
|
logger.info(`Max sell retries: ${botConfig.maxSellRetries}`);
|
|
logger.info(`Sell slippage: ${botConfig.sellSlippage}%`);
|
|
logger.info(`Price check interval: ${botConfig.priceCheckInterval} ms`);
|
|
logger.info(`Price check duration: ${botConfig.priceCheckDuration} ms`);
|
|
logger.info(`Take profit: ${botConfig.takeProfit}%`);
|
|
logger.info(`Stop loss: ${botConfig.stopLoss}%`);
|
|
|
|
logger.info('- Filters -');
|
|
logger.info(`Snipe list: ${botConfig.useSnipeList}`);
|
|
logger.info(`Snipe list refresh interval: ${SNIPE_LIST_REFRESH_INTERVAL} ms`);
|
|
logger.info(`Filter check interval: ${botConfig.filterCheckInterval} ms`);
|
|
logger.info(`Filter check duration: ${botConfig.filterCheckDuration} ms`);
|
|
logger.info(`Consecutive filter matches: ${botConfig.consecutiveMatchCount} ms`);
|
|
logger.info(`Check renounced: ${botConfig.checkRenounced}`);
|
|
logger.info(`Check freezable: ${botConfig.checkFreezable}`);
|
|
logger.info(`Check burned: ${botConfig.checkBurned}`);
|
|
logger.info(`Min pool size: ${botConfig.minPoolSize.toFixed()}`);
|
|
logger.info(`Max pool size: ${botConfig.maxPoolSize.toFixed()}`);
|
|
|
|
logger.info('------- CONFIGURATION END -------');
|
|
|
|
logger.info('Bot is running! Press CTRL + C to stop it.');
|
|
}
|
|
|
|
const runListener = async () => {
|
|
logger.level = LOG_LEVEL;
|
|
logger.info('Bot is starting...');
|
|
|
|
const marketCache = new MarketCache(connection);
|
|
const poolCache = new PoolCache();
|
|
let txExecutor: TransactionExecutor;
|
|
|
|
switch (TRANSACTION_EXECUTOR) {
|
|
case 'warp': {
|
|
txExecutor = new WarpTransactionExecutor(WARP_FEE);
|
|
break;
|
|
}
|
|
default: {
|
|
txExecutor = new DefaultTransactionExecutor(connection);
|
|
break;
|
|
}
|
|
}
|
|
|
|
const wallet = getWallet(PRIVATE_KEY.trim());
|
|
const quoteToken = getToken(QUOTE_MINT);
|
|
const botConfig = <BotConfig>{
|
|
wallet,
|
|
quoteAta: getAssociatedTokenAddressSync(quoteToken.mint, wallet.publicKey),
|
|
checkRenounced: CHECK_IF_MINT_IS_RENOUNCED,
|
|
checkFreezable: CHECK_IF_FREEZABLE,
|
|
checkBurned: CHECK_IF_BURNED,
|
|
minPoolSize: new TokenAmount(quoteToken, MIN_POOL_SIZE, false),
|
|
maxPoolSize: new TokenAmount(quoteToken, MAX_POOL_SIZE, false),
|
|
quoteToken,
|
|
quoteAmount: new TokenAmount(quoteToken, QUOTE_AMOUNT, false),
|
|
oneTokenAtATime: ONE_TOKEN_AT_A_TIME,
|
|
useSnipeList: USE_SNIPE_LIST,
|
|
autoSell: AUTO_SELL,
|
|
autoSellDelay: AUTO_SELL_DELAY,
|
|
maxSellRetries: MAX_SELL_RETRIES,
|
|
autoBuyDelay: AUTO_BUY_DELAY,
|
|
maxBuyRetries: MAX_BUY_RETRIES,
|
|
unitLimit: COMPUTE_UNIT_LIMIT,
|
|
unitPrice: COMPUTE_UNIT_PRICE,
|
|
takeProfit: TAKE_PROFIT,
|
|
stopLoss: STOP_LOSS,
|
|
buySlippage: BUY_SLIPPAGE,
|
|
sellSlippage: SELL_SLIPPAGE,
|
|
priceCheckInterval: PRICE_CHECK_INTERVAL,
|
|
priceCheckDuration: PRICE_CHECK_DURATION,
|
|
filterCheckInterval: FILTER_CHECK_INTERVAL,
|
|
filterCheckDuration: FILTER_CHECK_DURATION,
|
|
consecutiveMatchCount: CONSECUTIVE_FILTER_MATCHES,
|
|
};
|
|
|
|
const bot = new Bot(connection, marketCache, poolCache, txExecutor, botConfig);
|
|
const valid = await bot.validate();
|
|
|
|
if (!valid) {
|
|
logger.info('Bot is exiting...');
|
|
process.exit(1);
|
|
}
|
|
|
|
if (PRE_LOAD_EXISTING_MARKETS) {
|
|
await marketCache.init({ quoteToken });
|
|
}
|
|
|
|
const runTimestamp = Math.floor(new Date().getTime() / 1000);
|
|
const listeners = new Listeners(connection);
|
|
await listeners.start({
|
|
walletPublicKey: wallet.publicKey,
|
|
quoteToken,
|
|
autoSell: AUTO_SELL,
|
|
cacheNewMarkets: CACHE_NEW_MARKETS,
|
|
});
|
|
|
|
listeners.on('market', (updatedAccountInfo: KeyedAccountInfo) => {
|
|
const marketState = MARKET_STATE_LAYOUT_V3.decode(updatedAccountInfo.accountInfo.data);
|
|
marketCache.save(updatedAccountInfo.accountId.toString(), marketState);
|
|
});
|
|
|
|
listeners.on('pool', async (updatedAccountInfo: KeyedAccountInfo) => {
|
|
const poolState = LIQUIDITY_STATE_LAYOUT_V4.decode(updatedAccountInfo.accountInfo.data);
|
|
const poolOpenTime = parseInt(poolState.poolOpenTime.toString());
|
|
const exists = await poolCache.get(poolState.baseMint.toString());
|
|
|
|
if (!exists && poolOpenTime > runTimestamp) {
|
|
poolCache.save(updatedAccountInfo.accountId.toString(), poolState);
|
|
await bot.buy(updatedAccountInfo.accountId, poolState);
|
|
}
|
|
});
|
|
|
|
listeners.on('wallet', async (updatedAccountInfo: KeyedAccountInfo) => {
|
|
const accountData = AccountLayout.decode(updatedAccountInfo.accountInfo.data);
|
|
|
|
if (accountData.mint.equals(quoteToken.mint)) {
|
|
return;
|
|
}
|
|
|
|
await bot.sell(updatedAccountInfo.accountId, accountData);
|
|
});
|
|
|
|
printDetails(wallet, quoteToken, bot);
|
|
};
|
|
|
|
runListener();
|