Solidity Types and Storage
- Solidity is designed to compile down to Ethereum Virtual Machine (EVM) bytecode, and the EVM has a specific, low-level, word-oriented architecture: 1 word = 4 bytes = 32 bits
uint8 (1 byte, 8 bits)
uint16 (2 bytes, 16 b)
uint32 (4 bytes, 32b)
uint64 (8 bytes, 64b)
uint128 (16 bytes, 128b)
uint256(32 bytes, 256bits), etc.
- EVM storage and memory operations are byte-addressable, not bit-addressable
- so minimum unit8 and max uint256
uint8 a = 5; // ✅ fits in 1 byte
uint256 b = 500; // ✅ takes full 32-byte word
boolean flag = true; // fits in 1 byte or uint8
Scalar Data Types in Solidity
| Type |
Description |
Size (bits) |
Storage Implication |
uint<N> |
Unsigned integer (0 to 2ⁿ - 1) |
8–256 (1 byte - 32 byte) |
Word-aligned packing |
int<N> |
Signed integer (−2ⁿ⁻¹ to 2ⁿ⁻¹−1) |
8–256(1 byte - 32 byte) |
Same as above |
bool |
true or false |
8 (1 byte ) |
Can be packed tightly |
address |
Ethereum address (20 bytes) |
160 bits(20 bytes, but will use 32 Bytes), the leftmost 12 bytes are padded with zeros, or can be packed with smaller variables , such as uint8 — uint96 (as 12 bytes are unused) |
|
| Fits in one word, |
|
|
|
bytes<N> |
Fixed-size byte array |
N ≤ 32 |
One word or packed |
bytes |
Dynamically-sized byte array (like string) |
N/A |
Pointer to memory |
string |
UTF-8 encoded dynamic byte array |
N/A |
Pointer to memory |
enum |
User-defined named values (0, 1, 2…) |
8+ bits |
Stored as uint8+ |
Notes on Packing
- Packing only works for storage, not memory or calldata
- Solidity will only pack when variables are declared sequentially
- Solidity packs upto a word (or 4 bytes = 32 bits)
- In dynamic arrays in Solidity (e.g.
bytes, string, bytes[], etc): The first word, 32 bytes of memory store the array length, not the actual data.
example
pragma solidity ^0.8.0;
contract Privacy {
bool public locked = true; // slot 0, 8 bits, 1 byte
uint256 public ID = block.timestamp; // slot 1, 32 bytes
uint8 private flattening = 10; // slot 2, 8 bits, 1 byte
uint8 private denomination = 255;// slot 2 (16 bits total),
uint16 private awkwardness = uint16(block.timestamp); // slot 2 (32 bits in total, 1 word )
bytes32[3] private data; // data [0] slot 3, data[1] slot 4, data [2] slot 5
constructor(bytes32[3] memory _data) {
data = _data;
}
function unlock(bytes16 _key) public {
require(_key == bytes16(data[2]));// get slot 5 from web3 js/ console, then take half of them for bytes16
locked = false;
}
}
| Storage Slot |
Contents |
0 |
locked |
1 |
ID |
2 |
flattening, denomination, awkwardness (packed) |
3 |
data[0] |
4 |
data[1] |
5 |
data[2] |
Type conversions
- INTEGER (padding and truncation on the left) - If an integer is explicitly converted to a smaller type, higher-order bits are cut off: