Resources

Solana Example#

Create a Swap#

// swap.ts
import { client } from './DexClient';
/**
 * Example: Execute a swap from SOL to USDC
 */
async function executeSwap() {
  try {
    if (!process.env.SOLANA_PRIVATE_KEY) {
      throw new Error('Missing SOLANA_PRIVATE_KEY in .env file');
    }
    // Get quote to fetch token information
    console.log("Getting token information...");
    const quote = await client.dex.getQuote({
        chainId: '501',
        fromTokenAddress: '11111111111111111111111111111111', // SOL
        toTokenAddress: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', // USDC
        amount: '1000000', // Small amount for quote
        slippage: '0.5'
    });
    const tokenInfo = {
        fromToken: {
            symbol: quote.data[0].fromToken.tokenSymbol,
            decimals: parseInt(quote.data[0].fromToken.decimal),
            price: quote.data[0].fromToken.tokenUnitPrice
        },
        toToken: {
            symbol: quote.data[0].toToken.tokenSymbol,
            decimals: parseInt(quote.data[0].toToken.decimal),
            price: quote.data[0].toToken.tokenUnitPrice
        }
    };
    // Convert amount to base units (for display purposes)
    const humanReadableAmount = 0.1; // 0.1 SOL
    const rawAmount = (humanReadableAmount * Math.pow(10, tokenInfo.fromToken.decimals)).toString();
    console.log("\nSwap Details:");
    console.log("--------------------");
    console.log(`From: ${tokenInfo.fromToken.symbol}`);
    console.log(`To: ${tokenInfo.toToken.symbol}`);
    console.log(`Amount: ${humanReadableAmount} ${tokenInfo.fromToken.symbol}`);
    console.log(`Amount in base units: ${rawAmount}`);
    console.log(`Approximate USD value: $${(humanReadableAmount * parseFloat(tokenInfo.fromToken.price)).toFixed(2)}`);
    // Execute the swap
    console.log("\nExecuting swap...");
    const swapResult = await client.dex.executeSwap({
      chainId: '501', // Solana chain ID
      fromTokenAddress: '11111111111111111111111111111111', // SOL
      toTokenAddress: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', // USDC
      amount: rawAmount,
      slippage: '0.5', // 0.5% slippage
      userWalletAddress: process.env.SOLANA_WALLET_ADDRESS!
    });
    console.log('Swap executed successfully:');
    console.log(JSON.stringify(swapResult, null, 2));

    return swapResult;
  } catch (error) {
    if (error instanceof Error) {
      console.error('Error executing swap:', error.message);
      // API errors include details in the message
      if (error.message.includes('API Error:')) {
        const match = error.message.match(/API Error: (.*)/);
        if (match) console.error('API Error Details:', match[1]);
      }
    }
    throw error;
  }
}
// Run if this file is executed directly
if (require.main === module) {
  executeSwap()
    .then(() => process.exit(0))
    .catch((error) => {
      console.error('Error:', error);
      process.exit(1);
    });
}
export { executeSwap };

Get a Quote#

const quote = await client.dex.getQuote({
    chainId: '501',  // Solana
    fromTokenAddress: '11111111111111111111111111111111', // SOL
    toTokenAddress: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', // USDC
    amount: '100000000',  // 0.1 SOL (in lamports)
    slippage: '0.5'     // 0.5%
});

Swap-Instructions Execution#

Import the necessary libraries:

// Required Solana dependencies for DEX interaction
import {
    Connection,          // Handles RPC connections to Solana network
    Keypair,             // Manages wallet keypairs for signing
    PublicKey,           // Handles Solana public key conversion and validation
    TransactionInstruction,    // Core transaction instruction type
    TransactionMessage,        // Builds transaction messages (v0 format)
    VersionedTransaction,      // Supports newer transaction format with lookup tables
    RpcResponseAndContext,     // RPC response wrapper type
    SimulatedTransactionResponse,  // Simulation result type
    AddressLookupTableAccount,     // For transaction size optimization
    PublicKeyInitData              // Public key input type
} from "@solana/web3.js";
import base58 from "bs58";    // Required for private key decoding

Initialize your connection and wallet:

// Note: Consider using a reliable RPC endpoint with high rate limits for production
const connection = new Connection(
   process.env.SOLANA_RPC_URL || "https://api.mainnet-beta.solana.com"
);
// Initialize wallet for signing
// This wallet will be the fee payer and transaction signer
const wallet = Keypair.fromSecretKey(
   Uint8Array.from(base58.decode(userPrivateKey))
);

Set up the parameters for your swap:

