Solana Jito bundles

在这篇文章中,我们将学习使用 TS 构建和提交 Jito bundler 到Solana网络的 实用示例。

Solana Jito bundles
一键发币: SUI | SOL | BNB | ETH | BASE | ARB | OP | POLYGON | AVAX | FTM | OK

在这篇博客中,你将学习:

  • Solana 的 交易大小限制
  • Jito bundles 是什么以及为什么我们需要它们
  • 使用 TS 构建和提交 Jito bundler 到网络的 实用示例GitHub 仓库)

1、引言

Solana 是一个快速、廉价且高性能的网络;它拥有最高的 TPS,优于其他所有链。但现实世界并不完美,任何事物都有代价。当你创造如此高效且廉价的东西时,必然会有取舍。其中一个取舍就是交易大小限制。

2、Solana 交易大小限制

Solana 网络强制执行 1232 字节 的交易大小限制,以确保每个交易都能放入单个网络数据包中。这一设计选择是经过深思熟虑且高效的。Solana 网络遵循最大传输单元(MTU)大小为 1280 字节的标准,与 IPv6 MTU 一致,并在扣除必要的头部(IPv6 头部 40 字节和分片头部 8 字节)后,剩余 1232 字节可用于数据包内容。你可以在这里了解更多

虽然 这通常足够 用于大多数用例,但在某些情况下,你可能需要更多。如果你出于某种原因想要提交两个交易,并希望其中一个先于另一个提交,并确保如果第一个交易出现问题,第二个交易永远不会到达网络,

你不能同时签署并发送两个交易,因为无法保证第一个交易会先被提交。相反,你必须先签署并提交第一个交易,等待确认后再签署并提交第二个交易。这个过程会给用户带来糟糕的体验,因为他们需要为单一操作签署两次。那么,解决方案是什么?这就是 Jito Bundles 的作用。

3、Jito

Jito 是 Solana 网络上最大的项目之一;他们做了很多酷炫的事情(查看他们的博客),但我们现在关注的是 Jito Bundles 功能。Jito Bundles 包含最多五个按顺序执行且原子化的交易,确保要么全部成功,要么全部失败。

Bundles 是如何工作的?

  • 交易者将 Bundles 提交给区块引擎
  • 区块引擎模拟 Bundles 以确定最有利可图的组合
  • 获胜的 Bundles 被发送给验证节点以包含在区块中
  • 验证节点以原子方式执行 Bundles 并收集小费

4、实用示例

在这个示例中,你将构建并提交一个 Bundle 到 Jito。你可以在 GitHub 仓库 中找到示例代码。

类似于 Solana 的优先费用,Jito 使用一种称为“Jito 小费”的机制来优先处理 Bundles。这些用户定义的金额激励验证节点优先处理 Bundles,确保更快的执行。

小费只是一个将 SOL 转账到已知 Jito 小费账户的指令,此指令应在你的 Bundle 中作为另一笔交易的一部分或作为独立交易存在,我们将在这里查看两者。

如果你继续克隆 仓库,打开 README 文件并按照说明安装依赖项(我使用的是 Bun,但你可以自由选择其他工具),然后设置环境变量,你会发现 src 文件夹中有两个文件:embedded-tip-ix.tsseparated-tip-tx.ts,它们非常相似,唯一的区别在于我们是在其中一个 Bundle 的交易中嵌入小费指令(最后一个交易),而在另一个中将其分离为独立交易。你可以在此处阅读有关小费最佳实践的更多信息 这里这里

简而言之:你应该尽量将小费指令放在 Bundle 的最后,并且最好将其放在交易中而不是独立交易中,在这种情况下,你可以确保你的小费不会因恶意验证节点或 叔块 而浪费掉。

现在让我们来看看 src/embedded-tip-ix.ts 文件并跳过导入部分(因为它很无聊),你首先看到的函数是 getRandomeTipAccountAddress

const getRandomeTipAccountAddress = async (
  searcherClient: searcher.SearcherClient,
) => {
  const account = await searcherClient.getTipAccounts();
  return new PublicKey(account[Math.floor(Math.random() * account.length)]);
};

此函数获取小费账户并随机选择一个。使用随机账户而不是始终选择第一个账户可以增加我们的 Bundle 成功的概率,如果有太多交易尝试向同一个账户小费,可能会导致其中一些失败。

接下来是 buildMemoTransaction 函数:

const MEMO_PROGRAM_ID = "Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo";

const buildMemoTransaction = (
  keypair: Keypair,
  message: string,
  recentBlockhash: string,
  tipIx?: TransactionInstruction,
): VersionedTransaction => {
  const ix = new TransactionInstruction({
    keys: [
      {
        pubkey: keypair.publicKey,
        isSigner: true,
        isWritable: true,
      },
    ],
    programId: new PublicKey(MEMO_PROGRAM_ID),
    data: Buffer.from(message),
  });

  const instructions = [ix];

  if (tipIx) instructions.push(tipIx);

  const messageV0 = new TransactionMessage({
    payerKey: keypair.publicKey,
    recentBlockhash: recentBlockhash,
    instructions,
  }).compileToV0Message();

  const tx = new VersionedTransaction(messageV0);

  tx.sign([keypair]);

  console.log("txn signature is: ", bs58.encode(tx.signatures[0]));
  return tx;
};

