# 监控raydium v4新流动性池

## 基本思路

1. 监听Raydium Liquidity Pool V4的log，过滤出包含initialize2方法的交易
2. 解析交易，获得流动池信息

```
# 用到的库
const solanaWeb3 = require('@solana/web3.js');
const splToken = require('@solana/spl-token');
const { Metadata, deprecated } = require('@metaplex-foundation/mpl-token-metadata');
const bs58 = require('bs58');
const { struct, u8, nu64 } = require('@solana/buffer-layout');
```

## 1. 监听Raydium Liquidity Pool V4的log

```
const connection = new solanaWeb3.Connection('{rpc}', 'confirmed'); // {rpc}处填写为自己的rpc
const raydiumV4Address = '675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8'
const raydiumV4PublicKey = new solanaWeb3.PublicKey('675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8');

connection.onLogs(
        raydiumV4PublicKey,
        ({ logs, err, signature }) => {
            if (err) return;

            if (logs && logs.some(log => log.includes("initialize2"))) {
                console.log("Signature for 'initialize2':", signature);
            }
        },
        "confirmed"
    );
```

这样我们就可以获取到所有刚创建池子的交易，输出应该如下：

```
Monitoring logs for program: 675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8
Signature for 'initialize2': 2YY7BM9NMH8fbSgNsy6YhWg6hEvnbpau4kNdmTXYcqs7UHo6NxY8MuZZH7qJke71vuNvmQmUUDA8LdkdgqYyrFP9
```

## 2. 解析交易

在solscan里可以看到交易2YY7BM9NMH8fbSgNsy6YhWg6hEvnbpau4kNdmTXYcqs7UHo6NxY8MuZZH7qJke71vuNvmQmUUDA8LdkdgqYyrFP9的详细信息。

找到Instruction Details部分，找到与Raydium Liquidity Pool V4交互的指令部分，如下图

<figure><img src="/files/4zmeIlkLZ2kCvjIsiv9L" alt=""><figcaption></figcaption></figure>

在这里，我们关注两部分：一个Input Accounts，可以获得代币信息；另一个是Instruction Data可以获得数量信息

```
# 解析交易
const tx = await connection.getParsedTransaction(
        signature,
        {
            maxSupportedTransactionVersion: 0,
            commitment: 'confirmed'
        });
```

```
# 获取Input Accounts
const accounts = tx?.transaction.message.instructions.find(ix => ix.programId.toBase58() === raydiumV4Address).accounts;

# 获取池子地址和配对的代币地址，index分别为4，8和9
const LPIndex = 4;
const tokenAIndex = 8;
const tokenBIndex = 9;

const LPAccount = accounts[LPIndex]
const tokenAAccount = accounts[tokenAIndex];
const tokenBAccount = accounts[tokenBIndex];

# 获取两种代币的名字和精度
async function fetchTokenInfo(tokenPublicKey){

    const mintAddress = new solanaWeb3.PublicKey(tokenPublicKey);
    const mintInfo = await splToken.getMint(connection, mintAddress);
    // console.log("Decimals: " + mintInfo.decimals);

    let metadataPda = await deprecated.Metadata.getPDA(mintAddress);
    let metdadataContent =  await Metadata.fromAccountAddress(connection, metadataPda);

    return [metdadataContent.data.name, mintInfo.decimals];
}

const tokenA = await fetchTokenInfo(tokenAAccount.toBase58());
const tokenB = await fetchTokenInfo(tokenBAccount.toBase58());
```

```
# 解码指令数据
const instructionData = bs58.decode(tx?.transaction.message.instructions.find(ix => ix.programId.toBase58() === raydiumV4Address).data);
const RAYDIUM_INSTRUCTION_LAYOUT = struct([
        u8('discriminator'),
        u8('nonce'),
        nu64('opentime'),
        nu64('initPcAmount'),
        nu64('initCoinAmount')
    ]);
const decodedInstruction = RAYDIUM_INSTRUCTION_LAYOUT.decode(instructionData);
```

opentime字段应该是开盘时间，有的是0（立即开盘），有的是linux时间戳，还在观察

最后打印一下结果

```
console.log(`TX: https://solscan.io/tx/${txId}`);
const displayData = [
    { "Token": tokenA[0].replace(/\x00/g, ''), "Account Public Key": tokenAAccount.toBase58(), "Amount": decodedInstruction.initCoinAmount/Math.pow(10, tokenA[1]) },
    { "Token": tokenB[0].replace(/\x00/g, ''), "Account Public Key": tokenBAccount.toBase58(), "Amount": decodedInstruction.initPcAmount/Math.pow(10, tokenB[1]) }
];
console.log(`New LP: ${LPAccount.toBase58()}`)
if(decodedInstruction.opentime !== 0) {
    const opentime = moment.unix(decodedInstruction.opentime).tz('Asia/Shanghai').format('YYYY-MM-DD HH:mm:ss');
    console.log(`opentime: ${opentime}`)
}
console.table(displayData);
```

## 完整代码

```
const solanaWeb3 = require('@solana/web3.js');
const splToken = require('@solana/spl-token');
const { Metadata, deprecated } = require('@metaplex-foundation/mpl-token-metadata');
const bs58 = require('bs58');
const { struct, u8, nu64 } = require('@solana/buffer-layout');
// const moment = require('moment-timezone');

