Skip to content

Layout of State Variables in Storage and Transient Storage ​

Statically-sized variables (everything except mapping and dynamically-sized array types) are laid out in storage consecutively starting from position 0 in Ethereum.

💡 NeoVM Difference: Key-Value Architecture

EVM uses a linear, sequential 256-bit slot model for storage where variables occupy contiguous 32-byte slots. NeoVM uses a dynamic Key-Value storage model. Keys are arbitrary-length byte arrays. Neo DevPack for Solidity computes a unique, deterministic hash for every state variable using SHA256 rather than assigning sequential slots.

For a split reader guide to mappings, arrays, structs, and key derivation, see Storage and Mappings.

Transient Storage ​

💡 NeoVM Difference: Transient Keyword

Ethereum Solidity v0.8.24 introduced the transient data location keyword (EIP-1153) to store data that is discarded after every transaction. NeoVM does not currently have a native transient storage mechanism.

Using the transient keyword in Neo DevPack for Solidity is currently parsed for compatibility but will emit a compilation warning or error depending on the use-case, as the runtime does not support reverting state without a transaction failure.

Deterministic Key Derivation ​

The Neo DevPack for Solidity compiler derives storage keys by utilizing the SHA256 hashing algorithm.

For simple, non-dynamic variables, the storage key is the hash of the variable name:

storage_key = SHA256(variable_name)

Because keys are derived by name, renaming a state variable between contract upgrades will cause the contract to lose access to the previously stored data.

Mappings and Dynamic Arrays ​

Mappings and arrays on Neo use iterative hashing to produce deterministic, collision-free keys, similar in concept to EVM's keccak256(key . slot), but adapted for Neo's key-value infrastructure.

For mappings, keys are derived iteratively:

// Simple mapping: mapping(address => uint256) balances;
slot_hash = SHA256("balances")
storage_key = SHA256(serialize(key) || slot_hash)

// Nested mapping: mapping(address => mapping(uint256 => bool)) approvals;
slot_hash   = SHA256("approvals")
level_1     = SHA256(serialize(outer_key) || slot_hash)
level_2     = SHA256(serialize(inner_key) || level_1)
// level_2 is the final storage key

Array Storage Layout ​

Storage arrays use a length key and per-element keys:

length_key = SHA256(variable_name)
element_key(i) = SHA256(serialize(i) || SHA256(variable_name))

The .push() operation retrieves the length, increments it, and writes the new element to element_key(i).

Struct Storage Layout ​

Structs stored in state variables are serialized as a single blob using StdLib.serialize(). The entire struct is read and written atomically.

solidity
struct Config {
    address admin;
    uint256 fee;
    bool paused;
}

Config public config;
// In storage: mapped to a single key: SHA256("config")
// Value: the binary serialized output of the struct

Note: Because struct storage is atomic, reading a single field requires deserializing the entire struct. For frequently accessed individual fields, consider using separate state variables.

Storage Serialization Formats ​

When encoding values to create storage keys or persisting them to storage, Neo DevPack for Solidity uses specific binary formats:

TypeSerialization Strategy
IntegersBig-endian byte array, padded to declared bit width. Signed integers are sign-extended; unsigned are zero-extended.
BooleansSingle byte: 0x00 (false) or 0x01 (true).
AddressRaw 20 bytes (UInt160), no padding.
Bytes/StringRaw UTF-8 bytes, no length prefix.
StructsStdLib.serialize() binary payload.

Because NeoVM handles the underlying Get and Put syscalls using these arbitrary byte arrays, you do not have to worry about EVM tight-packing optimizations (like packing two uint128 variables into a single 256-bit slot). The NeoVM bills storage byte-by-byte based on the actual size of the payload.

MIT Licensed