Skip to content

Commit 2e32764

Browse files
committed
Added StoragePlus chapters.
1 parent ba41a0b commit 2e32764

File tree

10 files changed

+1293
-0
lines changed

10 files changed

+1293
-0
lines changed

docs/storage-plus/_category_.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"label": "StoragePlus",
3+
"position": 6,
4+
"link": {
5+
"type": "doc",
6+
"id": "introduction"
7+
}
8+
}

docs/storage-plus/basics.md

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
---
2+
sidebar_position: 1
3+
---
4+
5+
# Basics
6+
7+
## Containers
8+
9+
`cw-storage-plus` provides several storage containers that can be used to store data on the
10+
blockchain. The fundamental ones are:
11+
12+
- [`Item`](containers/item)
13+
- [`Map`](containers/map)
14+
- [`Deque`](containers/deque)
15+
16+
An [`Item`](containers/item) is a simple container that stores a single value. The other two are a
17+
little more involved - they are collections capable of storing multiple values, and provide several
18+
methods to interact with them.
19+
20+
## Keys and prefixes
21+
22+
One task of a storage library like `cw-storage-plus` is to manage the namespace of keys provided by
23+
the blockchain.
24+
25+
When constructing a container, you must provide a key of type `&'static str` (or use
26+
[`new_dyn`](https://docs.rs/cw-storage-plus/latest/cw_storage_plus/struct.Item.html#method.new_dyn)).
27+
This usually means you'll be providing a string literal or some constant.
28+
29+
In the case of an [`Item`](containers/item), the provided string is the exact key (a standard UTF-8
30+
string) under which the value will be saved. In the case of collections, the provided string is a
31+
prefix (sometimes called `namespace` in the code); the collection will use this prefix to generate
32+
keys (by appending to it) for the individual values it stores.
33+
34+
As a contract dev, it is your responsibility to ensure that the keys you provide are unique and do
35+
not conflict with keys used by other parts of the contract.
36+
37+
:::tip
38+
39+
Over time, we've learned that using long keys hurts storage performance. If you _gotta go fast_, a
40+
good approach might be to only provide single-character ASCII prefixes for your containers, like
41+
_"a"_, _"b"_, _"c"_, etc.
42+
43+
:::
44+
45+
Generally speaking, we tried to make it so that as long as you use a unique prefix for each
46+
container, you shouldn't have to worry about key conflicts.
47+
48+
TODO: Research the key collisions in the warning below more! Then we can provide better advice.
49+
50+
:::warning
51+
52+
...yet there's a small _but_. A `Map`'s prefix is length-prefixed. An `Item` is saved without any
53+
sort of length-prefixing of the key. It is, in theory, possible for an `Item`'s key to conflict
54+
with one of the keys generated by a `Map` or `Deque`. In practice, this is unlikely unless you use
55+
very long prefixes or start your `Item`'s key with the null byte. Probably don't do that!
56+
57+
:::
58+
59+
## Values
60+
61+
In `cw-storage-plus`, every value is saved using JSON serialization. For that to be possible, only
62+
types that implement [`serde::Serialize`](https://docs.rs/serde/1.0.201/serde/trait.Serialize.html)
63+
and [`serde::Deserialize`](https://docs.rs/serde/1.0.201/serde/trait.Deserialize.html) are allowed.
64+
65+
Most of the Rust standard library types already implement these traits. Additionally, types you'll
66+
find in `cosmwasm_std` (like
67+
[`Addr`](https://docs.rs/cosmwasm-std/2.0.3/cosmwasm_std/struct.Addr.html),
68+
[`Decimal`](https://docs.rs/cosmwasm-std/2.0.3/cosmwasm_std/struct.Decimal.html),
69+
[`Binary`](https://docs.rs/cosmwasm-std/2.0.3/cosmwasm_std/struct.Binary.html) or
70+
[`Coin`](https://docs.rs/cosmwasm-std/2.0.3/cosmwasm_std/struct.Coin.html)) often do as well.
71+
72+
If you're writing a custom type and wish to send it across call or put it in storage, you'll have to
73+
derive these traits.
74+
75+
:::tip
76+
77+
*cw-storage-plus* uses [*serde-json-wasm*](https://github.com/CosmWasm/serde-json-wasm) under the hood.
78+
This provides determinism in some corners - [*serde_json*](https://github.com/serde-rs/json)
79+
would not be suited for the blockchain world! On top of that, `serde-json-wasm` is just smaller.
80+
81+
:::
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"label": "Containers",
3+
"position": 2
4+
}
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
---
2+
sidebar_position: 3
3+
---
4+
5+
# Deque
6+
7+
A `Deque` is a container that imitates a traditional double-ended queue. It's designed for efficient
8+
pushes and pops from either the beginning or end of the queue, making it suitable for use as a queue or stack.
9+
However, it's not optimized for insertions or deletions from the middle.
10+
11+
:::tip
12+
13+
More information can be found in the [API docs].
14+
15+
:::
16+
17+
## Deque operations
18+
19+
The main operations available for a `Deque` are [`push_back`], [`push_front`], [`pop_back`], and
20+
[`pop_front`]. It is also possible to check the [`len`]gth of the deque, [`get`] an element by
21+
index, and [`iter`]ate over the elements.
22+
23+
[`push_back`]: https://docs.rs/cw-storage-plus/latest/cw_storage_plus/struct.Deque.html#method.push_back
24+
[`push_front`]: https://docs.rs/cw-storage-plus/latest/cw_storage_plus/struct.Deque.html#method.push_front
25+
[`pop_back`]: https://docs.rs/cw-storage-plus/latest/cw_storage_plus/struct.Deque.html#method.pop_back
26+
[`pop_front`]: https://docs.rs/cw-storage-plus/latest/cw_storage_plus/struct.Deque.html#method.pop_front
27+
[`len`]: https://docs.rs/cw-storage-plus/latest/cw_storage_plus/struct.Deque.html#method.len
28+
[`get`]: https://docs.rs/cw-storage-plus/latest/cw_storage_plus/struct.Deque.html#method.get
29+
[`iter`]: https://docs.rs/cw-storage-plus/latest/cw_storage_plus/struct.Deque.html#method.iter
30+
31+
- `push_back`: Adds an element to the end of the deque. O(1) time complexity.
32+
- `push_front`: Adds an element to the beginning of the deque. O(1) time complexity.
33+
- `pop_back`: Removes and returns the last element. Returns `None` if the deque is empty. O(1) time complexity.
34+
- `pop_front`: Removes and returns the first element. Returns `None` if the deque is empty. O(1) time complexity.
35+
- `len`: Returns the number of elements in the deque. O(1) time complexity.
36+
- `is_empty`: Returns `true` if the deque contains no elements. O(1) time complexity.
37+
- `get`: Retrieves an element by index. Returns `None` if the index is out of bounds. O(1) time complexity.
38+
- `front`: Returns a reference to the first element without removing it. Returns `None` if the deque is empty.
39+
- `back`: Returns a reference to the last element without removing it. Returns `None` if the deque is empty.
40+
- `iter`: Returns an iterator over the elements of the deque.
41+
42+
:::warning
43+
44+
The maximum capacity of a `Deque` is `u32::MAX - 1` elements. Attempting to push more elements
45+
is considered **Undefined Behavior**.
46+
47+
:::
48+
49+
## Deque lifecycle
50+
51+
Creating a `Deque` doesn't immediately commit anything to storage. To add elements to the deque, you
52+
need to use the `push_back` or `push_front` methods.
53+
54+
Values in a `Deque` must implement the `Serialize` and `Deserialize` traits from the [`serde`] crate
55+
to be stored.
56+
57+
### Lifecycle example
58+
59+
```rust
60+
use cw_storage_plus::Deque;
61+
62+
// Create a new deque. It doesn't exist in storage yet.
63+
let deque: Deque<u32> = Deque::new("d");
64+
assert_eq!(deque.len(&storage).unwrap(), 0);
65+
66+
// Add elements to the deque
67+
deque.push_back(&mut storage, &2).unwrap();
68+
deque.push_back(&mut storage, &3).unwrap();
69+
deque.push_front(&mut storage, &1).unwrap();
70+
71+
// Check the length
72+
assert_eq!(deque.len(&storage).unwrap(), 3);
73+
74+
// Remove elements
75+
assert_eq!(deque.pop_back(&mut storage).unwrap(), Some(3));
76+
assert_eq!(deque.pop_front(&mut storage).unwrap(), Some(1));
77+
78+
// Check the final state
79+
assert_eq!(deque.len(&storage).unwrap(), 1);
80+
assert_eq!(deque.get(&storage, 0).unwrap(), Some(2));
81+
```
82+
83+
## Usage examples
84+
85+
### Using Deque as a queue
86+
87+
```rust
88+
use cw_storage_plus::Deque;
89+
90+
let queue: Deque<String> = Deque::new("q");
91+
92+
// Enqueue elements
93+
queue.push_back(&mut storage, &"first".to_string()).unwrap();
94+
queue.push_back(&mut storage, &"second".to_string()).unwrap();
95+
96+
// Dequeue elements
97+
assert_eq!(queue.pop_front(&mut storage).unwrap(), Some("first".to_string()));
98+
assert_eq!(queue.pop_front(&mut storage).unwrap(), Some("second".to_string()));
99+
assert_eq!(queue.pop_front(&mut storage).unwrap(), None);
100+
```
101+
102+
### Using Deque as a stack
103+
104+
```rust
105+
use cw_storage_plus::Deque;
106+
107+
let stack: Deque<u32> = Deque::new("s");
108+
109+
// Push elements
110+
stack.push_back(&mut storage, &1).unwrap();
111+
stack.push_back(&mut storage, &2).unwrap();
112+
stack.push_back(&mut storage, &3).unwrap();
113+
114+
// Pop elements
115+
assert_eq!(stack.pop_back(&mut storage).unwrap(), Some(3));
116+
assert_eq!(stack.pop_back(&mut storage).unwrap(), Some(2));
117+
assert_eq!(stack.pop_back(&mut storage).unwrap(), Some(1));
118+
assert_eq!(stack.pop_back(&mut storage).unwrap(), None);
119+
```
120+
121+
### Iterating over elements
122+
123+
```rust
124+
use cw_storage_plus::Deque;
125+
126+
let deque: Deque<u32> = Deque::new("d");
127+
128+
deque.push_back(&mut storage, &1).unwrap();
129+
deque.push_back(&mut storage, &2).unwrap();
130+
deque.push_back(&mut storage, &3).unwrap();
131+
132+
let sum: u32 = deque.iter(&storage).unwrap().map(|r| r.unwrap()).sum();
133+
assert_eq!(sum, 6);
134+
```
135+
136+
### Maintaining a log store
137+
138+
It is possible to use a `Deque` to maintain a store of log events or transaction records. This is
139+
useful when you want to keep a history of production level events to ease in debugging a deployed
140+
instance of a contract.
141+
142+
```rust
143+
use cw_storage_plus::Deque;
144+
use serde::{Serialize, Deserialize};
145+
use cosmwasm_std::testing::*;
146+
use cosmwasm_std::Addr;
147+
148+
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
149+
struct Log {
150+
block_height: u64,
151+
sender_addr: String,
152+
event: String,
153+
}
154+
155+
// Initialize the Deque for storing logs
156+
let logs: Deque<Log> = Deque::new("logs");
157+
158+
// Simulating contract execution context
159+
let env = mock_env();
160+
let info = message_info(&Addr::unchecked("sender"), &[]);
161+
let event = "funds_transferred".to_string();
162+
163+
// Add a new log entry
164+
logs.push_front(
165+
&mut storage,
166+
&Log {
167+
block_height: env.block.height,
168+
sender_addr: info.sender.to_string(),
169+
event: event.clone(),
170+
},
171+
).unwrap();
172+
173+
// Optionally, limit the number of stored logs
174+
const MAX_LOGS: u32 = 100;
175+
if logs.len(&storage).unwrap() > MAX_LOGS {
176+
logs.pop_back(&mut storage).unwrap();
177+
}
178+
179+
// Retrieve the most recent log
180+
let latest_log = logs.get(&storage, 0).unwrap().unwrap();
181+
assert_eq!(latest_log.event, "funds_transferred");
182+
```
183+
184+
[`serde`]: https://serde.rs/
185+
[API docs]: https://docs.rs/cw-storage-plus/latest/cw_storage_plus/struct.Deque.html

0 commit comments

Comments
 (0)