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 super::*;
use crate::instruction::OpCode;

#[test]
fn high_level_limits_instructions_to_entry_range() {
    // Script: PUSH1; RET; PUSH2; RET
    let script = [
        OpCode::Push1.byte(),
        OpCode::Ret.byte(),
        OpCode::Push2.byte(),
        OpCode::Ret.byte(),
    ];
    let nef_bytes = build_nef(&script);
    let manifest = ContractManifest::from_json_str(
        r#"
            {
                "name": "Multi",
                "abi": {
                    "methods": [
                        { "name": "entry", "parameters": [], "returntype": "Integer", "offset": 0 },
                        { "name": "other", "parameters": [], "returntype": "Integer", "offset": 2 }
                    ],
                    "events": []
                },
                "permissions": [],
                "trusts": "*"
            }
            "#,
    )
    .expect("manifest parsed");

    let decompilation = Decompiler::new()
        .decompile_bytes_with_manifest(&nef_bytes, Some(manifest), OutputFormat::All)
        .expect("decompile succeeds");

    let high_level = decompilation
        .high_level
        .as_deref()
        .expect("high-level output");
    assert!(
        high_level.contains("return 1;"),
        "entry body should return 1: {high_level}"
    );
    assert!(
        high_level.contains("fn other() -> int {"),
        "additional manifest methods should be emitted in high-level view"
    );
    assert!(
        high_level.contains("return 2;"),
        "additional method body should be decompiled: {high_level}"
    );
    let before_other = high_level
        .split("fn other")
        .next()
        .expect("entry section present");
    assert!(
        !before_other.contains("0002"),
        "entry section should not contain helper instructions"
    );
}

#[test]
fn high_level_trims_initslot_boundaries() {
    let Some(nef_bytes) = try_load_testing_nef("Contract_Delegate.nef") else {
        eprintln!("Skipping: Contract_Delegate.nef not found in devpack artifacts");
        return;
    };
    let Some(manifest) = try_load_testing_manifest("Contract_Delegate.manifest.json") else {
        eprintln!("Skipping: Contract_Delegate.manifest.json not found");
        return;
    };

    let decompilation = Decompiler::new()
        .decompile_bytes_with_manifest(&nef_bytes, Some(manifest), OutputFormat::All)
        .expect("decompile succeeds");

    let high_level = decompilation
        .high_level
        .as_deref()
        .expect("high-level output");
    assert!(
        high_level.contains("// 0000: INITSLOT"),
        "entry block should still be rendered"
    );
    let sum_block = high_level
        .split("\n    fn testDelegate(")
        .next()
        .expect("sumFunc section");
    assert!(
        !sum_block.contains("// 000C: INITSLOT"),
        "should stop at the first INITSLOT boundary for sumFunc"
    );
    assert!(
        !sum_block.contains("return t23;"),
        "duplicate return from appended block should not appear"
    );
    assert!(
        high_level.contains("fn sub_0x000C(arg0, arg1)"),
        "inferred private helper should be rendered as a separate method"
    );
}