Browse Source

Merge pull request #28 from ThomAille/alternative_getProgramAccount

Alternative getProgramAccounts
Filip Dunđer 1 năm trước cách đây
mục cha
commit
f6f1b1bbbd
4 tập tin đã thay đổi với 82 bổ sung180 xóa
  1. 17 28
      buy.ts
  2. 0 106
      liquidity/liquidity.ts
  3. 60 42
      package-lock.json
  4. 5 4
      package.json

+ 17 - 28
buy.ts

@@ -25,7 +25,6 @@ import {
   Commitment,
 } from '@solana/web3.js';
 import {
-  getAllAccountsV4,
   getTokenAccounts,
   RAYDIUM_LIQUIDITY_PROGRAM_ID_V4,
   OPENBOOK_PROGRAM_ID,
@@ -145,16 +144,7 @@ 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(
-    solanaConnection,
-    quoteToken.mint,
-    commitment,
-  );
-  existingLiquidityPools = new Set(
-    allLiquidityPools.map((p) => p.id.toString()),
-  );
-
+  logger.info(`Loading existing markets...`);
   // get all open-book markets
   const allMarkets = await getAllMarketsV3(
     solanaConnection,
@@ -162,14 +152,9 @@ async function init(): Promise<void> {
     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}`,
+    `Loaded ${existingOpenBookMarkets.size} ${quoteToken.symbol} markets`,
   );
-
   // check existing wallet for associated token account of quote mint
   const tokenAccounts = await getTokenAccounts(
     solanaConnection,
@@ -202,20 +187,18 @@ async function init(): Promise<void> {
   loadSnipeList();
 }
 
-export async function processRaydiumPool(updatedAccountInfo: KeyedAccountInfo) {
-  let accountData: LiquidityStateV4 | undefined;
+export async function processRaydiumPool(
+  id: PublicKey,
+  poolState: LiquidityStateV4,
+) {
   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`);
+    logger.error({ ...poolState, error: e }, `Failed to process pool`);
   }
 }
 
@@ -343,14 +326,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 - 106
liquidity/liquidity.ts

@@ -1,7 +1,6 @@
 import { Commitment, Connection, PublicKey } from '@solana/web3.js';
 import {
   Liquidity,
-  LIQUIDITY_STATE_LAYOUT_V4,
   LiquidityPoolKeys,
   Market,
   TokenAccount,
@@ -13,7 +12,6 @@ import {
 } from '@raydium-io/raydium-sdk';
 import { TOKEN_PROGRAM_ID } from '@solana/spl-token';
 import { MinimalMarketLayoutV3 } from '../market';
-import bs58 from 'bs58';
 
 export const RAYDIUM_LIQUIDITY_PROGRAM_ID_V4 = MAINNET_PROGRAM_ID.AmmV4;
 export const OPENBOOK_PROGRAM_ID = MAINNET_PROGRAM_ID.OPENBOOK_MARKET;
@@ -24,57 +22,6 @@ export const MINIMAL_MARKET_STATE_LAYOUT_V3 = struct([
   publicKey('asks'),
 ]);
 
-export type MinimalLiquidityAccountData = {
-  id: PublicKey;
-  version: 4;
-  programId: PublicKey;
-};
-
-export async function getAllAccountsV4(
-  connection: Connection,
-  quoteMint: PublicKey,
-  commitment?: Commitment,
-): Promise<MinimalLiquidityAccountData[]> {
-  const { span } = LIQUIDITY_STATE_LAYOUT_V4;
-  const accounts = await connection.getProgramAccounts(
-    RAYDIUM_LIQUIDITY_PROGRAM_ID_V4,
-    {
-      dataSlice: { offset: 0, length: 0 },
-      commitment: commitment,
-      filters: [
-        { dataSize: span },
-        {
-          memcmp: {
-            offset: LIQUIDITY_STATE_LAYOUT_V4.offsetOf('quoteMint'),
-            bytes: quoteMint.toBase58(),
-          },
-        },
-        {
-          memcmp: {
-            offset: LIQUIDITY_STATE_LAYOUT_V4.offsetOf('marketProgramId'),
-            bytes: OPENBOOK_PROGRAM_ID.toBase58(),
-          },
-        },
-        {
-          memcmp: {
-            offset: LIQUIDITY_STATE_LAYOUT_V4.offsetOf('status'),
-            bytes: bs58.encode([6, 0, 0, 0, 0, 0, 0, 0]),
-          },
-        },
-      ],
-    },
-  );
-
-  return accounts.map(
-    (info) =>
-      <MinimalLiquidityAccountData>{
-        id: info.pubkey,
-        version: 4,
-        programId: RAYDIUM_LIQUIDITY_PROGRAM_ID_V4,
-      },
-  );
-}
-
 export function createPoolKeys(
   id: PublicKey,
   accountData: LiquidityStateV4,
@@ -115,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,

+ 60 - 42
package-lock.json

@@ -7,18 +7,19 @@
       "name": "solana-sniper-bot",
       "dependencies": {
         "@raydium-io/raydium-sdk": "^1.3.1-beta.47",
-        "@solana/spl-token": "^0.3.11",
+        "@solana/spl-token": "^0.4.0",
+        "@solana/web3.js": "^1.89.1",
         "bigint-buffer": "^1.1.5",
         "bn.js": "^5.2.1",
         "bs58": "^5.0.0",
-        "dotenv": "^16.3.2",
-        "pino": "^8.17.2",
+        "dotenv": "^16.4.1",
+        "pino": "^8.18.0",
         "pino-pretty": "^10.3.1",
         "pino-std-serializers": "^6.2.2"
       },
       "devDependencies": {
         "@types/bn.js": "^5.1.5",
-        "prettier": "^3.2.1",
+        "prettier": "^3.2.4",
         "ts-node": "^10.9.2",
         "typescript": "^5.3.3"
       }
@@ -113,6 +114,23 @@
         "@solana/web3.js": "^1.73.0"
       }
     },
