In this tutorial, I am going to show you how simply you can develop an Ethereum DApp from scratch using Truffle, Metamask, and Ganache. To set up your system with necessary dependencies, refer to my previous article and set up your system to begin developing your first DApp.
Before we dive into the development, let me give you an overview of the architecture of Ethereum DApp we are going to develop and how different puzzles are going to fit into the picture. So, let’s start the journey to build your Ethereum DApp.
What are Ethereum DApps?
DApps are the decentralized applications that run on a peer-to-peer network, unlike traditional web apps that run on centralized servers. When these DApp are built over the Ethereum network, these are known as “Ethereum DApps”. In decentralized apps, all the user data is stored on immutable distributed ledger and every participating node has a copy of that ledger. These DApps' logic that runs on the blockchain nodes is written in solidity language and traditionally called as “smart contracts”.
The architecture of our Ethereum DApp
Client’s Browser
It is just like a normal browser of any web application written in HTML, CSS, and JavaScript.
Web3.js
Web3.js is a collection of libraries that enables your browser to interact with the blockchain. It enables you to read and write data from smart contracts, transfer ethers between accounts and much more.
Web3 Provider
Ethereum network contains nodes and all nodes share the same copy of data. Setting a “web3 provider” in web3.js tells our code which node we are going to read and write data from. We are using Metamask in our implementation that injects its web3 provider in the browser. Metamask is a browser extension for Chrome and Firefox that lets users securely manage their Ethereum accounts and private keys, and use these accounts to interact with websites that are using Web3.js.
Ethereum Virtual Machine
Every Ethereum node in Ethereum network runs their own EVM implementation and is responsible for running the same instructions of smart contracts over the Ethereum network.
Ok, that’s all about the theory to wake you up. Let’s start with the development.
What we are going to develop?
We are going to develop a simple decentralized newsfeed system. News publisher can post news on blockchain with their account addresses. This news will be immutable and displayed in the same order as they are posted.
Our Final DApp will look something like this,
You can refer to the code at GitHub anytime you face issues. But I urge you to code each step yourself along with me.
Step 1 - Setting up the Structure
I presume that you have set the development environment already. In command prompt create a new directory and unbox “truffle-box” in that respective directory to get the structure of the project.
mkdir postnews
cd postnews
truffle unbox pet-shop
This will set up the following basic structure in your folder,
Let’s examine the project structure Truffle Box gave us.
Contracts - As obvious with the name, all our smart contracts will reside here.
Migrations - Includes Migration files to migrate smart contracts to blockchain.
Node_modules - Contains all node dependencies
Src - Home for all client-side application code
Test - Where we write tests for our smart contract either in solidity or JavaScript. We are not going to write any tests in our implementation because we are building a basic implementation of Dapp. But tests are important to write when you are working on commercial projects because each migration of smart contract on blockchain causes gas cost. Thus, you want to make sure that your smart contracts are bug-free and perform the intended functionality.
Step 2 - Writing Smart Contract
Create a new file news.sol in contract folder and write the following code in it.
pragma solidity >=0.4.0 <=0.6.0;
contract news{
struct newsfeed{
address publisher;
string newsdesc;
}
mapping(uint => newsfeed) public newsfeeds;
uint public newsCount;
function addnews(string memory newsdesc) public {
newsCount++;
newsfeeds[newsCount].publisher = msg.sender;
newsfeeds[newsCount].newsdesc = newsdesc;
}
}
Let me explain this code. First, we specify that the current code is runnable on the solidity compiler version greater than 0.4.0 and smaller than 0.6.0 with “pragma solidity”. Then we initialized the contract and initialized the structure of our newsfeed containing the address of publisher (initialized with address data type) and the news itself (initialized with string data type).
To store this created structure we have created a mapping that is just like a key-value pair. The key to mapping is an unsigned integer and the value is the “newsfeed struct”.
“newscount” keeps track of the number of posted news and initially 0. The Addnews function will store publisher address and news in the struct referenced by the key value. “msg.sender” gets the address of account which has called the contract.
Simple. Isn’t it? Now let’s compile this smart contract to see if it runs properly. To do this, navigate to your project folder in the console and write.
truffle compile
Now let's migrate our current smart contract on Ganache. To do this, create a new file in Migration folder named “2_deploy_contracts.js”. Paste the following code in that file to migrate your contract,
var news = artifacts.require("./news.sol");
module.exports = function(deployer) {
deployer.deploy(news);
};
Save it and in console type,
truffle migrate
Make sure that Ganache is open and running while you migrate your contract. And hurrah! Your contract is successfully migrated to Ganache.
Step 3 - Developing a client-side application to interact with the contract
Go to your index.html file and paste the following code.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
<title>Decentralized Newsfeed</title>
<!-- Bootstrap -->
<link href="css/bootstrap.min.css" rel="stylesheet">
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body>
<div class="container">
<div class="row">
<div class="col-xs-12 col-sm-8 col-sm-push-2">
<h1 class="text-center">Decentralized Newsfeed</h1>
<hr/>
<br/>
</div>
</div>
<div id="newsRow" class="row">
<!-- PETS LOAD HERE -->
</div>
<!-- <button class="btn btn-default" type="button" class="aaaaaddProd">Add Product</button> -->
<!-- Button trigger modal -->
<button type="button" style="align:center;" class="btn btn-primary" data-toggle="modal" data-target="#exampleModalCenter">
Add News
</button>
</div>
<!-- Modal -->
<div class="modal fade" id="exampleModalCenter" tabindex="-1" role="dialog" aria-labelledby="exampleModalCenterTitle" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<!-- Modal Content-->
<div class="modal-content">
<!--Modal Header-->
<div class="modal-header">
<h5 class="modal-title" id="exampleModalCenterTitle">Add News</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<!--Modal Body-->
<div class="modal-body">
<form>
<div class="form-group">
<textarea class="form-control postbox" id="post" placeholder="Add you story Here..." rows="5"></textarea>
</div>
<button type="submit" data-dismiss="modal" class="btn btn-primary addNews">Post News</button>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<div id="postTemplate" style="display: none;">
<div class="col-md-12">
<div class="panel panel-default panel-pet">
<div class="panel-heading">
<strong>Publisher Address</strong> <h3 class="panel-title"> Account Address </h3>
</div>
<div class="panel-body">
<strong>Descrition</strong>: <span class="desc"> </span><br/>
</div>
</div>
</div>
</div>
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<!-- Include all compiled plugins (below), or include individual files as needed -->
<script src="js/bootstrap.min.js"></script>
<script src="js/web3.min.js"></script>
<script src="js/truffle-contract.js"></script>
<script src="js/app.js"></script>
</body>
</html>
Ok! Don’t scare yourself; it’s just some simple HTML. Let me go through it. The Add News button is navigating to the model that opens a dialogue for adding news. “Posttemplate” contains the layout of how your posts will be displayed on the screen.
To make this code in action let's write some code in app.js and see how it is going to link up with our smart contract.
In your “app.js” file, first, add the following code to set up the web3 provider for web3.js in the browser.
App = {
web3Provider: null,
contracts: {},
initWeb3: async function() {
// Modern dapp browsers...
if (window.ethereum) {
App.web3Provider = window.ethereum;
try {
// Request account access
await window.ethereum.enable();
} catch (error) {
// User denied account access...
console.error("User denied account access")
}
}
// Legacy dapp browsers...
else if (window.web3) {
App.web3Provider = window.web3.currentProvider;
}
// If no injected web3 instance is detected, fall back to Ganache
else {
App.web3Provider = new Web3.providers.HttpProvider('http://localhost:7545');
}
// App.web3Provider = new Web3.providers.HttpProvider('http://localhost:8545');
web3 = new Web3(App.web3Provider);
return App.initContract();
},
Extend app.js with the following code.
initContract: function() {
$.getJSON('news.json', function(data) {
// Get the necessary contract artifact file and instantiate it with truffle-contract
App.contracts.news = TruffleContract(data);
// Set the provider for our contract
App.contracts.news.setProvider(App.web3Provider);
// Use our contract to retrieve and mark the adopted pets
// return App.markAdopted();
return App.init();
});
// return App.bindEvents();
return App.AddNewsButton();
},
When we compile our contract “news.json” is created in the build folder. Truffle has a library called “TruffleContract” that keeps information about the contract. So using this library we set web3 provider for our contract. This enables our client-side application to interact with our smart contract deployed on blockchain.
Move forward and add the following code in app.js,
AddNewsButton: function() {
$(document).on('click', '.addNews', App.AddNews);
},
AddNews:function(event){
var post = document.getElementById('post').value
var postInstance;
App.contracts.news.deployed().then(function(instance){
postInstance = instance;
return postInstance.addnews(post);
});
console.log("News posted");
},
};
This code will take the user-entered news and pass it to our add news function in smart contract. Smart contract will write the publisher address and news on blockchain using mapping.
Finally, add the following code in app.js to render the HTML template with data on every page reload so your news will be displayed.
init: async function() {
// Load Products.
var postInstance;
App.contracts.news.deployed().then(function(instance){
postInstance = instance;
return postInstance.newsCount();
}).then(function(result){
var counts = result.c[0];
console.log("Total News : "+counts);
for (var i = 1; i <= counts; i ++) {
postInstance.newsfeeds(i).then(function(result)
{
console.log("Publisher Address:" +result[0]);
console.log("News:" +result[1]);
var newsRow = $('#newsRow');
var postTemplate = $('#postTemplate');
postTemplate.find('.panel-title').text(result[0]);
postTemplate.find('.desc').text(result[1]);
newsRow.append(postTemplate.html());
});
}
});
},
‘i’ initialized with 1 is passed as a key to newsfeeds mapping. This will fetch the structure details on index i. Structure details are just like an array having “publisher address” at 0 index and “newsdesc” at 1 index. This data will be retrieved from smart contract and append to “postTemplate” in index.html.
Now, in your console, type “npm run dev” to run your DApp.
This will open the DApp in your browser. Make sure you are logged in with metamask and it's running on port 7545.
Kudos! You have successfully developed your Dapp. Happy Coding!