neo-decompiler 0.11.0

Neo N3 NEF decompiler: parse, disassemble, lift bytecode to high-level pseudocode and C# skeletons, with a CLI, JSON reports, and optional WebAssembly bindings.
Documentation
use serde::Serialize;

use crate::native_contracts;
use crate::nef::{call_flag_labels, describe_call_flags, MethodToken};
use crate::util;

pub(in crate::cli) fn format_method_token_line(index: usize, token: &MethodToken) -> String {
    let report = build_method_token_report(token);
    let contract_label = report
        .native_contract
        .as_ref()
        .map(|entry| format!(" ({})", entry.label))
        .unwrap_or_default();
    let warning = report
        .warning
        .as_ref()
        .map(|w| format!(" // warning: {w}"))
        .unwrap_or_default();
    format!(
        "#{index}: hash={}{} method={} params={} returns={} flags=0x{:02X} ({}){}",
        util::format_hash(&token.hash),
        contract_label,
        token.method,
        token.parameters_count,
        token.has_return_value,
        token.call_flags,
        describe_call_flags(token.call_flags),
        warning
    )
}

pub(in crate::cli) fn build_method_token_report(token: &MethodToken) -> MethodTokenReport {
    let hint = native_contracts::describe_method_token(&token.hash, &token.method);
    let warning = hint.as_ref().and_then(|h| {
        if h.has_exact_method() {
            None
        } else {
            Some(format!(
                "native contract {} does not expose method {}",
                h.contract, token.method
            ))
        }
    });
    let native_contract = hint.as_ref().map(|h| NativeContractReport {
        contract: h.contract.to_string(),
        method: h.canonical_method.map(ToString::to_string),
        label: h.formatted_label(&token.method),
    });

    MethodTokenReport {
        method: token.method.clone(),
        hash_le: util::format_hash(&token.hash),
        hash_be: util::format_hash_be(&token.hash),
        parameters: token.parameters_count,
        returns: token.has_return_value,
        call_flags: token.call_flags,
        call_flags_description: describe_call_flags(token.call_flags),
        call_flag_labels: call_flag_labels(token.call_flags),
        returns_value: token.has_return_value,
        native_contract,
        warning,
    }
}

pub(in crate::cli) fn collect_warnings(tokens: &[MethodTokenReport]) -> Vec<String> {
    tokens
        .iter()
        .filter_map(|report| report.warning.as_ref().map(ToString::to_string))
        .collect()
}

#[derive(Serialize)]
pub(in crate::cli) struct MethodTokenReport {
    method: String,
    hash_le: String,
    hash_be: String,
    parameters: u16,
    returns: bool,
    call_flags: u8,
    call_flags_description: String,
    call_flag_labels: Vec<&'static str>,
    returns_value: bool,
    native_contract: Option<NativeContractReport>,
    warning: Option<String>,
}

#[derive(Serialize)]
pub(in crate::cli) struct NativeContractReport {
    contract: String,
    method: Option<String>,
    label: String,
}