<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>InkyWang (Inky)</title>
    <link>https://soldev.cn/InkyWang</link>
    <description>https://x.com/InkyWang</description>
    <language>en-us</language>
    <item>
      <title>Yellowstone gRPC 简明教程</title>
      <description>&lt;p&gt;&lt;img src="/uploads/photo/InkyWang/73cffe40-d6fc-4e2f-9940-07ebd2815dc2.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;我写了一个使用 Solana Yellowstone gRPC (Geyser) 插件快速获取链上数据的简明教程，仍在持续更新中~&lt;/p&gt;

&lt;p&gt;👇👇👇&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ChainBuff/yellowstone-grpc" rel="nofollow" target="_blank"&gt;https://github.com/ChainBuff/yellowstone-grpc&lt;/a&gt;&lt;/p&gt;</description>
      <author>InkyWang</author>
      <pubDate>Sat, 04 Jan 2025 20:08:55 +0800</pubDate>
      <link>https://soldev.cn/topics/131</link>
      <guid>https://soldev.cn/topics/131</guid>
    </item>
    <item>
      <title>适合初学者的 web3js 教程</title>
      <description>&lt;p&gt;&lt;img src="/uploads/photo/InkyWang/34e05bd2-6646-4aeb-8da7-27561171365b.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;最近看到社区有不少 Solana 技术的初学者，随即着手写了一个能快速上手的 Solana web3.js 教程，仍在持续更新中~&lt;/p&gt;

&lt;p&gt;👇👇👇&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ChainBuff/solana-web3js" rel="nofollow" target="_blank"&gt;https://github.com/ChainBuff/solana-web3js&lt;/a&gt;&lt;/p&gt;</description>
      <author>InkyWang</author>
      <pubDate>Wed, 20 Nov 2024 01:35:38 +0800</pubDate>
      <link>https://soldev.cn/topics/104</link>
      <guid>https://soldev.cn/topics/104</guid>
    </item>
    <item>
      <title>如何自己写一个聪明钱监控</title>
      <description>&lt;p&gt;聪明钱跟单指监听链上胜率高的地址后跟随买入和卖出，本文提供了一个简单的 demo，教你如何监控聪明钱地址。主要内容包括：① 基于 geyser grpc 监听聪明钱包地址与 pump 和 raydium 交互的交易；② 解析交易中的余额变动&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;聪明钱跟单风险极大，请对自己的钱包负责！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="用到的库"&gt;用到的库&lt;/h2&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import "dotenv/config";
import Client, {CommitmentLevel, SubscribeRequest} from '@triton-one/yellowstone-grpc';
import bs58 from "bs58";
import {Connection, PublicKey, LAMPORTS_PER_SOL} from "@solana/web3.js";
import{ Metadata, deprecated } from "@metaplex-foundation/mpl-token-metadata";
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="监听包含聪明钱账户的交易"&gt;监听包含聪明钱账户的交易&lt;/h2&gt;
&lt;p&gt;本部分使用 geyser grpc 订阅的方式获取链上与聪明钱地址有关的交易，并再次过滤了一下聪明钱地址与 pump 和 raydium 合约交互的交易（因为此地址可能会有一些其他类型的交易，例如此处未考虑在内的转账交易等）&lt;/p&gt;

&lt;p&gt;因为聪明钱的交易频率不高，所以此处所指的聪明钱其实是一个高频交易的 pump 狙击，经常可以做到 +0 和 +1 冲开盘&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const addr = ['orcACRJYTFjTeo2pV8TfYRTpmqfoYgbVi9GeANXTCc8'] // 聪明钱地址

const client = new Client('https://grpc.chainbuff.com', undefined, {
    "grpc.max_receive_message_length": 64 * 1024 * 1024, // 64MiB
});

const stream = await client.subscribe();

const streamClosed = new Promise&amp;lt;void&amp;gt;((resolve, reject) =&amp;gt; {
    stream.on("error", (error) =&amp;gt; {
        reject(error);
        stream.end();
    });
    stream.on("end", () =&amp;gt; {
        resolve();
    });
    stream.on("close", () =&amp;gt; {
        resolve();
    });
});

stream.on("data", async (data) =&amp;gt; {

    if (data.transaction) {
        const accountKeys = data.transaction.transaction.transaction.message.accountKeys.map(ak =&amp;gt; bs58.encode(ak));
        if (accountKeys.includes('6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P') || accountKeys.includes('675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8')) {

                // 解析余额变动
            checkBalances(data)
        }
    }
});

// 配置订阅
const request = {
    accounts: {},
    slots: {},
    transactions: {},
    blocks: {},
    blocksMeta: {},
    entry: {},
    commitment: CommitmentLevel.CONFIRMED,
    accountsDataSlice: [],
    ping: undefined,
};

request.transactions.tx = {
    vote: false,
    failed: false,
    signature: undefined,
    accountInclude: addr,
    accountExclude: [],
    accountRequired: [],
};

await new Promise&amp;lt;void&amp;gt;((resolve, reject) =&amp;gt; {
    stream.write(request, (err) =&amp;gt; {
        if (err === null || err === undefined) {
            resolve();
        } else {
            reject(err);
        }
    });
}).catch((reason) =&amp;gt; {
    console.error(reason);
    throw reason;
});

