Building a Decentralized Application with NextJS and Web3js on the Celo Network

Introduction

As we advance in the era of digital transformation, blockchain technology has taken a significant place in driving innovative solutions in various industries. One of the products of blockchain technology is the Decentralized Application, also known as a DApp.

DApps have the power to facilitate peer-to-peer interactions without the need for intermediaries. They offer transparency, immutability, security, and censorship resistance, among other benefits.

In this guide, we explored the process of building a DApp using NextJS and Web3js on the Celo network, a mobile-first blockchain platform that makes financial dApps and crypto payments accessible to anyone with a mobile phone.

We built a basic chat forum where users can send messages stored on the blockchain. The focus was to understand how to set up the development environment, connect to the Celo network using Web3js, write DApp code, and deploy the DApp.

The Project Structure and Building the Contract

Here is the both file and folder structure to follow in order to build up this Dapp

├─── node_modules
├─── pages
│    ├─── index.js
│    ├─── _app.js
│    └─── chat.js
├─── components
│    ├─── layout.js
│    └─── chatbox.js
├─── lib
│    ├─── celo.js
│    └─── chat-utils.js
├─── styles
│    ├─── global.css
├─── package.json
└─── README.md

Here’s what each file and folder does:

  • node_modules: This is where all the packages that you installed via npm will be located.
  • pages: It contains all the pages for our application.
  • components: It contains all the reusable components in our application.
  • lib: This directory will house utilities to interact with the Celo blockchain network.
  • styles: All our CSS files live here.
  • package.json: It keeps track of all dependencies and scripts for the project.
  • README.md: Documentation for the project.

Now let’s proceed to install the necessary packages. We need next, react, react-dom for our React setup. We’ll also need web3 to interact with the Celo network and @celo/contractkit which provides utilities to interact with Celo contracts.

npx create-next-app

Fill out the prompt as you will like to customize as your dapp.

npm install next web3  @celo/contractkit
  1. Now, create the directories and files as indicated in the structure above.

Before writing our code, we’ll need to create and compile our Solidity smart contract. Unlike the previous example, we’ll be using the Remix IDE to write, compile, and deploy our smart contract.

  1. Navigate to the Remix IDE in your web browser. Click on the + icon in the top-left corner to create a new file. Name it Chat.sol.

  2. In the Chat.sol file, add the following code:

pragma solidity >=0.4.22 <0.9.0;

contract Chat {
    event Message(string sender, string content);

    function sendMessage(string memory sender, string memory content) public {
        emit Message(sender, content);
    }
}
  1. Once you’ve written your smart contract, click on the “Solidity compiler” button in the left-hand toolbar (it looks like a clipboard), and then click on the “Compile Chat.sol” button.

  2. After compiling your smart contract, click on the “Deploy & run transactions” button in the left-hand toolbar (it looks like a plug). From here, you can deploy your smart contract to the Celo network.

Note: To deploy your contract to the Celo network, you’ll need to have MetaMask installed and connected to the Celo network. Then, select “Injected Web3” as the environment in the Remix IDE.

Now that we have our smart contract ready, let’s proceed to write our code.

With the project structure and smart contract in place, we can begin creating our DApp.

  1. Set up web3 and Celo:

Create a new file lib/celo.js:

import { newKit } from '@celo/contractkit'

// let web3 = new Web3(window.ethereum)

export const kit = newKit('https://alfajores-forno.celo-testnet.org')

  1. Set up Chat Utility:

Create a new file lib/chat-utils.js:

export const messages = []

export function sendMessage(sender, content) {
  messages.push({ sender, content })
}

export function getMessages() {
  return messages
}
  1. Set up the Layout component:

Create a new file components/layout.js:

import Head from 'next/head'

export default function Layout({ children }) {
  return (
    <div>
      <Head>
        <title>Celo DApp</title>
        <link rel="icon" href="/favicon.ico" />
      </Head>
      <main>{children}</main>
    </div>
  )
}
  1. Set up the Chatbox component:

Create a new file components/chatbox.js:

import { useState } from 'react'
import { sendMessage } from '../lib/chat-utils'

export default function Chatbox() {
  const [message, setMessage] = useState('')

  const handleSubmit = (e) => {
    e.preventDefault()

    // For the purpose of this tutorial, the sender will be 'User1'
    sendMessage('User1', message)

    // Clear the text field
    setMessage('')
  }

  return (
    <form onSubmit={handleSubmit}>
      <input type="text" value={message} onChange={(e) => setMessage(e.target.value)} required />
      <button type="submit">Send</button>
    </form>
  )
}
  1. Set up the Chat Page:

Edit the file pages/chat.js:

import { useEffect, useState } from 'react'
import { getMessages } from '../lib/chat-utils'
import Chatbox from '../components/chatbox'
import Layout from '../components/layout'
 // another coming here later


export default function Chat() {
  const [messages, setMessages] = useState([])
 // another coming here later

  useEffect(() => {
    setMessages(getMessages())
  }, [])
 // another coming here later

  return (
    <Layout>
      <div>
        {messages.map((msg, i) => (
          <div key={i}>
            <p>
              <strong>{msg.sender}:</strong> {msg.content}
            </p>
          </div>
        ))}
        <Chatbox />
      </div>
    </Layout>
  )
}

