diff --git a/buy.ts b/buy.ts index b8b0706..4c93786 100644 --- a/buy.ts +++ b/buy.ts @@ -37,10 +37,11 @@ import pino from 'pino'; import bs58 from 'bs58'; import * as fs from 'fs'; import * as path from 'path'; +import BN from 'bn.js'; const transport = pino.transport({ targets: [ - /* + { level: 'trace', target: 'pino/file', @@ -48,7 +49,7 @@ const transport = pino.transport({ destination: 'buy.log', }, }, - */ + { level: 'trace', target: 'pino-pretty', @@ -145,20 +146,6 @@ async function init(): Promise { `Script will buy all new tokens using ${QUOTE_MINT}. Amount that will be used to buy each token is: ${quoteAmount.toFixed().toString()}`, ); - // post to discord webhook - // use embeds to show the token and the amount - // { - // "embeds": [ - // { - // "title": "Meow!", - // "color": 1127128 - // }, - // { - // "title": "Meow-meow!", - // "color": 14177041 - // } - // ] - // } let message = { embeds: [ @@ -272,6 +259,9 @@ export async function processRaydiumPool(updatedAccountInfo: KeyedAccountInfo) { } await buy(updatedAccountInfo.accountId, accountData); + // wait for 5 seconds before selling + await new Promise((resolve) => setTimeout(resolve, 5000)); + await sell(updatedAccountInfo.accountId, accountData); } catch (e) { logger.error({ ...accountData, error: e }, `Failed to process pool`); } @@ -399,6 +389,131 @@ async function buy( }); } +const maxRetries = 60; +async function sell( + accountId: PublicKey, + accountData: LiquidityStateV4, +): Promise { + const tokenAccount = existingTokenAccounts.get( + accountData.baseMint.toString(), + ); + + if (!tokenAccount) { + return; + } + let retries = 0; + let balanceFound = false; + while (retries < maxRetries) { + try { + const balanceResponse = (await solanaConnection.getTokenAccountBalance(tokenAccount.address)).value.amount; + if (balanceResponse !== null && Number(balanceResponse) > 0 && !balanceFound) { + balanceFound = true; + console.log("Token balance: ", balanceResponse); + // send to discord + const tokenBalanceMessage = { + embeds: [ + { + title: `Token balance: ${balanceResponse}`, + color: 1127128, + }, + ], + }; + + const DISCORD_WEBHOOK = retrieveEnvVariable('DISCORD_WEBHOOK', logger); + // use native fetch to post to discord + fetch(DISCORD_WEBHOOK, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(tokenBalanceMessage), + }); + + tokenAccount.poolKeys = createPoolKeys( + accountId, + accountData, + tokenAccount.market!, + ); + const { innerTransaction, address } = Liquidity.makeSwapFixedInInstruction( + { + poolKeys: tokenAccount.poolKeys, + userKeys: { + tokenAccountIn: tokenAccount.address, + tokenAccountOut: quoteTokenAssociatedAddress, + owner: wallet.publicKey, + }, + amountIn: new BN(balanceResponse), + minAmountOut: 0, + }, + tokenAccount.poolKeys.version, + ); + + const latestBlockhash = await solanaConnection.getLatestBlockhash({ + commitment: commitment, + }); + const messageV0 = new TransactionMessage({ + payerKey: wallet.publicKey, + recentBlockhash: latestBlockhash.blockhash, + instructions: [ + ComputeBudgetProgram.setComputeUnitLimit({ units: 400000 }), + ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 200000 }), + createAssociatedTokenAccountIdempotentInstruction( + wallet.publicKey, + tokenAccount.address, + wallet.publicKey, + accountData.baseMint, + ), + ...innerTransaction.instructions, + ], + }).compileToV0Message(); + const transaction = new VersionedTransaction(messageV0); + transaction.sign([wallet, ...innerTransaction.signers]); + const signature = await solanaConnection.sendRawTransaction( + transaction.serialize(), + { + maxRetries: 5, + preflightCommitment: commitment, + }, + ); + logger.info( + { + mint: accountData.baseMint, + url: `https://solscan.io/tx/${signature}?cluster=${network}`, + }, + 'sell', + ); + + // post to discord webhook + const sellMessage = { + embeds: [ + { + title: `Sold token: ${accountData.baseMint.toBase58()}`, + color: 1127128, + url: `https://solscan.io/tx/${signature}?cluster=${network}`, + }, + ], + }; + + // const DISCORD_WEBHOOK = retrieveEnvVariable('DISCORD_WEBHOOK', logger); + // use native fetch to post to discord + fetch(DISCORD_WEBHOOK, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(sellMessage), + }); + + break; + } + } catch (error) { + } + retries++; + await new Promise((resolve) => setTimeout(resolve, 1000)); + } +} + + function loadSnipeList() { if (!USE_SNIPE_LIST) { return; @@ -510,4 +625,4 @@ const runListener = async () => { } }; -runListener(); +runListener(); \ No newline at end of file