(Part 2) Ethereum Solidity Development - Deploying, Securiy And ERC20 Compliance(PT 2)

in #utopian-io6 years ago

Repository

https://github.com/igormuba/EthereumSolidityClass/tree/web3contract

What Will I Learn?

  • Ethereum events
  • Ethereum functions
  • ERC20 compliance
  • Start a test environment on Node

Requirements

State the requirements the user needs in order to follow this tutorial.

  • Internet connection
  • Code editor
  • Browser

Difficulty

  • Intermediate

Tutorial Contents

Let us start our private Ethereum blockchain so we can learn on a more realistic environment instead of testing functions on Remix, with this environment we can use the Metamask later and develop real web applications, like test ICOs and practice crypto kitties like apps.

Assuming you are a linux user, like me, first you need node package manager, you can get it by going into the terminal and telling it

sudo apt install npm

If you are using Windows or Mac you can get NPM with NodeJS here
https://nodejs.org/en/

Now, let us install globally the Ethereum test network, this network will be local on your machine but will behave by any means necessary just like the real Ethereum blockchain.

npm install -g ethereumjs-testrpc

Now we launch the Ethereum testing network

testrpc

Notice that the testing environment has created 10 Ethereum adresses with Ethereum inside them, this is, of course, not real Ethereum, but will do the job for your tests :)
Wish it were real though haha

Now we can start to interact with this local environment, note that the real costs for a real application might be different, but the test network is more powerful than Remix online IDE.

Let us also install web3 so we can develop browser applications later

npm install -g [email protected]

Initiating the Node directory

Inside the directory of the project do

npm init

And

npm install web3

Now open the node console by typping in the terminal

node

And in the console do

Web3 = require('web3')

then

provider = new Web3.providers.HttpProvider("http://localhost:8545")

Notice that if you have changed the port or you are doing on a remote server or any other personalized setting you can see what is the address that should be passed as a string argument to the HttpProvider function on the console where you have openned the testrpc

And, last,

web3 = new Web3(provider)

Now we have web3 on our project.

On the testrpc, please, copy the wallet that you were given, you will use them to transfer tokens around

In my case, the accounts I have got are

(0) 0xfe7f63e4a14151e4275e429341714b500db8445d
(1) 0xed38a2696c6ce473e820ea7974d4fb0884effb96
(2) 0x7a9ea8150c1640fb62eab4eca340620e28703f62
(3) 0xa400bae51c74c788aa01d513a06190d15fec1518
(4) 0x57efc776efdf76c93cfc4ab8ba7ba350048fc690
(5) 0x14e8249a479f95d649d818fc44f6a703a4c13e5a
(6) 0x014dbe37e198ef993406beb0075e335de32bb1ae
(7) 0x39e365a77fb2b32faf0b2ca90c4cd47edb6011fa
(8) 0xd2cc66f827e1c5c43a31e9d5f842582717b72702
(9) 0xaa41a595476ea690860eb9f0c447aaaad3498dcf

A Simple Token Contract

This is the code from the last class

pragma solidity ^0.5.0;

import /usr/local/lib/node_modules/web3/;

provider = new Web3.providers.HttpProvider("http://localhost:8545")
web3 = new Web3(provider)

contract firstClassContract{
    string private stringVariable = "Variable name";
    int private unsignedInteger = 10;

    function setStringVariable(string memory newVariable) public {
        stringVariable = newVariable;
    }

    function getStringVariable() public view returns (string memory){
        return stringVariable;
        //test
    }

}

Let us change it a little bit to turn it into an actually useful contract.

First, remove the variables stringVariable and unsignedInteger

We want to specify who is the creator of the contract, it is useful because we will set his balance, we can do it with

address public creator; //address of contract creator to be defined

This looks like a variable but we will have to make sure this variable can't be changed. So we will use it only on the constructor function. Be sure to avoid using this variable anywhere else.

uint256 public totalSupply; //total coin supply
mapping (address => uint256) public balances; //like a dictionary, an address represents
                                                                                                // a positive integer

The above is what really turns this contract into a coin contract.
You can notice that the coins are nothing more than a number inside a variable, yet some numbers inside some functions inside some contracts are very valuable. Kinda frustrating, right?

Constructor

The constructor function is called only once and only when the contract is created. This can ensure that some variables can't be "attacked", for example, we don't want other people to hijack our contract.

function firstClassContract() public{
        creator = msg.sender; //the creator of the contract
        //msg.sender is automatically generated on contract deploy
        totalSupply = 100; //sets the total supply
        balances[creator] = totalSupply; //gives all the supply to the contract creator
    }

This is very simple for now.

Tranfering our tokens

First, we will set a function to get the balance of the person that is calling it. It is a view function, it means it only reads data, does not write anything. Be very careful with function types.

function balanceOf(address memory owner) public view returns(uint256 memory){
        return balances[owner];
    }

Now this is a very common send function.
I have adapted it to use the memory declaration to save resources. You can read about it at
https://blog.zeppelin.solutions/a-gentle-introduction-to-ethereum-programming-part-2-7bbf15e1a953

