GUIDE TO ERC-20 – How to create your own ERC-20 tokens on Ethereum

Posted by

Why should you have a better understanding of ERC-20 token creation on Ethtereum? Because Initial Coin Offerings (ICO) are on everyone’s lips and are often hailed as THE new form of raising capital. In my opinion, this hype surrounding ICOs is quite understandable if you compare the ICO with its counterpart from the traditional, centralized financial world – the IPO (= Initial Public Offering):

  • Initial Public Offering (IPO): The way to the traditional, centralized stock exchanges of this world such as the New York Stock Exchange (NYSE) is reserved exclusively for large companies. Going public involves a lengthy, expensive process, usually involving investment banks. This form of raising capital remains closed to the vast majority of companies and private individuals anyway.
  • Initial Coin Offering (ICO): It looks completely different with the ICO! In the DeFi world, absolutely anyone can launch and sell their own coin to raise money for a personal project or business with minimal investment of time and money. No wonder, then, that this alternative form of “IPO” generates attention.

In the following mini-tutorial I would like to show you how you can create your own Ethereum coin (or token) very easily and in the shortest possible time. You don’t need any programming knowledge for this, you don’t need to install any developer tools… you don’t even have to pay gas fees for deploying the corresponding smart contract on Ethereum! But that doesn’t mean that this coin would be worthless. Since we are creating a coin based on Ethereum’s ERC-20 standard, this coin can be managed and transferred like any other standard coin with your wallet, it can be traded on decentralized exchanges such as Uniswap (provided that liquidity is available), etc.

“Why would I want to create my own coin?”, you may be asking yourself. Very simple: If you go through this process from A to Z, you will gain a super exciting insight into how Ethereum tokens work, even if you later leave the token on the blockchain and don’t do anything with it. So, let’s go!

Mini-tutorial to create your own ERC-20 token on Ethereum

Step 1: A token is represented by a smart contract running on a blockchain like Ethereum. That is, in the code of the smart contract launched on the blockchain, both parameters such as the coin name and the number of coins in circulation are defined, as well as the functions that the coin should offer, such as the function to transfer the coin from one Ethereum address to another. So if we want to launch a new coin, we have to write a smart contract (e.g. using the programming language Solidity for Ethereum), compile this smart contract and then deploy the compiled code on the blockchain. In order to tackle all of this, we first need the necessary tool; an Integrated Development Environment (IDE). We use Remix as the IDE in this tutorial, because Remix is very easy to use and purely web-based (i.e., without installation on your machine). So open now as the first step Remix.

Image: Screenshot Remix

Step 2: Next we create the smart contract for our new coin. To do this, click the icon CreateNewFile in the area FILE EXPLORERS. We name the new file in our example DeFiGuideToken.sol. The file extension .Sol stand for Solidity, a programming language for creating smart contracts on Ethereum.

Image: Screenshot Remix

Step 3: But don’t worry now! We’re not going to write hundreds of lines of code for our .Sol file. Instead, we simply take an already written standard code for an ERC-20 token (everything on the blockchain is open source!) and then adapt the code in a few places. So we now copy the whole block of code below and paste it into our file DeFiGuideToken.sol. (Note: I got the code below from the TokenFactory taken over and simplified a bit, so that the tutorial here is particularly easy.)

