StrataSolidity

Working with State

Mappings#

Mappings are the primary data structure in Solidity smart contracts. StrataSolidity has strong support for reasoning about mappings, including simple mappings, nested mappings, and mappings with computed keys.

Simple mappings#

mapping(address => uint256) public balances;

/// @custom:strata ensures balances[to] == old(balances[to]) + amount
function mint(address to, uint256 amount) external {
    balances[to] += amount;
}

Mapping access in specifications uses the same syntax as Solidity: balances[key].

Nested mappings#

Nested mappings (e.g., ERC20 allowances) are fully supported:

mapping(address => mapping(address => uint256)) public allowance;

/// @custom:strata requires allowance[from][msg.sender] >= amount
/// @custom:strata ensures allowance[from][msg.sender] == old(allowance[from][msg.sender]) - amount
/// @custom:strata modifies allowance[from][msg.sender]
function transferFrom(address from, address to, uint256 amount) external {
    require(allowance[from][msg.sender] >= amount, "Allowance exceeded");
    allowance[from][msg.sender] -= amount;
    balances[from] -= amount;
    balances[to] += amount;
}

Computed keys#

Mappings keyed by hash functions are supported — keccak256 and sha256 are modeled as uninterpreted functions:

mapping(bytes32 => uint256) public data;

/// @custom:strata ensures data[keccak256(abi.encodePacked(a, b))] == value
function store(address a, uint256 b, uint256 value) external {
    data[keccak256(abi.encodePacked(a, b))] = value;
}

The verifier treats keccak256 as a function that is consistent (same input → same output) but otherwise uninterpreted — it does not reason about the hash algorithm itself.

Mapping semantics#

Solidity mappings have infinite domain with default zero values — every key “exists” and maps to zero until explicitly set. StrataSolidity models this using SMT arrays with select/store operations, which faithfully captures this semantics.

Current limitation: struct fields through mappings#

Accessing struct fields through mapping values is not yet supported in specifications:

struct Payment { uint256 amount; bool captured; }
mapping(bytes32 => Payment) public payments;

/// @custom:strata ensures payments[id].amount == amt  // ❌ NOT SUPPORTED

Workaround: Flatten structs into separate mappings:

mapping(bytes32 => uint256) public paymentAmounts;
mapping(bytes32 => bool) public paymentCaptured;

/// @custom:strata ensures paymentAmounts[id] == amt  // ✅ SUPPORTED

See Current Limitations for details.

View this page’s source on GitHub