await streamClosed;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样就监听过滤出了所有聪明钱地址跟 pump 和 raydium 交互的交易，后续通过一个&lt;code&gt;checkBalances&lt;/code&gt;函数来解析余额变动
注意此处 grpc 订阅链接（&lt;a href="https://grpc.chainbuff.com" rel="nofollow" target="_blank"&gt;https://grpc.chainbuff.com&lt;/a&gt;）为社区私有节点，可联系我获取&lt;/p&gt;
&lt;h2 id="解析余额变动"&gt;解析余额变动&lt;/h2&gt;
&lt;p&gt;先来看一笔 pump 买入的交易示例，主要关注两部分：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;solana 余额的变化&lt;/li&gt;
&lt;li&gt;聪明钱账户代币余额的变化&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src="/uploads/photo/InkyWang/e0cc9de6-cd45-4e5b-a138-c6773ac198a5.png!large" title="" alt=""&gt;
&lt;img src="/uploads/photo/InkyWang/e68c6874-1419-4732-8309-22fc92a3ae81.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;可以看到这笔交易实际总花费 2.545549387 个 sol 买入了 80,144,432.9898 个某代币，所以我们的 checkBalances 函数先打印一下这两部分&lt;/p&gt;
&lt;pre class="highlight hack"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;checkBalances&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;// sol balance&lt;/span&gt;
    &lt;span class="n"&gt;console&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'preBalances'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;preBalances&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;console&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'postBalances'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;postBalances&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;// token balance&lt;/span&gt;
    &lt;span class="n"&gt;console&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'preTokenBalances'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;preTokenBalances&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;console&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'postTokenBalances'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;postTokenBalances&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;console&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'---\n'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;一个示例的输出应如下，preBalances 和 postBalances 的首个元素为此账户 sol 余额的变动，preTokenBalances 和 postTokenBalances 为此笔交易中账户代币余额的变化，代币余额变化我们只关注 owner 为此聪明钱地址的部分&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;preBalances [
  '32562846002', '0',
  '65832298',    '281867236367207',
  '2039280',     '501231920',
  '1',           '1141440',
  '1',           '1009200',
  '934087680',   '8530000',
  '731913600',   '1461600',
  '0'
]
postBalances [
  '30017296615', '2039280',
  '77832298',    '281867261431207',
  '2039280',     '3007631920',
  '1',           '1141440',
  '1',           '1009200',
  '934087680',   '8530000',
  '731913600',   '1461600',
  '0'
]
preTokenBalances [
  {
    accountIndex: 4,
    mint: 'CdUVc4xQ3trWvksxyAcu22exniWxVARdrWQTPF6Qpump',
    uiTokenAmount: {
      uiAmount: 982409836.065574,
      decimals: 6,
      amount: '982409836065574',
      uiAmountString: '982409836.065574'
    },
    owner: 'HRAWGWZ86FBGdQCjoG27psb49cbFBRWN3LVwhQ8QXEBp',
    programId: 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'
  }
]
postTokenBalances [
  {
    accountIndex: 1,
    mint: 'CdUVc4xQ3trWvksxyAcu22exniWxVARdrWQTPF6Qpump',
    uiTokenAmount: {
      uiAmount: 80144432.9898,
      decimals: 6,
      amount: '80144432989800',
      uiAmountString: '80144432.9898'
    },
    owner: 'orcACRJYTFjTeo2pV8TfYRTpmqfoYgbVi9GeANXTCc8',
    programId: 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'
  },
  {
    accountIndex: 4,
    mint: 'CdUVc4xQ3trWvksxyAcu22exniWxVARdrWQTPF6Qpump',
    uiTokenAmount: {
      uiAmount: 902265403.075774,
      decimals: 6,
      amount: '902265403075774',
      uiAmountString: '902265403.075774'
    },
    owner: 'HRAWGWZ86FBGdQCjoG27psb49cbFBRWN3LVwhQ8QXEBp',
    programId: 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'
  }
]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;所以，完整的&lt;code&gt;checkBalances&lt;/code&gt;函数应该如下&lt;/p&gt;
&lt;pre class="highlight hack"&gt;&lt;code&gt;&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;TokenAccount&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;accountIndex&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;mint&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;uiTokenAmount&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;uiAmount&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;decimals&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;uiAmountString&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="n"&gt;owner&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;programId&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;checkBalances&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="n"&gt;console&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'slot:'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;slot&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;console&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`https://solscan.io/tx/${bs58.encode(data.transaction.transaction.signature)}`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// sol balance&lt;/span&gt;
    &lt;span class="c1"&gt;// console.log('preBalances', data.transaction.transaction.meta.preBalances)&lt;/span&gt;
    &lt;span class="c1"&gt;// console.log('postBalances', data.transaction.transaction.meta.postBalances)&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;preBalance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;preBalances&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;postBalance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;postBalances&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;balanceChange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;postBalance&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;preBalance&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="no"&gt;LAMPORTS_PER_SOL&lt;/span&gt;
    &lt;span class="n"&gt;console&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;balanceChange&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'sol'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;// token balance&lt;/span&gt;
    &lt;span class="c1"&gt;// console.log('preTokenBalances', data.transaction.transaction.meta.preTokenBalances)&lt;/span&gt;
    &lt;span class="c1"&gt;// console.log('postTokenBalances', data.transaction.transaction.meta.postTokenBalances)&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;preTokenBalances&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TokenAccount&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;preTokenBalances&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;postTokenBalances&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TokenAccount&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;postTokenBalances&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;postTokenBalance&lt;/span&gt; &lt;span class="no"&gt;of&lt;/span&gt; &lt;span class="no"&gt;postTokenBalances&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;postTokenBalance&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

            &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;mint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;postTokenBalance&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mint&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;postTokenAmount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;postTokenBalance&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uiTokenAmount&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uiAmount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;preTokenAmount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;preTokenBalances&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;preBalance&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;preBalance&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;owner&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="n"&gt;postTokenBalance&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;owner&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;preBalance&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mint&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="n"&gt;mint&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uiTokenAmount&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uiAmount&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;tokenBalanceChange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;postTokenAmount&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;preTokenAmount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// 此处获取代币名称&lt;/span&gt;
            &lt;span class="n"&gt;let&lt;/span&gt; &lt;span class="n"&gt;metadataPda&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;deprecated&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Metadata&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPDA&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PublicKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mint&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
            &lt;span class="n"&gt;let&lt;/span&gt; &lt;span class="n"&gt;metdadataContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nc"&gt;Metadata&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromAccountAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;metadataPda&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;tokenName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;metdadataContent&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;

            &lt;span class="n"&gt;console&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokenBalanceChange&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tokenName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mint&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;console&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'---\n'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="/uploads/photo/InkyWang/9ab7a32d-0653-4237-9fe6-2fd51b89ab63.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;关于买入和卖出可以通过 sol 和代币余额的正负来判断，如上面输出是 1 笔买单和 4 笔卖单。&lt;/p&gt;

&lt;p&gt;最后，监控到聪明钱后，可以根据自己的策略买入和卖出，也可以将日志消息推送给 telegram 机器人，实时的接收通知消息。&lt;/p&gt;</description>
      <author>InkyWang</author>
      <pubDate>Fri, 18 Oct 2024 23:18:46 +0800</pubDate>
      <link>https://soldev.cn/topics/83</link>
      <guid>https://soldev.cn/topics/83</guid>
    </item>
    <item>
      <title>如何自己写一个 pump.fun 狙击枪</title>
      <description>&lt;p&gt;更新于 2024/10/11 &lt;a href="https://x.com/InkyWang" rel="nofollow" target="_blank"&gt;https://x.com/InkyWang&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;本文转载自 Buff 社区：&lt;a href="https://chainbuff.com" rel="nofollow" target="_blank"&gt;https://chainbuff.com&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;买卖 Pump 平台代币风险极大，请对自己的钱包负责！&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Pump 作为 Solana 的发币平台，每天有大量的新流动池被创建，本文提供了一个 demo，教你如何狙击 pump 上的新流动池。主要内容包括：① 基于 geyser grpc 监听 pump 新流动池创建；② 获取买入和卖出新流动池代币所需要的账户；③ 基于 Jito 上链的买入；④ 卖出&lt;/p&gt;
&lt;h2 id="用到的库"&gt;用到的库&lt;/h2&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import "dotenv/config";
import Client, {CommitmentLevel, SubscribeRequest} from '@triton-one/yellowstone-grpc';
import bs58 from "bs58";
import {Connection, PublicKey, Transaction, SystemProgram} from "@solana/web3.js";
import {AnchorProvider, Program, Wallet} from "@coral-xyz/anchor";
import {getKeypairFromEnvironment} from "@solana-developers/helpers";
import fs from "fs";
import {createAssociatedTokenAccountInstruction, getAssociatedTokenAddress} from "@solana/spl-token";
import { BN } from "bn.js";
import axios from "axios";
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="监听pump新流动池创建"&gt;监听 pump 新流动池创建&lt;/h2&gt;
&lt;p&gt;为了保证获取链上数据的速度，本部分采用 geyser grpc 订阅的方法获取链上数据。&lt;/p&gt;

&lt;p&gt;以下为订阅 pump 新池子的代码，主要步骤为：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;创建 grpc 订阅的客户端&lt;/li&gt;
&lt;li&gt;订阅交易中包含 pump 账户（6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P）的交易&lt;/li&gt;
&lt;li&gt;判断日志中是否有 InitializeMint2 方法&lt;/li&gt;
&lt;li&gt;打印一些新池子账户，包括代币 Mint 账户，Bonding Curve 账户和 Associated Bonding Curve 账户，都是后面买入操作需要的&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const client = new Client('https://grpc.chainbuff.com', undefined, {
    "grpc.max_receive_message_length": 64 * 1024 * 1024, // 64MiB
});

