
Comment commencer à développer avec le SDK Solana Web3.js 2.0
Sommaire
Avant de commencer, nous tenons à remercier Evan et Nick pour leur relecture de cet article. Leurs précieux commentaires et analyses nous ont été très utiles.
Introduction
Le SDK Solana Web3.js est une puissante bibliothèque TypeScript et JavaScript permettant de créer des applications Solana sur Node.js, le web et React Native. Le 7 novembre 2024, Anza a présenté la très attendue version 2.0 du SDK, qui apporte de nombreuses fonctionnalités JavaScript modernes et améliorations. Parmi les principales nouveautés figurent les types JS standard pour les grands entiers et la cryptographie, ainsi que des bundles plus légers, ce qui en fait une mise à niveau majeure pour les développeurs.
Si vous utilisiez @solana/web3.js, vous devrez soit migrer votre logiciel vers le nouveau package v2.0, soit indiquer explicitement la version afin de rester sur la v1.x.
Dans cet article, nous explorerons les dernières nouveautés du SDK Web3.js 2.0, vous guiderons tout au long du processus de migration et vous proposerons un exemple pour vous aider à démarrer.
Cet article suppose une bonne compréhension des concepts fondamentaux de Solana, notamment l’envoi de transactions, le modèle de comptes de Solana, les blockhashes et les frais de priorité, ainsi qu’une expérience en TypeScript ou JavaScript. Il est recommandé de connaître la version précédente du SDK Web3.js, mais ce n’est pas indispensable. Commençons !
Quelles sont les nouveautés de Web3.js 2.0 ?
Découvrons rapidement ce que propose le nouveau SDK Web3.js 2.0 :
1. Performances améliorées
Opérations cryptographiques plus rapides : la génération de paires de clés, la signature des transactions et la vérification des messages sont jusqu’à 10 fois plus rapides grâce aux API cryptographiques natives des environnements JavaScript modernes, comme Node.js et les navigateurs actuels.
2. Des applications plus légères et efficaces
Web3.js 2.0 est entièrement compatible avec le tree shaking, ce qui vous permet de n’inclure que les parties de la bibliothèque que vous utilisez et de réduire ainsi la taille de votre bundle. De plus, le nouveau SDK ne possède aucune dépendance externe, ce qui garantit un build léger et sécurisé.
3. Une flexibilité accrue
Les développeurs peuvent désormais créer des solutions sur mesure en :
- Définissant des instances RPC avec des méthodes personnalisées
- Utilisant des transports réseau ou des signataires de transactions spécialisés
- Composant des primitives personnalisées pour le réseau, la confirmation des transactions et les codecs
Les nouveaux clients TypeScript pour les programmes on-chain sont désormais hébergés dans l’organisation GitHub @solana-program. Ces clients sont générés automatiquement avec Codama, ce qui permet aux développeurs de générer rapidement des clients pour leurs programmes personnalisés.
Faut-il déjà passer à Web3.js v2 ?
En février 2025 :
- Si vous créez une nouvelle application Solana en JS/TS et utilisez des programmes existants comme le programme système, le programme de tokens, le programme de tokens associés et d’autres programmes courants, vous pouvez dès maintenant utiliser web3.js v2.
- Si vous créez des applications on-chain personnalisées avec Anchor, mieux vaut peut-être attendre : Anchor ne prend pas encore en charge web3.js v2 nativement. Vous pouvez attendre une prochaine mise à jour d’Anchor. Vous pouvez aussi utiliser Codama pour créer un client TypeScript pour vos applications on-chain, mais cela demande un peu plus de travail.
Migrer depuis web3.js version 1
Si vous avez utilisé web3.js v1, voici un bref résumé des principales différences :
Paires de clés
Partout où vous utilisiez Keypair, vous devez désormais utiliser un KeyPairSigner. Keypair.generate() devient generateKeyPairSigner(). De plus, keypair s’écrit désormais keyPair partout, conformément à la convention camelCase habituelle de JS/TS.
Les clés secrètes sont désormais appelées privateKey et sont accessibles dans keyPairSigner.privateKey. En règle générale, dans web3.js v2, vous utilisez KeyPairSigner partout où secretKey était utilisé dans web3.js v1.
Adresses / Clés publiques
Les endroits qui utilisaient une PublicKey dans web3.js v1 utilisent simplement une adresse dans web3.js v2. Par exemple, les KeyPairSigner possèdent une propriété keypairSigner.address, qui correspond à leur clé publique. Vous pouvez convertir une clé publique sous forme de chaîne en adresse avec la fonction address.
Montants en SOL et en tokens
Les montants utilisent le type JS natif BigInt. Vous devez donc ajouter n à la fin des nombres, ce qui donne par exemple 1n au lieu de 1.
Fabriques
De nombreuses fonctionnalités sont configurables. Plutôt que de disposer d’une implémentation prédéfinie, par exemple doThing(), vous disposez donc d’une fabrique, appelée doThingFactory(), qui vous permet de créer votre propre fonction doThing(). Par exemple :
- Pour envoyer et confirmer des transactions, vous exécutez une fois
sendAndConfirmTransactionFactory()avec les options de votre choix, puis récupérez une fonctionsendAndConfirmTransaction()personnalisée. Vous pouvez ensuite utiliser votre fonctionsendAndConfirmTransaction()chaque fois que vous devez envoyer et confirmer une transaction. - Pour obtenir un airdrop sur devnet ou localnet, vous exécutez une fois
airdropFactory(), puis récupérez une fonctionairdrop()personnalisée que vous pouvez utiliser chaque fois que vous souhaitez un airdrop.
Comment envoyer des transactions avec Web3.js 2.0
Helius a récemment publié Kite, un framework TypeScript pour web3.js v2, qui comprend des fonctions tout-en-un pour les tâches Solana les plus courantes.
Nous allons créer avec Web3.js 2.0 un programme côté client qui transfère des lamports vers un autre portefeuille. Ce programme présentera des techniques permettant d’améliorer le taux de réussite des transactions et de réduire leur délai de confirmation.
Nous respecterons les bonnes pratiques suivantes pour envoyer des transactions :
- Récupérer le blockhash le plus récent avec un niveau d’engagement confirmed
- Définir les frais de priorité selon les recommandations de la Priority Fee API de Helius
- Optimiser les unités de calcul
- Envoyer la transaction avec maxRetries défini sur 0 et skipPreflight défini sur true
Cette approche garantit des performances et une fiabilité optimales, même en cas de congestion du réseau.
Prérequis
- Installer Node.js
- Un IDE compatible, comme VS Code ou Cursor
Installation
Commencez par créer un projet Node.js de base pour structurer votre application.
Exécutez la commande suivante pour créer un fichier package.json qui gérera les dépendances et les métadonnées de votre projet :
npm init -yCréez un répertoire src, puis ajoutez-y un fichier index.ts qui contiendra le code principal :
mkdir src
touch src/index.tsUtilisez ensuite npm pour installer les dépendances nécessaires afin de travailler avec le SDK Solana Web3.js 2.0 :
npm install @solana/web3.js@2 @solana-program/system @solana-program/compute-budget esrunVoici une description de chaque package :
@solana/web3.js: le SDK Solana Web3.js 2.0 est indispensable pour créer et gérer des transactions Solana@solana-program/system: donne accès au programme système de Solana et permet d’effectuer des opérations telles que le transfert de lamports@solana-program/compute-budget: sert à définir les frais de priorité et à optimiser les unités de calcul des transactionsesrunpermet d’exécuter simplement des applications TypeScript depuis la ligne de commande, sans configuration ni fonction wrapper.
Définir les adresses de transfert
Dans index.ts, définissons les adresses source et de destination du transfert de lamports. Nous utiliserons la fonction address() pour générer la clé publique de destination à partir de la chaîne fournie.
Pour la source, nous dériverons le KeyPair à l’aide de sa secretKey.
import { address, createKeyPairSignerFromBytes, getBase58Encoder } from "@solana/web3.js";
const destinationAddress = address("public-key-to-send-lamports-to");
const secretKey = "add-your-private-key";
const sourceKeypair = await createKeyPairSignerFromBytes(getBase58Encoder().encode(secretKey));
Configurer les connexions RPC
Nous pouvons ensuite configurer les connexions RPC nécessaires. La fonction createSolanaRpc établit la communication avec le serveur RPC via un transport HTTP par défaut, ce qui suffit à la plupart des cas d’usage.
De même, nous utilisons createSolanaRpcSubscriptions pour établir une connexion WebSocket. Les rpc_url et wss_url se trouvent dans le tableau de bord Helius : il vous suffit de vous inscrire ou de vous connecter, puis d’accéder à la section « Endpoints ».
La fonction sendAndConfirmTransactionFactory crée un expéditeur de transactions réutilisable. Cet expéditeur nécessite une connexion RPC pour envoyer les transactions et un abonnement RPC pour surveiller leur état.
import {
// ...
createSolanaRpcSubscriptions,
createSolanaRpc,
sendAndConfirmTransactionFactory,
} from "@solana/web3.js";
const rpc_url = "https://mainnet.helius-rpc.com/?api-key=<your-key>";
const wss_url = "wss://mainnet.helius-rpc.com/?api-key=<your-key>";
const rpc = createSolanaRpc(rpc_url);
const rpcSubscriptions = createSolanaRpcSubscriptions(wss_url);
const sendAndConfirmTransaction = sendAndConfirmTransactionFactory({
rpc,
rpcSubscriptions,
});Créer une instruction de transfert
L’inclusion d’un blockhash récent empêche les doublons et attribue une durée de vie aux transactions : chaque transaction doit inclure un blockhash valide pour être acceptée et exécutée. Pour cette transaction, nous récupérerons le blockhash le plus récent avec le niveau d’engagement confirmed.
Ensuite, nous utiliserons getTransferSolInstruction() pour créer une instruction de transfert prédéfinie fournie par le programme système. Il faut indiquer le montant, la source
et la destination. La source doit toujours être un Signer, tandis que la destination doit être une adresse publique.
import {
// ...
lamports,
} from "@solana/web3.js";
import { getTransferSolInstruction } from "@solana-program/system";
/**
* STEP 1: CREATE THE TRANSFER TRANSACTION
*/
const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
const instruction = getTransferSolInstruction({
amount: lamports(1n),
destination: destinationAddress,
source: sourceKeypair,
});
Créer le message de transaction
Nous allons ensuite créer le message de transaction. Tous les messages de transaction tiennent désormais compte de la version, ce qui évite de devoir gérer différents types, par exemple Transaction et VersionedTransaction.
Nous définirons la source comme payeur des frais, inclurons le blockhash et ajouterons l’instruction de transfert des lamports.
import {
// ...
pipe,
createTransactionMessage,
setTransactionMessageFeePayer,
setTransactionMessageLifetimeUsingBlockhash,
appendTransactionMessageInstruction,
} from "@solana/web3.js";
// ...
const transactionMessage = pipe(
createTransactionMessage({ version: 0 }),
(message) => setTransactionMessageFeePayer(sourceKeypair.address, message),
(message) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, message),
(message) => appendTransactionMessageInstruction(instruction, message),
);
console.log("Transaction message created");
Courante en programmation fonctionnelle, la fonction pipe crée une séquence de fonctions dans laquelle la sortie de l’une devient l’entrée de la suivante. Ici, elle construit progressivement un message de transaction en appliquant des transformations telles que la définition du payeur des frais et de la durée de vie, ainsi que l’ajout d’instructions.
Initialiser le message de transaction :
createTransactionMessage({ version: 0 }) commence avec un message de transaction de base.
Définir le payeur des frais :
message => setTransactionMessageFeePayer(fromKeypair.address, message) ajoute l’adresse du payeur des frais.
Définir la durée de vie avec le blockhash
message => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, message) utilise le blockhash le plus récent pour garantir que la transaction est valide pendant une durée définie.
Ajouter l’instruction de transfert
message => appendTransactionMessageInstruction(instruction, message) ajoute l’action, par exemple le transfert de lamports, au message.
Chaque fonction fléchée message => (...) modifie le message, puis transmet sa version mise à jour à l’étape suivante afin de produire un nouveau message de transaction entièrement construit.
Signer la transaction
Nous signerons la transaction avec le signataire indiqué, le Keypair source.
import {
// ...
signTransactionMessageWithSigners,
} from "@solana/web3.js";
// ...
/**
* STEP 2: SIGN THE TRANSACTION
*/
const signedTransaction = await signTransactionMessageWithSigners(transactionMessage);
console.log("Transaction signed");
Estimer les frais de priorité
À ce stade, nous pouvons envoyer et confirmer la transaction. Nous devons toutefois l’optimiser en définissant les frais de priorité et en ajustant les unités de calcul. Ces optimisations améliorent le taux de réussite des transactions et réduisent leur délai de confirmation, en particulier lorsque le réseau est congestionné.
Pour définir les frais de priorité, nous utiliserons la Priority Fee API de Helius. Celle-ci nécessite la transaction sérialisée au format Base64. Bien que l’API prenne également en charge l’encodage Base58, le SDK actuel fournit directement la transaction au format Base64, ce qui simplifie le processus.
import {
// ...
getBase64EncodedWireTransaction,
} from "@solana/web3.js";
/**
* STEP 3: GET PRIORITY FEE FROM SIGNED TRANSACTION
*/
const base64EncodedWireTransaction = getBase64EncodedWireTransaction(signedTransaction);
const response = await fetch(rpc_url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
jsonrpc: "2.0",
id: "helius-example",
method: "getPriorityFeeEstimate",
params: [
{
transaction: base64EncodedWireTransaction,
options: {
transactionEncoding: "base64",
priorityLevel: "High",
},
},
],
}),
});
const { result } = await response.json();
const priorityFee = result.priorityFeeEstimate;
console.log("Setting priority fee to ", priorityFee);
Définir priorityLevel sur High suffit généralement. Toutefois, la mise en œuvre de stratégies avancées pour les frais de priorité, notamment avec des transactions sérialisées et des clés de compte, peut considérablement améliorer le taux de réussite des transactions lorsque le réseau est congestionné.
Optimiser les unités de calcul
Ensuite, nous estimerons le nombre réel d’unités de calcul consommées par le message de transaction.
Nous ajouterons ensuite une marge de 10 % en multipliant cette valeur par 1,1. Cette marge tient compte des unités de calcul utilisées par les frais de priorité et par les instructions supplémentaires relatives aux unités de calcul que nous intégrerons plus tard.
Certaines instructions, comme le transfert de lamports, peuvent présenter une estimation d’unités de calcul plus faible. Pour garantir des ressources suffisantes, nous avons ajouté une protection qui fixe le nombre minimal d’unités de calcul à 1 000 si l’estimation est inférieure à ce seuil.
import {
// ...
getComputeUnitEstimateForTransactionMessageFactory,
} from "@solana/web3.js";
/**
* STEP 4: OPTIMIZE COMPUTE UNITS
*/
const getComputeUnitEstimateForTransactionMessage = getComputeUnitEstimateForTransactionMessageFactory({
rpc,
});
// Request an estimate of the actual compute units this message will consume.
let computeUnitsEstimate = await getComputeUnitEstimateForTransactionMessage(transactionMessage);
computeUnitsEstimate = computeUnitsEstimate < 1000 ? 1000 : Math.ceil(computeUnitsEstimate * 1.1);
console.log("Setting compute units to ", computeUnitsEstimate);
Reconstruire et signer la transaction
Nous disposons maintenant des frais de priorité et des unités de calcul nécessaires à cette transaction. Comme la transaction a déjà été signée, nous ne pouvons pas lui ajouter directement de nouvelles instructions. Nous allons donc reconstruire l’intégralité du message de transaction avec un nouveau blockhash.
Les blockhashes ne sont valides que pendant environ 1 à 2 minutes, et la récupération des frais de priorité et des unités de calcul prend du temps. Pour éviter que le blockhash n’expire pendant l’envoi de la transaction, il est plus sûr d’en récupérer un nouveau lors de sa reconstruction.
Dans cette transaction reconstruite, nous inclurons deux instructions supplémentaires :
- Une instruction pour définir les frais de priorité ;
- Une autre instruction pour définir les unités de calcul
Enfin, nous signerons cette transaction mise à jour afin de la préparer à l’envoi :
import {
// ...
appendTransactionMessageInstructions,
} from "@solana/web3.js";
import { getSetComputeUnitLimitInstruction, getSetComputeUnitPriceInstruction } from "@solana-program/compute-budget";
/**
* STEP 5: REBUILD AND SIGN FINAL TRANSACTION
*/
const { value: finalLatestBlockhash } = await rpc.getLatestBlockhash().send();
const finalTransactionMessage = appendTransactionMessageInstructions(
[
getSetComputeUnitPriceInstruction({ microLamports: priorityFee }),
getSetComputeUnitLimitInstruction({ units: computeUnitsEstimate }),
],
transactionMessage,
);
setTransactionMessageLifetimeUsingBlockhash(finalLatestBlockhash, finalTransactionMessage);
const finalSignedTransaction = await signTransactionMessageWithSigners(finalTransactionMessage);
console.log("Rebuilt the transaction and signed it");
Envoyer et confirmer la transaction
La transaction signée est ensuite envoyée et confirmée à l’aide de la fonction sendAndConfirmTransaction.
Le niveau d’engagement est défini sur confirmed, conformément au blockhash récupéré précédemment, tandis que maxRetries est défini sur 0. L’option skipPreflight est définie sur true, ce qui contourne les vérifications préalables pour accélérer l’exécution. N’utilisez toutefois cette option que si vous êtes certain que la signature de votre transaction est vérifiée et qu’il n’existe aucune autre erreur.
La fonction sendAndConfirmTransaction a été créée précédemment en fournissant les URL RPC et d’abonnement RPC. L’utilisation de l’URL d’abonnement RPC permet de vérifier l’état de la transaction sans avoir à effectuer d’interrogation manuelle.
Dans la section de gestion des erreurs, le code recherche les erreurs survenues pendant les vérifications préalables. Comme nous avons défini skipPreflight sur true, cette vérification est redondante. Elle sera toutefois utile si vous ne définissez pas cette option sur true.
import {
getSignatureFromTransaction,
isSolanaError,
SOLANA_ERROR__JSON_RPC__SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE,
} from "@solana/web3.js";
import { getSystemErrorMessage, isSystemError } from "@solana-program/system";
/**
* STEP 6: SEND AND CONFIRM THE FINAL TRANSACTION
*/
console.log("Sending and confirming transaction");
await sendAndConfirmTransaction(finalSignedTransaction, {
commitment: "confirmed",
maxRetries: 0n,
skipPreflight: true,
});
console.log("Transfer confirmed: ", getSignatureFromTransaction(finalSignedTransaction));
Exécuter le code
Enfin, nous pouvons exécuter le code :
npx esrun send-transaction.ts
Conclusion
La sortie du SDK Solana Web3.js 2.0 est une mise à jour majeure qui permet aux développeurs de créer sur Solana des applications plus rapides, plus efficaces et plus évolutives. En adoptant les standards JavaScript modernes et en introduisant des fonctionnalités telles que les API cryptographiques natives, la compatibilité avec le tree shaking et les clients TypeScript générés automatiquement, le SDK améliore considérablement l’expérience des développeurs et les performances des applications.
Le code complet de l’exemple de programmation est disponible sur GitHub.
Si vous avez lu jusqu’ici, merci, anon ! Saisissez votre adresse e-mail ci-dessous pour ne manquer aucune nouveauté sur Solana. Vous souhaitez aller plus loin ? Découvrez les derniers articles du blog Helius et poursuivez dès aujourd’hui votre aventure sur Solana.
Ressources
Articles associés
Abonnez-vous à Helius
Suivez les dernières actualités du développement sur Solana et recevez une notification à chaque publication


