In the previous post, we covered memory, calldata, and storage in Solidity, which are essential concepts for managing data in smart contracts. In this post, we’ll build on our simple bank setup example and explore mapping and nested mapping, key concepts for storing and retrieving data efficiently in Solidity.
Current State of the our Banking Smart Contract
So far, we have the following Solidity contract where we can:
- Add users by executing the
createUserAccountfunction and submitting values like800000, princechaddhaor200, pwnmachine. - Access user balances using the
userAccountsarray by entering an index value like0,1, and so on.
However, this setup works only if you know the exact index of the user. Imagine having hundreds of users in the contract, finding a specific user’s balance would be extremely inefficient.
What Is Mapping in Solidity?
A mapping in Solidity is a data structure that acts like a key-value store. It allows us to map keys to corresponding values, offering efficient data storage and lookup.
Why Use Mapping?
In our previous example, we used an array of user accounts. While this works for small-scale projects, it can be inefficient when dealing with hundreds or thousands of users. Accessing a specific user’s balance using an array requires knowing the exact index, which isn’t practical.
To solve this, we use mapping, which allows us to link user data directly using a unique key, similar to a dictionary or hashmap in other languages. In simple words if we search for a user we will get their balance directly instead of like preivlege we have to
Here’s the basic syntax for a mapping:
mapping (string => uint256) public UserAccountBalance;
This creates a mapping where:
- Key: A
string(user’s name) - Value: A
uint256(user’s balance)
Now updating our Simple bank contract with Mapping
Let’s create a Mapping to store balances by user name and update the createUserAccount function to include mapping:
pragma solidity 0.8.26;
contract FirstContract {
uint public balance;
uint256[] public accountBalances;
// Struct for user account
struct Username {
uint256 balance;
string name;
}
// Array to store users
Username[] public userAccounts;
// Mapping to store balances by user name
<strong> mapping (string => uint256) public UserAccountBalance;
</strong>
// Function to create a new user account
function createUserAccount(uint256 _balance, string memory _name) public {
userAccounts.push(Username(_balance, _name));
<strong> UserAccountBalance[_name] = _balance; // Update mapping
</strong> }
// Deposit function
function deposit(uint amount) public {
balance += amount;
}
// Withdraw function
function withdraw(uint amount) public {
require(amount <= balance, "Insufficient balance");
balance -= amount;
}
// Fallback functions
receive() external payable {
balance += msg.value;
}
fallback() external payable {}
}
Now let’s deploy the updated code on Remix IDE
- Deploy the updated contract in Remix IDE.
- Use the
createUserAccountfunction to add users like this:
1000000, princechaddha100000, pwnmachine<code>10000, testuser
Once users are added, use the UserAccountBalance public getter to search for their balances.
Example Queries:
- Enter
princechaddha→ Returns<code>1000000 - Enter
testuser→ Returns<code>10000 - Enter an invalid value like
randomuser→ Returns0(default value)
Important Things to Know About Mappings
- Mappings Can Only Be Declared in Storage
Mappings cannot be declared inside functions. They must be defined at the contract level because mappings work directly with blockchain storage. Invalid Example:
function invalidMapping() public {
mapping (string => uint256) tempMapping; // Not allowed
}
- Mappings Cannot Be Iterated Over
There’s no way to loop through keys in a mapping because mappings don’t keep a list of keys. Every possible key exists, and its value defaults to0unless explicitly set. - Mappings Cannot Be Returned
You cannot return entire mappings from a function. Solidity doesn’t support this because mappings don’t track keys internally.
What Is Nested Mapping?
A nested mapping is a mapping inside another mapping. It’s useful when you need to link multiple keys to a value, like tracking balances across different tokens for the same user.
Example of Nested Mapping
Here’s how we can track balances for users across different tokens:
pragma solidity 0.8.26;
contract NestedMappingExample {
// Nested mapping to track token balances by user
mapping (string => mapping (string => uint256)) public tokenBalances;
// Function to set balances
function setBalance(string memory _user, string memory _token, uint256 _balance) public {
tokenBalances[_user][_token] = _balance;
}
// Function to get balance
function getBalance(string memory _user, string memory _token) public view returns (uint256) {
return tokenBalances[_user][_token];
}
}
Example Queries:
- Set balance:
setBalance("princechaddha", "ETH", 5000) - Check balance:
getBalance("princechaddha", "ETH")→ Returns5000
Why Use Nested Mapping?
Nested mappings allow you to create a more organized and scalable way to store related data. In a banking example, this could track balances for different tokens, accounts, or even account permissions.
Conclusion
We’ve covered mappings and nested mappings in Solidity, showing how to efficiently manage user data. Mappings offer a simple way to store and retrieve values by keys, while nested mappings take things further by supporting more complex relationships.