
So startest du mit dem Solana Web3.js 2.0 SDK
Bevor wir einsteigen, möchten wir Evan und Nick dafür danken, dass sie diesen Artikel geprüft haben. Ihr wertvolles Feedback und ihre Einblicke haben uns sehr geholfen.
Einführung
Das Solana Web3.js SDK ist eine leistungsstarke TypeScript- und JavaScript-Bibliothek, mit der du Solana-Anwendungen für Node.js-, Web- und React-Native-Plattformen entwickeln kannst. Am 7. November 2024 stellte Anza das mit Spannung erwartete Update auf SDK 2.0 vor. Es bringt zahlreiche moderne JavaScript-Funktionen und Verbesserungen. Zu den wichtigsten Neuerungen gehören standardmäßige JS-Typen für bigints und Kryptografie sowie kleinere Bundles. Damit ist es ein bedeutendes Upgrade für Entwickler.
Wenn du bisher @solana/web3.js verwendet hast, musst du deine Software entweder auf das neue v2.0-Paket portieren oder die Version explizit angeben, um sie auf v1.x festzulegen.
In diesem Artikel sehen wir uns die neuesten Änderungen im Web3.js 2.0 SDK an, führen dich durch die Migration und zeigen dir anhand eines Beispiels, wie du loslegst.
Dieser Artikel setzt ein solides Verständnis grundlegender Solana-Konzepte voraus. Dazu gehören das Senden von Transaktionen, das Solana-Account-Modell, Blockhashes und Prioritätsgebühren. Außerdem solltest du Erfahrung mit TypeScript oder JavaScript haben. Kenntnisse der vorherigen Version des Web3.js SDK sind empfehlenswert, aber nicht zwingend erforderlich. Legen wir los!
Was ist neu in Web3.js 2.0?
Sehen wir uns kurz an, was das neue Web3.js 2.0 SDK bietet:
1. Leistungsverbesserungen
Schnellere kryptografische Operationen: Das Erzeugen von Schlüsselpaaren, das Signieren von Transaktionen und das Verifizieren von Nachrichten sind bis zu 10-mal schneller. Möglich machen das native Kryptografie-APIs in modernen JavaScript-Umgebungen wie Node.js und aktuellen Browsern.
2. Kleinere und effizientere Anwendungen
Web3.js 2.0 unterstützt Tree Shaking vollständig. So bindest du nur die tatsächlich verwendeten Teile der Bibliothek ein und minimierst dein Bundle. Außerdem kommt das neue SDK ohne externe Abhängigkeiten aus. Das sorgt für einen schlanken und sicheren Build.
3. Mehr Flexibilität
Entwickler können jetzt eigene Lösungen erstellen, indem sie:
- RPC-Instanzen mit eigenen Methoden definieren
- Spezialisierte Netzwerktransporte oder Transaktionssignierer verwenden
- Eigene Primitives für Netzwerke, Transaktionsbestätigungen und Codecs kombinieren
Die neuen TypeScript-Clients für On-Chain-Programme werden jetzt in der GitHub-Organisation @solana-program gehostet. Diese Clients werden mit Codama automatisch generiert. So können Entwickler schnell Clients für eigene Programme erzeugen.
Solltest du bereits zu Web3.js v2 wechseln?
Stand Februar 2025:
- Wenn du eine neue Solana-App in JS/TS entwickelst und bestehende Programme wie das Systemprogramm, Tokenprogramm, Associated-Token-Programm oder andere gängige Programme verwendest, kannst du web3.js v2 bereits einsetzen.
- Wenn du mit Anchor eigene On-Chain-Apps entwickelst, solltest du eventuell noch warten – Anchor unterstützt web3.js v2 noch nicht standardmäßig. Du kannst auf ein zukünftiges Anchor-Update warten. Alternativ kannst du mit Codama einen TypeScript-Client für deine On-Chain-Apps erstellen, allerdings ist das etwas aufwendiger.
Migration von web3.js Version 1
Wenn du web3.js v1 verwendet hast, findest du hier eine kurze Zusammenfassung der wichtigsten Unterschiede:
Schlüsselpaare
Überall dort, wo du bisher Keypair verwendet hast, nutzt du jetzt KeyPairSigner. Keypair.generate() heißt jetzt generateKeyPairSigner(). Außerdem wird keypair nun überall als keyPair geschrieben, entsprechend der üblichen camelCase-Schreibweise in JS/TS.
Geheime Schlüssel heißen jetzt privateKey und sind über keyPairSigner.privateKey zugänglich. In web3.js v2 verwendest du in der Regel KeyPairSigner überall dort, wo in web3.js v1 ein secretKey verwendet wurde.
Adressen / öffentliche Schlüssel
Wo web3.js v1 einen PublicKey verwendet hat, nutzt web3.js v2 einfach eine Adresse. KeyPairSigner haben beispielsweise eine Eigenschaft keypairSigner.address, die ihren öffentlichen Schlüssel enthält. Mit der Funktion address kannst du einen öffentlichen Schlüssel als String in eine Adresse umwandeln.
SOL- und Token-Beträge
Beträge verwenden den nativen JS-Typ BigInt. Hänge daher n an Zahlen an. Ein Wert wird also als 1n statt als 1 geschrieben.
Factories
Viele Funktionen sind konfigurierbar. Statt einer vordefinierten Implementierung wie doThing() gibt es daher eine Factory namens doThingFactory(). Mit ihr kannst du deine eigene Funktion doThing() erstellen. Zum Beispiel:
- Um Transaktionen zu senden und zu bestätigen, führst du
sendAndConfirmTransactionFactory()einmal mit deinen gewünschten Optionen aus. Du erhältst eine eigene FunktionsendAndConfirmTransaction(), die du anschließend immer verwenden kannst, wenn du eine Transaktion senden und bestätigen möchtest. - Um einen Airdrop auf devnet oder localnet zu erhalten, führst du
airdropFactory()einmal aus. Du erhältst eine eigene Funktionairdrop(), die du jederzeit für einen Airdrop verwenden kannst.
So sendest du Transaktionen mit Web3.js 2.0
Helius hat kürzlich Kite veröffentlicht, ein TypeScript-Framework für web3.js v2. Es enthält One-Shot-Funktionen für die meisten gängigen Solana-Aufgaben.
Wir entwickeln mit Web3.js 2.0 ein clientseitiges Programm, das Lamports an eine andere Wallet überträgt. Dabei zeigen wir Techniken, mit denen sich die Erfolgsquote von Transaktionen erhöhen und ihre Bestätigung beschleunigen lässt.
Beim Senden von Transaktionen halten wir uns an diese Best Practices:
- Den neuesten Blockhash mit dem Commitment-Level confirmed abrufen
- Prioritätsgebühren gemäß der Empfehlung der Priority Fee API von Helius festlegen
- Compute Units optimieren
- Die Transaktion mit maxRetries auf 0 und skipPreflight auf true senden
Dieser Ansatz sorgt selbst bei Netzwerküberlastung für optimale Leistung und Zuverlässigkeit.
Voraussetzungen
- Node.js installieren
- Eine kompatible IDE wie VS Code oder Cursor
Installation
Erstelle zunächst ein einfaches Node.js-Projekt, um deine Anwendung zu strukturieren.
Führe den folgenden Befehl aus, um eine Datei package.json zu erstellen, die deine Abhängigkeiten und Projektmetadaten verwaltet:
npm init -yErstelle ein Verzeichnis src und darin eine Datei index.ts für den Hauptcode:
mkdir src
touch src/index.tsInstalliere anschließend mit npm die erforderlichen Abhängigkeiten für die Arbeit mit dem Web3.js 2.0 SDK von Solana:
npm install @solana/web3.js@2 @solana-program/system @solana-program/compute-budget esrunDie einzelnen Pakete haben folgende Aufgaben:
@solana/web3.js: Das Solana Web3.js 2.0 SDK ist für das Erstellen und Verwalten von Solana-Transaktionen erforderlich@solana-program/system: Bietet Zugriff auf das Solana-Systemprogramm und ermöglicht Operationen wie Lamport-Übertragungen@solana-program/compute-budget: Dient zum Festlegen von Prioritätsgebühren und Optimieren der Compute Units für Transaktionenesrunbietet eine einfache Möglichkeit, TypeScript-Apps ohne Konfiguration oder Wrapper-Funktionen über die Befehlszeile auszuführen.
Transferadressen definieren
Definieren wir in index.ts die Quell- und Zieladressen für die Übertragung von Lamports. Mit der Funktion address() erzeugen wir aus dem angegebenen String den öffentlichen Zielschlüssel.
Für die Quelle leiten wir KeyPair aus dem zugehörigen secretKey ab.
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));
RPC-Verbindungen konfigurieren
Als Nächstes richten wir die erforderlichen RPC-Verbindungen ein. Die Funktion createSolanaRpc stellt über einen standardmäßigen HTTP-Transport eine Verbindung zum RPC-Server her. Das reicht für die meisten Anwendungsfälle aus.
Entsprechend verwenden wir createSolanaRpcSubscriptions, um eine WebSocket-Verbindung herzustellen. rpc_url und wss_url findest du im Helius Dashboard. Registriere dich oder melde dich an und öffne den Bereich „Endpoints“.
Die Funktion sendAndConfirmTransactionFactory erstellt einen wiederverwendbaren Transaktions-Sender. Dieser benötigt eine RPC-Verbindung zum Senden von Transaktionen und ein RPC-Abonnement zur Überwachung des Transaktionsstatus.
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,
});Transferanweisung erstellen
Ein aktueller Blockhash verhindert Duplikate und gibt Transaktionen eine begrenzte Gültigkeitsdauer. Jede Transaktion muss einen gültigen Blockhash enthalten, damit sie zur Ausführung akzeptiert wird. Für diese Transaktion rufen wir den neuesten Blockhash mit dem Commitment-Level confirmed ab.
Anschließend erstellen wir mit getTransferSolInstruction() eine vordefinierte Transferanweisung des Systemprogramms. Dafür müssen Betrag und Quelle angegeben werden
, ebenso wie das Ziel. Die Quelle muss immer ein Signer sein, während das Ziel eine öffentliche Adresse sein sollte.
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,
});
Transaktionsnachricht erstellen
Anschließend erstellen wir die Transaktionsnachricht. Alle Transaktionsnachrichten berücksichtigen jetzt ihre Version. Du musst daher nicht mehr verschiedene Typen wie Transaction und VersionedTransaction separat behandeln.
Wir legen die Quelle als Gebührenzahler fest, fügen den Blockhash hinzu und ergänzen die Anweisung zur Übertragung der 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");
Die in der funktionalen Programmierung häufig verwendete Pipe-Funktion erstellt eine Abfolge von Funktionen, bei der die Ausgabe einer Funktion zur Eingabe der nächsten wird. Hier baut sie schrittweise eine Transaktionsnachricht auf. Dazu wendet sie Transformationen an, die den Gebührenzahler und die Gültigkeitsdauer festlegen und Anweisungen hinzufügen.
Transaktionsnachricht initialisieren:
createTransactionMessage({ version: 0 }) beginnt mit einer einfachen Transaktionsnachricht.
Gebührenzahler festlegen:
message => setTransactionMessageFeePayer(fromKeypair.address, message) fügt die Adresse des Gebührenzahlers hinzu.
Gültigkeitsdauer mit dem Blockhash festlegen
message => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, message) stellt mithilfe des neuesten Blockhashes sicher, dass die Transaktion innerhalb eines bestimmten Zeitraums gültig ist.
Transferanweisung hinzufügen
message => appendTransactionMessageInstruction(instruction, message) hängt die Aktion, etwa die Übertragung von Lamports, an die Nachricht an.
Jede Pfeilfunktion message => (...) ändert die Nachricht und übergibt die aktualisierte Version an den nächsten Schritt. So entsteht eine vollständig aufgebaute neue Transaktionsnachricht.
Transaktion signieren
Wir signieren die Transaktion mit dem angegebenen Signierer, dem Keypair der Quelle.
import {
// ...
signTransactionMessageWithSigners,
} from "@solana/web3.js";
// ...
/**
* STEP 2: SIGN THE TRANSACTION
*/
const signedTransaction = await signTransactionMessageWithSigners(transactionMessage);
console.log("Transaction signed");
Prioritätsgebühren schätzen
An diesem Punkt könnten wir die Transaktion senden und bestätigen. Wir sollten sie jedoch zunächst optimieren, indem wir Prioritätsgebühren festlegen und die Compute Units anpassen. Diese Optimierungen erhöhen die Erfolgsquote und verkürzen die Bestätigungszeit, insbesondere bei Netzwerküberlastung.
Zum Festlegen der Prioritätsgebühren verwenden wir die Priority Fee API von Helius. Dafür benötigen wir die serialisierte Transaktion im Base64-Format. Die API unterstützt auch Base58, aber das aktuelle SDK stellt die Transaktion direkt im Base64-Format bereit und vereinfacht damit den Prozess.
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);
In der Regel reicht es aus, priorityLevel auf High zu setzen. Fortgeschrittene Strategien für Prioritätsgebühren, etwa mit serialisierten Transaktionen und Account-Schlüsseln, können die Erfolgsquote bei Netzwerküberlastung jedoch deutlich erhöhen.
Compute Units optimieren
Als Nächstes schätzen wir die Compute Units, die die Transaktionsnachricht tatsächlich verbraucht.
Dann fügen wir einen Puffer von 10 % hinzu, indem wir diesen Wert mit 1,1 multiplizieren. Dieser Puffer berücksichtigt die Compute Units für Prioritätsgebühren und zusätzliche Compute-Unit-Anweisungen, die wir später ergänzen.
Einige Anweisungen, etwa die Übertragung von Lamports, können eine niedrigere Schätzung der Compute Units ergeben. Um ausreichende Ressourcen sicherzustellen, setzen wir die Compute Units vorsichtshalber auf mindestens 1000, wenn die Schätzung unter diesem Grenzwert liegt.
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);
Transaktion neu erstellen und signieren
Nun kennen wir die für diese Transaktion erforderlichen Prioritätsgebühren und Compute Units. Da die Transaktion bereits signiert wurde, können wir ihr nicht direkt neue Anweisungen hinzufügen. Stattdessen erstellen wir die gesamte Transaktionsnachricht mit einem neuen Blockhash neu.
Blockhashes sind nur etwa ein bis zwei Minuten gültig. Das Abrufen der Prioritätsgebühren und Compute Units benötigt etwas Zeit. Damit der Blockhash nicht während des Sendens abläuft, sollten wir beim Neuerstellen der Transaktion einen neuen abrufen.
Die neu erstellte Transaktion enthält zwei zusätzliche Anweisungen:
- Eine Anweisung zum Festlegen der Prioritätsgebühren und
- eine weitere Anweisung zum Festlegen der Compute Units
Zum Schluss signieren wir die aktualisierte Transaktion und bereiten sie so für die Übermittlung vor:
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");
Transaktion senden und bestätigen
Anschließend wird die signierte Transaktion mit der Funktion sendAndConfirmTransaction gesendet und bestätigt.
Das Commitment-Level wird passend zum zuvor abgerufenen Blockhash auf confirmed gesetzt, während maxRetries den Wert 0 erhält. Die Option skipPreflight wird auf true gesetzt. Dadurch werden die Preflight-Prüfungen übersprungen und die Ausführung beschleunigt. Verwende diese Option jedoch nur, wenn du sicher bist, dass die Transaktionssignatur verifiziert wurde und keine anderen Fehler vorliegen.
sendAndConfirmTransaction wurde zuvor durch Angabe der URLs für RPC und das RPC-Abonnement erstellt. Die URL des RPC-Abonnements dient zur Prüfung des Transaktionsstatus. Manuelles Polling ist dadurch nicht erforderlich.
Im Abschnitt zur Fehlerbehandlung prüft der Code, ob während der Preflight-Prüfungen Fehler aufgetreten sind. Da wir skipPreflight auf true gesetzt haben, ist diese Prüfung redundant. Wenn du den Wert nicht auf true setzt, ist sie jedoch hilfreich.
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));
Code ausführen
Zum Schluss können wir den Code ausführen:
npx esrun send-transaction.ts
Fazit
Die Veröffentlichung des Web3.js 2.0 SDK von Solana ist ein wegweisendes Update. Es ermöglicht Entwicklern, schnellere, effizientere und skalierbare Anwendungen auf Solana zu erstellen. Das SDK nutzt moderne JavaScript-Standards und führt Funktionen wie native Kryptografie-APIs, Tree Shaking und automatisch generierte TypeScript-Clients ein. Dadurch verbessert es die Entwicklungserfahrung und Anwendungsleistung deutlich.
Den vollständigen Code für das Programmierbeispiel findest du auf GitHub.
Wenn du bis hierhin gelesen hast: Danke, anon! Gib unten deine E-Mail-Adresse ein, damit du keine Neuigkeiten zu Solana verpasst. Möchtest du tiefer einsteigen? Entdecke die neuesten Artikel im Helius-Blog und setze deine Solana-Reise noch heute fort.
Ressourcen
Ähnliche Artikel
Helius abonnieren
Bleib bei der Solana-Entwicklung auf dem Laufenden und erhalte Updates, wenn wir neue Beiträge veröffentlichen


