02 — Fields & Storage
Covenant maps every field declaration to one or more EVM storage slots. The compiler handles packing, hash-derived keys, and initialisation automatically.
Scalar fields
contract Scalars {
field count: u256 = 0
field name: String = ""
field enabled: Bool = true
field owner: Address
}
Address has no default — it is initialised to msg.sender at deployment automatically if omitted.
Mappings
contract Balances {
field balances: Map<Address, u256>
action balance_of(who: Address) -> u256 {
return self.balances[who];
}
action credit(who: Address, amount: u256) {
only(owner);
self.balances[who] += amount;
}
}
Maps compile to keccak256(key . slot) — identical to Solidity mapping.
Nested maps
field allowances: Map<Address, Map<Address, u256>>
Access: self.allowances[owner][spender].
Lists
field voters: List<Address>
Lists expose .push(), .len(), and index access. They store length in slot N and elements at keccak256(N) + i.
Storage packing
Fields smaller than 32 bytes are packed into a single slot when declared consecutively and their combined width ≤ 256 bits:
field status: u8 // ─┐ packed into one slot
field flags: u8 // ─┘
field value: u256 // next slot
Initialisation order
Fields are initialised in declaration order at construction time. You may reference earlier fields:
field cap: u256 = 1_000_000
field minted: u256 = 0
field remaining: u256 = self.cap // legal — cap is already set