* Verify every deployed contract on Etherscan, from the recorded addresses. * npx hardhat run scripts/verify.js --network sepolia * npx hardhat run scripts/verify.js --network mainnet * Reads deployments/<network>.json and verifies the storage, the extension and * the renderer with the exact constructor arguments they were deployed with — * no hand-copying addresses or remembering that the renderer's third argument * Needs ETHERSCAN_API_KEY in .env. Safe to re-run: a contract Etherscan has * already verified is reported and skipped, not treated as a failure. * STORAGE / EXTENSION / RENDERER override the recorded addresses * FORCE=1 re-verify even if already verified * Run it a minute or so after deploying — Etherscan has to have indexed the * deployment transaction before it can verify the code. import hre from "hardhat"; import { verifyContract } from "@nomicfoundation/hardhat-verify/verify"; import { connect, main, readDeployment } from "./_lib.js"; const { ethers, net, chainId } = await connect(); if (chainId === 31337n) { throw new Error("nothing to verify on a local chain — use --network sepolia or mainnet"); if (!process.env.ETHERSCAN_API_KEY) { throw new Error("set ETHERSCAN_API_KEY in .env (one V2 key covers every chain)"); const d = readDeployment(net); const pick = (k) => process.env[k.toUpperCase()] ?? d[k]; name: "ColorgasmStorage", address: pick("storage"), name: "ColorgasmRedeemExtension", address: pick("extension"), constructorArgs: [pick("creator"), d.firstId ?? 1, d.edition ?? 100], name: "ColorgasmRendererV3", address: pick("renderer"), constructorArgs: [pick("extension"), pick("storage"), d.preEncoded ?? false], const missing = targets.filter((t) => !t.address); if (missing.length > 0) { `no address for ${missing.map((t) => t.name).join(", ")} on "${net}" — ` + `deploy first, or pass ${missing.map((t) => t.name.replace("Colorgasm", "").toUpperCase()).join("/")}=0x…`, `verifying ${targets.length} contracts on ${net}\n` + ` artwork stored ${d.preEncoded ? "pre-encoded (base64)" : "raw"} — the renderer's ` + `constructor argument must match this or the bytecode will not\n`, for (const t of targets) { const code = await ethers.provider.getCode(t.address); results.push({ ...t, status: "NO CONTRACT" }); console.log(`✗ ${t.name.padEnd(26)} ${t.address} nothing deployed at this address`); constructorArgs: t.constructorArgs, force: process.env.FORCE === "1", results.push({ ...t, status: "verified" }); console.log(`✓ ${t.name.padEnd(26)} ${t.address}`); const already = /already verified|already been verified/i.test(e.message ?? ""); results.push({ ...t, status: already ? "already verified" : "FAILED" }); `${already ? "•" : "✗"} ${t.name.padEnd(26)} ${t.address} ` + (already ? "already verified" : (e.message ?? "").split("\n")[0]), // The SSTORE2 pointers that hold the artwork are not verifiable and do not // need to be: they have no source, only a STOP byte followed by raw data. // Anyone can read them with eth_getCode and compare against the files — // which is exactly what scripts/inspect.js does. `\nthe artwork chunks are SSTORE2 pointers — data, not code, so there is no\n` + `source to verify. Check them with:\n` + ` FROM_ID=1 TO_ID=100 npx hardhat run scripts/inspect.js --network ${net}`, const bad = results.filter((r) => r.status === "FAILED" || r.status === "NO CONTRACT"); console.log(`\n${bad.length} contract(s) not verified — see above`); console.log(`\nall ${results.length} contracts verified on ${net}`);