Dernière mise à jour : 09/01/2025
Ce que vous allez construire
Dans le Guide du flux de transaction complet, vous avez appris à créer des transactions sans frais en utilisant Kora. Il existe cependant de nombreux scénarios où une seule transaction est insuffisante ou il n'y a pas assez d'espace dans une seule transaction pour inclure une instruction de paiement Kora. Dans ce guide, nous allons construire une démo qui montre comment utiliser Kora pour signer et envoyer un bundle de transactions au moteur de blocs de Jito pour une exécution atomique sur Solana Mainnet. Le serveur Kora paiera le pourboire Jito et tous les frais de transaction.
Le résultat final sera un système de bundles Jito fonctionnel :
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━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
Voici comment nous le construisons.
Prérequis
Avant de commencer ce tutoriel, assurez-vous d'avoir :
- Terminé le Guide du flux de transaction complet de Kora — nous nous appuyons sur ces concepts
- Node.js (LTS ou version ultérieure)
- Familiarité avec les transactions Solana
- Familiarité avec les bundles Jito
Kora v2.2.0 Beta
Important : Ce guide nécessite la version bêta de Kora v2.2.0. Vous pouvez trouver la version ici. Il s'agit d'une pré-version et peut contenir des bugs.
cargo install kora-cli@2.2.0-beta.7
Les bases des bundles Jito
Sur Solana, chaque instruction dans une transaction est atomique — si une instruction échoue, la transaction entière échoue. Les bundles sont un outil qui vous permet d'exécuter jusqu'à 5 transactions de manière atomique et séquentielle. Les bundles sont incentivés par un pourboire, plus le pourboire est élevé, plus la priorité est élevée.
Ce guide suppose que vous avez une compréhension de base et une expérience avec les bundles Jito.
Structure du projet
Le code d'exemple pour cette démo se trouve dans les exemples 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
Clonez le dépôt kora et accédez au répertoire jito-bundles :
git clone https://github.com/solana-foundation/kora.gitcd kora/examples/jito-bundles
Configuration du serveur Kora
kora.toml
La configuration clé pour la prise en charge des bundles :
[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"
Paramètres importants pour la prise en charge des bundles :
- sign_bundle / sign_and_send_bundle — Active les méthodes RPC de bundle
- allow_transfer = true — Le signataire de Kora paie le pourboire Jito, il a donc besoin de la permission de transfert
- bundle.enabled = true — Interrupteur principal pour la fonctionnalité de bundle
- Nous utilisons l'URL publique du moteur de blocs mainnet pour cette démo. En production, vous utiliseriez l'URL privée du moteur de blocs.
signers.toml
[signer_pool]strategy = "round_robin"[[signers]]name = "main_signer"type = "memory"private_key_env = "KORA_PRIVATE_KEY"
Assurez-vous de renommer .env.example en .env et définissez la variable
d'environnement KORA_PRIVATE_KEY avec votre clé privée mainnet. Le
portefeuille signataire a besoin de SOL sur le mainnet pour payer :
- Les frais de transaction pour toutes les transactions du bundle
- Le pourboire Jito (minimum 1 000 lamports)
Important : Ce guide démontre l'utilisation des pourboires Jito sur Solana Mainnet. Les pourboires ne sont pas remboursables.
Démarrage du serveur
Depuis le répertoire server/ :
kora --config kora.toml --rpc-url https://api.mainnet-beta.solana.com rpc start --signers-config signers.toml
Ou utilisez le script fourni. Depuis le répertoire server/ :
../scripts/start-kora.sh
Implémentation du client
Nous allons parcourir l'implémentation du client étape par étape, en commençant par les importations.
Importations et configuration
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};
Nous configurons :
- Les imports de Solana Kit pour construire les transactions
- Le programme Memo pour nos transactions de démonstration (vous le remplaceriez par de vraies opérations)
- Le System Program pour le transfert de pourboire Jito
- La configuration pour les points de terminaison RPC et les paramètres de bundle
Comptes de pourboire Jito
Jito possède 8 comptes de pourboire auxquels vous pouvez envoyer des SOL. Nous en sélectionnons un au hasard pour cette démo.
// 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)];}
Les comptes de pourboire sont des adresses gérées par Jito. Envoyer des SOL à l'une d'entre elles signale le montant de votre pourboire aux validator.
Étape 1 : Initialiser les clients
Nous initialisons le client Kora avec notre clé API, qui correspond à ce qui est
configuré dans kora.toml. En production, vous chargeriez ceci depuis une
variable d'environnement.
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 };}
Étape 2 : Configuration des clés
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 };}
Nous utilisons generateKeyPairSigner() pour créer une keypair fraîche pour la
démo. Comme la keypair ne fait que signer les instructions memo et n'a pas à
payer de frais Kora (selon notre configuration), aucun SOL ou autre jeton n'est
nécessaire. Le signataire de Kora (récupéré via getPayerSigner) paie tous les
frais et le pourboire Jito.
Étape 3 : Créer les transactions du bundle
Créons maintenant un bundle de transactions. Nous créons plusieurs transactions, chacune avec ses propres instructions uniques. Nous utilisons ici des instructions memo uniques pour vérifier facilement nos transactions après leur arrivée sur 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;}
Important : Pourboire payé par le signataire Kora : Comme nous voulons que
le nœud Kora paie notre pourboire Jito, nous utilisons un signataire « no-op »
(noopSigner), où l'adresse de Kora est la source du transfert de pourboire.
Kora signera ceci lors du traitement du bundle.
Étape 4 : Signer et soumettre le bundle
Nous pouvons maintenant rassembler le tout et envoyer le bundle à Kora pour signature et soumission au moteur de blocs de 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));
Exécution de la démo
1. Démarrer le serveur Kora
cd examples/jito-bundles/serverkora --config kora.toml --rpc-url https://api.mainnet-beta.solana.com rpc start --signers-config signers.toml
2. Exécuter le client
Dans un nouveau terminal, naviguez vers le répertoire client/ et exécutez la
démo :
cd examples/jito-bundles/client# Install dependenciespnpm install# Run the demopnpm start
Résultat attendu
Vous devriez voir l'exécution étape par étape avec un bundle réussi à la fin. Le bundle va :
- Créer 4 transactions de mémo
- Ajouter un pourboire Jito (1 000 lamports) à la dernière transaction
- Faire signer toutes les transactions par Kora en tant que payeur de frais
- Soumettre atomiquement au moteur de blocs de Jito
Remarque :
- Le routeur par défaut de Jito peut atteindre des limites de débit. Si vous obtenez une erreur 429, vous pouvez réessayer plus tard ou demander des limites plus élevées. Consultez la documentation sur la limitation de débit de Jito pour plus d'informations.
- Parce que notre démo utilise un pourboire très petit, le bundle peut ne pas atterrir sur le Mainnet de Solana. Si vous ne voyez pas le bundle sur l'explorateur de bundles de Jito, vous pouvez réessayer plus tard avec un pourboire plus élevé.
Comprendre ce qui s'est passé
Voici ce qui s'est passé différemment des transactions uniques :
- Transactions multiples — Au lieu d'une seule transaction, nous en avons créé 4 qui doivent s'exécuter ensemble
- Pourboire Jito — Nous avons ajouté un transfert de pourboire (payé par le signataire de Kora) pour inciter les validateurs
- Validation du bundle — Kora a validé que toutes les transactions
répondent aux exigences spécifiées dans
kora.toml - Soumission atomique — Toutes les transactions soumises comme une seule unité à Jito par notre serveur Kora avec tous les frais et pourboires payés par le signataire de Kora
Le résultat : soit les 4 transactions s'exécutent en séquence, soit aucune ne s'exécute. Aucun état partiel.
Ressources supplémentaires
- Besoin d'aide ? Posez vos questions sur
Solana Stack Exchange avec un tag
Kora - Documentation Jito — Documentation officielle Jito MEV
- Méthodes RPC de Bundle — signBundle, signAndSendBundle, estimateBundleFee
- Dépôt GitHub — Code source et exemples
Is this page helpful?