这是一个简单的函数,用于构建一个无实际用途的备忘录交易,但它非常便宜且简单,因此非常适合我们的示例,因为我们将在主网上运行它。它还接受一个可选的小费指令,并在提供时将其推送到交易中。

暂且搁置这一点,让我们进入重点,main 函数的前几行只是设置几个变量。

const main = async () => {
  const blockEngineUrl = process.env.BLOCK_ENGINE_URL || "";
  console.log("BLOCK_ENGINE_URL:", blockEngineUrl);

  const authKeypairPath = process.env.AUTH_KEYPAIR_PATH || "";
  console.log("AUTH_KEYPAIR_PATH:", authKeypairPath);
  const decodedKey = new Uint8Array(
    JSON.parse(Fs.readFileSync(authKeypairPath).toString()) as number[],
  );
  const keypair = Keypair.fromSecretKey(decodedKey);

  const bundleTransactionLimit = parseInt(
    process.env.BUNDLE_TRANSACTION_LIMIT || "5",
  );

之后,我们需要创建搜寻客户端,这是我们用来与 Jito 服务器通信的工具

  // 创建与 Jito 交互的搜寻客户端
  const searcherClient = searcher.searcherClient(blockEngineUrl);

我们还可以订阅 onBundleRequest 以便在我们向网络发送 Bundle 后接收更新

  // 订阅 Bundle 结果
  searcherClient.onBundleResult(
    (result) => {
      console.log("received bundle result:", result);
    },
    (e) => {
      throw e;
    },
  );

然后我们将获取小费账户并构建小费指令

  // 获取随机的小费账户地址
  const tipAccount = await getRandomeTipAccountAddress(searcherClient);
  console.log("tip account:", tipAccount);

  const rpcUrl = process.env.RPC_URL || "";
  console.log("RPC_URL:", rpcUrl);

  // 获取最新的区块哈希
  const connection = new Connection(rpcUrl, "confirmed");
  const blockHash = await connection.getLatestBlockhash();

  // 构建转账指令
  const tipIx = SystemProgram.transfer({
    fromPubkey: keypair.publicKey,
    toPubkey: tipAccount,
    lamports: 1000,
  });

如前所述,小费可以包含在一个 Bundle 的交易中,也可以是一个独立的交易。

  const transactions = [
    buildMemoTransaction(keypair, "jito test 1", blockHash.blockhash),
    // 将小费指令包含在第二个交易中
    buildMemoTransaction(keypair, "jito test 2", blockHash.blockhash, tipIx),
  ];

但是我们也可以将其作为一个独立的交易来执行,如下所示:

  const tipTx = new VersionedTransaction(
    new TransactionMessage({
      payerKey: keypair.publicKey,
      recentBlockhash: blockHash.blockhash,
      instructions: [tipIx],
    }).compileToV0Message(),
  );
  tipTx.sign([keypair]);

  const transactions = [
    buildMemoTransaction(keypair, "jito test 1", blockHash.blockhash),
    buildMemoTransaction(keypair, "jito test 2", blockHash.blockhash),
  ];

一切准备就绪后,我们可以使用这些交易构建一个捆绑包,并将其提交给Jito。

  const jitoBundle = new bundle.Bundle(
    [...transactions, tipTx],
    bundleTransactionLimit,
  );

  try {
    const resp = await searcherClient.sendBundle(jitoBundle);
    console.log("resp:", resp);
  } catch (e) {
    console.error("error sending bundle:", e);
  }

这是示例文件 src/embedded-tip-ix.ts 的完整代码 链接

现在,如果你运行该文件,你应该会看到交易签名和捆绑包ID。你可以在任何浏览器中检查这些交易,并在 Jito 探索器 中检查捆绑包ID(可能需要几分钟才能显示出来)

txn signature is:  5YQVsedCaaf1bCbTUJgd23vNfte2doB1K3CB6tKxCH7KYY7Y8rDwbmetCcgBufhz8nY1nWDeCRNqhUkWNydnsjeZ
txn signature is:  3mnhGK2X2FVnsada8YL46TYdZc7BfR14GdYTjJWk8mrG2WzBJGXdyN7aLVNP3ZkWGwUBpGypjW7JWFeYGKNRa2vR
resp: 09d2c693a232d48781f69d786276b8af04be9138b0777d313b18251271825b3c

恭喜,你已经成功提交了一个捆绑包

5、结束语

Jito 捆绑包为解决Solana交易大小限制提供了一种强大的解决方案,确保复杂操作的无缝和原子化执行。通过利用小费和智能交易捆绑,开发人员可以优化用户体验并充分利用Solana的能力。有了本指南和提供的示例,你现在拥有将Jito捆绑包有效集成到Solana项目中的工具。尝试一下并探索可能性吧!


原文链接:Atom transactions using Jito bundles

DefiPlot翻译整理,转载请标明出处

免责声明:本站资源仅用于学习目的,也不应被视为投资建议,读者在采取任何行动之前应自行研究并对自己的决定承担全部责任。
通过 NowPayments 打赏