Skip to content

Add a Mapping

Let's make our Smart Contract inherently secure. We will allow only withdrawals for previously done deposits. It's the first step towards understanding Token Contracts, which you will see later!

Add the following things to the Smart Contract:

//SPDX-License-Identifier: MIT

pragma solidity ^0.8.4;

contract MappingsStructExample {

    mapping(address => uint) public balanceReceived;

    function getBalance() public view returns(uint) {
        return address(this).balance;
    }

    function sendMoney() public payable {
        balanceReceived[msg.sender] += msg.value;
    }

    function withdrawAllMoney(address payable _to) public {
        uint balanceToSend = balanceReceived[msg.sender];
        balanceReceived[msg.sender] = 0;
        _to.transfer(balanceToSend);
    }
}

To understand what's going on here:

When someone sends money using the "sendMoney" function, we track the msg.value (amount in Wei) with the balanceReceived mapping for the person who interacted with the Smart Contract.

If that same person tries to withdraw money again using "withdrawAllMoney", we look in that mapping how much he sent there previously, then reset the mapping and send the amount.

Re-Entrancy and Checks-Effects-Interaction Pattern

You are eventually wondering why we don't do the following:

function withdrawAllMoney(address payable _to) public {
    _to.transfer(balanceReceived[msg.sender]);
    balanceReceived[msg.sender] = 0;
}

This follows the so-called Checks-Effects-Interaction pattern. As a rule of thumb: You interact with outside addresses last, no matter what. Unless you have a trusted source. So, first set your Variables to the state you want, as if someone could call back to the Smart Contract before you can execute the next line after .transfer(...). Read more about this here: https://fravoll.github.io/solidity-patterns/checks_effects_interactions.html

Deploy the new Smart Contract

Head over to the "Deploy & Run Transactions" Plugin and deploy a new Instance.

Deposit and Withdraw

We will deposit 1 Ether from two different accounts and then withdraw Ether again:

  1. select Account#1 from the Accounts Dropdown
  2. Value: 1 Ether
  3. Hit the "sendMoney" button
  4. Select Account#2 from the Accounts Dropdown
  5. Value: 1 Ether
  6. Hit the "sendMoney" button

Now check the Balance of the Smart Contract:

You have 2 Ether (2 * 10^18 Wei) in the Smart Contract. But if you check the accounts individual amount from the mapping, then you see that each account can max withdraw 1 Ether:

Withdraw all Money to Account#3

Let's withdraw all the funds stored in the Smart Contract to Account#3 from the Accounts Dropdown:

  1. Copy the address of Account#3 (select Account#3, copy)
  2. Paste the Address into the withdrawAllMoney input field, but don't hit the button yet
  3. Go back to Account#1
  4. Hit the "withdrawAllMoney" button with Account#3-Address in the input field
  5. Select Account#2 from the Accounts Dropdown
  6. Hit the "withdrawAllMoney" button again with Account#3-Address in the input field
  7. Check the Balance of the Smart Contract and of Account#3

Withdrawing all money is fun, but not very useful yet. Let's add another functionality to withdraw partial funds.

Try yourself first

Want to give it a try yourself first before you proceed?

Extend the Smart Contract and use the Mapping allow partial sending. That means, the user can specify an amount to send. The Smart Contract checks if the amount isn't larger than what the user previously deposited, deducts the amount from the users balance and sends it.