Building DApps on Ethereum from scratch

Hello folks, though we are passing a tough time I hope you all are well. Today I am going to write about blockchain. Especially on Ethereum Smart Contract. There are thousands of tutorials on the internet but I didn’t find one complete. Also, most of them are out-dated.

In the tutorial, we will develop a news portal based on blockchain. For the sake of simplicity, I am keeping the features very limited.

  • The user will register on the portal using an ethereum account.
  • The user will be able to publish news on the portal using that account.

Tools we are going to use are,

  • Truffle framework
  • Ethereum (Ganache)
  • Solidity (To write smart contract) := version 0.8.0
  • Golang (To cache data)
  • PostgreSQL (To store cache data)
  • Go-ethereum (To connect backend with ethereum blockchain)
  • VueJS + Web3js (To implement frontend for the dApp)
  • Metamask (To interact with the blockchain)


High-level architecture

Application structure (Frontend):

  • bindings: bindings directory contains codes required to work with other languages.
  • build: build directory contains generated codes are contract compilation.
  • contracts: contains smart contract source code.
  • migrations: contains smart contract migration scripts.
  • node_modules: contains node third-party modules/libraries.
  • public: public resources for the VueJS app.
  • src: VueJS app source code.
  • test: contains app test files.
  • truffle-config.js: hold truffle configuration.

Step1: A user will register in our platform using their cryptographic identity. To hold user information in the smart contract we will need a structure.

struct Writer {
   string name;
   string title;
   string email;
   int created_at;

For the user, we are holding the above information. And to keep the record of all users, we are having a map of users.

mapping(address => Writer) writers;

Notice that, we are indexing data using the user’s ethereum address.

We have another map to check if a writer already registered.

mapping(address => bool) isWriterExists;

Now we have to expose a function, so users can register themselves.

function createAuthor(string memory name, string memory title, string memory email, int created_at) public payable {
   require(bytes(name).length > 0, "Name is required");
   require(bytes(title).length > 0, "Title is required");
   require(bytes(email).length > 0, "Email is required");
   require(created_at > 0, "CreatedAt is invalid");
   require(isWriterExists[msg.sender] == false, "User already registered");    isWriterExists[msg.sender] = true;
   writers[msg.sender] = Writer(name, title, email, created_at);

We have put to modifier publicand payable . public means the function is publicly accessible and payable means to execute this function you have to spend ether and it will make a change to data. With require function, we have defined that these conditions must meet to execute the function. We have few other functions as well.

getAuthorProfile — To retrieve the author's profile information.

updateAuthor — To change the author’s information.

isAuthorRegistered — To check if the author is already registered.

When the user will come for the first time and will try to write news the frontend app will ask to create a profile with name, title, and email. After putting the when user will click on Create Account, the app will interact with meta mask for authorization. And once the user will authorize, the request will be executed.

To execute the request successfully user has to put enough gas limit otherwise request will fail. After successful registration in a similar way, you can update your information. Now we want to write news, right?

To hold the data of post we have the structure,

struct Post {
   string title;
   string details;
   address writer;
   int created_at;
   int updated_at;

and map

mapping(string => Post) posts;

To check if a post id exists, we have another map.

mapping(string => bool) isPostIdExists;

We also have two events to broadcast the activity of the smart contract.

  1. Post created

event PostCreated(string id, string title, int createdAt);

2. Post updated

event PostUpdated(string id, string title);

Now we need to create the post,

function createPost(string memory id, string memory title, string memory details, int created_at) public payable {
   require(isWriterExists[msg.sender] == true, "User not registered");
   require(isPostIdExists[id] == false, "PostId already exists");
   require(bytes(title).length > 0, "Title is required");
   require(bytes(details).length > 0, "Details is required");
   require(created_at > 0, "CreatedAt is invalid");    isPostIdExists[id] = true;
   posts[id] = Post(title, details, msg.sender, created_at, 0);    emit PostCreated(id, title, created_at);

createPost function works the similar way as createAuthor. One thing additionally we are doing here is broadcasting the event.

emit PostCreated(id, title, created_at);

Here will come our backend application. Golang based backend application listens for events and when it gets any event it stores that in the database. The frontend application lists these data through the rest API exposed from the backend.

If you click on details it will show the data and data will be fetched from the blockchain using web3.

Please check the source code README to know how to compile and deploy the smart contracts and the backend application.

Source codes,