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

in #utopian-io5 years ago

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

https://steemit.com/utopian-io/@igormuba/part-6-ethereum-solidity-custom-varaible-functionalities-libraries-using-libraries-for-security-pt-6
https://steemit.com/utopian-io/@igormuba/part-4-ethereum-solidity-custom-access-modifiers-security-breach-alerts-assert-and-require-pt-4
https://steemit.com/utopian-io/@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
https://steemit.com/utopian-io/@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
https://steemit.com/utopian-io/@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
https://steemit.com/utopian-io/@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

Sort:  

Another beautiful tutorial, thank you.
I like the simplicity in delivering the ideas and concepts.
Couple of notes:

  • I did not see the aspect of multi-sig implemented here. I guess you're just referring to the concept now under the Bank umbrella, and this would be referenced under other work?
  • I suggest instead of just putting links to your other work, is to use markdown approach which make the display much neater, via using square brackets containing a word about the link [WORDS HERE ] , followed directly by brackets (LINK HERE), so that the final outcome would be [REF WORDS] (LINK) (remove space between them to actually work as a link).
  • Also any images you used, either put some relevant references, or if they are CC mention that.
  • One point to ask about decreasing the user balance when sending funds, is there a potential for the sending function to fail, and hence a need to do a rollback on the decrease in user balance? or that scenario could never happen?

Your contribution has been evaluated according to Utopian policies and guidelines, as well as a predefined set of questions pertaining to the category.

To view those questions and the relevant answers related to your post, click here.


Need help? Chat with us on Discord.

[utopian-moderator]

Hi, thank you for the review and for pointing where I can pay closer attention

Regarding the first point, I tried to approach the characteristic of multisig under the "bank" example to show how different wallets can have different stake on a contract balance, I will further cover this is the future but I need to think of better examples, though I think the bank example was good to show how different stakes can easily be defined on a contract

On your last point, according to the logic I have implement I don't think such error can occur because the only way this could happen is if the user tries to withdraw more than the contract has in balance but I can't think right now how could that happen without triggering errors before the change in balances.
That said, I have warned on the beginning of the tutorial that the goal of the tutorial is to demonstrate a sple implementation of a multisig wallet, not to go us on security or safe math. But if someone wants to be extra sure he can wrap every line of code inside a require funtion, that would roll back the function and everything in case an error occurs

Posted using Partiko Android

Thank you for your review, @mcfarhat! Keep up the good work!

Hi, @igormuba!

You just got a 0.26% upvote from SteemPlus!
To get higher upvotes, earn more SteemPlus Points (SPP). On your Steemit wallet, check your SPP balance and click on "How to earn SPP?" to find out all the ways to earn.
If you're not using SteemPlus yet, please check our last posts in here to see the many ways in which SteemPlus can improve your Steem experience on Steemit and Busy.

Hi @igormuba!

Your post was upvoted by @steem-ua, new Steem dApp, using UserAuthority for algorithmic post curation!
Your post is eligible for our upvote, thanks to our collaboration with @utopian-io!
Feel free to join our @steem-ua Discord server

Hey, @igormuba!

Thanks for contributing on Utopian.
We’re already looking forward to your next contribution!

Get higher incentives and support Utopian.io!
Simply set @utopian.pay as a 5% (or higher) payout beneficiary on your contribution post (via SteemPlus or Steeditor).

Want to chat? Join us on Discord https://discord.gg/h52nFrV.

Vote for Utopian Witness!

Congratulations @igormuba! You have completed the following achievement on the Steem blockchain and have been rewarded with new badge(s) :

You received more than 1000 as payout for your posts. Your next target is to reach a total payout of 2000

Click here to view your Board
If you no longer want to receive notifications, reply to this comment with the word STOP

To support your work, I also upvoted your post!

Do not miss the last post from @steemitboard:

SteemWhales has officially moved to SteemitBoard Ranking
SteemitBoard - Witness Update

Support SteemitBoard's project! Vote for its witness and get one more award!

Coin Marketplace

STEEM 0.26
TRX 0.11
JST 0.033
BTC 63869.25
ETH 3055.04
USDT 1.00
SBD 3.88