+    "node_modules/@raydium-io/raydium-sdk/node_modules/@solana/spl-token": {
+      "version": "0.3.11",
+      "resolved": "https://registry.npmjs.org/@solana/spl-token/-/spl-token-0.3.11.tgz",
+      "integrity": "sha512-bvohO3rIMSVL24Pb+I4EYTJ6cL82eFpInEXD/I8K8upOGjpqHsKUoAempR/RnUlI1qSFNyFlWJfu6MNUgfbCQQ==",
+      "dependencies": {
+        "@solana/buffer-layout": "^4.0.0",
+        "@solana/buffer-layout-utils": "^0.2.0",
+        "@solana/spl-token-metadata": "^0.1.2",
+        "buffer": "^6.0.3"
+      },
+      "engines": {
+        "node": ">=16"
+      },
+      "peerDependencies": {
+        "@solana/web3.js": "^1.88.0"
+      }
+    },
     "node_modules/@solana/buffer-layout": {
       "version": "4.0.1",
       "resolved": "https://registry.npmjs.org/@solana/buffer-layout/-/buffer-layout-4.0.1.tgz",
@@ -182,9 +200,9 @@
       }
     },
     "node_modules/@solana/spl-token": {
-      "version": "0.3.11",
-      "resolved": "https://registry.npmjs.org/@solana/spl-token/-/spl-token-0.3.11.tgz",
-      "integrity": "sha512-bvohO3rIMSVL24Pb+I4EYTJ6cL82eFpInEXD/I8K8upOGjpqHsKUoAempR/RnUlI1qSFNyFlWJfu6MNUgfbCQQ==",
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/@solana/spl-token/-/spl-token-0.4.0.tgz",
+      "integrity": "sha512-jjBIBG9IsclqQVl5Y82npGE6utdCh7Z9VFcF5qgJa5EUq2XgspW3Dt1wujWjH/vQDRnkp9zGO+BqQU/HhX/3wg==",
       "dependencies": {
         "@solana/buffer-layout": "^4.0.0",
         "@solana/buffer-layout-utils": "^0.2.0",
@@ -195,7 +213,7 @@
         "node": ">=16"
       },
       "peerDependencies": {
-        "@solana/web3.js": "^1.88.0"
+        "@solana/web3.js": "^1.89.1"
       }
     },
     "node_modules/@solana/spl-token-metadata": {
@@ -229,9 +247,9 @@
       }
     },
     "node_modules/@solana/web3.js": {
-      "version": "1.89.0",
-      "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.89.0.tgz",
-      "integrity": "sha512-b6PJxNL/DX+J2zccj3kzxZ6HyUF92tc8L9CjMlnTYKCdotAk163ygQ/jbHDT0yYs7pGeXAszyLuaqUXJ8bxwpA==",
+      "version": "1.89.1",
+      "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.89.1.tgz",
+      "integrity": "sha512-t9TTLtPQxtQB3SAf/5E8xPXfVDsC6WGOsgKY02l2cbe0HLymT7ynE8Hu48Lk5qynHCquj6nhISfEHcjMkYpu/A==",
       "dependencies": {
         "@babel/runtime": "^7.23.4",
         "@noble/curves": "^1.2.0",
@@ -266,6 +284,25 @@
         "base-x": "^3.0.2"
       }
     },
