#!/usr/bin/env node /** * Build per-token metadata fragments from the original Arweave metadata. * * node scripts/fetch-metadata.js [outDir=meta] [from=1] [to=100] * * For each token it downloads the original JSON, drops the image fields (they * are replaced onchain by the AVIF data URI), and writes the inner JSON * fragment — no surrounding braces — to /.json. Those are the exact * bytes storeArtwork expects as `metadata`, so each token keeps precisely the * name, description and attributes it was minted with. * * Plain node, no chain access. Re-run it freely; review the fragments before * uploading (scripts/store.js compares them byte for byte). */ import { mkdirSync, writeFileSync } from "node:fs"; import { join } from "node:path"; const BASE = process.env.ARWEAVE_BASE ?? "https://arweave.net/2ydrm9ZHH7qxYrybOI4XeY9elm2JjpwSVXjcBhaZL6k"; const STRIP = ["image", "image_url", "image_details", "animation_url"]; const [outDir = process.env.META_DIR ?? "meta", from = "1", to = "100"] = process.argv.slice(2); mkdirSync(outDir, { recursive: true }); let total = 0; for (let id = Number(from); id <= Number(to); id++) { const res = await fetch(`${BASE}/${id}`); if (!res.ok) throw new Error(`#${id}: HTTP ${res.status} from ${BASE}/${id}`); const json = await res.json(); const dropped = STRIP.filter((k) => k in json); for (const k of dropped) delete json[k]; const fragment = JSON.stringify(json).slice(1, -1); // drop the surrounding { } const path = join(outDir, `${id}.json`); writeFileSync(path, fragment); total += fragment.length; console.log( `#${id}: ${fragment.length} B -> ${path}` + (dropped.length > 0 ? ` (dropped ${dropped.join(", ")})` : " (no image field found!)"), ); } console.log( `\n${total} bytes of metadata total. Review the fragments, then store them with\n` + ` npx hardhat run scripts/store.js --network `, );