DEV Community

ASHDEEP SINGH
ASHDEEP SINGH

Posted on

Intro to EVM II

Hello World !

Last week we learnt about EVM and it's slots. this week this learning continues and we dive deeper into it and understand other aspects of storage.

Dynamic Type In Storage

In Solidity, dynamically-sized state variables (such as dynamic arrays and mappings) are stored differently from statically-sized state variables. This distinction is made because dynamic state variables can grow and shrink, and their data can't fit neatly into a single storage slot.

Marker Slots: The storage slot assigned to a dynamically-sized variable serves as a "marker" or "pointer" rather than directly holding the data. This slot typically holds metadata, such as the length of a dynamic array.
Separate Storage for Data: The actual data of the dynamically-sized variable is stored in separate storage locations. The starting position of this data is computed using a hashing function.

Formula to the memory address where the i-th element of the array c is stored.
c is the array.
slot_c is the storage slot of the array c.
keccak(x) is the Keccak hash function applied to slot x.
i is the index of the element in the array.
The address of the i-th element of the array c can be calculated as:
Address(c[i])=keccak(slot_c)+ i

Mapping In Storage

Mappings in Solidity are stored in a unique way to ensure efficient access and avoid collisions. The marker slot for a mapping marks its existence but does not directly store any of the mapping's key-value pairs. Instead, the key-value pairs are stored at specific computed storage locations based on the keys.
Marker Slot: The storage slot assigned to the mapping itself (the marker slot) serves as a reference point but doesn't store any actual key-value pairs. This slot only indicates that a mapping exists.

Formula to the memory address where the values of mapping c is stored.
k is the key of mapping
p is the position (or storage slot) of the mapping declaration in the contract.
. denotes concatenation.
The address of the key element of the mapping c can be calculated as:
Address(c[k])=keccak256(k . p)

State variables

State variables of contracts are stored in storage in a compact way such that multiple values sometimes use the same storage slot (except dynamically-sized arrays and mappings).

Multiple, contiguous items that need less than 32 bytes are packed into a single storage slot if possible, according to the following rules:

The first item in a storage slot is stored lower-order aligned:

This means that when multiple items are packed into a single storage slot, the first item is aligned to the lowest byte address of the slot. In other words, the first item is stored starting from the first byte of the slot, without any padding or gaps.

For example, if a slot is 32 bytes long and the first item is a uint8 (1 byte), it will be stored in the first byte of the slot (bytes 0-1). If the next item is a uint16 (2 bytes), it will be stored in bytes 1-3, and so on.

Value types use only as many bytes as are necessary to store them:

This means that each item is stored in the minimum number of bytes required to represent its value, without any extra padding or waste.

For example:

  • A uint8 (1 byte) will only use 1 byte of storage.
  • A uint16 (2 bytes) will only use 2 bytes of storage.
  • A uint256 (32 bytes) will use the full 32 bytes of storage.

If a value type does not fit the remaining part of a storage slot, it is stored in the next storage slot.

Structs and array data always start a new slot and their items are packed tightly according to these rules.

Items following struct or array data always start a new storage slot.

For example, consider the following contracts:
Contract A (base-most contract):
uint public x;

Contract B (inherits from A):
uint public y;

Contract C (inherits from B):
uint public z;

The ordering of state variables in Contract C would be:

x (from Contract A)
y (from Contract B)
z (from Contract C)

If x, y, and z are all uint256 (32 bytes), they would share the same storage slot, with x occupying the first 32 bytes, y occupying the next 32 bytes, and z occupying the final 32 bytes.

That's all for this week folks.
Stay tuned for more.

Top comments (0)