neo-decompiler 0.6.2

Minimal tooling for inspecting Neo N3 NEF bytecode
Documentation
import { ManifestParseError } from "./errors.js";

export function parseManifest(json) {
  let value;
  if (typeof json === "string") {
    try {
      value = JSON.parse(json);
    } catch (cause) {
      throw new ManifestParseError(
        `invalid manifest JSON: ${cause.message}`,
        { code: "InvalidJson" },
      );
    }
  } else {
    value = json;
  }
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
    throw new ManifestParseError(
      "manifest must be a JSON object",
      { code: "InvalidStructure" },
    );
  }
  return {
    name: value.name,
    groups: Array.isArray(value.groups) ? value.groups : [],
    supportedStandards: Array.isArray(value.supportedstandards)
      ? value.supportedstandards
      : [],
    features: {
      storage: Boolean(value.features?.storage),
      payable: Boolean(value.features?.payable),
    },
    abi: {
      methods: Array.isArray(value.abi?.methods)
        ? value.abi.methods.map((method) => ({
            name: method.name,
            parameters: Array.isArray(method.parameters)
              ? method.parameters.map((parameter) => ({
                  name: parameter.name,
                  kind: parameter.type ?? parameter.kind ?? "Any",
                }))
              : [],
            returnType: method.returntype ?? "Void",
            offset:
              typeof method.offset === "number" && method.offset >= 0
                ? method.offset
                : null,
            safe: Boolean(method.safe),
          }))
        : [],
      events: Array.isArray(value.abi?.events)
        ? value.abi.events.map((event) => ({
            name: event.name,
            parameters: Array.isArray(event.parameters)
              ? event.parameters.map((parameter) => ({
                  name: parameter.name,
                  kind: parameter.type ?? parameter.kind ?? "Any",
                }))
              : [],
          }))
        : [],
    },
    permissions: Array.isArray(value.permissions) ? value.permissions : [],
    trusts:
      value.trusts === undefined
        ? null
        : Array.isArray(value.trusts)
          ? value.trusts
          : value.trusts,
    extra: value.extra ?? null,
  };
}

export function sanitizeIdentifier(input) {
  let ident = "";
  for (const character of input) {
    if (/[A-Za-z0-9]/u.test(character)) {
      ident += character;
    } else if (
      (character === "_" || /\s|-/u.test(character)) &&
      !ident.endsWith("_")
    ) {
      ident += "_";
    }
  }

  ident = ident.replace(/_+$/u, "");
  if (ident.length === 0) {
    ident = "param";
  }
  if (/^[0-9]/u.test(ident)) {
    ident = `_${ident}`;
  }
  return ident;
}

export function makeUniqueIdentifier(base, used) {
  if (!used.has(base)) {
    used.add(base);
    return base;
  }

  let index = 1;
  while (used.has(`${base}_${index}`)) {
    index += 1;
  }
  const candidate = `${base}_${index}`;
  used.add(candidate);
  return candidate;
}

export function sanitizeParameterNames(parameters) {
  const used = new Set();
  return parameters.map((parameter) =>
    makeUniqueIdentifier(sanitizeIdentifier(parameter.name), used),
  );
}

export function formatManifestType(kind) {
  const normalized = String(kind).toLowerCase();
  switch (normalized) {
    case "void":
      return "void";
    case "boolean":
      return "bool";
    case "integer":
      return "int";
    case "string":
      return "string";
    case "hash160":
      return "hash160";
    case "hash256":
      return "hash256";
    case "bytearray":
      return "bytes";
    case "signature":
      return "signature";
    case "array":
      return "array";
    case "map":
      return "map";
    case "interopinterface":
      return "interop";
    case "any":
      return "any";
    default:
      return String(kind);
  }
}

export function formatManifestParameters(parameters) {
  const names = sanitizeParameterNames(parameters);
  return parameters
    .map((parameter, index) => `${names[index]}: ${formatManifestType(parameter.kind)}`)
    .join(", ");
}