Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document SnapshotItem #129

Merged
merged 3 commits into from
Sep 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs-test-gen/templates/storage.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,15 @@ mod users {
}
}

fn advance_height(env: &mut Env, blocks: u64) {
env.block.height += blocks;
}

#[test]
fn doctest() {
#[allow(unused_variables, unused_mut)]
let mut storage = cosmwasm_std::testing::MockStorage::new();
let mut env = cosmwasm_std::testing::mock_env();

let users = cw_storage_plus::IndexedMap::<Addr, _, _>::new(
"uu",
Expand Down
4 changes: 3 additions & 1 deletion src/pages/cw-storage-plus/containers/_meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,7 @@
"item": "Item",
"map": "Map",
"deque": "Deque",
"indexed-map": "IndexedMap"
"indexed-map": "IndexedMap",
"snapshot-item": "SnapshotItem",
"snapshot-map": "SnapshotMap"
}
109 changes: 109 additions & 0 deletions src/pages/cw-storage-plus/containers/snapshot-item.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
---
tags: ["storage-plus", "containers"]
---

import { Callout } from "nextra/components";

# SnapshotItem

A `SnapshotItem` is a container that contains a single value that is potentially stored in some
storage identified by a unique key - just like an [`Item`]. It's worth familiarizing yourself with
the `Item` type first, as everything we talked about there is applicable to `SnapshotItem`.

On top of that, `SnapshotItem` 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 `SnapshotItem` 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 `SnapshotItem` is no different from a regular
`Item` 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 a price history

<Callout>
The constructor of `SnapshotItem` takes 3 "namespace" arguments: - the main namespace, similar to
the `Item` constructor - two additional unique namespaces, which are used to store the changelog
metadata
</Callout>

Let's say we want to keep a history of prices for a specific trading pair.

```rust template="storage"
use cw_storage_plus::{SnapshotItem, Strategy};

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

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

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

price
.save(&mut storage, &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, env.block.height - 60)
.unwrap(),
None
);
assert_eq!(
price
.may_load_at_height(&storage, env.block.height - 50)
.unwrap(),
None
);

// Before/at the current block, the price was 0.81
assert_eq!(
price
.may_load_at_height(&storage, env.block.height - 49)
.unwrap(),
Some(Decimal::percent(81))
);
assert_eq!(
price
.may_load_at_height(&storage, 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, env.block.height + 1)
.unwrap(),
Some(Decimal::percent(92))
);
assert_eq!(
price
.may_load_at_height(&storage, env.block.height + 50)
.unwrap(),
Some(Decimal::percent(92))
);
```

[`Item`]: item
[`Strategy`]: https://docs.rs/cw-storage-plus/latest/cw_storage_plus/enum.Strategy.html
[`add_checkpoint`]:
https://docs.rs/cw-storage-plus/latest/cw_storage_plus/struct.SnapshotItem.html#method.add_checkpoint
[`may_load_at_height`]:
https://docs.rs/cw-storage-plus/latest/cw_storage_plus/struct.SnapshotItem.html#method.may_load_at_height
[`StdError::NotFound`]:
https://docs.rs/cosmwasm-std/latest/cosmwasm_std/enum.StdError.html#variant.NotFound
[`serde`]: https://serde.rs/
[API docs]: https://docs.rs/cw-storage-plus/latest/cw_storage_plus/struct.SnapshotItem.html