FortiBlox LogoFortiBlox Docs
NexusSDKs

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.js

For TypeScript projects, types are included automatically.

Additional Dependencies

For complete examples, you may also want:

npm install bs58 dotenv

Authentication 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/rpc

Load 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

  1. Use Commitment Wisely: Lower commitment levels (processed) are faster but less reliable
  2. Batch Operations: Combine multiple requests when possible
  3. Cache Static Data: Program IDs, token metadata, etc.
  4. Connection Pooling: Reuse connection instances
  5. Preflight Checks: Skip when you're confident (skipPreflight: true)
  6. 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

Additional Resources