Storage types — evmstate
Skip to content

Storage types

This reference explains how the library handles different Solidity storage types.

Value types

Simple values stored directly in slots.

Integers (uint/int)

uint256 public counter = 0;
"counter": {
  "kind": "primitive",
  "name": "counter",
  "type": "uint256",
  "trace": [
    {
      "current": { "hex": "0x00", "decoded": 0n },
      "modified": true,
      "next": { "hex": "0x01", "decoded": 1n },
      "path": [],
      "fullExpression": "counter",
      "slots": ["0x0000000000000000000000000000000000000000000000000000000000000000"]
    }
  ]
}

Booleans

bool public flag = false;
"flag": {
  "kind": "primitive",
  "name": "flag",
  "type": "bool",
  "trace": [
    {
      "current": { "hex": "0x00", "decoded": false },
      "modified": true,
      "next": { "hex": "0x01", "decoded": true },
      "path": [],
      "fullExpression": "flag",
      "slots": ["0x0000000000000000000000000000000000000000000000000000000000000001"]
    }
  ]
}

Addresses

address public owner = address(0);
"owner": {
  "kind": "primitive",
  "name": "owner",
  "type": "address",
  "trace": [
    {
      "current": { 
        "hex": "0x0000000000000000000000000000000000000000",
        "decoded": "0x0000000000000000000000000000000000000000"
      },
      "modified": true,
      "next": {
        "hex": "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4",
        "decoded": "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4"
      },
      "path": [],
      "fullExpression": "owner",
      "slots": ["0x0000000000000000000000000000000000000000000000000000000000000002"]
    }
  ]
}

Mappings

Mappings compute slots based on keys and the base slot.

Simple mapping

mapping(address => uint256) public balances;
"balances": {
  "kind": "mapping",
  "name": "balances",
  "type": "mapping(address => uint256)",
  "trace": [
    {
      "current": { "hex": "0x00", "decoded": 0n },
      "modified": true,
      "next": { "hex": "0x2386f26fc10000", "decoded": 10000000000000000n },
      "path": [
        {
          "kind": "mapping_key",
          "key": "0x1234567890123456789012345678901234567890",
          "keyType": "address"
        }
      ],
      "fullExpression": "balances[0x1234567890123456789012345678901234567890]",
      "slots": ["0x8e9c0c9f9fb928592f2fb0a9314450706c27839d034893b88d8ed2f54cf1bd5e"]
    }
  ]
}

Nested mapping

mapping(address => mapping(address => uint256)) public allowances;
"allowances": {
  "kind": "mapping",
  "name": "allowances",
  "type": "mapping(address => mapping(address => uint256))",
  "trace": [
    {
      "current": { "hex": "0x00", "decoded": 0n },
      "modified": true,
      "next": { "hex": "0x0de0b6b3a7640000", "decoded": 1000000000000000000n },
      "path": [
        {
          "kind": "mapping_key",
          "key": "0x1234567890123456789012345678901234567890",
          "keyType": "address"
        },
        {
          "kind": "mapping_key",
          "key": "0x5678901234567890123456789012345678901234",
          "keyType": "address"
        }
      ],
      "fullExpression": "allowances[0x1234...][0x5678...]",
      "slots": ["0x6a70ff112c49166454439c4e0a5f7db9e9a4ac61f326f232ac9eb1a9b05f4eef"]
    }
  ]
}

Arrays

Arrays store length at the base slot and elements at computed slots.

Dynamic arrays

uint256[] public numbers;
"numbers": {
  "kind": "dynamic_array",
  "name": "numbers",
  "type": "uint256[]",
  "trace": [
    {
      // Array length
      "current": { "hex": "0x02", "decoded": 2n },
      "modified": true,
      "next": { "hex": "0x03", "decoded": 3n },
      "path": [
        { "kind": "array_length", "name": "_length" }
      ],
      "fullExpression": "numbers._length",
      "slots": ["0x0000000000000000000000000000000000000000000000000000000000000006"]
    },
    {
      // Element access
      "current": { "hex": "0x00", "decoded": 0n },
      "modified": true,
      "next": { "hex": "0x2a", "decoded": 42n },
      "path": [
        { "kind": "array_index", "index": 2n }
      ],
      "fullExpression": "numbers[2]",
      "slots": ["0x5de13444fe158c7b5525d0d208535a5f84ca2f75ce5219b9c55fb55643beb57c"]
    }
  ]
}

