介绍很长一段时间以来,我都想知道这些监控应用程序和机器人是如何工作的。经过一番痛苦的搜索,我偶然发现了Helius的GeyserEnhancedWebsockets。虽然使用它们并不免费([您需要商业或专业计划]),但它们是您可以使用的非常强大的工具。将GeyserEnhance
<!--StartFragment-->
很长一段时间以来,我都想知道这些监控应用程序和机器人是如何工作的。经过一番痛苦的搜索,我偶然发现了 Helius 的 Geyser Enhanced Websockets。虽然使用它们并不免费([您需要商业或专业计划]),但它们是您可以使用的非常强大的工具。
将 Geyser Enhanced Websockets 与 Helius 结合使用非常简单 - 粘贴您想要监控的地址并运行一些代码。您可以监控任何东西:NFT、钱包、程序、平台,几乎任何东西。您可以制作钱包跟踪器、代币跟踪器、买卖监视器、交易量监视器等。
此类 API 和工具有时可能要花费数千美元,但本文将展示一些仅需花费一小部分成本即可制作的示例。只需 499 美元(商业计划),制作自己的工具的投资回报率是无限的;与他人共享该工具或自己使用它将为您提供优势,并为您提供无限的方式来处理实时 Solana 数据。 <!--EndFragment--> <!--StartFragment-->
<!--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 代币(基本上是他们在池中的份额的收据代币)时,他们将撤销其移除流动性的能力(如果铸币权仍在他们手中,请警惕他们可能会铸造更多代币并将其出售到池中)。
<!--EndFragment--> <!--StartFragment-->
要获取更多数据,您可以查看交易的 JSON 结构,该结构可在官方 Solana[文档]中找到。或者,您可以保存 JSON 响应并使用此[格式化程序查看结构。这非常重要,因为一旦您知道交易的一般结构,您就可以提取使用区块浏览器时看到的任何数据。
下面是通用的 JSON,它包含两个重要的、深度嵌套的对象:Transaction和Meta。在Transaction中,我们有消息对象,它包含最近的 blockhash、accountKeys和instructions。在Meta中,我们有前/后余额(Lamport 余额)、innerInstructions、logMessages和前/后代币余额。
<!--EndFragment--> <!--StartFragment-->
看到 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-->
这个例子相对小众,但它展示了你可以使用日志查看所有内容。通过以下请求,我们将查看 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-->
我们在这里做的事情是:
<!--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-->
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!