Files
proxmox/config/token-mapping-loader.cjs

150 lines
5.2 KiB
JavaScript
Raw Permalink Normal View History

/**
* Load token mapping from config/token-mapping.json and config/token-mapping-multichain.json.
* Used by relay service, bridge/LP tooling, and docs. Safe to publish (no secrets).
*
* Usage:
* const { getRelayTokenMapping, getTokenMappingForPair } = require('../config/token-mapping-loader.cjs');
* const map = getRelayTokenMapping(); // 138 -> Mainnet (chain138Address -> mainnetAddress)
* const pair = getTokenMappingForPair(138, 651940); // { tokens, addressMapFromTo, addressMapToFrom }
*
* @version 2026-02-16
*/
const path = require('path');
const fs = require('fs');
const DEFAULT_JSON_PATH = path.resolve(__dirname, 'token-mapping.json');
const DEFAULT_MULTICHAIN_JSON_PATH = path.resolve(__dirname, 'token-mapping-multichain.json');
let _cache = null;
let _multichainCache = null;
function loadTokenMappingJson(jsonPath = DEFAULT_JSON_PATH) {
if (_cache && _cache.path === jsonPath) return _cache.data;
try {
const raw = fs.readFileSync(jsonPath, 'utf8');
const data = JSON.parse(raw);
_cache = { path: jsonPath, data };
return data;
} catch (e) {
return null;
}
}
function loadTokenMappingMultichainJson(jsonPath = DEFAULT_MULTICHAIN_JSON_PATH) {
if (_multichainCache && _multichainCache.path === jsonPath) return _multichainCache.data;
try {
const raw = fs.readFileSync(jsonPath, 'utf8');
const data = JSON.parse(raw);
_multichainCache = { path: jsonPath, data };
return data;
} catch (e) {
return null;
}
}
/**
* Build object suitable for relay config.tokenMapping: Chain 138 address -> Mainnet address.
* Only includes tokens that have a mainnetAddress (canonical or wrapped).
*
* @param {string} [jsonPath]
* @returns {{ [chain138Address: string]: string }}
*/
function getRelayTokenMapping(jsonPath) {
const data = loadTokenMappingJson(jsonPath);
if (!data || !Array.isArray(data.tokens)) return {};
const out = {};
for (const t of data.tokens) {
if (t.chain138Address && t.mainnetAddress) {
out[t.chain138Address] = t.mainnetAddress;
}
}
return out;
}
/**
* Get full token list with relaySupported and mainnet info.
*
* @param {string} [jsonPath]
* @returns {Array<{ key: string, name: string, chain138Address: string, mainnetAddress: string|null, relaySupported: boolean, notes: string }>}
*/
function getTokenList(jsonPath) {
const data = loadTokenMappingJson(jsonPath);
if (!data || !Array.isArray(data.tokens)) return [];
return data.tokens;
}
/**
* Get token mapping for a chain pair from token-mapping-multichain.json.
* Tries (fromChainId, toChainId) then (toChainId, fromChainId) and returns tokens in fromto order.
*
* @param {number|string} fromChainId
* @param {number|string} toChainId
* @param {string} [jsonPath]
* @returns {{ tokens: Array<{ key: string, name: string, addressFrom: string, addressTo: string, notes?: string }>, addressMapFromTo: Record<string, string>, addressMapToFrom: Record<string, string> } | null}
*/
function getTokenMappingForPair(fromChainId, toChainId, jsonPath) {
const data = loadTokenMappingMultichainJson(jsonPath);
if (!data || !Array.isArray(data.pairs)) return null;
const from = Number(fromChainId);
const to = Number(toChainId);
let pair = data.pairs.find((p) => p.fromChainId === from && p.toChainId === to);
let reverse = false;
if (!pair) {
pair = data.pairs.find((p) => p.fromChainId === to && p.toChainId === from);
reverse = true;
}
if (!pair || !Array.isArray(pair.tokens)) return null;
const tokens = reverse
? pair.tokens.map((t) => ({ key: t.key, name: t.name, addressFrom: t.addressTo, addressTo: t.addressFrom, notes: t.notes }))
: pair.tokens;
const addressMapFromTo = {};
const addressMapToFrom = {};
for (const t of tokens) {
if (t.addressFrom && t.addressTo) {
addressMapFromTo[t.addressFrom.toLowerCase()] = t.addressTo;
addressMapToFrom[t.addressTo.toLowerCase()] = t.addressFrom;
}
}
return { tokens, addressMapFromTo, addressMapToFrom };
}
/**
* Get all chain pairs defined in token-mapping-multichain.json.
*
* @param {string} [jsonPath]
* @returns {Array<{ fromChainId: number, toChainId: number, notes?: string }>}
*/
function getAllMultichainPairs(jsonPath) {
const data = loadTokenMappingMultichainJson(jsonPath);
if (!data || !Array.isArray(data.pairs)) return [];
return data.pairs.map((p) => ({ fromChainId: p.fromChainId, toChainId: p.toChainId, notes: p.notes }));
}
/**
* Resolve token address on target chain from source chain address using multichain mapping.
*
* @param {number|string} fromChainId
* @param {number|string} toChainId
* @param {string} tokenAddressOnSource - address on fromChainId
* @param {string} [jsonPath]
* @returns {string|undefined} address on toChainId, or undefined if not mapped
*/
function getMappedAddress(fromChainId, toChainId, tokenAddressOnSource, jsonPath) {
const result = getTokenMappingForPair(fromChainId, toChainId, jsonPath);
if (!result) return undefined;
return result.addressMapFromTo[String(tokenAddressOnSource).toLowerCase()];
}
module.exports = {
loadTokenMappingJson,
loadTokenMappingMultichainJson,
getRelayTokenMapping,
getTokenList,
getTokenMappingForPair,
getAllMultichainPairs,
getMappedAddress,
DEFAULT_JSON_PATH,
DEFAULT_MULTICHAIN_JSON_PATH
};