#!/usr/bin/env node /** * Address Checksum Validator and Fixer * Validates and optionally fixes EIP-55 checksummed addresses in token lists */ import { readFileSync, writeFileSync } from 'fs'; import { fileURLToPath } from 'url'; import { dirname, resolve } from 'path'; import { ethers } from 'ethers'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); function isChecksummed(address) { try { return ethers.isAddress(address) && address === ethers.getAddress(address); } catch { return false; } } function checksumAddress(address) { try { // Convert to lowercase first if it's not valid, then checksum return ethers.getAddress(address.toLowerCase()); } catch { return null; } } function validateAndFixAddresses(filePath, dryRun = true) { console.log(`\nšŸ” ${dryRun ? 'Validating' : 'Fixing'} addresses in: ${filePath}\n`); // Read token list file let tokenList; try { const fileContent = readFileSync(filePath, 'utf-8'); tokenList = JSON.parse(fileContent); } catch (error) { console.error('āŒ Error reading or parsing token list file:'); console.error(` ${error.message}`); process.exit(1); } const issues = []; const fixed = []; // Check all addresses if (tokenList.tokens && Array.isArray(tokenList.tokens)) { tokenList.tokens.forEach((token, index) => { if (token.address) { if (!isChecksummed(token.address)) { const checksummed = checksumAddress(token.address); if (checksummed) { issues.push({ index, token: token.symbol || token.name, original: token.address, checksummed, type: 'non-checksummed' }); if (!dryRun) { token.address = checksummed; fixed.push({ index, token: token.symbol || token.name, original: token.address, fixed: checksummed }); } } else { issues.push({ index, token: token.symbol || token.name, original: token.address, checksummed: null, type: 'invalid' }); } } } }); } // Report results if (issues.length === 0) { console.log('āœ… All addresses are properly checksummed!\n'); return 0; } console.log(`Found ${issues.length} address issue(s):\n`); issues.forEach(issue => { if (issue.type === 'invalid') { console.error(`āŒ Token[${issue.index}] (${issue.token}): Invalid address format: ${issue.original}`); } else { console.log(`āš ļø Token[${issue.index}] (${issue.token}):`); console.log(` Original: ${issue.original}`); console.log(` Checksummed: ${issue.checksummed}`); } }); if (!dryRun && fixed.length > 0) { console.log(`\nāœļø Fixed ${fixed.length} address(es)\n`); // Write back to file writeFileSync(filePath, JSON.stringify(tokenList, null, 2) + '\n', 'utf-8'); console.log(`āœ… Updated file: ${filePath}\n`); } else if (dryRun && issues.some(i => i.type !== 'invalid')) { console.log(`\nšŸ’” Run with --fix to automatically fix checksummed addresses\n`); } return issues.some(i => i.type === 'invalid') ? 1 : 0; } // Main const args = process.argv.slice(2); const filePath = args.find(arg => !arg.startsWith('--')) || resolve(__dirname, '../lists/dbis-138.tokenlist.json'); const dryRun = !args.includes('--fix'); if (!filePath) { console.error('Usage: node checksum-addresses.js [path/to/token-list.json] [--fix]'); process.exit(1); } const exitCode = validateAndFixAddresses(filePath, dryRun); process.exit(exitCode);