Skip to content

Parity and Limitations

This page documents the current state of parity between the Neo Solidity compiler's embedded runtime and the production Neo N3 virtual machine, known limitations, and planned improvements.

What "Parity" Means

Parity refers to how closely the embedded NeoVM runtime in neo-solc matches the behavior of the official Neo N3 reference implementation. Full parity means identical behavior for all opcodes, syscalls, gas accounting, and edge cases.

The embedded runtime is designed for local testing and development. It prioritizes correctness of core functionality over exhaustive edge-case coverage. For production validation, always test on Neo-Express or testnet.

Current Status Summary

As of January 2026, the embedded runtime passes 320+ tests and is considered production-ready for core functionality.

CategoryStatusAccuracy
Opcode executionCompleteFull Neo N3 opcode suite
Stack operationsCompleteAll stack/slot operations
Arithmetic and logicCompleteIncluding MODMUL, MODPOW, SQRT
Control flowCompleteAll jump variants, CALL/CALLA/CALLT
Exception handlingCompleteTRY/CATCH/FINALLY with rethrow
Storage syscallsCompleteGet/Put/Delete/Find with iterators
Runtime syscallsCompletePlatform, network, time, gas, notifications
Crypto syscallsCompleteSHA256, RIPEMD160, Keccak256, Murmur32, CheckSig
Gas accounting~85% accuratePer-opcode and per-syscall tables
Iterator handlingFunctionalMaterialized (not streaming)
Native contractsPartialCore methods implemented, stubs for others
Blockchain accessorsPartialPlaceholder/stub data

Known Gaps by Priority

P1 -- High Priority (Correctness)

Exception Handling Stack Unwinding

Stack unwinding during exception propagation and the associated gas effects need verification against the Neo N3 specification. The current implementation handles the common cases correctly, but edge cases involving nested try/catch/finally blocks with complex stack states may diverge.

Impact: Contracts with deeply nested exception handling may behave differently in production.

Workaround: Test exception-heavy code paths on Neo-Express.

Gas Precision for Complex Operations

Gas accounting is approximately 85% accurate. The main gaps are:

  • Dynamic costs for large integer operations (the runtime uses fixed costs instead of size-dependent costs)
  • Complex compound operations where gas depends on collection size
  • Some syscall costs are approximations of the Neo N3 fee schedule

Impact: Gas estimates from the embedded runtime may undercount or overcount by up to 15% for complex operations.

Workaround: Use Neo-Express for accurate gas measurement before mainnet deployment. Budget a safety margin of 20% above embedded runtime estimates.

P2 -- Medium Priority (Performance and Spec Compliance)

Iterator Streaming

Storage.Find currently materializes all matching entries into memory before returning the iterator. The production Neo N3 implementation uses lazy streaming, loading entries on demand.

Impact: Functional correctness is not affected. Memory usage may be higher than production for large result sets. Performance characteristics differ for contracts that iterate over many storage entries.

Workaround: None needed for correctness. Be aware that memory usage in tests may not reflect production behavior for large datasets.

ByteString vs Buffer Type Distinction

The embedded runtime treats both ByteString and Buffer as generic byte arrays. In the Neo N3 specification, these are distinct types with different mutation semantics:

  • ByteString is immutable (copy-on-write)
  • Buffer is mutable (in-place modification)

Impact: Code that relies on the distinction between mutable and immutable byte sequences may behave differently. Most Solidity-compiled contracts do not depend on this distinction.

Workaround: Avoid relying on ByteString immutability guarantees in tests. Verify byte mutation behavior on Neo-Express if your contract uses low-level byte operations.

P3 -- Low Priority (Nice to Have)

Blockchain Accessors

GetTransaction, GetBlock, and GetContract return placeholder/stub data rather than real blockchain state. The embedded runtime does not maintain a simulated blockchain.

Impact: Contracts that read blockchain metadata (block height, transaction data, other contract state) will receive fixed test values.

Workaround: Use Neo-Express for integration tests that depend on blockchain state.

Additional Hash Functions

The runtime implements SHA256, RIPEMD160, Keccak256, and Murmur32. Additional hash functions may be needed if the Neo N3 CryptoLib native contract surface expands.

