StorageMap

A StorageMap, a.k.a. a hash table, is a structure which associates a value v with a key k. The key is used to find the position in the table (memory) where the value is stored.

The benefit of a hash table is that no matter where the value is in the table the computation required to find the location of that value is constant i.e. it has an order of 1 O(1).

Sway provides a flexible StorageMap because it uses generics for both k & v with the caveat that k and v have to be a single value. The value can be a struct, tuple, array etc. therefore if you'd like to have a complex k or v then the data needs to be wrapped into a single type.

Declaration

The StorageMap type is included in the prelude therefore we do not need to import it. We'll be using msg_sender() in the subsequent section so we'll import that here.

After the import we initialize our StorageMap as described in the initialization section.


storage {
    // k = Identity, v = u64
    balance: StorageMap<Identity, u64> = StorageMap::<Identity, u64> {},
    // k = (Identity, u64), v = bool
    user: StorageMap<(Identity, u64), bool> = StorageMap::<(Identity, u64), bool> {},
}

There are two storage variables: balance & user. balance takes a single value as the key while user wraps two values into a tuple and uses that as a key.

Reading from Storage

Retrieving data from a storage variable is done through the .get(key) method. That is to say that we state which storage variable we would like to read from and append .get() to the end while providing the key for the data that we want to retrieve. The method get returns an Option; if there is no value for key in the map, get will return None.

In this example we wrap the Identity of the caller with their provided id into a tuple and use that as the key.

#[storage(read)]
fn reading_from_storage(id: u64) {
    let user = storage.user.get((msg_sender().unwrap(), id)).read();
}

This contract method handles the returned Option by calling unwrap_or to set user to zero if the map user doesn't have an entry for the key.

Writing to Storage

Writing to storage is similar to reading. The difference is that we use a different method .insert(key, value).

In this example we retrieve the balance of the caller and then increment their balance by 1.

#[storage(read, write)]
fn writing_to_storage() {
    let balance = storage.balance.get(msg_sender().unwrap()).read();
    storage.balance.insert(msg_sender().unwrap(), balance + 1);
}