From 7fe20210033266ea762bd181a3a24e543aa82219 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Thu, 22 Jul 2021 09:50:44 +0200 Subject: [PATCH 1/4] Add owned Addr PrimaryKey impls --- packages/storage-plus/src/keys.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/packages/storage-plus/src/keys.rs b/packages/storage-plus/src/keys.rs index 9a546e04a..1a6ec1534 100644 --- a/packages/storage-plus/src/keys.rs +++ b/packages/storage-plus/src/keys.rs @@ -165,6 +165,23 @@ impl<'a> Prefixer<'a> for &'a Addr { } } +/// owned variant. +impl<'a> PrimaryKey<'a> for Addr { + type Prefix = (); + type SubPrefix = (); + + fn key(&self) -> Vec<&[u8]> { + // this is simple, we don't add more prefixes + vec![self.as_bytes()] + } +} + +impl<'a> Prefixer<'a> for Addr { + fn prefix(&self) -> Vec<&[u8]> { + vec![&self.as_bytes()] + } +} + // this auto-implements PrimaryKey for all the IntKey types impl<'a, T: Endian + Clone> PrimaryKey<'a> for IntKey { type Prefix = (); From c8e69e2bb283133c0f51e380b006231bd78a120b Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Thu, 22 Jul 2021 09:52:12 +0200 Subject: [PATCH 2/4] Use Addr for TokenIndexes owner index --- contracts/cw721-base/src/contract.rs | 8 +------- contracts/cw721-base/src/state.rs | 8 ++------ 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/contracts/cw721-base/src/contract.rs b/contracts/cw721-base/src/contract.rs index ddf991020..0d4b45982 100644 --- a/contracts/cw721-base/src/contract.rs +++ b/contracts/cw721-base/src/contract.rs @@ -513,13 +513,7 @@ fn query_tokens( let res: Result, _> = tokens() .idx .owner - .pks( - deps.storage, - Vec::from(owner_addr.as_ref()), - start, - None, - Order::Ascending, - ) + .pks(deps.storage, owner_addr, start, None, Order::Ascending) .take(limit) .collect(); let pks = res?; diff --git a/contracts/cw721-base/src/state.rs b/contracts/cw721-base/src/state.rs index 41b662e0e..e2d09b5f5 100644 --- a/contracts/cw721-base/src/state.rs +++ b/contracts/cw721-base/src/state.rs @@ -53,7 +53,7 @@ pub fn increment_tokens(storage: &mut dyn Storage) -> StdResult { pub struct TokenIndexes<'a> { // pk goes to second tuple element - pub owner: MultiIndex<'a, (Vec, Vec), TokenInfo>, + pub owner: MultiIndex<'a, (Addr, Vec), TokenInfo>, } impl<'a> IndexList for TokenIndexes<'a> { @@ -65,11 +65,7 @@ impl<'a> IndexList for TokenIndexes<'a> { pub fn tokens<'a>() -> IndexedMap<'a, &'a str, TokenInfo, TokenIndexes<'a>> { let indexes = TokenIndexes { - owner: MultiIndex::new( - |d, k| (Vec::from(d.owner.as_ref()), k), - "tokens", - "tokens__owner", - ), + owner: MultiIndex::new(|d, k| (d.owner.clone(), k), "tokens", "tokens__owner"), }; IndexedMap::new("tokens", indexes) } From 39a12928ae30a955f39468f43f31f1c217b53d92 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Thu, 22 Jul 2021 10:00:47 +0200 Subject: [PATCH 3/4] Update IndexedMap docs --- packages/storage-plus/README.md | 40 +++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/packages/storage-plus/README.md b/packages/storage-plus/README.md index 973939ad0..828ab99ae 100644 --- a/packages/storage-plus/README.md +++ b/packages/storage-plus/README.md @@ -394,7 +394,7 @@ Let's use it to illustrate `IndexedMap` definition and usage. ```rust pub struct TokenIndexes<'a> { // pk goes to second tuple element - pub owner: MultiIndex<'a, (Vec, Vec), TokenInfo>, + pub owner: MultiIndex<'a, (Addr, Vec), TokenInfo>, } impl<'a> IndexList for TokenIndexes<'a> { @@ -407,7 +407,7 @@ impl<'a> IndexList for TokenIndexes<'a> { pub fn tokens<'a>() -> IndexedMap<'a, &'a str, TokenInfo, TokenIndexes<'a>> { let indexes = TokenIndexes { owner: MultiIndex::new( - |d, k| (Vec::from(d.owner.as_ref()), k), + |d, k| (d.owner.clone(), k), "tokens", "tokens__owner", ), @@ -420,7 +420,7 @@ Let's discuss this piece by piece: ```rust pub struct TokenIndexes<'a> { // pk goes to second tuple element - pub owner: MultiIndex<'a, (Vec, Vec), TokenInfo>, + pub owner: MultiIndex<'a, (Addr, Vec), TokenInfo>, } ``` @@ -443,11 +443,11 @@ You can see this in the token creation code: (Incidentally, this is using `update` instead of `save`, to avoid overwriting an already existing token). Then, it will be indexed by token `owner` (which is an `Addr`), so that we can list all the tokens an owner has. -That's why the `owner` index key is `(Vec, Vec)`. The first owned element is the `owner` data -(converted to `Vec`), whereas the second one is the `token_id` (also converted to `Vec`). +That's why the `owner` index key is `(Addr, Vec)`. The first owned element is the `owner` data +, whereas the second one is the `token_id` (converted internally to `Vec`). The important thing here is that the key (and its components, in the case of a combined key) must implement -the `PrimaryKey` trait. You can see that both the 2-tuple `(_, _)` and `Vec` do implement `PrimaryKey`: +the `PrimaryKey` trait. You can see that the 2-tuple `(_, _)`, `Addr`, and `Vec` do implement `PrimaryKey`: ```rust impl<'a, T: PrimaryKey<'a> + Prefixer<'a>, U: PrimaryKey<'a>> PrimaryKey<'a> for (T, U) { @@ -462,6 +462,18 @@ impl<'a, T: PrimaryKey<'a> + Prefixer<'a>, U: PrimaryKey<'a>> PrimaryKey<'a> for } ``` +```rust +impl<'a> PrimaryKey<'a> for Addr { + type Prefix = (); + type SubPrefix = (); + + fn key(&self) -> Vec<&[u8]> { + // this is simple, we don't add more prefixes + vec![self.as_bytes()] + } +} +``` + ```rust impl<'a> PrimaryKey<'a> for Vec { type Prefix = (); @@ -487,9 +499,9 @@ impl<'a> IndexList for TokenIndexes<'a> { ``` This implements the `IndexList` trait for `TokenIndexes`. -Note: this code is more or less boiler-plate, and needed for the internals. Do not try to customize this, +Note: this code is more or less boiler-plate, and needed for the internals. Do not try to customize this; just return a list of all indexes. -Implementing this trait serves two purposes (which are really one and the same): it allows the indexes +Implementing this trait serves two purposes (which are really one, and the same): it allows the indexes to be queried through `get_indexes`, and, it allows `TokenIndexes` to be treated as an `IndexList`. So that it can be passed as a parameter during `IndexedMap` construction, below: @@ -497,7 +509,7 @@ it can be passed as a parameter during `IndexedMap` construction, below: pub fn tokens<'a>() -> IndexedMap<'a, &'a str, TokenInfo, TokenIndexes<'a>> { let indexes = TokenIndexes { owner: MultiIndex::new( - |d, k| (Vec::from(d.owner.as_ref()), k), + |d, k| (d.owner.clone(), k), "tokens", "tokens__owner", ), @@ -512,17 +524,17 @@ index (es) is (are) created, and then, the `IndexedMap` is created (using `Index During index creation, we must supply an index function per index ```rust owner: MultiIndex::new( - |d, k| (Vec::from(d.owner.as_ref()), k), + |d, k| (d.owner.clone, k), ``` -, which is the one that will take the value and the primary key (which is always in `Vec` form) of the +, which is the one that will take the value, and the primary key (which is always in `Vec` form) of the original map, and create the index key from them. Of course, this requires that the elements required for the index key are present in the value (which makes sense). Besides the index function, we must also supply the namespace of the pk, and the one for the new index. --- -After that, we just create (and return) the `IndexedMap`: +After that, we just create and return the `IndexedMap`: ```rust IndexedMap::new("tokens", indexes) @@ -552,7 +564,7 @@ Notice this uses `prefix()`, explained above in the `Map` section. let res: Result, _> = tokens() .idx .owner - .prefix(Vec::from(owner_addr.as_ref())) + .prefix(owner_addr) .range(deps.storage, start, None, Order::Ascending) .take(limit) .collect(); @@ -568,7 +580,7 @@ Another example that is slightly similar, but returning only the `token_id`s, us .owner .pks( deps.storage, - Vec::from(owner_addr.as_ref()), + owner_addr, start, None, Order::Ascending, From 76ac226b3a647037df85084b51c921705c3313ef Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Thu, 22 Jul 2021 11:53:52 +0200 Subject: [PATCH 4/4] Add explicit types to index function params for clarity --- contracts/cw721-base/src/state.rs | 6 +++++- packages/storage-plus/README.md | 6 +++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/contracts/cw721-base/src/state.rs b/contracts/cw721-base/src/state.rs index e2d09b5f5..35b12032f 100644 --- a/contracts/cw721-base/src/state.rs +++ b/contracts/cw721-base/src/state.rs @@ -65,7 +65,11 @@ impl<'a> IndexList for TokenIndexes<'a> { pub fn tokens<'a>() -> IndexedMap<'a, &'a str, TokenInfo, TokenIndexes<'a>> { let indexes = TokenIndexes { - owner: MultiIndex::new(|d, k| (d.owner.clone(), k), "tokens", "tokens__owner"), + owner: MultiIndex::new( + |d: &TokenInfo, k: Vec| (d.owner.clone(), k), + "tokens", + "tokens__owner", + ), }; IndexedMap::new("tokens", indexes) } diff --git a/packages/storage-plus/README.md b/packages/storage-plus/README.md index 828ab99ae..3b03e78e9 100644 --- a/packages/storage-plus/README.md +++ b/packages/storage-plus/README.md @@ -407,7 +407,7 @@ impl<'a> IndexList for TokenIndexes<'a> { pub fn tokens<'a>() -> IndexedMap<'a, &'a str, TokenInfo, TokenIndexes<'a>> { let indexes = TokenIndexes { owner: MultiIndex::new( - |d, k| (d.owner.clone(), k), + |d: &TokenInfo, k: Vec| (d.owner.clone(), k), "tokens", "tokens__owner", ), @@ -509,7 +509,7 @@ it can be passed as a parameter during `IndexedMap` construction, below: pub fn tokens<'a>() -> IndexedMap<'a, &'a str, TokenInfo, TokenIndexes<'a>> { let indexes = TokenIndexes { owner: MultiIndex::new( - |d, k| (d.owner.clone(), k), + |d: &TokenInfo, k: Vec| (d.owner.clone(), k), "tokens", "tokens__owner", ), @@ -524,7 +524,7 @@ index (es) is (are) created, and then, the `IndexedMap` is created (using `Index During index creation, we must supply an index function per index ```rust owner: MultiIndex::new( - |d, k| (d.owner.clone, k), + |d: &TokenInfo, k: Vec| (d.owner.clone(), k), ``` , which is the one that will take the value, and the primary key (which is always in `Vec` form) of the