// Configure swap parameters
const baseUrl = "https://web3.okx.com/api/v5/dex/aggregator/swap-instruction";
const params = {
    chainId: "501",              // Solana mainnet chain ID
    feePercent: "1",             // Platform fee percentage
    amount: "1000000",           // Amount in smallest denomination (lamports for SOL)
    fromTokenAddress: "11111111111111111111111111111111",  // SOL mint address
    toTokenAddress: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",  // USDC mint address
    slippage: "0.1",             // Slippage tolerance in percentage
    userWalletAddress: userAddress,   // Wallet performing the swap
    priceTolerance: "0",         // Maximum allowed price impact
    autoSlippage: "false",       // Use fixed slippage instead of auto
    pathNum: "3"                 // Maximum routes to consider
};

Process the Swap Instructions:#

// Helper function to convert DEX API instructions to Solana format
function createTransactionInstruction(instruction) {
    return new TransactionInstruction({
        programId: new PublicKey(instruction.programId),  // DEX program ID
        keys: instruction.accounts.map((key) => ({
            pubkey: new PublicKey(key.pubkey),    // Account address
            isSigner: key.isSigner,     // True if account must sign tx
            isWritable: key.isWritable  // True if instruction modifies account
        })),
        data: Buffer.from(instruction.data, 'base64')  // Instruction parameters
    });
}
// Fetch optimal swap route and instructions from DEX
const timestamp = new Date().toISOString();
const requestPath = "/api/v5/dex/aggregator/swap-instruction";
const queryString = "?" + new URLSearchParams(params).toString();
const headers = getHeaders(timestamp, "GET", requestPath, queryString);
const response = await fetch(
    `https://web3.okx.com${requestPath}${queryString}`,
    { method: 'GET', headers }
);
const { data } = await response.json();
const { instructionLists, addressLookupTableAccount } = data;
// Process DEX instructions into Solana-compatible format
const instructions = [];
// Remove duplicate lookup table addresses returned by DEX
const uniqueLookupTables = Array.from(new Set(addressLookupTableAccount));
console.log("Lookup tables to load:", uniqueLookupTables);
// Convert each DEX instruction to Solana format
if (instructionLists?.length) {
    instructions.push(...instructionLists.map(createTransactionInstruction));
}

Handle Address Lookup Tables#

// Process lookup tables for transaction optimization
// Lookup tables are crucial for complex swaps that interact with many accounts
// They significantly reduce transaction size and cost
const addressLookupTableAccounts = [];
if (uniqueLookupTables?.length > 0) {
    console.log("Loading address lookup tables...");
    // Fetch all lookup tables in parallel for better performance
    const lookupTableAccounts = await Promise.all(
        uniqueLookupTables.map(async (address) => {
            const pubkey = new PublicKey(address);
            // Get lookup table account data from Solana
            const account = await connection
                .getAddressLookupTable(pubkey)
                .then((res) => res.value);
            if (!account) {
                throw new Error(`Could not fetch lookup table account ${address}`);
            }
            return account;
        })
    );
    addressLookupTableAccounts.push(...lookupTableAccounts);
}

Create and Sign Transaction#

// Get recent blockhash for transaction timing and uniqueness
const latestBlockhash = await connection.getLatestBlockhash('finalized');
// Create versioned transaction message (V0 format required for lookup table support)
const messageV0 = new TransactionMessage({
    payerKey: wallet.publicKey,     // Fee payer address
    recentBlockhash: latestBlockhash.blockhash,  // Transaction timing
    instructions                     // Swap instructions from DEX
}).compileToV0Message(addressLookupTableAccounts);  // Include lookup tables
// Create new versioned transaction with optimizations
const transaction = new VersionedTransaction(messageV0);
// Simulate transaction to check for errors
// This helps catch issues before paying fees
const result = await connection.simulateTransaction(transaction);
// Sign transaction with fee payer wallet
transaction.sign([wallet]);

Execute Transaction#

// Send transaction to Solana
// skipPreflight=false ensures additional validation
// maxRetries helps handle network issues
const txId = await connection.sendRawTransaction(transaction.serialize(), {
    skipPreflight: false,  // Run preflight validation
    maxRetries: 5         // Retry on failure
});
// Log transaction results
console.log("Transaction ID:", txId);
console.log("Explorer URL:", `https://solscan.io/tx/${txId}`);
// Wait for confirmation
await connection.confirmTransaction({
    signature: txId,
    blockhash: latestBlockhash.blockhash,
    lastValidBlockHeight: latestBlockhash.lastValidBlockHeight
});
console.log("Transaction confirmed!");