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
| Feature | Usage |
|---|---|
| Swap Execution | Purchase and sale transactions |
| Quote API | Get best swap routes and pricing |
| Price API | Token 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 Code | Cause | Resolution |
|---|---|---|
API_KEY_MISSING | No Jupiter API key configured | Set JUPITER_API_KEY in env |
QUOTE_FAILED | Failed to get swap quote | Check token addresses, try again |
WALLET_NOT_FOUND | Wallet not loaded | Ensure wallet is in env vars |
SWAP_FAILED | On-chain execution failed | Check balance, slippage settings |
MISSING_TRANSACTION | Quote has no transaction | Provide taker in quote request |