+    "node_modules/@solana/web3.js/node_modules/node-fetch": {
+      "version": "2.7.0",
+      "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
+      "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
+      "dependencies": {
+        "whatwg-url": "^5.0.0"
+      },
+      "engines": {
+        "node": "4.x || >=6.0.0"
+      },
+      "peerDependencies": {
+        "encoding": "^0.1.0"
+      },
+      "peerDependenciesMeta": {
+        "encoding": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/@tsconfig/node10": {
       "version": "1.0.9",
       "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
@@ -383,9 +420,9 @@
       }
     },
     "node_modules/axios": {
-      "version": "1.6.5",
-      "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.5.tgz",
-      "integrity": "sha512-Ii012v05KEVuUoFWmMW/UQv9aRIc3ZwkWDcM+h5Il8izZCtRVpDUfwpoFf7eOtajT3QiGR4yDUx7lPqHJULgbg==",
+      "version": "1.6.7",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz",
+      "integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==",
       "dependencies": {
         "follow-redirects": "^1.15.4",
         "form-data": "^4.0.0",
@@ -605,9 +642,9 @@
       }
     },
     "node_modules/dotenv": {
-      "version": "16.3.2",
-      "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.2.tgz",
-      "integrity": "sha512-HTlk5nmhkm8F6JcdXvHIzaorzCoziNQT9mGxLPVXW8wJF1TiGSL60ZGB4gHWabHOaMmWmhvk2/lPHfnBiT78AQ==",
+      "version": "16.4.1",
+      "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.1.tgz",
+      "integrity": "sha512-CjA3y+Dr3FyFDOAMnxZEGtnW9KBR2M0JvvUtXNW+dYJL5ROWxP9DUHCwgFqpMk0OXCc0ljhaNTr2w/kutYIcHQ==",
       "engines": {
         "node": ">=12"
       },
@@ -880,25 +917,6 @@
       "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
       "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
     },
-    "node_modules/node-fetch": {
-      "version": "2.7.0",
-      "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
-      "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
-      "dependencies": {
-        "whatwg-url": "^5.0.0"
-      },
-      "engines": {
-        "node": "4.x || >=6.0.0"
-      },
-      "peerDependencies": {
-        "encoding": "^0.1.0"
-      },
-      "peerDependenciesMeta": {
-        "encoding": {
-          "optional": true
-        }
-      }
-    },
     "node_modules/node-gyp-build": {
       "version": "4.8.0",
       "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.0.tgz",
@@ -924,9 +942,9 @@
       }
     },
     "node_modules/pino": {
-      "version": "8.17.2",
-      "resolved": "https://registry.npmjs.org/pino/-/pino-8.17.2.tgz",
-      "integrity": "sha512-LA6qKgeDMLr2ux2y/YiUt47EfgQ+S9LznBWOJdN3q1dx2sv0ziDLUBeVpyVv17TEcGCBuWf0zNtg3M5m1NhhWQ==",
+      "version": "8.18.0",
+      "resolved": "https://registry.npmjs.org/pino/-/pino-8.18.0.tgz",
+      "integrity": "sha512-Mz/gKiRyuXu4HnpHgi1YWdHQCoWMufapzooisvFn78zl4dZciAxS+YeRkUxXl1ee/SzU80YCz1zpECCh4oC6Aw==",
       "dependencies": {
         "atomic-sleep": "^1.0.0",
         "fast-redact": "^3.1.1",
@@ -983,9 +1001,9 @@
       "integrity": "sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA=="
     },
     "node_modules/prettier": {
-      "version": "3.2.1",
-      "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.1.tgz",
-      "integrity": "sha512-qSUWshj1IobVbKc226Gw2pync27t0Kf0EdufZa9j7uBSJay1CC+B3K5lAAZoqgX3ASiKuWsk6OmzKRetXNObWg==",
+      "version": "3.2.4",
+      "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.4.tgz",
+      "integrity": "sha512-FWu1oLHKCrtpO1ypU6J0SbK2d9Ckwysq6bHj/uaCP26DxrPpppCLQRGVuqAxSTvhF00AcvDRyYrLNW7ocBhFFQ==",
       "dev": true,
       "bin": {
         "prettier": "bin/prettier.cjs"

+ 5 - 4
package.json

@@ -6,18 +6,19 @@
   },
   "dependencies": {
     "@raydium-io/raydium-sdk": "^1.3.1-beta.47",
-    "@solana/spl-token": "^0.3.11",
+    "@solana/spl-token": "^0.4.0",
+    "@solana/web3.js": "^1.89.1",
     "bigint-buffer": "^1.1.5",
     "bn.js": "^5.2.1",
     "bs58": "^5.0.0",
-    "dotenv": "^16.3.2",
-    "pino": "^8.17.2",
+    "dotenv": "^16.4.1",
+    "pino": "^8.18.0",
     "pino-pretty": "^10.3.1",
     "pino-std-serializers": "^6.2.2"
   },
   "devDependencies": {
     "@types/bn.js": "^5.1.5",
-    "prettier": "^3.2.1",
+    "prettier": "^3.2.4",
     "ts-node": "^10.9.2",
     "typescript": "^5.3.3"
   }