Jelajahi Sumber

feat: use pool open time for pool detection

Filip Dunder 1 tahun lalu
induk
melakukan
ee3e252de7
3 mengubah file dengan 46 tambahan dan 166 penghapusan
  1. 20 30
      buy.ts
  2. 0 100
      liquidity/liquidity.ts
  3. 26 36
      market/market.ts

+ 20 - 30
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<void> {
     `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(
+  logger.info(`Loading existing markets...`);
+  // get all open-book markets
+  const allMarkets = await getAllMarketsV3(
+    solanaConnection,
     quoteToken.mint,
+    commitment,
   );
-  existingLiquidityPools = new Set(
-    allLiquidityPools.map((p) => p.id.toString()),
-  );
-
-  // 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}`,
+    `Loaded ${existingOpenBookMarkets.size} ${quoteToken.symbol} markets`,
   );
-
-  const tokenAccounts = await getTokenAccounts(
-    solanaConnection,
-    wallet.publicKey,
-    commitment,
-  );
-
   // check existing wallet for associated token account of quote mint
   const tokenAccounts = await getTokenAccounts(
     solanaConnection,
@@ -202,18 +187,17 @@ async function init(): Promise<void> {
   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,

+ 0 - 100
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<LiquidityJsonResponse>(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<LiquidityPoolKeys> {
-  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,

+ 26 - 36
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<MinimalMarketStateLayoutV3>;
 
 export async function getAllMarketsV3(
-): Promise<{ id: string; programId: PublicKey }[]> {
-  const url = 'https://cache.prism.ag/openbook.json';
-
-  try {
-    const response = await axios.get<JsonResponse>(url);
-    // @ts-ignore
-    const json: JsonResponse = response.data;
+  connection: Connection,
+  quoteMint: PublicKey,
+  commitment?: Commitment,
+): Promise<MinimalOpenBookAccountData[]> {
+  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(),
+        },
+      },
+    ],
+  });
 
-    return json.result
-      .map(account => ({
-        id: account.pubkey,
+  return accounts.map(
+    (info) =>
+      <MinimalOpenBookAccountData>{
+        id: info.pubkey,
         programId: OPENBOOK_PROGRAM_ID,
-      }));
-  } catch (error) {
-    console.error('Error during data retrieval:', error);
-    return [];
-  }
+      },
+  );
 }