在Raydium上启动流动性池并采取保护措施以对抗机器人操纵(第...部分)

  • Shyft_to
  • 发布于 2024-07-16 14:21
  • 阅读 13

本文介绍了如何在Raydium上启动流动性池并采取保护措施以防止机器人操纵,核心策略是通过预先购买来迷惑交易机器人,使其认为代币价格过高而不敢轻易购买,从而增加价格稳定性。文章还提供了使用Solana CLI、Raydium SDK和Jito创建和发送Jito Bundle的具体步骤和代码示例。

在 Raydium 上启动流动性池,并采取保护策略以应对机器人操纵(第一部分)

在 Raydium 上启动代币并通过预购买策略防止价格操纵的终极指南

在 Solana 上启动流动性池并进行首次交易

Raydium 是一个建立在 Solana 上的去中心化交易所,提供高速的代币交换。通过利用自动化做市商(AMM),用户可以高效地交易各种代币,而无需依赖中心化中介机构。

然而,Raydium 上的新流动性池始终面临交易机器人操纵市场的威胁,导致人为的价格飙升,然后突然暴跌,这对投资者信心产生负面影响。为了防止这种情况,

在本文中,我们将探讨一种通过自己进行初始购买来应对此问题的策略。这将误导机器人认为代币已经太贵,购买风险太大,从而增加价格稳定性。

完整的代码可以在GitHub 上的这里找到,供你参考,欢迎克隆该项目并尝试一下。

开始之前

在深入实施之前,必须安装以下工具和库:

  • Solana CLI: 用于与 Solana 网络交互。
  • Raydium SDK: 用于与 Raydium 协议交互。
  • Jito: 用于创建和发送 Jito 包。
  • Node.js 和 npm(或 yarn): 用于项目管理和包安装。

涉及的步骤——摘要

虽然整个过程最初可能看起来很复杂,但以下步骤将使你清楚地了解每个阶段。

  1. 创建流动性池指令: 使用 Raydium SDK 生成必要的指令,以启动流动性池创建过程。
  2. 预先确定池地址: 预先获取池的地址,以便在同一交易中进行后续的代币购买。
  3. 地址查找表: 通过创建对常用地址的引用,而不是在每个指令中包含整个地址,来优化交易大小。
  4. Jito 包: 将所有生成的指令打包成一个 Jito 包。Jito 包是一种将多个交易分组到单个原子操作中的机制,它可以确保包中的所有交易完全执行,或者在任何部分失败时回滚。它们还需要少量的小费来优先处理交易。
  5. 发送和监视包: 将包传输到 Jito 网络,并持续跟踪其状态,必要时实施重试。

初始化——设置 NodeJS 项目

首先,创建一个新的 Node.js 项目并安装所需的依赖项:

npm install @solana/web3.js @raydium-io/raydium-sdk jito-solana typescript

为了有效地与 Solana 网络和 Raydium 交互,我们需要设置必要的配置参数。在你的项目目录中创建一个名为 config.ts 的文件,并添加以下代码:

import {
  Connection,
  Keypair,
  PublicKey,
} from '@solana/web3.js';
import {
  ENDPOINT as _ENDPOINT,
  Currency,
  LOOKUP_TABLE_CACHE,
  MAINNET_PROGRAM_ID,
  RAYDIUM_MAINNET,
  Token,
  TOKEN_PROGRAM_ID,
  TxVersion,
} from '@raydium-io/raydium-sdk';

export const rpcUrl: string = '一个 Jito RPC 节点 URL'
export const rpcToken: string | undefined = undefined

export const wallet = Keypair.fromSecretKey(
  new Uint8Array([\
    ...key here\
  ])
)
export const lpCreateWallet = new Keypair.fromSecretKey(new Uint8Array([...]));

export const connection = new Connection(
  rpcUrl,
  'confirmed'
)

export const PROGRAMIDS = MAINNET_PROGRAM_ID

export const ENDPOINT = _ENDPOINT

export const RAYDIUM_MAINNET_API = RAYDIUM_MAINNET

export const makeTxVersion = TxVersion.V0 // LEGACY