At this point, you have a basic chat setup. However, these messages are not yet stored on the Celo blockchain, as we have not interacted with our smart contract deployed via the Remix IDE. Let’s implement that next.

Establishing Connection Between Our Next App and The Contract

Let’s now connect our app with the Celo network and interact with our smart contract. To do this, we’ll need the ABI (Application Binary Interface) of our contract and the contract’s address. You can find both in the Remix IDE.

  1. Add the ABI to the project:

Create a new file lib/Chat.json and paste your ABI into this file. The file should look something like this:

{
  "abi": [
    // ... your ABI here
  ]
}
  1. Connect to the Contract:

Update lib/chat-utils.js with the following:

import { kit } from './celo'
import Chat from './Chat.json'

let messages = []
let contract

async function initContract() {
  const networkId = await kit.contracts.getGoldToken()

  contract = new kit.web3.eth.Contract(
    Chat.abi,
    networkId && networkId.address,
  )

  contract.events.Message({}, (error, event) => {
    if (error) console.error('Error on event', error)
    else {
      const { sender, content } = event.returnValues
      messages.push({ sender, content })
    }
  })
}

initContract()

export function sendMessage(sender, content) {
  const tx = contract.methods.sendMessage(sender, content).send({ from: kit.defaultAccount })
  console.log('Sent message:', tx)
}

export function getMessages() {
  return messages
}

Here, we first initiate the connection to our contract. We listen to the Message events emitted by the contract and push the messages to our local messages array. In sendMessage, we call the contract’s sendMessage method and log the resulting transaction.

Replace the contract address and ABI with the appropriate ones for your contract.

Note: Make sure the account you’re using (specified by kit.defaultAccount) is the same one you used in Remix to deploy your contract, as it’s the only account that’s authorized to interact with the contract.

  1. Connect MetaMask:

In order to interact with the Celo network, we’ll need to connect our DApp to MetaMask. Create a new file lib/connectWallet.js:

import { useEffect, useState } from 'react'
import { kit } from './celo'

export default function useWallet() {
  const [isConnected, setConnected] = useState(false)

  const connect = async () => {
    if (window.celo) {
      try {
        await window.celo.enable()
        kit.defaultAccount = window.celo.selectedAddress
        setConnected(true)
      } catch (error) {
        alert('Failed to connect wallet')
      }
    } else {
      alert('Please install a Celo wallet extension')
    }
  }

  useEffect(() => {
    connect()
  }, [])

  return isConnected
}

Then, import this hook in pages/chat.js and use it to ensure the wallet is connected before rendering the chat:

import { useEffect, useState } from 'react'
import { getMessages } from '../lib/chat-utils'
import Chatbox from '../components/chatbox'
import Layout from '../components/layout'
import useWallet from '../lib/connectWallet'

export default function Chat() {
  const [messages, setMessages] = useState([])
  const isConnected = useWallet()

  useEffect(() => {
    setMessages(getMessages())
  }, [])

  if (!isConnected) {
    return <div>Connecting...</div>
  }

  return (
    <Layout>
      {/*...*/}
    </Layout>
  )
}

That’s it! Start the development server with npm run dev and navigate to http://localhost:3000/chat in your browser. The chat DApp should now be live, and you should be able to send messages that get recorded on the Celo network.

Remember to replace the contract address and ABI with the ones for your contract, and make sure that you are connected to the Celo network in MetaMask. Also, consider adding error handling and loading states for a better user experience.

Note: Always ensure to keep your private keys secure and avoid pushing sensitive data to public repositories for security reasons but this a test and be careful when building for production.

Repository and Output

  1. Here is the link to the repository
  2. Sample Output in figure below.

Conclusion

In conclusion, building a DApp with NextJS and Web3js on the Celo network is a promising development pathway for blockchain enthusiasts. While our focus was on a chat DApp, the fundamentals learned in this guide can be extended to more complex DApps, providing more functionalities and solving various real-world problems.

Building DApps involve interactions with smart contracts, understanding the basics of blockchain transactions, and knowledge of web development. The experience of building this DApp should provide a foundational understanding of these aspects.

As you move forward in your DApp development journey, remember that the possibilities are endless. With the tools and technologies at hand, and the power of blockchain technology, you can create innovative solutions that can transform various sectors of the economy, promote financial inclusion, and ensure a decentralized and secure digital future. Happy coding!

3 Likes

Great one…

When are we starting?

Moving this to Proposals > New Requests Category and created a Trello card for it.

Planning to manage requests in the Proposals section starting next week or the following. Thanks for sharing :slight_smile:

1 Like

@Celo_Academy when are you checking this?

1 Like

@Celo_Academy

1 Like

Impressive idea! If your tutorial gets the green light, it will undoubtedly contribute to the growth of the Celo ecosystem.

1 Like

Thanks for updating the description. Approved! :mortar_board: :seedling:

This needs an urgent review. Who is available?

I’ll review this