150 lines
5.2 KiB
JavaScript
150 lines
5.2 KiB
JavaScript
|
|
/**
|
||
|
|
* 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 from→to 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
|
||
|
|
};
|