(Part 11) Ethereum Solidity - Multisig Contract As Bank,k Multiple Users, And Where To Implement App Logic(PT 11)

Repository

https://github.com/igormuba/EthereumSolidityClasses/tree/master/class11

What Will I Learn?

  • Make a multisig contract in the didactic form of a "bank"
  • Basic multisig security
  • Track, add and reduce the user stake (balance) of the multisig contract

Requirements

  • Internet connection
  • Code editor
  • Browser

Difficulty

  • Intermediate

Tutorial Contents

It is very likely that, whatever the dapp you are building, it will be designed to hold the balance of multiple users. On this tutorial I will show one simple implementation of a bank to show the pure form of how can you manage the balance of multiple users, allowing them to deposit Ethereum into the bank and withdraw their balance, but not more.

At this moment we won't worry about safe math and security, but on previous tutorials, I mention some security methods that can be useful for a wide range of applications. If you want to read more about security, please, check the tutorials below

@igormuba/part-6-ethereum-solidity-custom-varaible-functionalities-libraries-using-libraries-for-security-pt-6
@igormuba/part-4-ethereum-solidity-custom-access-modifiers-security-breach-alerts-assert-and-require-pt-4
@igormuba/part-2-ethereum-solidity-development-deploying-securiy-and-erc20-compliance-pt-2

I will, however, mention some basic security steps that are simple enough to keep them in mind on any contract you write, like the order at which balances update should take place.;

Base bank contract

image.png

As always, I am using the last version of solidity, the version 5, so the skeleton of the contract is

pragma solidity ^0.5.0;

contract bank{
}

Deposit and Withdrawal

Let us think for a moment what functionalities the bank contract will need to do the basic functionality:

  • Deposit: receive an amount of value form the user and record on his account on the bank that his balance grew, so that he can withdraw it later
  • Withdrawal: send Ethereum form the bank to the user address and reduce the value of his account so he can not withdraw more money than he has deposited

The deposit function can be simplified as a fallback function, I talk about fallback functions on this other tutorial
@igormuba/part-7-ethereum-solidity-fallback-ethereum-fractions-and-collateral-backed-contract-pt-7
But in short, a fallback function is the function that is executed when a call is made to the contract and there is no other function on the contract with the same signature, that is, no other function with the same name and that receives the same arguments.

Also, now, the withdrawal function needs to do two things, it needs to ensure that who withdrawals from the bank is a member and that he has enough balance on the bank so that his balance is equal or greater than the amount he wants to withdraw, also it needs to receive as an argument the amount the member of the bank wants to withdraw to, first, compare the value with the amount he has in the account, and then use the number to effectively send Ethereum from the contract to his balance.

The design I thought for them is

pragma solidity ^0.5.0;

contract bank{
function() external payable{ //deposit function is a fallback function
}

function withdrawal(uint amount) public isMember{ //withdrawal with functionalities described above
}

}

Modifier for members and member account

image.png

On the withdrawal, we have added a modifier isMember to ensure that whoever wants to withdraw money from the bank is indeed a member that has balance in his account. Let us implement this modifier.
More about modifiers on my tutorial
@igormuba/part-4-ethereum-solidity-custom-access-modifiers-security-breach-alerts-assert-and-require-pt-4

What do our modifier need to do

  • Check who wants to withdraw
  • See if he has balance in the bank

The design I came out with is

modifier isMember(){
        require(_members[msg.sender]>0); //if members balance is less than zero throws an error
        _;
    }

Now, we need something to hold the members account balance, for this, we can simply use a mapping to map an integer value (that will be his balance in wei) to the address of each user

mapping(address => uint) private _members; //maps a balance to an address

So the design of the bank contract should look like

pragma solidity ^0.5.0;

contract bank{
mapping(address => uint) private _members; //maps a balance to an address
modifier isMember(){
        require(_members[msg.sender]>0); //if members balance is less than zero throws an error
        _;
    }

function() external payable{ //deposit function is a fallback function
}

function withdrawal(uint amount) public isMember{ //withdrawal with functionalities described above
}

}

Managing withdrawals and deposits

