Solana blockchain has emerged as a top choice for developers, thanks to its high throughput and low transaction fees. With the release of @solana/web3.js 2.0, the latest iteration of the official JavaScript library, interacting with the Solana network has become more efficient, modular, and developer-friendly. This article provides a comprehensive walkthrough of executing a SOL transfer using @solana/web3.js 2.0, covering everything from environment setup and key configuration to transaction construction, signing, and on-chain verification.
Whether you're new to Solana development or looking to upgrade your skills with the latest tooling, this guide delivers practical insights and a fully replicable workflow. We’ll walk through a complete TypeScript implementation, explaining each step in detail and highlighting best practices for secure and reliable blockchain interactions.
Core Keywords
Solana transfer, @solana/web3.js 2.0, SOL transaction, blockchain development, RPC client, transaction signing, TypeScript, Web3.js
Setting Up the Development Environment
Before diving into code, ensure your development environment is properly configured. You’ll need:
- Node.js (v18 or higher recommended)
- TypeScript
@solana/web3.jsversion 2.0+- A
.envfile for securely storing sensitive data
Install dependencies using:
npm install @solana/web3.js dotenv
npm install -D typescript ts-node👉 Discover how to securely manage blockchain keys and boost your development workflow.
This setup enables modular imports from @solana/web3.js, a major improvement in version 2.0 that reduces bundle size and improves performance by loading only the functions you need.
Configuring Environment Variables and Keys
Security is paramount in blockchain development. Never hardcode private keys or RPC URLs in your source files. Instead, use environment variables via the dotenv package.
Create a .env file in your project root:
PRIVATE_KEY=your_base58_private_key
SOL_ADDRESS1=sender_wallet_address
SOL_ADDRESS2=receiver_wallet_address
SOL_RPC_URL=https://api.devnet.solana.com
WSS_PROVIDER=wss://api.devnet.solana.comIn your code, load these variables safely:
import dotenv from "dotenv";
dotenv.config();
const privateKey = process.env.PRIVATE_KEY;
const user1 = process.env.SOL_ADDRESS1;
const user2 = process.env.SOL_ADDRESS2;Validate all required variables are present before proceeding:
if (!privateKey || !user1 || !user2) {
console.error("Missing environment variables.");
process.exit(1);
}This approach keeps credentials out of version control and aligns with production-grade security standards.
Connecting to the Solana Network via RPC
To interact with the blockchain, you need to connect to a Solana node using an HTTP RPC endpoint and a WebSocket for real-time updates.
const rpc = createSolanaRpc(httpProvider);
const rpcSubscriptions = createSolanaRpcSubscriptions(wssProvider);The createSolanaRpc function establishes a connection for sending requests (e.g., fetching blockhashes), while createSolanaRpcSubscriptions enables listening to transaction confirmations in real time.
Upon successful connection:
console.log(`✅ - Connected to ${httpProvider}`);Using a reliable RPC provider—especially in production—is critical for uptime and performance. Public endpoints like Devnet are suitable for testing.
👉 Learn how to optimize your RPC strategy for faster, more reliable transactions.
Generating the Signer and Managing Keys
In Solana, every transaction must be signed by a signer—typically a wallet holding SOL to pay fees and authorize actions.
Version 2.0 introduces improved key handling. Here’s how to create a signer from a private key:
const secretKey = private_key as string;
const signer = await createKeyPairSignerFromBytes(
getBase58Encoder().encode(secretKey)
);Alternatively, if using a byte array (e.g., from a JSON keypair file):
const keypairBytes = JSON.parse(fs.readFileSync("path/to/key.json"));
const signer = await createKeyPairSignerFromBytes(new Uint8Array(keypairBytes));The createKeyPairSignerFromBytes utility securely derives a signer from raw key material, ensuring compatibility with Solana’s Ed25519 cryptography.
Building the Transaction Message
A Solana transaction consists of a message containing instructions and metadata. Use the pipe pattern for clean, functional composition:
const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
const transactionMessage = pipe(
createTransactionMessage({ version: 0 }),
(tx) => setTransactionMessageFeePayer(user1Address, tx),
(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),
(tx) =>
appendTransactionMessageInstruction(
getTransferSolInstruction({
amount: lamports(LAMPORTS_PER_SOL / BigInt(2)), // 0.5 SOL
destination: user2Address,
source: signer,
}),
tx
)
);Key components:
- Fee payer: The account covering transaction fees (usually the sender).
- Blockhash: Ensures the transaction is valid only within a specific timeframe.
- Instruction: The
getTransferSolInstructioncreates a system-program instruction to transfer lamports (1 SOL = 1,000,000,000 lamports).
Signing and Sending the Transaction
Once the message is built, it must be signed by all required signers:
const signedTransaction = await signTransactionMessageWithSigners(transactionMessage);Then, send and confirm using a factory function:
const sendAndConfirmTransaction = sendAndConfirmTransactionFactory({
rpc,
rpcSubscriptions,
});
await sendAndConfirmTransaction(signedTransaction, {
commitment: "confirmed",
skipPreflight: true,
});commitment: "confirmed"ensures the transaction reaches network consensus.skipPreflight: trueskips simulation (use cautiously—only when confident in transaction validity).
On success:
const signature = getSignatureFromTransaction(signedTransaction);
console.log("✅ - Transfer successful:", signature);This signature can be used to look up the transaction on Solana Explorer.
Local Testing and Verification
Always test transactions on Devnet or a local validator before deploying to Mainnet.
To run:
ts-node solana_transfer.tsExpected output:
✅ - Connected to https://api.devnet.solana.com
✅ - Transfer successful: 5wzqJ8aK7ZqLmVd1fYpE9sW2tR7uXcV4nB6vDmP1eK2r...Verify the balance change using:
const balance = await rpc.getBalance(user2Address).send();
console.log(`New balance: ${Number(balance.value) / LAMPORTS_PER_SOL} SOL`);Frequently Asked Questions (FAQ)
Q: What is @solana/web3.js 2.0?
A: It’s the latest version of Solana’s official JavaScript/TypeScript SDK, featuring modular imports, improved typing, and enhanced performance for building dApps.
Q: How do I transfer SOL using web3.js?
A: You create a transaction message, add a transfer instruction, sign it with a valid signer, and send it via an RPC client using sendAndConfirmTransaction.
Q: Why use environment variables for keys?
A: To prevent accidental exposure of private keys in code repositories. Always use .env files and add them to .gitignore.
Q: What is the difference between lamports and SOL?
A: Lamports are the smallest unit of SOL (1 SOL = 1,000,000,000 lamports), similar to how satoshis relate to Bitcoin.
Q: Can I use this code on Mainnet?
A: Yes, but replace Devnet RPC URLs with Mainnet endpoints and ensure proper key management and error handling.
Q: What does “skipPreflight” do?
A: It bypasses transaction simulation. Use it to save time during testing but disable it in production to catch errors early.
👉 Explore advanced tools to test and deploy your Solana dApp with confidence.
This guide equips you with everything needed to execute secure, efficient SOL transfers using @solana/web3.js 2.0. By following modern practices—modular imports, environment isolation, and functional composition—you’re well-positioned to build robust Web3 applications on Solana.