🚨 I just released a new article: Master Solidity for Blockchain: Step-by-Step Guide 🚨

The Ultimate Blockchain Programming Tutorial with Ethereum, Solidity & Web3.js

By Gregory McCubbin ·

Blockchain is one of the most exciting technological innovations of the 21st century, opening up a world of new possibilities for developers.

In this guide, you’ll learn first hand how to harness the power of this exciting new technology in your own applications. Use this 2-hour video below to accompany this written guide.

It doesn’t matter whether you’re an experienced developer, or just learning to code -- you don’t need to be a blockchain expert to follow along. I’ll teach you blockchain programming from scratch by building a full application step-by-step.

Let’s get a sneak peak of the application that we’re going to build in this tutorial. We’ll create a digital collectible game inspired by the popular website Cryptokitties.

Cryptokitties

Cryptokitties is a famous blockchain game where you can collect and breed digital cats. Instead of collecting kittens in our app, we’re going to collect unique color tokens instead.

Blockchain Programming

You'll be able to create new color tokens and claim them so that they can be held in a digital blockchain wallet.

What is a Blockchain?

If you’re brand new, you might be wondering what a blockchain is or how it works. That’s alright! I’ll show you.

Let’s look at how we’ll use the blockchain in the application that we’ll build in order to understand how it works.

Inside our app, we’re going to create new collectible color tokens. If we were going to do this inside a traditional application, we would normally store these tokens inside a database that we control.

This would work just fine, but it has one major problem: you cannot take full ownership of your collectible item and send it somewhere else.

Let’s say you wanted to withdraw your color token and use it inside another application.

You can't do that with a traditional database, but you CAN do it with a blockchain application.

See, a blockchain is a giant shared database that anyone can access on the web:

Blockchain Diagram

It’s a peer-to-peer network of computers, or nodes, that all talk to one another. All of these computers share the responsibility of storing the data redundantly across each computer.

This redundancy is what allows the data to be stored publicly — it prevents discrepancies in the data because all of the computers must agree that they have the same information!

In our case, the blockchain will keep track of how many color tokens have been created, and who owns them. It will also facilitate the transfer of color tokens between users.

What is a Non-Fungible Token (ERC-721)?

Just like CryptoKitties, our color tokens are going to be non-fungible tokens.

ERC-721 Non-Fungible Token

“What on earth does that mean?” You might ask.

Haha, that’s quite alright. Let me explain.

You might have heard of some cryptocurrencies referred to as “tokens”. These are a specific type of token that represent money.

Some real world examples are “0x”, “OMG”, “MKR”. These are all ERC-20 tokens powered by the Ethereum blockchain.

