如何使用 Geyser 增强型 Websockets 监控 Solana 交易

  • 想样
  • 更新于 4天前
  • 阅读 39

介绍很长一段时间以来,我都想知道这些监控应用程序和机器人是如何工作的。经过一番痛苦的搜索,我偶然发现了Helius的GeyserEnhancedWebsockets。虽然使用它们并不免费([您需要商业或专业计划]),但它们是您可以使用的非常强大的工具。将GeyserEnhance

<!--StartFragment-->

介绍

很长一段时间以来,我都想知道这些监控应用程序和机器人是如何工作的。经过一番痛苦的搜索,我偶然发现了 Helius 的 Geyser Enhanced Websockets。虽然使用它们并不免费([您需要商业或专业计划]),但它们是您可以使用的非常强大的工具。

将 Geyser Enhanced Websockets 与 Helius 结合使用非常简单 - 粘贴您想要监控的地址并运行一些代码。您可以监控任何东西:NFT、钱包、程序、平台,几乎任何东西。您可以制作钱包跟踪器、代币跟踪器、买卖监视器、交易量监视器等。

此类 API 和工具有时可能要花费数千美元,但本文将展示一些仅需花费一小部分成本即可制作的示例。只需 499 美元(商业计划),制作自己的工具的投资回报率是无限的;与他人共享该工具或自己使用它将为您提供优势,并为您提供无限的方式来处理实时 Solana 数据。 <!--EndFragment--> <!--StartFragment-->

Raydium 的新型泳池监控器

<!--EndFragment-->


const WebSocket = require('ws');

// Create a WebSocket connection
const ws = new WebSocket('wss://atlas-mainnet.helius-rpc.com?api-key=YOUR_API_KEY');

// Function to send a request to the WebSocket server
function sendRequest(ws) {
    const request = {
        jsonrpc: "2.0",
        id: 420,
        method: "transactionSubscribe",
        params: [
            {   failed: false,
                accountInclude:    ["675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8"]
            },
            {
                commitment: "confirmed",
                encoding: "jsonParsed",
                transactionDetails: "full",
                maxSupportedTransactionVersion: 0
            }
        ]
    };
    ws.send(JSON.stringify(request));
}

<!--StartFragment-->

这段代码片段的第一部分非常简单。我们设置 API 密钥,并发送一个请求,其中包含我们想要监控的帐户,这里是675kPX9……又名 Raydium 链上程序。通过此请求,我们将获得与 Raydium 交互的所有已确认、未失败的交易。发送此请求通常会导致每秒返回数千笔交易,因此让我们专注于一种过滤噪音的简单方法。

<!--EndFragment--> <!--StartFragment-->

滤除噪音

下面的代码片段是主要的事件处理过滤逻辑。 on message 函数根据日志解析从交易返回的数据。在本例中,我们正在查看带有“initialize2: InitializeInstruction2”日志的所有交易——它告诉我们用户何时在 Raydium 上创建新的流动性池。您还可以使用任何其他日志,具体取决于您要监控的内容。我建议对您想要监控的内容进行测试交易。例如,您可以向池中添加流动性并查看日志如何查找该交易,然后过滤这些日志以获取所有添加流动性的交易。

<!--EndFragment-->


ws.on('open', function open() {
    console.log('WebSocket is open');
    sendRequest(ws);  // Send a request once the WebSocket is open
});

ws.on('message', async function incoming(data) {
    const messageStr = data.toString('utf8');
    try {
        const messageObj = JSON.parse(messageStr);

        const result = messageObj.params.result;
        const logs = result.transaction.meta.logMessages;
        const signature = result.signature; // Extract the signature
        const accountKeys = result.transaction.transaction.message.accountKeys.map(ak => ak.pubkey); // Extract only pubkeys

        if (logs && logs.some(log => log.includes("initialize2: InitializeInstruction2"))) {
            // Log the signature, and the public keys of the AMM ID
            console.log('Transaction signature:', signature);
            console.log('AMM ID:', accountKeys[2]); // Corrected to the third account for AMM ID
        }
    } catch (e) {

    }
});

ws.on('error', function error(err) {
    console.error('WebSocket error:', err);
});