We have implemented the functions to manage deposits and withdrawals, but so far they still do nothing, the deposit one receives Ether but does not register the increment in the balance of the user, to fix it we can use the logic

function() external payable{
        _members[msg.sender]+=msg.value; //adds the sent value to the member balance
    }

And for the withdrawal function

function withdrawal(uint amount) public isMember{
        require(_members[msg.sender]>=amount); //thows error if user withdraw more than balance
    _members[msg.sender]-=amount; //reduces balance before sending value
        msg.sender.transfer(amount); //send the withdraw requested
    } 

Notice that we use require(_members[msg.sender]>=amount) to avoid attempts of withdrawing more than the user actually has in balance.

Also, notice that we reduce the balance of the user before sending the value. This step is very important and if you do this in other order you are susceptible to a hack like the DAO hack.
Read more about the DAO hack and the exploit they have used here
http://hackingdistributed.com/2016/06/18/analysis-of-the-dao-exploit/
But in short, it was possible because the update of balances was done after the rest of the logic of a function, so be careful with the order you do things!

It is always recommended, when dealing with numbers and balances, to use a safe math library, like the one form Zepellin OS
https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/math/SafeMath.sol

So far our code looks like this

pragma solidity ^0.5.0;

contract bank{

mapping(address => uint) private _members; //maps a balance to an address
modifier isMember(){
        require(_members[msg.sender]>0); //if members balance is less than zero throws an error
        _;
    }

function() external payable{
        _members[msg.sender]+=msg.value; //adds the sent value to the member balance
    }

function withdrawal(uint amount) public isMember{
        require(_members[msg.sender]>=amount); //thows error if user withdraw more than balance
    _members[msg.sender]-=amount; //reduces balance before sending value
        msg.sender.transfer(amount); //send the withdraw requested
    } 

}

Checking balance

The last step, before doing the testings, is to implement a function to check the user balance, so we can see if it works

function myBalance() public isMember view returns (uint){
        return _members[msg.sender];
    }

Testing

Using the Ethereum Virtual Machine (testing environment) from http://remix.ethereum.org

I have deployed the contract, now if I send 1000000000000000000 wei (e Ethereum) to the contract let us see what happens
If you don't know how Ethereum fractions (like wei and gwei) work you can see it here
@igormuba/part-7-ethereum-solidity-fallback-ethereum-fractions-and-collateral-backed-contract-pt-7

image.png

The deposit was sent and the balance is in the bank, now I can make a withdrawal of half of that amount, 500000000000000000 wei

image.png

And the balance is updated.

If we try to withdraw more than we have on the bank we will get an error.

NOTE: The transactions on this contract do not take into account the miner fees. In reality, if you deposit 1 Ethereum you need more than 1 Ethereum on your wallet to pay for the fees to deposit, and you can't deposit all of you balance because to call the withdrawal function you need to pay from your caller wallet the fees for the miners

Making the numbers more readable

When implementing a dapp, it is recommended to keep the contract as simple as possible and keep all the conversion logic on the front end of the application, in this case, you could use a landing page with JavaScript logic to convert Ethereum to wei so the contract still works with wei value but the user can make the requests in Ether and fractions

Yes, working with wei is very annoying, it is a very small unit of measurement, the smallest one for Ethereum, just like Satoshi is the smallest unit of measurement for Bitcoin.

You can fix this by making the contract multiply the input for 1000000000000000000 (there are 18 zeroes there if you want to count, there you go, just saved you from the work of doing that), but it is not recommended at all

We could implement a function that works with those numbers, but keep in mind that currently Solidity does not have support for type casting and float.

You can implement a function

function EtherToWei(uint number) pure private returns (uint){
        return number*1000000000000000000;
    }

And then use that to convert all numbers, but honestly, the best way to do that is on the front end of your application, with JavaScript. The user is not supposed to have access to the contract unless he really knows what he is doing. So I do not recommend implementing unit conversion logic on the contract.

Curriculum

Beneficiaries

This post has as beneficiaries
@utopian.pay with 5%
using the SteemPeak beneficiary tool
image.png

H2
H3
H4
Upload from PC
Video gallery
3 columns
2 columns
1 column
7 Comments