UNPKG

delegate-framework

Version:

A TypeScript framework for building robust, production-ready blockchain workflows with comprehensive error handling, logging, and testing. Maintained by delegate.fun

158 lines 6.97 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.JupiterClient = void 0; const web3_js_1 = require("@solana/web3.js"); const error_handling_1 = require("../../utils/error-handling"); class JupiterClient { constructor(config) { this.requestId = 0; this.config = { quoteApiUrl: JupiterClient.DEFAULT_QUOTE_API_URL, timeout: JupiterClient.DEFAULT_TIMEOUT, retries: JupiterClient.DEFAULT_RETRIES, ...config, }; this.logger = this.config.logger; } /** * Get a quote for swapping tokens * @param fromMint - The input token mint * @param toMint - The output token mint * @param amount - The amount to swap (in smallest unit) * @param slippageBps - Slippage tolerance in basis points (default: 100 = 1%) * @returns Quote response from Jupiter */ async getQuote(fromMint, toMint, amount, slippageBps = 100) { // Validate inputs if (!fromMint || !toMint) { (0, error_handling_1.throwError)('Invalid mint addresses provided', 'Jupiter Quote Error'); } if (!amount || Number(amount) <= 0) { (0, error_handling_1.throwError)('Invalid amount provided', 'Jupiter Quote Error'); } if (slippageBps < 0 || slippageBps > 10000) { (0, error_handling_1.throwError)('Slippage must be between 0 and 10000 basis points', 'Jupiter Quote Error'); } return this.makeRequest(async () => { const url = `${this.config['quoteApiUrl']}/quote?inputMint=${fromMint.toBase58()}&outputMint=${toMint.toBase58()}&amount=${amount}&slippageBps=${slippageBps}`; this.logger?.debug('Requesting Jupiter quote', { fromMint: fromMint.toBase58(), toMint: toMint.toBase58(), amount, slippageBps, url, }); const response = await fetch(url); if (!response || typeof response.ok !== 'boolean') { throw new Error('No response received from Jupiter'); } if (!response.ok) { (0, error_handling_1.throwError)(`HTTP ${response.status}: ${response.statusText}`, 'Jupiter API Error'); } const quoteResponse = await response.json(); this.logger?.debug('Jupiter quote received', { inAmount: quoteResponse.inAmount, outAmount: quoteResponse.outAmount, priceImpactPct: quoteResponse.priceImpactPct, otherAmountThreshold: quoteResponse.otherAmountThreshold, }); return quoteResponse; }, 'getQuote'); } /** * Get a swap transaction from Jupiter * @param quoteResponse - The quote response from getQuote * @param swapParams - Additional swap parameters * @returns Versioned transaction ready for signing */ async getSwapTransaction(quoteResponse, swapParams) { if (!quoteResponse) { (0, error_handling_1.throwError)('Quote response is required', 'Jupiter Swap Error'); } if (!swapParams) { (0, error_handling_1.throwError)('Swap parameters are required', 'Jupiter Swap Error'); } return this.makeRequest(async () => { this.logger?.debug('Requesting Jupiter swap transaction', { userPublicKey: swapParams.userPublicKey, }); const response = await fetch(`${this.config['quoteApiUrl']}/swap`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(swapParams), }); if (!response || typeof response.ok !== 'boolean') { throw new Error('No response received from Jupiter'); } if (!response.ok) { const errorText = await response.text(); (0, error_handling_1.throwError)(`HTTP ${response.status}: ${errorText}`, 'Jupiter Swap Error'); } const { swapTransaction } = await response.json(); if (!swapTransaction) { (0, error_handling_1.throwError)('No swap transaction received from Jupiter', 'Jupiter Swap Error'); } this.logger?.debug('Jupiter swap transaction received', { transactionSize: swapTransaction.length, }); return web3_js_1.VersionedTransaction.deserialize(Buffer.from(swapTransaction, 'base64')); }, 'getSwapTransaction'); } /** * Make a request with retry logic and error handling * @param operation - The operation to perform * @param operationName - Name of the operation for logging * @returns Result of the operation */ async makeRequest(operation, operationName) { const requestId = ++this.requestId; this.logger?.debug(`Request ${requestId} started: ${operationName}`); let lastError; for (let attempt = 1; attempt <= this.config['retries']; attempt++) { try { const result = await Promise.race([ operation(), new Promise((_, reject) => { setTimeout(() => { reject(new Error(`Operation timed out after ${this.config['timeout']}ms`)); }, this.config['timeout']); }), ]); this.logger?.debug(`Request ${requestId} completed: ${operationName}`, { result }); return result; } catch (error) { // If error is not an Error instance, wrap it lastError = error instanceof Error ? error : new Error(String(error)); this.logger?.warn(`Request ${requestId} attempt ${attempt} failed: ${operationName}`, lastError); if (attempt === this.config['retries']) { this.logger?.error(`Request ${requestId} failed after ${attempt} attempts: ${operationName}`, lastError); (0, error_handling_1.throwError)(lastError, `Jupiter API Request Failed (${operationName})`); } await this.delay(Math.pow(2, attempt - 1) * 1000); } } throw lastError; } /** * Utility method for delays * @param ms - Milliseconds to delay */ delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } /** * Get the current configuration * @returns Current client configuration */ getConfig() { return this.config; } } exports.JupiterClient = JupiterClient; JupiterClient.DEFAULT_TIMEOUT = 30000; JupiterClient.DEFAULT_RETRIES = 3; JupiterClient.DEFAULT_QUOTE_API_URL = "https://quote-api.jup.ag/v6"; //# sourceMappingURL=jupiter.js.map