Address Keys in Mappings¶
If you come from the traditional Development world, then integer keys are nothing particularly interesting probably. It's very much like using an array or a hash map or something similar.
Internal Storage of Mappings
Here's a little advanced detour to how mappings and arrays are stored internally in the EVM.
Array data is located starting at keccak256(p) and it is laid out in the same way as statically-sized array data would: One element after the other, potentially sharing storage slots if the elements are not longer than 16 bytes. Dynamic arrays of dynamic arrays apply this rule recursively.
The value corresponding to a mapping key k is located at keccak256(h(k) . p) where . is concatenation and h is a function that is applied to the key depending on its type:
- for value types, h pads the value to 32 bytes in the same way as when storing the value in memory.
- for strings and byte arrays, h computes the keccak256 hash of the unpadded data.
Find more information here on the Solidity page: https://docs.soliditylang.org/en/v0.8.3/internals/layout_in_storage.html?highlight=storage#mappings-and-dynamic-arrays
Addresses are a cool thing in Solidity. They are like a bank account number, an IBAN if you wish. You know who transacts with your Smart Contract and the Smart Contract knows who you are.
The cool thing is, addresses can be keys for arrays and mappings. And in our example we map addresses to boolean values. We could use this for white-listing for example. So, if an address is allowed to do a certain action in our Smart Contract then we can white-list it.
Let's see how that behaves. First, let's check the value for your own address:
- Copy the Address from the Dropdown - there is a little copy icon next to the dropdown, press it
- Paste the Address into the input field next to "myAddressMapping"
- Press the Button
- The value should be "false"
Set Address Keys in a Mapping¶
Let's have a closer look at the function setMyAddressToTrue
:
function setMyAddressToTrue() public {
myAddressMapping[msg.sender] = true;
}
This function does several things:
- it accesses the global msg-object and gets the senders address. So, if you are interacting with a specific address, then for the Smart Contract that address will be in
msg.sender
- It accesses the
myAddressMapping
mapping and sets the value "true" for the current address.
Let's give it a try!
- simply click the "setMyAddressToTrue" button. There is no input field, because the address will automatically be the one you use to interact with the Smart Contract
- Retrieve the value again
- It should be true
Mappings of mappings¶
Because of the way mappings are stored within the EVM, it's possible to create mappings of mappings as well. For example:
mapping (uint => mapping(uint => bool)) uintUintBoolMapping;
Let's try a little coding exercise to get a feeling for mappings yourself!
Code Exercise
Try to use the mapping above, which is not public, and create both a getter and a setter function for it.
In case you get stuck, you can see the solution below.
Solution Code Exercise
mapping(uint => mapping(uint => bool)) uintUintBoolMapping;
function setUintUintBoolMapping(uint _index1, uint _index2, bool _value) public {
uintUintBoolMapping[_index1][_index2] = _value;
}
function getUintUintBoolMapping(uint _index1, uint _index2) public view returns (bool) {
return uintUintBoolMapping[_index1][_index2];
}
You might have seen, you can't name the getter function with the same name as the variable. But if you are making the variable public, then Solidity automatically creates a getter function with the same name as the variable. Peculiar detail to keep in mind here.
Alright, that's it for this lab. Congratulations, you created your first mapping and know how to access mappings.