const stream = await client.subscribe();

const streamClosed = new Promise&amp;lt;void&amp;gt;((resolve, reject) =&amp;gt; {
    stream.on("error", (error) =&amp;gt; {
        reject(error);
        stream.end();
    });
    stream.on("end", () =&amp;gt; {
        resolve();
    });
    stream.on("close", () =&amp;gt; {
        resolve();
    });
});

stream.on("data", async (data) =&amp;gt; {

    if (data.transaction &amp;amp;&amp;amp; data.transaction.transaction.meta.logMessages &amp;amp;&amp;amp; data.transaction.transaction.meta.logMessages.some(log =&amp;gt; log.includes("Program log: Instruction: InitializeMint2"))) {

        console.log('slot:', data.transaction.slot);
        const accountKeys = data.transaction.transaction.transaction.message.accountKeys.map(ak =&amp;gt; bs58.encode(ak));
        console.log('Transaction signature:', bs58.encode(data.transaction.transaction.signature));
        console.log('Mint:', accountKeys[1]);
        console.log('Bonding Curve:', accountKeys[3]);
        console.log('Associated Bonding Curve:', accountKeys[4]);
        console.log('---\n')
    }
});

const request = {
    accounts: {},
    slots: {},
    transactions: {},
    blocks: {},
    blocksMeta: {},
    entry: {},
    commitment: CommitmentLevel.CONFIRMED,
    accountsDataSlice: [],
    ping: undefined,
};
request.transactions.tx = {
    vote: false,
    failed: false,
    signature: undefined,
    accountInclude: ["6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"], // pump
    accountExclude: [],
    accountRequired: [],
};

await new Promise&amp;lt;void&amp;gt;((resolve, reject) =&amp;gt; {
    stream.write(request, (err) =&amp;gt; {
        if (err === null || err === undefined) {
            resolve();
        } else {
            reject(err);
        }
    });
}).catch((reason) =&amp;gt; {
    console.error(reason);
    throw reason;
});

await streamClosed;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输出应如下：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;slot: 295023291
Transaction signature: 2HoK765u7TqoYUbDH8FL1MbuKqqV2gtRgoBuw3muoZh72hmowTmgRXxHRJB5fb3nv7D7xp3GwZCAbPJZHEmhCUw1
Mint: 29M9i98AVuWDHV4w1Bfodu9XT7D5y7GpL4zWd134pump
Bonding Curve: HPrDgc4teTeVt1QWAJLMvKJsowgKXKzvUnWKVg3mEVha
Associated Bonding Curve: 9cJRoqJVGr4SXp1TF6UfMG5scTDkh9MdVjvWGnuAGEoh
---

slot: 295023297
Transaction signature: 63DDLJJ9E4gfzmPf8urUWWh3Zx4fGzbRcxCJMq884WBAxxRpHE16e2eWsp9yHJS7MaM3EgvYwBMg6QRaKD2pJxA7
Mint: FxuwwXaZGt41f1wZDBHQ7y282iw7FC7MzEjUn3dcpump
Bonding Curve: 8fiNJQa8a8YWVm3rzkqBMV1xCnDKrfoWmivPpJr5cwRj
Associated Bonding Curve: J9fh1SvH1voMSND9AeR4ZzSAAu4hRfMmRiizNxUUarZB
---
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="买入"&gt;买入&lt;/h2&gt;
&lt;p&gt;买入操作需要与 pump 合约交互，首先我们需要 pump 合约的的 idl 文件，在此获取：&lt;a href="https://chainbuff.com/d/11" rel="nofollow" target="_blank"&gt;https://chainbuff.com/d/11&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;观察一笔 pump 的买入交易会发现，在调用 pump 合约的买入指令时，&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;输入账户 1，2，8，9，10，11，12 是固定的；3，4，5 从刚才监听到的新池子创建交易中已经获取到；6 和 7 分别是 ATA 和我们的买入账户&lt;/li&gt;
&lt;li&gt;指令参数分别是买入的代币数量和最大的 sol 花费&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src="/uploads/photo/InkyWang/dec562b7-1325-4fd4-8da1-154272ccd381.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;因此，买入的交易可以按照下面的方法组装&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;创建交易&lt;/li&gt;
&lt;li&gt;添加 ATA 创建指令&lt;/li&gt;
&lt;li&gt;添加 pump 买入指令&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const payer = getKeypairFromEnvironment("PUMP_SECRET_KEY"); // 钱包私钥从本地.env文件中获取
console.log(payer.publicKey.toBase58())
const connection = new Connection('http://127.0.0.1:8899', 'confirmed');
const wallet = new Wallet(payer);
const provider = new AnchorProvider(connection, wallet, {
    commitment: 'confirmed',
});
const pumpIDL = JSON.parse(fs.readFileSync('./pump_idl.json', 'utf8')); // 此处需要IDL文件
const pumpProgramId = new PublicKey('6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P');
const pumpProgram = new Program(pumpIDL, pumpProgramId, provider);

// 创建交易
let transaction = new Transaction();

// 添加ATA创建指令
transaction.add(
    createAssociatedTokenAccountInstruction(
        payer.publicKey,
        associatedUser,
        payer.publicKey,
        new PublicKey(accountKeys[1]),
    )
)

// 获取ATA
const associatedUser = await getAssociatedTokenAddress(new PublicKey(accountKeys[1]), payer.publicKey, false);

