Containers
Map

Map

A Map is a key-value store. Unlike the raw storage backend, the keys and values of a map are typed.

Keys

The key type has to implement the PrimaryKey (opens in a new tab) trait. Most commonly, the key is simply a String or Addr (opens in a new tab). Other types include binary strings (Vec<u8>, [u8; N], &[u8]), numerical types, or even tuples, which can be used to create composite keys.

💡

Unlike values, keys do not need to implement anything like serde::Serialize or serde::Deserialize. Key encoding is handled by the PrimaryKey trait.

Values

The values, as usual, are serialized as JSON and must implement the Serialize (opens in a new tab) and Deserialize (opens in a new tab) traits.

Operations

Similar to an Item, a Map defines methods like load, save, and may_load. The difference is that Map's methods take a key as an argument.

Most CosmWasm-enabled chains will enable iteration. If they do, it is possible to iterate over the entries in a map using methods like keys (opens in a new tab) or range (opens in a new tab).

More information can be found in the API docs (opens in a new tab).

Usage examples

Keeping users' balances

Checking a user's balance

use cw_storage_plus::Map;
 
let balances: Map<String, u128> = Map::new("balances");
 
let alice_balance = balances
    .may_load(&storage, "alice".to_string())
    .unwrap()
    .unwrap_or(0); // if the entry does not exist yet, assume the balance is 0
 
assert_eq!(alice_balance, 0);

Updating a user's balance

use cw_storage_plus::Map;
 
let balances: Map<String, u128> = Map::new("balances");
 
let mut alice_balance = balances
    .may_load(&storage, "alice".to_string())
    .unwrap()
    .unwrap_or(0); // if the entry does not exist yet, assume the balance is 0
 
alice_balance += 100;
 
balances.save(&mut storage, "alice".to_string(), &alice_balance).unwrap();
assert_eq!(balances.load(&storage, "alice".to_string()).unwrap(), 100);

Keeping users' balances with a composite key

Basic use

use cw_storage_plus::Map;
 
// The first string is the user's address, the second is the denomination
let balances: Map<(String, String), u128> = Map::new("balances");
 
let mut alice_balance = balances
    .may_load(&storage, ("alice".to_string(), "uusd".to_string()))
    .unwrap()
    .unwrap_or(0); // if the entry does not exist yet, assume the balance is 0
 
alice_balance += 100;
 
balances
    .save(&mut storage, ("alice".to_string(), "uusd".to_string()), &alice_balance)
    .unwrap();
 
assert_eq!(
    balances
        .load(&storage, ("alice".to_string(), "uusd".to_string()))
        .unwrap(),
    100
);

Iterating over composite keys

use cw_storage_plus::Map;
 
let balances: Map<(String, String), u128> = Map::new("balances");
 
balances.save(&mut storage, ("alice".to_string(), "uusd".to_string()), &100).unwrap();
balances.save(&mut storage, ("alice".to_string(), "osmo".to_string()), &200).unwrap();
balances.save(&mut storage, ("bob".to_string(), "uusd".to_string()), &300).unwrap();
 
let all_balances: Vec<_> = balances
    .range(&storage, None, None, Order::Ascending)
    .collect::<Result<_, _>>()
    .unwrap();
 
assert_eq!(
    all_balances,
    vec![
        (("bob".to_string(), "uusd".to_string()), 300),
        (("alice".to_string(), "osmo".to_string()), 200),
        (("alice".to_string(), "uusd".to_string()), 100),
    ]
);
 
let alices_balances: Vec<_> = balances
    .prefix("alice".to_string())
    .range(&storage, None, None, Order::Ascending)
    .collect::<Result<_, _>>()
    .unwrap();
 
assert_eq!(alices_balances, vec![("osmo".to_string(), 200), ("uusd".to_string(), 100)]);
⚠️

As seen here, the order of keys isn't always lexicographic.

If you need to rely on iteration order in maps with composite keys, here's how things work: under the hood, every component of a composite key is length-prefixed except for the last one. If you're only iterating over the last component, you can expect things to be ordered lexicographically. For non-final components, shorter strings will always come before longer ones.

In the example, note how "bob" (a non-last component) comes before "alice". Also note how once we lock the first component to "alice", entries are ordered lexicographically by the second component.