<pre class="wp-block-code"><code>pragma solidity ^0.4.4;
/* LEVEL 1: Die Grundstruktur eines Ethereum Tokens gemäss ERC-20 Standard.
Dies beinhaltet die Deklaration der Grundfunktionalitäten wie z.B. Transfer, welche dann weiter unten in LEVEL 2 &amp; 3 ausprogrammiert werden müssen. */
contract Token {
    /// @return total amount of tokens
    function totalSupply() constant returns (uint256 supply) {}
    /// @param _owner The address from which the balance will be retrieved
    /// @return The balance
    function balanceOf(address _owner) constant returns (uint256 balance) {}
    /// @notice send `_value` token to `_to` from `msg.sender`
    /// @param _to The address of the recipient
    /// @param _value The amount of token to be transferred
    /// @return Whether the transfer was successful or not
    function transfer(address _to, uint256 _value) returns (bool success) {}
    /// @notice send `_value` token to `_to` from `_from` on the condition it is approved by `_from`
    /// @param _from The address of the sender
    /// @param _to The address of the recipient
    /// @param _value The amount of token to be transferred
    /// @return Whether the transfer was successful or not
    function transferFrom(address _from, address _to, uint256 _value) returns (bool success) {}
    /// @notice `msg.sender` approves `_addr` to spend `_value` tokens
    /// @param _spender The address of the account able to transfer the tokens
    /// @param _value The amount of wei to be approved for transfer
    /// @return Whether the approval was successful or not
    function approve(address _spender, uint256 _value) returns (bool success) {}
    /// @param _owner The address of the account owning tokens
    /// @param _spender The address of the account able to transfer the tokens
    /// @return Amount of remaining tokens allowed to spent
    function allowance(address _owner, address _spender) constant returns (uint256 remaining) {}
    event Transfer(address indexed _from, address indexed _to, uint256 _value);
    event Approval(address indexed _owner, address indexed _spender, uint256 _value);
}
/* LEVEL 2: Hier werden nun die wichtigste Funktionen wie z.B. Transfer aufprogrammiert. */
contract StandardToken is Token {
    function transfer(address _to, uint256 _value) returns (bool success) {
        //Default assumes totalSupply can't be over max (2^256 - 1).
        //If your token leaves out totalSupply and can issue more tokens as time goes on, you need to check if it doesn't wrap.
        //Replace the if with this one instead.
        //if (balances&#91;msg.sender] &gt;= _value &amp;&amp; balances&#91;_to] + _value &gt; balances&#91;_to]) {
        if (balances&#91;msg.sender] &gt;= _value &amp;&amp; _value &gt; 0) {
            balances&#91;msg.sender] -= _value;
            balances&#91;_to] += _value;
            Transfer(msg.sender, _to, _value);
            return true;
        } else { return false; }
    }
    function transferFrom(address _from, address _to, uint256 _value) returns (bool success) {
        //same as above. Replace this line with the following if you want to protect against wrapping uints.
        //if (balances&#91;_from] &gt;= _value &amp;&amp; allowed&#91;_from]&#91;msg.sender] &gt;= _value &amp;&amp; balances&#91;_to] + _value &gt; balances&#91;_to]) {
        if (balances&#91;_from] &gt;= _value &amp;&amp; allowed&#91;_from]&#91;msg.sender] &gt;= _value &amp;&amp; _value &gt; 0) {
            balances&#91;_to] += _value;
            balances&#91;_from] -= _value;
            allowed&#91;_from]&#91;msg.sender] -= _value;
            Transfer(_from, _to, _value);
            return true;
        } else { return false; }
    }
    function balanceOf(address _owner) constant returns (uint256 balance) {
        return balances&#91;_owner];
    }
    function approve(address _spender, uint256 _value) returns (bool success) {
        allowed&#91;msg.sender]&#91;_spender] = _value;
        Approval(msg.sender, _spender, _value);
        return true;
    }
    function allowance(address _owner, address _spender) constant returns (uint256 remaining) {
      return allowed&#91;_owner]&#91;_spender];
    }
    mapping (address =&gt; uint256) balances;
    mapping (address =&gt; mapping (address =&gt; uint256)) allowed;
    uint256 public totalSupply;
}
/* LEVEL 3: Und hier werden dann die Grundparameter wie der Token Name oder das Token Symbol gesetzt */
contract DeFiGuideToken is StandardToken {
    function () {
        //if ether is sent to this address, send it back.
        throw;
    }
    /* Public variables of the token */
    /*
    NOTE:
    The following variables are OPTIONAL vanities. One does not have to include them.
    They allow one to customise the token contract &amp; in no way influences the core functionality.
    Some wallets/interfaces might not even bother to look at this information.
    */
    string public name;                   //fancy name: eg Simon Bucks
    uint8 public decimals;                //How many decimals to show. ie. There could 1000 base units with 3 decimals. Meaning 0.980 SBX = 980 base units. It's like comparing 1 wei to 1 ether.
    string public symbol;                 //An identifier: eg SBX
    string public version = 'H0.1';       //human 0.1 standard. Just an arbitrary versioning scheme.
    function DeFiGuideToken(
        ) {
        balances&#91;msg.sender] = 1000;               // ADJUST HERE (total number of tokens)
        totalSupply = 1000;                        // ADJUST HERE (total number of tokens)
        name = "DeFiGuideToken";                   // ADJUST HERE (name of the token)
        decimals = 0;                              // ADJUST HERE (decimal places of the token)
        symbol = "DGT";                            // ADJUST HERE (syombol or ticker of the token)
    }
    /* Approves and then calls the receiving contract */
    function approveAndCall(address _spender, uint256 _value, bytes _extraData) returns (bool success) {
        allowed&#91;msg.sender]&#91;_spender] = _value;
        Approval(msg.sender, _spender, _value);
        //call the receiveApproval function on the contract you want to be notified. This crafts the function signature manually so one doesn't have to include a contract in here just for this.
        //receiveApproval(address _from, uint256 _value, address _tokenContract, bytes _extraData)
        //it is assumed that when does this that the call *should* succeed, otherwise one would use vanilla approve instead.
        if(!_spender.call(bytes4(bytes32(sha3("receiveApproval(address,uint256,address,bytes)"))), msg.sender, _value, this, _extraData)) { throw; }
        return true;
    }
}</code></pre>

