Satori

教你代币安全监测

炒币的时候经常会遇到一些盘,如果你盲目的冲进去,可能就完蛋了。比如说 https://dexscreener.com/solana/36kqsauU1HLRxecizvXWMosV1t1mQEeaeXDEfZLvkRY4 这个盘,最高的时候涨到773.2M,然后迅速砸盘,一切都发生在三个小时以内。这就是因为他的mint authority还存在,每当有人买入,他就迅速冻结那个人币,导致别人只能买不能卖。你现在看这个盘可能发现mint authority已经是none了,但是之前是还有的,他修改authority是https://solscan.io/tx/3v3EuUm19Vii9vNNfexvgg7rFgy1wcmtGQemRZ94yUsk1awY9eLRGPpVUPMPxmL5WsDUi5Du8a5Ray4V1eCb55ot这个交易。

本文将简单判断代币是否是貔貅盘以及计算计算 LP 燃烧的比例。判断代币是否还有mint权限是看mint authority ,判断代币是否是貔貅盘,本质上是看代币的 freeze authority是否还在。计算LP燃烧的比例则是直接通过pool的address,来看这个池子里的lp供应量(supply) 和 储备量,来计算整个的比例判断burn了的比例 先引入一些包

import { nu64, struct, u32, u8, Layout } from "@solana/buffer-layout";
import { Connection, PublicKey } from "@solana/web3.js";
import type { ParsedAccountData } from "@solana/web3.js";
import { LIQUIDITY_STATE_LAYOUT_V4 } from "@raydium-io/raydium-sdk";
import dotenv from "dotenv";
import { logger } from "./logger";

然后自定义boollayout 以及 publickeylayout类, 这两个的作用主要是定义在buffer中如何读取和存储这两个类型的数据

class PublicKeyLayout extends Layout<PublicKey> {
  constructor(property: string) {
    super(32, property);
  }
 
  decode(buffer: Buffer, offset = 0): PublicKey {
    return new PublicKey(buffer.slice(offset, offset + this.span));
  }
 
  encode(src: PublicKey, buffer: Buffer, offset = 0): number {
    buffer.set(src.toBytes(), offset);
    return this.span;
  }
}
 
const publicKey = (property: string) => new PublicKeyLayout(property);
 
class BoolLayout extends Layout<boolean> {
  constructor(property: string) {
    super(1, property);
  }
 
  decode(buffer: Buffer, offset = 0): boolean {
    return buffer[offset] === 1;
  }
 
  encode(src: boolean, buffer: Buffer, offset = 0): number {
    buffer[offset] = src ? 1 : 0;
    return this.span;
  }
}
 
const bool = (property: string) => new BoolLayout(property);

接下来就是定义token的结构

export interface Token {
  mintAuthorityOption: 1 | 0;
  mintAuthority: PublicKey;
  supply: bigint;
  decimals: number;
  isInitialized: boolean;
  freezeAuthorityOption: 1 | 0;
  freezeAuthority: PublicKey;
}
 
export const MintLayout = struct<Token>([
  u32("mintAuthorityOption"),
  publicKey("mintAuthority"),
  nu64("supply"),
  u8("decimals"),
  bool("isInitialized"),
  u32("freezeAuthorityOption"),
  publicKey("freezeAuthority"),
]);

解析直接调用decode方法即可

export const fetchAndParseMint = async (
  mint: PublicKey,
  solanaConnection: Connection
): Promise<Token | null> => {
  try {
    let { data } = (await solanaConnection.getAccountInfo(mint)) || {};
    if (!data) return null;
 
    return MintLayout.decode(data);
  } catch {
    return null;
  }
};

获取池子的信息 然后计算比例

export const fetchLiqudityPoolState = async (
  pool: PublicKey,
  solanaConnection: Connection
) => {
  try {
    const acc = await solanaConnection.getMultipleAccountsInfo([
      new PublicKey(pool),
    ]);
    const parsed = acc.map((v: any) =>
      LIQUIDITY_STATE_LAYOUT_V4.decode(v.data)
    );
 
    const lpMint = parsed[0].lpMint;
    const lpReserve = parsed[0].lpReserve;
 
    const accInfo = await solanaConnection.getParsedAccountInfo(
      new PublicKey(lpMint)
    );
    const mintInfo = (accInfo.value?.data as ParsedAccountData)?.parsed?.info;
 
    const lpReserve2 = lpReserve / Math.pow(10, mintInfo?.decimals);
    const actualSupply = mintInfo?.supply / Math.pow(10, mintInfo?.decimals);
 
    //Calculate burn percentage
    const maxLpSupply = Math.max(actualSupply, lpReserve - 1);
    const burnAmt = lpReserve - actualSupply;
    const burnPct = (burnAmt / lpReserve) * 100;
 
    return burnPct;
  } catch {
    return null;
  }
};

完整的代码可以参考我的github