const connection = new solanaWeb3.Connection('http://45.250.254.126:1256', 'confirmed');
const raydiumV4Address = '675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8'
const raydiumV4PublicKey = new solanaWeb3.PublicKey('675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8');

async function main() {

    console.log("Monitoring logs for program:", raydiumV4PublicKey.toString());

    connection.onLogs(
        raydiumV4PublicKey,
        ({ logs, err, signature }) => {
            if (err) return;

            if (logs && logs.some(log => log.includes("initialize2"))) {
                // console.log("Signature for 'initialize2':", signature);
                fetchPoolInfo(signature, connection);
            }
        },
        "confirmed"
    );

    // fetchPoolInfo('2isUeFKrgwpfh6kGKjaQ6SrrjxAHycyxqnvaDbR8EVFLo4R2jadxGp8o4gsHkBZgru2U3DRDRHggttfmbNj37gun', connection);
}

async function fetchPoolInfo(signature, connection) {
    const tx = await connection.getParsedTransaction(
        signature,
        {
            maxSupportedTransactionVersion: 0,
            commitment: 'confirmed'
        });

    const accounts = tx?.transaction.message.instructions.find(ix => ix.programId.toBase58() === raydiumV4Address).accounts;

    if (!accounts) {
        console.log("No accounts found in the transaction.");
        return;
    }

    const LPIndex = 4;
    const tokenAIndex = 8;
    const tokenBIndex = 9;

    const LPAccount = accounts[LPIndex]
    const tokenAAccount = accounts[tokenAIndex];
    const tokenBAccount = accounts[tokenBIndex];

    const tokenA = await fetchTokenInfo(tokenAAccount.toBase58());
    const tokenB = await fetchTokenInfo(tokenBAccount.toBase58());

    const instructionData = bs58.decode(tx?.transaction.message.instructions.find(ix => ix.programId.toBase58() === raydiumV4Address).data);
    // console.log(instructionData)
    const RAYDIUM_INSTRUCTION_LAYOUT = struct([
        u8('discriminator'),
        u8('nonce'),
        nu64('opentime'),
        nu64('initPcAmount'),
        nu64('initCoinAmount')
    ]);

    const decodedInstruction = RAYDIUM_INSTRUCTION_LAYOUT.decode(instructionData);
    // console.log('Decoded instruction:', {
    //     discriminator: {
    //         type: 'u8',
    //         data: decodedInstruction.discriminator
    //     },
    //     nonce: {
    //         type: 'u8',
    //         data: decodedInstruction.nonce
    //     },
    //     opentime: {
    //         type: 'u64',
    //         data: decodedInstruction.opentime
    //     },
    //     initPcAmount: {
    //         type: 'u64',
    //         data: decodedInstruction.initPcAmount.toString()
    //     },
    //     initCoinAmount: {
    //         type: 'u64',
    //         data: decodedInstruction.initCoinAmount.toString()
    //     }
    // });

    console.log(`TX: https://solscan.io/tx/${signature}`);
    const displayData = [
        { "Token": tokenA[0].replace(/\x00/g, ''), "Account Public Key": tokenAAccount.toBase58(), "Amount": decodedInstruction.initCoinAmount/Math.pow(10, tokenA[1]) },
        { "Token": tokenB[0].replace(/\x00/g, ''), "Account Public Key": tokenBAccount.toBase58(), "Amount": decodedInstruction.initPcAmount/Math.pow(10, tokenB[1]) }
    ];
    console.log(`New LP: ${LPAccount.toBase58()}`)
    // if(decodedInstruction.opentime !== 0) {
    //     const opentime = moment.unix(decodedInstruction.opentime).tz('Asia/Shanghai').format('YYYY-MM-DD HH:mm:ss');
    //     console.log(`opentime: ${opentime}`)
    // }
    console.table(displayData);
}

async function fetchTokenInfo(tokenPublicKey){

    const mintAddress = new solanaWeb3.PublicKey(tokenPublicKey);
    const mintInfo = await splToken.getMint(connection, mintAddress);
    // console.log("Decimals: " + mintInfo.decimals);
    // console.log("Supply: " + mintInfo.supply);

    let metadataPda = await deprecated.Metadata.getPDA(mintAddress);
    let metdadataContent =  await Metadata.fromAccountAddress(connection, metadataPda);

    return [metdadataContent.data.name, mintInfo.decimals];
}

main().catch(console.error);
// https://dexscreener.com/?rankBy=pairAge&order=asc&chainIds=solana&dexIds=raydium
```

<figure><img src="/files/sLpHoFJiLtWkHiBFbQls" alt=""><figcaption></figcaption></figure>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://chainbuff.gitbook.io/tutorials/solana/lian-shang-ce-le/jian-kong-raydium-v4-xin-liu-dong-xing-chi.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