// 添加pump买入指令
const amount = 17231 * 1e6;
const solAmount = 0.001 * 1e9;
transaction.add(
    await pumpProgram.methods
        .buy(new BN(amount.toString()), new BN(solAmount.toString()))
        .accounts({
            global: new PublicKey('4wTV1YmiEkRvAtNtsSGPtUrqRYQMe5SKy2uB4Jjaxnjf'),
            feeRecipient: new PublicKey('CebN5WGQ4jvEPvsVU4EoHEpgzq1VV7AbicfhtW4xC9iM'),
            mint: new PublicKey(accountKeys[1]),
            bondingCurve: new PublicKey(accountKeys[3]),
            associatedBondingCurve: new PublicKey(accountKeys[4]),
            associatedUser: associatedUser,
            user: payer.publicKey,
            systemProgram: new PublicKey('11111111111111111111111111111111'),
            tokenProgram: new PublicKey('TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'),
            rent: new PublicKey('SysvarRent111111111111111111111111111111111'),
            eventAuthority: new PublicKey('Ce6TQqeHC9p8KetsN6JsjHK7UTZk7nasjjnr7XxXp9F1'),
            program: new PublicKey('6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P')
        })
        .instruction()
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;至此，pump 新池子买入交易的组装其实就已经完成，通过对交易进行签名后，就可以正常发送。&lt;/p&gt;

&lt;p&gt;此处可以在自己的 rpc 上模拟一下交易指令是否组装正确&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const { blockhash } = await connection.getLatestBlockhash();
transaction.recentBlockhash = blockhash;
transaction.feePayer = payer.publicKey;

const signedTransaction = await wallet.signTransaction(transaction);

// simulate
const simulationResult = await connection.simulateTransaction(signedTransaction);
console.log(JSON.stringify(simulationResult));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输出如下，没有报错&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{"context":{"apiVersion":"2.0.3","slot":295026886},"value":{"accounts":null,"err":null,"innerInstructions":null,"logs":["Program ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL invoke [1]","Program log: Create","Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [2]","Program log: Instruction: GetAccountDataSize","Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 1569 of 382633 compute units","Program return: TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA pQAAAAAAAAA=","Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success","Program 11111111111111111111111111111111 invoke [2]","Program 11111111111111111111111111111111 success","Program log: Initialize the associated token account","Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [2]","Program log: Instruction: InitializeImmutableOwner","Program log: Please upgrade to SPL Token 2022 for immutable owner support","Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 1405 of 376046 compute units","Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success","Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [2]","Program log: Instruction: InitializeAccount3","Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4188 of 372164 compute units","Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success","Program ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL consumed 32307 of 400000 compute units","Program ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL success","Program 6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P invoke [1]","Program log: Instruction: Buy","Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [2]","Program log: Instruction: Transfer","Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4645 of 347555 compute units","Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success","Program 11111111111111111111111111111111 invoke [2]","Program 11111111111111111111111111111111 success","Program 11111111111111111111111111111111 invoke [2]","Program 11111111111111111111111111111111 success","Program 6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P invoke [2]","Program 6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P consumed 2003 of 335467 compute units","Program 6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P success","Program data: vdt/007mYe5mbiGblezgN2DrBjE9HC+Ww3+vh6e5SKBBfQ/CypN13zNdCAAAAAAAwDEMAwQAAAABDEX33hExn+8SWHycXRZ2xxy10o60OnPy0sawSA80E8GrRAlnAAAAADOdYXMHAAAAQV1OEOGSAwAz8T13AAAAAEHFO8RPlAIA","Program 6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P consumed 35954 of 367693 compute units","Program 6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P success"],"replacementBlockhash":null,"returnData":null,"unitsConsumed":68261}}
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="Jito发送交易"&gt;Jito 发送交易&lt;/h2&gt;
&lt;p&gt;使用 Jito 上链只需要①在交易的最后添加一笔给 jito tip 账户转账的指令，用于支付小费；②将交易发给 Jito 的 block engine&lt;/p&gt;

&lt;p&gt;具体代码如下&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// 添加支付小费指令
transaction.add(SystemProgram.transfer({
    fromPubkey: wallet.publicKey,
    toPubkey: new PublicKey('DttWaMuVvTiduZRnguLF7jNxTgiMBZ1hyAumKUiL2KRL'),
    lamports: 0.001*1e9,
}))

const { blockhash } = await connection.getLatestBlockhash();
transaction.recentBlockhash = blockhash;
transaction.feePayer = payer.publicKey;

const signedTransaction = await wallet.signTransaction(transaction);

// 发送交易
const serializedTransaction = signedTransaction.serialize();
const base58Transaction = bs58.encode(serializedTransaction);

const bundle_data = {
    jsonrpc: "2.0",
    id: 1,
    method: "sendBundle",
    params: [[base58Transaction]]
};
const bundle_resp = await axios.post(`https://frankfurt.mainnet.block-engine.jito.wtf/api/v1/bundles`, bundle_data, {
    headers: {
        'Content-Type': 'application/json'
    }
});
const bundle_id = bundle_resp.data.result
console.log(`sent to frankfurt, bundle id: ${bundle_id}`)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;成功的买入示例交易可以在此查看：&lt;a href="https://solscan.io/tx/3SsYxUY5F9w2Wk6APMSEGbSz7Yw13tYrRax2PECk1yF6XsTXVWuG2jQLwxCaJxdRN9MhewwZGcSpv58qMwdmLyie" rel="nofollow" target="_blank"&gt;https://solscan.io/tx/3SsYxUY5F9w2Wk6APMSEGbSz7Yw13tYrRax2PECk1yF6XsTXVWuG2jQLwxCaJxdRN9MhewwZGcSpv58qMwdmLyie&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="卖出"&gt;卖出&lt;/h2&gt;
&lt;p&gt;观察一笔卖出的交易指令会发现，输入账户与买入相同，指令参数变为了卖出的代币数量和获得的最少 sol 数量。可参照之前的买入操作写自己的卖出策略。&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/InkyWang/063ca516-dbfd-492d-9a1c-52de08b5d7a0.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;卖出策略因人而异，不仅要保证自己获取链上数据的速度和上链的速度，要买的快，还要卖的好。但要问我哪个更重要，我想说，买的快不如卖的好。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;最后，geyser grpc 订阅服务对于个人来说比较昂贵，可联系我获取社区节点的 geyser 服务，限制使用人数，无频率限制。&lt;/p&gt;</description>
      <author>InkyWang</author>
      <pubDate>Sat, 12 Oct 2024 00:00:14 +0800</pubDate>
      <link>https://soldev.cn/topics/80</link>
      <guid>https://soldev.cn/topics/80</guid>
    </item>
    <item>
      <title>Jito ShredStream 数据接口配置</title>
      <description>&lt;p&gt;&lt;a href="https://solana.com/docs/terminology#shred" rel="nofollow" target="_blank" title=""&gt;Shred&lt;/a&gt;作为 solana 网络中数据传播的最小单元，以最快的速度在网络中以 udp 的形式传播。通过拼装解析 Shred 数据流可以使得交易者抢占先机。本文教你如何开启基于 Jito 的 ShredStream，更详细的教程请参与&lt;a href="https://jito-labs.gitbook.io/mev/searcher-services/shredstream" rel="nofollow" target="_blank" title=""&gt;Jito 文档&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="申请授权pubkey"&gt;申请授权 pubkey&lt;/h2&gt;
&lt;p&gt;在此处填写 jito 提供的表单&lt;a href="https://web.miniextensions.com/WV3gZjFwqNqITsMufIEp" rel="nofollow" target="_blank" title=""&gt;注册授权 pubkey&lt;/a&gt;，此账户中不应有任何的资金，提交时只需要提交 pubkey 即可。&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/InkyWang/16980133-2acc-40b6-a636-2a5097b16448.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h2 id="防火墙放行jito的ip"&gt;防火墙放行 jito 的 ip&lt;/h2&gt;
&lt;p&gt;此处放行了 jito 所有区域的 ip，可以在&lt;a href="https://jito-labs.gitbook.io/mev/searcher-services/shredstream#firewall-configuration" rel="nofollow" target="_blank" title=""&gt;此处&lt;/a&gt;查看。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ufw allow from 74.118.140.240
ufw allow from 202.8.8.174
ufw allow from 145.40.93.84
ufw allow from 145.40.93.41
ufw allow from 141.98.216.96
ufw allow from 64.130.48.56
ufw allow from 64.130.53.8
ufw allow from 64.130.53.57
ufw allow from 202.8.9.160
ufw allow from 202.8.9.19
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="下载配置jito-shredstream-proxy"&gt;下载配置 jito-shredstream-proxy&lt;/h2&gt;
&lt;p&gt;查询自己的 tvu 端口&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bash -c "$(curl -fsSL https://raw.githubusercontent.com/jito-labs/shredstream-proxy/master/scripts/get_tvu_port.sh)"
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;开启 jito-shredstream 数据流服务，默认接收到的 shreds 数据在本地 udp 20000 端口&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone https://github.com/jito-labs/shredstream-proxy.git --recurse-submodules

RUST_LOG=info cargo run --release --bin jito-shredstream-proxy -- shredstream \
    --block-engine-url https://frankfurt.mainnet.block-engine.jito.wtf \
    --auth-keypair /root/xyz4g33hXuR84saNFe3tRgcu9NbB8AeqX5YMcLetMek.json \
    --desired-regions amsterdam,frankfurt \
    --dest-ip-ports 127.0.0.1:8001
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;--block-engine-url 为节点最近的 jito 区块引擎地址&lt;/li&gt;
&lt;li&gt;--auth-keypair 为被授权的私钥&lt;/li&gt;
&lt;li&gt;--desired-regions 为想要接收 shreds 的区域&lt;/li&gt;
&lt;li&gt;--dest-ip-ports 为要绑定的接收 shreds 的 IP 和端口，即 tvu 端口&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src="/uploads/photo/InkyWang/ca60b710-36b3-438b-aa56-84e8a6987888.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h2 id="一个简单的示例"&gt;一个简单的示例&lt;/h2&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;use pcap::{Capture, Device};
use solana_ledger::shred::Shred;
use std::error::Error;

fn main() -&amp;gt; Result&amp;lt;(), Box&amp;lt;dyn Error&amp;gt;&amp;gt; {
    // 获取网络设备列表
    let devices = Device::list()?;
    // 打印可用设备列表
    // println!("可用网络设备:");
    for (i, device) in devices.iter().enumerate() {
        println!("{}. {}", i, device.name);
    }
    // 选择要监听的设备（这里假设使用第一个设备，你可以根据需要修改）
    let device = &amp;amp;devices[0];
    // 创建捕获器
    let mut cap = Capture::from_device(device.name.as_str())?
        .promisc(true)
        .snaplen(65535)
        .open()?;
    // 设置过滤器只捕获UDP 20000端口的数据包
    cap.filter("udp port 20000", true)?;
    println!("开始监听 {} 上的UDP 20000端口...", device.name);
    while let Ok(packet) = cap.next_packet() {
        // UDP头部长度为8字节，我们需要跳过它
        let payload = &amp;amp;packet.data[42..]; // 跳过以太网头(14字节) + IP头(20字节) + UDP头(8字节)
        match Shred::new_from_serialized_shred(payload.to_vec()) {
            Ok(shred) =&amp;gt; {
                println!("接收到Shred");
                println!("ID: {:?}", shred.id());
                println!("Slot: {}", shred.slot());
                println!("Index: {}", shred.index());
                println!("数据完整: {}", shred.data_complete());
                if shred.is_data() {
                    println!("类型: 数据Shred");
                    let payload = shred.payload();
                    println!("payload长度: {}", payload.len());

                } else {
                    println!("类型: 编码Shred");
                }
                println!("------------------------");
            },
            Err(e) =&amp;gt; println!("解析Shred失败: {:?}", e),
        }
    }
    Ok(())
}
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cargo run
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="/uploads/photo/InkyWang/c3fa8b99-edb9-4804-8b60-c3832af038fc.png!large" title="" alt=""&gt;&lt;/p&gt;</description>
      <author>InkyWang</author>
      <pubDate>Tue, 08 Oct 2024 01:45:27 +0800</pubDate>
      <link>https://soldev.cn/topics/77</link>
      <guid>https://soldev.cn/topics/77</guid>
    </item>
    <item>
      <title>Yellowstone geyser 插件配置</title>
      <description>&lt;p&gt;更新于 2024/10/08 &lt;a href="https://x.com/InkyWang" rel="nofollow" target="_blank"&gt;https://x.com/InkyWang&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Geyser 插件数据流服务支持数据订阅和过滤，相比与普通的 rpc 请求可以更快速的获取链上数据。本文教你如何在自建 rpc 上安装 geyser 插件，结合之前&lt;a href="https://chainbuff.com/d/2" rel="nofollow" target="_blank" title=""&gt;如何运行一个 Solana RPC 节点教程&lt;/a&gt;，只需要下载和配置 geyser 文件和在启动参数中添加 geyser 配置文件路径。&lt;/p&gt;
&lt;h2 id="下载geyser"&gt;下载 geyser&lt;/h2&gt;
&lt;p&gt;根据自己 solana-cli 的版本在官方仓库找到对应版本，此处的 solana-cli 版本为 1.8.15&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wget https://github.com/rpcpool/yellowstone-grpc/releases/download/v1.15.3%2Bsolana.1.18.15/yellowstone-grpc-geyser-release-x86_64-unknown-linux-gnu.tar.bz2

apt-get install bzip2

tar -xvjf yellowstone-grpc-geyser-release-x86_64-unknown-linux-gnu.tar.bz2 
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="配置geyser"&gt;配置 geyser&lt;/h2&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vim yellowstone-config.json

{
    "libpath": "/root/yellowstone-grpc-geyser-release/lib/libyellowstone_grpc_geyser.so",
    "log": {
        "level": "info"
    },
    "grpc": {
        "address": "0.0.0.0:10001",
        "snapshot_plugin_channel_capacity": null,
        "snapshot_client_channel_capacity": "500_000_000",
        "channel_capacity": "5_000_000",
        "unary_concurrency_limit": 5000,
        "unary_disabled": false,
        "max_decoding_message_size": "16_777_216"
    },
    "prometheus": {
        "address": "0.0.0.0:8999"
    },
    "block_fail_action": "log"
}
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="启用geyser"&gt;启用 geyser&lt;/h3&gt;
&lt;p&gt;参照先前的 solana rpc 节点启动脚本，需要在脚本中添加 geyser 配置文件的位置&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;--geyser-plugin-config /root/yellowstone-config.json
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;完整的启动脚本如下&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nb"&gt;exec &lt;/span&gt;solana-validator &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--geyser-plugin-config&lt;/span&gt; /root/yellowstone-config.json &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--ledger&lt;/span&gt; /root/sol/ledger &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--accounts&lt;/span&gt; /root/sol/accounts &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--identity&lt;/span&gt; /root/validator-keypair.json &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--known-validator&lt;/span&gt; 7Np41oeYqPefeNQEHSv1UDhYrehxin3NStELsSKCT4K2 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--known-validator&lt;/span&gt; GdnSyH3YtwcxFvQrVVJMm1JhTS4QVX7MFsX56uJLUfiZ &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--known-validator&lt;/span&gt; DE1bawNcRJB9rVm3buyMVfr8mBEoyyu73NBovf2oXJsJ &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--known-validator&lt;/span&gt; CakcnaRDHka2gXyfbEd2d3xsvkJkqsLw2akB3zsN1D2S &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--entrypoint&lt;/span&gt; entrypoint.mainnet-beta.solana.com:8001 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--entrypoint&lt;/span&gt; entrypoint2.mainnet-beta.solana.com:8001 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--entrypoint&lt;/span&gt; entrypoint3.mainnet-beta.solana.com:8001 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--entrypoint&lt;/span&gt; entrypoint4.mainnet-beta.solana.com:8001 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--entrypoint&lt;/span&gt; entrypoint5.mainnet-beta.solana.com:8001 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--expected-genesis-hash&lt;/span&gt; 5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--full-rpc-api&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--no-voting&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--private-rpc&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--rpc-port&lt;/span&gt; 8899 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--gossip-port&lt;/span&gt; 8001 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--dynamic-port-range&lt;/span&gt; 8000-8020 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--wal-recovery-mode&lt;/span&gt; skip_any_corrupted_record &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--limit-ledger-size&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--account-index&lt;/span&gt; program-id &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--account-index&lt;/span&gt; spl-token-mint &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--account-index&lt;/span&gt; spl-token-owner &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--enable-rpc-transaction-history&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--enable-cpi-and-log-storage&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--init-complete-file&lt;/span&gt; /root/init-completed &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--log&lt;/span&gt; /root/solana-rpc.log
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="使用教程"&gt;使用教程&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://docs.triton.one/project-yellowstone/dragons-mouth-grpc-subscriptions" rel="nofollow" target="_blank"&gt;https://docs.triton.one/project-yellowstone/dragons-mouth-grpc-subscriptions&lt;/a&gt;
&lt;a href="https://github.com/rpcpool/yellowstone-grpc/tree/master/examples" rel="nofollow" target="_blank"&gt;https://github.com/rpcpool/yellowstone-grpc/tree/master/examples&lt;/a&gt;&lt;/p&gt;</description>
      <author>InkyWang</author>
      <pubDate>Tue, 08 Oct 2024 01:04:44 +0800</pubDate>
      <link>https://soldev.cn/topics/76</link>
      <guid>https://soldev.cn/topics/76</guid>
    </item>
    <item>
      <title>如何监控 solana raydium v4 新流动性池创建</title>
      <description>&lt;p&gt;更新于 2024/09/18 &lt;a href="https://x.com/InkyWang" rel="nofollow" target="_blank"&gt;https://x.com/InkyWang&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;本教程实现了监听 solana 链上 raydium dex v4 的新流动性池创建，通过解析交易获得了配对代币名称和数量，可以按照类似方法获得池子更多信息。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;如有错误，欢迎指正。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="基本思路"&gt;基本思路&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;监听 Raydium Liquidity Pool V4 的 log，过滤出包含 initialize2 方法的交易&lt;/li&gt;
&lt;li&gt;解析交易，获得流动池信息&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# 用到的库
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');
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="1. 监听Raydium Liquidity Pool V4的log"&gt;1. 监听 Raydium Liquidity Pool V4 的 log&lt;/h2&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const connection = new solanaWeb3.Connection('{rpc}', 'confirmed'); // {rpc}处填写为自己的rpc
const raydiumV4Address = '675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8'
const raydiumV4PublicKey = new solanaWeb3.PublicKey('675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8');

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

            if (logs &amp;amp;&amp;amp; logs.some(log =&amp;gt; log.includes("initialize2"))) {
                console.log("Signature for 'initialize2':", signature);
            }
        },
        "confirmed"
    );
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样我们就可以获取到所有刚创建池子的交易，输出应该如下：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Monitoring logs for program: 675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8
Signature for 'initialize2': 2YY7BM9NMH8fbSgNsy6YhWg6hEvnbpau4kNdmTXYcqs7UHo6NxY8MuZZH7qJke71vuNvmQmUUDA8LdkdgqYyrFP9
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="2. 解析交易"&gt;2. 解析交易&lt;/h2&gt;
&lt;p&gt;在 solscan 里可以看到交易&lt;a href="https://solscan.io/tx/2YY7BM9NMH8fbSgNsy6YhWg6hEvnbpau4kNdmTXYcqs7UHo6NxY8MuZZH7qJke71vuNvmQmUUDA8LdkdgqYyrFP9" rel="nofollow" target="_blank" title=""&gt;2YY7BM9NMH8fbSgNsy6YhWg6hEvnbpau4kNdmTXYcqs7UHo6NxY8MuZZH7qJke71vuNvmQmUUDA8LdkdgqYyrFP9&lt;/a&gt;的详细信息。&lt;/p&gt;

