diff --git a/static/img/kv.svg b/static/img/kv.svg
index d9ce02507b..22a136c609 100644
--- a/static/img/kv.svg
+++ b/static/img/kv.svg
@@ -1,7 +1,56 @@
-
-
\ No newline at end of file
+
+
+
+ Key / Value Database- Arbitrary key length- SCALE encoded values
+
+
+
diff --git a/versioned_docs/version-4.0.0-alpha.1/datastructures/custom.md b/versioned_docs/version-4.0.0-alpha.1/datastructures/custom.md
index 3b7eec23f7..39943dcbf8 100644
--- a/versioned_docs/version-4.0.0-alpha.1/datastructures/custom.md
+++ b/versioned_docs/version-4.0.0-alpha.1/datastructures/custom.md
@@ -3,48 +3,139 @@ title: Custom Data Structures
slug: /datastructures/custom-datastructure
---
-:::caution
-TODO
+The `ink_storage` crate provides useful utilities and data structures to organize and
+manipulate the contract's storage. However, contract authors should know that they can
+also create their own custom data structures.
-Beware, this page is no longer up to date for 4.0!
-:::
+## Using custom types on storage
+Any custom type wanting to be compatible with ink! storage must implement the
+[`Storable`](https://docs.rs/ink_storage_traits/4.0.0-beta/ink_storage_traits/trait.Storable.html)
+trait, so it can be SCALE
+[`encoded`](https://docs.rs/parity-scale-codec/3.2.2/parity_scale_codec/trait.Encode.html)
+and
+[`decoded`](https://docs.rs/parity-scale-codec/3.2.2/parity_scale_codec/trait.Decode.html).
+Additionally, the traits
+[`StorageLayout`](https://docs.rs/ink_storage/latest/ink_storage/traits/trait.StorageLayout.html)
+and [`TypeInfo`](https://docs.rs/scale-info/2.3.1/scale_info/trait.TypeInfo.html)
+are required as well. But don't worry, usually these traits can just be derived:
+
+```rust
+/// A custom type that we can use in our contract storage
+#[derive(scale::Decode, scale::Encode)]
+#[cfg_attr(
+ feature = "std",
+ derive(scale_info::TypeInfo, ink::storage::traits::StorageLayout)
+)]
+pub struct Inner {
+ value: bool,
+}
-While the `ink_storage` crate provides tons of useful utilities and data structures to organize and manipulate the contract's storage contract authors are not limited by its capabilities. By implementing the core `SpreadLayout`/`PackedLayout` traits (and the `StorageLayout` trait for supporting the metadata generated for the `.contract` bundle) users are able to define their very own custom storage data structures with their own set of requirement and features that work along the `ink_storage` data structures as long as they fulfill the mere requirements stated by those two traits.
+#[ink(storage)]
+pub struct ContractStorage {
+ inner: Inner,
+}
+```
-A basic example of a custom struct is shown below:
+Even better: there is a macro
+[`#[ink::storage_item`](https://docs.rs/ink_macro/4.0.0-beta.1/ink_macro/attr.storage_item.html),
+which derives all necessary traits for you. If there is no need to implement any special
+behaviour, the above code example can be simplified further as follows:
-``` rust
-struct Inner {
- value: bool
+```rust
+/// A custom type that we can use in our contract storage
+#[ink::storage_item]
+pub struct Inner {
+ value: bool,
}
#[ink(storage)]
-pub struct MyContract {
- inner: Inner
+pub struct ContractStorage {
+ inner: Inner,
}
```
-Compiling the above will result in errors. While having an inner struct which holds only a boolean might not be the best idea, it serves well to illustrate how to implement the trait:
+Naturally, you can as well implement any required trait manually. Please directly refer to
+the relevant trait documentations for more information.
+
+:::note
+
+The `#[ink::storage_item]` macro is responsible for storage key calculation of
+non-[`Packed`](https://docs.rs/ink_storage_traits/4.0.0-beta.1/ink_storage_traits/trait.Packed.html)
+types. Without it, the key for non-`Packed` fields will be zero. Using this macro is
+necessary if you don't plan to use a
+[`ManualKey`](https://docs.rs/ink_storage_traits/4.0.0-beta.1/ink_storage_traits/struct.ManualKey.html)
+on a non-`Packed` type.
+
+Types with custom implementations of the ink! storage traits can still use this macro only
+for key calculation by disabling the derives: `#[ink::storage_item(derive = false)]`.
+
+:::
+
+## Generic storage fields
+
+It is possible to use generic data types in your storage, as long as any generic type
+satisfies the required storage trait bounds. In fact, we already witnessed this in the
+previous sections about the
+[`Mapping`](https://docs.rs/ink_storage/4.0.0-beta.1/ink_storage/struct.Mapping.html).
-``` rust
-impl SpreadLayout for Inner {
- const FOOTPRINT: u64 = 1;
+Let's say you want a mapping where accessing a non-existent key should just return
+it's default value, akin to how mappings work in Solidity. Additionally, you want to know
+how many values there are in the mapping (its length). This could be implemented as a
+thin wrapper around the ink! `Mapping` as follows:
- fn pull_spread(ptr: &mut KeyPtr) -> Self {
- Self {
- value: SpreadLayout::pull_spread(ptr),
+```rust
+/// Values for this map need to implement the `Default` trait.
+/// Naturally, they also must be compatible with contract storage.
+/// Note that the underlying `Mapping` type only supports `Packed` values.
+#[ink::storage_item]
+pub struct DefaultMap {
+ values: Mapping,
+ length: u32,
+}
+
+impl DefaultMap {
+ /// Accessing non-existent keys will return the default value.
+ pub fn get(&self, key: &K) -> V {
+ self.values.get(key).unwrap_or_default()
+ }
+
+ /// Inserting into the map increases its length by one.
+ pub fn set(&mut self, key: I, value: &U)
+ where
+ I: scale::EncodeLike,
+ E: scale::EncodeLike + Storable,
+ {
+ if self.values.insert(key, value).is_none() {
+ self.length += 1
}
}
- fn push_spread(&self, ptr: &mut KeyPtr) {
- SpreadLayout::push_spread(&self.value, ptr);
+ /// Removing a value from the map decreases its length by one.
+ pub fn remove(&mut self, key: &K) {
+ if self.values.take(key).is_some() {
+ self.length -= 1
+ }
}
- fn clear_spread(&self, ptr: &mut KeyPtr) {
- SpreadLayout::clear_spread(&self.value, ptr);
+ /// Return how many values the mapping contains
+ pub fn len(&self) -> u32 {
+ self.length
}
}
+/// `DefaultMap` is compatible with contract storage.
+#[ink(storage)]
+pub struct MyContract {
+ my_map: DefaultMap,
+}
```
-You can check what each method does in the [trait's docs](https://docs.rs/ink_storage/4.0.0-beta/ink_storage/traits/trait.SpreadLayout.html). Check how some data structures are implemented, such as [Mapping](https://docs.rs/ink_storage/4.0.0-beta/src/ink_storage/lazy/mapping.rs.html#113).
+:::caution
+
+Generic data types may substantially increase your contracts overall code size, making it
+more costly to store on-chain.
+
+The reason for this is [Rust's monomorphization](https://rustwasm.github.io/twiggy/concepts/generic-functions-and-monomorphization.html).
+
+:::
+
diff --git a/versioned_docs/version-4.0.0-alpha.1/datastructures/mapping.md b/versioned_docs/version-4.0.0-alpha.1/datastructures/mapping.md
index 4b759dd94d..ceaf4043bf 100644
--- a/versioned_docs/version-4.0.0-alpha.1/datastructures/mapping.md
+++ b/versioned_docs/version-4.0.0-alpha.1/datastructures/mapping.md
@@ -1,78 +1,131 @@
---
-title: Working with Mapping
+title: Working with Mapping
slug: /datastructures/mapping
---
-:::caution
-TODO
+In this section we demonstrate how to work with ink! [`Mapping`](https://docs.rs/ink_storage/4.0.0-beta.1/ink_storage/struct.Mapping.html).
-Beware, this page is no longer up to date for 4.0!
-:::
-
-In this section we want to demonstrate how to work with ink! [`Mapping`](https://docs.rs/ink_storage/4.0.0-beta/ink_storage/struct.Mapping.html).
-
-Here is an example of a mapping from a user to a number:
+Here is an example of a mapping from a user to a `Balance`:
```rust
#[ink(storage)]
-#[derive(SpreadAllocate)]
pub struct MyContract {
- // Store a mapping from AccountIds to a u32
- map: ink_storage::Mapping,
+ /// Assign a balance to every account.
+ balances: ink::storage::Mapping,
}
```
This means that for a given key, you can store a unique instance of a value type. In this
-case, each "user" gets their own number.
-
-## Initializing a Mapping
+case, each "user" gets credited their own balance.
-In order to correctly initialize a `Mapping` we need two things:
-1. An implementation of the [`SpreadAllocate`](https://docs.rs/ink_storage/4.0.0-beta/ink_storage/traits/trait.SpreadAllocate.html) trait on our storage struct
-2. The [`ink_lang::utils::initalize_contract`](https://docs.rs/ink_lang/4.0.0-beta/ink_lang/utils/fn.initialize_contract.html) initializer
+## Example: Using a `Mapping`
-Not initializing storage before you use it is a common mistake that can break your smart
-contract. If you do not initialize your `Mapping`'s correctly you may end up with
-different `Mapping`'s operating on the same set of storage entries 😱.
+The following example contract utilizes a `Mapping` so that anyone can deposit and withdraw
+balance for their own account:
```rust
#![cfg_attr(not(feature = "std"), no_std)]
#[ink::contract]
mod mycontract {
- use ink_storage::traits::SpreadAllocate;
+ use ink::storage::Mapping;
#[ink(storage)]
- #[derive(SpreadAllocate)]
pub struct MyContract {
- // Store a mapping from AccountIds to a u32
- map: ink_storage::Mapping,
+ /// Assign a balance to every account ID
+ balances: Mapping,
}
impl MyContract {
- #[ink(constructor)]
- pub fn new(count: u32) -> Self {
- // This call is required in order to correctly initialize the
- // `Mapping`s of our contract.
- ink_lang::utils::initialize_contract(|contract: &mut Self| {
- let caller = Self::env().caller();
- contract.map.insert(&caller, &count);
- })
+ /// Constructor to initialize the contract with an empty mapping.
+ #[ink(constructor, payable)]
+ pub fn new() -> Self {
+ let balances = Mapping::default();
+ Self { balances }
}
- #[ink(constructor)]
- pub fn default() -> Self {
- // Even though we're not explicitly initializing the `Mapping`,
- // we still need to call this
- ink_lang::utils::initialize_contract(|_| {})
+ /// Retrieve the balance of the caller.
+ #[ink(message)]
+ pub fn get_balance(&self) -> Option {
+ let caller = self.env().caller();
+ self.balances.get(caller)
}
- // Grab the number at the caller's AccountID, if it exists
- #[ink(message)]
- pub fn get(&self) -> u32 {
- let caller = Self::env().caller();
- self.map.get(&caller).unwrap_or_default()
+ /// Credit more money to the contract.
+ #[ink(message, payable)]
+ pub fn transfer(&mut self) {
+ let caller = self.env().caller();
+ let balance = self.balances.get(caller).unwrap_or(0);
+ let endowment = self.env().transferred_value();
+ self.balances.insert(caller, &(balance + endowment));
+ }
+
+ /// Withdraw all your balance from the contract.
+ pub fn withdraw(&mut self) {
+ let caller = self.env().caller();
+ let balance = self.balances.get(caller).unwrap();
+ self.balances.remove(caller);
+ self.env().transfer(caller, balance).unwrap()
}
}
}
+
+```
+
+## Considerations when using the `Mapping` type
+
+One of the main purposes of the ink! `Mapping` is to allow storing a lot of values.
+
+:::note
+
+There are many additional datastructures accessible under `ink::prelude::collections`, such
+such as `HashMap` or `BTreeMap` (to name a few). Note that these datastructures all exhibit
+`Packed` storage loading behavior, as opposed to the ink! `Mapping`!
+
+:::
+
+### Storage loading behaviour
+
+Each `Mapping` value lives under it's own storage key. Briefly, this means that `Mapping`s
+are lazily loaded in ink!. In other words, if your message only accesses a single key of a
+mapping, it will not load the whole mapping but only the value being accessed.
+
+```rust
+// This causes only a single storage access and the decoding of a single "MyValue" struct,
+// no matter how many elements there are inside the mapping.
+let foo: MyValue = my_mapping.get(0)?;
+
+for n in 0..5 {
+ // This causes a storage access and a decoding operation for each loop iteration.
+ // It is not possible to "fetch" all key/value pairs directly at once.
+ let bar: MyValue = my_mapping.get(n)?;
+}
+```
+
+Furthermore, it follows that mapping values do not have a contiguous storage layout and it is
+not possible to iterate over the contents of a map.
+
+
+### Updating values
+
+The attentive reader may have noticed that accessing mapping values via the `Mapping::get()`
+method will result in an owned value (a local copy), as opposed to a direct reference
+into the storage. Changes to this value won't be reflected in the contracts storage
+"automatically". To avoid this common pitfall, the value must be inserted again at the same
+key after it was modified. The `transfer` function from above example illustrates this:
+
+```rust
+pub fn transfer(&mut self) {
+ let caller = self.env().caller();
+ // `balance` is a local value and not a reference to the value on storage!
+ let balance = self.balances.get(caller).unwrap_or(0);
+ let endowment = self.env().transferred_value();
+ // The following line of code would have no effect to the balance of the
+ // caller stored in contract storage:
+ //
+ // balance += endowment;
+ //
+ // Instead, we use the `insert` function to write it back like so:
+ self.balances.insert(caller, &(balance + endowment));
+}
```
diff --git a/versioned_docs/version-4.0.0-alpha.1/datastructures/overview.md b/versioned_docs/version-4.0.0-alpha.1/datastructures/overview.md
index 42fda8ace9..f92c4154a1 100644
--- a/versioned_docs/version-4.0.0-alpha.1/datastructures/overview.md
+++ b/versioned_docs/version-4.0.0-alpha.1/datastructures/overview.md
@@ -3,46 +3,25 @@ title: Overview
slug: /datastructures/overview
---
-The `ink_storage` crate acts as the standard storage library for ink! smart contracts. At
-the moment it only provides a single low-level primitive for interacting with storage,
-the [`Mapping`](https://docs.rs/ink_storage/4.0.0-beta/ink_storage/struct.Mapping.html).
-
-The `Mapping` is a mapping of key-value pairs directly to the contract storage. Its main advantage
-is to be simple and lightweight. As such, it does not provide any high-level
-functionality, such as iteration or automatic clean-up. Smart contract authors will need
-to implement any high level functionality themselves.
-
-## Eager Loading
-
-When executing a contract, all the fields of the `#[ink(storage)]` struct will be pulled
-from storage, regardless of whether or not they are used during the message execution.
-
-Smart contract authors should be aware of this behaviour since it could potentially
-affect their contract performance. For example, consider the following storage struct:
-
-```rust
-#[ink(storage)]
-pub struct EagerLoading {
- a: i32,
- b: ink_prelude::vec::Vec,
-}
-
-impl EagerLoading {
- #[ink(message)]
- pub fn read_a(&self) {
- let a = self.a;
- }
-}
-```
-
-In `EagerLoading::read_a()` we only read the `a` storage item. However, the `b` storage
-item will still be loaded from storage. As a reminder, this means accessing the
-underlying database and SCALE decoding the value. This can incur high costs, especially
-as the number of elements in `b` grows.
-
-:::note
-
-Eager loading does **not** apply to `Mapping` fields, though, as key lookups in mappings
-are done directly from contract storage.
-
-:::
+The `ink_storage` crate acts as the standard storage library for ink! smart contracts.
+At the moment it provides two primitives for interacting with storage,
+[`Mapping`](https://docs.rs/ink_storage/4.0.0-beta.1/ink_storage/struct.Mapping.html)
+and [`Lazy`](https://docs.rs/ink_storage/4.0.0-beta.1/ink_storage/struct.Lazy.html).
+
+`Mapping` is a mapping of key-value pairs directly to the contract storage. It is very
+similar to traditional hash tables and comparable to the `mapping` type Solidity offers.
+As a core ingredient to the ink! language, its main advantage is being simple and
+lightweight: It favors being efficient in terms of gas costs and code size
+over providing a lot of high-level functionality found in other implementations
+like the `ink::prelude::collections::HashMap` type.
+Overall, the ink! `Mapping` will be solid choice for most contracts. Moreover, smart
+contracts developers can implement advanced features themselves.
+
+`Lazy` is a wrapper type that can be used over any other storage compatible type.
+This allows smart contract developers fine grained manual control over the layout of
+the contract storage by assigning a separate storage cell for the field. For example,
+it can be used to prevent the contract from eagerly loading large storage fields
+during each contract call.
+Conceivably, it may be desirable to change certain aspects on how your contract deals with
+its storage variables. You can find out more about this in the section about the ink!
+[Storage Layout](https://use.ink/versioned_docs/version-4.0.0-alpha.1/datastructures/storage-layout).
diff --git a/versioned_docs/version-4.0.0-alpha.1/datastructures/spread-storage-layout.md b/versioned_docs/version-4.0.0-alpha.1/datastructures/spread-storage-layout.md
deleted file mode 100644
index 0bdadb7d3f..0000000000
--- a/versioned_docs/version-4.0.0-alpha.1/datastructures/spread-storage-layout.md
+++ /dev/null
@@ -1,51 +0,0 @@
----
-title: Spread Storage Layout
-slug: /datastructures/spread-storage-layout
----
-
-:::caution
-TODO
-
-Beware, this page is no longer up to date for 4.0!
-:::
-
-### Storage Organization
-
-The following schema depicts the storage which is exposed
-to ink! by the contracts pallet:
-
-
-
-
-
-ink!'s storage operates by storing and loading entries into and from a single storage
-cell. At the moment there is no way to customize this behaviour. Depending on the data
-we're dealing with, this can end up being good or bad.
-
-For example, if we have a somewhat small `ink_prelude::vec::Vec` loading all the elements
-at the same time can be advantegous - especially if we expect our message to interact
-with most of them in a single call.
-
-On the other hand, this can be problematic if we're loading a large `Vec` and only
-operating on a few elements.
-
-### Spreading
-
-ink! spreads information to as many cells as possible. For example if you have the
-following `#[ink(storage)]` struct every field will live in its own single storage cell.
-Note that for `b` all 32 bytes will share the same cell!
-
-```rust
-#[ink(storage)]
-pub struct Spread {
- a: i32,
- b: [u8; 32],
-}
-```
-
-The following schema depicts the storage layout for a vector with three elements,
-persisted to storage in a spread layout.
-
-
-
-
diff --git a/versioned_docs/version-4.0.0-alpha.1/datastructures/storage-in-metadata.md b/versioned_docs/version-4.0.0-alpha.1/datastructures/storage-in-metadata.md
index e48ff955f9..c15582d4c1 100644
--- a/versioned_docs/version-4.0.0-alpha.1/datastructures/storage-in-metadata.md
+++ b/versioned_docs/version-4.0.0-alpha.1/datastructures/storage-in-metadata.md
@@ -3,6 +3,211 @@ title: Metadata Format
slug: /datastructures/storage-in-metadata
---
-:::caution
-TODO: Add documentation on the `storage ` field in the metadata.
-:::
+The storage layout of a contract is reflected inside the metadata. It allows third-party
+tooling to work with contract storage and can also help to better understand the storage
+layout of any given contract.
+
+Given a contract with the following storage:
+
+```rust
+#[ink(storage)]
+pub struct MyContract {
+ balance: Balance,
+ block: BlockNumber,
+ lazy: Lazy,
+}
+```
+
+The storage will be reflected inside the metadata as like follows:
+
+```json
+"root": {
+ "layout": {
+ "struct": {
+ "fields": [
+ {
+ "layout": {
+ "leaf": {
+ "key": "0x00000000",
+ "ty": 0
+ }
+ },
+ "name": "balance"
+ },
+ {
+ "layout": {
+ "leaf": {
+ "key": "0x00000000",
+ "ty": 1
+ }
+ },
+ "name": "block"
+ },
+ {
+ "layout": {
+ "root": {
+ "layout": {
+ "leaf": {
+ "key": "0xb1f4904e",
+ "ty": 2
+ }
+ },
+ "root_key": "0xb1f4904e"
+ }
+ },
+ "name": "lazy"
+ }
+ ],
+ "name": "MyContract"
+ }
+ },
+ "root_key": "0x00000000"
+}
+```
+
+We observe that the storage layout is represented as a tree, where tangible storage values
+end up inside a `leaf`. Because of
+[`Packed`](https://docs.rs/ink_storage_traits/4.0.0-beta.1/ink_storage_traits/trait.Packed.html)
+encoding, leafs can share the same storage key, and
+in order to reach them you'd need fetch and decode the whole storage cell under this key.
+
+A `root_key` is meant to either be used to directly access a `Packed` storage field
+or to serve as the base key for calculating the actual keys needed to access values in
+non-`Packed` fields (such as `Mapping`s).
+
+## Storage key calculation for ink! `Mapping` values
+
+Base storage keys are always 4 bytes in size. However, the storage API of the contracts
+pallet supports keys of arbitrary length. In order to reach a mapping value, the storage
+key of said value is calculated at runtime.
+
+The formula to calculate the base storage key `S` used to access a mapping value under the
+key `K` for a mapping with base key `B` can be expressed as follows:
+
+```
+S = B + scale::encode(K)
+```
+
+In words, find the base (root) key of the mapping and concatenate it with the
+SCALE encoded key of the mapped value to obtain the actual storage key used to
+access the mapped value.
+
+## Accessing storage items with the `contractsApi` runtime call API
+
+There are two ways to query for storage fields of smart contracts from outside a contract.
+Both methods are accessible via the [`polkadot-js`](https://polkadot.js.org/apps/) web UI.
+
+The straight forward way to query a contracts storage is via a
+[`runtime API`](https://polkadot.js.org/apps/#/runtime) call, using the `contractsApi`
+endpoint provided by the contracts pallet. The endpoint provides a `getStorage` method,
+which just expects a contract address and a storage key as arguments.
+
+For example, to access the root storage struct under the key `0x00000000` of a contract,
+just specify the contract's address and the storage key `0x00000000` as-is. The API call
+will return the scale-encoded root storage struct of the contract.
+
+## Accessing storage items with the `childState` RPC call API
+
+Under the hood, each contract gets its own
+[child trie](https://paritytech.github.io/substrate/master/frame_support/storage/child/index.html), where its storage items are actually stored.
+
+Additionally, the contracts pallet uses the
+[`Blake2 128 Concat`](https://paritytech.github.io/substrate/master/frame_support/struct.Blake2_128Concat.html)
+[`Transparent hashing algorithm`](https://docs.substrate.io/build/runtime-storage/#transparent-hashing-algorithms)
+to calculate storage keys for any stored item inside the child trie.
+You'll need to account for that as well.
+
+With that in mind, to directly access storage items of any on-chain contract using a
+childState [`RPC call`](https://polkadot.js.org/apps/#/rpc), you'll need the following:
+- The child trie ID of the contract, represented as a [`PrefixedStorageKey`](https://docs.rs/sp-storage/10.0.0/sp_storage/struct.PrefixedStorageKey.html)
+- The hashed storage key of the storage field
+
+### Finding the contracts child trie ID
+
+The child trie ID is the `Blake2_256` hash of the contracts instantiation nonce
+concatenated to it's `AccountId`. You can find it in
+[`polkadot-js chainstate query interface`](https://polkadot.js.org/apps/#/chainstate),
+where you need to execute the `contracts_contractInfoOf` state query.
+
+It can also be calculate manually according to the following code snippet. The
+instantiation note of the contract must be still be known. You can get it using the
+`contracts_nonce` chain state query in polkadot-js UI.
+
+```rust
+use sp_core::crypto::Ss58Codec;
+use parity_scale_codec::Encode;
+
+// Given our contract ID is 5DjcHxSfjAgCTSF9mp6wQBJWBgj9h8uh57c7TNx1mL5hdQp4
+let account: AccountId32 =
+ Ss58Codec::from_string("5DjcHxSfjAgCTSF9mp6wQBJWBgj9h8uh57c7TNx1mL5hdQp4").unwrap();
+// Given our instantiation nonce was 1
+let nonce: u64 = 1;
+
+// The child trie ID can be calculated as follows:
+let trie_id = (&account, nonce).using_encoded(Blake2_256::hash);
+```
+
+### Calculate the `PrefixedStorageKey` from the child trie ID
+A `PrefixedStorageKey` based on the child trie ID can be constructed using the `ChildInfo`
+primitive as follows:
+
+```rust
+use sp_core::storage::ChildInfo;
+let prefixed_storage_key = ChildInfo::new_default(&trie_id).into_prefixed_storage_key();
+```
+
+### Calculate the storage key using transparent hashing
+
+Finally, we calculate the hashed storage key of the storage item we are wanting to access.
+The algorithm is simple: `Blake2_128` hash the storage key and then concatenate the unhashed
+key to the hash. Given you want to access the storage item under the `0x00000000`,
+it will look like this in code:
+
+```rust
+use frame_support::Blake2_128Concat;
+
+// The base key is 0x00000000
+let storage_key = Blake2_128Concat::hash(&[0, 0, 0, 0]);
+```
+
+### A full example
+
+Let's recap the last few paragraphs into a full example. Given:
+
+* A contract at address `5DjcHxSfjAgCTSF9mp6wQBJWBgj9h8uh57c7TNx1mL5hdQp4`
+* With instantiation nonce of `1`
+* The root storage struct is to be found at base key `0x00000000`
+
+The following Rust program demonstrates how to calculate the `PrefixedStorageKey` of the
+contracts child trie, as well as the hashed key for the storage struct, which can then be
+used with the `chilstate` RPC endpoint function `getStorage` in polkadot-js to receive
+the root storage struct of the contract:
+
+```rust
+use frame_support::{sp_runtime::AccountId32, Blake2_128Concat, Blake2_256, StorageHasher};
+use parity_scale_codec::Encode;
+use sp_core::{crypto::Ss58Codec, storage::ChildInfo};
+use std::ops::Deref;
+
+fn main() {
+ // Find the child storage trie ID
+ let account_id = "5DjcHxSfjAgCTSF9mp6wQBJWBgj9h8uh57c7TNx1mL5hdQp4";
+ let account: AccountId32 = Ss58Codec::from_string(account_id).unwrap();
+ let instantiation_nonce = 1u64;
+ let trie_id = (account, instantiation_nonce).using_encoded(Blake2_256::hash);
+ assert_eq!(
+ hex::encode(trie_id),
+ "2fa252b7f996d28fd5d8b11098c09e56295eaf564299c6974421aa5ed887803b"
+ );
+
+ // Calculate the PrefixedStorageKey based on the trie ID
+ let prefixed_storage_key = ChildInfo::new_default(&trie_id).into_prefixed_storage_key();
+ println!("0x{}", hex::encode(prefixed_storage_key.deref()));
+ // 0x3a6368696c645f73746f726167653a64656661756c743a2fa252b7f996d28fd5d8b11098c09e56295eaf564299c6974421aa5ed887803b
+
+ // Calculate the storage key using transparent hashing
+ let storage_key = Blake2_128Concat::hash(&[0, 0, 0, 0]);
+ println!("0x{}", hex::encode(&storage_key));
+ // 0x11d2df4e979aa105cf552e9544ebd2b500000000
+}
+```
diff --git a/versioned_docs/version-4.0.0-alpha.1/datastructures/storage-layout.md b/versioned_docs/version-4.0.0-alpha.1/datastructures/storage-layout.md
new file mode 100644
index 0000000000..79733371bc
--- /dev/null
+++ b/versioned_docs/version-4.0.0-alpha.1/datastructures/storage-layout.md
@@ -0,0 +1,159 @@
+---
+title: Storage Layout
+slug: /datastructures/storage-layout
+---
+
+Smart contract authors are given some flexibility in regards on how they want to organize
+the storage layout of their contracts.
+Let's dive deeper into the concepts behind ink! storage to get a better understanding
+of some of its implications and limitations.
+
+## Storage Organization
+
+The following schema depicts the storage which is exposed
+to ink! by the contracts pallet:
+
+
+
+
+
+Storage data is always encoded with the
+[`SCALE`](https://docs.substrate.io/reference/scale-codec/) codec.
+The storage API operates by storing and loading entries into and from a single storages
+cells, where each storage cell is accessed under its own dedicated storage key. To some
+extent, the storage API works similar to a traditional key-value database.
+
+## Packed vs Non-Packed layout
+
+Types that can be stored entirely under a single storage cell are considered
+[`Packed`](https://docs.rs/ink_storage_traits/4.0.0-beta.1/ink_storage_traits/trait.Packed.html).
+By default, ink! tries to store all storage struct fields under a single storage cell.
+Consequentially, with a `Packed` storage layout, any message interacting with the contract
+storage will always need to operate on the entire contract storage struct.
+
+For example, if we have a somewhat small contract storage struct consisting of only a few
+tiny fields, pulling everything from the storage inside every message is not
+problematic. It may even be advantageous - especially if we expect most messages to
+interact with most of the storage fields.
+
+On the other hand, this can get problematic if we're storing a large `ink::prelude::vec::Vec`
+in the contract storage but provide messages that do not need to read and write from this
+`Vec`. In that scenario, each and every contract message bears runtime overhead by dealing
+with that `Vec`, regardless whether they access it or not. This results in extra gas costs.
+To solve this problem we need to turn our storage into a non-packed layout somehow.
+
+:::caution
+
+If any type exhibiting `Packed` layout gets large enough (an ever growing `Vec` might be
+a prime candidate for this), it will break your contract.
+This is because for encoding and decoding storage items, there is a buffer with only limited
+capacity (around 16KB in the default configuration) available. This means any contract
+trying to decode more than that will trap! If you are unsure about the potential size a
+datastructure might get, consider using an ink! `Mapping`, which can store an arbitrary
+number of elements, instead.
+
+:::
+
+## Eager Loading vs. Lazy Loading
+ink! provides means of breaking the storage up into smaller pieces, which can be loaded
+on demand, with the
+[`Lazy`](https://paritytech.github.io/ink/ink/storage/struct.Lazy.html) primitive.
+Wrapping any storage field inside a `Lazy` struct makes the storage
+struct in which that field appears also
+non-`Packed`, preventing it from being eagerly loaded during arbitrary storage operations.
+
+The following example demonstrates how we can solve the problem introduced in the above
+section. You'll notice that for the lazily loaded storage field, we now work with getters
+and setters to access and modify the underlying storage value:
+
+```rust
+#![cfg_attr(not(feature = "std"), no_std)]
+
+#[ink::contract]
+mod mycontract {
+ use ink::prelude::vec::Vec;
+ use ink::storage::Lazy;
+
+ #[derive(Default)]
+ #[ink(storage)]
+ pub struct MyContract {
+ tiny_value: Balance,
+ /// This vector might get large and expensive to work with.
+ /// We want to enforce a non-`Packed` storage layout.
+ large_vec: Lazy>,
+ }
+
+ impl MyContract {
+ #[ink(constructor)]
+ pub fn new() -> Self {
+ Self::default()
+ }
+
+ /// Because `large_vec` is loaded lazily, this message is always cheap.
+ #[ink(message)]
+ pub fn get_balance(&self) -> Balance {
+ self.tiny_value
+ }
+
+ /// Lazy fields like `large_vec` provide `get()` and `set()` storage operators.
+ #[ink(message)]
+ pub fn add_balance(&mut self, value: Balance) {
+ let mut balances = self.large_vec.get_or_default();
+ balances.push(value);
+ self.large_vec.set(&balances);
+ }
+ }
+}
+```
+
+:::caution
+
+`ink::prelude::vec::Vec`'s are always loaded in their entirety. This is because all elements
+of the `ink::prelude::vec::Vec` live under a single storage key. Wrapping the
+`ink::prelude::vec::Vec` inside `Lazy`, like the
+provided example above does, has no influence on its inner layout. If you are dealing with
+large or sparse arrays on contract storage, consider using a `Mapping` instead.
+
+:::
+
+## Manual vs. Automatic Key Generation
+
+By default, keys are calculated automatically for you, thanks to the
+[`AutoKey`](https://docs.rs/ink_storage_traits/4.0.0-beta.1/ink_storage_traits/struct.AutoKey.html)
+primitive. They'll be generated at compile time and ruled out for conflicts.
+However, for non-`Packed` types like `Lazy` or the `Mapping`, the
+[`ManualKey`](https://docs.rs/ink_storage_traits/4.0.0-beta.1/ink_storage_traits/struct.ManualKey.html)
+primitive allows manual control over the storage key of a field like so:
+
+```rust
+#[ink(storage)]
+pub struct MyContract {
+ /// The storage key for this field is always `0x0000007f`
+ inner: Lazy>,
+}
+```
+
+This may be advantageous: Your storage key will always stay the same, regardless of
+the version of your contract or ink! itself (note that the key calculation algorithm may
+change with future ink! versions).
+
+:::tip
+
+Using `ManualKey` instead of `AutoKey` might be especially desirable for upgradable
+contracts, as using `AutoKey` might result in a different storage key for the same field
+in a newer version of the contract. This may break your contract after an upgrade 😱!
+
+:::
+
+## Considerations
+
+It might be worthwhile to think about the desired storage layout of your contract. While
+using a `Packed` layout will keep your contracts overall code size smaller, it can cause
+unnecessarily high gas costs. Thus we consider it a good practice to break up large
+or complex storage layouts into reasonably sized distinct storage cells.
+
+:::note
+
+ink! `Mapping`s are always non-`Packed` and loaded lazily, one key-value pair at the time.
+
+:::
diff --git a/versioned_sidebars/version-4.0.0-alpha.1-sidebars.json b/versioned_sidebars/version-4.0.0-alpha.1-sidebars.json
index 94003fefb8..b48e623014 100644
--- a/versioned_sidebars/version-4.0.0-alpha.1-sidebars.json
+++ b/versioned_sidebars/version-4.0.0-alpha.1-sidebars.json
@@ -59,7 +59,7 @@
"Storage & Data Structures": [
"datastructures/overview",
"datastructures/mapping",
- "datastructures/spread-storage-layout",
+ "datastructures/storage-layout",
"datastructures/custom",
"datastructures/storage-in-metadata"
],
@@ -83,4 +83,4 @@
"brand-assets/cargo-contract"
]
}
-}
+}
\ No newline at end of file