UNPKG

@shogun-sdk/money-legos

Version:

Shogun Money Legos: clients and types for quotes, memes, prices, balances, fees, validations, etc.

207 lines 7.58 kB
import * as CONSTANTS from '../types/constants.js'; import { HttpApi } from './httpApi.js'; export class SymbolConversion { constructor(baseURL, rateLimiter) { Object.defineProperty(this, "assetToIndexMap", { enumerable: true, configurable: true, writable: true, value: new Map() }); Object.defineProperty(this, "exchangeToInternalNameMap", { enumerable: true, configurable: true, writable: true, value: new Map() }); Object.defineProperty(this, "assetToDecimalsMap", { enumerable: true, configurable: true, writable: true, value: new Map() }); Object.defineProperty(this, "httpApi", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "refreshIntervalMs", { enumerable: true, configurable: true, writable: true, value: 60000 }); Object.defineProperty(this, "refreshInterval", { enumerable: true, configurable: true, writable: true, value: null }); Object.defineProperty(this, "initialized", { enumerable: true, configurable: true, writable: true, value: false }); this.httpApi = new HttpApi(baseURL, CONSTANTS.ENDPOINTS.INFO, rateLimiter); } async initialize() { if (this.initialized) return; try { await this.refreshAssetMaps(); this.startPeriodicRefresh(); this.initialized = true; } catch (error) { console.error('Failed to initialize SymbolConversion:', error); throw error; } } ensureInitialized() { if (!this.initialized) { throw new Error('SymbolConversion must be initialized before use. Call initialize() first.'); } } async getInternalName(exchangeName) { this.ensureInitialized(); return this.exchangeToInternalNameMap.get(exchangeName); } startPeriodicRefresh() { if (this.refreshInterval !== null) { clearInterval(this.refreshInterval); } // Use standard setInterval that works in both Node.js and browser this.refreshInterval = setInterval(() => { this.refreshAssetMaps().catch(console.error); }, this.refreshIntervalMs); } async refreshAssetMaps() { try { const [perpMeta, spotMeta] = await Promise.all([ this.httpApi.makeRequest({ type: CONSTANTS.InfoType.PERPS_META_AND_ASSET_CTXS }), this.httpApi.makeRequest({ type: CONSTANTS.InfoType.SPOT_META_AND_ASSET_CTXS }), ]); this.assetToIndexMap.clear(); this.exchangeToInternalNameMap.clear(); this.assetToDecimalsMap.clear(); // Handle perpetual assets perpMeta[0].universe.forEach((asset, index) => { const internalName = `${asset.name}-PERP`; this.assetToIndexMap.set(internalName, index); this.exchangeToInternalNameMap.set(asset.name, internalName); this.assetToDecimalsMap.set(internalName, asset.szDecimals); }); // Handle spot assets spotMeta[0].tokens.forEach((token) => { const universeItem = spotMeta[0].universe.find((item) => item.tokens[0] === token.index); if (universeItem) { const internalName = `${token.name}-SPOT`; const exchangeName = universeItem.name; const index = universeItem.index; this.assetToIndexMap.set(internalName, 10000 + index); this.exchangeToInternalNameMap.set(exchangeName, internalName); this.assetToDecimalsMap.set(internalName, token.szDecimals); } }); } catch (error) { console.error('Failed to refresh asset maps:', error); } } async getExchangeName(internalName) { await this.ensureInitialized(); for (const [exchangeName, name] of this.exchangeToInternalNameMap.entries()) { if (name === internalName) { return exchangeName; } } return undefined; } async getAssetIndex(assetSymbol) { await this.ensureInitialized(); return this.assetToIndexMap.get(assetSymbol); } async getAllAssets() { await this.ensureInitialized(); const perp = []; const spot = []; for (const [asset, _] of this.assetToIndexMap.entries()) { if (asset.endsWith('-PERP')) { perp.push(asset); } else if (asset.endsWith('-SPOT')) { spot.push(asset); } } return { perp, spot }; } async getAssetDecimals(assetSymbol) { await this.ensureInitialized(); return this.assetToDecimalsMap.get(assetSymbol); } async convertSymbol(symbol, mode = '', symbolMode = '') { await this.ensureInitialized(); let rSymbol; if (mode === 'reverse') { for (const [key, value] of this.exchangeToInternalNameMap.entries()) { if (value === symbol) { return key; } } rSymbol = symbol; } else { rSymbol = this.exchangeToInternalNameMap.get(symbol) || symbol; } if (symbolMode === 'SPOT') { if (!rSymbol.endsWith('-SPOT')) { rSymbol = symbol + '-SPOT'; } } else if (symbolMode === 'PERP') { if (!rSymbol.endsWith('-PERP')) { rSymbol = symbol + '-PERP'; } } return rSymbol; } async convertSymbolsInObject(obj, symbolsFields = ['coin', 'symbol'], symbolMode = '') { await this.ensureInitialized(); if (typeof obj !== 'object' || obj === null) { return this.convertToNumber(obj); } if (Array.isArray(obj)) { return Promise.all(obj.map((item) => this.convertSymbolsInObject(item, symbolsFields, symbolMode))); } const convertedObj = {}; for (const [key, value] of Object.entries(obj)) { if (symbolsFields.includes(key)) { convertedObj[key] = await this.convertSymbol(value, '', symbolMode); } else if (key === 'side') { convertedObj[key] = value === 'A' ? 'sell' : value === 'B' ? 'buy' : value; } else { convertedObj[key] = await this.convertSymbolsInObject(value, symbolsFields, symbolMode); } } return convertedObj; } convertToNumber(value) { if (typeof value === 'string') { if (/^-?\d+$/.test(value)) { return parseInt(value, 10); } else if (/^-?\d*\.\d+$/.test(value)) { return parseFloat(value); } } return value; } async convertResponse(response, symbolsFields = ['coin', 'symbol'], symbolMode = '') { return this.convertSymbolsInObject(response, symbolsFields, symbolMode); } } //# sourceMappingURL=symbolConversion.js.map