From 036802dbb224e717a731c5c6c64f5aa35aa4345b Mon Sep 17 00:00:00 2001 From: "Dr. Q and Company" Date: Wed, 22 Apr 2026 23:57:52 -0400 Subject: [PATCH 1/2] Update seed-canonical-heads.mjs Mangled code block now looks readable. --- scripts/seed-canonical-heads.mjs | 80 +++++++++++++++++++++++++------- 1 file changed, 64 insertions(+), 16 deletions(-) diff --git a/scripts/seed-canonical-heads.mjs b/scripts/seed-canonical-heads.mjs index 507e5e226..a0bc20492 100644 --- a/scripts/seed-canonical-heads.mjs +++ b/scripts/seed-canonical-heads.mjs @@ -4,25 +4,73 @@ import { toChecksumAddress } from "ethereumjs-util"; const LABELS_DIR = path.resolve(import.meta.dirname, "../labels"); -const PLATFORM_MAP = { - ethereum: { caip2: "eip155:1", assetNamespace: "erc20" }, - "polygon-pos": { caip2: "eip155:137", assetNamespace: "erc20" }, - "binance-smart-chain": { caip2: "eip155:56", assetNamespace: "erc20" }, - linea: { caip2: "eip155:59144", assetNamespace: "erc20" }, - base: { caip2: "eip155:8453", assetNamespace: "erc20" }, - "optimistic-ethereum": { caip2: "eip155:10", assetNamespace: "erc20" }, - "arbitrum-one": { caip2: "eip155:42161", assetNamespace: "erc20" }, - scroll: { caip2: "eip155:534352", assetNamespace: "erc20" }, - monad: { caip2: "eip155:143", assetNamespace: "erc20" }, - hyperevm: { caip2: "eip155:999", assetNamespace: "erc20" }, - avalanche: { caip2: "eip155:43114", assetNamespace: "erc20" }, - zksync: { caip2: "eip155:324", assetNamespace: "erc20" }, - solana: { +const PLATFORM_MAP = [ + { + platform: "ethereum", + caip2: "eip155:1", + assetNamespace: "erc20", + }, + { + platform: "polygon-pos", + caip2: "eip155:137", + assetNamespace: "erc20", + }, + { + platform: "binance-smart-chain", + caip2: "eip155:56", + assetNamespace: "erc20", + }, + { + platform: "linea", + caip2: "eip155:59144", + assetNamespace: "erc20", + }, + { + platform: "base", + caip2: "eip155:8453", + assetNamespace: "erc20", + }, + { + platform: "optimistic-ethereum", + caip2: "eip155:10", + assetNamespace: "erc20", + }, + { + platform: "arbitrum-one", + caip2: "eip155:42161", + assetNamespace: "erc20", + }, + { + platform: "scroll", + caip2: "eip155:534352", + assetNamespace: "erc20", + }, + { + platform: "monad", + caip2: "eip155:143", + assetNamespace: "erc20", + }, + { + platform: "hyperevm", + caip2: "eip155:999", + assetNamespace: "erc20", + }, + { + platform: "avalanche", + caip2: "eip155:43114", + assetNamespace: "erc20", + }, + { + platform: "zksync", + caip2: "eip155:324", + assetNamespace: "erc20", + }, + { + platform: "solana", caip2: "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp", assetNamespace: "token", }, -}; - +]; const CANONICAL_HEAD_GROUPS = [ { coingeckoId: "ethereum", From 2d65f8c50290bd8398daec93810b27b343a89f26 Mon Sep 17 00:00:00 2001 From: "Dr. Q and Company" Date: Thu, 23 Apr 2026 00:19:12 -0400 Subject: [PATCH 2/2] Update seed-canonical-heads.mjs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This resolves Cursor’s bug it caught while maintaining form . --- scripts/seed-canonical-heads.mjs | 76 ++++++++++++++++++-------------- 1 file changed, 42 insertions(+), 34 deletions(-) diff --git a/scripts/seed-canonical-heads.mjs b/scripts/seed-canonical-heads.mjs index a0bc20492..488751b89 100644 --- a/scripts/seed-canonical-heads.mjs +++ b/scripts/seed-canonical-heads.mjs @@ -2,8 +2,15 @@ import fs from "node:fs"; import path from "node:path"; import { toChecksumAddress } from "ethereumjs-util"; +/** Directory where label JSON files are stored */ const LABELS_DIR = path.resolve(import.meta.dirname, "../labels"); +/** + * CAIP-2 + asset namespace mapping for every platform CoinGecko returns. + * This used to be a plain object (fast string-key lookup). It is now an array + * for readability and future extensibility, so we must use .find() instead of + * direct bracket access. + */ const PLATFORM_MAP = [ { platform: "ethereum", @@ -70,7 +77,27 @@ const PLATFORM_MAP = [ caip2: "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp", assetNamespace: "token", }, -]; +] as const; + +/** + * Convert a CoinGecko platform + contract address into a full CAIP-19 identifier. + * Updated to work with the new array-based PLATFORM_MAP and added helpful warning. + */ +function platformToCaip19(platform: string, address: string): string | null { + const mapping = PLATFORM_MAP.find((entry) => entry.platform === platform); + + if (!mapping) { + console.warn(`No mapping found for platform: ${platform}`); + return null; + } + + if (mapping.assetNamespace === "erc20") { + return `${mapping.caip2}/${mapping.assetNamespace}:${toChecksumAddress(address)}`; + } + + return `${mapping.caip2}/${mapping.assetNamespace}:${address}`; +} + const CANONICAL_HEAD_GROUPS = [ { coingeckoId: "ethereum", @@ -98,7 +125,7 @@ const CANONICAL_HEAD_GROUPS = [ }, ]; -function caip19ToFilePath(caip19) { +function caip19ToFilePath(caip19: string): string { const slashIndex = caip19.lastIndexOf("/"); const chain = caip19.substring(0, slashIndex); const asset = caip19.substring(slashIndex + 1); @@ -106,7 +133,7 @@ function caip19ToFilePath(caip19) { return path.join(LABELS_DIR, chain, `${asset}.json`); } -function readOrCreateLabelFile(filePath) { +function readOrCreateLabelFile(filePath: string) { if (fs.existsSync(filePath)) { return JSON.parse(fs.readFileSync(filePath, "utf-8")); } @@ -114,7 +141,7 @@ function readOrCreateLabelFile(filePath) { return { labels: [] }; } -function writeLabelFile(filePath, data) { +function writeLabelFile(filePath: string, data: any) { const dir = path.dirname(filePath); if (!fs.existsSync(dir)) { @@ -124,19 +151,6 @@ function writeLabelFile(filePath, data) { fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n"); } -function platformToCaip19(platform, address) { - const mapping = PLATFORM_MAP[platform]; - if (!mapping) { - return null; - } - - if (mapping.assetNamespace === "erc20") { - return `${mapping.caip2}/${mapping.assetNamespace}:${toChecksumAddress(address)}`; - } - - return `${mapping.caip2}/${mapping.assetNamespace}:${address}`; -} - async function fetchCoinGeckoList() { const res = await fetch( "https://api.coingecko.com/api/v3/coins/list?include_platform=true", @@ -153,18 +167,19 @@ async function seed() { console.log("Fetching CoinGecko coin list...\n"); const coinList = await fetchCoinGeckoList(); - const coinById = new Map(coinList.map((c) => [c.id, c])); + const coinById = new Map(coinList.map((c: any) => [c.id, c])); - const summary = []; + const summary: Array<{ + member: string; + head: string; + action: "created" | "updated" | "skipped"; + }> = []; for (const group of CANONICAL_HEAD_GROUPS) { const coin = coinById.get(group.coingeckoId); if (!coin) { - console.warn( - `⚠ CoinGecko ID "${group.coingeckoId}" not found — skipping`, - ); - + console.warn(`⚠ CoinGecko ID "${group.coingeckoId}" not found — skipping`); continue; } @@ -173,13 +188,12 @@ async function seed() { for (const [platform, address] of Object.entries(coin.platforms)) { if (!address) continue; - const memberCaip19 = platformToCaip19(platform, address); + const memberCaip19 = platformToCaip19(platform, address as string); if (!memberCaip19) continue; if (memberCaip19 === group.headCaip19) { console.log(` ✓ ${memberCaip19} (head — skipping)`); - continue; } @@ -214,15 +228,9 @@ async function seed() { console.log("\n=== Summary ==="); console.log(`Total processed: ${summary.length}`); - console.log( - `Created: ${summary.filter((s) => s.action === "created").length}`, - ); - console.log( - `Updated: ${summary.filter((s) => s.action === "updated").length}`, - ); - console.log( - `Skipped: ${summary.filter((s) => s.action === "skipped").length}`, - ); + console.log(`Created: ${summary.filter((s) => s.action === "created").length}`); + console.log(`Updated: ${summary.filter((s) => s.action === "updated").length}`); + console.log(`Skipped: ${summary.filter((s) => s.action === "skipped").length}`); } seed().catch(console.error);