@shogun-sdk/money-legos
Version:
Shogun Money Legos: clients and types for quotes, memes, prices, balances, fees, validations, etc.
207 lines • 7.58 kB
JavaScript
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