Solana Web3.js SDK
Use FortiBlox Nexus RPC with @solana/web3.js for JavaScript and TypeScript applications
Solana Web3.js SDK
The @solana/web3.js library is the official JavaScript SDK for Solana. This guide shows you how to use it with FortiBlox Nexus RPC for building web and Node.js applications.
Installation
Install the Solana Web3.js library:
npm install @solana/web3.jsFor TypeScript projects, types are included automatically.
Additional Dependencies
For complete examples, you may also want:
npm install bs58 dotenvAuthentication Setup
FortiBlox requires authentication via API key in the request headers.
Environment Variables
Create a .env file in your project root:
FORTIBLOX_API_KEY=your-api-key-here
FORTIBLOX_RPC_URL=https://nexus.fortiblox.com/rpcLoad environment variables in your application:
import dotenv from 'dotenv';
dotenv.config();Connection Configuration
Configure the connection with FortiBlox endpoint and API key:
import { Connection } from '@solana/web3.js';
const connection = new Connection(
process.env.FORTIBLOX_RPC_URL || 'https://nexus.fortiblox.com/rpc',
{
commitment: 'confirmed',
httpHeaders: {
'X-API-Key': process.env.FORTIBLOX_API_KEY || ''
}
}
);Code Examples
1. Get Account Balance
Fetch the SOL balance of any account:
import { Connection, PublicKey, LAMPORTS_PER_SOL } from '@solana/web3.js';
async function getBalance(address: string) {
try {
const connection = new Connection(
'https://nexus.fortiblox.com/rpc',
{
httpHeaders: { 'X-API-Key': process.env.FORTIBLOX_API_KEY || '' }
}
);
const publicKey = new PublicKey(address);
const balance = await connection.getBalance(publicKey);
console.log(`Address: ${address}`);
console.log(`Balance: ${balance / LAMPORTS_PER_SOL} SOL`);
console.log(`Lamports: ${balance}`);
return balance;
} catch (error) {
console.error('Error fetching balance:', error);
throw error;
}
}
// Example usage
getBalance('7EqQdEUXxogiCVXpZUyS6b8Z3Z1dkrKR1A5Q8cPcGvQh');2. Get Account Info
Retrieve detailed account information:
import { Connection, PublicKey } from '@solana/web3.js';
async function getAccountInfo(address: string) {
try {
const connection = new Connection(
'https://nexus.fortiblox.com/rpc',
{
commitment: 'confirmed',
httpHeaders: { 'X-API-Key': process.env.FORTIBLOX_API_KEY || '' }
}
);
const publicKey = new PublicKey(address);
const accountInfo = await connection.getAccountInfo(publicKey);
if (!accountInfo) {
console.log('Account not found');
return null;
}
console.log('Account Info:');
console.log('- Lamports:', accountInfo.lamports);
console.log('- Owner:', accountInfo.owner.toBase58());
console.log('- Executable:', accountInfo.executable);
console.log('- Rent Epoch:', accountInfo.rentEpoch);
console.log('- Data Length:', accountInfo.data.length);
return accountInfo;
} catch (error) {
console.error('Error fetching account info:', error);
throw error;
}
}
// Example usage
getAccountInfo('TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA');3. Send Transaction
Send a SOL transfer transaction:
import {
Connection,
Keypair,
PublicKey,
SystemProgram,
Transaction,
sendAndConfirmTransaction,
LAMPORTS_PER_SOL
} from '@solana/web3.js';
async function sendSol(
fromKeypair: Keypair,
toAddress: string,
amountSol: number
) {
try {
const connection = new Connection(
'https://nexus.fortiblox.com/rpc',
{
commitment: 'confirmed',
httpHeaders: { 'X-API-Key': process.env.FORTIBLOX_API_KEY || '' }
}
);
const toPubkey = new PublicKey(toAddress);
const lamports = amountSol * LAMPORTS_PER_SOL;
// Create transaction
const transaction = new Transaction().add(
SystemProgram.transfer({
fromPubkey: fromKeypair.publicKey,
toPubkey: toPubkey,
lamports: lamports,
})
);
// Send and confirm transaction
console.log('Sending transaction...');
const signature = await sendAndConfirmTransaction(
connection,
transaction,
[fromKeypair],
{
commitment: 'confirmed',
preflightCommitment: 'confirmed',
}
);
console.log('Transaction successful!');
console.log('Signature:', signature);
console.log('Explorer:', `https://explorer.solana.com/tx/${signature}`);
return signature;
} catch (error) {
console.error('Error sending transaction:', error);
throw error;
}
}
// Example usage
// const keypair = Keypair.fromSecretKey(/* your secret key */);
// sendSol(keypair, '7EqQdEUXxogiCVXpZUyS6b8Z3Z1dkrKR1A5Q8cPcGvQh', 0.1);4. Get Recent Transactions
Fetch transaction history for an account:
import { Connection, PublicKey } from '@solana/web3.js';
async function getRecentTransactions(address: string, limit: number = 10) {
try {
const connection = new Connection(
'https://nexus.fortiblox.com/rpc',
{
httpHeaders: { 'X-API-Key': process.env.FORTIBLOX_API_KEY || '' }
}
);
const publicKey = new PublicKey(address);
// Get signatures for address
const signatures = await connection.getSignaturesForAddress(
publicKey,
{ limit }
);
console.log(`Found ${signatures.length} recent transactions:`);
// Fetch transaction details
for (const sig of signatures) {
console.log('\n---');
console.log('Signature:', sig.signature);
console.log('Slot:', sig.slot);
console.log('Block Time:', sig.blockTime
? new Date(sig.blockTime * 1000).toLocaleString()
: 'N/A');
console.log('Status:', sig.err ? 'Failed' : 'Success');
if (sig.memo) {
console.log('Memo:', sig.memo);
}
}
return signatures;
} catch (error) {
console.error('Error fetching transactions:', error);
throw error;
}
}
// Example usage
getRecentTransactions('7EqQdEUXxogiCVXpZUyS6b8Z3Z1dkrKR1A5Q8cPcGvQh', 5);5. Get Program Accounts
Query all accounts owned by a specific program:
import { Connection, PublicKey } from '@solana/web3.js';
async function getProgramAccounts(programId: string) {
try {
const connection = new Connection(
'https://nexus.fortiblox.com/rpc',
{
commitment: 'confirmed',
httpHeaders: { 'X-API-Key': process.env.FORTIBLOX_API_KEY || '' }
}
);
const programPubkey = new PublicKey(programId);
console.log(`Fetching accounts for program: ${programId}`);
const accounts = await connection.getProgramAccounts(programPubkey, {
commitment: 'confirmed',
encoding: 'base64',
});
console.log(`Found ${accounts.length} accounts`);
accounts.slice(0, 5).forEach((account, index) => {
console.log(`\nAccount ${index + 1}:`);
console.log('- Address:', account.pubkey.toBase58());
console.log('- Lamports:', account.account.lamports);
console.log('- Data Length:', account.account.data.length);
console.log('- Owner:', account.account.owner.toBase58());
});
return accounts;
} catch (error) {
console.error('Error fetching program accounts:', error);
throw error;
}
}
// Example usage - Token Program
getProgramAccounts('TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA');6. Monitor Account Changes
Subscribe to real-time account updates using WebSockets:
import { Connection, PublicKey } from '@solana/web3.js';
async function monitorAccount(address: string) {
try {
const connection = new Connection(
'https://nexus.fortiblox.com/rpc',
{
wsEndpoint: 'wss://nexus.fortiblox.com/rpc',
httpHeaders: { 'X-API-Key': process.env.FORTIBLOX_API_KEY || '' }
}
);
const publicKey = new PublicKey(address);
console.log(`Monitoring account: ${address}`);
console.log('Press Ctrl+C to stop\n');
// Subscribe to account changes
const subscriptionId = connection.onAccountChange(
publicKey,
(accountInfo, context) => {
console.log('Account updated!');
console.log('- Slot:', context.slot);
console.log('- Lamports:', accountInfo.lamports);
console.log('- Data Length:', accountInfo.data.length);
console.log('- Owner:', accountInfo.owner.toBase58());
console.log('---');
},
'confirmed'
);
console.log('Subscription ID:', subscriptionId);
// Keep process running
process.on('SIGINT', async () => {
console.log('\nUnsubscribing...');
await connection.removeAccountChangeListener(subscriptionId);
process.exit(0);
});
} catch (error) {
console.error('Error monitoring account:', error);
throw error;
}
}
// Example usage
monitorAccount('7EqQdEUXxogiCVXpZUyS6b8Z3Z1dkrKR1A5Q8cPcGvQh');7. Get Block Information
Fetch details about a specific block:
import { Connection } from '@solana/web3.js';
async function getBlockInfo(slot?: number) {
try {
const connection = new Connection(
'https://nexus.fortiblox.com/rpc',
{
httpHeaders: { 'X-API-Key': process.env.FORTIBLOX_API_KEY || '' }
}
);
// Get latest slot if not provided
if (!slot) {
slot = await connection.getSlot('confirmed');
console.log('Using latest slot:', slot);
}
// Get block information
const block = await connection.getBlock(slot, {
maxSupportedTransactionVersion: 0
});
if (!block) {
console.log('Block not found or not finalized yet');
return null;
}
console.log('\nBlock Information:');
console.log('- Blockhash:', block.blockhash);
console.log('- Parent Slot:', block.parentSlot);
console.log('- Block Time:', block.blockTime
? new Date(block.blockTime * 1000).toLocaleString()
: 'N/A');
console.log('- Transactions:', block.transactions.length);
console.log('- Block Height:', block.blockHeight);
return block;
} catch (error) {
console.error('Error fetching block:', error);
throw error;
}
}
// Example usage
getBlockInfo();8. Parse Transaction Details
Fetch and parse a transaction by signature:
import { Connection } from '@solana/web3.js';
async function getTransactionDetails(signature: string) {
try {
const connection = new Connection(
'https://nexus.fortiblox.com/rpc',
{
httpHeaders: { 'X-API-Key': process.env.FORTIBLOX_API_KEY || '' }
}
);
console.log('Fetching transaction:', signature);
const transaction = await connection.getTransaction(signature, {
maxSupportedTransactionVersion: 0,
commitment: 'confirmed'
});
if (!transaction) {
console.log('Transaction not found');
return null;
}
console.log('\nTransaction Details:');
console.log('- Slot:', transaction.slot);
console.log('- Block Time:', transaction.blockTime
? new Date(transaction.blockTime * 1000).toLocaleString()
: 'N/A');
console.log('- Fee:', transaction.meta?.fee, 'lamports');
console.log('- Status:', transaction.meta?.err ? 'Failed' : 'Success');
if (transaction.meta?.err) {
console.log('- Error:', JSON.stringify(transaction.meta.err));
}
console.log('- Instructions:', transaction.transaction.message.instructions.length);
console.log('- Account Keys:', transaction.transaction.message.accountKeys.length);
// Show balance changes
if (transaction.meta?.preBalances && transaction.meta?.postBalances) {
console.log('\nBalance Changes:');
transaction.transaction.message.accountKeys.forEach((key, index) => {
const pre = transaction.meta!.preBalances[index];
const post = transaction.meta!.postBalances[index];
const change = post - pre;
if (change !== 0) {
console.log(`- ${key.toBase58()}: ${change > 0 ? '+' : ''}${change} lamports`);
}
});
}
return transaction;
} catch (error) {
console.error('Error fetching transaction details:', error);
throw error;
}
}
// Example usage
getTransactionDetails('YOUR_TRANSACTION_SIGNATURE_HERE');Error Handling
Implement robust error handling for common scenarios:
import { Connection, PublicKey } from '@solana/web3.js';
async function robustGetBalance(address: string): Promise<number | null> {
const connection = new Connection(
'https://nexus.fortiblox.com/rpc',
{
httpHeaders: { 'X-API-Key': process.env.FORTIBLOX_API_KEY || '' }
}
);
try {
const publicKey = new PublicKey(address);
const balance = await connection.getBalance(publicKey);
return balance;
} catch (error: any) {
// Handle specific error types
if (error.message?.includes('Invalid public key')) {
console.error('Invalid Solana address provided');
return null;
}
if (error.message?.includes('429')) {
console.error('Rate limit exceeded. Please retry later.');
return null;
}
if (error.message?.includes('timeout')) {
console.error('Request timed out. Please check your connection.');
return null;
}
if (error.message?.includes('401') || error.message?.includes('403')) {
console.error('Authentication failed. Check your API key.');
return null;
}
// Unknown error
console.error('Unexpected error:', error.message);
return null;
}
}Retry Logic
Implement exponential backoff for transient failures:
async function fetchWithRetry<T>(
fetchFn: () => Promise<T>,
maxRetries: number = 3,
baseDelay: number = 1000
): Promise<T> {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await fetchFn();
} catch (error: any) {
const isLastAttempt = attempt === maxRetries - 1;
// Don't retry on authentication errors
if (error.message?.includes('401') || error.message?.includes('403')) {
throw error;
}
if (isLastAttempt) {
throw error;
}
// Exponential backoff
const delay = baseDelay * Math.pow(2, attempt);
console.log(`Attempt ${attempt + 1} failed. Retrying in ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
throw new Error('Max retries exceeded');
}
// Usage
const balance = await fetchWithRetry(() =>
connection.getBalance(new PublicKey(address))
);Best Practices
1. Connection Reuse
Reuse connection instances instead of creating new ones:
// Good: Create once, reuse everywhere
export const connection = new Connection(
process.env.FORTIBLOX_RPC_URL || 'https://nexus.fortiblox.com/rpc',
{
commitment: 'confirmed',
httpHeaders: { 'X-API-Key': process.env.FORTIBLOX_API_KEY || '' }
}
);
// Bad: Creating new connections repeatedly
function someFunction() {
const connection = new Connection(...); // Don't do this
}2. Commitment Levels
Choose appropriate commitment levels:
// For read operations where speed matters
const balance = await connection.getBalance(pubkey, 'processed');
// For critical operations requiring finality
const balance = await connection.getBalance(pubkey, 'finalized');
// Default: Good balance of speed and reliability
const balance = await connection.getBalance(pubkey, 'confirmed');3. Batch Requests
Use getMultipleAccountsInfo for fetching multiple accounts:
async function getMultipleBalances(addresses: string[]) {
const publicKeys = addresses.map(addr => new PublicKey(addr));
const accountsInfo = await connection.getMultipleAccountsInfo(
publicKeys,
'confirmed'
);
return accountsInfo.map((info, index) => ({
address: addresses[index],
balance: info?.lamports || 0
}));
}4. Transaction Simulation
Simulate transactions before sending:
import { Transaction } from '@solana/web3.js';
async function simulateBeforeSend(
transaction: Transaction,
signers: Keypair[]
) {
// Get recent blockhash
transaction.recentBlockhash = (
await connection.getLatestBlockhash()
).blockhash;
transaction.feePayer = signers[0].publicKey;
// Simulate
const simulation = await connection.simulateTransaction(transaction);
if (simulation.value.err) {
console.error('Simulation failed:', simulation.value.err);
console.log('Logs:', simulation.value.logs);
throw new Error('Transaction simulation failed');
}
console.log('Simulation successful!');
console.log('Units consumed:', simulation.value.unitsConsumed);
// Now send the actual transaction
transaction.sign(...signers);
const signature = await connection.sendRawTransaction(
transaction.serialize()
);
return signature;
}5. WebSocket Management
Properly manage WebSocket subscriptions:
class AccountMonitor {
private connection: Connection;
private subscriptionId: number | null = null;
constructor(apiKey: string) {
this.connection = new Connection(
'https://nexus.fortiblox.com/rpc',
{
wsEndpoint: 'wss://nexus.fortiblox.com/rpc',
httpHeaders: { 'X-API-Key': apiKey }
}
);
}
async start(address: string, callback: (accountInfo: any) => void) {
const publicKey = new PublicKey(address);
this.subscriptionId = this.connection.onAccountChange(
publicKey,
callback,
'confirmed'
);
console.log('Monitoring started:', this.subscriptionId);
}
async stop() {
if (this.subscriptionId !== null) {
await this.connection.removeAccountChangeListener(this.subscriptionId);
this.subscriptionId = null;
console.log('Monitoring stopped');
}
}
}Performance Tips
- Use Commitment Wisely: Lower commitment levels (
processed) are faster but less reliable - Batch Operations: Combine multiple requests when possible
- Cache Static Data: Program IDs, token metadata, etc.
- Connection Pooling: Reuse connection instances
- Preflight Checks: Skip when you're confident (
skipPreflight: true) - Parallel Requests: Use
Promise.all()for independent operations
// Parallel requests
const [balance, accountInfo, signatures] = await Promise.all([
connection.getBalance(publicKey),
connection.getAccountInfo(publicKey),
connection.getSignaturesForAddress(publicKey, { limit: 5 })
]);TypeScript Tips
Enable strict typing for better development experience:
import {
Connection,
PublicKey,
AccountInfo,
ParsedAccountData,
Commitment
} from '@solana/web3.js';
// Type-safe configuration
interface FortiBloxConfig {
rpcUrl: string;
apiKey: string;
commitment?: Commitment;
}
function createConnection(config: FortiBloxConfig): Connection {
return new Connection(config.rpcUrl, {
commitment: config.commitment || 'confirmed',
httpHeaders: { 'X-API-Key': config.apiKey }
});
}
// Typed responses
async function getTypedAccountInfo(
address: string
): Promise<AccountInfo<Buffer> | null> {
const publicKey = new PublicKey(address);
return await connection.getAccountInfo(publicKey);
}Next Steps
- Explore Anchor Framework for program development
- Check out Python SDK for backend services
- Review API Reference for all available methods
- Join our Discord for community support