&lt;p&gt;找到 Instruction Details 部分，找到与 Raydium Liquidity Pool V4 交互的指令部分，如下图：
&lt;img src="/uploads/photo/InkyWang/7c325b71-8ba4-401e-ad53-e2d849b5470a.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;在这里，我们关注两部分：一个 Input Accounts，可以获得代币信息；另一个是 Instruction Data 可以获得数量信息&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# 解析交易
const tx = await connection.getParsedTransaction(
        signature,
        {
            maxSupportedTransactionVersion: 0,
            commitment: 'confirmed'
        });
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight hack"&gt;&lt;code&gt;&lt;span class="c1"&gt;# 获取Input Accounts&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;accounts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;instructions&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ix&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ix&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;programId&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toBase58&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="n"&gt;raydiumV4Address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;# 获取池子地址和配对的代币地址，index分别为4，8和9&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;LPIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;tokenAIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;tokenBIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;LPAccount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;LPIndex&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;tokenAAccount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;tokenAIndex&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;tokenBAccount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;tokenBIndex&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="c1"&gt;# 获取两种代币的名字和精度&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;fetchTokenInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;tokenPublicKey&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;

    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;mintAddress&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;solanaWeb3&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PublicKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokenPublicKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;mintInfo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;splToken&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getMint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mintAddress&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// console.log("Decimals: " + mintInfo.decimals);&lt;/span&gt;

    &lt;span class="n"&gt;let&lt;/span&gt; &lt;span class="n"&gt;metadataPda&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;deprecated&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Metadata&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPDA&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mintAddress&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;let&lt;/span&gt; &lt;span class="n"&gt;metdadataContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nc"&gt;Metadata&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromAccountAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;metadataPda&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;metdadataContent&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mintInfo&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;decimals&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;tokenA&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchTokenInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokenAAccount&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toBase58&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;tokenB&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchTokenInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokenBAccount&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toBase58&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# 解码指令数据
