UNPKG

@moonwell-fi/moonwell-sdk

Version:

TypeScript Interface for Moonwell

303 lines 12.4 kB
/** * Lunar Indexer API Client * * Client for interacting with the Lunar Indexer REST API endpoints. * Provides functions for fetching comptroller, market, token, and portfolio data. */ import axios, {} from "axios"; import { attachRetryInterceptor } from "./retry.js"; // ============================================================================ // Error Handling // ============================================================================ export class LunarIndexerError extends Error { constructor(message, statusCode, endpoint, originalError) { super(message); Object.defineProperty(this, "statusCode", { enumerable: true, configurable: true, writable: true, value: statusCode }); Object.defineProperty(this, "endpoint", { enumerable: true, configurable: true, writable: true, value: endpoint }); Object.defineProperty(this, "originalError", { enumerable: true, configurable: true, writable: true, value: originalError }); this.name = "LunarIndexerError"; } } /** * Determine if an error should trigger fallback to Ponder/on-chain */ export function shouldFallback(error) { if (axios.isAxiosError(error)) { const axiosError = error; const isNetworkError = !axiosError.response; const is5xxError = !!axiosError.response && axiosError.response.status >= 500; const is404Error = !!axiosError.response && axiosError.response.status === 404; if (isNetworkError || is5xxError || is404Error) { return true; } // 4xx errors (except 404) should NOT fallback - fail fast return false; } // Unknown errors should fallback return true; } export const DEFAULT_LUNAR_TIMEOUT_MS = 10_000; // ============================================================================ // Lunar Indexer Client Class // ============================================================================ export class LunarIndexerClient { constructor(config) { Object.defineProperty(this, "client", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "stakingClient", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "vaultsClient", { enumerable: true, configurable: true, writable: true, value: void 0 }); this.client = axios.create({ baseURL: `${config.baseUrl}/api/v1/core`, timeout: config.timeout || DEFAULT_LUNAR_TIMEOUT_MS, headers: { "Content-Type": "application/json", }, }); this.stakingClient = axios.create({ baseURL: `${config.baseUrl}/api/v1/staking`, timeout: config.timeout || DEFAULT_LUNAR_TIMEOUT_MS, headers: { "Content-Type": "application/json", }, }); this.vaultsClient = axios.create({ baseURL: `${config.baseUrl}/api/v1/vaults`, timeout: config.timeout || DEFAULT_LUNAR_TIMEOUT_MS, headers: { "Content-Type": "application/json", }, }); // Retry transient failures (5xx, network errors, timeouts) silently before // surfacing to callers. 4xx (incl. 404) bypasses retries — see ./retry.ts. attachRetryInterceptor(this.client); attachRetryInterceptor(this.stakingClient); attachRetryInterceptor(this.vaultsClient); } /** * Get comptroller data for a specific chain */ async getComptroller(chainId) { try { const response = await this.client.get(`/comptroller/${chainId}`); return response.data; } catch (error) { throw new LunarIndexerError(`Failed to fetch comptroller for chain ${chainId}`, axios.isAxiosError(error) ? error.response?.status : undefined, `/comptroller/${chainId}`, error); } } /** * List all markets for a specific chain with pagination * Returns full market data with real-time values, APYs, and incentives */ async listMarkets(chainId, options) { try { const params = {}; if (options?.limit) params.limit = options.limit.toString(); if (options?.cursor) params.cursor = options.cursor; const response = await this.client.get(`/markets/${chainId}`, { params }); return response.data; } catch (error) { throw new LunarIndexerError(`Failed to list markets for chain ${chainId}`, axios.isAxiosError(error) ? error.response?.status : undefined, `/markets/${chainId}`, error); } } /** * Get a single market by marketId (format: chainId-marketAddress) * Returns full market data with real-time values, APYs, and incentives */ async getMarket(marketId) { try { const response = await this.client.get(`/market/${marketId}`); return response.data; } catch (error) { throw new LunarIndexerError(`Failed to fetch market ${marketId}`, axios.isAxiosError(error) ? error.response?.status : undefined, `/market/${marketId}`, error); } } /** * Get market snapshots with optional time range and granularity */ async getMarketSnapshots(marketId, options) { try { const params = {}; if (options?.limit) params.limit = options.limit.toString(); if (options?.cursor) params.cursor = options.cursor; if (options?.granularity) params.granularity = options.granularity; if (options?.startTime) params.startTime = options.startTime.toString(); if (options?.endTime) params.endTime = options.endTime.toString(); const response = await this.client.get(`/market/${marketId}/snapshots`, { params }); return response.data; } catch (error) { throw new LunarIndexerError(`Failed to fetch market snapshots for ${marketId}`, axios.isAxiosError(error) ? error.response?.status : undefined, `/market/${marketId}/snapshots`, error); } } /** * List all tokens for a specific chain with pagination */ async listTokens(chainId, options) { try { const params = {}; if (options?.limit) params.limit = options.limit.toString(); if (options?.cursor) params.cursor = options.cursor; const response = await this.client.get(`/tokens/${chainId}`, { params }); return response.data; } catch (error) { throw new LunarIndexerError(`Failed to list tokens for chain ${chainId}`, axios.isAxiosError(error) ? error.response?.status : undefined, `/tokens/${chainId}`, error); } } /** * Get a single token by tokenId (format: chainId-tokenAddress) */ async getToken(tokenId) { try { const response = await this.client.get(`/token/${tokenId}`); return response.data; } catch (error) { throw new LunarIndexerError(`Failed to fetch token ${tokenId}`, axios.isAxiosError(error) ? error.response?.status : undefined, `/token/${tokenId}`, error); } } /** * Get account portfolio with historical positions * NOTE: USD fields (supplyBalanceUsd, borrowBalanceUsd) are being added by Lunar team */ async getAccountPortfolio(accountAddress, options) { try { const params = { startTime: options.startTime.toString(), endTime: options.endTime.toString(), }; if (options.granularity) params.granularity = options.granularity; if (options.chainId) params.chainId = options.chainId.toString(); if (options.market) params.market = options.market; const response = await this.client.get(`/account/${accountAddress.toLowerCase()}/portfolio`, { params }); return response.data; } catch (error) { throw new LunarIndexerError(`Failed to fetch portfolio for account ${accountAddress}`, axios.isAxiosError(error) ? error.response?.status : undefined, `/account/${accountAddress}/portfolio`, error); } } /** * Get staking snapshots for a specific chain (stkWELL staking) */ async getStakingSnapshots(chainId, options) { try { const params = {}; if (options?.limit) params.limit = options.limit.toString(); if (options?.cursor) params.cursor = options.cursor; if (options?.granularity) params.granularity = options.granularity; if (options?.startTime) params.startTime = options.startTime.toString(); if (options?.endTime) params.endTime = options.endTime.toString(); const response = await this.stakingClient.get(`/snapshots/${chainId}`, { params }); return response.data; } catch (error) { throw new LunarIndexerError(`Failed to fetch staking snapshots for chain ${chainId}`, axios.isAxiosError(error) ? error.response?.status : undefined, `/snapshots/${chainId}`, error); } } /** * Get vault account portfolio with historical positions */ async getVaultAccountPortfolio(accountAddress, options) { try { const params = { startTime: options.startTime.toString(), endTime: options.endTime.toString(), }; if (options.granularity) params.granularity = options.granularity; if (options.chainId) params.chainId = options.chainId.toString(); if (options.vault) params.vault = options.vault; const response = await this.vaultsClient.get(`/account/${accountAddress.toLowerCase()}/portfolio`, { params }); return response.data; } catch (error) { throw new LunarIndexerError(`Failed to fetch vault portfolio for account ${accountAddress}`, axios.isAxiosError(error) ? error.response?.status : undefined, `/account/${accountAddress}/portfolio`, error); } } /** * Get vault staking snapshots for a specific chain (MetaMorpho vault staking) */ async getVaultStakingSnapshots(chainId, options) { try { const params = {}; if (options?.limit) params.limit = options.limit.toString(); if (options?.cursor) params.cursor = options.cursor; if (options?.granularity) params.granularity = options.granularity; if (options?.startTime) params.startTime = options.startTime.toString(); if (options?.endTime) params.endTime = options.endTime.toString(); if (options?.vaultAddress) params.vaultAddress = options.vaultAddress; const response = await this.stakingClient.get(`/vault-snapshots/${chainId}`, { params }); return response.data; } catch (error) { throw new LunarIndexerError(`Failed to fetch vault staking snapshots for chain ${chainId}`, axios.isAxiosError(error) ? error.response?.status : undefined, `/vault-snapshots/${chainId}`, error); } } } // ============================================================================ // Factory Function // ============================================================================ /** * Create a new Lunar Indexer client instance */ export function createLunarIndexerClient(config) { return new LunarIndexerClient(config); } //# sourceMappingURL=lunar-indexer-client.js.map