ws.on('close', function close() {
    console.log('WebSocket is closed');
});

<!--StartFragment-->

知道交易有日志后,我们提取两样东西:签名,以便我们可以比较和验证我们程序的准确性;以及账户密钥,即 AMM ID(即所述池的 AMM 地址),因为许多机器人/狙击手使用 AMM ID 来发起交易。

<!--EndFragment--> <!--StartFragment-->

如果愿意,您还可以提取更多数据。例如,您可以获得创建者(通常是accountKeys中的第 17 个公钥)、用于创建池的代币(来自前/后代币余额或内部指令)、代币数量,甚至创建者收到的 LP 代币。创建者收到的 LP 代币数量可用于制作流动性销毁监视器,因为当池的创建者销毁他们的 LP 代币(基本上是他们在池中的份额的收据代币)时,他们将撤销其移除流动性的能力(如果铸币权仍在他们手中,请警惕他们可能会铸造更多代币并将其出售到池中)。

JSON 结构

<!--EndFragment--> <!--StartFragment-->

要获取更多数据,您可以查看交易的 JSON 结构,该结构可在官方 Solana[文档]中找到。或者,您可以保存 JSON 响应并使用此[格式化程序查看结构。这非常重要,因为一旦您知道交易的一般结构,您就可以提取使用区块浏览器时看到的任何数据。

下面是通用的 JSON,它包含两个重要的、深度嵌套的对象:TransactionMeta。在Transaction中,我们有消息对象,它包含最近的 blockhash、accountKeysinstructions。在Meta中,我们有前/后余额(Lamport 余额)、innerInstructionslogMessages和前/后代币余额

<!--EndFragment--> <!--StartFragment-->

Pump.Fun 监控示例

看到 Solana 上 meme 币的当前元数据后,我制作了一个非常简单的 pump.fun 监视器。只需将我们正在监控的地址替换为“ 6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P ”,即 pump.fun 链上程序,并在消息函数上使用此地址:

<!--EndFragment-->


ws.on('message', function incoming(data) {
    const messageStr = data.toString('utf8');
    try {
        const messageObj = JSON.parse(messageStr);

        const result = messageObj.params.result;
        const logs = result.transaction.meta.logMessages;
        const signature = result.signature; // Extract the signature
        const accountKeys = result.transaction.transaction.message.accountKeys.map(ak => ak.pubkey);

        if (logs && logs.some(log => log.includes('Program log: Instruction: InitializeMint2'))) {
            console.log('New pump.fun token!');
            console.log('tx:', signature);
            console.log('Creator:', accountKeys[0]);
            console.log('Token:', accountKeys[1]);

            // Log the first and second account keys if they exist

        }
    } catch (e) {

    }
});

<!--StartFragment-->

与 Raydium 的方法类似,我们查看与 pump.fun 程序交互的所有交易,并根据所需日志进行过滤(我们这里有一个模式:日志、日志、日志)。一旦我们拥有了包含所需日志的交易,我们就会提取签名、创建者和代币本身!

您可以看到accountKeys和签名如何嵌套在 JSON 中的同一位置,就像 Raydium 示例中一样,因此很容易获取创建者、令牌和签名。大多数交易的 JSON 结构基本相同,但某些值可能位于不同位置或以不同顺序排列 — 您将在下一个示例中看到这一点。

无论如何,这都是一款非常简单的新 pump.fun 代币监控器。使用此信息,您可以购买代币或仅出于监控目的了解其存在。

<!--EndFragment--> <!--StartFragment-->

upiter DCA 监测仪

这个例子相对小众,但它展示了你可以使用日志查看所有内容。通过以下请求,我们将查看 Jupiter 的 DCA([美元成本平均法])程序,更具体地说,查看使用 Jupiter 进行的每笔 DCA 购买。此示例可用于套利交易计算或查看潜在的市场影响。查看 DCA 购买的潜在市场影响是相当未充分利用的数据。

如您所见,到目前为止还没有做任何复杂的事情。我们在顶部添加了一个 base58 导入,并将地址更改为 Jupiter DCA 程序。接下来的部分可能看起来相当复杂,确实如此,但我觉得有必要举一个更复杂的例子。 <!--EndFragment-->


const WebSocket = require("ws");
const bs58 = require("bs58");
// Create a WebSocket connection
const ws = new WebSocket(
  "wss://atlas-mainnet.helius-rpc.com?api-key=YOUR_API_KEY"
);

// Function to send a request to the WebSocket server
function sendRequest(ws) {
  const request = {
    jsonrpc: "2.0",
    id: 420,
    method: "transactionSubscribe",
    params: [
      {
        failed: false,
        accountInclude: ["DCA265Vj8a9CEuX1eb1LWRnDT7uK6q1xMipnNyatn23M"],
      },
      {
        commitment: "confirmed",
        encoding: "jsonParsed",
        transactionDetails: "full",
        maxSupportedTransactionVersion: 0,
      },
    ],
  };
  ws.send(JSON.stringify(request));
}

<!--StartFragment-->

我们在这里做的事情是:

  • 使用 DCA 日志仅过滤这些类型的交易。
  • 从交易中提取用户输入铸币输出铸币。
  • 获取指令数据原始并将字节转换为输入参数,例如用户输入的金额、销售频率以及每次销售的数量。

<!--EndFragment-->


ws.on("message", async function incoming(data) {
  const messageStr = data.toString("utf8");
  try {
    const messageObj = JSON.parse(messageStr);

    const instructions = messageObj.params.result.transaction.transaction.message.instructions; 
    const result = messageObj.params.result;
    const logs = result.transaction.meta.logMessages;
    // Extract only pubkeys

    if (
      logs &&
      logs.some((log) => log.includes("Program log: Instruction: OpenDcaV2"))
    ) {
      instructions.forEach((instruction) => {
        if (instruction.programId.includes("DCA265")) {
          if (instruction.accounts.length === 13) {
            console.log("User:", instruction.accounts[2]);
            console.log("Input Mint:", instruction.accounts[3]);
            console.log("Output Mint:", instruction.accounts[4]);

            const data = instruction.data;
            const bytedata = bs58.decode(data);

            const hexString = bytedata.toString("hex");
            const inAmountbytes = hexString.substring(16 * 2, 24 * 2);
            const cycleFrequencyBytes = hexString.substring(32 * 2, (32 + 8) * 2);
            const inAmountPerCycleBytes = hexString.substring(24 * 2, 32 * 2);

            // Reverse the byte order for little-endian interpretation
            const reversedCycleFrequencyBytes = cycleFrequencyBytes
              .match(/.{1,2}/g)
              .reverse()
              .join("");
            const reversedInAmountBytes = inAmountbytes
              .match(/.{1,2}/g)
              .reverse()
              .join("");
            const reversedInAmountPerCycleBytes = inAmountPerCycleBytes
              .match(/.{1,2}/g)
              .reverse()
              .join("");
            const cycleFrequency = BigInt("0x" + reversedCycleFrequencyBytes);
            const inAmount = BigInt("0x" + reversedInAmountBytes);
            const inAmountPerCycle = BigInt("0x" + reversedInAmountPerCycleBytes);

            console.log("Cycle Frequency every", cycleFrequency.toString() + " seconds");
            console.log("Amount input:", inAmount.toString());
            console.log("Amount  per cycle:", inAmountPerCycle.toString());

          }
        }
      });

    }
  } catch (e) {}
});*

<!--StartFragment-->

解密原始指令数据

当您看到原始指令数据时,它通常与输入参数相对应。通过这种方式获取数据并不困难,而且我们不必反序列化任何内容,这对大多数人来说可能是一个缺点。

例如,让我们从此[交易]中获取以下数据:

8e772b6da2340bb12e783a66000000006d9415754e00000037ca8a3a270000003c000000000000001000000000000000010000000000000000010000000000000000010000000000000000

现在,转到 hexed.it 并粘贴它:

<!--EndFragment--> <!--StartFragment-->

在右侧,我们输入336971797613进行搜索。单击“查找下一个”,显示inAmount值在字节中的位置。

<!--EndFragment-->

  • 原创
  • 学分: 0
  • 分类: Solana
  • 标签:
点赞 0
收藏 0
分享

0 条评论

请先 登录 后评论
想样
想样
江湖只有他的大名,没有他的介绍。