UNPKG

@solana-tracker/data-api

Version:

Official Solana Tracker Data API client for accessing Solana Data

633 lines (632 loc) 25.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Client = exports.ValidationError = exports.RateLimitError = exports.DataApiError = void 0; class DataApiError extends Error { constructor(message, status, code) { super(message); this.status = status; this.code = code; this.name = 'DataApiError'; } } exports.DataApiError = DataApiError; class RateLimitError extends DataApiError { constructor(message, retryAfter) { super(message, 429, 'RATE_LIMIT_EXCEEDED'); this.retryAfter = retryAfter; this.name = 'RateLimitError'; } } exports.RateLimitError = RateLimitError; class ValidationError extends DataApiError { constructor(message) { super(message, 400, 'VALIDATION_ERROR'); this.name = 'ValidationError'; } } exports.ValidationError = ValidationError; /** * Solana Tracker Data API client */ class Client { /** * Creates a new instance of the Solana Tracker Data API client * @param config Configuration options including API key */ constructor(config) { this.apiKey = config.apiKey; this.baseUrl = config.baseUrl || 'https://data.solanatracker.io'; } /** * Makes a request to the API * @param endpoint The API endpoint * @param options Additional fetch options * @returns The API response */ async request(endpoint, options) { const headers = { 'x-api-key': this.apiKey, 'Content-Type': 'application/json', ...options === null || options === void 0 ? void 0 : options.headers, }; try { const response = await fetch(`${this.baseUrl}${endpoint}`, { ...options, headers, }); if (!response.ok) { if (response.status === 429) { const retryAfter = response.headers.get('Retry-After'); if (options === null || options === void 0 ? void 0 : options.disableLogs) { console.warn(`Rate limit exceeded for ${endpoint}. Retry after: ${retryAfter || '1'} seconds`); } throw new RateLimitError('Rate limit exceeded', retryAfter ? parseInt(retryAfter) : undefined); } throw new DataApiError(`API request failed: ${response.status} ${response.statusText}`, response.status); } return response.json(); } catch (error) { if (error instanceof DataApiError) { throw error; } throw new DataApiError('An unexpected error occurred'); } } /** * Validates a Solana public key * @param address The address to validate * @param paramName The parameter name for error messaging * @throws ValidationError if the address is invalid */ validatePublicKey(address, paramName) { // Basic validation - a more robust implementation would use the PublicKey class from @solana/web3.js if (!address || !/^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(address)) { throw new ValidationError(`Invalid ${paramName}: ${address}`); } } // ======== TOKEN ENDPOINTS ======== /** * Get comprehensive information about a specific token * @param tokenAddress The token's mint address * @returns Detailed token information */ async getTokenInfo(tokenAddress) { this.validatePublicKey(tokenAddress, 'tokenAddress'); return this.request(`/tokens/${tokenAddress}`); } /** * Get token information by searching with a pool address * @param poolAddress The pool address * @returns Detailed token information */ async getTokenByPool(poolAddress) { this.validatePublicKey(poolAddress, 'poolAddress'); return this.request(`/tokens/by-pool/${poolAddress}`); } /** * Get token holders information * @param tokenAddress The token's mint address * @returns Information about token holders */ async getTokenHolders(tokenAddress) { this.validatePublicKey(tokenAddress, 'tokenAddress'); return this.request(`/tokens/${tokenAddress}/holders`); } /** * Get top 20 token holders * @param tokenAddress The token's mint address * @returns Top holders information */ async getTopHolders(tokenAddress) { this.validatePublicKey(tokenAddress, 'tokenAddress'); return this.request(`/tokens/${tokenAddress}/holders/top`); } /** * Get the all-time high price for a token * @param tokenAddress The token's mint address * @returns All-time high price data */ async getAthPrice(tokenAddress) { this.validatePublicKey(tokenAddress, 'tokenAddress'); return this.request(`/tokens/${tokenAddress}/ath`); } /** * Get tokens created by a specific wallet * @param wallet The deployer wallet address * @returns List of tokens created by the deployer */ async getTokensByDeployer(wallet) { this.validatePublicKey(wallet, 'wallet'); return this.request(`/deployer/${wallet}`); } /** * Search for tokens with flexible filtering options * @param params Search parameters and filters * @returns Search results */ async searchTokens(params) { const queryParams = new URLSearchParams(); for (const [key, value] of Object.entries(params)) { if (value !== undefined) { queryParams.append(key, value.toString()); } } return this.request(`/search?${queryParams}`); } /** * Get the latest tokens * @param page Page number (1-10) * @returns List of latest tokens */ async getLatestTokens(page = 1) { if (page < 1 || page > 10) { throw new ValidationError('Page must be between 1 and 10'); } return this.request(`/tokens/latest?page=${page}`); } /** * Get information about multiple tokens * @param tokenAddresses Array of token addresses * @returns Information about multiple tokens */ async getMultipleTokens(tokenAddresses) { if (tokenAddresses.length > 20) { throw new ValidationError('Maximum of 20 tokens per request'); } tokenAddresses.forEach((addr) => this.validatePublicKey(addr, 'tokenAddress')); return this.request('/tokens/multi', { method: 'POST', body: JSON.stringify({ tokens: tokenAddresses }), }); } /** * Get trending tokens * @param timeframe Optional timeframe for trending calculation * @returns List of trending tokens */ async getTrendingTokens(timeframe) { const validTimeframes = ['5m', '15m', '30m', '1h', '2h', '3h', '4h', '5h', '6h', '12h', '24h']; if (timeframe && !validTimeframes.includes(timeframe)) { throw new ValidationError(`Invalid timeframe. Must be one of: ${validTimeframes.join(', ')}`); } const endpoint = timeframe ? `/tokens/trending/${timeframe}` : '/tokens/trending'; return this.request(endpoint); } /** * Get tokens sorted by volume * @param timeframe Optional timeframe for volume calculation * @returns List of tokens sorted by volume */ async getTokensByVolume(timeframe) { const validTimeframes = ['5m', '15m', '30m', '1h', '6h', '12h', '24h']; if (timeframe && !validTimeframes.includes(timeframe)) { throw new ValidationError(`Invalid timeframe. Must be one of: ${validTimeframes.join(', ')}`); } const endpoint = timeframe ? `/tokens/volume/${timeframe}` : '/tokens/volume'; return this.request(endpoint); } /** * Get an overview of latest, graduating, and graduated tokens * @returns Token overview */ async getTokenOverview() { return this.request('/tokens/multi/all'); } /** * Get graduated tokens * @returns List of graduated tokens */ async getGraduatedTokens() { return this.request('/tokens/multi/graduated'); } // ======== PRICE ENDPOINTS ======== /** * Get price information for a token * @param tokenAddress The token's mint address * @param priceChanges Include price change percentages * @returns Price data */ async getPrice(tokenAddress, priceChanges) { this.validatePublicKey(tokenAddress, 'tokenAddress'); const query = priceChanges ? '?priceChanges=true' : ''; return this.request(`/price?token=${tokenAddress}${query}`); } /** * Get historic price information for a token * @param tokenAddress The token's mint address * @returns Historic price data */ async getPriceHistory(tokenAddress) { this.validatePublicKey(tokenAddress, 'tokenAddress'); return this.request(`/price/history?token=${tokenAddress}`); } /** * Get price at a specific timestamp * @param tokenAddress The token's mint address * @param timestamp Unix timestamp * @returns Price at the specified timestamp */ async getPriceAtTimestamp(tokenAddress, timestamp) { this.validatePublicKey(tokenAddress, 'tokenAddress'); return this.request(`/price/history/timestamp?token=${tokenAddress}&timestamp=${timestamp}`); } /** * Get lowest and highest price in a time range * @param tokenAddress The token's mint address * @param timeFrom Start time (unix timestamp) * @param timeTo End time (unix timestamp) * @returns Price range data */ async getPriceRange(tokenAddress, timeFrom, timeTo) { this.validatePublicKey(tokenAddress, 'tokenAddress'); return this.request(`/price/history/range?token=${tokenAddress}&time_from=${timeFrom}&time_to=${timeTo}`); } /** * Get price information for a token (POST method) * @param tokenAddress The token's mint address * @param priceChanges Include price change percentages * @returns Price data */ async postPrice(tokenAddress, priceChanges) { this.validatePublicKey(tokenAddress, 'tokenAddress'); return this.request('/price', { method: 'POST', body: JSON.stringify({ token: tokenAddress, priceChanges: priceChanges || false }) }); } /** * Get price information for multiple tokens * @param tokenAddresses Array of token addresses * @param priceChanges Include price change percentages * @returns Price data for multiple tokens */ async getMultiplePrices(tokenAddresses, priceChanges) { if (tokenAddresses.length > 100) { throw new ValidationError('Maximum of 100 tokens per request'); } tokenAddresses.forEach((addr) => this.validatePublicKey(addr, 'tokenAddress')); const query = priceChanges ? '&priceChanges=true' : ''; return this.request(`/price/multi?tokens=${tokenAddresses.join(',')}${query}`); } /** * Get price information for multiple tokens (POST method) * @param tokenAddresses Array of token addresses * @param priceChanges Include price change percentages * @returns Price data for multiple tokens */ async postMultiplePrices(tokenAddresses, priceChanges) { if (tokenAddresses.length > 100) { throw new ValidationError('Maximum of 100 tokens per request'); } tokenAddresses.forEach((addr) => this.validatePublicKey(addr, 'tokenAddress')); return this.request('/price/multi', { method: 'POST', body: JSON.stringify({ tokens: tokenAddresses, priceChanges: priceChanges || false }) }); } // ======== WALLET ENDPOINTS ======== /** * Get basic wallet information * @param owner Wallet address * @returns Basic wallet data */ async getWalletBasic(owner) { this.validatePublicKey(owner, 'owner'); return this.request(`/wallet/${owner}/basic`); } /** * Get all tokens in a wallet * @param owner Wallet address * @returns Detailed wallet data */ async getWallet(owner) { this.validatePublicKey(owner, 'owner'); return this.request(`/wallet/${owner}`); } /** * Get wallet tokens with pagination * @param owner Wallet address * @param page Page number * @returns Paginated wallet data */ async getWalletPage(owner, page) { this.validatePublicKey(owner, 'owner'); return this.request(`/wallet/${owner}/page/${page}`); } /** * Get wallet trades * @param owner Wallet address * @param cursor Pagination cursor * @param showMeta Include token metadata * @param parseJupiter Parse Jupiter swaps * @param hideArb Hide arbitrage transactions * @returns Wallet trades data */ async getWalletTrades(owner, cursor, showMeta, parseJupiter, hideArb) { this.validatePublicKey(owner, 'owner'); const params = new URLSearchParams(); if (cursor) params.append('cursor', cursor.toString()); if (showMeta) params.append('showMeta', 'true'); if (parseJupiter) params.append('parseJupiter', 'true'); if (hideArb) params.append('hideArb', 'true'); const query = params.toString() ? `?${params.toString()}` : ''; return this.request(`/wallet/${owner}/trades${query}`); } // ======== TRADE ENDPOINTS ======== /** * Get trades for a token * @param tokenAddress Token address * @param cursor Pagination cursor * @param showMeta Include token metadata * @param parseJupiter Parse Jupiter swaps * @param hideArb Hide arbitrage transactions * @returns Token trades data */ async getTokenTrades(tokenAddress, cursor, showMeta, parseJupiter, hideArb) { this.validatePublicKey(tokenAddress, 'tokenAddress'); const params = new URLSearchParams(); if (cursor) params.append('cursor', cursor.toString()); if (showMeta) params.append('showMeta', 'true'); if (parseJupiter) params.append('parseJupiter', 'true'); if (hideArb) params.append('hideArb', 'true'); const query = params.toString() ? `?${params.toString()}` : ''; return this.request(`/trades/${tokenAddress}${query}`); } /** * Get trades for a specific token and pool * @param tokenAddress Token address * @param poolAddress Pool address * @param cursor Pagination cursor * @param showMeta Include token metadata * @param parseJupiter Parse Jupiter swaps * @param hideArb Hide arbitrage transactions * @returns Pool-specific token trades data */ async getPoolTrades(tokenAddress, poolAddress, cursor, showMeta, parseJupiter, hideArb) { this.validatePublicKey(tokenAddress, 'tokenAddress'); this.validatePublicKey(poolAddress, 'poolAddress'); const params = new URLSearchParams(); if (cursor) params.append('cursor', cursor.toString()); if (showMeta) params.append('showMeta', 'true'); if (parseJupiter) params.append('parseJupiter', 'true'); if (hideArb) params.append('hideArb', 'true'); const query = params.toString() ? `?${params.toString()}` : ''; return this.request(`/trades/${tokenAddress}/${poolAddress}${query}`); } /** * Get trades for a specific token, pool, and wallet * @param tokenAddress Token address * @param poolAddress Pool address * @param owner Wallet address * @param cursor Pagination cursor * @param showMeta Include token metadata * @param parseJupiter Parse Jupiter swaps * @param hideArb Hide arbitrage transactions * @returns User-specific pool trades data */ async getUserPoolTrades(tokenAddress, poolAddress, owner, cursor, showMeta, parseJupiter, hideArb) { this.validatePublicKey(tokenAddress, 'tokenAddress'); this.validatePublicKey(poolAddress, 'poolAddress'); this.validatePublicKey(owner, 'owner'); const params = new URLSearchParams(); if (cursor) params.append('cursor', cursor.toString()); if (showMeta) params.append('showMeta', 'true'); if (parseJupiter) params.append('parseJupiter', 'true'); if (hideArb) params.append('hideArb', 'true'); const query = params.toString() ? `?${params.toString()}` : ''; return this.request(`/trades/${tokenAddress}/${poolAddress}/${owner}${query}`); } /** * Get trades for a specific token and wallet * @param tokenAddress Token address * @param owner Wallet address * @param cursor Pagination cursor * @param showMeta Include token metadata * @param parseJupiter Parse Jupiter swaps * @param hideArb Hide arbitrage transactions * @returns User-specific token trades data */ async getUserTokenTrades(tokenAddress, owner, cursor, showMeta, parseJupiter, hideArb) { this.validatePublicKey(tokenAddress, 'tokenAddress'); this.validatePublicKey(owner, 'owner'); const params = new URLSearchParams(); if (cursor) params.append('cursor', cursor.toString()); if (showMeta) params.append('showMeta', 'true'); if (parseJupiter) params.append('parseJupiter', 'true'); if (hideArb) params.append('hideArb', 'true'); const query = params.toString() ? `?${params.toString()}` : ''; return this.request(`/trades/${tokenAddress}/by-wallet/${owner}${query}`); } // ======== CHART DATA ENDPOINTS ======== /** * Get OHLCV data for a token * @param tokenAddress Token address * @param type Time interval (e.g., "1s", "1m", "1h", "1d") * @param timeFrom Start time (Unix timestamp in seconds) * @param timeTo End time (Unix timestamp in seconds) * @param marketCap Return chart for market cap instead of pricing * @param removeOutliers Disable outlier removal if set to false (default: true) * @returns OHLCV chart data */ async getChartData(tokenAddress, type, timeFrom, timeTo, marketCap, removeOutliers) { this.validatePublicKey(tokenAddress, 'tokenAddress'); const params = new URLSearchParams(); if (type) params.append('type', type); if (timeFrom) params.append('time_from', timeFrom.toString()); if (timeTo) params.append('time_to', timeTo.toString()); if (marketCap) params.append('marketCap', 'true'); if (removeOutliers === false) params.append('removeOutliers', 'false'); const query = params.toString() ? `?${params.toString()}` : ''; return this.request(`/chart/${tokenAddress}${query}`); } /** * Get OHLCV data for a specific token and pool * @param tokenAddress Token address * @param poolAddress Pool address * @param type Time interval (e.g., "1s", "1m", "1h", "1d") * @param timeFrom Start time (Unix timestamp in seconds) * @param timeTo End time (Unix timestamp in seconds) * @param marketCap Return chart for market cap instead of pricing * @param removeOutliers Disable outlier removal if set to false (default: true) * @returns OHLCV chart data for a specific pool */ async getPoolChartData(tokenAddress, poolAddress, type, timeFrom, timeTo, marketCap, removeOutliers) { this.validatePublicKey(tokenAddress, 'tokenAddress'); this.validatePublicKey(poolAddress, 'poolAddress'); const params = new URLSearchParams(); if (type) params.append('type', type); if (timeFrom) params.append('time_from', timeFrom.toString()); if (timeTo) params.append('time_to', timeTo.toString()); if (marketCap) params.append('marketCap', 'true'); if (removeOutliers === false) params.append('removeOutliers', 'false'); const query = params.toString() ? `?${params.toString()}` : ''; return this.request(`/chart/${tokenAddress}/${poolAddress}${query}`); } /** * Get holder count chart data * @param tokenAddress Token address * @param type Time interval (e.g., "1s", "1m", "1h", "1d") * @param timeFrom Start time (Unix timestamp in seconds) * @param timeTo End time (Unix timestamp in seconds) * @returns Holder count chart data */ async getHoldersChart(tokenAddress, type, timeFrom, timeTo) { this.validatePublicKey(tokenAddress, 'tokenAddress'); const params = new URLSearchParams(); if (type) params.append('type', type); if (timeFrom) params.append('time_from', timeFrom.toString()); if (timeTo) params.append('time_to', timeTo.toString()); const query = params.toString() ? `?${params.toString()}` : ''; return this.request(`/holders/chart/${tokenAddress}${query}`); } // ======== PNL DATA ENDPOINTS ======== /** * Get PnL data for all positions of a wallet * @param wallet Wallet address * @param showHistoricPnL Add PnL data for 1d, 7d and 30d intervals (BETA) * @param holdingCheck Additional check for current holding value * @param hideDetails Return only summary without data for each token * @returns Wallet PnL data */ async getWalletPnL(wallet, showHistoricPnL, holdingCheck, hideDetails) { this.validatePublicKey(wallet, 'wallet'); const params = new URLSearchParams(); if (showHistoricPnL) params.append('showHistoricPnL', 'true'); if (holdingCheck) params.append('holdingCheck', 'true'); if (hideDetails) params.append('hideDetails', 'true'); const query = params.toString() ? `?${params.toString()}` : ''; return this.request(`/pnl/${wallet}${query}`); } /** * Get the first 100 buyers of a token with PnL data * @param tokenAddress Token address * @returns First buyers data with PnL */ async getFirstBuyers(tokenAddress) { this.validatePublicKey(tokenAddress, 'tokenAddress'); return this.request(`/first-buyers/${tokenAddress}`); } /** * Get PnL data for a specific token in a wallet * @param wallet Wallet address * @param tokenAddress Token address * @returns Token-specific PnL data */ async getTokenPnL(wallet, tokenAddress) { this.validatePublicKey(wallet, 'wallet'); this.validatePublicKey(tokenAddress, 'tokenAddress'); return this.request(`/pnl/${wallet}/${tokenAddress}`); } // ======== TOP TRADERS ENDPOINTS ======== /** * Get the most profitable traders across all tokens * @param page Page number (optional) * @param expandPnL Include detailed PnL data for each token * @param sortBy Sort results by metric ("total" or "winPercentage") * @returns Top traders data */ async getTopTraders(page, expandPnL, sortBy) { const params = new URLSearchParams(); if (expandPnL) params.append('expandPnL', 'true'); if (sortBy) params.append('sortBy', sortBy); const query = params.toString() ? `?${params.toString()}` : ''; const endpoint = page ? `/top-traders/all/${page}${query}` : `/top-traders/all${query}`; return this.request(endpoint); } /** * Get top 100 traders by PnL for a token * @param tokenAddress Token address * @returns Top traders for a specific token */ async getTokenTopTraders(tokenAddress) { this.validatePublicKey(tokenAddress, 'tokenAddress'); return this.request(`/top-traders/${tokenAddress}`); } // ======== ADDITIONAL ENDPOINTS ======== /** * Get detailed stats for a token over various time intervals * @param tokenAddress Token address * @returns Detailed token stats */ async getTokenStats(tokenAddress) { this.validatePublicKey(tokenAddress, 'tokenAddress'); return this.request(`/stats/${tokenAddress}`); } /** * Get detailed stats for a specific token and pool * @param tokenAddress Token address * @param poolAddress Pool address * @returns Detailed token-pool stats */ async getPoolStats(tokenAddress, poolAddress) { this.validatePublicKey(tokenAddress, 'tokenAddress'); this.validatePublicKey(poolAddress, 'poolAddress'); return this.request(`/stats/${tokenAddress}/${poolAddress}`); } } exports.Client = Client;