OS Trading Engine
Technical Documentation
Integrations
Jupiter Aggregator

Jupiter Aggregator

Nexgent integrates with Jupiter Aggregator for token swaps on Solana. Jupiter finds the best routes across all Solana DEXes to get optimal pricing.

Overview

FeatureUsage
Swap ExecutionPurchase and sale transactions
Quote APIGet best swap routes and pricing
Price APIToken price feeds (USD)

Configuration

# Required for swap execution
JUPITER_API_KEY=your-api-key-here

# Optional: Override API URLs
JUPITER_API_URL=https://lite-api.jup.ag/price/v3
⚠️

The Jupiter Ultra API (api.jup.ag/ultra) requires an API key. Get one at jup.ag (opens in a new tab).


Swap Provider

The JupiterSwapProvider handles all swap operations.

Architecture

┌─────────────────┐
│ Trading Executor│
└────────┬────────┘


┌─────────────────┐
│  Swap Service   │  ← Provider abstraction
└────────┬────────┘


┌─────────────────┐
│Jupiter Provider │  ← Implementation
└────────┬────────┘


┌─────────────────┐
│ Jupiter API     │
│ (api.jup.ag)    │
└─────────────────┘

Get Quote

// infrastructure/external/jupiter/jupiter-swap-provider.ts
 
async getQuote(request: SwapQuoteRequest): Promise<SwapQuote> {
  const url = new URL('https://api.jup.ag/ultra/v1/order');
  url.searchParams.set('inputMint', request.inputMint);
  url.searchParams.set('outputMint', request.outputMint);
  url.searchParams.set('amount', request.amount.toString());
  
  // Include taker address to get transaction in response
  if (request.walletAddress) {
    url.searchParams.set('taker', request.walletAddress);
  }
  
  const response = await fetch(url.toString(), {
    headers: {
      'x-api-key': process.env.JUPITER_API_KEY!,
    },
  });
  
  const data = await response.json();
  
  return {
    requestId: data.requestId,
    inputAmount: parseInt(data.inAmount),
    outputAmount: parseInt(data.outAmount),
    priceImpact: parseFloat(data.priceImpactPct || '0'),
    slippageBps: data.slippageBps,
    transaction: data.transaction, // Base64-encoded transaction
    routes: data.routePlan,
    swapPayload: data, // Store full response
  };
}

Execute Swap

async executeSwap(request: SwapExecuteRequest): Promise<SwapResult> {
  const { quote, walletAddress, isSimulation } = request;
  
  // Simulation mode: Return quote data without on-chain execution
  if (isSimulation) {
    return {
      success: true,
      transactionHash: null,
      inputAmount: quote.inputAmount,
      outputAmount: quote.outputAmount,
      actualPrice: quote.inputAmount / quote.outputAmount,
      priceImpact: quote.priceImpact,
      swapPayload: quote.swapPayload,
    };
  }
  
  // Live mode: Sign and execute on-chain
  
  // 1. Get keypair from wallet store
  const keypair = walletStore.getKeypair(walletAddress);
  if (!keypair) {
    throw new SwapServiceError('Wallet not loaded', 'WALLET_NOT_FOUND');
  }
  
  // 2. Sign the transaction
  const signedTransaction = this.signTransaction(quote.transaction!, keypair);
  
  // 3. Submit to Jupiter execute endpoint
  const result = await this.submitToJupiter(signedTransaction, quote.requestId);
  
  return {
    success: true,
    transactionHash: result.signature,
    inputAmount: parseInt(result.inputAmountResult || quote.inputAmount.toString()),
    outputAmount: parseInt(result.outputAmountResult || quote.outputAmount.toString()),
    priceImpact: quote.priceImpact,
    swapPayload: result,
  };
}

Transaction Signing

private signTransaction(base64Transaction: string, keypair: Keypair): string {
  // Decode base64 to buffer
  const transactionBuffer = Buffer.from(base64Transaction, 'base64');
  
  // Deserialize as VersionedTransaction (Jupiter uses v0)
  const transaction = VersionedTransaction.deserialize(transactionBuffer);
  
  // Sign with wallet keypair
  transaction.sign([keypair]);
  
  // Re-encode to base64
  const signedBuffer = transaction.serialize();
  return Buffer.from(signedBuffer).toString('base64');
}