Step 4: Now it’s a matter of adapting the code in a few places (see comments ADJUST HERE in code) to create our own custom token. At the following code positions we can define the total number of tokens to be created (totalSupply), the name of our token (name), the number of decimal places (decimals) and the token symbol (symbol). In our example we create the DeFiGuide token with the symbol DGT, of which a total of 1000 pieces should be in circulation. We also define that there should be no decimals which means you can only transfer entire DGT tokens and not fractions of them.

    function DeFiGuideToken(
        ) {
        balances[msg.sender] = 1000;   // ADJUST HERE (total number of tokens)
        totalSupply = 1000;            // ADJUST HERE (total number of tokens)
        name = "DeFiGuideToken";       // ADJUST HERE (name of the token)
        decimals = 0;                  // ADJUST HERE (decimal places of the token)
        symbol = "DGT";                // ADJUST HERE (symbol or ticker of the token)
    }

Step 4: Next we have to compile the DeFiGuideToken.sol code. For this we switch to the area SOLIDITY COMPILERS, where we click the button Compile DeFiGuideToken.sol. We can simply leave the compiler settings as they are by default. Note: We get some compiler warnings here. But we can generously ignore them, since we only want to do a small, simple experiment here. If you later really want to create your “serious” token and there is real money involved, then you have to choose a slightly different, more extensive approach, which, however, goes beyond our mini-tutorial.

Image: Screenshot Remix

Step 5: Now we want to deploy the smart contract on the blockchain and switch to the tab DEPLOY & RUN TRANSACTIONS. Now comes the trick: Deploying a new smart contract on the Ethereum mainnet is currently quite an expensive affair (hopefully that will change with Ethereum 2.0 this summer!). However, we do not want to spend any money on our little experiment, so we do not place our smart contract on the Ethereum mainnet, but on the Ethereum testnet Rinkeby. A testnet like Rinkeby works pretty much identically to the mainnet, but the tokens on the testnet have no value and therefore you can try out all kinds of smart contracts there for free and without restraint before it gets serious and your smart contract is irreversibly deployed on the Ethereum mainnet. We now capture the deployment form as shown in the screenshot below.

Step 6: Now you’re probably thinking, we can just click Deploy and our smart contract is published on the blockchain. Unfortunately, it’s not that easy. Because although we want to deploy to an Ethereum testnet, we still have to pay gas fees for the deployment… just like on the Ethereum mainnet. We want a test environment that is as realistic as possible, and gas fees should not be missing. The nice thing about the testnet is that we receive the Ether tokens for paying the gas fees for free via a so-called faucet. For this we first open MetaMask wallet, switch to Rinkeby Test Network (see dropdown in the top center of the MetaMask window) and now copy the wallet address (just below the text ‘Account 1’).

Image: Screenshot Remix

Step 7: Now we can get the Ether necessary for the deployment of the smart contract. To do this, open the Rinkeby Faucet and paste the previously copied wallet address. Now click Send Me ETH (click only once, otherwise you will be banned!) and a few seconds later you will see 0.1 ETH in your MetaMask wallet.

Image: Screenshot Rinkeby Faucet

Step 8: And now the time has come. Now you can finally click that Deploy button in the Remixer window. This automatically opens the MetaMask wallet. Next we confirm the transaction and this deploys our smart contract ont on the Ethereum testnet.

Image: Screenshot Remix

Step 9: Et voilà! The smart contract is already immortalized on the blockchain. At the bottom left of the remix window you can see the new Ethereum address on which our smart contract was deployed. If you feel like it, you can now copy this address and and past it to Etherscan to view your completed smart contract deployment transaction.

Image: Screenshot Etherscan

Step 10: As a conclusion, we now want to take a closer look at the 1000 DeFiGuideTokens we have just created to show that these tokens are actually on the blockchain and we can manage them like all other tokens via our wallet. For this we open our MetaMask wallet again, we click Import tokens and paste our smart contract address from Remix there.

Image: Screenshot Remix

Step 11: And already we see in our wallet the 1000 DGT tokens, which we can now freely send to others with via our wallet.

Image: Screenshot Remix

That’s it! I hope you enjoyed the mini-tutorial and that it encouraged you to experiment with smart contracts.

(Disclaimer)

GET NOTIFIED ABOUT NEW POSTS!

Sign up to get an email notification every time we publish a new blog post.

We don’t spam! See our privacy policy.