Store & Retrieve values with Maps
Last updated
Last updated
Before getting started, make sure you have the following ready:
You are generally familiar with command-line interfaces (CLI).
You have installed Rust and set up your development environment as described in one of the sources below:
We recommend going through both the & the tutorials.
In the , you created a smart contract designed for the storage and retrieval of a singular numerical value. This tutorial demonstrates how to enhance your smart contract to handle a distinct number for each user by employing the Mapping type. The ink! language offers the Mapping type as a means to organize data in key-value pairs. For instance, the code below shows how to map a number to a specific user:
Using the Mapping
data type allows you to maintain a distinct storage value for every key. In this tutorial, every AccountId serves as a unique key that is linked to a singular stored numeric value within my_map
.
Consequently, each user is limited to storing, increasing, and accessing the value tied to their specific AccountId.
Mapping
The initial step involves setting up the mapping between an AccountId
and its corresponding stored value. This requires specifying both the mapping key and the value it is associated with.
The example below demonstrates how to establish a Mapping
and accessing a value:
Setting the Caller of the Contract
In the example provided earlier, you may have observed the usage of the Self::env().caller()
function. This function, which can be invoked at any point within the contract's logic, consistently identifies the entity calling the contract. It's crucial to distinguish between the contract caller and the origin caller. If a contract invoked by a user subsequently triggers another contract, the Self::env().caller()
in this secondary contract refers to the first contract's address, not the initiating user's.
Applying the Contract Caller Information
Understanding the identity of the contract caller is beneficial in various contexts. For instance, Self::env().caller()
can be leveraged to establish an access control mechanism that restricts users from interacting only with their data. Additionally, it can be utilized to record the contract's owner at the time of its deployment, as shown in the following example:
By storing the contract caller under the owner
identifier, you can subsequently develop functions to verify if the present contract caller is the contract's owner.
You are now ready to introduce a storage map to the incrementer
contract.
To add a storage map to the incrementer
contract:
Open a terminal shell on your computer, if needed.
Verify that you are in the incrementer
project folder.
Open the lib.rs
file in a text editor.
Import the Mapping
type.
Add the mapping key from AccountId
to the i32
data type stored as my_map
.
In the new
constructor create a new Mapping
and use that to initialize your contract.
In the default
constructor add a new default Mapping
along with the already defined default value
.
Add a get_mine()
function to read my_map
using the Mapping API's get()
method and return my_map
for the contract caller.
Add a new test to the initialize accounts.
Save your changes and close the file.
Use the test
subcommand and nightly
toolchain to test your work by running the following command:
The command should display output similar to the following to indicate successful test completion:
The final step of the Incrementer
contract involves empowering users to modify their specific values.
This capability can be facilitated through the use of the Mapping API within the smart contract, which grants straightforward access to storage items.
For instance, to replace an existing value for a storage item, you can utilize the Mapping::insert()
function with a previously used key. Moreover, values can be updated by initially retrieving them from storage with Mapping::get()
, followed by applying Mapping::insert()
to input the new value. Should Mapping::get()
find no current value for a specified key, it will return None
.
Given that the Mapping API offers unmediated access to storage, the Mapping::remove()
function can be employed to eliminate the value associated with a particular key from storage.
To add functions for inserting and removing values into the contract:
Open a terminal shell on your computer, if needed.
Verify that you are in the incrementer
project folder.
Open the lib.rs
file in a text editor.
Add an inc_mine()
function that allows the contract caller to get the my_map
storage item and insert an incremented value
into the mapping.
Add a remove_mine()
function that allows the contract caller to clear the my_map
storage item from storage.
Add a new test to verify that the inc_mine()
functions works as expected.
Add a new test to verify that the remove_mine()
functions works as expected.
Check your work using the test
subcommand:
The command should display output similar to the following to indicate successful test completion: