Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@ethernauta/cli

Package Overview
Dependencies
Maintainers
1
Versions
13
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@ethernauta/cli - npm Package Compare versions

Comparing version
0.0.44
to
0.0.46
+166
dist/execute-C3gnIegB.js
import { readFileSync, readdirSync, writeFileSync } from "node:fs";
import { join, resolve } from "node:path";
import { DescriptionSchema, to_selector } from "@ethernauta/abi";
import { emit_file_basename_for, emit_name_for, generate } from "@ethernauta/abi/generator";
import { Bytes4Schema } from "@ethernauta/core";
import { bytes_to_hex } from "@ethernauta/utils";
import { array, object, parse, string } from "valibot";
//#region src/execute.ts
function selector_hex(signature) {
return parse(Bytes4Schema, bytes_to_hex(to_selector(signature)));
}
function parse_flags(args) {
let in_path;
let out_dir;
for (let i = 0; i < args.length; i++) {
const arg = args[i];
if (arg === "--in") in_path = args[++i];
else if (arg === "--out") out_dir = args[++i];
}
if (!in_path || !out_dir) throw new Error("usage: ethernauta abi --in <path> --out <dir>");
return {
in_path: resolve(process.cwd(), in_path),
out_dir: resolve(process.cwd(), out_dir)
};
}
function load_abi(path) {
const raw = JSON.parse(readFileSync(path, "utf8"));
if (Array.isArray(raw)) return raw;
if (Array.isArray(raw.abi)) return raw.abi;
throw new Error(`expected an ABI JSON array or a foundry artifact with an \`abi\` array at ${path}`);
}
function signature_key(d) {
if (d.type !== "function") return d.type;
const param_types = d.inputs.map((i) => i.type).join(",");
return `${d.name}(${param_types})`;
}
function dedupe_by_signature(descriptions) {
const seen = /* @__PURE__ */ new Set();
const out = [];
for (const d of descriptions) {
const key = signature_key(d);
if (seen.has(key)) continue;
seen.add(key);
out.push(d);
}
return out;
}
function write_barrel(out_dir, functions, generated_emit_names) {
const seen = /* @__PURE__ */ new Set();
const lines = [];
for (const f of functions) {
if (f.type !== "function") continue;
const js_name = emit_name_for(f, functions);
if (seen.has(js_name)) continue;
if (!generated_emit_names.has(js_name)) continue;
seen.add(js_name);
const basename = emit_file_basename_for(f, functions);
lines.push(`export { ${js_name} } from "./${basename}"`);
}
writeFileSync(join(out_dir, "methods", "index.ts"), `${lines.join("\n")}\n`);
}
function is_generatable(d) {
if (d.type !== "function") return false;
return true;
}
function execute_abi(args) {
const { in_path, out_dir } = parse_flags(args);
const raw = load_abi(in_path);
const generatable = dedupe_by_signature(parse(array(DescriptionSchema), raw).filter((d) => d.type === "function")).filter(is_generatable);
const result = generate(generatable, out_dir);
write_barrel(out_dir, generatable, new Set(result.generated));
console.log(`regenerated ${result.generated.length} methods into ${out_dir}/methods/`);
if (result.skipped.length > 0) {
console.warn(`skipped ${result.skipped.length} method(s):`);
for (const s of result.skipped) console.warn(` - ${s.name}: ${s.reason}`);
}
}
function parse_registry_flags(args) {
let in_dir;
let out_file;
for (let i = 0; i < args.length; i++) {
const arg = args[i];
if (arg === "--in") in_dir = args[++i];
else if (arg === "--out") out_file = args[++i];
}
if (!in_dir || !out_file) throw new Error("usage: ethernauta registry --in <dir> --out <file>");
return {
in_dir: resolve(process.cwd(), in_dir),
out_file: resolve(process.cwd(), out_file)
};
}
function walk_abi_jsons(root) {
const found = [];
const stack = [root];
while (stack.length > 0) {
const dir = stack.pop();
for (const entry of readdirSync(dir, { withFileTypes: true })) {
const full = join(dir, entry.name);
if (entry.isDirectory()) stack.push(full);
else if (entry.name.endsWith(".abi.json")) found.push(full);
}
}
return found.sort();
}
object({
signature: string(),
name: string(),
types: array(string()),
param_names: array(string()),
source: string()
});
function collect_entries(files, root) {
const out = /* @__PURE__ */ new Map();
for (const file of files) {
const raw = load_abi(file);
const descriptions = parse(array(DescriptionSchema), raw);
for (const d of descriptions) {
if (d.type !== "function") continue;
const types = d.inputs.map((i) => i.type);
const param_names = d.inputs.map((i) => i.name);
const signature = `${d.name}(${types.join(",")})`;
const selector = selector_hex(signature);
const relative = file.startsWith(`${root}/`) ? file.slice(root.length + 1) : file;
const existing = out.get(selector);
if (existing) {
if (existing.signature !== signature) throw new Error(`selector collision ${selector}: '${existing.signature}' (${existing.source}) vs '${signature}' (${relative})`);
continue;
}
out.set(selector, {
signature,
name: d.name,
types,
param_names,
source: relative
});
}
}
return out;
}
function format_registry(entries) {
return `// AUTO-GENERATED — do not edit. Run \`pnpm --filter @ethernauta/erc generate\`.
export const REGISTRY = {
${Array.from(entries.entries()).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([selector, entry]) => {
const types = entry.types.map((t) => JSON.stringify(t)).join(", ");
const names = entry.param_names.map((n) => JSON.stringify(n)).join(", ");
return ` ${JSON.stringify(selector)}: { name: ${JSON.stringify(entry.name)}, signature: ${JSON.stringify(entry.signature)}, types: [${types}], param_names: [${names}] },`;
}).join("\n")}
} as const
export type RegistrySelector = keyof typeof REGISTRY
export type RegistryEntry = (typeof REGISTRY)[RegistrySelector]
`;
}
function execute_registry(args) {
const { in_dir, out_file } = parse_registry_flags(args);
const files = walk_abi_jsons(in_dir);
const entries = collect_entries(files, in_dir);
writeFileSync(out_file, format_registry(entries));
console.log(`wrote ${entries.size} registry entries from ${files.length} ABI JSONs to ${out_file}`);
}
//#endregion
export { execute_registry as n, execute_abi as t };
//# sourceMappingURL=execute-C3gnIegB.js.map
{"version":3,"file":"execute-C3gnIegB.js","names":[],"sources":["../src/execute.ts"],"sourcesContent":["import {\n readdirSync,\n readFileSync,\n writeFileSync,\n} from \"node:fs\"\nimport { join, resolve } from \"node:path\"\nimport {\n type Description,\n DescriptionSchema,\n to_selector,\n} from \"@ethernauta/abi\"\nimport {\n emit_file_basename_for,\n emit_name_for,\n generate,\n} from \"@ethernauta/abi/generator\"\nimport { type Bytes4, Bytes4Schema } from \"@ethernauta/core\"\nimport { bytes_to_hex } from \"@ethernauta/utils\"\nimport {\n array,\n type InferOutput,\n object,\n parse,\n string,\n} from \"valibot\"\n\nfunction selector_hex(signature: string): Bytes4 {\n return parse(\n Bytes4Schema,\n bytes_to_hex(to_selector(signature)),\n )\n}\n\nfunction parse_flags(args: string[]) {\n let in_path: string | undefined\n let out_dir: string | undefined\n for (let i = 0; i < args.length; i++) {\n const arg = args[i] as string\n if (arg === \"--in\") {\n in_path = args[++i]\n } else if (arg === \"--out\") {\n out_dir = args[++i]\n }\n }\n if (!in_path || !out_dir) {\n throw new Error(\n \"usage: ethernauta abi --in <path> --out <dir>\",\n )\n }\n return {\n in_path: resolve(process.cwd(), in_path),\n out_dir: resolve(process.cwd(), out_dir),\n }\n}\n\nfunction load_abi(path: string): unknown[] {\n const raw = JSON.parse(readFileSync(path, \"utf8\"))\n if (Array.isArray(raw)) return raw\n if (Array.isArray(raw.abi)) return raw.abi\n throw new Error(\n `expected an ABI JSON array or a foundry artifact with an \\`abi\\` array at ${path}`,\n )\n}\n\nfunction signature_key(d: Description): string {\n if (d.type !== \"function\") return d.type\n const param_types = d.inputs.map((i) => i.type).join(\",\")\n return `${d.name}(${param_types})`\n}\n\nfunction _snake_or_kebab(name: string): string {\n if (name.includes(\"_\")) return name\n return name\n .replace(/([a-z0-9])([A-Z])/g, \"$1-$2\")\n .toLowerCase()\n}\n\nfunction dedupe_by_signature(\n descriptions: Description[],\n): Description[] {\n const seen = new Set<string>()\n const out: Description[] = []\n for (const d of descriptions) {\n const key = signature_key(d)\n if (seen.has(key)) continue\n seen.add(key)\n out.push(d)\n }\n return out\n}\n\nfunction write_barrel(\n out_dir: string,\n functions: Description[],\n generated_emit_names: Set<string>,\n): void {\n const seen = new Set<string>()\n const lines: string[] = []\n for (const f of functions) {\n if (f.type !== \"function\") continue\n const js_name = emit_name_for(f, functions)\n if (seen.has(js_name)) continue\n if (!generated_emit_names.has(js_name)) continue\n seen.add(js_name)\n const basename = emit_file_basename_for(f, functions)\n lines.push(`export { ${js_name} } from \"./${basename}\"`)\n }\n writeFileSync(\n join(out_dir, \"methods\", \"index.ts\"),\n `${lines.join(\"\\n\")}\\n`,\n )\n}\n\nfunction is_generatable(d: Description): boolean {\n if (d.type !== \"function\") return false\n return true\n}\n\nexport function execute_abi(args: string[]): void {\n const { in_path, out_dir } = parse_flags(args)\n const raw = load_abi(in_path)\n const descriptions = parse(array(DescriptionSchema), raw)\n const functions = dedupe_by_signature(\n descriptions.filter((d) => d.type === \"function\"),\n )\n const generatable = functions.filter(is_generatable)\n const result = generate(generatable, out_dir)\n write_barrel(\n out_dir,\n generatable,\n new Set(result.generated),\n )\n console.log(\n `regenerated ${result.generated.length} methods into ${out_dir}/methods/`,\n )\n if (result.skipped.length > 0) {\n console.warn(\n `skipped ${result.skipped.length} method(s):`,\n )\n for (const s of result.skipped) {\n console.warn(` - ${s.name}: ${s.reason}`)\n }\n }\n}\n\nfunction parse_registry_flags(args: string[]) {\n let in_dir: string | undefined\n let out_file: string | undefined\n for (let i = 0; i < args.length; i++) {\n const arg = args[i] as string\n if (arg === \"--in\") {\n in_dir = args[++i]\n } else if (arg === \"--out\") {\n out_file = args[++i]\n }\n }\n if (!in_dir || !out_file) {\n throw new Error(\n \"usage: ethernauta registry --in <dir> --out <file>\",\n )\n }\n return {\n in_dir: resolve(process.cwd(), in_dir),\n out_file: resolve(process.cwd(), out_file),\n }\n}\n\nfunction walk_abi_jsons(root: string): string[] {\n const found: string[] = []\n const stack = [root]\n while (stack.length > 0) {\n const dir = stack.pop() as string\n for (const entry of readdirSync(dir, {\n withFileTypes: true,\n })) {\n const full = join(dir, entry.name)\n if (entry.isDirectory()) {\n stack.push(full)\n } else if (entry.name.endsWith(\".abi.json\")) {\n found.push(full)\n }\n }\n }\n return found.sort()\n}\n\nconst RegistryEntrySchema = object({\n signature: string(),\n name: string(),\n types: array(string()),\n param_names: array(string()),\n source: string(),\n})\ntype RegistryEntry = InferOutput<typeof RegistryEntrySchema>\n\nfunction collect_entries(\n files: string[],\n root: string,\n): Map<Bytes4, RegistryEntry> {\n const out = new Map<Bytes4, RegistryEntry>()\n for (const file of files) {\n const raw = load_abi(file)\n const descriptions = parse(\n array(DescriptionSchema),\n raw,\n )\n for (const d of descriptions) {\n if (d.type !== \"function\") continue\n const types = d.inputs.map((i) => i.type)\n const param_names = d.inputs.map((i) => i.name)\n const signature = `${d.name}(${types.join(\",\")})`\n const selector = selector_hex(signature)\n const relative = file.startsWith(`${root}/`)\n ? file.slice(root.length + 1)\n : file\n const existing = out.get(selector)\n if (existing) {\n if (existing.signature !== signature) {\n throw new Error(\n `selector collision ${selector}: '${existing.signature}' (${existing.source}) vs '${signature}' (${relative})`,\n )\n }\n continue\n }\n out.set(selector, {\n signature,\n name: d.name,\n types,\n param_names,\n source: relative,\n })\n }\n }\n return out\n}\n\nfunction format_registry(\n entries: Map<Bytes4, RegistryEntry>,\n): string {\n const sorted = Array.from(entries.entries()).sort(\n ([a], [b]) => (a < b ? -1 : a > b ? 1 : 0),\n )\n const rows = sorted.map(([selector, entry]) => {\n const types = entry.types\n .map((t) => JSON.stringify(t))\n .join(\", \")\n const names = entry.param_names\n .map((n) => JSON.stringify(n))\n .join(\", \")\n return ` ${JSON.stringify(selector)}: { name: ${JSON.stringify(entry.name)}, signature: ${JSON.stringify(entry.signature)}, types: [${types}], param_names: [${names}] },`\n })\n return `// AUTO-GENERATED — do not edit. Run \\`pnpm --filter @ethernauta/erc generate\\`.\n\nexport const REGISTRY = {\n${rows.join(\"\\n\")}\n} as const\n\nexport type RegistrySelector = keyof typeof REGISTRY\nexport type RegistryEntry = (typeof REGISTRY)[RegistrySelector]\n`\n}\n\nexport function execute_registry(args: string[]): void {\n const { in_dir, out_file } = parse_registry_flags(args)\n const files = walk_abi_jsons(in_dir)\n const entries = collect_entries(files, in_dir)\n writeFileSync(out_file, format_registry(entries))\n console.log(\n `wrote ${entries.size} registry entries from ${files.length} ABI JSONs to ${out_file}`,\n )\n}\n"],"mappings":";;;;;;;;;AA0BA,SAAS,aAAa,WAA2B;CAC/C,OAAO,MACL,cACA,aAAa,YAAY,SAAS,CAAC,CACrC;AACF;AAEA,SAAS,YAAY,MAAgB;CACnC,IAAI;CACJ,IAAI;CACJ,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;EACpC,MAAM,MAAM,KAAK;EACjB,IAAI,QAAQ,QACV,UAAU,KAAK,EAAE;OACZ,IAAI,QAAQ,SACjB,UAAU,KAAK,EAAE;CAErB;CACA,IAAI,CAAC,WAAW,CAAC,SACf,MAAM,IAAI,MACR,+CACF;CAEF,OAAO;EACL,SAAS,QAAQ,QAAQ,IAAI,GAAG,OAAO;EACvC,SAAS,QAAQ,QAAQ,IAAI,GAAG,OAAO;CACzC;AACF;AAEA,SAAS,SAAS,MAAyB;CACzC,MAAM,MAAM,KAAK,MAAM,aAAa,MAAM,MAAM,CAAC;CACjD,IAAI,MAAM,QAAQ,GAAG,GAAG,OAAO;CAC/B,IAAI,MAAM,QAAQ,IAAI,GAAG,GAAG,OAAO,IAAI;CACvC,MAAM,IAAI,MACR,6EAA6E,MAC/E;AACF;AAEA,SAAS,cAAc,GAAwB;CAC7C,IAAI,EAAE,SAAS,YAAY,OAAO,EAAE;CACpC,MAAM,cAAc,EAAE,OAAO,KAAK,MAAM,EAAE,IAAI,EAAE,KAAK,GAAG;CACxD,OAAO,GAAG,EAAE,KAAK,GAAG,YAAY;AAClC;AASA,SAAS,oBACP,cACe;CACf,MAAM,uBAAO,IAAI,IAAY;CAC7B,MAAM,MAAqB,CAAC;CAC5B,KAAK,MAAM,KAAK,cAAc;EAC5B,MAAM,MAAM,cAAc,CAAC;EAC3B,IAAI,KAAK,IAAI,GAAG,GAAG;EACnB,KAAK,IAAI,GAAG;EACZ,IAAI,KAAK,CAAC;CACZ;CACA,OAAO;AACT;AAEA,SAAS,aACP,SACA,WACA,sBACM;CACN,MAAM,uBAAO,IAAI,IAAY;CAC7B,MAAM,QAAkB,CAAC;CACzB,KAAK,MAAM,KAAK,WAAW;EACzB,IAAI,EAAE,SAAS,YAAY;EAC3B,MAAM,UAAU,cAAc,GAAG,SAAS;EAC1C,IAAI,KAAK,IAAI,OAAO,GAAG;EACvB,IAAI,CAAC,qBAAqB,IAAI,OAAO,GAAG;EACxC,KAAK,IAAI,OAAO;EAChB,MAAM,WAAW,uBAAuB,GAAG,SAAS;EACpD,MAAM,KAAK,YAAY,QAAQ,aAAa,SAAS,EAAE;CACzD;CACA,cACE,KAAK,SAAS,WAAW,UAAU,GACnC,GAAG,MAAM,KAAK,IAAI,EAAE,GACtB;AACF;AAEA,SAAS,eAAe,GAAyB;CAC/C,IAAI,EAAE,SAAS,YAAY,OAAO;CAClC,OAAO;AACT;AAEA,SAAgB,YAAY,MAAsB;CAChD,MAAM,EAAE,SAAS,YAAY,YAAY,IAAI;CAC7C,MAAM,MAAM,SAAS,OAAO;CAK5B,MAAM,cAHY,oBADG,MAAM,MAAM,iBAAiB,GAAG,GAExC,EAAE,QAAQ,MAAM,EAAE,SAAS,UAAU,CAEtB,EAAE,OAAO,cAAc;CACnD,MAAM,SAAS,SAAS,aAAa,OAAO;CAC5C,aACE,SACA,aACA,IAAI,IAAI,OAAO,SAAS,CAC1B;CACA,QAAQ,IACN,eAAe,OAAO,UAAU,OAAO,gBAAgB,QAAQ,UACjE;CACA,IAAI,OAAO,QAAQ,SAAS,GAAG;EAC7B,QAAQ,KACN,WAAW,OAAO,QAAQ,OAAO,YACnC;EACA,KAAK,MAAM,KAAK,OAAO,SACrB,QAAQ,KAAK,OAAO,EAAE,KAAK,IAAI,EAAE,QAAQ;CAE7C;AACF;AAEA,SAAS,qBAAqB,MAAgB;CAC5C,IAAI;CACJ,IAAI;CACJ,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;EACpC,MAAM,MAAM,KAAK;EACjB,IAAI,QAAQ,QACV,SAAS,KAAK,EAAE;OACX,IAAI,QAAQ,SACjB,WAAW,KAAK,EAAE;CAEtB;CACA,IAAI,CAAC,UAAU,CAAC,UACd,MAAM,IAAI,MACR,oDACF;CAEF,OAAO;EACL,QAAQ,QAAQ,QAAQ,IAAI,GAAG,MAAM;EACrC,UAAU,QAAQ,QAAQ,IAAI,GAAG,QAAQ;CAC3C;AACF;AAEA,SAAS,eAAe,MAAwB;CAC9C,MAAM,QAAkB,CAAC;CACzB,MAAM,QAAQ,CAAC,IAAI;CACnB,OAAO,MAAM,SAAS,GAAG;EACvB,MAAM,MAAM,MAAM,IAAI;EACtB,KAAK,MAAM,SAAS,YAAY,KAAK,EACnC,eAAe,KACjB,CAAC,GAAG;GACF,MAAM,OAAO,KAAK,KAAK,MAAM,IAAI;GACjC,IAAI,MAAM,YAAY,GACpB,MAAM,KAAK,IAAI;QACV,IAAI,MAAM,KAAK,SAAS,WAAW,GACxC,MAAM,KAAK,IAAI;EAEnB;CACF;CACA,OAAO,MAAM,KAAK;AACpB;AAE4B,OAAO;CACjC,WAAW,OAAO;CAClB,MAAM,OAAO;CACb,OAAO,MAAM,OAAO,CAAC;CACrB,aAAa,MAAM,OAAO,CAAC;CAC3B,QAAQ,OAAO;AACjB,CAAC;AAGD,SAAS,gBACP,OACA,MAC4B;CAC5B,MAAM,sBAAM,IAAI,IAA2B;CAC3C,KAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,MAAM,SAAS,IAAI;EACzB,MAAM,eAAe,MACnB,MAAM,iBAAiB,GACvB,GACF;EACA,KAAK,MAAM,KAAK,cAAc;GAC5B,IAAI,EAAE,SAAS,YAAY;GAC3B,MAAM,QAAQ,EAAE,OAAO,KAAK,MAAM,EAAE,IAAI;GACxC,MAAM,cAAc,EAAE,OAAO,KAAK,MAAM,EAAE,IAAI;GAC9C,MAAM,YAAY,GAAG,EAAE,KAAK,GAAG,MAAM,KAAK,GAAG,EAAE;GAC/C,MAAM,WAAW,aAAa,SAAS;GACvC,MAAM,WAAW,KAAK,WAAW,GAAG,KAAK,EAAE,IACvC,KAAK,MAAM,KAAK,SAAS,CAAC,IAC1B;GACJ,MAAM,WAAW,IAAI,IAAI,QAAQ;GACjC,IAAI,UAAU;IACZ,IAAI,SAAS,cAAc,WACzB,MAAM,IAAI,MACR,sBAAsB,SAAS,KAAK,SAAS,UAAU,KAAK,SAAS,OAAO,QAAQ,UAAU,KAAK,SAAS,EAC9G;IAEF;GACF;GACA,IAAI,IAAI,UAAU;IAChB;IACA,MAAM,EAAE;IACR;IACA;IACA,QAAQ;GACV,CAAC;EACH;CACF;CACA,OAAO;AACT;AAEA,SAAS,gBACP,SACQ;CAaR,OAAO;;;EAZQ,MAAM,KAAK,QAAQ,QAAQ,CAAC,EAAE,MAC1C,CAAC,IAAI,CAAC,OAAQ,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,CAExB,EAAE,KAAK,CAAC,UAAU,WAAW;EAC7C,MAAM,QAAQ,MAAM,MACjB,KAAK,MAAM,KAAK,UAAU,CAAC,CAAC,EAC5B,KAAK,IAAI;EACZ,MAAM,QAAQ,MAAM,YACjB,KAAK,MAAM,KAAK,UAAU,CAAC,CAAC,EAC5B,KAAK,IAAI;EACZ,OAAO,KAAK,KAAK,UAAU,QAAQ,EAAE,YAAY,KAAK,UAAU,MAAM,IAAI,EAAE,eAAe,KAAK,UAAU,MAAM,SAAS,EAAE,YAAY,MAAM,mBAAmB,MAAM;CACxK,CAIG,EAAE,KAAK,IAAI,EAAE;;;;;;AAMlB;AAEA,SAAgB,iBAAiB,MAAsB;CACrD,MAAM,EAAE,QAAQ,aAAa,qBAAqB,IAAI;CACtD,MAAM,QAAQ,eAAe,MAAM;CACnC,MAAM,UAAU,gBAAgB,OAAO,MAAM;CAC7C,cAAc,UAAU,gBAAgB,OAAO,CAAC;CAChD,QAAQ,IACN,SAAS,QAAQ,KAAK,yBAAyB,MAAM,OAAO,gBAAgB,UAC9E;AACF"}
+1
-1
#!/usr/bin/env node
import { n as execute_registry, t as execute_abi } from "./execute-by8xsAv7.js";
import { n as execute_registry, t as execute_abi } from "./execute-C3gnIegB.js";

@@ -4,0 +4,0 @@ //#region src/bin.ts

@@ -1,3 +0,3 @@

import { n as execute_registry, t as execute_abi } from "./execute-by8xsAv7.js";
import { n as execute_registry, t as execute_abi } from "./execute-C3gnIegB.js";
export { execute_abi, execute_registry };

@@ -5,3 +5,3 @@ {

"type": "module",
"version": "0.0.44",
"version": "0.0.46",
"publishConfig": {

@@ -23,5 +23,5 @@ "access": "public"

"dependencies": {
"@ethernauta/abi": "0.0.44",
"@ethernauta/core": "0.0.44",
"@ethernauta/utils": "0.0.44"
"@ethernauta/abi": "0.0.46",
"@ethernauta/core": "0.0.46",
"@ethernauta/utils": "0.0.46"
},

@@ -42,4 +42,5 @@ "peerDependencies": {

"typecheck": "tsc --noEmit",
"test:unit": "vitest"
"test:unit": "vitest",
"test:integration": "echo no integration tests"
}
}

@@ -17,3 +17,3 @@ import {

} from "@ethernauta/abi/generator"
import { type Bytes4, bytes4Schema } from "@ethernauta/core"
import { type Bytes4, Bytes4Schema } from "@ethernauta/core"
import { bytes_to_hex } from "@ethernauta/utils"

@@ -30,3 +30,3 @@ import {

return parse(
bytes4Schema,
Bytes4Schema,
bytes_to_hex(to_selector(signature)),

@@ -189,3 +189,3 @@ )

const registryEntrySchema = object({
const RegistryEntrySchema = object({
signature: string(),

@@ -197,3 +197,3 @@ name: string(),

})
type RegistryEntry = InferOutput<typeof registryEntrySchema>
type RegistryEntry = InferOutput<typeof RegistryEntrySchema>

@@ -200,0 +200,0 @@ function collect_entries(

import { readFileSync, readdirSync, writeFileSync } from "node:fs";
import { join, resolve } from "node:path";
import { DescriptionSchema, to_selector } from "@ethernauta/abi";
import { emit_file_basename_for, emit_name_for, generate } from "@ethernauta/abi/generator";
import { bytes4Schema } from "@ethernauta/core";
import { bytes_to_hex } from "@ethernauta/utils";
import { array, object, parse, string } from "valibot";
//#region src/execute.ts
function selector_hex(signature) {
return parse(bytes4Schema, bytes_to_hex(to_selector(signature)));
}
function parse_flags(args) {
let in_path;
let out_dir;
for (let i = 0; i < args.length; i++) {
const arg = args[i];
if (arg === "--in") in_path = args[++i];
else if (arg === "--out") out_dir = args[++i];
}
if (!in_path || !out_dir) throw new Error("usage: ethernauta abi --in <path> --out <dir>");
return {
in_path: resolve(process.cwd(), in_path),
out_dir: resolve(process.cwd(), out_dir)
};
}
function load_abi(path) {
const raw = JSON.parse(readFileSync(path, "utf8"));
if (Array.isArray(raw)) return raw;
if (Array.isArray(raw.abi)) return raw.abi;
throw new Error(`expected an ABI JSON array or a foundry artifact with an \`abi\` array at ${path}`);
}
function signature_key(d) {
if (d.type !== "function") return d.type;
const param_types = d.inputs.map((i) => i.type).join(",");
return `${d.name}(${param_types})`;
}
function dedupe_by_signature(descriptions) {
const seen = /* @__PURE__ */ new Set();
const out = [];
for (const d of descriptions) {
const key = signature_key(d);
if (seen.has(key)) continue;
seen.add(key);
out.push(d);
}
return out;
}
function write_barrel(out_dir, functions, generated_emit_names) {
const seen = /* @__PURE__ */ new Set();
const lines = [];
for (const f of functions) {
if (f.type !== "function") continue;
const js_name = emit_name_for(f, functions);
if (seen.has(js_name)) continue;
if (!generated_emit_names.has(js_name)) continue;
seen.add(js_name);
const basename = emit_file_basename_for(f, functions);
lines.push(`export { ${js_name} } from "./${basename}"`);
}
writeFileSync(join(out_dir, "methods", "index.ts"), `${lines.join("\n")}\n`);
}
function is_generatable(d) {
if (d.type !== "function") return false;
return true;
}
function execute_abi(args) {
const { in_path, out_dir } = parse_flags(args);
const raw = load_abi(in_path);
const generatable = dedupe_by_signature(parse(array(DescriptionSchema), raw).filter((d) => d.type === "function")).filter(is_generatable);
const result = generate(generatable, out_dir);
write_barrel(out_dir, generatable, new Set(result.generated));
console.log(`regenerated ${result.generated.length} methods into ${out_dir}/methods/`);
if (result.skipped.length > 0) {
console.warn(`skipped ${result.skipped.length} method(s):`);
for (const s of result.skipped) console.warn(` - ${s.name}: ${s.reason}`);
}
}
function parse_registry_flags(args) {
let in_dir;
let out_file;
for (let i = 0; i < args.length; i++) {
const arg = args[i];
if (arg === "--in") in_dir = args[++i];
else if (arg === "--out") out_file = args[++i];
}
if (!in_dir || !out_file) throw new Error("usage: ethernauta registry --in <dir> --out <file>");
return {
in_dir: resolve(process.cwd(), in_dir),
out_file: resolve(process.cwd(), out_file)
};
}
function walk_abi_jsons(root) {
const found = [];
const stack = [root];
while (stack.length > 0) {
const dir = stack.pop();
for (const entry of readdirSync(dir, { withFileTypes: true })) {
const full = join(dir, entry.name);
if (entry.isDirectory()) stack.push(full);
else if (entry.name.endsWith(".abi.json")) found.push(full);
}
}
return found.sort();
}
object({
signature: string(),
name: string(),
types: array(string()),
param_names: array(string()),
source: string()
});
function collect_entries(files, root) {
const out = /* @__PURE__ */ new Map();
for (const file of files) {
const raw = load_abi(file);
const descriptions = parse(array(DescriptionSchema), raw);
for (const d of descriptions) {
if (d.type !== "function") continue;
const types = d.inputs.map((i) => i.type);
const param_names = d.inputs.map((i) => i.name);
const signature = `${d.name}(${types.join(",")})`;
const selector = selector_hex(signature);
const relative = file.startsWith(`${root}/`) ? file.slice(root.length + 1) : file;
const existing = out.get(selector);
if (existing) {
if (existing.signature !== signature) throw new Error(`selector collision ${selector}: '${existing.signature}' (${existing.source}) vs '${signature}' (${relative})`);
continue;
}
out.set(selector, {
signature,
name: d.name,
types,
param_names,
source: relative
});
}
}
return out;
}
function format_registry(entries) {
return `// AUTO-GENERATED — do not edit. Run \`pnpm --filter @ethernauta/erc generate\`.
export const REGISTRY = {
${Array.from(entries.entries()).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([selector, entry]) => {
const types = entry.types.map((t) => JSON.stringify(t)).join(", ");
const names = entry.param_names.map((n) => JSON.stringify(n)).join(", ");
return ` ${JSON.stringify(selector)}: { name: ${JSON.stringify(entry.name)}, signature: ${JSON.stringify(entry.signature)}, types: [${types}], param_names: [${names}] },`;
}).join("\n")}
} as const
export type RegistrySelector = keyof typeof REGISTRY
export type RegistryEntry = (typeof REGISTRY)[RegistrySelector]
`;
}
function execute_registry(args) {
const { in_dir, out_file } = parse_registry_flags(args);
const files = walk_abi_jsons(in_dir);
const entries = collect_entries(files, in_dir);
writeFileSync(out_file, format_registry(entries));
console.log(`wrote ${entries.size} registry entries from ${files.length} ABI JSONs to ${out_file}`);
}
//#endregion
export { execute_registry as n, execute_abi as t };
//# sourceMappingURL=execute-by8xsAv7.js.map
{"version":3,"file":"execute-by8xsAv7.js","names":[],"sources":["../src/execute.ts"],"sourcesContent":["import {\n readdirSync,\n readFileSync,\n writeFileSync,\n} from \"node:fs\"\nimport { join, resolve } from \"node:path\"\nimport {\n type Description,\n DescriptionSchema,\n to_selector,\n} from \"@ethernauta/abi\"\nimport {\n emit_file_basename_for,\n emit_name_for,\n generate,\n} from \"@ethernauta/abi/generator\"\nimport { type Bytes4, bytes4Schema } from \"@ethernauta/core\"\nimport { bytes_to_hex } from \"@ethernauta/utils\"\nimport {\n array,\n type InferOutput,\n object,\n parse,\n string,\n} from \"valibot\"\n\nfunction selector_hex(signature: string): Bytes4 {\n return parse(\n bytes4Schema,\n bytes_to_hex(to_selector(signature)),\n )\n}\n\nfunction parse_flags(args: string[]) {\n let in_path: string | undefined\n let out_dir: string | undefined\n for (let i = 0; i < args.length; i++) {\n const arg = args[i] as string\n if (arg === \"--in\") {\n in_path = args[++i]\n } else if (arg === \"--out\") {\n out_dir = args[++i]\n }\n }\n if (!in_path || !out_dir) {\n throw new Error(\n \"usage: ethernauta abi --in <path> --out <dir>\",\n )\n }\n return {\n in_path: resolve(process.cwd(), in_path),\n out_dir: resolve(process.cwd(), out_dir),\n }\n}\n\nfunction load_abi(path: string): unknown[] {\n const raw = JSON.parse(readFileSync(path, \"utf8\"))\n if (Array.isArray(raw)) return raw\n if (Array.isArray(raw.abi)) return raw.abi\n throw new Error(\n `expected an ABI JSON array or a foundry artifact with an \\`abi\\` array at ${path}`,\n )\n}\n\nfunction signature_key(d: Description): string {\n if (d.type !== \"function\") return d.type\n const param_types = d.inputs.map((i) => i.type).join(\",\")\n return `${d.name}(${param_types})`\n}\n\nfunction _snake_or_kebab(name: string): string {\n if (name.includes(\"_\")) return name\n return name\n .replace(/([a-z0-9])([A-Z])/g, \"$1-$2\")\n .toLowerCase()\n}\n\nfunction dedupe_by_signature(\n descriptions: Description[],\n): Description[] {\n const seen = new Set<string>()\n const out: Description[] = []\n for (const d of descriptions) {\n const key = signature_key(d)\n if (seen.has(key)) continue\n seen.add(key)\n out.push(d)\n }\n return out\n}\n\nfunction write_barrel(\n out_dir: string,\n functions: Description[],\n generated_emit_names: Set<string>,\n): void {\n const seen = new Set<string>()\n const lines: string[] = []\n for (const f of functions) {\n if (f.type !== \"function\") continue\n const js_name = emit_name_for(f, functions)\n if (seen.has(js_name)) continue\n if (!generated_emit_names.has(js_name)) continue\n seen.add(js_name)\n const basename = emit_file_basename_for(f, functions)\n lines.push(`export { ${js_name} } from \"./${basename}\"`)\n }\n writeFileSync(\n join(out_dir, \"methods\", \"index.ts\"),\n `${lines.join(\"\\n\")}\\n`,\n )\n}\n\nfunction is_generatable(d: Description): boolean {\n if (d.type !== \"function\") return false\n return true\n}\n\nexport function execute_abi(args: string[]): void {\n const { in_path, out_dir } = parse_flags(args)\n const raw = load_abi(in_path)\n const descriptions = parse(array(DescriptionSchema), raw)\n const functions = dedupe_by_signature(\n descriptions.filter((d) => d.type === \"function\"),\n )\n const generatable = functions.filter(is_generatable)\n const result = generate(generatable, out_dir)\n write_barrel(\n out_dir,\n generatable,\n new Set(result.generated),\n )\n console.log(\n `regenerated ${result.generated.length} methods into ${out_dir}/methods/`,\n )\n if (result.skipped.length > 0) {\n console.warn(\n `skipped ${result.skipped.length} method(s):`,\n )\n for (const s of result.skipped) {\n console.warn(` - ${s.name}: ${s.reason}`)\n }\n }\n}\n\nfunction parse_registry_flags(args: string[]) {\n let in_dir: string | undefined\n let out_file: string | undefined\n for (let i = 0; i < args.length; i++) {\n const arg = args[i] as string\n if (arg === \"--in\") {\n in_dir = args[++i]\n } else if (arg === \"--out\") {\n out_file = args[++i]\n }\n }\n if (!in_dir || !out_file) {\n throw new Error(\n \"usage: ethernauta registry --in <dir> --out <file>\",\n )\n }\n return {\n in_dir: resolve(process.cwd(), in_dir),\n out_file: resolve(process.cwd(), out_file),\n }\n}\n\nfunction walk_abi_jsons(root: string): string[] {\n const found: string[] = []\n const stack = [root]\n while (stack.length > 0) {\n const dir = stack.pop() as string\n for (const entry of readdirSync(dir, {\n withFileTypes: true,\n })) {\n const full = join(dir, entry.name)\n if (entry.isDirectory()) {\n stack.push(full)\n } else if (entry.name.endsWith(\".abi.json\")) {\n found.push(full)\n }\n }\n }\n return found.sort()\n}\n\nconst registryEntrySchema = object({\n signature: string(),\n name: string(),\n types: array(string()),\n param_names: array(string()),\n source: string(),\n})\ntype RegistryEntry = InferOutput<typeof registryEntrySchema>\n\nfunction collect_entries(\n files: string[],\n root: string,\n): Map<Bytes4, RegistryEntry> {\n const out = new Map<Bytes4, RegistryEntry>()\n for (const file of files) {\n const raw = load_abi(file)\n const descriptions = parse(\n array(DescriptionSchema),\n raw,\n )\n for (const d of descriptions) {\n if (d.type !== \"function\") continue\n const types = d.inputs.map((i) => i.type)\n const param_names = d.inputs.map((i) => i.name)\n const signature = `${d.name}(${types.join(\",\")})`\n const selector = selector_hex(signature)\n const relative = file.startsWith(`${root}/`)\n ? file.slice(root.length + 1)\n : file\n const existing = out.get(selector)\n if (existing) {\n if (existing.signature !== signature) {\n throw new Error(\n `selector collision ${selector}: '${existing.signature}' (${existing.source}) vs '${signature}' (${relative})`,\n )\n }\n continue\n }\n out.set(selector, {\n signature,\n name: d.name,\n types,\n param_names,\n source: relative,\n })\n }\n }\n return out\n}\n\nfunction format_registry(\n entries: Map<Bytes4, RegistryEntry>,\n): string {\n const sorted = Array.from(entries.entries()).sort(\n ([a], [b]) => (a < b ? -1 : a > b ? 1 : 0),\n )\n const rows = sorted.map(([selector, entry]) => {\n const types = entry.types\n .map((t) => JSON.stringify(t))\n .join(\", \")\n const names = entry.param_names\n .map((n) => JSON.stringify(n))\n .join(\", \")\n return ` ${JSON.stringify(selector)}: { name: ${JSON.stringify(entry.name)}, signature: ${JSON.stringify(entry.signature)}, types: [${types}], param_names: [${names}] },`\n })\n return `// AUTO-GENERATED — do not edit. Run \\`pnpm --filter @ethernauta/erc generate\\`.\n\nexport const REGISTRY = {\n${rows.join(\"\\n\")}\n} as const\n\nexport type RegistrySelector = keyof typeof REGISTRY\nexport type RegistryEntry = (typeof REGISTRY)[RegistrySelector]\n`\n}\n\nexport function execute_registry(args: string[]): void {\n const { in_dir, out_file } = parse_registry_flags(args)\n const files = walk_abi_jsons(in_dir)\n const entries = collect_entries(files, in_dir)\n writeFileSync(out_file, format_registry(entries))\n console.log(\n `wrote ${entries.size} registry entries from ${files.length} ABI JSONs to ${out_file}`,\n )\n}\n"],"mappings":"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"}