# 聪明钱监控

## 用到的库

```
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";
```

## 监听包含聪明钱账户的交易

本部分使用geyser grpc订阅的方式获取链上与聪明钱地址有关的交易，并再次过滤了一下聪明钱地址与pump和raydium合约交互的交易（因为此地址可能会有一些其他类型的交易，例如此处未考虑在内的转账交易等）

因为聪明钱的交易频率不高，所以此处所指的聪明钱其实是一个高频交易的pump狙击，经常可以做到+0和+1冲开盘

```
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<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) {
        const accountKeys = data.transaction.transaction.transaction.message.accountKeys.map(ak => 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<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;
```

这样就监听过滤出了所有聪明钱地址跟pump和raydium交互的交易，后续通过一个`checkBalances`函数来解析余额变动

## 解析余额变动

先来看一笔pump买入的交易示例，主要关注两部分：

* solana余额的变化
* 聪明钱账户代币余额的变化

<figure><img src="/files/inZ6qezKFV0nQ0NN57Bm" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/xkb99hOgpPAEd3ddhSC6" alt=""><figcaption></figcaption></figure>

可以看到这笔交易实际总花费2.545549387个sol买入了80,144,432.9898个某代币，所以我们的checkBalances函数先打印一下这两部分

```
async function checkBalances (data) {

    // sol balance
    console.log('preBalances', data.transaction.transaction.meta.preBalances)
    console.log('postBalances', data.transaction.transaction.meta.postBalances)

    // token balance
    console.log('preTokenBalances', data.transaction.transaction.meta.preTokenBalances)
    console.log('postTokenBalances', data.transaction.transaction.meta.postTokenBalances)

    console.log('---\n')
}
```

一个示例的输出应如下，preBalances 和 postBalances 的首个元素为此账户sol余额的变动，preTokenBalances 和 postTokenBalances 为此笔交易中账户代币余额的变化，代币余额变化我们只关注owner为此聪明钱地址的部分

```
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'
  }
]
```

所以，完整的`checkBalances`函数应该如下

```
interface TokenAccount {
    accountIndex: number;
    mint: string;
    uiTokenAmount: {
        uiAmount: number;
        decimals: number;
        amount: string;
        uiAmountString: string;
    };
    owner: string;
    programId: string;
}

async function checkBalances (data) {

    console.log('slot:', data.transaction.slot);
    console.log(`https://solscan.io/tx/${bs58.encode(data.transaction.transaction.signature)}`);

    // sol balance
    // console.log('preBalances', data.transaction.transaction.meta.preBalances)
    // console.log('postBalances', data.transaction.transaction.meta.postBalances)
    const preBalance = data.transaction.transaction.meta.preBalances[0]
    const postBalance = data.transaction.transaction.meta.postBalances[0]
    const balanceChange = (postBalance - preBalance)/LAMPORTS_PER_SOL
    console.log(balanceChange, 'sol')

    // token balance
    // console.log('preTokenBalances', data.transaction.transaction.meta.preTokenBalances)
    // console.log('postTokenBalances', data.transaction.transaction.meta.postTokenBalances)
    const preTokenBalances: TokenAccount[] = data.transaction.transaction.meta.preTokenBalances
    const postTokenBalances: TokenAccount[] = data.transaction.transaction.meta.postTokenBalances
    for (const postTokenBalance of postTokenBalances) {
        if (addr.includes(postTokenBalance.owner)) {
						
            const mint = postTokenBalance.mint;
            const postTokenAmount= postTokenBalance.uiTokenAmount.uiAmount;
            const preTokenAmount = preTokenBalances.find(preBalance => preBalance.owner === postTokenBalance.owner && preBalance.mint === mint)?.uiTokenAmount.uiAmount || 0;
            const tokenBalanceChange = postTokenAmount - preTokenAmount;
						
	    // 此处获取代币名称
            let metadataPda = await deprecated.Metadata.getPDA(new PublicKey(mint));
            let metdadataContent =  await Metadata.fromAccountAddress(connection, metadataPda);
            const tokenName = metdadataContent.data.name

            console.log(tokenBalanceChange, tokenName, mint)
        }
    }

    console.log('---\n')
}
```

<figure><img src="/files/9rU1ijTHtvlUMkLQrH45" alt=""><figcaption></figcaption></figure>

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

最后，监控到聪明钱后，可以根据自己的策略买入和卖出，也可以将日志消息推送给telegram机器人，实时的接收通知消息。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://chainbuff.gitbook.io/tutorials/solana/lian-shang-ce-le/cong-ming-qian-jian-kong.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