function sendTokens(address memory receiver, uint256 memory amount) 
        public returns(bool){
            address owner = msg.sender; //this is the caller of the function
            
            require(amount > 0); //the caller has to have a balance of more than zero tokens
            require(balances[owner] >= amount); //the balance must be bigger than the transaction amount
            
            balances[owner] -= amount; //deduct from the balance of the sender first
            balances[receiver] += amount; //add to the balance of the receiver after
            return true;
        }
    }

NOTE: Whenever you are making a function that does transfer of tokens you need to deduct from the sender before you add to the balance of the receiver. The DAO was hacked because of a function that added funds to the receiver before checking if the sender had the amount.
You can read more about the exploit used on the attack here
http://hackingdistributed.com/2016/06/18/analysis-of-the-dao-exploit/

So, if you want to evolve into a blockchain developer you must study 3 timer harder security and data integrity than you study real development. Any mistake can be very expensive!

Now you have a very basic contract that does the basic. But it does not comply to any token standard

The ERC20 standard

The ERC20 standard for token contracts is a design pattern for creating tokens on the Ethereum network and make integration with third parties easy.

For example, by using the ERC20 you can create a token and right away use it in many already well stablished wallets and even descentralized Ethereum based exchanges because if your contract follows the design pattern those wallets and exchanges already know what functions to call and how to work with your coin. So knowing what design pattern to use is very important.

According to Wikipedia
https://en.wikipedia.org/wiki/ERC-20

To comply with the ERC20 standard your token must implement at the following functions (it can have more functions and functionalities, though)

  • totalSupply() public view returns (uint256 totalSupply)
  • balanceOf(address _owner) public view returns (uint256 balance)
  • transfer(address _to, uint256 _value) public returns (bool success)
  • transferFrom(address _from, address _to, uint256 _value) public returns (bool success)
  • approve(address _spender, uint256 _value) public returns (bool success)
  • allowance(address _owner, address _spender) public view returns (uint256 remaining)

But also, according to the Ethereum Improvement Proposal available at
https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md

There are other requirements, like giving a name, symbol and decimals, though the decimals is optional.

Let us make our token ERC20 compliant.

First, create the variables with name and symbol and set their values on the constructor.

Declaring them outside the constructor

string public _tokenName;
string public _tokenSymbol;

In the beginning of the constructor

