NEF File Structure and Smart Contract Serialization in Neo N3

The NEF (Neo Executable Format) is a binary container that stores Neo N3 smart contracts using a fixed header layout, variable-length metadata fields, and a double SHA-256 checksum, implementing the ISerializable interface for deterministic encoding and decoding.

Smart contracts on the Neo N3 blockchain are distributed and executed in a compact binary format known as the NEF (Neo Executable Format). Understanding the NEF file structure is essential for developers compiling, deploying, or auditing smart contracts, as it defines exactly how contract metadata, bytecode, and static call references are serialized for on-chain storage. This article examines the NEF3 specification as implemented in the neo-project/neo repository, detailing the binary layout, serialization mechanics, and verification processes.

What Is the NEF (Neo Executable Format)?

The NEF file serves as the standard binary container for Neo N3 smart contracts. It encapsulates everything the Neo Virtual Machine requires to load and execute a contract: the compiled AVM bytecode, compiler metadata, source references, and static call descriptors. According to the Neo source code, the format follows the NEF3 specification, identified by a fixed magic number that distinguishes it from earlier iterations.

NEF File Structure and Binary Layout

The exact binary layout is defined in src/Neo/SmartContract/NefFile.cs. The file consists of a fixed header followed by variable-length fields, serialized sequentially using little-endian encoding.

Magic Header and Compiler Metadata

The file begins with a 4-byte Magic field set to 0x3346454E (the ASCII string "NEF3" in little-endian). This constant identifies the file format version.

Immediately following is the Compiler field: a fixed 64-byte array containing the compiler name and version string, padded with null bytes. The source code uses ReadFixedString(64) to extract this value during deserialization.

Source URL and Reserved Fields

The Source field stores the URL of the contract's source code as a variable-length UTF-8 string, prefixed with a VarInt length indicator. The implementation limits this to 256 bytes using ReadVarString(256).

A single Reserve byte follows, which must be 0 and is reserved for future extensions. After the method tokens array, an additional 2-byte Reserve field (also zero-filled) provides further padding for alignment.

Method Tokens and Script

The Tokens field is an array of MethodToken structures representing static call descriptors. It is serialized as a VarInt count followed by each token's binary representation. The deserializer enforces a maximum of 128 tokens using ReadSerializableArray<MethodToken>(128).

The Script field contains the compiled AVM bytecode as a variable-length byte array. Its size is constrained by ExecutionEngineLimits.Default.MaxItemSize to prevent resource exhaustion attacks.

Checksum

The final 4 bytes contain the Checksum, calculated as the first four bytes of a double SHA-256 hash of all preceding data. This ensures file integrity during distribution and deployment.

Serialization Mechanics and the ISerializable Interface

All NEF components implement the ISerializable interface defined in src/Neo/IO/ISerializable.cs. This contract requires three members: Size (computed binary size), Serialize(BinaryWriter writer), and Deserialize(ref MemoryReader reader).

The serialization protocol follows the specification in docs/serialization-format.md. Key rules include:

  • Primitive types (uint32, ushort, byte) are written in little-endian order.
  • Variable-length integers (VarInt) encode size prefixes efficiently for compactness.
  • Strings use UTF-8 encoding with a VarInt length prefix (WriteVarString).
  • Byte arrays use a VarInt length prefix (WriteVarBytes).
  • Arrays are serialized as a VarInt count followed by each element's serialization.
  • Nullable items include a preceding boolean flag.

The MemoryReader and BinaryWriter abstractions ensure deterministic, cross-platform binary compatibility between different node implementations.

MethodToken Structure for Static Calls

Static contract invocations are encoded using the MethodToken structure defined in src/Neo/SmartContract/MethodToken.cs. Each token represents a callable method in an external contract and contains five fields:

  1. Hash (UInt160): The target contract's script hash.
  2. Method (string, max 32 bytes): The method name, which must not start with an underscore.
  3. ParametersCount (ushort): The number of parameters the method accepts.
  4. HasReturnValue (bool): Indicates whether the method returns a value.
  5. CallFlags (byte): Execution flags that must be a subset of CallFlags.All.

During deserialization, the NefFile validates that the method name does not begin with an underscore and that the CallFlags value is valid. The array of tokens is limited to 128 entries to prevent excessive static linking.

Deserialization and Verification Flow

When loading a NEF file, the static NefFile.Parse method executes a strict validation pipeline defined in src/Neo/SmartContract/NefFile.cs.

Step-by-Step Deserialization

  1. Magic validation: Reads the first 4 bytes and verifies they equal 0x3346454E ("NEF3").
  2. Compiler extraction: Reads 64 bytes as a fixed string.
  3. Source URL: Reads a variable string with a 256-byte limit.
  4. Reserved byte: Consumes 1 byte and asserts it equals 0.
  5. Method tokens: Deserializes an array of MethodToken objects, capped at 128 items.
  6. Reserved padding: Consumes 2 bytes and asserts they equal 0.
  7. Script loading: Reads the AVM bytecode using ReadVarMemory, constrained by ExecutionEngineLimits.Default.MaxItemSize.
  8. Checksum extraction: Reads the final 4 bytes as the stored checksum.
  9. Optional verification: Validates the checksum against ComputeChecksum and enforces VM item size limits.