Fixed-size arrays

uint256[3] public fixedNumbers;
"fixedNumbers": {
  "kind": "static_array",
  "name": "fixedNumbers",
  "type": "uint256[3]",
  "trace": [
    {
      "current": { "hex": "0x00", "decoded": 0n },
      "modified": true,
      "next": { "hex": "0x2a", "decoded": 42n },
      "path": [
        { "kind": "array_index", "index": 1n }
      ],
      "fullExpression": "fixedNumbers[1]",
      "slots": ["0x0000000000000000000000000000000000000000000000000000000000000008"]
    }
  ]
}

Structs

Structs organize fields sequentially in storage.

struct User {
  uint256 id;
  address wallet;
  bool active;
}
 
User public admin;
"admin": {
  "kind": "struct",
  "name": "admin",
  "type": "struct Contract.User",
  "trace": [
    {
      "current": { "hex": "0x00", "decoded": 0n },
      "modified": true,
      "next": { "hex": "0x01", "decoded": 1n },
      "path": [
        { "kind": "struct_field", "name": "id" }
      ],
      "fullExpression": "admin.id",
      "slots": ["0x000000000000000000000000000000000000000000000000000000000000000a"]
    },
    {
      "current": {
        "hex": "0x0000000000000000000000000000000000000000",
        "decoded": "0x0000000000000000000000000000000000000000"
      },
      "modified": true,
      "next": {
        "hex": "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4",
        "decoded": "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4"
      },
      "path": [
        { "kind": "struct_field", "name": "wallet" }
      ],
      "fullExpression": "admin.wallet",
      "slots": ["0x000000000000000000000000000000000000000000000000000000000000000b"]
    }
  ]
}

Strings and bytes

Strings and bytes can be stored inline (short) or across multiple slots (long).

Short strings/bytes

string public shortString = "short string";
"shortString": {
  "kind": "bytes",
  "name": "shortString",
  "type": "string",
  "trace": [
    {
      "current": {
        "decoded": 12n,
        "hex": "0x0c",
      },
      "fullExpression": "shortString._length",
      "modified": false,
      "path": [
        {
          "kind": "bytes_length",
          "name": "_length",
        },
      ],
      "slots": [
        "0x0000000000000000000000000000000000000000000000000000000000000000",
      ],
    },
    {
      "current": {
        "decoded": "short string",
        "hex": "0x73686f727420737472696e67",
      },
      "fullExpression": "shortString",
      "modified": false,
      "path": [],
      "slots": [
        "0x0000000000000000000000000000000000000000000000000000000000000000",
      ],
    },
  ],
}

Long strings/bytes

bytes public longBytes = "0xabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd";
"longBytes": {
  "kind": "bytes",
  "name": "longBytes",
  "type": "bytes",
  "trace": [
    {
      "current": {
        "decoded": 60n,
        "hex": "0x3c",
      },
      "fullExpression": "longBytes._length",
      "modified": false,
      "path": [
        {
          "kind": "bytes_length",
          "name": "_length",
        },
      ],
      "slots": [
        "0x0000000000000000000000000000000000000000000000000000000000000001",
      ],
    },
    {
      "current": {
        "decoded": "0xabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd",
        "hex": "0xabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd",
      },
      "fullExpression": "longBytes",
      "modified": false,
      "path": [],
      "slots": [
        "0x0000000000000000000000000000000000000000000000000000000000000001",
        "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6",
        "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf7",
      ],
    },
  ],
}

Storage packing

Multiple small variables can be packed into a single slot:

uint8 public value1;   // 1 byte
uint16 public value2;  // 2 bytes
bool public flag;      // 1 byte
// All packed into slot 0
"value1": {
  "kind": "primitive",
  "name": "value1",
  "type": "uint8",
  "trace": [
    {
      "current": { "hex": "0x00", "decoded": 0 },
      "modified": true,
      "next": { "hex": "0x2a", "decoded": 42 },
      "fullExpression": "value1",
      "slots": ["0x0000000000000000000000000000000000000000000000000000000000000000"],
      "offset": 0,
      "size": 1
    }
  ]
}