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:
- select Account#1 from the Accounts Dropdown
- Value: 1 Ether
- Hit the "sendMoney" button
- Select Account#2 from the Accounts Dropdown
- Value: 1 Ether
- 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:
- Copy the address of Account#3 (select Account#3, copy)
- Paste the Address into the withdrawAllMoney input field, but don't hit the button yet
- Go back to Account#1
- Hit the "withdrawAllMoney" button with Account#3-Address in the input field
- Select Account#2 from the Accounts Dropdown
- Hit the "withdrawAllMoney" button again with Account#3-Address in the input field
- 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.