function firstClassContract() public{
        _tokenName = "ERC20 Token"; //sets the name of the token
        _tokenSymbol = "ERCT" //ticker symbol of the token
//[... more content below hidden for better visualization]

ERC20 events

Solidity implements events, that behave like callback functions, and are very useful for us to update the user interface without having to actively reload the page. We are not yet implementing any user interface, but, supposing this was a real contract for a real project, some third party interface could want to use our token, so they would be able to work with our contract and update the interface without having to check the blockchain for updates, because the callback event would alert that something has changed, making them, and us, literally save money! So let us define those events to make our contract ERC20 compliant

The event are very straight forward

event Transfer(address indexed _from, address indexed _to, uint256 _value)
event Approval(address indexed _owner, address indexed _spender, uint256 _value)

Both of them receive the from, where and amount of tokens transfered and alert the sender that the transfer happened.

The ERC20 functions

balanceOf(we already have it)

We already have the balanceOf function, let us do the total supply.

totalSupply

Remember the class about view vs pure? There is 2 ways you could implement this function. One is kinda cheating. The first and "right" way to do this is by using view and really checking on the blockchain what is the total supply of the coin.

function totalSupply() public view returns (uint256 memory) {
        return totalSupply;
    }

The second and "cheating" way to do it (DON'T TELL ANYONE I TAUGHT YOU THAT!) is to use a pure function, this one is cheaper to execute since it does not check the blockchain.

function totalSupply() public pure returns (uint256 memory) {
        return 100; //hardcode it to return the total supply from the constructor
    }

The last solution is hardcoded and a bad practice, but for all effects it works the same and is cheaper to execute! Again DON'T TELL I TAUGHT YOU THAT

transfer

We have a very similar function on our contract already, but WE NEED TO CHANGE THE NAME as the very reason of ERC20 is to make the working of many contracts similar so third party can use them without even checking the code, as they should know what functions to call to do anything, so, change the name of the function sendTokens to transfer and call it a day. Don't work harder, work smarted ;)

allowance

To make the next function work we need to add a mapping of what addresses can spend from other, this allows for delegation of the right to spend tokens from your account
So, create hte variable

mapping (address => mapping (address => uint256)) private _allowed;

The above is like a dictionary with the list of all accounts that are allowed to spend from what accounts

Now, for the function

function allowance(address owner, address spender) public view returns (uint256){
        return _allowed[owner][spender]; //adds to the allowance mapping
    }

Will add the addresses to the mapping above

approve

This works together with the next function and is used to allow third parties to spend your tokens, this is like delegating the right to use your wallet to someone else

function approve(address _spender, uint256 _value) public returns (bool success) {
        allowance[msg.sender][_spender] = _value;

        Approval(msg.sender, _spender, _value);

        return true;
    }

If you are going to use this code for production purpouses, a good read, recommended by the creators of the ERC20 standard, about attack vectors are
https://docs.google.com/document/d/1YLPtQxZu1UAvO9cZ1O2RPXBbT0mooh4DYKjA_jp-RLM/edit
And
https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729

transferFrom

This is one of the functions that make the ERC20 special, this function allows third parties (contracts for example) to make transfers on your behalf,

We can tweak the transfer function
This is the transfer function

function transfer(address memory receiver, uint256 memory amount) 
        public returns(bool){
            address owner = msg.sender; //this is the caller of the function
            
            require(amount > 0); //the caller has to have a balance of more than zero tokens
            require(balances[owner] >= amount); //the balance must be bigger than the transaction amount
            
            balances[owner] -= amount; //deduct from the balance of the sender first
            balances[receiver] += amount; //add to the balance of the receiver after
            return true;
        }
    }

Copy and create one identical to the above, but add on the arguments it receive a sender, before the receiver
so it becomes

function transferFrom(address memory sender, address memory receiver, uint256 memory amount) 

And add a condition inside it using the require

require(_value <= allowance[_from][msg.sender]); //requires that the caller of the function has the permission to send this value from the sender of the amount

Also, inside the function, there are 2 other places that we need to change the owner into sender

require(balances[sender] >= amount); //the balance must be bigger than the transaction amount
            
balances[sender] -= amount; //deduct from the balance of the sender first

In the end, a simple ERC20 compliant token

In the end the code, with all fixes and changes, should look like this (feel free to copy, but don't use this for production, only use for learning purpouses as it does not implement safe math and a few other security measures!!)

pragma solidity ^0.5.0;

import /usr/local/lib/node_modules/web3/;

provider = new Web3.providers.HttpProvider("http://localhost:8545")
web3 = new Web3(provider)

contract firstClassContract{

    string public _tokenName;
    string public _tokenSymbol;
    address public creator; //address of contract creator to be defined
    uint256 public _totalSupply; //total coin supply
    mapping (address => mapping (address => uint256)) private _allowed; //allowance
    mapping (address => uint256) public balances; //like a dictionary, an address represents
                                                  // a positive integer
    // new variables can't be created
    //the above ones are created during contract creation

    //below is the constructor function called only when the contract is created
    constructor() public{
        _tokenName = "ERC20 Token"; //sets the name of the token
        _tokenSymbol = "ERCT"; //ticker symbol of the token
        creator = msg.sender; //the creator of the contract
        //msg.sender is automatically generated on contract deploy
        _totalSupply = 100; //sets the total supply
        balances[creator] = _totalSupply; //gives all the supply to the contract creator
    }

    event Transfer(address indexed _from, address indexed _to, uint256 _value);
    event Approval(address indexed _owner, address indexed _spender, uint256 _value);

    function balanceOf(address owner) public view returns(uint256){
        return balances[owner];
    }

    function allowance(address owner, address spender) public view returns (uint256){
        return _allowed[owner][spender]; //adds to the allowance mapping
    }

    function transfer(address receiver, uint256 amount) 
        public returns(bool){
            address owner = msg.sender; //this is the caller of the function
            
            require(amount > 0); //the caller has to have a balance of more than zero tokens
            require(balances[owner] >= amount); //the balance must be bigger than the transaction amount
            
            balances[owner] -= amount; //deduct from the balance of the sender first
            balances[receiver] += amount; //add to the balance of the receiver after
            return true;
        }
         function transferFrom(address sender, address receiver, uint256 amount) 
        public returns(bool){
            require(amount <= _allowed[sender][msg.sender]); //requires that the caller of the function has the permission to send this value from the sender of the amount
            address owner = msg.sender; //this is the caller of the function
            
            require(amount > 0); //the caller has to have a balance of more than zero tokens
            require(balances[sender] >= amount); //the balance must be bigger than the transaction amount
            
            balances[sender] -= amount; //deduct from the balance of the sender first
            balances[receiver] += amount; //add to the balance of the receiver after
            return true;
        }

        function totalSupply() public view returns (uint256) {
        return _totalSupply;
    }



    }
   

If we put that on remix, first remove the

import /usr/local/lib/node_modules/web3/;

provider = new Web3.providers.HttpProvider("http://localhost:8545")
web3 = new Web3(provider)

so we can compile and deploy to test

Curriculum

Sort:  

Thank you for your contribution @igormuba.

Good work on developing this tutorial. It's good that you put the results of each development you make.

We are waiting for your next tutorial.

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? Write a ticket on https://support.utopian.io/.
Chat with us on Discord.
[utopian-moderator]

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

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!

Coin Marketplace

STEEM 0.20
TRX 0.13
JST 0.030
BTC 66709.93
ETH 3505.67
USDT 1.00
SBD 2.71