Store & Retrieve values with Maps
Prerequisites
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:
In the Develop the Counter smart contract section, 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.
Initialize a Mapping
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.
Add mapping to the smart contract
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 thei32
data type stored asmy_map
.In the
new
constructor create a newMapping
and use that to initialize your contract.In the
default
constructor add a new defaultMapping
along with the already defined defaultvalue
.Add a
get_mine()
function to readmy_map
using the Mapping API'sget()
method and returnmy_map
for the contract caller.Add a new test to the initialize accounts.
Save your changes and close the file.
Use the
test
subcommand andnightly
toolchain to test your work by running the following command:The command should display output similar to the following to indicate successful test completion:
Enabling Values Modification
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 themy_map
storage item and insert an incrementedvalue
into the mapping.Add a
remove_mine()
function that allows the contract caller to clear themy_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:
Last updated