狙击pump
更新于2024/12/15
Pump作为Solana上的一个代币公平发售平台,每天有大量的新流动池被创建,本文提供了一个demo,教你如何狙击pump上的新流动池。主要内容包括:① 基于geyser grpc监听pump新流动池创建;② 获取买入和卖出新流动池代币所需要的账户;③ 基于Jito上链的买入;④ 卖出
用到的库
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";
监听pump新流动池创建
为了保证获取链上数据的速度,本部分采用geyser grpc订阅的方法获取链上数据。
以下为订阅pump新池子的代码,主要步骤为:
创建grpc订阅的客户端
订阅交易中包含pump账户(6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P)的交易
判断日志中是否有InitializeMint2方法
打印一些新池子账户,包括代币Mint账户,Bonding Curve账户和Associated Bonding Curve账户,都是后面买入操作需要的
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<void>((resolve, reject) => {
stream.on("error", (error) => {
reject(error);
stream.end();
});
stream.on("end", () => {
resolve();
});
stream.on("close", () => {
resolve();
});
});
stream.on("data", async (data) => {
if (data.transaction && data.transaction.transaction.meta.logMessages && data.transaction.transaction.meta.logMessages.some(log => log.includes("Program log: Instruction: InitializeMint2"))) {
console.log('slot:', data.transaction.slot);
const accountKeys = data.transaction.transaction.transaction.message.accountKeys.map(ak => 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<void>((resolve, reject) => {
stream.write(request, (err) => {
if (err === null || err === undefined) {
resolve();
} else {
reject(err);
}
});
}).catch((reason) => {
console.error(reason);
throw reason;
});
await streamClosed;
输出应如下:
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
---
买入
买入操作需要与pump合约交互,首先我们需要pump合约的的idl文件,在此获取
观察一笔pump的买入交易会发现,在调用pump合约的买入指令时,
输入账户1,2,8,9,10,11,12是固定的;3,4,5从刚才监听到的新池子创建交易中已经获取到;6和7分别是ATA和我们的买入账户
指令参数分别是买入的代币数量和最大的sol花费
因此,买入的交易可以按照下面的方法组装
创建交易
添加ATA创建指令
添加pump买入指令
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
const associatedUser = await getAssociatedTokenAddress(new PublicKey(accountKeys[1]), payer.publicKey, false);
// 添加ATA创建指令
transaction.add(
createAssociatedTokenAccountInstruction(
payer.publicKey,
associatedUser,
payer.publicKey,
new PublicKey(accountKeys[1]),
)
)
// 添加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()
);
至此,pump新池子买入交易的组装其实就已经完成,通过对交易进行签名后,就可以正常发送。
此处可以在自己的rpc上模拟一下交易指令是否组装正确
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));
输出如下,没有报错
{"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}}
Jito发送交易
使用Jito上链只需要①在交易的最后添加一笔给jito tip账户转账的指令,用于支付小费;②将交易发给Jito的block engine
具体代码如下
// 添加支付小费指令
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}`)
成功的买入示例交易可以在此查看:https://solscan.io/tx/3SsYxUY5F9w2Wk6APMSEGbSz7Yw13tYrRax2PECk1yF6XsTXVWuG2jQLwxCaJxdRN9MhewwZGcSpv58qMwdmLyie
卖出
观察一笔卖出的交易指令会发现,输入账户与买入相同,指令参数变为了卖出的代币数量和获得的最少sol数量。可参照之前的买入操作写自己的卖出策略。
卖出策略因人而异,不仅要保证自己获取链上数据的速度和上链的速度,要买的快,还要卖的好。但要问我哪个更重要,我想说,买的快不如卖的好。
最后更新于