狙击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数量。可参照之前的买入操作写自己的卖出策略。

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

最后更新于