Cover photo

Solidity Sundays #4 (structs, mappings, receiving eth)

Welcome back to week 4. Last week we wrapped things up with our first mini contract catch up here. But we realised that that contract was essentially single use, and there must be much more efficient (and cheaper) ways of storing users information than requiring them to deploy their own contract. So this week we’re going to look at how we can store many more users information and make it only editable by the user themselves. This cranks up the difficulty level a bit but it’s nothing we can’t handle.

Our V2 Contract
Our V2 Contract
    struct User {
        string username;
        string heaviestBag;
        uint256 daysSinceDegened;
    }

First things first, this time we’re going to bundle together all of a single users information into a data structure called User. To create a struct you just have to give the struct a name and inside declate all variables (and their data types) that make up that struct.

    mapping(address => bool) public registeredMember;

Next is our first mapping. Mappings are essentially key, value pairs which connect one piece of information to another. So for example, this mapping links an address to a bool (true or false) called registeredMember. We’ll see where this is used later in the code but this mapping stores whether any Ethereum address is registered (returns true) or not (returns false).

mapping(address => User) public userDetails;

Here we have another mapping this time linking an address to the User struct we created earlier. This is what will allow us to store multiple users in the contract and have each instance of the User struct be linked to an Ethereum address (it’s owners). Now we have all the moving parts necessary to store multiple users information within our new contract, we just need a function to allow them to do it.

function createUser(
  string memory _username,
  string memory _heaviestBag, 
  uint256 _daysSinceDegened)
  public payable {
}

Another improvement we’ve added from our last contract is that we can now have the users create all their information in one transaction rather than having to call separate setUsername, setHeaviestBag, setDaysSinceDegened functions which is can get annoying. So this function takes all three as arguments, is a public function but is also payable? Interestingly by default smart contracts on Ethereum don’t know how to handle being sent ETH so if you intend to have a function receive ETH (such as an NFT mint function) you have to declare the function payable when creating it.

require(msg.value >= 0.01 ether);

In the next line is where we make use of the ‘payable’ nature of this function and we require that the msg.value (how much eth is sent along with the transaction) is greater than or equal to 0.01eth. So any attempts someone makes to createUser without sending the necessary eth will revert.

require(registeredMember[msg.sender] == false);

This is where we use the first mapping we created earlier, to check that the address of msg.sender is not already linked to a User (as we intend to only have one User per address).

So in summary only those who send the necessary eth and have not already registered a user will have their transaction go through.

registeredMember[msg.sender] = true;

Here we set the previously mentioned mapping to true, so this msg.sender won’t be able to call createUser in our contract again after this.

userDetails[msg.sender] = User(_username,_heaviestBag, _daysSinceDegened);

Then finally we create a User struct with the variables given as arguments into the contract and using the userDetails mapping we connect that struct to the address of msg.sender.

Summary

So this week we’ve made a V2 of the original contract we worked on in the first 3 weeks so that our contract is now scalable to be used by multiple users.

This week we added structs, mappings and payable functions to our Solidity toolbag and we’re pretty close to having most of the core functionality down. Solidity By Example links included for more details if necessary.

We’ll build on this contract a little more next week and then after that start looking at some real world contract examples to test what we’ve learned so far.

Challenge Time

Currently there is one glaring issue with our contract that could cause is a lot of pain in the future if it was deployed and used on mainnet as it is. Brownie points for anyone who can work out what it is!

See you again next week!