Skip to content

Commit

Permalink
doc: Update Nested Mappings Example (#268)
Browse files Browse the repository at this point in the history
Description
============
- Updated ink! code snippets to conform to v4.
- Removed outdated and unnecessary code sections.
- Removed outdated PackedAllocate comment and
  code snippet.
- Added functions:
  - vote
  - add_proposal
  - get_proposal
  • Loading branch information
0xf333 authored Aug 28, 2023
1 parent c6c5db0 commit a0d3dac
Showing 1 changed file with 51 additions and 27 deletions.
78 changes: 51 additions & 27 deletions docs/intro/ink-vs-solidity.md
Original file line number Diff line number Diff line change
Expand Up @@ -593,23 +593,29 @@ in ink! this _seems_ like it could be represented like so:
#[ink::contract]
mod dao {

#[derive(SpreadAllocate)]
use ink::{
prelude::vec::Vec,
storage::Mapping,
};

#[ink(storage)]
pub struct Proposal {
voted_yes: Mapping<AccountId, bool>,
}

#[ink(storage)]
#[derive(SpreadAllocate)]
pub struct Dao {
proposals: Vec<Proposal>,
is_whitelisted: Mapping<AccountId, bool>,
}

impl Dao{
#[ink(constructor)]
pub fn new(/*...*/) -> Self {
//required for mappings
ink_lang::utils::initialize_contract(|contract| {/*...*/})
pub fn new() -> Self {
Self {
is_whitelisted: Mapping::default(),
proposals: Vec::new(),
}
}
}
}
Expand All @@ -620,55 +626,73 @@ However, this will cause an error due to the nested mapping. [This answer](https
So, as of now, to get around this issue an alternate data structure will need to be used. A data-structure that can be interchanged with the `Mapping` syntax and with minimal additional implementations is the `BTreeMap`. `BTreeMap` is less efficient than `Mapping`, but is an easy workaround until nested mappings are allowed. This will be used in the nested struct. Additional `derive`s will need to be added to be compatible with the #[ink(storage)] struct (see below).

```rust
#![cfg_attr(not(feature = "std"), no_std, no_main)]

#[ink::contract]
mod dao {

use ink_prelude::collections::BTreeMap;
use ink::{
prelude::{
collections::BTreeMap,
vec::Vec,
},
storage::Mapping,
};

#[derive(
scale::Encode,
scale::Decode,
SpreadLayout,
PackedLayout,
SpreadAllocate,
Debug,
)]
#[cfg_attr(
feature = "std",
derive(scale_info::TypeInfo, ink_storage::traits::StorageLayout)
derive(scale_info::TypeInfo)
)]
pub struct Proposal {
voted_yes: BTreeMap<AccountId, bool>,
}

#[ink(storage)]
#[derive(SpreadAllocate)]
#[derive(Debug)]
pub struct Dao {
proposals: Vec<Proposal>,
is_whitelisted: Mapping<AccountId, bool>,
}

impl Dao{
#[ink(constructor)]
pub fn new(/*...*/) -> Self {
//required for mappings
ink_lang::utils::initialize_contract(|contract| {/*...*/})
pub fn new() -> Self {
Self {
is_whitelisted: Mapping::default(),
proposals: Vec::new(),
}
}
}
}
```

This almost works as expected. However, there is still one issue. `SpreadAllocate` (used with `Mapping`) requires that `Vec<Proposal>` implements `PackedAllocate`. To fix this, `Proposal` needs to implement `PackedAllocate`. See [here](https://docs.rs/ink_storage/3.3.1/ink_storage/traits/trait.PackedAllocate.html) for details + examples. See the following for this example:

```rust
use ink_primitives::Key;
#[ink(message)]
pub fn add_proposal(&mut self) {
self.proposals.push(Proposal {
voted_yes: BTreeMap::new(),
});
}

pub struct Proposal {
voted_yes: BTreeMap<AccountId, bool>,
}
#[ink(message)]
pub fn vote(&mut self, proposal_id: u32, vote: bool) {
let proposal = self.proposals
.get_mut(proposal_id as usize)
.unwrap();

proposal.voted_yes
.insert(Self::env().caller(), vote);
}

impl ink_storage::traits::PackedAllocate for Proposal {
fn allocate_packed(&mut self, at: &Key){
PackedAllocate::allocate_packed(&mut *self, at)
#[ink(message)]
pub fn get_proposal(&self, proposal_id: u32) -> BTreeMap<AccountId, bool> {
self.proposals
.get(proposal_id as usize)
.unwrap()
.voted_yes
.clone()
}
}
}
```
Expand Down

0 comments on commit a0d3dac

Please sign in to comment.