最終更新日: 2025-01-09
構築するもの
完全なトランザクションフローガイドでは、Koraを使用してガスレストランザクションを作成する方法を学びました。しかし、単一のトランザクションでは不十分な場合や、Kora支払い命令を含めるために単一のトランザクションに十分なスペースがない場合など、多くのシナリオが存在します。このガイドでは、Koraを使用してトランザクションのバンドルに署名し、Solana Mainnet上でのアトミックな実行のためにJitoのブロックエンジンに送信する方法を示すデモを構築します。Koraサーバーは、Jitoチップとすべてのトランザクション手数料を支払います。
最終的な結果は、動作するJitoバンドルシステムになります:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━KORA JITO BUNDLE DEMO━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━[1/4] Initializing clients→ Kora RPC: http://localhost:8080/→ Solana RPC: https://api.mainnet-beta.solana.com[2/4] Setting up keypairs→ Sender: BYJVBqQ2xV9GECc84FeoPQy2DpgoonZQFQu97MMWTbBc→ Kora signer address: 3Z1Ef7YaxK8oUMoi6exf7wYZjZKWJJsrzJXSt1c3qrDE[3/4] Creating bundle transactions→ Blockhash: 7HZUaMqV...→ Tip account: 96gYZGLn...→ Transaction 1: Kora Memo "Bundle tx #1"→ Transaction 2: Kora Memo "Bundle tx #2"→ Transaction 3: Kora Memo "Bundle tx #3"→ Transaction 4: Kora Memo "Bundle tx #4" + Jito tip✓ 4 transactions created for bundle[4/4] Signing and sending bundle✓ Bundle submitted to Jito block engine→ Bundle UUID: 8f4a3b2c-1d5e-6f7a-8b9c-0d1e2f3a4b5c━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━SUCCESS: Bundle confirmed on Solana━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━Bundle UUID:8f4a3b2c-1d5e-6f7a-8b9c-0d1e2f3a4b5c
構築方法は次のとおりです。
前提条件
このチュートリアルを始める前に、以下を確認してください:
- Kora完全トランザクションフローガイドを完了していること — これらの概念をベースに構築します
- Node.js(LTS以降)
- Solanaトランザクションに関する知識
- Jitoバンドルに関する知識
Kora v2.2.0ベータ版
重要: このガイドはKora v2.2.0ベータ版を必要とします。リリースはこちらで確認できます。これはプレリリース版であり、バグが含まれる可能性があります。
cargo install kora-cli@2.2.0-beta.7
Jitoバンドルの基本
Solanaでは、トランザクション内のすべての命令はアトミックです。つまり、1つの命令が失敗すると、トランザクション全体が失敗します。バンドルは、最大5つのトランザクションをアトミックかつ順次実行できるツールです。バンドルはチップによってインセンティブ化されており、チップが高いほど優先度が高くなります。
このガイドは、Jitoバンドルに関する基本的な理解と経験があることを前提としています。
プロジェクト構造
このデモのサンプルコードは、 Koraの例にあります:
jito-bundles/├── client/│ ├── src/│ │ └── index.ts # Bundle demo implementation│ └── package.json├── server/│ ├── kora.toml # Kora configuration with bundles enabled│ └── signers.toml # Signer configuration└── scripts/└── start-kora.sh # Server startup script
koraリポジトリをクローンして、jito-bundlesディレクトリに移動してください:
git clone https://github.com/solana-foundation/kora.gitcd kora/examples/jito-bundles
Koraサーバー設定
kora.toml
バンドルサポートの主要な設定:
[kora]rate_limit = 100[kora.auth]api_key = "kora_facilitator_api_key_example"[kora.enabled_methods]sign_bundle = truesign_and_send_bundle = trueestimate_bundle_fee = trueget_blockhash = trueget_config = trueget_payer_signer = true[validation]max_allowed_lamports = 1000000max_signatures = 10price_source = "Mock"allowed_programs = ["11111111111111111111111111111111", # System Program"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr", # Memo Program][validation.fee_payer_policy.system]allow_transfer = true # Required for Jito tip transfers[validation.price]type = "free" # No payment required for this demo[kora.bundle]enabled = true[kora.bundle.jito]block_engine_url = "https://mainnet.block-engine.jito.wtf"
バンドルサポートに重要な設定:
- sign_bundle / sign_and_send_bundle — バンドルRPCメソッドを有効化
- allow_transfer = true — Koraの署名者がJitoチップを支払うため、転送権限が必要
- bundle.enabled = true — バンドル機能のマスタースイッチ
- このデモでは公開メインネットのブロックエンジンURLを使用しています。本番環境では、プライベートブロックエンジンURLを使用してください。
signers.toml
[signer_pool]strategy = "round_robin"[[signers]]name = "main_signer"type = "memory"private_key_env = "KORA_PRIVATE_KEY"
.env.exampleを.envにリネームし、KORA_PRIVATE_KEY
環境変数をメインネットの秘密鍵に設定してください。署名者ウォレットには、以下の支払いのためにメインネット上のSOLが必要です:
- すべてのバンドルトランザクションの手数料
- Jitoチップ(最小1,000 lamport)
重要:このガイドでは、Solanaメインネット上でのJitoチップの使用方法を示しています。チップは返金不可です。
サーバーの起動
server/ディレクトリから:
kora --config kora.toml --rpc-url https://api.mainnet-beta.solana.com rpc start --signers-config signers.toml
または提供されているスクリプトを使用してください。server/ディレクトリから:
../scripts/start-kora.sh
クライアント実装
インポートから始めて、クライアント実装を段階的に説明します。
インポートと設定
import { KoraClient } from "@solana/kora";import {createNoopSigner,address,getBase64EncodedWireTransaction,partiallySignTransactionMessageWithSigners,Blockhash,KeyPairSigner,pipe,createTransactionMessage,setTransactionMessageFeePayerSigner,setTransactionMessageLifetimeUsingBlockhash,appendTransactionMessageInstruction,generateKeyPairSigner} from "@solana/kit";import { getAddMemoInstruction } from "@solana-program/memo";import { getTransferSolInstruction } from "@solana-program/system";const MINIMUM_JITO_TIP = 1_000n; // lamportsconst CONFIG = {solanaRpcUrl: "https://api.mainnet-beta.solana.com",koraRpcUrl: "http://localhost:8080/",jitoTipLamports: MINIMUM_JITO_TIP,bundleSize: 4, // We'll create 4 transactions for this demopollIntervalMs: 6000,pollTimeoutMs: 60000};
以下を設定しています:
- トランザクション構築のための Solana Kit のインポート
- デモトランザクション用の Memo program(実際の操作に置き換えます)
- Jitoチップ転送用の System Program
- RPCエンドポイントとバンドルパラメータの 設定
Jitoチップアカウント
Jitoには、SOLを送信できる 8つのチップアカウント があります。このデモではランダムに1つ選択します。
// Jito tip accounts - one is randomly selected by the block engineconst JITO_TIP_ACCOUNTS = ["96gYZGLnJYVFmbjzopPSU6QiEV5fGqZNyN9nmNhvrZU5","HFqU5x63VTqvQss8hp11i4wVV8bD44PvwucfZ2bU7gRe","Cw8CFyM9FkoMi7K7Crf6HNQqf4uEMzpKw6QNghXLvLkY","ADaUMid9yfUytqMBgopwjb2DTLSokTSzL1zt6iGPaS49","DfXygSm4jCyNCybVYYK6DwvWqjKee8pbDmJGcLWNDXjh","ADuUkR4vqLUMWXxW9gh6D6L8pMSawimctcNZ5pGwDcEt","DttWaMuVvTiduZRnguLF7jNxTgiMBZ1hyAumKUiL2KRL","3AVi9Tg9Uo68tJfuvoKvqKNWKkC5wPdSSdeBnizKZ6jT"];function getRandomTipAccount(): string {return JITO_TIP_ACCOUNTS[Math.floor(Math.random() * JITO_TIP_ACCOUNTS.length)];}
チップアカウントはJitoが運用するアドレスです。いずれかにSOLを送信することで、validatorに対してチップ額を通知します。
ステップ1:クライアントの初期化
APIキーを使用してKoraクライアントを初期化します。これはkora.tomlで設定されたものと一致します。本番環境では、環境変数から読み込むことになります。
async function initializeClients() {console.log("\n[1/4] Initializing clients");console.log(" → Kora RPC:", CONFIG.koraRpcUrl);console.log(" → Solana RPC:", CONFIG.solanaRpcUrl);const client = new KoraClient({rpcUrl: CONFIG.koraRpcUrl,apiKey: "kora_facilitator_api_key_example"});return { client };}
ステップ2:キーのセットアップ
async function setupKeys(client: KoraClient) {console.log("\n[2/4] Setting up keypairs");const senderKeypair = await generateKeyPairSigner();console.log(" → Sender:", senderKeypair.address);const { signer_address } = await client.getPayerSigner();console.log(" → Kora signer address:", signer_address);return { senderKeypair, signer_address };}
デモ用に新しいkeypairを作成するためにgenerateKeyPairSigner()を使用します。keypairはmemo
instructionsに署名するだけで、Kora手数料を支払う必要がない(設定による)ため、SOLやその他のトークンは不要です。Koraの署名者(getPayerSignerで取得)がすべての手数料とJitoチップを支払います。
ステップ3:バンドルトランザクションの作成
それでは、トランザクションのバンドルを作成しましょう。複数のトランザクションを作成し、それぞれに独自のinstructionsを持たせます。ここでは一意のmemo instructionsを使用して、Solana Mainnetに着地した後のトランザクションを簡単に検証できるようにしています。
async function createBundleTransactions(client: KoraClient,senderKeypair: KeyPairSigner,signer_address: string) {console.log("\n[3/4] Creating bundle transactions");const noopSigner = createNoopSigner(address(signer_address));const latestBlockhash = await client.getBlockhash();const tipAccount = getRandomTipAccount();console.log(" → Blockhash:", latestBlockhash.blockhash.slice(0, 8) + "...");console.log(" → Tip account:", tipAccount.slice(0, 8) + "...");const transactions: string[] = [];for (let i = 0; i < CONFIG.bundleSize; i++) {const isLastTransaction = i === CONFIG.bundleSize - 1;console.log(` → Transaction ${i + 1}: Kora Memo "Bundle tx #${i + 1}"${isLastTransaction ? " + Jito tip" : ""}`);// Build transaction with memolet transactionMessage = pipe(createTransactionMessage({version: 0}),(tx) => setTransactionMessageFeePayerSigner(noopSigner, tx),(tx) =>setTransactionMessageLifetimeUsingBlockhash({blockhash: latestBlockhash.blockhash as Blockhash,lastValidBlockHeight: 0n},tx),(tx) =>appendTransactionMessageInstruction(getAddMemoInstruction({memo: `Kora Bundle tx #${i + 1} of ${CONFIG.bundleSize}`,signers: [senderKeypair]}),tx),// Add Jito tip to the LAST transaction only(tx) =>isLastTransaction? appendTransactionMessageInstruction(getTransferSolInstruction({source: noopSigner,destination: address(tipAccount),amount: CONFIG.jitoTipLamports}),tx): tx);// Sign with sender keypair (required for memo instruction)const signedTransaction =await partiallySignTransactionMessageWithSigners(transactionMessage);const base64Transaction =getBase64EncodedWireTransaction(signedTransaction);transactions.push(base64Transaction);}console.log(` ✓ ${transactions.length} transactions created for bundle`);return transactions;}
重要:Kora署名者によるチップ支払い:KoraノードにJitoチップを支払わせたいため、「何もしない」署名者(noopSigner)を使用します。ここではKoraのアドレスがチップ転送の送信元となります。Koraはバンドル処理時にこれに署名します。
ステップ4:バンドルの署名と送信
これで、すべてをまとめてバンドルをKoraに送信し、署名とJitoのブロックエンジンへの提出を行うことができます。
async function main() {console.log("\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");console.log("KORA JITO BUNDLE DEMO");console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");try {// Step 1: Initialize clientsconst { client } = await initializeClients();// Step 2: Setup keysconst { senderKeypair, signer_address } = await setupKeys(client);// Step 3: Create bundle transactionsconst transactions = await createBundleTransactions(client,senderKeypair,signer_address);// Step 4: Sign and send bundleconsole.log("\n[4/4] Signing and sending bundle");const { bundle_uuid } = await client.signAndSendBundle({transactions,signer_key: signer_address});console.log("\nBundle UUID:");console.log(bundle_uuid);} catch (error) {console.error("\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");console.error("ERROR: Demo failed");console.error("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");console.error("\nDetails:", error);process.exit(1);}}main().catch((e) => console.error("Error:", e));
デモの実行
1. Koraサーバーを起動する
cd examples/jito-bundles/serverkora --config kora.toml --rpc-url https://api.mainnet-beta.solana.com rpc start --signers-config signers.toml
2. クライアントを実行する
新しいターミナルで、client/ディレクトリに移動してデモを実行します:
cd examples/jito-bundles/client# Install dependenciespnpm install# Run the demopnpm start
期待される出力
ステップバイステップの実行が表示され、最後に成功したバンドルが表示されるはずです。バンドルは以下を実行します:
- 4つのメモトランザクションを作成
- 最後のトランザクションにJitoチップ(1,000 lamport)を追加
- すべてのトランザクションが手数料支払者としてKoraによって署名される
- Jitoのブロックエンジンにアトミックに送信
注意:
- Jitoのデフォルトルーターはレート制限に達する可能性があります。429エラーが発生した場合は、後でもう一度試すか、より高い制限をリクエストできます。詳細については、Jitoのレート制限ドキュメントをご確認ください。
- このデモは非常に小さなチップを使用しているため、バンドルがSolana Mainnetに到達しない可能性があります。Jitoのバンドルエクスプローラーでバンドルが表示されない場合は、より高いチップで後ほど再試行できます。
何が起こったかを理解する
単一トランザクションとは異なり、以下のことが行われました:
- 複数のトランザクション — 1つのトランザクションではなく、一緒に実行する必要がある4つのトランザクションを作成しました
- Jitoチップ — バリデーターにインセンティブを与えるために、チップ転送(Koraの署名者が支払う)を追加しました
- バンドル検証 —
Koraはすべてのトランザクションが
kora.tomlで指定された要件を満たしていることを検証しました - アトミック送信 — すべてのトランザクションが単一のユニットとしてJitoに送信され、すべての手数料とチップはKoraサーバーによってKoraの署名者が支払いました
結果:4つのトランザクションすべてが順番に実行されるか、まったく実行されないかのいずれかです。部分的な状態は発生しません。
追加リソース
- サポートが必要ですか? Solana Stack ExchangeでCODE_PLACEHOLDER_6f88a92936780336_ENDタグを付けて質問してください
- Jitoドキュメント — 公式Jito MEVドキュメント
- Bundle RPCメソッド — signBundle、signAndSendBundle、estimateBundleFee
- GitHubリポジトリ — ソースコードと例
Is this page helpful?