export const addLookupTableInfo = LOOKUP_TABLE_CACHE // only mainnet. other = undefined

这些主要只是运行项目所需的配置。rpcUrl 接受运行 Jito 的 RPC 节点的 URL。wallet 是你的 solana 钱包,你将使用它来发送交易,而 lpCreateWallet 是将创建流动性池的钱包。我们将在整个指南中根据需要添加更多配置详细信息。

注意: 出于安全原因,强烈建议使用环境变量或者安全的配置管理系统来存储私钥。避免将它们直接硬编码到你的代码中。

现在我们将继续定义代币信息。为了定义这些信息,有一些辅助对象可以从 Raydium SDK 导入:

//继续 config.ts

export const DEFAULT_TOKEN = {
  SOL: new Currency(9, "USDC", "USDC"),
  WSOL: new Token(
    TOKEN_PROGRAM_ID,
    new PublicKey("So11111111111111111111111111111111111111112"),
    9,
    "WSOL",
    "WSOL"
  ),
  USDC: new Token(
    TOKEN_PROGRAM_ID,
    new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"),
    6,
    "USDC",
    "USDC"
  ),
};

从 Raydium SDK 导入的代币类已用于定义涉及的代币。SOL 是此示例中使用的报价 mint,它通常是平台上大多数交换的一部分。这里使用的基础 mint 代币是 USDC,但是你可以用你想交换的代币的详细信息替换它。成功初始化后,我们将转向交换过程。

池创建和交换过程

一旦我们建立了运行该过程所需的全部配置,我们现在就可以继续进行实际的池创建和交换过程。

这将是实际过程的起点,为此,我们将在项目根目录中的 index.ts 文件中定义它。

import { TokenAmount } from "@raydium-io/raydium-sdk";
import { DEFAULT_TOKEN, connection, wallet } from "./config";
import { getWalletTokenAccount } from "./utils";
import BN from "bn.js";
import { createPoolIx } from "./lpCreate";
import { PublicKey } from "@solana/web3.js";
import createLookupTable from "./createLookupTable";
import createSwapIx from "./swapCreate";
import submitJitoBundle from "./submitJitoBundle";

