// Address warning module. // Provides local and async (RPC-based) warning checks for Ethereum addresses. // Returns arrays of {type, message, severity} objects. const { isScamAddress } = require("./scamlist"); const { isBurnAddress } = require("./constants"); const { checkEtherscanLabel } = require("./etherscanLabels"); const { log } = require("./log"); /** * Check an address against local-only lists (scam, burn, self-send). * Synchronous — no network calls. * * @param {string} address - The target address to check. * @param {object} [options] - Optional context. * @param {string} [options.fromAddress] - Sender address (for self-send check). * @returns {Array<{type: string, message: string, severity: string}>} */ function getLocalWarnings(address, options = {}) { const warnings = []; const addr = address.toLowerCase(); if (isScamAddress(addr)) { warnings.push({ type: "scam", message: "This address is on a known scam/fraud list. Do not send funds to this address.", severity: "critical", }); } if (isBurnAddress(addr)) { warnings.push({ type: "burn", message: "This is a known null/burn address. Funds sent here are permanently destroyed and cannot be recovered.", severity: "critical", }); } if (options.fromAddress && addr === options.fromAddress.toLowerCase()) { warnings.push({ type: "self-send", message: "You are sending to your own address.", severity: "warning", }); } return warnings; } /** * Check an address against local lists AND via RPC queries. * Async — performs network calls to check contract status and tx history. * * @param {string} address - The target address to check. * @param {object} provider - An ethers.js provider instance. * @param {object} [options] - Optional context. * @param {string} [options.fromAddress] - Sender address (for self-send check). * @returns {Promise>} */ async function getFullWarnings(address, provider, options = {}) { const warnings = getLocalWarnings(address, options); let isContract = false; try { const code = await provider.getCode(address); if (code && code !== "0x") { isContract = true; warnings.push({ type: "contract", message: "This address is a smart contract, not a regular wallet.", severity: "warning", }); } } catch (e) { log.errorf("contract check failed:", e.message); } // Skip tx count check for contracts — they may legitimately have // zero inbound EOA transactions. if (!isContract) { try { const txCount = await provider.getTransactionCount(address); if (txCount === 0) { warnings.push({ type: "new-address", message: "This address has never sent a transaction. Double-check it is correct.", severity: "info", }); } } catch (e) { log.errorf("tx count check failed:", e.message); } } // Etherscan label check (best-effort async — network failures are silent). // Runs for ALL addresses including contracts, since many dangerous // flagged addresses on Etherscan (drainers, phishing contracts) are contracts. try { const etherscanWarning = await checkEtherscanLabel(address); if (etherscanWarning) { warnings.push(etherscanWarning); } } catch (e) { log.errorf("etherscan label check failed:", e.message); } return warnings; } module.exports = { getLocalWarnings, getFullWarnings };