Skip to content

Commit

Permalink
Merge pull request #342 from CosmWasm/cw721-token-indexes-refactor
Browse files Browse the repository at this point in the history
Cw721 token indexes refactor
  • Loading branch information
maurolacy committed Jul 22, 2021
2 parents 767ca70 + 76ac226 commit 61def6e
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 23 deletions.
8 changes: 1 addition & 7 deletions contracts/cw721-base/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -513,13 +513,7 @@ fn query_tokens(
let res: Result<Vec<_>, _> = 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?;
Expand Down
4 changes: 2 additions & 2 deletions contracts/cw721-base/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ pub fn increment_tokens(storage: &mut dyn Storage) -> StdResult<u64> {

pub struct TokenIndexes<'a> {
// pk goes to second tuple element
pub owner: MultiIndex<'a, (Vec<u8>, Vec<u8>), TokenInfo>,
pub owner: MultiIndex<'a, (Addr, Vec<u8>), TokenInfo>,
}

impl<'a> IndexList<TokenInfo> for TokenIndexes<'a> {
Expand All @@ -66,7 +66,7 @@ impl<'a> IndexList<TokenInfo> 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: &TokenInfo, k: Vec<u8>| (d.owner.clone(), k),
"tokens",
"tokens__owner",
),
Expand Down
40 changes: 26 additions & 14 deletions packages/storage-plus/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<u8>, Vec<u8>), TokenInfo>,
pub owner: MultiIndex<'a, (Addr, Vec<u8>), TokenInfo>,
}

impl<'a> IndexList<TokenInfo> for TokenIndexes<'a> {
Expand All @@ -407,7 +407,7 @@ impl<'a> IndexList<TokenInfo> 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: &TokenInfo, k: Vec<u8>| (d.owner.clone(), k),
"tokens",
"tokens__owner",
),
Expand All @@ -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<u8>, Vec<u8>), TokenInfo>,
pub owner: MultiIndex<'a, (Addr, Vec<u8>), TokenInfo>,
}
```

Expand All @@ -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<u8>, Vec<u8>)`. The first owned element is the `owner` data
(converted to `Vec<u8>`), whereas the second one is the `token_id` (also converted to `Vec<u8>`).
That's why the `owner` index key is `(Addr, Vec<u8>)`. The first owned element is the `owner` data
, whereas the second one is the `token_id` (converted internally to `Vec<u8>`).

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<u8>` do implement `PrimaryKey`:
the `PrimaryKey` trait. You can see that the 2-tuple `(_, _)`, `Addr`, and `Vec<u8>` do implement `PrimaryKey`:

```rust
impl<'a, T: PrimaryKey<'a> + Prefixer<'a>, U: PrimaryKey<'a>> PrimaryKey<'a> for (T, U) {
Expand All @@ -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<u8> {
type Prefix = ();
Expand All @@ -487,17 +499,17 @@ impl<'a> IndexList<TokenInfo> 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:

```rust
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: &TokenInfo, k: Vec<u8>| (d.owner.clone(), k),
"tokens",
"tokens__owner",
),
Expand All @@ -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: &TokenInfo, k: Vec<u8>| (d.owner.clone(), k),
```

, which is the one that will take the value and the primary key (which is always in `Vec<u8>` form) of the
, which is the one that will take the value, and the primary key (which is always in `Vec<u8>` 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)
Expand Down Expand Up @@ -552,7 +564,7 @@ Notice this uses `prefix()`, explained above in the `Map` section.
let res: Result<Vec<_>, _> = tokens()
.idx
.owner
.prefix(Vec::from(owner_addr.as_ref()))
.prefix(owner_addr)
.range(deps.storage, start, None, Order::Ascending)
.take(limit)
.collect();
Expand All @@ -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,
Expand Down
17 changes: 17 additions & 0 deletions packages/storage-plus/src/keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T> {
type Prefix = ();
Expand Down

0 comments on commit 61def6e

Please sign in to comment.