Submit to Jupiter

private async submitToJupiter(
  signedTransaction: string,
  requestId: string
): Promise<JupiterExecuteResponse> {
  const response = await fetch('https://api.jup.ag/ultra/v1/execute', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'x-api-key': process.env.JUPITER_API_KEY!,
    },
    body: JSON.stringify({
      signedTransaction,
      requestId,
    }),
  });
  
  const result = await response.json();
  
  if (result.status === 'Failed') {
    throw new SwapServiceError(
      `Swap execution failed: ${result.error}`,
      'SWAP_FAILED'
    );
  }
  
  return result;
}

Price Provider

Jupiter also provides token prices via their Price API.

Price Fetch

// infrastructure/external/jupiter/price/jupiter-price-provider.ts
 
async getTokenPrice(tokenAddress: string): Promise<TokenPrice> {
  const url = `https://lite-api.jup.ag/price/v3?ids=${tokenAddress}`;
  
  const response = await fetch(url);
  const data = await response.json();
  
  const tokenData = data[tokenAddress];
  if (!tokenData || !tokenData.usdPrice) {
    throw new PriceFeedServiceError('Token not found', 'TOKEN_NOT_FOUND');
  }
  
  // Jupiter returns USD price, convert to SOL using Pyth
  const solUsdPrice = PriceService.getInstance().getSolPrice();
  const priceSol = tokenData.usdPrice / solUsdPrice;
  
  return {
    tokenAddress: tokenAddress.toLowerCase(),
    priceSol,
    priceUsd: tokenData.usdPrice,
    priceChange24h: tokenData.priceChange24h || 0,
    lastUpdated: new Date(),
  };
}

Batch Price Fetch

async getMultipleTokenPrices(tokenAddresses: string[]): Promise<TokenPrice[]> {
  const results: TokenPrice[] = [];
  const solUsdPrice = PriceService.getInstance().getSolPrice();
  
  // Process in batches of 50 (Jupiter limit)
  for (let i = 0; i < tokenAddresses.length; i += 50) {
    const batch = tokenAddresses.slice(i, i + 50);
    const addressesString = batch.join(',');
    
    const url = `https://lite-api.jup.ag/price/v3?ids=${addressesString}`;
    const response = await fetch(url);
    const data = await response.json();
    
    for (const address of batch) {
      const tokenData = data[address];
      if (tokenData?.usdPrice) {
        results.push({
          tokenAddress: address.toLowerCase(),
          priceSol: tokenData.usdPrice / solUsdPrice,
          priceUsd: tokenData.usdPrice,
          priceChange24h: tokenData.priceChange24h || 0,
          lastUpdated: new Date(),
        });
      }
    }
  }
  
  return results;
}

API Reference

Quote Response

interface JupiterQuoteResponse {
  requestId: string;          // Unique request ID
  transaction?: string;       // Base64 transaction (if taker provided)
  inAmount: string;           // Input amount (lamports/smallest unit)
  outAmount: string;          // Expected output amount
  slippageBps: number;        // Slippage in basis points
  priceImpactPct?: string;    // Price impact percentage
  routePlan?: unknown[];      // Swap routes
  inputMint?: string;         // Input token mint
  outputMint?: string;        // Output token mint
}

Execute Response

interface JupiterExecuteResponse {
  status: 'Success' | 'Failed';
  signature?: string;          // Transaction hash
  slot?: string;               // Solana slot
  error?: string;              // Error message
  inputAmountResult?: string;  // Actual input
  outputAmountResult?: string; // Actual output
  swapEvents?: Array<{
    inputMint: string;
    inputAmount: string;
    outputMint: string;
    outputAmount: string;
  }>;
}

Error Handling

Error CodeCauseResolution
API_KEY_MISSINGNo Jupiter API key configuredSet JUPITER_API_KEY in env
QUOTE_FAILEDFailed to get swap quoteCheck token addresses, try again
WALLET_NOT_FOUNDWallet not loadedEnsure wallet is in env vars
SWAP_FAILEDOn-chain execution failedCheck balance, slippage settings
MISSING_TRANSACTIONQuote has no transactionProvide taker in quote request