aztec-nr - noir_aztec::state_vars

Struct Map

pub struct Map<K, V, Context> {
    pub context: Context,
    /* private fields */
}

A key-value container for state variables.

A Map wraps another state variable type (the 'value') and creates independent instances of it for each key, resulting in effectively as many state variables as there are key values. This behavior is similar to a Solidity mapping (K => V).

Use Cases

Any scenario in which a fixed number of variables is insufficient to represent contract state requires Map e.g. per-election configuration in a voting contract, per-user state, etc. Map also composes naturally into more complex containers: an array could be implemented as a Map with indices as keys.

Note that some private state variables, such as PrivateMutable and PrivateSet cannot be wrapped in a Map. These types implement the OwnedStateVariable trait, and as such should instead be wrapped using the Owned container, which can be thought of as a specialization of Map for private state 'owned' by a single account.

Examples

Since Map is also a StateVariable, it is declared in the contract's #[storage] struct along with all others:

#[storage]
struct Storage<C> {
    is_admin: Map<AztecAddress, PublicMutable<bool, C>, C>,
    vote_tallies: Map<ElectionId, PublicMutable<u128, C>, C>,
}

Multiple Maps

There is no limit to how many Map containers a contract can have, and Maps can themselves wrap other Maps, resulting in nested layouts.

#[storage]
struct Storage<C> {
    // A nested map where the first key is an address and the second a `Year` type, such that
    // self.storage.user_yearly_config.at(user).at(year) results in distinct `UserConfig` variables for each
    // `(user, year)` tuple.
    user_yearly_config: Map<AztecAddress, Map<Year, UserConfig<C>, C>, C>,
}

Requirements

The value type V must implement the StateVariable trait. The key type K must implement the ToField trait in such a way that the Field representation is unique for each key.

For key types that cannot be converted into a Field, consider wrapping them in a type that implements ToField as the hash of the key.

struct MyKey {
    a: Field,
    b: Field,
}

struct MyKeyWrapper {
    inner: MyKey,
}

impl ToField for MyKeyWrapper {
    fn to_field(self) -> Field {
        poseidon2_hash([self.inner.a, self.inner.b])
    }
}

#[storage]
struct Storage<C> {
    a: Map<MyKeyWrapper, PublicMutable<bool, C>, C>,
}

Implementation Details

Like all other state variables, Map gets assigned a unique storage slot. It uses this value to derive unique storage slots for each key, resulting in independent state variables. Nothing gets stored at Map's storage slot. This is equivalent to Solidity's implementation of mapping.

Because the storage slot derivation is done using a hash function, it is not possible to compute the key (preimage) used to obtain a given derived state variable slot.

Fields

context: Context

Implementations

impl<Context, K, V> Map<K, V, Context>

pub fn at<let N: u32>(self, key: K) -> V
where K: ToField, V: StateVariable<N, Context>

Returns the state variable for a key.

This derives a unique storage slot for key and returns a state variable of type V at that slot. It is equivalent to accessing a mapping via the [] operator in Solidity (e.g. balances[user] would be balances.at(user)).

Example

#[external("utility")]
fn get_election_vote_tallies(election: ElectionId) -> u128 {
    self.storage.vote_tallies.at(election).read()
}

Trait implementations

impl<Context, K, V> StateVariable<1, Context> for Map<K, V, Context>

pub fn new(context: Context, storage_slot: Field) -> Self pub fn get_storage_slot(self) -> Field