Because these tokens represent money, they’re called “fungible” tokens (as opposed to "non-fungible”). Don’t let me lose you yet! I’ll explain what that means.

A fungible token represents an asset that can be exchanged for any other asset of equal value in its class.

Us Dollar Cryptocurrency

For example, 1 US dollar is equal to any other US dollar. You can freely exchange $1 bills with one another because they’re worth the same.

That’s fungibility.

However, think about other collectible items like trading cards for example.

Holographic Charizard Non-Fungible Asset

One Pokemon card might be extremely valuable compared to another Pokemon card. You wouldn’t want to trade your holographic Charizard for any other low-value Pokemon!

That’s NON-fungibility.

Pokemon cards are non-fungible because one trading card isn’t necessarily worth the same as another.

In computer science terms, one way to think about it is this: non-fungible assets have an ID, but fungible assets do not (because they’re not unique).

In our application, we’re going to create a non-fungible token to represent our collectible color tokens.

Just like CryptoKitties, we’ll use the ERC-721 standard to code it in our app. This is simply an agreed-upon convention for how these tokens are built so that they can be bought, sold, traded, and kept in digital wallets.

Part 1: Project Setup

Now let’s set up your local blockchain programming environment so that you can start building the project. We’ll start by installing all the dependencies you need.

Now let’s set up your local blockchain programming environment so that you can start building the project. We’ll start by installing all the dependencies you need.

Node.js

The first dependency is NPM, which comes bundled with Node.js. We’ll use NPM & Node to install other packages, and run the app we’ll create in this tutorial.

You might have node already installed. You can check by typing this command into your terminal:

$ node -v

If you haven’t installed it yet, you can download it directly from the Node.js website.

Ganache Personal Blockchain

he next dependency is the Ganache personal blockchain. This is a development blockchain that will run on your local machine, that will mimic the behavior of a real live blockchain.

You’ll be able to to create smart contracts, and build blockchain applications with Ganache without having to risk actual money on a real blockchain. 🙂

You can download the latest version of Ganache for your operating system here.

Once you’ve installed Ganache, go ahead and open it.

Ganache Personal Blockchain

Hooray! Now you have a development blockchain running on your computer!

Here you can see that Ganache provide 10 blockchain accounts, each credited with 100 Ether (this is fake cryptocurrency; it isn’t worth anything).

Each account has an address, which you can see above. This is much like the “username” on the blockchain.

Additionally, each account also has a private key, which is like a password. You can reveal the private key by clicking the “key” icon, but make sure you don’t share this information with anyone else.

Truffle Framework

The next dependency is the Truffle framework which will allow you to develop smart contracts, the building blocks of blockchain based applications.

Truffle will allow you to write the source code for the smart contracts, create tests, compile them, and deploy them to a blockchain.

You can install Truffle from your command line with NPM like this:

$ npm install -g [email protected]

NOTE: you must use this exact version of Truffle that I’ve specified here in order to follow along with this tutorial.

Metamask Ethereum Wallet

Metamask Ethereum Wallet Browser Extension

The next dependency is Metamask. This is an Ethereum Wallet that turns your web browser into a blockchain browser. Mostly likely, the web browser you’re reading this on doesn’t connect to the blockchain on its own. That’s why you must install the Metamask extension for Google Chrome.

You can install Metamask with this link, or search for it in the Google Chrome web store. Once you’ve installed it, make sure it is enabled it in your list of Chrome extensions (it should be "checked"). You can confirm that it has been activated when you see a fox icon in the top right hand corner of your Chrome browser.

Part 2: Create Smart Contract

Now let’s jump in and start doing actual blockchain programming! We’ll start by creating a new project where we’ll store all the application code.

Instead of setting up everything from scratch, I’m going to use a custom template that I’ve already created so that you can get started quickly.

You can clone this template from GitHub and simultaneously create your new project like this:

$ git clone https://github.com/dappuniversity/starter_kit nft

Hooray! You’ve just created a new tutorial project called “nft”.

You can enter into he newly created project directory like this:

$ cd nft

Now open this project code inside your favorite text editor of choice (if you don’t have one already, I like Sublime Text).

If you browse the project directory structure, you’ll notice that I’ve already done all the configuration for you. Let me turn your attention to the truffle-config.js file:

This is the main settings file for the project. It defines how the project connects to the blockchain and more.

For example, I have already connected to Ganache on 127.0.0.1 port 7545 (see the networks > development part).

Also, if you’re familiar with truffle already, you might notice that I’ve modified so that the smart contracts directory is nested under the src folder. Doing this allows us to expose the smart contracts to the user interface, as we’ll see later in the tutorial!

Now let’s start coding the smart contract. Create a new file inside your project like this:

$ touch ./src/contracts/Color.sol

This is the file where we’ll write all the color token source code with the Solidity programming language.

Let’s create the basic structure for the smart contract like this:

pragma solidity ^0.5.0;

contract Color {

}

Let me explain this code. First, we start off by declaring the version of the Solidity programming language that we’ll use to code the smart contract, in this case version 0.5.0.

Then, we create the smart contract with the contract keyword, and the name Color. All the remaining Solidity code that we’ll write in this tutorial will go inside of the curly braces.

Now let me pause and explain something. Because we’re creating a token that follows a specific set of rules, i.e., the ERC-721 standard, there’s no need to reinvent the wheel when developing this.

See, ERC-721 is a standard that specifies how non-fungible tokens work. It requires that each token have a specific set of functions and behavior so that it’s compatible with wallets, marketplaces, and more.

Because it follows a specific set of rules, we can rely upon an external library to provide most of the code that we need for this token without having to write everything ourselves.

In this case, we’re going to use the OpenZeppelin solidity library. You must install it in your project before continuing like this:

$ nom install @openzeppelin/contracts —save

Next let’s install another dependency which will help us use this library in our project:

$ npm install truffle-flattener —save

Now that that’s installed, run this command from your terminal:

$ ./node_modules/.bin/truffle-flattener ./node_modules/@openzeppelin/contracts/token/ERC721/ERC721Full.sol > src/contracts/ERC721Full.sol

Let me explain what this does. We’re going to import an ERC-721 token library into the smart contract that we’ll create today from the OpenZeppelin library.

To avoid version conflicts, we just extracted all the code from this library with truffle flattener and moved it to the same directory as the smart contract we’re going to develop in this tutorial. You can watch my full explanation of this process in the video for a more in depth explanation.

Now that we have this dependency in our project, let’s import the library into our smart contract like this:

pragma solidity ^0.5.0;

import "./ERC721Full.sol";

contract Color is ERC721Full {

}

Let me explain what we just did. We imported the ERC721 full library from our project with the import line. Then make our smart contract inherit from this library with this line:

contract Color is ERC721Full {

This will allow our contract to absorb all the basic behavior supplied by the ERC721 library from Openzeppelin that we just installed.

Next, we can customize our token by giving it a name and a symbol like this:

contract Color is ERC721Full {

  constructor() ERC721Full("Color", "COLOR") public {
  }

}

In this step, we added a constructor function to customize the token. This is a special function that gets run once and only once: whenever the smart contract is created the first time, i.e., deployed to the blockchain.

In our case, we also called the constructor function of the parent smart contract ERC721Full and passing in custom arguments like the name ”Color" and the symbol“COLOR".

Great! Now we have a basic smart contract. Before we continue, let's ensure that everything is working properly. We will check to ensure that the source code compiles like this:

$ truffle compile

YAY! It worked. If you experience errors in this step, go back and ensure that your code matches the final project.

Now let's add some test coverage for the smart contract. We will create a new file for the tests like this:

$ mkdir test/Color.test.js

Inside this file, we'll create our first test like this:

const Color = artifacts.require('./Color.sol')

require('chai')
  .use(require('chai-as-promised'))
  .should()

contract('Color', (accounts) => {
  let contract

  before(async () => {
    contract = await Color.deployed()
  })

  describe('deployment', async () => {
    it('deploys successfully', async () => {
      const address = contract.address
      assert.notEqual(address, 0x0)
      assert.notEqual(address, '')
      assert.notEqual(address, null)
      assert.notEqual(address, undefined)
    })

    it('has a name', async () => {
      const name = await contract.name()
      assert.equal(name, 'Color')
    })

    it('has a symbol', async () => {
      const symbol = await contract.symbol()
      assert.equal(symbol, 'COLOR')
    })

  })

})

Let me explain this code:

  • First, we set up the test by importing the smart contract artifact, i.e., const Color = artifacts.require('./Color.sol').
  • Then, we create the layout for the test using syntax provided by the Mocha testing framework. We use the contract function to compartmentalize our smart contract's test suite.
  • Next, we fetch a deployed copy of the smart contract from the blockchain with contract = await Color.deployed(). We do this before each test example runs because we call it inside the before() function.
  • Finally, we write 3 test examples that check for the following: 1. successful deployment, 2. correct name, i.e., "Color", and 3. correct symbol, i.e., "COLOR".

Now we can run the tests like this:

$ truffle test

YAY! They pass. 😃

Part 3: Mint Tokens

Now let's continue developing the smart contract. We'll add a function that will allow us to create new color tokens like this:

// E.G. color = "#FFFFFF"
function mint(string memory _color) public {

}

This is the basic structure of the function. It will accept 1 argmument of string type, which will be a hexidecimal code that corresponds to the token's color. For example, if we want to create a red token, we will pass "#FF0000" when we call this function. Or if we want to create a black token, we'll use "#000000".

Also, note that we declared this function public so that it can be called outside of the smart contract, i.e., from our tests and from the client side application which we'll create soon!

Next, let's create a place to keep track of the color tokens whenever they're created. We'll use an array, which we can declare at the top of our smart contract like this:

contract Color is ERC721Full {
  string[] public colors;
  // ....
}

Next, we'll create a fast "lookup" that will tell us if the color exists. We'll use a "mapping" like this:

contract Color is ERC721Full {
  string[] public colors;
  mapping(string => bool) _colorExists;
  // ....
}

This mapping is a key-value store, much like an associative array or hash in other programming languages. It will use the color's hex code (string) as a key, and will return a boolean value (true/false) that will tell us whether the color exists or not. We're using this mapping because this functionality is not provided by the colors array. We'll see how it's used in the next step.

Now let's finish building the mint() function. First, we'll add the colors to the array this:

// E.G. color = "#FFFFFF"
function mint(string memory _color) public {
  uint _id = colors.push(_color);
}

We'll use the index of the color in the array as its _id. We can do this because the pus() method returns the index, so we can conveniently store its value in the _id variable.

Next, we'll call the _mint() function from the open zeppelin ERC721Full contract that we inherit from like this:

// E.G. color = "#FFFFFF"
function mint(string memory _color) public {
  uint _id = colors.push(_color);
  _mint(msg.sender, _id);
}

This function accepts 2 arguments: the owner of the token, and the id. We'll use Solidity's special msg.sender, which tells us the address of the user that's calling the mint() function. Then we'll use the _id that we created in the previous step.

Next, we'll add the color to the _colorExists mapping like this:

// E.G. color = "#FFFFFF"
function mint(string memory _color) public {
  uint _id = colors.push(_color);
  _mint(msg.sender, _id);
  _colorExists[_color] = true;
}

Finally, we'll check to make sure that the color DOES NOT exist before we execute the function like this:

// E.G. color = "#FFFFFF"
function mint(string memory _color) public {
  require(!_colorExists[_color]);
  uint _id = colors.push(_color);
  _mint(msg.sender, _id);
  _colorExists[_color] = true;
}

We use Solidity's require() function to do this. If the expression passed into this function evaluates to true, then the mint() function will continue execution. If it's false, it will stop execution, throw an error, and no other code will run inside the mint() function.

This will prevent users from creating the same color twice, i.e., they won't be able to create 2 tokens of the same hex value "#FFFFFF". In this case, if the color exists already, the function will halt and throw an exception. If it doesn't exist yet, the function will continue as normal.

Now let's add test coverage for all of this code. Head over to your test suite, and create a new test example for the mint() function like this:

const Color = artifacts.require('./Color.sol')

require('chai')
  .use(require('chai-as-promised'))
  .should()

contract('Color', (accounts) => {
  let contract

  before(async () => {
    contract = await Color.deployed()
  })

  // ...

  describe('minting', async () => {

    it('creates a new token', async () => {
      const result = await contract.mint('#EC058E')
      const totalSupply = await contract.totalSupply()
      // SUCCESS
      assert.equal(totalSupply, 1)
      const event = result.logs[0].args
      assert.equal(event.tokenId.toNumber(), 1, 'id is correct')
      assert.equal(event.from, '0x0000000000000000000000000000000000000000', 'from is correct')
      assert.equal(event.to, accounts[0], 'to is correct')

      // FAILURE: cannot mint same color twice
      await contract.mint('#EC058E').should.be.rejected;
    })
  })

})

Let me explain this test:

  • First, we call the mint() function to check what the result is after.
  • Then, we assert that the totalSupply() is correct, i.e., 1 because we just have 1 token at this point.
  • Next, we check the event logs from with result.logs[0].args. This allows us to check that id, to, and from are correct.
  • Finally, we ensure that the mint() function fails if we try to mint the same color twice, i.e., "EC058E".

Now we can add the final test example that checks to see if we can list out all the colors. First, we'll mint several color tokens, and then ensure that they're returned from the colors array like this:

const Color = artifacts.require('./Color.sol')

require('chai')
  .use(require('chai-as-promised'))
  .should()

contract('Color', (accounts) => {
  let contract

  before(async () => {
    contract = await Color.deployed()
  })

  // ...

  describe('indexing', async () => {
    it('lists colors', async () => {
      // Mint 3 more tokens
      await contract.mint('#5386E4')
      await contract.mint('#FFFFFF')
      await contract.mint('#000000')
      const totalSupply = await contract.totalSupply()

      let color
      let result = []

      for (var i = 1; i <= totalSupply; i++) {
        color = await contract.colors(i - 1)
        result.push(color)
      }

      let expected = ['#EC058E', '#5386E4', '#FFFFFF', '#000000']
      assert.equal(result.join(','), expected.join(','))
    })
  })

})

Now we have completed the entire test suite for the smart contract. Let's run all the tests to see if they pass:

$ truffle test

YAY! Green tests. ✅ 😃

Now we have one final step before we continue. We must deploy the smart contract to the blockchain.

First, make sure that Ganache is running.

Then, create a new migrations file like this:

$ touch migrations/2_deploy_contracts.js

Now add the following code inside this file:

const Color = artifacts.require("Color");

module.exports = function(deployer) {
  deployer.deploy(Color);
};

This file is responsible for taking the smart contract and deploying it to the blockchain. We can execute this file, and put the smart contract on the blockchain, by running the migration like this:

$ truffle migrate

Part 4: Client Side Application

Yay! Now we have everything we need to build out the client side application that will allow us to create new color tokens, and list out all of the existing tokens on the page.

Before we continue, let's start the web server process so that we can observe our code changes as we build the client side app. Run this command from your terminal to do just that:

$ npm run start

You should automatically see a new web page pop up that looks like this:

Blockchain Website Starter Kit

The interface you see here is provided by the starter kit template that you cloned when you set the project up. We can use this as a starting point. We'll modify all the code in the src/components/App.js file to create our client side application.

The first thing we'll do is connect our application to the blockchain. We'll do this with the Web3.jslibrary. In order to use it, we must first import it at the top of the App.js file like this:

import React, { Component } from 'react';
import Web3 from 'web3'
import './App.css';

Next, we'll instantiate the web3 connection by creating a new function called loadWeb3() inside the component like this:

class App extends Component {

  async loadWeb3() {
    if (window.ethereum) {
      window.web3 = new Web3(window.ethereum)
      await window.ethereum.enable()
    }
    else if (window.web3) {
      window.web3 = new Web3(window.web3.currentProvider)
    }
    else {
      window.alert('Non-Ethereum browser detected. You should consider trying MetaMask!')
    }
  }

  // ...
}

Don't worry if the code inside this function doesn't make sense, or even looks scary! I have simply followed the exact instructions provided by Metamask on how to implement a web3 connection in our application. Because they've provided this for us, there's no need to reinvent the wheel here (you can watch the video for a further explanation).

Now we must call this function. We'll create a new special function called componentWillMount() provided by React that will be called whenever the component loads. We can call loadWeb3() inside it like this:

class App extends Component {

  async componentWillMount() {
    await this.loadWeb3()
  }
  // ...
}

Now let's load all the data from the blockchain. In order to do this, we must first import the smart contract ABI into the component. We'll do that at the top just like this:

import React, { Component } from 'react';
import Web3 from 'web3'
import './App.css';
import Color from '../abis/Color.json'

Now let's create a function to load all of the blockchain data like this:

class App extends Component {
  // ...
async loadBlockchainData() {
  const web3 = window.web3
  // Load account
  const accounts = await web3.eth.getAccounts()
  this.setState({ account: accounts[0] })

  const networkId = await web3.eth.net.getId()
  const networkData = Color.networks[networkId]
  if(networkData) {
    const abi = Color.abi
    const address = networkData.address
    const contract = new web3.eth.Contract(abi, address)
    this.setState({ contract })
    const totalSupply = await contract.methods.totalSupply().call()
    this.setState({ totalSupply })
    // Load Colors
    for (var i = 1; i <= totalSupply; i++) {
      const color = await contract.methods.colors(i - 1).call()
      this.setState({
        colors: [...this.state.colors, color]
      })
    }
  } else {
    window.alert('Smart contract not deployed to detected network.')
  }
}

This code does several things:

  • First, we fetch the account from Metamask with web3.eth.getAccounts(). Then, we add it to the react state object with this.setState({ account: accounts[0] }).
  • Next, we detect the network from Metamask, i.e., "Ganache", with await web3.eth.net.getId().
  • Then, we see if the smart contract has an address on the detected network with Color.networks[networkId]. If it does, we'll continue loading. If not, we'll alert the user.
  • Then, we create a JavaScript version of the smart contract with web3 using the abi and the address with new web3.eth.Contract(abi, address), and then assign it to the React state object.
  • Finally, we fetch all of the colors and assign them to the React state object so that they can be loaded on the page. This should look very similar to the procedure that we did inside of the test suite.

Now, let's do 2 more things before we finish off the client side app. First, let's set the default state inside of React's constructor function like this:

constructor(props) {
  super(props)
  this.state = {
    account: '',
    contract: null,
    totalSupply: 0,
    colors: []
  }
}

Finally, let's create a function that will fire whenever we mint new tokens like this:

mint = (color) => {
  this.state.contract.methods.mint(color).send({ from: this.state.account })
  .once('receipt', (receipt) => {
    this.setState({
      colors: [...this.state.colors, color]
    })
  })
}

This function uses web3 to interact with the smart contract, and call its mint() function, specifying th e address of the user who's signing the transaction. You can see a full explanation of this function in the video.

Fwew, we've covered a lot of ground here! We just have 1 final step which is to add all of the HTML code for the application. You can do this by clearing out all of the code inside the render() function and replacing it with this:

render() {
  return (
    <div>
      <nav className="navbar navbar-dark fixed-top bg-dark flex-md-nowrap p-0 shadow">
        <a
          className="navbar-brand col-sm-3 col-md-2 mr-0"
          href="http://www.dappuniversity.com/bootcamp"
          target="_blank"
          rel="noopener noreferrer"
        >
          Color Tokens
        </a>
        <ul className="navbar-nav px-3">
          <li className="nav-item text-nowrap d-none d-sm-none d-sm-block">
            <small className="text-white"><span id="account">{this.state.account}</span></small>
          </li>
        </ul>
      </nav>
      <div className="container-fluid mt-5">
        <div className="row">
          <main role="main" className="col-lg-12 d-flex text-center">
            <div className="content mr-auto ml-auto">
              <h1>Issue Token</h1>
              <form onSubmit={(event) => {
                event.preventDefault()
                const color = this.color.value
                this.mint(color)
              }}>
                <input
                  type='text'
                  className='form-control mb-1'
                  placeholder='e.g. #FFFFFF'
                  ref={(input) => { this.color = input }}
                />
                <input
                  type='submit'
                  className='btn btn-block btn-primary'
                  value='MINT'
                />
              </form>
            </div>
          </main>
        </div>
        <hr/>
        <div className="row text-center">
          { this.state.colors.map((color, key) => {
            return(
              <div key={key} className="col-md-3 mb-3">
                <div className="token" style={{ backgroundColor: color }}></div>
                <div>{color}</div>
              </div>
            )
          })}
        </div>
      </div>
    </div>
  );

This code serves 3 primary functions:

  • Displays the current address on the Navbar
  • Provides a form to issue new tokens, i.e., mint() them
  • Displays all the existing tokens on the page in a 3-column grid system, showing the color value and the color code for each token

At this point, your completed App.js file should looks like this:

import React, { Component } from 'react';
import Web3 from 'web3'
import './App.css';
import Color from '../abis/Color.json'

class App extends Component {

  async componentWillMount() {
    await this.loadWeb3()
    await this.loadBlockchainData()
  }

  async loadWeb3() {
    if (window.ethereum) {
      window.web3 = new Web3(window.ethereum)
      await window.ethereum.enable()
    }
    else if (window.web3) {
      window.web3 = new Web3(window.web3.currentProvider)
    }
    else {
      window.alert('Non-Ethereum browser detected. You should consider trying MetaMask!')
    }
  }

  async loadBlockchainData() {
    const web3 = window.web3
    // Load account
    const accounts = await web3.eth.getAccounts()
    this.setState({ account: accounts[0] })

    const networkId = await web3.eth.net.getId()
    const networkData = Color.networks[networkId]
    if(networkData) {
      const abi = Color.abi
      const address = networkData.address
      const contract = new web3.eth.Contract(abi, address)
      this.setState({ contract })
      const totalSupply = await contract.methods.totalSupply().call()
      this.setState({ totalSupply })
      // Load Colors
      for (var i = 1; i <= totalSupply; i++) {
        const color = await contract.methods.colors(i - 1).call()
        this.setState({
          colors: [...this.state.colors, color]
        })
      }
    } else {
      window.alert('Smart contract not deployed to detected network.')
    }
  }

  mint = (color) => {
    this.state.contract.methods.mint(color).send({ from: this.state.account })
    .once('receipt', (receipt) => {
      this.setState({
        colors: [...this.state.colors, color]
      })
    })
  }

  constructor(props) {
    super(props)
    this.state = {
      account: '',
      contract: null,
      totalSupply: 0,
      colors: []
    }
  }

  render() {
    return (
      <div>
        <nav className="navbar navbar-dark fixed-top bg-dark flex-md-nowrap p-0 shadow">
          <a
            className="navbar-brand col-sm-3 col-md-2 mr-0"
            href="http://www.dappuniversity.com/bootcamp"
            target="_blank"
            rel="noopener noreferrer"
          >
            Color Tokens
          </a>
          <ul className="navbar-nav px-3">
            <li className="nav-item text-nowrap d-none d-sm-none d-sm-block">
              <small className="text-white"><span id="account">{this.state.account}</span></small>
            </li>
          </ul>
        </nav>
        <div className="container-fluid mt-5">
          <div className="row">
            <main role="main" className="col-lg-12 d-flex text-center">
              <div className="content mr-auto ml-auto">
                <h1>Issue Token</h1>
                <form onSubmit={(event) => {
                  event.preventDefault()
                  const color = this.color.value
                  this.mint(color)
                }}>
                  <input
                    type='text'
                    className='form-control mb-1'
                    placeholder='e.g. #FFFFFF'
                    ref={(input) => { this.color = input }}
                  />
                  <input
                    type='submit'
                    className='btn btn-block btn-primary'
                    value='MINT'
                  />
                </form>
              </div>
            </main>
          </div>
          <hr/>
          <div className="row text-center">
            { this.state.colors.map((color, key) => {
              return(
                <div key={key} className="col-md-3 mb-3">
                  <div className="token" style={{ backgroundColor: color }}></div>
                  <div>{color}</div>
                </div>
              )
            })}
          </div>
        </div>
      </div>
    );
  }
}

export default App;

Hooray! You have just completed your first blockchain application. Now go follow the instructions in the video on how to add your account from Ganache to Metamask so that you can start issuing tokens with the app.

Happy with this tutorial? Then you NEED to join my free training here where I'll show you how to build a real world blockchain app so that you can become a highly paid blockchain developer!