If any verification fails, the parser throws a FormatException or ArgumentException, ensuring node-level integrity.

Checksum Calculation

The checksum is calculated by taking a double SHA-256 hash of the entire NEF file except the last four bytes, then extracting the first four bytes as a little-endian uint32 using BinaryPrimitives.ReadUInt32LittleEndian. During deserialization in NefFile.Parse, this computed value is compared against the stored CheckSum field; a mismatch throws a FormatException.

From Source Code to NEF: Smart Contract Serialization

The transformation of a smart contract from high-level source to NEF binary involves three stages:

  1. Compilation: The neo-devpack-dotnet compiler translates C# source code into AVM (Application Virtual Machine) bytecode, producing the Script field.

  2. Metadata collection: The compiler populates the Compiler string (name and version), optional Source URL, and analyzes static call sites to generate the MethodToken array.

  3. Binary packaging: A NefFile instance is constructed with these fields, and Serialize writes the binary stream to disk. The resulting .nef file is what developers deploy to the Neo blockchain using ContractManagement syscalls.

The ToJson method provides a human-readable JSON representation of all NEF fields, useful for debugging and API responses.

Working with NEF Files in C#

The Neo core library provides robust APIs for parsing and inspecting NEF files. Below is a complete example demonstrating how to load a NEF file, validate its checksum, and inspect its contents.

using Neo.SmartContract;
using Neo.IO;
using System;
using System.IO;
using Newtonsoft.Json.Linq;

// Load raw bytes from disk
byte[] raw = File.ReadAllBytes("MyContract.nef");

// Parse and validate the NEF file
NefFile nef = NefFile.Parse(raw);

// Display basic metadata
Console.WriteLine($"Compiler : {nef.Compiler}");
Console.WriteLine($"Source   : {nef.Source}");
Console.WriteLine($"Script size : {nef.Script.Length} bytes");
Console.WriteLine($"Checksum : 0x{nef.CheckSum:X8}");

// Inspect static call tokens
foreach (var token in nef.Tokens)
{
    Console.WriteLine($" → Call {token.Hash} .{token.Method} " +
                      $"({token.ParametersCount} args, " +
                      $"return={(token.HasReturnValue)}) Flags={token.CallFlags}");
}

// Verify checksum integrity manually
if (nef.CheckSum != NefFile.ComputeChecksum(nef))
    throw new InvalidOperationException("Invalid NEF checksum");

// Convert to JSON for debugging / API responses
JObject json = nef.ToJson();
Console.WriteLine(json.ToString());

This example utilizes the NefFile.Parse method from src/Neo/SmartContract/NefFile.cs to handle the complex deserialization pipeline, including magic number validation, reserved field checks, and checksum verification.

Summary

  • The NEF (Neo Executable Format) is the standard binary container for Neo N3 smart contracts, identified by the magic number 0x3346454E ("NEF3").
  • The file structure consists of a 64-byte compiler string, variable-length source URL (max 256 bytes), reserved bytes, an array of up to 128 MethodTokens, the AVM script, and a 4-byte double SHA-256 checksum.
  • All components implement ISerializable, using little-endian encoding, VarInt prefixes, and strict size limits enforced during NefFile.Parse.
  • MethodToken entries encode static cross-contract calls with target hash, method name, parameter count, and call flags.
  • Checksum verification uses the first four bytes of a double SHA-256 hash of the entire file except the last four bytes, ensuring integrity before deployment.

Frequently Asked Questions

What does NEF stand for in Neo N3?

NEF stands for Neo Executable Format. It is the binary file format (version NEF3) that stores the compiled AVM bytecode, compiler metadata, source references, and static call descriptors required to deploy and execute smart contracts on the Neo N3 blockchain.

How is the NEF checksum calculated and verified?

The checksum is calculated by taking a double SHA-256 hash of the entire NEF file except the last four bytes, then extracting the first four bytes as a little-endian uint32. During deserialization in NefFile.Parse, this computed value is compared against the stored CheckSum field; a mismatch throws a FormatException.

What are MethodTokens in a NEF file?

MethodTokens are static call descriptors that allow a contract to invoke other contracts without dynamic resolution. Each token contains the target contract's UInt160 hash, method name (max 32 bytes, cannot start with '_'), parameter count, return value flag, and call flags. The NEF format limits contracts to 128 MethodTokens.

What limits are enforced during NEF deserialization?

The NefFile.Parse method enforces several limits to prevent resource exhaustion: the source URL is capped at 256 bytes, the MethodToken array is limited to 128 entries, and the script size is constrained by ExecutionEngineLimits.Default.MaxItemSize. Additionally, reserved bytes must be zero, and the magic number must exactly match 0x3346454E.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →