From e5b88a66fbe96e3a36e4b2527faebd64b2dce186 Mon Sep 17 00:00:00 2001 From: OneRobotBoii Date: Tue, 26 Mar 2024 21:20:06 +0700 Subject: [PATCH 1/4] Add ONE_TOKEN_AT_A_TIME flag and update logging --- .env.copy | 3 ++- buy.ts | 17 ++++++++++++++++- constants/constants.ts | 2 +- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/.env.copy b/.env.copy index ca7891c..ecbcc30 100644 --- a/.env.copy +++ b/.env.copy @@ -11,4 +11,5 @@ AUTO_SELL=true MAX_SELL_RETRIES=5 AUTO_SELL_DELAY=1000 LOG_LEVEL=info -MIN_POOL_SIZE=10 \ No newline at end of file +MIN_POOL_SIZE=10 +ONE_TOKEN_AT_A_TIME=true diff --git a/buy.ts b/buy.ts index a0b496c..eacc0ab 100644 --- a/buy.ts +++ b/buy.ts @@ -48,6 +48,7 @@ import { SNIPE_LIST_REFRESH_INTERVAL, USE_SNIPE_LIST, MIN_POOL_SIZE, + ONE_TOKEN_AT_A_TIME, } from './constants'; const solanaConnection = new Connection(RPC_ENDPOINT, { @@ -70,6 +71,9 @@ let quoteToken: Token; let quoteTokenAssociatedAddress: PublicKey; let quoteAmount: TokenAmount; let quoteMinPoolSizeAmount: TokenAmount; +let processingToken: Boolean = false; + + let snipeList: string[] = []; @@ -110,6 +114,7 @@ async function init(): Promise { logger.info( `Min pool size: ${quoteMinPoolSizeAmount.isZero() ? 'false' : quoteMinPoolSizeAmount.toFixed()} ${quoteToken.symbol}`, ); + logger.info(`One token at a time: ${ONE_TOKEN_AT_A_TIME}`); logger.info(`Buy amount: ${quoteAmount.toFixed()} ${quoteToken.symbol}`); logger.info(`Auto sell: ${AUTO_SELL}`); logger.info(`Sell delay: ${AUTO_SELL_DELAY === 0 ? 'false' : AUTO_SELL_DELAY}`); @@ -265,6 +270,8 @@ async function buy(accountId: PublicKey, accountData: LiquidityStateV4): Promise preflightCommitment: COMMITMENT_LEVEL, }); logger.info({ mint: accountData.baseMint, signature }, `Sent buy tx`); + processingToken = true; + const confirmation = await solanaConnection.confirmTransaction( { signature, @@ -288,6 +295,7 @@ async function buy(accountId: PublicKey, accountData: LiquidityStateV4): Promise } } catch (e) { logger.debug(e); + processingToken = false; logger.error({ mint: accountData.baseMint }, `Failed to buy token`); } } @@ -380,6 +388,7 @@ async function sell(accountId: PublicKey, mint: PublicKey, amount: BigNumberish) `Confirmed sell tx`, ); sold = true; + processingToken = false; } catch (e: any) { // wait for a bit before retrying await new Promise((resolve) => setTimeout(resolve, 100)); @@ -388,6 +397,7 @@ async function sell(accountId: PublicKey, mint: PublicKey, amount: BigNumberish) logger.error({ mint }, `Failed to sell token, retry: ${retries}/${MAX_SELL_RETRIES}`); } } while (!sold && retries < MAX_SELL_RETRIES); + processingToken = false; } function loadSnipeList() { @@ -408,7 +418,8 @@ function loadSnipeList() { } function shouldBuy(key: string): boolean { - return USE_SNIPE_LIST ? snipeList.includes(key) : true; + logger.info(processingToken, 'Is processing token buy') + return USE_SNIPE_LIST ? snipeList.includes(key) : ONE_TOKEN_AT_A_TIME ? !processingToken : true } const runListener = async () => { @@ -505,6 +516,10 @@ const runListener = async () => { logger.info(`Listening for raydium changes: ${raydiumSubscriptionId}`); logger.info(`Listening for open book changes: ${openBookSubscriptionId}`); + logger.info('------------------- 🚀 ---------------------'); + logger.info('Bot is running! Press CTRL + C to stop it.'); + logger.info('------------------- 🚀 ---------------------'); + if (USE_SNIPE_LIST) { setInterval(loadSnipeList, SNIPE_LIST_REFRESH_INTERVAL); } diff --git a/constants/constants.ts b/constants/constants.ts index 32ae760..181446f 100644 --- a/constants/constants.ts +++ b/constants/constants.ts @@ -16,4 +16,4 @@ export const PRIVATE_KEY = retrieveEnvVariable('PRIVATE_KEY', logger); export const QUOTE_MINT = retrieveEnvVariable('QUOTE_MINT', logger); export const QUOTE_AMOUNT = retrieveEnvVariable('QUOTE_AMOUNT', logger); export const MIN_POOL_SIZE = retrieveEnvVariable('MIN_POOL_SIZE', logger); - +export const ONE_TOKEN_AT_A_TIME = retrieveEnvVariable('ONE_TOKEN_AT_A_TIME', logger) === 'true'; From 727c779ab56e5c6637b0412ea303104875ef7ced Mon Sep 17 00:00:00 2001 From: OneRobotBoii Date: Wed, 27 Mar 2024 15:17:09 +0700 Subject: [PATCH 2/4] Clean up CLI logs --- .gitignore | 3 ++- buy.ts | 7 +++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index e4ff98a..d8ffbe8 100644 --- a/.gitignore +++ b/.gitignore @@ -133,4 +133,5 @@ pnpm-lock.yaml .pnp.* # JetBrains -.idea \ No newline at end of file +.idea +hailmary.code-workspace diff --git a/buy.ts b/buy.ts index eacc0ab..253bb44 100644 --- a/buy.ts +++ b/buy.ts @@ -174,6 +174,7 @@ export async function processRaydiumPool(id: PublicKey, poolState: LiquidityStat `Skipping pool, smaller than ${quoteMinPoolSizeAmount.toFixed()} ${quoteToken.symbol}`, `Swap quote in amount: ${poolSize.toFixed()}`, ); + logger.info(`-------------------🤖🔧------------------- \n`); return; } } @@ -281,6 +282,7 @@ async function buy(accountId: PublicKey, accountData: LiquidityStateV4): Promise COMMITMENT_LEVEL, ); if (!confirmation.value.err) { + logger.info(`-------------------🟢------------------- `); logger.info( { mint: accountData.baseMint, @@ -377,7 +379,7 @@ async function sell(accountId: PublicKey, mint: PublicKey, amount: BigNumberish) logger.info({ mint, signature }, `Error confirming sell tx`); continue; } - + logger.info(`-------------------🔴------------------- `); logger.info( { dex: `https://dexscreener.com/solana/${mint}?maker=${wallet.publicKey}`, @@ -418,7 +420,8 @@ function loadSnipeList() { } function shouldBuy(key: string): boolean { - logger.info(processingToken, 'Is processing token buy') + logger.info(`-------------------🤖🔧------------------- `); + logger.info(`Processing token: ${processingToken}`) return USE_SNIPE_LIST ? snipeList.includes(key) : ONE_TOKEN_AT_A_TIME ? !processingToken : true } From 01941d8b16d7608322daeee041356f6d1f91dec5 Mon Sep 17 00:00:00 2001 From: OneRobotBoii Date: Fri, 29 Mar 2024 23:27:26 +0700 Subject: [PATCH 3/4] feat: Add MAX_POOL_SIZE check --- .env.copy | 1 + buy.ts | 24 ++++++++++++++++++++++++ constants/constants.ts | 1 + 3 files changed, 26 insertions(+) diff --git a/.env.copy b/.env.copy index ecbcc30..0c298e3 100644 --- a/.env.copy +++ b/.env.copy @@ -12,4 +12,5 @@ MAX_SELL_RETRIES=5 AUTO_SELL_DELAY=1000 LOG_LEVEL=info MIN_POOL_SIZE=10 +MAX_POOL_SIZE=50 ONE_TOKEN_AT_A_TIME=true diff --git a/buy.ts b/buy.ts index 253bb44..071701f 100644 --- a/buy.ts +++ b/buy.ts @@ -48,6 +48,7 @@ import { SNIPE_LIST_REFRESH_INTERVAL, USE_SNIPE_LIST, MIN_POOL_SIZE, + MAX_POOL_SIZE, ONE_TOKEN_AT_A_TIME, } from './constants'; @@ -71,6 +72,7 @@ let quoteToken: Token; let quoteTokenAssociatedAddress: PublicKey; let quoteAmount: TokenAmount; let quoteMinPoolSizeAmount: TokenAmount; +let quoteMaxPoolSizeAmount: TokenAmount; let processingToken: Boolean = false; @@ -90,6 +92,7 @@ async function init(): Promise { quoteToken = Token.WSOL; quoteAmount = new TokenAmount(Token.WSOL, QUOTE_AMOUNT, false); quoteMinPoolSizeAmount = new TokenAmount(quoteToken, MIN_POOL_SIZE, false); + quoteMaxPoolSizeAmount = new TokenAmount(quoteToken, MAX_POOL_SIZE, false); break; } case 'USDC': { @@ -102,6 +105,7 @@ async function init(): Promise { ); quoteAmount = new TokenAmount(quoteToken, QUOTE_AMOUNT, false); quoteMinPoolSizeAmount = new TokenAmount(quoteToken, MIN_POOL_SIZE, false); + quoteMaxPoolSizeAmount = new TokenAmount(quoteToken, MAX_POOL_SIZE, false); break; } default: { @@ -114,6 +118,9 @@ async function init(): Promise { logger.info( `Min pool size: ${quoteMinPoolSizeAmount.isZero() ? 'false' : quoteMinPoolSizeAmount.toFixed()} ${quoteToken.symbol}`, ); + logger.info( + `Max pool size: ${quoteMaxPoolSizeAmount.isZero() ? 'false' : quoteMaxPoolSizeAmount.toFixed()} ${quoteToken.symbol}`, + ); logger.info(`One token at a time: ${ONE_TOKEN_AT_A_TIME}`); logger.info(`Buy amount: ${quoteAmount.toFixed()} ${quoteToken.symbol}`); logger.info(`Auto sell: ${AUTO_SELL}`); @@ -179,6 +186,23 @@ export async function processRaydiumPool(id: PublicKey, poolState: LiquidityStat } } + if (!quoteMaxPoolSizeAmount.isZero()) { + const poolSize = new TokenAmount(quoteToken, poolState.swapQuoteInAmount, true); + + if (poolSize.gt(quoteMaxPoolSizeAmount)) { + logger.warn( + { + mint: poolState.baseMint, + pooled: `${poolSize.toFixed()} ${quoteToken.symbol}`, + }, + `Skipping pool, bigger than ${quoteMaxPoolSizeAmount.toFixed()} ${quoteToken.symbol}`, + `Swap quote in amount: ${poolSize.toFixed()}`, + ); + logger.info(`-------------------🤖🔧------------------- \n`); + return; + } + } + if (CHECK_IF_MINT_IS_RENOUNCED) { const mintOption = await checkMintable(poolState.baseMint); diff --git a/constants/constants.ts b/constants/constants.ts index 181446f..eb97806 100644 --- a/constants/constants.ts +++ b/constants/constants.ts @@ -16,4 +16,5 @@ export const PRIVATE_KEY = retrieveEnvVariable('PRIVATE_KEY', logger); export const QUOTE_MINT = retrieveEnvVariable('QUOTE_MINT', logger); export const QUOTE_AMOUNT = retrieveEnvVariable('QUOTE_AMOUNT', logger); export const MIN_POOL_SIZE = retrieveEnvVariable('MIN_POOL_SIZE', logger); +export const MAX_POOL_SIZE = retrieveEnvVariable('MAX_POOL_SIZE', logger); export const ONE_TOKEN_AT_A_TIME = retrieveEnvVariable('ONE_TOKEN_AT_A_TIME', logger) === 'true'; From 704c46bbb931e980f668f3ef36e5e6a1f5285c3d Mon Sep 17 00:00:00 2001 From: OneRobotBoii Date: Tue, 2 Apr 2024 20:58:09 +0700 Subject: [PATCH 4/4] Update .gitignore file to exclude Visual Studio Code workspace files --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index d8ffbe8..fa81bfa 100644 --- a/.gitignore +++ b/.gitignore @@ -134,4 +134,6 @@ pnpm-lock.yaml # JetBrains .idea -hailmary.code-workspace + +# Visual Studio Code +*.code-workspace