From ee3e252de77d55d5e1217d4e944660aa1623feaa Mon Sep 17 00:00:00 2001 From: Filip Dunder Date: Fri, 9 Feb 2024 14:56:18 +0100 Subject: [PATCH] feat: use pool open time for pool detection --- buy.ts | 52 +++++++++------------ liquidity/liquidity.ts | 100 ----------------------------------------- market/market.ts | 62 +++++++++++-------------- 3 files changed, 47 insertions(+), 167 deletions(-) diff --git a/buy.ts b/buy.ts index b0a23d2..0e08f4f 100644 --- a/buy.ts +++ b/buy.ts @@ -25,7 +25,6 @@ import { Commitment, } from '@solana/web3.js'; import { - getAllAccountsV4, getTokenAccounts, RAYDIUM_LIQUIDITY_PROGRAM_ID_V4, OPENBOOK_PROGRAM_ID, @@ -145,31 +144,17 @@ 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()}`, ); - // get all existing liquidity pools - const allLiquidityPools = await getAllAccountsV4( - quoteToken.mint, - ); - existingLiquidityPools = new Set( - allLiquidityPools.map((p) => p.id.toString()), - ); - + logger.info(`Loading existing markets...`); // get all open-book markets - const allMarkets = await getAllMarketsV3(); - 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}`, - ); - - const tokenAccounts = await getTokenAccounts( + const allMarkets = await getAllMarketsV3( solanaConnection, - wallet.publicKey, + quoteToken.mint, commitment, ); - + existingOpenBookMarkets = new Set(allMarkets.map((p) => p.id.toString())); + logger.info( + `Loaded ${existingOpenBookMarkets.size} ${quoteToken.symbol} markets`, + ); // check existing wallet for associated token account of quote mint const tokenAccounts = await getTokenAccounts( solanaConnection, @@ -202,18 +187,17 @@ async function init(): Promise { loadSnipeList(); } -export async function processRaydiumPool(updatedAccountInfo: KeyedAccountInfo) { +export async function processRaydiumPool( + id: PublicKey, + poolState: LiquidityStateV4, +) { let accountData: LiquidityStateV4 | undefined; try { - accountData = LIQUIDITY_STATE_LAYOUT_V4.decode( - updatedAccountInfo.accountInfo.data, - ); - - if (!shouldBuy(accountData.baseMint.toString())) { + if (!shouldBuy(poolState.baseMint.toString())) { return; } - await buy(updatedAccountInfo.accountId, accountData); + await buy(id, poolState); } catch (e) { logger.error({ ...accountData, error: e }, `Failed to process pool`); } @@ -343,14 +327,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, diff --git a/liquidity/liquidity.ts b/liquidity/liquidity.ts index 854c1f3..38667a3 100644 --- a/liquidity/liquidity.ts +++ b/liquidity/liquidity.ts @@ -12,26 +12,6 @@ import { } from '@raydium-io/raydium-sdk'; import { TOKEN_PROGRAM_ID } from '@solana/spl-token'; import { MinimalMarketLayoutV3 } from '../market'; -import bs58 from 'bs58'; -import axios from 'axios'; - -interface LiquidityPool { - id: string; - baseMint: string; - quoteMint: string; - // ... autres propriétés -} - -interface LiquidityJsonResponse { - official: LiquidityPool[]; - unOfficial: LiquidityPool[]; -} - -interface MinimalLiquidityAccountData { - id: string; - version: number; - programId: string; -} export const RAYDIUM_LIQUIDITY_PROGRAM_ID_V4 = MAINNET_PROGRAM_ID.AmmV4; export const OPENBOOK_PROGRAM_ID = MAINNET_PROGRAM_ID.OPENBOOK_MARKET; @@ -42,33 +22,6 @@ export const MINIMAL_MARKET_STATE_LAYOUT_V3 = struct([ publicKey('asks'), ]); -export async function getAllAccountsV4( - quoteMint: PublicKey, -): Promise<{ id: string; version: number; programId: PublicKey }[]> { - const url = 'https://api.raydium.io/v2/sdk/liquidity/mainnet.json'; - try { - const response = await axios.get(url); - // @ts-ignore - const json = response.data; - const filteredPools = json.official.concat(json.unOfficial) - .filter(pool => { - if (!pool) { - console.log('Pool undefined:', pool); - return false; - } - return pool.quoteMint && pool.quoteMint === quoteMint.toBase58(); - }); - return filteredPools.map(pool => ({ - id: pool.id, - version: 4, - programId: RAYDIUM_LIQUIDITY_PROGRAM_ID_V4, // Assurez-vous que cette constante est définie - })); - } catch (error) { - console.error('Error during data retrieval:', error); - return []; - } -} - export function createPoolKeys( id: PublicKey, accountData: LiquidityStateV4, @@ -109,59 +62,6 @@ export function createPoolKeys( }; } -export async function getAccountPoolKeysFromAccountDataV4( - connection: Connection, - id: PublicKey, - accountData: LiquidityStateV4, - commitment?: Commitment, -): Promise { - const marketInfo = await connection.getAccountInfo(accountData.marketId, { - commitment: commitment, - dataSlice: { - offset: 253, // eventQueue - length: 32 * 3, - }, - }); - - const minimalMarketData = MINIMAL_MARKET_STATE_LAYOUT_V3.decode( - marketInfo!.data, - ); - - return { - id, - baseMint: accountData.baseMint, - quoteMint: accountData.quoteMint, - lpMint: accountData.lpMint, - baseDecimals: accountData.baseDecimal.toNumber(), - quoteDecimals: accountData.quoteDecimal.toNumber(), - lpDecimals: 5, - version: 4, - programId: RAYDIUM_LIQUIDITY_PROGRAM_ID_V4, - authority: Liquidity.getAssociatedAuthority({ - programId: RAYDIUM_LIQUIDITY_PROGRAM_ID_V4, - }).publicKey, - openOrders: accountData.openOrders, - targetOrders: accountData.targetOrders, - baseVault: accountData.baseVault, - quoteVault: accountData.quoteVault, - marketVersion: 3, - marketProgramId: accountData.marketProgramId, - marketId: accountData.marketId, - marketAuthority: Market.getAssociatedAuthority({ - programId: accountData.marketProgramId, - marketId: accountData.marketId, - }).publicKey, - marketBaseVault: accountData.baseVault, - marketQuoteVault: accountData.quoteVault, - marketBids: minimalMarketData.bids, - marketAsks: minimalMarketData.asks, - marketEventQueue: minimalMarketData.eventQueue, - withdrawQueue: accountData.withdrawQueue, - lpVault: accountData.lpVault, - lookupTableAccount: PublicKey.default, - }; -} - export async function getTokenAccounts( connection: Connection, owner: PublicKey, diff --git a/market/market.ts b/market/market.ts index b6bf288..bff51f0 100644 --- a/market/market.ts +++ b/market/market.ts @@ -1,31 +1,12 @@ -import {PublicKey } from '@solana/web3.js'; +import { Commitment, Connection, PublicKey } from '@solana/web3.js'; import { GetStructureSchema, + MARKET_STATE_LAYOUT_V3, } from '@raydium-io/raydium-sdk'; import { MINIMAL_MARKET_STATE_LAYOUT_V3, OPENBOOK_PROGRAM_ID, } from '../liquidity'; -import axios from 'axios'; - -interface AccountData { - data: string[]; - executable: boolean; - lamports: number; - owner: string; - rentEpoch: number; - space: number; -} - -interface MarketAccount { - account: AccountData; - pubkey: string; -} - -interface JsonResponse { - jsonrpc: string; - result: MarketAccount[]; -} export type MinimalOpenBookAccountData = { id: PublicKey; @@ -36,21 +17,30 @@ export type MinimalMarketLayoutV3 = GetStructureSchema; export async function getAllMarketsV3( -): Promise<{ id: string; programId: PublicKey }[]> { - const url = 'https://cache.prism.ag/openbook.json'; + connection: Connection, + quoteMint: PublicKey, + commitment?: Commitment, +): Promise { + const { span } = MARKET_STATE_LAYOUT_V3; + const accounts = await connection.getProgramAccounts(OPENBOOK_PROGRAM_ID, { + dataSlice: { offset: 0, length: 0 }, + commitment: commitment, + filters: [ + { dataSize: span }, + { + memcmp: { + offset: MARKET_STATE_LAYOUT_V3.offsetOf('quoteMint'), + bytes: quoteMint.toBase58(), + }, + }, + ], + }); - try { - const response = await axios.get(url); - // @ts-ignore - const json: JsonResponse = response.data; - - return json.result - .map(account => ({ - id: account.pubkey, + return accounts.map( + (info) => + { + id: info.pubkey, programId: OPENBOOK_PROGRAM_ID, - })); - } catch (error) { - console.error('Error during data retrieval:', error); - return []; - } + }, + ); }