import fs from 'node:fs'
import os from 'node:os'
import path from 'node:path'
const BASE = 'https://raw.githubusercontent.com/0xsequence/token-directory/main'
function cacheDir() {
return path.join(os.homedir(), '.cache', 'sequence-token-directory')
}
async function fetchJson(url: string) {
const res = await fetch(url)
if (!res.ok) throw new Error(`fetch failed: ${res.status} ${url}`)
return res.json()
}
export async function resolveErc20BySymbol(chainId: number, symbol: string) {
const indexUrl = `${BASE}/index/index.json`
const index = await fetchJson(indexUrl)
// Find chain folder by chainId
let chainName: string | null = null
let sha256: string | null = null
for (const [name, meta] of Object.entries<any>(index.index)) {
if (name === '_external') continue
if (String(meta?.chainId) !== String(chainId)) continue
chainName = name
sha256 = meta?.tokenLists?.['erc20.json'] || null
break
}
if (!chainName || !sha256) throw new Error(`No ERC20 list for chainId=${chainId}`)
// Cache per-chain list by hash
const fp = path.join(cacheDir(), `${chainId}.erc20.${String(sha256).slice(0, 12)}.json`)
if (fs.existsSync(fp)) {
const cached = JSON.parse(fs.readFileSync(fp, 'utf8'))
return cached.tokens.find((t: any) => String(t.symbol).toUpperCase() === symbol.toUpperCase()) || null
}
const listUrl = `${BASE}/index/${chainName}/erc20.json`
const list = await fetchJson(listUrl)
fs.mkdirSync(path.dirname(fp), { recursive: true })
fs.writeFileSync(fp, JSON.stringify(list), 'utf8')
return list.tokens.find((t: any) => String(t.symbol).toUpperCase() === symbol.toUpperCase()) || null
}