Skip to main content

StorageValue

A branded type representing a 32-byte (256-bit) EVM storage slot value. In the EVM, each contract has 2^256 storage slots, and each slot stores exactly 32 bytes.

Overview

StorageValueType provides:
  • Compile-time type branding via Uint8Array
  • 32-byte fixed size validation
  • Conversion to/from hex strings and bigint
  • Constant-time equality comparison

Type Definition

import type { brand } from '../../../brand.js';

export type StorageValueType = Uint8Array & {
  readonly [brand]: "StorageValue";
  readonly length: 32;
};

Constants

import * as StorageValue from '@voltaire/primitives/StorageValue';

StorageValue.SIZE;  // 32

Construction

from

Create from bigint, hex string, or Uint8Array:
import * as StorageValue from '@voltaire/primitives/StorageValue';

// From bigint
const val1 = StorageValue.from(123n);

// From hex string
const val2 = StorageValue.from("0x0000000000000000000000000000000000000000000000000000000000000001");

// From Uint8Array (must be 32 bytes)
const val3 = StorageValue.from(new Uint8Array(32));

fromHex

Create from hex string (with or without 0x prefix):
import * as StorageValue from '@voltaire/primitives/StorageValue';

const val = StorageValue.fromHex("0x0000000000000000000000000000000000000000000000000000000000000064");

Conversion

toHex

Convert to 0x-prefixed hex string:
import * as StorageValue from '@voltaire/primitives/StorageValue';

const val = StorageValue.from(123n);
const hex = StorageValue.toHex(val);
// "0x000000000000000000000000000000000000000000000000000000000000007b"

toUint256

Convert to bigint:
import * as StorageValue from '@voltaire/primitives/StorageValue';

const val = StorageValue.from(123n);
const num = StorageValue.toUint256(val);
// 123n

Comparison

equals

Compare two StorageValues using constant-time comparison (timing-attack safe):
import * as StorageValue from '@voltaire/primitives/StorageValue';

const a = StorageValue.from(100n);
const b = StorageValue.from(100n);
const c = StorageValue.from(200n);

StorageValue.equals(a, b);  // true
StorageValue.equals(a, c);  // false

EVM Storage Mechanics

Storage in the EVM is a key-value store where:
  • Keys are 256-bit (32-byte) slot indices
  • Values are 256-bit (32-byte) words
  • Uninitialized slots return zero
  • Writing zero can trigger gas refunds
import * as StorageValue from '@voltaire/primitives/StorageValue';

// Slot 0 typically stores first state variable
const slot0 = StorageValue.from(0n);

// ERC-20 balances often at slot 0 (mapping)
const balanceSlot = StorageValue.from(
  "0x0000000000000000000000000000000000000000000000000000000000000000"
);

Use Cases

  • Reading/writing contract storage via JSON-RPC
  • EVM execution and state management
  • Storage proof verification
  • State diff analysis
  • Contract state inspection tools

Example: Storage Access

import * as StorageValue from '@voltaire/primitives/StorageValue';
import * as Keccak256 from '@voltaire/crypto/Keccak256';
import * as Address from '@voltaire/primitives/Address';

// Calculate ERC-20 balance storage slot
// balanceOf[address] uses: keccak256(address . slot)
function getBalanceSlot(holder: string, mappingSlot: bigint): string {
  const address = Address.from(holder);
  const slot = StorageValue.from(mappingSlot);

  // Concatenate address (left-padded to 32 bytes) with slot
  const data = new Uint8Array(64);
  data.set(new Uint8Array(12), 0);  // 12 zero bytes
  data.set(address, 12);             // 20-byte address
  data.set(slot, 32);                // 32-byte slot

  return Keccak256.hashToHex(data);
}

// Get balance slot for an address at mapping slot 0
const slot = getBalanceSlot("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", 0n);

API Reference

MethodDescription
from(value)Create from bigint, hex, or Uint8Array
fromHex(hex)Create from hex string
toHex(value)Convert to hex string
toUint256(value)Convert to bigint
equals(a, b)Constant-time equality check