Impact: None for current contracts. Future Neo N3 protocol updates may add hash functions not yet supported.

Native Contract Method Surface

The full method surface of Policy, ContractManagement, and Ledger native contracts is not completely implemented. Core methods (Deploy, Update, GetContract, fee queries) work; less common methods return stub values.

Impact: Contracts calling uncommon native contract methods may receive incorrect results in tests.

Workaround: Test native contract interactions on Neo-Express.

Completed Items

The following items were previously tracked as gaps and have been resolved:

  • CheckSig / CheckMultisig -- Real secp256k1 verification with DER and compact signature support
  • Storage syscalls -- Complete Get/Put/Delete/Find with iterator token disposal
  • Runtime syscalls -- Platform, network, time, gas, notifications, checkWitness
  • Crypto syscalls -- SHA256, RIPEMD160, Keccak256, Murmur32, Hash160, Hash256
  • All opcodes -- Full Neo N3 opcode suite with proper stack effects
  • Gas accounting -- Per-opcode and per-syscall tables with ~85% spec accuracy

Compiler-Level Limitations

These are not runtime parity issues but fundamental limitations of the Solidity-to-NeoVM compilation target.

Blocked EVM Features

The following Solidity/EVM features are not supported and will produce E3001 UnsupportedFeature errors:

FeatureReason
delegatecallNo equivalent in NeoVM execution model
staticcallNeoVM uses call flags instead
Inline assembly (assembly { ... })EVM-specific opcodes
selfdestructNo equivalent in Neo N3
extcodesize / extcodecopyNo bytecode introspection in NeoVM
CREATE2Neo uses deterministic contract hashing
block.difficulty / block.prevrandaoNot available in Neo N3
tx.gaspriceNeo uses a different fee model

Partial Features with Neo-Specific Semantics

FeatureNeo Behavior
msg.valueNot directly applicable; Neo uses NEP-17 token transfers
Function overloadingSupported with constraints on ABI name uniqueness
Dynamic arrays in storageMust use mappings or fixed-size arrays
receive() / fallback()Mapped to Neo contract entry points

Dynamic Call Sites and Wildcard Permissions

Contracts that use dynamic contract addresses (computed at runtime rather than hardcoded) require wildcard permissions in the manifest. This is a security concern because wildcard permissions grant the contract the ability to call any other contract.

Use the --deny-wildcard-* flags and --manifest-permissions to enforce explicit permission policies. See CLI Reference for details.

Intentionally Different Behaviors

Some behaviors are intentionally different from the production Neo N3 runtime:

BehaviorEmbedded RuntimeProductionReason
Random numbersDeterministic seedBlock-based entropyReproducible tests
Block timestampsFixed test valueReal timestampsReproducible tests
Network magicTest valueMainnet/testnet magicIsolation
Contract hashesComputed from test dataComputed from deploymentTest isolation
Oracle responsesDeterministic pseudo IDReal oracle networkNo external dependencies

These differences are by design and ensure that tests are deterministic and reproducible without requiring a running Neo node.

Planned Improvements

The following improvements are planned but not yet scheduled:

  • Gas precision tightening -- Align dynamic gas costs with the Neo N3 specification for large integer and collection operations
  • Streaming iterators -- Replace materialized iterator implementation with lazy streaming
  • ByteString/Buffer distinction -- Implement proper mutation semantics
  • Differential testing framework -- Automated comparison of embedded runtime output against Neo-Express
  • Fuzzing framework -- Property-based testing of opcode and syscall implementations

Reporting Issues

If you encounter a behavior difference between the embedded runtime and production Neo N3:

  1. Verify the behavior on Neo-Express to confirm it is a runtime parity issue
  2. Check this page to see if the gap is already documented
  3. Report the issue with a minimal reproduction case

Issue tracker: github.com/r3e-network/neo-solidity/issues

Include:

  • The Solidity source code that demonstrates the issue
  • Expected behavior (from Neo-Express or the Neo N3 specification)
  • Actual behavior from the embedded runtime
  • Compiler version (neo-solc --version)

See Also

MIT Licensed