Skip to main content

SnapshotMap

A SnapshotMap is a key-value store like Map. It's worth familiarizing yourself with the Map type first, as everything we talked about there is applicable to SnapshotMap.

On top of that, SnapshotMap makes it a little simpler to maintain a history of values at various block heights. This involves saving "checkpoints" at some points in time - just how that is done is decided by the Strategy type passed to the SnapshotMap constructor.

Strategy

There are currently 3 built-in strategies, although in the future this might be open to extension.

  • EveryBlock - a checkpoint is (conceptually) added at the beginning of every block, and historical data is available for any height
  • Never - there's never a checkpoint saved, and the SnapshotMap is no different from a regular Map in terms of features. Any call to may_load_at_height will return StdError::NotFound
  • Selected - the add_checkpoint method has to be called manually to add a checkpoint at the given height. Keep in mind that when calling may_load_at_height later, the height has to be the same as the one passed to add_checkpoint. If you try to load a value at a height when no checkpoint was saved, the method will return StdError::NotFound.

Usage examples

Maintaining price histories for various trading pairs

tip

The constructor of SnapshotMap takes 3 "namespace" arguments: the main namespace, similar to the Map constructor and two additional unique namespaces, which are used to store the changelog metadata.

use cw_storage_plus::{SnapshotMap, Strategy};

let price: SnapshotMap<(&str, &str), Decimal> = SnapshotMap::new("p", "p1", "p2", Strategy::EveryBlock);

price
.save(&mut storage, ("OSMO", "ATOM"), &Decimal::percent(81), env.block.height)
.unwrap();

advance_height(&mut env, 50); // fast forward 50 blocks

price
.save(&mut storage, ("OSMO", "ATOM"), &Decimal::percent(92), env.block.height)
.unwrap();

// Before/at the first save, the price was unknown (uninitialized state)
assert_eq!(
price
.may_load_at_height(&storage, ("OSMO", "ATOM"), env.block.height - 60)
.unwrap(),
None
);
assert_eq!(
price
.may_load_at_height(&storage, ("OSMO", "ATOM"), env.block.height - 50)
.unwrap(),
None
);

// Before/at the current block, the price was 0.81
assert_eq!(
price
.may_load_at_height(&storage, ("OSMO", "ATOM"), env.block.height - 49)
.unwrap(),
Some(Decimal::percent(81))
);
assert_eq!(
price
.may_load_at_height(&storage, ("OSMO", "ATOM"), env.block.height)
.unwrap(),
Some(Decimal::percent(81))
);

// After the current block, the price will come up as 0.92
assert_eq!(
price
.may_load_at_height(&storage, ("OSMO", "ATOM"), env.block.height + 1)
.unwrap(),
Some(Decimal::percent(92))
);
assert_eq!(
price
.may_load_at_height(&storage, ("OSMO", "ATOM"), env.block.height + 50)
.unwrap(),
Some(Decimal::percent(92))
);