const instructionData = bs58.decode(tx?.transaction.message.instructions.find(ix =&amp;gt; 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);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;opentime 字段应该是开盘时间，有的是 0（立即开盘），有的是 linux 时间戳，还在观察&lt;/p&gt;

&lt;p&gt;最后打印一下结果&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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);
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="完整代码"&gt;完整代码&lt;/h2&gt;&lt;pre class="highlight hack"&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;solanaWeb3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'@solana/web3.js'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;splToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'@solana/spl-token'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;Metadata&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;deprecated&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'@metaplex-foundation/mpl-token-metadata'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;bs58&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'bs58'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nu64&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'@solana/buffer-layout'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// const moment = require('moment-timezone');&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;connection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;solanaWeb3&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'http://45.250.254.126:1256'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'confirmed'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;raydiumV4Address&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8'&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;raydiumV4PublicKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;solanaWeb3&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PublicKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="n"&gt;console&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Monitoring logs for program:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;raydiumV4PublicKey&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

    &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onLogs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;raydiumV4PublicKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="n"&gt;logs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;signature&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logs&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;logs&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"initialize2"&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// console.log("Signature for 'initialize2':", signature);&lt;/span&gt;
                &lt;span class="nf"&gt;fetchPoolInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="s2"&gt;"confirmed"&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// fetchPoolInfo('2isUeFKrgwpfh6kGKjaQ6SrrjxAHycyxqnvaDbR8EVFLo4R2jadxGp8o4gsHkBZgru2U3DRDRHggttfmbNj37gun', connection);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;fetchPoolInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;tx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getParsedTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;maxSupportedTransactionVersion&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;commitment&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'confirmed'&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;accounts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;instructions&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ix&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ix&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;programId&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toBase58&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="n"&gt;raydiumV4Address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;console&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"No accounts found in the transaction."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;LPIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;tokenAIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;tokenBIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;LPAccount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;LPIndex&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;tokenAAccount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;tokenAIndex&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;tokenBAccount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;tokenBIndex&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;tokenA&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchTokenInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokenAAccount&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toBase58&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;tokenB&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchTokenInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokenBAccount&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toBase58&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;instructionData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bs58&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;instructions&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ix&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ix&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;programId&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toBase58&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="n"&gt;raydiumV4Address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// console.log(instructionData)&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;RAYDIUM_INSTRUCTION_LAYOUT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="nf"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'discriminator'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nf"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'nonce'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nf"&gt;nu64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'opentime'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nf"&gt;nu64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'initPcAmount'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nf"&gt;nu64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'initCoinAmount'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;decodedInstruction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;RAYDIUM_INSTRUCTION_LAYOUT&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instructionData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// console.log('Decoded instruction:', {&lt;/span&gt;
    &lt;span class="c1"&gt;//     discriminator: {&lt;/span&gt;
    &lt;span class="c1"&gt;//         type: 'u8',&lt;/span&gt;
    &lt;span class="c1"&gt;//         data: decodedInstruction.discriminator&lt;/span&gt;
    &lt;span class="c1"&gt;//     },&lt;/span&gt;
    &lt;span class="c1"&gt;//     nonce: {&lt;/span&gt;
    &lt;span class="c1"&gt;//         type: 'u8',&lt;/span&gt;
    &lt;span class="c1"&gt;//         data: decodedInstruction.nonce&lt;/span&gt;
    &lt;span class="c1"&gt;//     },&lt;/span&gt;
    &lt;span class="c1"&gt;//     opentime: {&lt;/span&gt;
    &lt;span class="c1"&gt;//         type: 'u64',&lt;/span&gt;
    &lt;span class="c1"&gt;//         data: decodedInstruction.opentime&lt;/span&gt;
    &lt;span class="c1"&gt;//     },&lt;/span&gt;
    &lt;span class="c1"&gt;//     initPcAmount: {&lt;/span&gt;
    &lt;span class="c1"&gt;//         type: 'u64',&lt;/span&gt;
    &lt;span class="c1"&gt;//         data: decodedInstruction.initPcAmount.toString()&lt;/span&gt;
    &lt;span class="c1"&gt;//     },&lt;/span&gt;
    &lt;span class="c1"&gt;//     initCoinAmount: {&lt;/span&gt;
    &lt;span class="c1"&gt;//         type: 'u64',&lt;/span&gt;
    &lt;span class="c1"&gt;//         data: decodedInstruction.initCoinAmount.toString()&lt;/span&gt;
    &lt;span class="c1"&gt;//     }&lt;/span&gt;
    &lt;span class="c1"&gt;// });&lt;/span&gt;

    &lt;span class="n"&gt;console&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`TX: https://solscan.io/tx/${signature}`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;displayData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"Token"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;tokenA&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;\x00&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s2"&gt;"Account Public Key"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;tokenAAccount&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toBase58&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="s2"&gt;"Amount"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;decodedInstruction&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;initCoinAmount&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nc"&gt;Math&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;pow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tokenA&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"Token"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;tokenB&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;\x00&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s2"&gt;"Account Public Key"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;tokenBAccount&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toBase58&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="s2"&gt;"Amount"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;decodedInstruction&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;initPcAmount&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nc"&gt;Math&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;pow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tokenB&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="n"&gt;console&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`New LP: ${LPAccount.toBase58()}`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;// if(decodedInstruction.opentime !== 0) {&lt;/span&gt;
    &lt;span class="c1"&gt;//     const opentime = moment.unix(decodedInstruction.opentime).tz('Asia/Shanghai').format('YYYY-MM-DD HH:mm:ss');&lt;/span&gt;
    &lt;span class="c1"&gt;//     console.log(`opentime: ${opentime}`)&lt;/span&gt;
    &lt;span class="c1"&gt;// }&lt;/span&gt;
    &lt;span class="n"&gt;console&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;displayData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;fetchTokenInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;tokenPublicKey&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;

    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;mintAddress&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;solanaWeb3&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PublicKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokenPublicKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;mintInfo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;splToken&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getMint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mintAddress&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// console.log("Decimals: " + mintInfo.decimals);&lt;/span&gt;
    &lt;span class="c1"&gt;// console.log("Supply: " + mintInfo.supply);&lt;/span&gt;

    &lt;span class="n"&gt;let&lt;/span&gt; &lt;span class="n"&gt;metadataPda&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;deprecated&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Metadata&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPDA&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mintAddress&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;let&lt;/span&gt; &lt;span class="n"&gt;metdadataContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nc"&gt;Metadata&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromAccountAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;metadataPda&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;metdadataContent&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mintInfo&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;decimals&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nb"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;console&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// https://dexscreener.com/?rankBy=pairAge&amp;amp;order=asc&amp;amp;chainIds=solana&amp;amp;dexIds=raydium&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="/uploads/photo/InkyWang/da9fb48d-c376-43eb-9dc5-9289e9352161.png!large" title="" alt=""&gt;&lt;/p&gt;</description>
      <author>InkyWang</author>
      <pubDate>Sat, 21 Sep 2024 16:09:52 +0800</pubDate>
      <link>https://soldev.cn/topics/71</link>
      <guid>https://soldev.cn/topics/71</guid>
    </item>
    <item>
      <title>如何运行一个 Solana RPC 节点</title>
      <description>&lt;p&gt;2024/08/07更新&lt;/p&gt;

