create account

Building Ethereum Dapps With ReactJS + Truffle Contract + Web3, A UI For TokenZendR A Smart Contract That Transfers ERC20 Tokens To Other Addresses by alofe.oluwafemi

View this thread on: hive.blogpeakd.comecency.com
· @alofe.oluwafemi · (edited)
$38.10
Building Ethereum Dapps With ReactJS + Truffle Contract + Web3, A UI For TokenZendR A Smart Contract That Transfers ERC20 Tokens To Other Addresses
#### Repository
https://github.com/facebook/react

![enter image description here](https://s3.amazonaws.com/alofe.oluwafemi/token-zender-app.gif)

### Overview 
In my last  [Post](https://steemit.com/utopian-io/@alofe.oluwafemi/create-a-smart-contract-that-transfers-erc20-tokens-to-any-erc20-compliant-address)  i wrote a tutorial on how you can code a smart contract that helps you transfer any of your ERC20 compatible token to another address, be it exchange wallet , metamask or MEW.  And we were able to write tests to assert that our smart contract works the way we want it to, also interact with it via the truffle console. If you have not checked it out yet,  you probably should.

This tutorial is a second part where we would build a clean interface using **React** together with **Truffle Contract** ,**web3js** and  **Bulma** for UI, that a user visiting our application can use to interact with the smart contract on the blockchain. 

#### What Will I Learn?
In the second part of this tutorial, you will learn how to: 

- Build standard Dapps interfaces using React
- Architecting And Workflow for easily building an interface for Dapps, built with truffle 
- Using **Truffle Contract** in combination with Web3 to interact with our contract on the blockchain

#### Requirements
For this tutorial, it's required for you to clone the repository from the previous tutorial, although it's not a requirement to read the first tutorial before you jump right into this one, as each tutorial address two different aspects of building an Ethereum Dapps and therefore can be considered self-contained.

- Clone the token-zender smart contract https://github.com/slim12kg/tokenzendr-contract
- Trufflesuite installed [installation guide here](https://github.com/trufflesuite/truffle)
- Ganache installed [private blockchain server](https://github.com/trufflesuite/ganache/releases)
- Metamask installed [install for chrome,firefox,opera ](https://metamask.io/)
- npx installed (npx comes with with npm 5.2+)
- Basic knowledge of React

This tutorial assumes you are using a *UNIX operating system*

#### Difficulty
- Intermediate

#### Tutorial Contents

To start with, create a new react app

```js
npx create-react-app token-zendr
```
![enter image description here](https://s3.amazonaws.com/alofe.oluwafemi/Series+2+-+1.png)

Remeber ealier we mention we will be using web3js, bulma & truffle contract. Now is the time to install them, to do so replace your project `package.json`  with what you see below, then run `npm install`

```js
{  
  "name": "token-zendr",  
  "version": "0.1.0",  
  "private": true,  
  "dependencies": {  
  "bulma-start": "0.0.2",  
  "react": "^16.4.2",  
  "react-dom": "^16.4.2",  
  "react-scripts": "1.1.4",  
  "truffle-contract": "^3.0.6",  
  "web3-js": "^1.0.5-beta.26"  
  },  
  "scripts": {  
  "start": "react-scripts start",  
  "build": "react-scripts build",  
  "test": "react-scripts test --env=jsdom",  
  "eject": "react-scripts eject"  
  }  
}
```
After successfull installation of the packages, fire up your app with `npm start`, your default browser will automatically fire up a new tab with the default react screen showing. Leave the tab open.

Before we proceed forward i need you to clone the smart contract that handles the transfer and deploy it on a your private blockchain (Ganache).

**Note** : Ganache must have been started on your machine before you run the following commands 

```js
git clone https://github.com/slim12kg/tokenzendr-contract.git

cd tokenzendr-contract

npm install

truffle console

truffle(development)> compile

truffle(development)> migrate
```
![enter image description here](https://s3.amazonaws.com/alofe.oluwafemi/Series-2-2.png)

If you open your Ganache, you should see the transactions mined and new blocks created similar to the images below

#### 8 Blocks Mined
![enter image description here](https://s3.amazonaws.com/alofe.oluwafemi/Series2-3.png)

#### Transactions Log Showing Contract Creation & Call
![enter image description here](https://s3.amazonaws.com/alofe.oluwafemi/Series-2-4.png)

One of the challenges, developers new to building ethereum Dapps faces is to have to always edit the address of the contract in thier code anytime the contract is redeployed. To solve the issue, my approach is to create a soft link of the **build** directory in the token *tokenzendr-contract* to the *src* directory of our new project.

This ensures that anytime the contract is redeployed we will be referencing the updated contract address. Genuis !!

From your command line run this command, substituiting your project path in the command as it applies to your project

```js
//Remember to substituite your project path as it applies to you
ln -s ~/Desktop/Dapps/tokenzendr-contract/build/ ~/Desktop/Dapps/token-zendr/src!
```
![Soft link build directory](https://s3.amazonaws.com/alofe.oluwafemi/Series-2-5.png)

If your screenshot looks like what we have above then you should carry on with the tutorial, else check that you specified the correct directory path and have substituited correctly the directory path that applies to you.

As a autonomous smart developer, we want to only support to transfer some vetted tokens or maybe the token creator will have to pay us to support the transfer of their token on our platform. The smart contract already have a method to add or remove supported tokens, but on our frontend we want to also do the same thing, but this time just list them in a json file with the token address, name, symbol, decimal.

Looking back on the smart contract we deployed to our private blockchain, one important thing to realise is that we did not only deploy the smart contract that handles the token transfer but also two ERC20 tokens (BearToken, CubToken) contract that we will be using for testing purpose in this tutorial. We will be transfering them between two addresses in our wallet which will be connected to the same same network they were deployed to.

Now that i've mentioned about two token been deployed alongside our transfer contract, you need to connect your *metamask* to custom *RPC*.  Click on *mainnet* a dropdown will drop with the available option to select custom RPC, click this option and enter the address `http://127.0.0.1:7545` for new RPC.

![Soft link build directory](https://s3.amazonaws.com/alofe.oluwafemi/Series-2-6.png)
 
Enter new RPC URL
![Soft link build directory](https://s3.amazonaws.com/alofe.oluwafemi/Series-2-7.png)

See the first address same as the one in Ganache show up with the same balance

![Soft link build directory](https://s3.amazonaws.com/alofe.oluwafemi/Series-2-8.png)

When the two tokens contract was deployed, it assigns all the total token supply to the address that was used to deploy the contract, in our own case the first account. We want the token balances to show in our wallet, this way you see your balance history as you make transfers, to do this simply open the `contract` directory of the build folder we created soft link for ealier, you would see files  `BearToken.json` and `CubToken.json` files. Under the *networks* section copy the `address` (contact address) value and add them as new tokens to metamask.

```js
...
"networks": {  
  "5777": {  
  "events": {},  
  "links": {},  
  "address": "0x8f0483125fcb9aaaefa9209d8e9d7b9c8b9fb90f",  
  "transactionHash": "0x2a3fd7782a1a7b5c4b388f639e949cac29ca9d51ed0343be91eb8b0b110c8f81"  
  }  
},
...
```
![Soft link build directory](https://s3.amazonaws.com/alofe.oluwafemi/Series-2-9.png)

We will begin by start writing our react components. This tutorial is a not beginners post therefore we won't  be coding all from scratch as this will obviously take a longer time. Instead i will pick at each component at a time and mention the role each play while relating it to the `App.js` file.

Oh, one quick addition to the setup process, create a `Tokens` directory in the `src` directory of the project, add three files `all.js`, `Bear.js` and Cub.js. The `Bear.js`and `Cub.js`will carry the information of the each tokens such as the `address`, `decimal`, `name`, `symbol`, `icon` and most importantly the `abi` and this will be the process of adding a new supported token. You can get the address, decimal and abi from the json file of each tokens in the build directory, usually on mainnet you can always get this information from etherscan.io

Finally create `icons` folder in the public folder and add the cub and bear token icons. You can find them here https://github.com/slim12kg/token-zendr-react-interface/tree/master/public/icons

In actual sense, the *ABI* (Application Baniary Interface) will contain much more information than displayed below.
```js
export default {  
  address: "0x8f0483125fcb9aaaefa9209d8e9d7b9c8b9fb90f",  
  decimal: 5,  
  name: "BearToken",  
  symbol: "Bear",  
  icon: "bear_x28.png",  
  abi: [  
	 {  
		"constant": true,  
		"inputs": [],  
		"name": "name",  
		"outputs": [  
			{  
				"name": "",  
				"type": "string"  
			 }  
		],  
	   ...,
	   "name": "Transfer",  
	  "type": "event"  
	  }  
	 ]
 }
```
Create a `Components` folder in the `src` directory, add the following components

```jsx
//InstallMetamask.js
import React from 'react';  
  
function InstallMetamask() {  
  return (  
 <div className="modal is-active">  
	 <div className="modal-background"></div>  
	 <div className="modal-content">  
		 <p className="image download-metamask">  
			 <a href="https://metamask.io/" rel="noopener noreferrer" target="_blank">  
			 <img src="https://metamask.io/img/metamask.png" alt=""></img>
			 </a>  
		 </p>  
	 </div>  
	 <button className="modal-close is-large" aria-label="close"></button>  
 </div>  
 )}  
  
export default InstallMetamask;	
```
This is a notification component that shows up when a user doesn't have metamask installed.

```jsx
//UnlcokMetaMask.js
import React from 'react';  
  
function UnlockMetamask(props) {  
  return (  
	 <div className="column is-4 is-offset-4">  
		 <div className="notification is-danger">  
			 <button className="delete"></button>  
			 {props.message}  
		 </div>  
	 </div>  
 )}  
  
export default UnlockMetamask;
```
This is also a notification component that displays a warning if the user metamask account is locked.

```jsx
//Nav.js
import React, { Component } from 'react';  
  
class Nav extends Component {  
	  render(){  
		  return (  
			 <nav className="navbar is-link" aria-label="main navigation">  
				 <div className="navbar-brand">  
					 <a className="navbar-item" href="/">  
						 <strong><i className="fa fa-coins"></i> {this.props.appName}</strong>  
					 </a>  
					  
					 <a role="button" className="navbar-burger" aria-label="menu" aria-expanded="false">  
						 <span aria-hidden="true"></span>  
						 <span aria-hidden="true"></span>  
						 <span aria-hidden="true"></span>  
					 </a>  
				 </div>  
				 <div className="navbar-menu">  
					 <div className="navbar-end">  
						 <a className="navbar-item">  
							 <div className="tags has-addons">  
								 <span className="tag">  
								 <i className="fa fa-signal"></i> &nbsp; Network  
								                                </span>  
								 <span className="tag is-danger">{this.props.network}</span>  
							 </div>  
						 </a>  
					 </div>  
				 </div>  
			 </nav>  
		 ) 
	 }
 }  
  
export default Nav;
```
This component purpose is to serve as our application nav. Its is passed the value of the network we are curretly connect to.

```jsx
//Description.js
import React from 'react';  
  
function Description(props) {  
  return (  
	 <section className="container">  
		 <div className="has-text-centered content">  
			 <br/>  
			 <h1 className="title is-4 is-uppercase has-text-danger">Simple Way To Transfer</h1>  
			 <h2 className="subtitle is-6 has-text-grey-light">ERC20 Tokens</h2>  
		 </div>  
	 </section>  
 )}  
  
export default Description;
```
Simply a component to add a descripion for our application.  I used *Easy Way To Transfer ERC20 Tokens*, you can modify it to whatever suites you

```jsx
//Container.js

import React, { Component } from 'react';  
import AddressBar from './AddressBar';  
import TokenBlock from './TokenBlock';  
import TradeMarkBlock from './TradeMarkBlock';  
import SortTokenBlock from './SortTokenBlock';  
import TransferToken from './TransferToken';  
import TransferHeader from './TransferHeader';  
import SuccessTransaction from './SuccessTransaction';  
  
class Container extends Component {  
  render(){  
	  return (  
		 <section className="container">  
			 <div className="columns">  
				<div className="is-half is-offset-one-quarter column">  
						 <div className="panel">  
						 {  this.props.tx ?  
						 <SuccessTransaction tx={this.props.tx} /> :  
						  ''  
						  }  
					  
					 <AddressBar account={this.props.account} tx={this.props.tx}/>  
					 {  
						 this.props.transferDetail.hasOwnProperty('name') ?  
						 <div>  
							 <TransferHeader token={this.props.transferDetail} />  
							 <TransferToken closeTransfer={this.props.closeTransfer}  
										  transferDetail={this.props.transferDetail}  
										  fields={this.props.fields}  
										  account={this.props.account}  
										  Transfer={this.props.Transfer}  
										  inProgress={this.props.inProgress}  
										  defaultGasPrice={this.props.defaultGasPrice}  
										  defaultGasLimit={this.props.defaultGasLimit}  
										  onInputChangeUpdateField={this.props.onInputChangeUpdateField}/>  
					 </div> :  
					 <div className={this.props.tx ? 'is-hidden' : ''}>  
						 <SortTokenBlock />  
						 <TokenBlock newTransfer={this.props.newTransfer} tokens={this.props.tokens} />  
					 </div>  
					 } 
					 <TradeMarkBlock tx={this.props.tx}/>  
					 </div>  
				 </div>  
			 </div>  
		 </section>  
		 ) 
	 }
 }  
  
export default Container;
```

This *Container* component holds several other components , toggles some components display as state changes and passes down their respectives props to them passed from `app.js` to it.
 
 **AddressBar Component** displays the address of the active account 
 **TokenBlock Component** list the supported tokens available in a user wallet
  **TradeMarkBlock Component** card footer shows images of  badges
  **SortTokenBlock Component**  to sort list of only supported tokens in a wallet ASC/DESC
  **TransferToken Component** contains the form to  make transfer. It takes the address, amount to transfer and gas limit
  **TransferHeader Component** shows information of token initiated for transfer, its name and description
  **SuccessTransaction Component** displays notification message that shows right after a successfull transfer

Finally is our `App.js` that handle our state and event and passes data to the `Container` component. I will be commenting each section of the code to shed more light.

```jsx
import React, { Component } from 'react';  
import Web3 from 'web3'  
import TruffleContract from 'truffle-contract'  
import Tokens from './Tokens/all';  
import Nav from './Components/Nav';  
import Description from './Components/Description';  
import Container from './Components/Container';  
import InstallMetamask from './Components/InstallMetamask';  
import UnlockMetamask from './Components/UnlockMetamask';  
import TokenZendR from './build/contracts/TokenZendR.json';  
  
class App extends Component {  
  constructor(){  
	  super();  
	
	  this.tokens = Tokens;  //list of supported tokens by token-zendr contract
	  this.appName = 'TokenZendR';  
	  this.isWeb3 = true; //If metamask is installed  
	  this.isWeb3Locked = false; //If metamask account is locked  
	  
	  //bind this methods to enable them change state from children components
	  this.newTransfer = this.newTransfer.bind(this);  
	  this.closeTransfer = this.closeTransfer.bind(this);  
	  this.onInputChangeUpdateField = this.onInputChangeUpdateField.bind(this);  
	  
	  this.state = {  
		  tzAddress: null,  		//address of the token-zendr contract
		  inProgress: false,  		//if a transfer action is in progress
		  tx: null,  				//tx returned after a successfull transaction
		  network: 'Checking...',  	//default message to show while detecting network
		  account: null,  			//address of the currently unlocked metamask
		  tokens: [],  				//list of supported tokens owned by the user address
		  transferDetail: {},  
		  fields: {  				//form fields to be submitted for a transfer to be initiated 
			  receiver: null,  
			  amount: null,  
			  gasPrice: null,  
			  gasLimit: null,  
		 },  	
		 defaultGasPrice: null,  	
		 defaultGasLimit: 200000  
	  };  
	  
	  let web3 = window.web3;  
	  
	  //set web3 & truffle contract
	  if (typeof web3 !== 'undefined') {  
		  // Use Mist/MetaMask's provider  
		  this.web3Provider = web3.currentProvider;  
		  this.web3 = new Web3(web3.currentProvider);  
		  
		  this.tokenZendr = TruffleContract(TokenZendR);  
		  this.tokenZendr.setProvider(this.web3Provider);  
		  
		  if (web3.eth.coinbase === null) this.isWeb3Locked = true;  
  
	 }else{  
	  this.isWeb3 = false;  
	 } 
 }  
 
  //switch statement to check the current network and set the
  //value to be displayed on the nav component 
  setNetwork = () => {  
	  let networkName,that = this;  
	  
	  this.web3.version.getNetwork(function (err, networkId) {  
	  switch (networkId) {  
	  case "1":  
		  networkName = "Main";  
		  break;  
	  case "2":  
		  networkName = "Morden";  
		  break;  
	  case "3":  
		  networkName = "Ropsten";  
		  break;  
	  case "4":  
		  networkName = "Rinkeby";  
		  break;  
	  case "42":  
		  networkName = "Kovan";  
		  break;  
	  default:  
		  networkName = networkId;  
	 }  
	 
	 that.setState({  
		  network: networkName  
	   })  
	}); 
 };  
 
 //When a new transfer is initiated
 //set details of the token to be
 //transfered such as the address, symbol.. etc
  newTransfer = (index) => {  
	  this.setState({  
	  transferDetail: this.state.tokens[index]  
	 }) 
 }; 
  
  //Called at the end of a successful
  //transfer to cclear form fields & transferDetails
  closeTransfer = () => {  
	  this.setState({  
		  transferDetail: {},  
		  fields: {},  
	 }) 
 };  
	 
  setGasPrice = () => {  
	  this.web3.eth.getGasPrice((err,price) => {  
		  price = this.web3.fromWei(price,'gwei');  
		  if(!err) this.setState({defaultGasPrice: price.toNumber()})  
	 }); 
 };  
 
  setContractAddress = ()=> {  
	  this.tokenZendr.deployed().then((instance) => {  
	  this.setState({tzAddress: instance.address});  
	 }); 
 };  
 
 //Reset app state
  resetApp = () => {  
	  this.setState({  
		  transferDetail: {},  
		  fields: {  
			  receiver: null,  
			  amount: null,  
			  gasPrice: null,  
			  gasLimit: null,  
		 },  
		 defaultGasPrice: null,  
	 })
  };  
 
  Transfer = () => {  
	  //Set to true to allow some component disabled
	  //and button loader to show transaction progress
	  this.setState({  
		  inProgress: true  
	  });  
	  
	  //Use the ABI of a token at a particular address to call its methods
	  let contract = this.web3.eth.contract(this.state.transferDetail.abi)
						  .at(this.state.transferDetail.address);  
	  let transObj = {
	  from: this.state.account,
	  gas: this.state.defaultGasLimit,
	  gasPrice: this.state.defaultGasPrice
	  };  
	  let app = this;  
	  //Use the decimal places the token creator set to get actual amount of tokens to transfer
	  let amount = this.state.fields.amount + 'e' + this.state.transferDetail.decimal;  
	  let symbol = this.state.transferDetail.symbol;  
	  let receiver = this.state.fields.receiver;  
  
  amount = new this.web3.BigNumber(amount).toNumber();  
  
  //Approve the token-zendr contract to spend on your behalf
  contract.approve(this.state.tzAddress, amount ,transObj, (err,response)=>{  
	  if(!err) {  
		  app.tokenZendr.deployed().then((instance) => {  
			  this.tokenZendrInstance = instance;  
			  this.watchEvents();  
			  
			  //Transfer the token to third party on user behalf
			  this.tokenZendrInstance.transferTokens(symbol, receiver, amount, transObj)  
				 .then((response,err) => {  
					  if(response) { 
						  app.resetApp();  
						  
						  app.setState({  
							  tx: response.tx,  
							  inProgress: false  
						  });  
					 }else{  
					  console.log(err);  
					 } 
				 }); 
			 }) 
		 }else{  
		  console.log(err);  
		 } 
	 }); 
 };  
 
 /**  
 * @dev Just a console log to list all transfers  
 */  
 watchEvents() {  
  let param = {from: this.state.account,to: this.state.fields.receiver,amount: this.state.fields.amount};  
  
  this.tokenZendrInstance.TransferSuccessful(param, {  
	  fromBlock: 0,  
	  toBlock: 'latest'  
  }).watch((error, event) => {  
	  console.log(event);  
	 }) 
 }  
 
  onInputChangeUpdateField = (name,value) => {  
	  let fields = this.state.fields;  
  
	  fields[name] = value;  
  
	  this.setState({  
		  fields  
	  });  
 };  
 
  componentDidMount(){  
	  let account = this.web3.eth.coinbase;  
	  let app = this;  
	  
	  this.setNetwork();  
	  this.setGasPrice();  
	  this.setContractAddress();  
  
	  this.setState({  
		  account  
	  });  
  
	  //Loop through list of allowed tokens
	  //using the token ABI & contract address
	  //call the balanceOf method to see if this
	  //address carries the token, then list on UI
	  Tokens.forEach((token) => {  
		  let contract = this.web3.eth.contract(token.abi);  
		  let erc20Token = contract.at(token.address);  
	  
	  erc20Token.balanceOf(account,function (err,response) {  
		  if(!err) {  
			  let decimal = token.decimal;  
			  let precision = '1e' + decimal;  
			  let balance = response.c[0] / precision;  
			  let name = token.name;  
			  let symbol = token.symbol;  
			  let icon = token.icon;  
			  let abi = token.abi;  
			  let address = token.address;  
	  
			  balance = balance >= 0 ? balance : 0;  
	  
			  let tokens = app.state.tokens;  
	  
			  if(balance > 0) tokens.push({  
				  decimal,  
				  balance,  
				  name,  
				  symbol,  
				  icon,  
				  abi,  
				  address,  
			 });  
			 
			  app.setState({  
				  tokens  
	          })  
			} 
		 }); 
	 }); 
 }  
 
  render() {  
	  if(this.isWeb3) {  
		  if(this.isWeb3Locked) {  
		  return (  
			 <div>  
				 <Nav appName={this.appName} network={this.state.network} />  
				 <UnlockMetamask message="Unlock Your Metamask/Mist Wallet" />  
			 </div> 
		 ) 
		 }else {  
		  return (  
			 <div>  
				 <Nav appName={this.appName} network={this.state.network} />  
				 <Description />  
				 <Container onInputChangeUpdateField={this.onInputChangeUpdateField}  
							  transferDetail={this.state.transferDetail}  
							  closeTransfer={this.closeTransfer}  
							  newTransfer={this.newTransfer}  
							  Transfer={this.Transfer}  
							  account={this.state.account}  
							  defaultGasPrice={this.state.defaultGasPrice}  
							  defaultGasLimit={this.state.defaultGasLimit}  
							  tx={this.state.tx}  
							  inProgress={this.state.inProgress}  
							  fields={this.state.fields}  
							  tokens={this.state.tokens} />  
			 </div>  
			) 
		} 
	}else{  
		 return(  
			<InstallMetamask />  
		 ) 
		} 
	 }
 }  
  
export default App;
```
Rename the `app.css` in src folder and replace it with the style below.

```css
img.meta-trademark {  
  width: 20%;  
}  
  
#token-lists {  
  height: 300px;  
  overflow-y: scroll;  
}  
  
#token-lists div.token:nth-child(even) {  
  background: #f5f5f5;  
}  
  
#token-lists div.token {  
  cursor: pointer;  
}  
  
.sortby {  
  font-weight: 300;  
  cursor: pointer;  
}  
  
.token-icon {  
  width: 28px;  
  height: 28px;  
}  
  
.download-metamask {  
  height: 30%;  
  cursor: pointer;  
  width: 70%;  
  margin: auto;  
}  
  
.is-ellipsis {  
  overflow: hidden;  
  text-overflow: ellipsis;  
}
```

Add font-awesome library CDN to your     `public/index.html` 

```html
<!DOCTYPE html>  
<html lang="en">  
<head>  
 <meta charset="utf-8">  
 <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">  
 <meta name="theme-color" content="#000000">  
 <script defer  
 src="https://use.fontawesome.com/releases/v5.1.0/js/all.js"></script>  
  <!--  
 manifest.json provides metadata used when your web app is added to the homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/ -->  <link rel="manifest" href="%PUBLIC_URL%/manifest.json">  
 <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">  
 <title>React App</title>  
</head>  
<body>  
<noscript>  
  You need to enable JavaScript to run this app.  
</noscript>  
<div id="root"></div>  
</body>  
</html>
```

By now the application will be displaying but without bulma styling, to fix this problem we need to include bulma in the `src/index.js` file.  Simply replace your index.js with this

```js
import React from 'react';  
import ReactDOM from 'react-dom';  
import App from './App';  
import '.././node_modules/bulma-start/css/main.css'  
import './app.css';  
  
  
ReactDOM.render(<App />, document.getElementById('root'));
```
Run `npm start` or refresh the application if its already opened. When i do so the first screen am presented with because my metamask account is locked is this screenshot below.

![enter image description here](https://s3.amazonaws.com/alofe.oluwafemi/Series-2-10.png)

Then i enter my metamask password, refresh the application and Voila!!
![enter image description here](https://s3.amazonaws.com/alofe.oluwafemi/Series-2-11.png)

Interact with the application and see what else you can add. I hope you find this tutorial well explained, very educative with quality content. Let me hear your thought in the comment section.

#### Curriculum

- [Part A - Create A Smart Contract That Transfers ERC20 Tokens To Any ERC20 Compliant Address](https://steemit.com/utopian-io/@alofe.oluwafemi/create-a-smart-contract-that-transfers-erc20-tokens-to-any-erc20-compliant-address)
-  [Part B - Ethereum Dapps With ReactJS + Truffle Contract + Web3, Building A UI For TokenZendR A Smart Contract That Transfers ERC20 Tokens To Other Addresses](#)

#### Proof of Work Done
https://github.com/slim12kg/tokenzendr-contract.git
https://github.com/slim12kg/token-zendr-react-interface
πŸ‘  , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , and 4 others
properties (23)
authoralofe.oluwafemi
permlinkbuilding-ethereum-dapps-with-reactjs-truffle-contract-web3-a-ui-for-tokenzendr-a-smart-contract-that-transfers-erc20-tokens-to
categoryutopian-io
json_metadata{"tags":["utopian-io","tutorials","react","ethereum","truffle"],"image":["https://s3.amazonaws.com/alofe.oluwafemi/token-zender-app.gif","https://s3.amazonaws.com/alofe.oluwafemi/Series+2+-+1.png","https://s3.amazonaws.com/alofe.oluwafemi/Series-2-2.png","https://s3.amazonaws.com/alofe.oluwafemi/Series2-3.png","https://s3.amazonaws.com/alofe.oluwafemi/Series-2-4.png","https://s3.amazonaws.com/alofe.oluwafemi/Series-2-5.png","https://s3.amazonaws.com/alofe.oluwafemi/Series-2-6.png","https://s3.amazonaws.com/alofe.oluwafemi/Series-2-7.png","https://s3.amazonaws.com/alofe.oluwafemi/Series-2-8.png","https://s3.amazonaws.com/alofe.oluwafemi/Series-2-9.png","https://s3.amazonaws.com/alofe.oluwafemi/Series-2-10.png","https://s3.amazonaws.com/alofe.oluwafemi/Series-2-11.png"],"links":["https://github.com/facebook/react","https://steemit.com/utopian-io/@alofe.oluwafemi/create-a-smart-contract-that-transfers-erc20-tokens-to-any-erc20-compliant-address","https://github.com/slim12kg/tokenzendr-contract","https://github.com/trufflesuite/truffle","https://github.com/trufflesuite/ganache/releases","https://metamask.io/","https://github.com/slim12kg/token-zendr-react-interface/tree/master/public/icons","#","https://github.com/slim12kg/tokenzendr-contract.git","https://github.com/slim12kg/token-zendr-react-interface"],"app":"steemit/0.1","format":"markdown"}
created2018-08-11 12:20:54
last_update2018-08-13 07:35:24
depth0
children9
last_payout2018-08-18 12:20:54
cashout_time1969-12-31 23:59:59
total_payout_value28.771 HBD
curator_payout_value9.331 HBD
pending_payout_value0.000 HBD
promoted0.000 HBD
body_length26,407
author_reputation1,787,365,775,527
root_title"Building Ethereum Dapps With ReactJS + Truffle Contract + Web3, A UI For TokenZendR A Smart Contract That Transfers ERC20 Tokens To Other Addresses"
beneficiaries[]
max_accepted_payout1,000,000.000 HBD
percent_hbd10,000
post_id67,851,745
net_rshares28,568,000,171,721
author_curate_reward""
vote details (68)
@portugalcoin ·
$8.24
Thank you for your contribution.
While I liked the content of your contribution, I would still like to extend one advice for your upcoming contributions: 

- Nice work on the explanations of your code, although adding a bit more comments to the code can be helpful as well


Your contribution has been evaluated according to [Utopian policies and guidelines](https://join.utopian.io/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](https://review.utopian.io/result/8/21311323).

---- 
Need help? Write a ticket on https://support.utopian.io/. 
Chat with us on [Discord](https://discord.gg/uTyJkNm). 
[[utopian-moderator]](https://join.utopian.io/)
πŸ‘  , , , ,
properties (23)
authorportugalcoin
permlinkre-alofeoluwafemi-building-ethereum-dapps-with-reactjs-truffle-contract-web3-a-ui-for-tokenzendr-a-smart-contract-that-transfers-erc20-tokens-to-20180811t212053876z
categoryutopian-io
json_metadata{"tags":["utopian-io"],"links":["https://join.utopian.io/guidelines","https://review.utopian.io/result/8/21311323","https://support.utopian.io/","https://discord.gg/uTyJkNm","https://join.utopian.io/"],"app":"steemit/0.1"}
created2018-08-11 21:20:54
last_update2018-08-11 21:20:54
depth1
children2
last_payout2018-08-18 21:20:54
cashout_time1969-12-31 23:59:59
total_payout_value6.186 HBD
curator_payout_value2.057 HBD
pending_payout_value0.000 HBD
promoted0.000 HBD
body_length765
author_reputation600,428,323,373,197
root_title"Building Ethereum Dapps With ReactJS + Truffle Contract + Web3, A UI For TokenZendR A Smart Contract That Transfers ERC20 Tokens To Other Addresses"
beneficiaries[]
max_accepted_payout1,000,000.000 HBD
percent_hbd10,000
post_id67,892,741
net_rshares6,109,753,791,661
author_curate_reward""
vote details (5)
@alofe.oluwafemi ·
Thank's for your review @portugalcoin.
properties (22)
authoralofe.oluwafemi
permlinkre-portugalcoin-re-alofeoluwafemi-building-ethereum-dapps-with-reactjs-truffle-contract-web3-a-ui-for-tokenzendr-a-smart-contract-that-transfers-erc20-tokens-to-20180811t220213458z
categoryutopian-io
json_metadata{"tags":["utopian-io"],"users":["portugalcoin"],"app":"steemit/0.1"}
created2018-08-11 22:02:15
last_update2018-08-11 22:02:15
depth2
children0
last_payout2018-08-18 22:02:15
cashout_time1969-12-31 23:59:59
total_payout_value0.000 HBD
curator_payout_value0.000 HBD
pending_payout_value0.000 HBD
promoted0.000 HBD
body_length38
author_reputation1,787,365,775,527
root_title"Building Ethereum Dapps With ReactJS + Truffle Contract + Web3, A UI For TokenZendR A Smart Contract That Transfers ERC20 Tokens To Other Addresses"
beneficiaries[]
max_accepted_payout1,000,000.000 HBD
percent_hbd10,000
post_id67,894,996
net_rshares0
@utopian-io ·
Thank you for your review, @portugalcoin!

So far this week you've reviewed 8 contributions. Keep up the good work!
properties (22)
authorutopian-io
permlinkre-re-alofeoluwafemi-building-ethereum-dapps-with-reactjs-truffle-contract-web3-a-ui-for-tokenzendr-a-smart-contract-that-transfers-erc20-tokens-to-20180811t212053876z-20180814t132521z
categoryutopian-io
json_metadata"{"app": "beem/0.19.42"}"
created2018-08-14 13:25:21
last_update2018-08-14 13:25:21
depth2
children0
last_payout2018-08-21 13:25:21
cashout_time1969-12-31 23:59:59
total_payout_value0.000 HBD
curator_payout_value0.000 HBD
pending_payout_value0.000 HBD
promoted0.000 HBD
body_length115
author_reputation152,955,367,999,756
root_title"Building Ethereum Dapps With ReactJS + Truffle Contract + Web3, A UI For TokenZendR A Smart Contract That Transfers ERC20 Tokens To Other Addresses"
beneficiaries[]
max_accepted_payout1,000,000.000 HBD
percent_hbd10,000
post_id68,165,039
net_rshares0
@steemitboard ·
Congratulations @alofe.oluwafemi! You have completed the following achievement on Steemit and have been rewarded with new badge(s) :

[![](https://steemitimages.com/70x80/http://steemitboard.com/notifications/payout.png)](http://steemitboard.com/@alofe.oluwafemi) Award for the total payout received

<sub>_Click on the badge to view your Board of Honor._</sub>
<sub>_If you no longer want to receive notifications, reply to this comment with the word_ `STOP`</sub>



> Do you like [SteemitBoard's project](https://steemit.com/@steemitboard)? Then **[Vote for its witness](https://v2.steemconnect.com/sign/account-witness-vote?witness=steemitboard&approve=1)** and **get one more award**!
properties (22)
authorsteemitboard
permlinksteemitboard-notify-alofeoluwafemi-20180818t155314000z
categoryutopian-io
json_metadata{"image":["https://steemitboard.com/img/notify.png"]}
created2018-08-18 15:53:12
last_update2018-08-18 15:53:12
depth1
children0
last_payout2018-08-25 15:53:12
cashout_time1969-12-31 23:59:59
total_payout_value0.000 HBD
curator_payout_value0.000 HBD
pending_payout_value0.000 HBD
promoted0.000 HBD
body_length689
author_reputation38,975,615,169,260
root_title"Building Ethereum Dapps With ReactJS + Truffle Contract + Web3, A UI For TokenZendR A Smart Contract That Transfers ERC20 Tokens To Other Addresses"
beneficiaries[]
max_accepted_payout1,000,000.000 HBD
percent_hbd10,000
post_id68,613,772
net_rshares0
@steemitboard ·
Congratulations @alofe.oluwafemi! You have completed the following achievement on Steemit and have been rewarded with new badge(s) :

[![](https://steemitimages.com/70x80/http://steemitboard.com/notifications/votes.png)](http://steemitboard.com/@alofe.oluwafemi) Award for the number of upvotes

<sub>_Click on the badge to view your Board of Honor._</sub>
<sub>_If you no longer want to receive notifications, reply to this comment with the word_ `STOP`</sub>



**Do not miss the last post from @steemitboard:**
[SteemitBoard and the Veterans on Steemit - The First Community Badge.](https://steemit.com/veterans/@steemitboard/steemitboard-and-the-veterans-on-steemit-the-first-community-badge)

> Do you like [SteemitBoard's project](https://steemit.com/@steemitboard)? Then **[Vote for its witness](https://v2.steemconnect.com/sign/account-witness-vote?witness=steemitboard&approve=1)** and **get one more award**!
properties (22)
authorsteemitboard
permlinksteemitboard-notify-alofeoluwafemi-20180824t172802000z
categoryutopian-io
json_metadata{"image":["https://steemitboard.com/img/notify.png"]}
created2018-08-24 17:28:03
last_update2018-08-24 17:28:03
depth1
children0
last_payout2018-08-31 17:28:03
cashout_time1969-12-31 23:59:59
total_payout_value0.000 HBD
curator_payout_value0.000 HBD
pending_payout_value0.000 HBD
promoted0.000 HBD
body_length918
author_reputation38,975,615,169,260
root_title"Building Ethereum Dapps With ReactJS + Truffle Contract + Web3, A UI For TokenZendR A Smart Contract That Transfers ERC20 Tokens To Other Addresses"
beneficiaries[]
max_accepted_payout1,000,000.000 HBD
percent_hbd10,000
post_id69,248,486
net_rshares0
@steemitboard ·
Congratulations @alofe.oluwafemi! You have completed the following achievement on the Steem blockchain and have been rewarded with new badge(s) :

<table><tr><td>https://steemitimages.com/60x70/http://steemitboard.com/@alofe.oluwafemi/votes.png?201811220958</td><td>You made more than 500 upvotes. Your next target is to reach 600 upvotes.</td></tr>
</table>

<sub>_[Click here to view your Board of Honor](https://steemitboard.com/@alofe.oluwafemi)_</sub>
<sub>_If you no longer want to receive notifications, reply to this comment with the word_ `STOP`</sub>



**Do not miss the last post from @steemitboard:**
<table><tr><td><a href="https://steemit.com/steemfest/@steemitboard/meet-the-steemians-contest-the-results-the-winners-and-the-prizes"><img src="https://steemitimages.com/64x128/https://cdn.steemitimages.com/DQmeLukvNFRsa7RURqsFpiLGEZZD49MiU52JtWmjS5S2wtW/image.png"></a></td><td><a href="https://steemit.com/steemfest/@steemitboard/meet-the-steemians-contest-the-results-the-winners-and-the-prizes">Meet the Steemians Contest - The results, the winners and the prizes</a></td></tr><tr><td><a href="https://steemit.com/steemfest/@steemitboard/meet-the-steemians-contest-special-attendees-revealed"><img src="https://steemitimages.com/64x128/https://cdn.steemitimages.com/DQmeLukvNFRsa7RURqsFpiLGEZZD49MiU52JtWmjS5S2wtW/image.png"></a></td><td><a href="https://steemit.com/steemfest/@steemitboard/meet-the-steemians-contest-special-attendees-revealed">Meet the Steemians Contest - Special attendees revealed</a></td></tr><tr><td><a href="https://steemit.com/steemfest/@steemitboard/meet-the-steemians-contest-intermediate-results"><img src="https://steemitimages.com/64x128/https://cdn.steemitimages.com/DQmeLukvNFRsa7RURqsFpiLGEZZD49MiU52JtWmjS5S2wtW/image.png"></a></td><td><a href="https://steemit.com/steemfest/@steemitboard/meet-the-steemians-contest-intermediate-results">Meet the Steemians Contest - Intermediate results</a></td></tr></table>

> Support [SteemitBoard's project](https://steemit.com/@steemitboard)! **[Vote for its witness](https://v2.steemconnect.com/sign/account-witness-vote?witness=steemitboard&approve=1)** and **get one more award**!
properties (22)
authorsteemitboard
permlinksteemitboard-notify-alofeoluwafemi-20181122t114413000z
categoryutopian-io
json_metadata{"image":["https://steemitboard.com/img/notify.png"]}
created2018-11-22 11:44:12
last_update2018-11-22 11:44:12
depth1
children0
last_payout2018-11-29 11:44:12
cashout_time1969-12-31 23:59:59
total_payout_value0.000 HBD
curator_payout_value0.000 HBD
pending_payout_value0.000 HBD
promoted0.000 HBD
body_length2,175
author_reputation38,975,615,169,260
root_title"Building Ethereum Dapps With ReactJS + Truffle Contract + Web3, A UI For TokenZendR A Smart Contract That Transfers ERC20 Tokens To Other Addresses"
beneficiaries[]
max_accepted_payout1,000,000.000 HBD
percent_hbd10,000
post_id75,726,693
net_rshares0
@steemitboard ·
Congratulations @alofe.oluwafemi! You received a personal award!

<table><tr><td>https://steemitimages.com/70x70/http://steemitboard.com/@alofe.oluwafemi/birthday1.png</td><td>Happy Birthday! - You are on the Steem blockchain for 1 year!</td></tr></table>

<sub>_You can view [your badges on your Steem Board](https://steemitboard.com/@alofe.oluwafemi) and compare to others on the [Steem Ranking](http://steemitboard.com/ranking/index.php?name=alofe.oluwafemi)_</sub>


**Do not miss the last post from @steemitboard:**
<table><tr><td><a href="https://steemit.com/drugwars/@steemitboard/drugwars-early-adopter"><img src="https://steemitimages.com/64x128/https://cdn.steemitimages.com/DQmYGN7R653u4hDFyq1hM7iuhr2bdAP1v2ApACDNtecJAZ5/image.png"></a></td><td><a href="https://steemit.com/drugwars/@steemitboard/drugwars-early-adopter">Are you a DrugWars early adopter? Benvenuto in famiglia!</a></td></tr></table>

###### [Vote for @Steemitboard as a witness](https://v2.steemconnect.com/sign/account-witness-vote?witness=steemitboard&approve=1) to get one more award and increased upvotes!
properties (22)
authorsteemitboard
permlinksteemitboard-notify-alofeoluwafemi-20190317t125027000z
categoryutopian-io
json_metadata{"image":["https://steemitboard.com/img/notify.png"]}
created2019-03-17 12:50:27
last_update2019-03-17 12:50:27
depth1
children0
last_payout2019-03-24 12:50:27
cashout_time1969-12-31 23:59:59
total_payout_value0.000 HBD
curator_payout_value0.000 HBD
pending_payout_value0.000 HBD
promoted0.000 HBD
body_length1,088
author_reputation38,975,615,169,260
root_title"Building Ethereum Dapps With ReactJS + Truffle Contract + Web3, A UI For TokenZendR A Smart Contract That Transfers ERC20 Tokens To Other Addresses"
beneficiaries[]
max_accepted_payout1,000,000.000 HBD
percent_hbd10,000
post_id81,458,939
net_rshares0
@steemitboard ·
Congratulations @alofe.oluwafemi! You received a personal award!

<table><tr><td>https://steemitimages.com/70x70/http://steemitboard.com/@alofe.oluwafemi/birthday2.png</td><td>Happy Steem Birthday! - You are on the Steem blockchain for 2 years!</td></tr></table>

<sub>_You can view [your badges on your Steem Board](https://steemitboard.com/@alofe.oluwafemi) and compare to others on the [Steem Ranking](https://steemitboard.com/ranking/index.php?name=alofe.oluwafemi)_</sub>


###### [Vote for @Steemitboard as a witness](https://v2.steemconnect.com/sign/account-witness-vote?witness=steemitboard&approve=1) to get one more award and increased upvotes!
properties (22)
authorsteemitboard
permlinksteemitboard-notify-alofeoluwafemi-20200317t115059000z
categoryutopian-io
json_metadata{"image":["https://steemitboard.com/img/notify.png"]}
created2020-03-17 11:51:00
last_update2020-03-17 11:51:00
depth1
children0
last_payout2020-03-24 11:51:00
cashout_time1969-12-31 23:59:59
total_payout_value0.000 HBD
curator_payout_value0.000 HBD
pending_payout_value0.000 HBD
promoted0.000 HBD
body_length654
author_reputation38,975,615,169,260
root_title"Building Ethereum Dapps With ReactJS + Truffle Contract + Web3, A UI For TokenZendR A Smart Contract That Transfers ERC20 Tokens To Other Addresses"
beneficiaries[]
max_accepted_payout1,000,000.000 HBD
percent_hbd10,000
post_id96,433,175
net_rshares0
@utopian-io ·
Hey @alofe.oluwafemi
**Thanks for contributing on Utopian**.
We’re already looking forward to your next contribution!

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

<a href='https://v2.steemconnect.com/sign/account-witness-vote?witness=utopian-io&approve=1'>Vote for Utopian Witness!</a>
properties (22)
authorutopian-io
permlinkre-building-ethereum-dapps-with-reactjs-truffle-contract-web3-a-ui-for-tokenzendr-a-smart-contract-that-transfers-erc20-tokens-to-20180814t101509z
categoryutopian-io
json_metadata"{"app": "beem/0.19.42"}"
created2018-08-14 10:15:09
last_update2018-08-14 10:15:09
depth1
children0
last_payout2018-08-21 10:15:09
cashout_time1969-12-31 23:59:59
total_payout_value0.000 HBD
curator_payout_value0.000 HBD
pending_payout_value0.000 HBD
promoted0.000 HBD
body_length307
author_reputation152,955,367,999,756
root_title"Building Ethereum Dapps With ReactJS + Truffle Contract + Web3, A UI For TokenZendR A Smart Contract That Transfers ERC20 Tokens To Other Addresses"
beneficiaries[]
max_accepted_payout1,000,000.000 HBD
percent_hbd10,000
post_id68,149,845
net_rshares0