Merge remote-tracking branch 'origin/HEAD'

This commit is contained in:
BoiiButBot
2024-02-10 13:29:51 +07:00
5 changed files with 120 additions and 304 deletions

160
buy.ts
View File

@ -3,7 +3,6 @@ import {
LIQUIDITY_STATE_LAYOUT_V4,
LiquidityPoolKeys,
LiquidityStateV4,
MARKET_STATE_LAYOUT_V2,
MARKET_STATE_LAYOUT_V3,
MarketStateV3,
Token,
@ -25,14 +24,13 @@ import {
Commitment,
} from '@solana/web3.js';
import {
getAllAccountsV4,
getTokenAccounts,
RAYDIUM_LIQUIDITY_PROGRAM_ID_V4,
OPENBOOK_PROGRAM_ID,
createPoolKeys,
} from './liquidity';
import { retrieveEnvVariable } from './utils';
import { getAllMarketsV3, MinimalMarketLayoutV3 } from './market';
import { getMinimalMarketV3, MinimalMarketLayoutV3 } from './market';
import pino from 'pino';
import bs58 from 'bs58';
import * as fs from 'fs';
@ -146,75 +144,6 @@ async function init(): Promise<void> {
`Script will buy all new tokens using ${QUOTE_MINT}. Amount that will be used to buy each token is: ${quoteAmount.toFixed().toString()}`,
);
let message = {
embeds: [
{
title: `Script will buy all new tokens using ${QUOTE_MINT}. Amount that will be used to buy each token is: ${quoteAmount.toFixed().toString()}`,
color: 1127128,
},
],
};
// post it to discord
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(message),
});
// get all existing liquidity pools
const allLiquidityPools = await getAllAccountsV4(
solanaConnection,
quoteToken.mint,
commitment,
);
existingLiquidityPools = new Set(
allLiquidityPools.map((p) => p.id.toString()),
);
// get all open-book markets
const allMarkets = await getAllMarketsV3(
solanaConnection,
quoteToken.mint,
commitment,
);
existingOpenBookMarkets = new Set(allMarkets.map((p) => p.id.toString()));
logger.info(
`Total ${quoteToken.symbol} markets ${existingOpenBookMarkets.size}`,
);
logger.info(
`Total ${quoteToken.symbol} pools ${existingLiquidityPools.size}`,
);
// post to discord webhook
message = {
embeds: [
{
title: `Total ${quoteToken.symbol} markets ${existingOpenBookMarkets.size}`,
color: 1127128,
},
{
title: `Total ${quoteToken.symbol} pools ${existingLiquidityPools.size}`,
color: 14177041,
},
],
};
// post it to discord
fetch(DISCORD_WEBHOOK, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(message),
});
// check existing wallet for associated token account of quote mint
const tokenAccounts = await getTokenAccounts(
solanaConnection,
@ -246,33 +175,34 @@ async function init(): Promise<void> {
// load tokens to snipe
loadSnipeList();
}
// Auto sell if enabled in .env file
const AUTO_SELL = retrieveEnvVariable('AUTO_SELL', logger);
export async function processRaydiumPool(updatedAccountInfo: KeyedAccountInfo) {
let accountData: LiquidityStateV4 | undefined;
try {
accountData = LIQUIDITY_STATE_LAYOUT_V4.decode(
updatedAccountInfo.accountInfo.data,
);
if (!shouldBuy(accountData.baseMint.toString())) {
function saveTokenAccount(mint: PublicKey, accountData: MinimalMarketLayoutV3) {
const ata = getAssociatedTokenAddressSync(mint, wallet.publicKey);
const tokenAccount = <MinimalTokenAccountData>{
address: ata,
mint: mint,
market: <MinimalMarketLayoutV3>{
bids: accountData.bids,
asks: accountData.asks,
eventQueue: accountData.eventQueue,
},
};
existingTokenAccounts.set(mint.toString(), tokenAccount);
return tokenAccount;
}
export async function processRaydiumPool(
id: PublicKey,
poolState: LiquidityStateV4,
) {
try {
if (!shouldBuy(poolState.baseMint.toString())) {
return;
}
await buy(updatedAccountInfo.accountId, accountData);
if (AUTO_SELL === 'true') {
const SELL_DELAY = Number(retrieveEnvVariable('SELL_DELAY', logger));
await new Promise((resolve) => setTimeout(resolve, SELL_DELAY));
await sell(updatedAccountInfo.accountId, accountData);
} else {
logger.info(
`Auto sell is disabled. To enable it, set AUTO_SELL=true in .env file`,
);
}
await buy(id, poolState);
} catch (e) {
logger.error({ ...accountData, error: e }, `Failed to process pool`);
logger.error({ ...poolState, error: e }, `Failed to process pool`);
}
}
@ -290,21 +220,7 @@ export async function processOpenBookMarket(
return;
}
const ata = getAssociatedTokenAddressSync(
accountData.baseMint,
wallet.publicKey,
);
existingTokenAccounts.set(accountData.baseMint.toString(), <
MinimalTokenAccountData
>{
address: ata,
mint: accountData.baseMint,
market: <MinimalMarketLayoutV3>{
bids: accountData.bids,
asks: accountData.asks,
eventQueue: accountData.eventQueue,
},
});
saveTokenAccount(accountData.baseMint, accountData);
} catch (e) {
logger.error({ ...accountData, error: e }, `Failed to process market`);
}
@ -314,12 +230,16 @@ async function buy(
accountId: PublicKey,
accountData: LiquidityStateV4,
): Promise<void> {
const tokenAccount = existingTokenAccounts.get(
accountData.baseMint.toString(),
);
let tokenAccount = existingTokenAccounts.get(accountData.baseMint.toString());
if (!tokenAccount) {
return;
// it's possible that we didn't have time to fetch open book data
const market = await getMinimalMarketV3(
solanaConnection,
accountData.marketId,
commitment,
);
tokenAccount = saveTokenAccount(accountData.baseMint, market);
}
tokenAccount.poolKeys = createPoolKeys(
@ -546,14 +466,20 @@ function shouldBuy(key: string): boolean {
const runListener = async () => {
await init();
const runTimestamp = Math.floor(new Date().getTime() / 1000);
const raydiumSubscriptionId = solanaConnection.onProgramAccountChange(
RAYDIUM_LIQUIDITY_PROGRAM_ID_V4,
async (updatedAccountInfo) => {
const key = updatedAccountInfo.accountId.toString();
const poolState = LIQUIDITY_STATE_LAYOUT_V4.decode(
updatedAccountInfo.accountInfo.data,
);
const poolOpenTime = parseInt(poolState.poolOpenTime.toString());
const existing = existingLiquidityPools.has(key);
if (!existing) {
if (poolOpenTime > runTimestamp && !existing) {
existingLiquidityPools.add(key);
const _ = processRaydiumPool(updatedAccountInfo);
const _ = processRaydiumPool(updatedAccountInfo.accountId, poolState);
}
},
commitment,
@ -592,10 +518,10 @@ const runListener = async () => {
},
commitment,
[
{ dataSize: MARKET_STATE_LAYOUT_V2.span },
{ dataSize: MARKET_STATE_LAYOUT_V3.span },
{
memcmp: {
offset: MARKET_STATE_LAYOUT_V2.offsetOf('quoteMint'),
offset: MARKET_STATE_LAYOUT_V3.offsetOf('quoteMint'),
bytes: quoteToken.mint.toBase58(),
},
},