&lt;p&gt;根据&lt;a href="https://docs.solanalabs.com/operations/requirements#rpc-node-recommendations" rel="nofollow" target="_blank" title=""&gt;官方要求&lt;/a&gt;，Solana RPC 节点至少需要 512GB 内存。实际测试下来，在开启 account-index 参数后，v1.18+ 版本节点稳定时需要 330GB 左右内存，偶尔峰值会到 430G（推荐 512G，不然可能会内存溢出），而 v1.17+ 版本则需要近 1TB 内存。&lt;/p&gt;
&lt;h3 id="挂载磁盘"&gt;挂载磁盘&lt;/h3&gt;
&lt;p&gt;官方推荐机器至少 3 个 NVMe 盘，一个系统盘，一个存账户数据，一个存账本数据。除系统盘外，每个硬盘推荐使用 2T 的存储空间。以下为创建项目目录和挂载磁盘的命令：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir /root/sol
mkdir /root/sol/accounts
mkdir /root/sol/ledger
mkdir /root/sol/bin
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# n + e
fdisk /dev/nvme0n1
fdisk /dev/nvme1n1

mkfs -t ext4 /dev/nvme0n1
mkfs -t ext4 /dev/nvme1n1

mount /dev/nvme0n1 /root/sol/ledger
mount /dev/nvme1n1 /root/sol/accounts

vim /etc/fstab
/dev/nvme0n1 /root/sol/ledger ext4 defaults 0 0
/dev/nvme1n1 /root/sol/accounts ext4 defaults 0 0
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="将cpu设置为performance模式"&gt;将 cpu 设置为 performance 模式&lt;/h3&gt;
&lt;p&gt;Solana 节点对 cpu 主频要求较高，推荐使用高频 cpu 并将性能设置为 performance 模式。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apt install linux-tools-common linux-tools-$(uname -r)

cpupower frequency-info

cpupower frequency-set --governor performance