async function main() {
.....//其余代码

代币和金额定义main 函数中,定义流动性池的基础代币和报价代币:

const inputToken = DEFAULT_TOKEN.WSOL; // 替换为基础代币
const outputToken = DEFAULT_TOKEN.USDC; // 替换为报价代币

const createLpBaseAmount = new BN(100000000000000);  //基础代币的数量
const createLpQuoteAmount = new BN(100000000000000); //报价代币的数量

这里的输入和输出代币在 config.ts 文件中定义,你可以根据需要使用自己的代币。

OpenBook 市场 ID 和交换金额

获取与你所需的代币对相对应的 OpenBook 市场 ID(本指南未涵盖)。使用 Raydium SDK 中的 TokenAmount 类指定初始交换金额:

// Openbook market ID
const marketId = "marketId";
const inputTokenAmount = new TokenAmount(inputToken, 10000); // 每次交换使用的 SOL 数量

在此示例中,我们将 10000(以 lamports 为单位)设置为用于前 N 次交换的任意帐户。

获取钱包代币账户

Raydium SDK 函数使用 TokenAccount 对象,因此我们需要一个工具将 Solana 钱包转换为 TokenAccount 对象。以下实用函数执行上述指定的任务(在 src/ 下的 utils.ts 中定义)

export async function getWalletTokenAccount(
  connection: Connection,
  wallet: PublicKey
): Promise<TokenAccount[]> {
  const walletTokenAccount = await connection.getTokenAccountsByOwner(wallet, {
    programId: TOKEN_PROGRAM_ID,
  });
  return walletTokenAccount.value.map((i) => ({
    pubkey: i.pubkey,
    programId: i.account.owner,
    accountInfo: SPL_ACCOUNT_LAYOUT.decode(i.account.data),
  }));
}

此函数检索与指定的钱包地址关联的代币帐户。

创建流动性池指令

由于我们已经设置了一个钱包代币帐户枚举器(在上一步中),我们现在可以深入研究 Create Pool 指令的代码:

  1. 获取必要的帐户信息: 检索钱包、代币和 OpenBook 市场的相关帐户数据。
  2. 构造流动性池指令: 生成创建池和转移初始流动性所需的指令。
  3. 处理潜在的错误和极端情况: 实施强大的错误处理以防止意外问题。

实施 createPoolIx 函数

让我们扩展上一节中介绍的 createPoolIx 函数:

import {
  LOOKUP_TABLE_CACHE,
  Liquidity,
  LiquidityPoolKeys,
  MAINNET_PROGRAM_ID,
  MARKET_STATE_LAYOUT_V3,
  Percent,
  TokenAccount,
  TokenAmount,
  TxVersion,
  jsonInfo2PoolKeys,
  InnerSimpleV0Transaction,
  LiquidityPoolKeysV4,
} from "@raydium-io/raydium-sdk";
import { getComputeBudgetConfig, getWalletTokenAccount } from "./utils";
import { DEFAULT_TOKEN, connection } from "./config";
import {
  Keypair,
  PublicKey,
  PublicKeyInitData,
  TransactionMessage,
  VersionedTransaction,
} from "@solana/web3.js";
import { unpackMint } from "@solana/spl-token";
import BN from "bn.js";

const inputToken = DEFAULT_TOKEN.WSOL; // USDC
const outputToken = DEFAULT_TOKEN.USDC; // RAY

interface createPoolIxReturnType {
  poolKeys: LiquidityPoolKeysV4;
  createPoolTx: VersionedTransaction;
}

// openbook market id

const marketId = "marketId here";

// raydium create pool instructions
export async function createPoolIx(
  marketId: PublicKey,
  wallet: Keypair,
  tokenAccounts: TokenAccount[],
  baseMint: PublicKey,
  quoteMint: PublicKey,
  baseAmount: BN,
  quoteAmount: BN
): Promise<void | createPoolIxReturnType> {

createPoolIx 函数需要特定的输入:OpenBook 市场 ID、LP 创建者钱包、代币帐户信息、基础 mint 和报价 mint 详细信息以及所需的代币数量。它返回一组池密钥和一个交易,或者返回一个错误。为了构造必要的池密钥,我们必须执行链上查找。

const tokenAccountInfo = await getWalletTokenAccount(
    connection,
    wallet.publicKey
  );
  const marketBufferInfo = await connection.getAccountInfo(marketId);
  if (!marketBufferInfo) throw Error("no marketBufferInfo");
  const {
    baseVault: marketBaseVault,
    quoteVault: marketQuoteVault,
    bids: marketBids,
    asks: marketAsks,
    eventQueue: marketEventQueue,
  } = MARKET_STATE_LAYOUT_V3.decode(marketBufferInfo.data);
  console.log("Base mint: ", baseMint.toString());
  console.log("Quote mint: ", quoteMint.toString());

  const accountInfo_base = await connection.getAccountInfo(baseMint);
  if (!accountInfo_base) throw Error("no accountInfo_base");
  const baseTokenProgramId = accountInfo_base.owner;
  const baseDecimals = unpackMint(
    baseMint,
    accountInfo_base,
    baseTokenProgramId
  ).decimals;
  console.log("Base Decimals: ", baseDecimals);

我们使用 MARKET_STATE_LAYOUT_V3 解码器从 OpenBook 市场帐户中提取必要的数据。这基本上是一个帐户布局,它可以帮助我们解码 OpenBook 帐户数据。此信息与代币详细信息结合使用,以创建全面的池密钥对象。

这几乎是我们如何开始池创建过程和初始步骤的前几个步骤。请查看本文的下一部分,在那里我们将继续完成创建池函数并使用 Jito 包设置交换。

下一步在 Raydium 上启动流动性池并采取保护策略以应对机器人操纵——第二部分

资源

  • 原文链接: blogs.shyft.to/launching...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
Shyft_to
Shyft_to
在 Solana上更快更智能地构建,使用Shyft的SuperIndexer、gRPC、RPC、API和SDK