/** * Load contract addresses from config/smart-contracts-master.json. * Usable from any Node app (CommonJS). Safe to publish (no secrets in JSON). * * Usage: * const { getContractAddress, getChainContracts, loadContractsIntoProcessEnv } = require('../config/contracts-loader.cjs'); * getContractAddress(138, 'CCIP_Router') // => '0x8078A09637e47Fa5Ed34F626046Ea2094a5CDE5e' * getContractAddress(138, 'CCIPWETH9_Bridge') // by contract key * loadContractsIntoProcessEnv() // set process.env.CCIP_ROUTER etc. from envVarMap when unset * * @version 2026-02-13 */ const path = require('path'); const fs = require('fs'); const DEFAULT_JSON_PATH = path.resolve(__dirname, 'smart-contracts-master.json'); let _cache = null; function loadMasterJson(jsonPath = DEFAULT_JSON_PATH) { if (_cache && _cache.path === jsonPath) return _cache.data; const raw = fs.readFileSync(jsonPath, 'utf8'); const data = JSON.parse(raw); _cache = { path: jsonPath, data }; return data; } /** * @param {number|string} chainId - e.g. 138 or '138' * @param {string} contractKey - key from contracts object (e.g. 'CCIP_Router') or env var name (e.g. 'CCIP_ROUTER' via envVarMap) * @param {string} [jsonPath] - optional path to master JSON * @returns {string|undefined} contract address or undefined */ function getContractAddress(chainId, contractKey, jsonPath) { const data = loadMasterJson(jsonPath); const chain = String(chainId); const chainData = data.chains && data.chains[chain]; if (!chainData) return undefined; const contracts = chainData.contracts || {}; const envVarMap = chainData.envVarMap || {}; // Direct contract key if (contracts[contractKey]) return contracts[contractKey]; // Env var name -> contract key -> address const mappedKey = envVarMap[contractKey]; if (mappedKey && contracts[mappedKey]) return contracts[mappedKey]; return undefined; } /** * @param {number|string} chainId * @param {string} [jsonPath] * @returns {{ [contractKey: string]: string }} all contracts for the chain */ function getChainContracts(chainId, jsonPath) { const data = loadMasterJson(jsonPath); const chain = String(chainId); const chainData = data.chains && data.chains[chain]; if (!chainData) return {}; return { ...(chainData.contracts || {}) }; } /** * Get mapper for a chain: either on-chain AddressMapper address or "canonical" (use canonical addresses, no contract). * Ensures mapper is provided across all networks with bridges or liquidity pools. * * @param {number|string} chainId - e.g. 138 or '651940' * @param {string} [jsonPath] * @returns {string|undefined} AddressMapper contract address, "canonical", or undefined if chain not in config */ function getMapperAddress(chainId, jsonPath) { const data = loadMasterJson(jsonPath); const chain = String(chainId); const chainData = data.chains && data.chains[chain]; if (!chainData) return undefined; return chainData.mapper; } /** * Set process.env from envVarMap for the given chain(s) when not already set. * @param {number|string|(number|string)[]} [chainIds] - default [138, 1] * @param {string} [jsonPath] */ function loadContractsIntoProcessEnv(chainIds, jsonPath) { const chains = Array.isArray(chainIds) ? chainIds : (chainIds != null ? [chainIds] : ['138', '1']); const data = loadMasterJson(jsonPath); for (const chain of chains) { const chainData = data.chains && data.chains[String(chain)]; if (!chainData || !chainData.envVarMap) continue; const contracts = chainData.contracts || {}; for (const [envVar, contractKey] of Object.entries(chainData.envVarMap)) { if (process.env[envVar] != null && process.env[envVar] !== '') continue; const addr = contracts[contractKey]; if (addr) process.env[envVar] = addr; } } } module.exports = { loadMasterJson, getContractAddress, getChainContracts, getMapperAddress, loadContractsIntoProcessEnv, DEFAULT_JSON_PATH };