Guide de développement Solana Kit
Un guide complet pour construire des applications Solana avec @solana/kit - le SDK JavaScript moderne, tree-shakeable et sans dépendances d'Anza.
Vue d'ensemble
Solana Kit (anciennement web3.js 2.0) est une réécriture complète du SDK Solana JavaScript avec :
- Tree-shakeable : Ne livrez que le code que vous utilisez (-78% de taille de bundle)
- Zéro dépendances : Aucun paquet tiers
- Design fonctionnel : Composable, pas de classes
- Crypto 10x plus rapide : Support Ed25519 natif
- TypeScript-first : Sécurité de type complète
Démarrage rapide
Installation
npm install @solana/kit
Pour les interactions avec des programmes spécifiques :
npm install @solana-program/system @solana-program/token
Exemple minimal
import {
createSolanaRpc,
createSolanaRpcSubscriptions,
generateKeyPairSigner,
lamports,
pipe,
createTransactionMessage,
setTransactionMessageFeePayer,
setTransactionMessageLifetimeUsingBlockhash,
appendTransactionMessageInstruction,
signTransactionMessageWithSigners,
sendAndConfirmTransactionFactory,
getSignatureFromTransaction,
} from "@solana/kit";
import { getTransferSolInstruction } from "@solana-program/system";
const LAMPORTS_PER_SOL = BigInt(1_000_000_000);
async function transferSol() {
// 1. Connexion à RPC
const rpc = createSolanaRpc("https://api.devnet.solana.com");
const rpcSubscriptions = createSolanaRpcSubscriptions("wss://api.devnet.solana.com");
// 2. Créer les signers
const sender = await generateKeyPairSigner();
const recipient = await generateKeyPairSigner();
// 3. Obtenir le blockhash
const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
// 4. Construire la transaction avec pipe
const transactionMessage = pipe(
createTransactionMessage({ version: 0 }),
(tx) => setTransactionMessageFeePayer(sender.address, tx),
(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),
(tx) => appendTransactionMessageInstruction(
getTransferSolInstruction({
amount: lamports(LAMPORTS_PER_SOL / BigInt(10)), // 0.1 SOL
destination: recipient.address,
source: sender,
}),
tx
)
);
// 5. Signer
const signedTx = await signTransactionMessageWithSigners(transactionMessage);
// 6. Envoyer et confirmer
const sendAndConfirm = sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions });
await sendAndConfirm(signedTx, { commitment: "confirmed" });
console.log("Signature:", getSignatureFromTransaction(signedTx));
}
Concepts fondamentaux
1. Connexions RPC
Kit sépare les connexions HTTP et WebSocket :
import { createSolanaRpc, createSolanaRpcSubscriptions } from "@solana/kit";
// HTTP pour les requêtes
const rpc = createSolanaRpc("https://api.devnet.solana.com");
// WebSocket pour les souscriptions
const rpcSubscriptions = createSolanaRpcSubscriptions("wss://api.devnet.solana.com");
// Effectuer des appels RPC
const slot = await rpc.getSlot().send();
const balance = await rpc.getBalance(address).send();
const { value: blockhash } = await rpc.getLatestBlockhash().send();
2. Signers
Kit utilise des interfaces de signer plutôt que des keypairs directement :
import {
generateKeyPairSigner,
createKeyPairSignerFromBytes,
address,
} from "@solana/kit";
// Générer un nouveau signer
const signer = await generateKeyPairSigner();
console.log("Address:", signer.address);
// À partir d'une clé secrète existante (Uint8Array)
const existing = await createKeyPairSignerFromBytes(secretKeyBytes);
// Créer une adresse à partir d'une chaîne
const addr = address("11111111111111111111111111111111");
3. Construction de transactions avec Pipe
Kit utilise la composition fonctionnelle via pipe :
import {
pipe,
createTransactionMessage,
setTransactionMessageFeePayer,
setTransactionMessageLifetimeUsingBlockhash,
appendTransactionMessageInstruction,
appendTransactionMessageInstructions,
prependTransactionMessageInstructions,
} from "@solana/kit";
const tx = pipe(
createTransactionMessage({ version: 0 }), // Créer un message v0
(tx) => setTransactionMessageFeePayer(payer.address, tx), // Définir le payeur de frais
(tx) => setTransactionMessageLifetimeUsingBlockhash(blockhash, tx), // Définir la durée de vie
(tx) => appendTransactionMessageInstruction(instruction1, tx), // Ajouter une instruction
(tx) => appendTransactionMessageInstructions([instruction2, instruction3], tx), // Ajouter plusieurs
);
4. Signature de transactions
import {
signTransactionMessageWithSigners,
partiallySignTransactionMessageWithSigners,
getSignatureFromTransaction,
} from "@solana/kit";
// Signer avec tous les signers de la transaction
const signedTx = await signTransactionMessageWithSigners(transactionMessage);
// Signature partielle (pour multisig)
const partiallySignedTx = await partiallySignTransactionMessageWithSigners(
transactionMessage
);
// Obtenir la signature avant d'envoyer
const signature = getSignatureFromTransaction(signedTx);
5. Envoi de transactions
import {
sendAndConfirmTransactionFactory,
sendTransactionWithoutConfirmingFactory,
getBase64EncodedWireTransaction,
} from "@solana/kit";
// Envoyer avec confirmation (recommandé)
const sendAndConfirm = sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions });
await sendAndConfirm(signedTx, { commitment: "confirmed" });
// Envoyer sans attendre la confirmation
const send = sendTransactionWithoutConfirmingFactory({ rpc });
await send(signedTx, { commitment: "confirmed" });
// Encodage manuel (bas niveau)
const encoded = getBase64EncodedWireTransaction(signedTx);
await rpc.sendTransaction(encoded, { encoding: "base64" }).send();
6. Récupération de comptes
import {
fetchEncodedAccount,
fetchEncodedAccounts,
assertAccountExists,
} from "@solana/kit";
// Récupérer un seul compte
const account = await fetchEncodedAccount(rpc, address);
if (account.exists) {
console.log("Lamports:", account.lamports);
console.log("Owner:", account.programAddress);
console.log("Data:", account.data);
}
// Récupérer plusieurs comptes
const accounts = await fetchEncodedAccounts(rpc, [addr1, addr2, addr3]);
// S'assurer que le compte existe (lève une erreur sinon)
assertAccountExists(account);
Référence des paquets
Paquet principal
| Import | Description |
|---|---|
@solana/kit |
Paquet principal - inclut tout ci-dessous |
Paquets individuels
| Paquet | Objectif |
|---|---|
@solana/rpc |
Création de client RPC |
@solana/rpc-subscriptions |
Souscriptions WebSocket |
@solana/signers |
Interfaces de signature |
@solana/addresses |
Utilitaires d'adresse |
@solana/keys |
Génération de clé |
@solana/transactions |
Compilation de transaction |
@solana/transaction-messages |
Construction de message |
@solana/accounts |
Récupération de compte |
@solana/codecs |
Encodage/décodage de données |
@solana/errors |
Gestion des erreurs |
Paquets de programmes
| Paquet | Programme |
|---|---|
@solana-program/system |
System Program |
@solana-program/token |
SPL Token |
@solana-program/token-2022 |
Token Extensions |
@solana-program/memo |
Memo Program |
@solana-program/compute-budget |
Compute Budget |
@solana-program/address-lookup-table |
Lookup Tables |
Modèles courants
Modèle 1 : Fonction auxiliaire pour Send & Confirm
import {
signTransactionMessageWithSigners,
sendAndConfirmTransactionFactory,
getSignatureFromTransaction,
CompilableTransactionMessage,
TransactionMessageWithBlockhashLifetime,
Commitment,
} from "@solana/kit";
function createTransactionSender(rpc: Rpc, rpcSubscriptions: RpcSubscriptions) {
const sendAndConfirm = sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions });
return async (
txMessage: CompilableTransactionMessage & TransactionMessageWithBlockhashLifetime,
commitment: Commitment = "confirmed"
) => {
const signedTx = await signTransactionMessageWithSigners(txMessage);
await sendAndConfirm(signedTx, { commitment, skipPreflight: false });
return getSignatureFromTransaction(signedTx);
};
}
// Utilisation
const sendTx = createTransactionSender(rpc, rpcSubscriptions);
const signature = await sendTx(transactionMessage);
Modèle 2 : Constructeur de transaction réutilisable
import {
pipe,
createTransactionMessage,
setTransactionMessageFeePayer,
setTransactionMessageLifetimeUsingBlockhash,
appendTransactionMessageInstructions,
IInstruction,
} from "@solana/kit";
async function buildTransaction(
rpc: Rpc,
feePayer: Address,
instructions: IInstruction[]
) {
const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
return pipe(
createTransactionMessage({ version: 0 }),
(tx) => setTransactionMessageFeePayer(feePayer, tx),
(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),
(tx) => appendTransactionMessageInstructions(instructions, tx)
);
}
Modèle 3 : Ajouter un budget de calcul
import {
getSetComputeUnitLimitInstruction,
getSetComputeUnitPriceInstruction,
} from "@solana-program/compute-budget";
const computeInstructions = [
getSetComputeUnitLimitInstruction({ units: 200_000 }),
getSetComputeUnitPriceInstruction({ microLamports: 1000n }),
];
const tx = pipe(
createTransactionMessage({ version: 0 }),
(tx) => setTransactionMessageFeePayer(payer.address, tx),
(tx) => setTransactionMessageLifetimeUsingBlockhash(blockhash, tx),
(tx) => prependTransactionMessageInstructions(computeInstructions, tx), // Prepend!
(tx) => appendTransactionMessageInstruction(mainInstruction, tx),
);
Modèle 4 : Transactions versionnées avec tables de lookup
import {
setTransactionMessageAddressLookupTable,
} from "@solana/kit";
// Récupérer la table de lookup
const lookupTableAccount = await fetchAddressLookupTable(rpc, lookupTableAddress);
const tx = pipe(
createTransactionMessage({ version: 0 }),
(tx) => setTransactionMessageFeePayer(payer.address, tx),
(tx) => setTransactionMessageLifetimeUsingBlockhash(blockhash, tx),
(tx) => setTransactionMessageAddressLookupTable(tx, lookupTableAccount),
(tx) => appendTransactionMessageInstructions(instructions, tx),
);
Sécurité de type
Kit fournit des types TypeScript complets :
import type {
Address,
Signature,
Lamports,
TransactionMessage,
Rpc,
RpcSubscriptions,
KeyPairSigner,
} from "@solana/kit";
// Les adresses sont des chaînes marquées
const addr: Address = address("11111111111111111111111111111111");
// Les Lamports sont des bigints marqués
const amount: Lamports = lamports(1_000_000_000n);
// Réponses RPC type-safe
const response = await rpc.getBalance(addr).send();
// response.value est typée comme Lamports
Conseils de performance
-
Importez uniquement ce dont vous avez besoin - Kit est tree-shakeable
// Bon - n'importe que ce qui est utilisé import { createSolanaRpc, generateKeyPairSigner } from "@solana/kit"; // Aussi bon - utilisez les sous-paquets pour des bundles plus petits import { createSolanaRpc } from "@solana/rpc"; import { generateKeyPairSigner } from "@solana/signers"; -
Réutilisez les connexions RPC - Ne créez pas par requête
// Créer une fois const rpc = createSolanaRpc(endpoint); // Réutiliser partout await rpc.getBalance(addr1).send(); await rpc.getBalance(addr2).send(); -
Traitez les requêtes par lots quand c'est possible
// Récupérer plusieurs comptes en une seule requête const accounts = await fetchEncodedAccounts(rpc, [addr1, addr2, addr3]); -
Utilisez skipPreflight avec prudence - Plus rapide mais pas de simulation
await sendAndConfirm(tx, { commitment: "confirmed", skipPreflight: true });
Gestion des erreurs
import { isSolanaError, SOLANA_ERROR__TRANSACTION_ERROR__INSUFFICIENT_FUNDS } from "@solana/errors";
try {
await sendAndConfirm(signedTx, { commitment: "confirmed" });
} catch (error) {
if (isSolanaError(error, SOLANA_ERROR__TRANSACTION_ERROR__INSUFFICIENT_FUNDS)) {
console.error("Pas assez de SOL pour la transaction");
} else if (isSolanaError(error)) {
console.error("Erreur Solana:", error.context);
} else {
throw error;
}
}
Migration depuis web3.js 1.x
Consultez la skill de migration séparée ou utilisez @solana/compat pour l'interopérabilité :
import {
fromLegacyPublicKey,
fromLegacyKeypair,
fromVersionedTransaction,
fromLegacyTransactionInstruction,
} from "@solana/compat";
// Convertir legacy PublicKey en Kit Address
const address = fromLegacyPublicKey(legacyPublicKey);
// Convertir legacy Keypair en Kit CryptoKeyPair (async)
const keyPair = await fromLegacyKeypair(legacyKeypair);
// Convertir legacy VersionedTransaction en Kit Transaction
const kitTransaction = fromVersionedTransaction(legacyVersionedTx);
// Convertir legacy TransactionInstruction en Kit Instruction
const kitInstruction = fromLegacyTransactionInstruction(legacyInstruction);
Note : Le paquet compat convertit les types legacy VERS Kit. Pour la conversion inverse, vous devrez peut-être construire manuellement des objets legacy.
Benchmarks de performance
Kit offre des améliorations de performance significatives par rapport à web3.js 1.x :
| Métrique | web3.js 1.x | @solana/kit | Amélioration |
|---|---|---|---|
| Génération de keypair | ~50ms | ~5ms | 10x plus rapide |
| Signature de transaction | ~20ms | ~2ms | 10x plus rapide |
| Taille de bundle | 311KB | 226KB | 26% plus petit |
| Latence de confirmation | ~400ms | ~200ms | ~200ms plus rapide |
Benchmarks de Triton One's Ping Thing service et Solana Explorer testing
Pourquoi c'est plus rapide
- Ed25519 natif : Utilise les APIs crypto natives du navigateur/runtime
- Zéro dépendances : Pas de surcharge de bibliothèque tierce
- Tree-shakeable : N'importe que le code que vous utilisez
- Pas de classes : Le design fonctionnel permet une meilleure optimisation
Ressources
Structure de la skill
solana-kit/
├── SKILL.md # Ce fichier
├── resources/
│ ├── packages-reference.md # Documentation complète des paquets
│ └── api-quick-reference.md # Tableau de recherche rapide
├── examples/
│ ├── transfer-sol/ # Transfert SOL basique
│ ├── create-token/ # Création de token SPL
│ ├── fetch-accounts/ # Récupération et décodage de compte
│ └── subscriptions/ # Souscriptions en temps réel
├── templates/
│ └── project-template.ts # Starter prêt à copier-coller
└── docs/
├── advanced-patterns.md # Modèles complexes
└── troubleshooting.md # Problèmes courants
Vérifier
- Un appel RPC/SDK réel a été émis (mainnet, devnet ou validateur local) et la charge utile de réponse est capturée dans la transcription, non pas simplement paraphrasée
- Chaque transaction a été simulée (
simulateTransactionou équivalent) avant toute étape de signature/envoi ; les logs de simulation sont joints - Pour toute transaction signée/envoyée, la signature résultante est enregistrée et confirmée en chaîne (statut retourné par
getSignatureStatusesou une URL explorateur) - Le slippage, les frais de priorité et les limites de compute unit ont été définis explicitement avec des valeurs numériques concrètes, non pas laissés aux défauts de la bibliothèque
- Les adresses de compte, les mints et les ID de programme utilisés dans l'exécution correspondent aux adresses solana-kit-sdk documentées pour le cluster ciblé (pas de mélange mainnet/devnet)
- Le chemin d'erreur a été exercé au moins une fois (solde insuffisant, oracle obsolète, blockhash expiré, etc.) et la gestion des erreurs de l'agent a produit un message lisible par l'homme