watch "grep 'cpu MHz' /proc/cpuinfo"
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="下载solana-cli"&gt;下载 solana-cli&lt;/h3&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# sh -c "$(curl -sSfL https://release.solana.com/stable/install)"
# 使用v1.18+版本，内存占用少
sh -c "$(curl -sSfL https://release.solana.com/v1.18.15/install)"

vim /root/.bashrc
export PATH="/root/.local/share/solana/install/active_release/bin:$PATH"
source /root/.bashrc

solana --version
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="创建验证者私钥"&gt;创建验证者私钥&lt;/h3&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;solana-keygen new -o validator-keypair.json
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="系统调优"&gt;系统调优&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;修改/etc/sysctl.conf&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vim /etc/sysctl.conf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;添加如下&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Increase UDP buffer sizes
net.core.rmem_default = 134217728
net.core.rmem_max = 134217728
net.core.wmem_default = 134217728
net.core.wmem_max = 134217728

# Increase memory mapped files limit
vm.max_map_count = 1000000

# Increase number of allowed open file descriptors
fs.nr_open = 1000000
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sysctl -p
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;修改/etc/systemd/system.conf&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vim /etc/systemd/system.conf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;添加如下&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DefaultLimitNOFILE=1000000
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;systemctl daemon-reload
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;修改/etc/security/limits.conf&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vim /etc/security/limits.conf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;添加如下&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Increase process file descriptor count limit
* - nofile 1000000
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ulimit -n 1000000 # 手动设置一下，不然需要重启机器
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="开启防火墙"&gt;开启防火墙&lt;/h3&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo ufw allow 22
sudo ufw allow 8000:8020/tcp
sudo ufw allow 8000:8020/udp
sudo ufw allow 8899 # http 端口
sudo ufw allow 8900 # websocket 端口

sudo ufw enable
sudo ufw status
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="创建启动脚本和服务"&gt;创建启动脚本和服务&lt;/h3&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vim /root/sol/bin/validator.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;添加如下&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nb"&gt;exec &lt;/span&gt;solana-validator &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--ledger&lt;/span&gt; /root/sol/ledger &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--accounts&lt;/span&gt; /root/sol/accounts &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--identity&lt;/span&gt; /root/validator-keypair.json &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--known-validator&lt;/span&gt; 7Np41oeYqPefeNQEHSv1UDhYrehxin3NStELsSKCT4K2 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--known-validator&lt;/span&gt; GdnSyH3YtwcxFvQrVVJMm1JhTS4QVX7MFsX56uJLUfiZ &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--known-validator&lt;/span&gt; DE1bawNcRJB9rVm3buyMVfr8mBEoyyu73NBovf2oXJsJ &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--known-validator&lt;/span&gt; CakcnaRDHka2gXyfbEd2d3xsvkJkqsLw2akB3zsN1D2S &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--entrypoint&lt;/span&gt; entrypoint.mainnet-beta.solana.com:8001 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--entrypoint&lt;/span&gt; entrypoint2.mainnet-beta.solana.com:8001 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--entrypoint&lt;/span&gt; entrypoint3.mainnet-beta.solana.com:8001 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--entrypoint&lt;/span&gt; entrypoint4.mainnet-beta.solana.com:8001 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--entrypoint&lt;/span&gt; entrypoint5.mainnet-beta.solana.com:8001 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--expected-genesis-hash&lt;/span&gt; 5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--full-rpc-api&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--no-voting&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--private-rpc&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--rpc-port&lt;/span&gt; 8899 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--gossip-port&lt;/span&gt; 8001 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--dynamic-port-range&lt;/span&gt; 8000-8020 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--wal-recovery-mode&lt;/span&gt; skip_any_corrupted_record &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--limit-ledger-size&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--account-index&lt;/span&gt; program-id &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--account-index&lt;/span&gt; spl-token-mint &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--account-index&lt;/span&gt; spl-token-owner &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--enable-rpc-transaction-history&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--enable-cpi-and-log-storage&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--init-complete-file&lt;/span&gt; /root/init-completed &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--log&lt;/span&gt; /root/solana-rpc.log

    &lt;span class="c"&gt;# 以下参数按需选择添加&lt;/span&gt;
    &lt;span class="c"&gt;# 务必了解每个参数的功能&lt;/span&gt;
    &lt;span class="c"&gt;# --rpc-bind-address 0.0.0.0 \&lt;/span&gt;
    &lt;span class="c"&gt;# --tpu-enable-udp \&lt;/span&gt;
    &lt;span class="c"&gt;# --only-known-rpc \&lt;/span&gt;
    &lt;span class="c"&gt;# --rpc-send-default-max-retries 0 \&lt;/span&gt;
    &lt;span class="c"&gt;# --rpc-send-service-max-retries 0 \&lt;/span&gt;
    &lt;span class="c"&gt;# --rpc-send-retry-ms 2000 \&lt;/span&gt;
    &lt;span class="c"&gt;# --minimal-snapshot-download-speed 1073741824 \&lt;/span&gt;
    &lt;span class="c"&gt;# --maximum-snapshot-download-abort 3 \&lt;/span&gt;
    &lt;span class="c"&gt;# --rpc-send-leader-count 1500 \&lt;/span&gt;
    &lt;span class="c"&gt;# --private-rpc \&lt;/span&gt;
    &lt;span class="c"&gt;# --accounts-index-memory-limit-mb 1024000 \&lt;/span&gt;
    &lt;span class="c"&gt;# --limit-ledger-size 50000000 \&lt;/span&gt;
    &lt;span class="c"&gt;# --minimal-snapshot-download-speed 1073741824 \&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;chmod +x /root/sol/bin/validator.sh
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vim /etc/systemd/system/sol.service
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;添加如下&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Unit]
Description=Solana Validator
After=network.target
StartLimitIntervalSec=0

[Service]
Type=simple
Restart=always
RestartSec=1
User=root
LimitNOFILE=1000000
LogRateLimitIntervalSec=0
Environment="PATH=/bin:/usr/bin:/root/.local/share/solana/install/active_release/bin"
ExecStart=/root/sol/bin/validator.sh

[Install]
WantedBy=multi-user.target
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# 系统服务相关命令
systemctl start sol
systemctl status sol
systemctl stop sol
systemctl restart sol
systemctl daemon-reload
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# 查看服务是否正常运行
tail -f /root/solana-rpc.log
journalctl -u sol -f --no-hostname -o cat
ps aux | grep solana-validator
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="查看同步进度"&gt;查看同步进度&lt;/h3&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;solana-keygen pubkey /root/validator-keypair.json
solana gossip | grep {pubkey}
solana catchup {pubkey}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果机器配置和网络没问题的话，应该可以看到在慢慢追块。
&lt;img src="/uploads/photo/InkyWang/fe13c379-c026-4a0c-bf71-3c4830a48d8d.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h3 id="参考"&gt;参考&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://docs.solanalabs.com/operations/setup-an-rpc-node" rel="nofollow" target="_blank"&gt;https://docs.solanalabs.com/operations/setup-an-rpc-node&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://solana.com/docs/rpc" rel="nofollow" target="_blank"&gt;https://solana.com/docs/rpc&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;</description>
      <author>InkyWang</author>
      <pubDate>Wed, 07 Aug 2024 21:00:52 +0800</pubDate>
      <link>https://soldev.cn/topics/60</link>
      <guid>https://soldev.cn/topics/60</guid>
    </item>
  </channel>
</rss>
