From ef29c5a27d29110eca72f1de975f76ba32a985d7 Mon Sep 17 00:00:00 2001 From: Tomasz Kurcz Date: Thu, 8 Aug 2024 19:41:51 +0200 Subject: [PATCH] storage-plus: document SnapshotItem --- docs-test-gen/templates/storage.tpl | 61 +++++++++++ src/pages/cw-storage-plus/_meta.json | 1 - .../cw-storage-plus/containers/_meta.json | 4 +- .../containers/snapshot-item.mdx | 101 ++++++++++++++++++ 4 files changed, 165 insertions(+), 2 deletions(-) create mode 100644 src/pages/cw-storage-plus/containers/snapshot-item.mdx diff --git a/docs-test-gen/templates/storage.tpl b/docs-test-gen/templates/storage.tpl index 5c3600fa..384bad57 100644 --- a/docs-test-gen/templates/storage.tpl +++ b/docs-test-gen/templates/storage.tpl @@ -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::::new( "uu", @@ -106,6 +111,62 @@ fn doctest() { users.save(&mut storage, addr, &user).unwrap(); } + use cw_storage_plus::{SnapshotItem, Strategy}; + + let price: SnapshotItem = SnapshotItem::new("p", "p1", "p2", Strategy::EveryBlock); + + price + .save(&mut storage, &Decimal::percent(81), env.block.height) + .unwrap(); + + advance_height(&mut env, 50); + + 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 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 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)) + ); + #[rustfmt::skip] {{code}} } diff --git a/src/pages/cw-storage-plus/_meta.json b/src/pages/cw-storage-plus/_meta.json index 7cbef22b..90aa872b 100644 --- a/src/pages/cw-storage-plus/_meta.json +++ b/src/pages/cw-storage-plus/_meta.json @@ -1,6 +1,5 @@ { "basics": "Basics", "containers": "Containers", - "snapshots": "Snapshots", "multi-indexes": "Multi index collections" } diff --git a/src/pages/cw-storage-plus/containers/_meta.json b/src/pages/cw-storage-plus/containers/_meta.json index f87bc0d8..bb682c05 100644 --- a/src/pages/cw-storage-plus/containers/_meta.json +++ b/src/pages/cw-storage-plus/containers/_meta.json @@ -2,5 +2,7 @@ "item": "Item", "map": "Map", "deque": "Deque", - "indexed-map": "IndexedMap" + "indexed-map": "IndexedMap", + "snapshot-item": "SnapshotItem", + "snapshot-map": "SnapshotMap" } diff --git a/src/pages/cw-storage-plus/containers/snapshot-item.mdx b/src/pages/cw-storage-plus/containers/snapshot-item.mdx new file mode 100644 index 00000000..0e6d3d8c --- /dev/null +++ b/src/pages/cw-storage-plus/containers/snapshot-item.mdx @@ -0,0 +1,101 @@ +--- +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 +- `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 + +```rust template="storage" +use cw_storage_plus::{SnapshotItem, Strategy}; + +let price: SnapshotItem = 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 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 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