Skip to main content

Composer Series - Building a Staking Defi App with Celo Composer and React

· 11 min read
Ernest Nnamdi


Hello there!! 🙋🏾‍♂️

This is the third instalment of the composer series where I demonstrate how to build defi applications in no time using the Celo-composer. The first two instalments were Building a crowdfunding refi dapp using celo composer and react and Building a decentralized newsfeed with Celo composer, React, and IPFS.

The Radical Shift

So here I’m thinking that every company, tech or otherwise will eventually come to be functionally web3-savvy to various degrees. Ultimately, if they play in these climes for long enough.

We have more companies globally, waking up to the fact they can do more with blockchain technology. This means, of course, they are probably already sighing at the tedium that comes with migrating to or incorporating web3 into their existing systems.

Blockchain companies are now burdened with the task of juggling both providing vital services and lowering the learning curve. At the critical merge point ( the point where new tech is introduced into the work dynamic), plan-to-execution time drops significantly ( this is precipitated by the need to work in a different way that requires the usage of the particular tech i.e web3 in this instance).

The obvious solution then becomes creating solutions that ease developers into web3. This my friends is where Celo stands clear of the crowd. In what regard you may ask? The success of web3 and the full potential of blockchain technology can only be realised with massive adoption. Without adoption, there will be no reason to build. That’s why Celo has taken the lead in the industry by providing tooling to support developers of all levels, from the expert to the web2 developer still toying with the idea of building defi applications. One of such tools is the Celo composer.

Celo Composer

The Celo Composer is a starter pack built on the react-celo toolkit to get you up and running fast in developing DApps on the Celo blockchain. This starter pack is best suited for web2 developers currently transitioning into web3 as it abstracts all the complexities involved in setting up and developing Defi applications and replaces them with a plug-and-play environment.

The starter pack, which currently supports React, React-Native, and Flutter requires little to no configurations from you as it eases you into the web3 sphere.

Now for today’s project

Here’s a list of what we’ll cover in this article:

  • ✅ Step 1: Setting up your environment.
  • ✅ Step 2: Creating your smart contract.
  • ✅ Step 3: Deploying your smart contract.
  • ✅ Step 4: Getting started with the frontend.
  • ✅ Step 5: Interacting with your smart contract from the frontend.

What are we building?

In this article, we are going to use the Celo Composer starter-kit which comes pre-integrated with NextJS, and also Tailwind for styling. This dapp will allow users whose wallet are connected, stake their tokens and earn returns on them.


This is the end result of our project today. If your learning style is “code first”, you can find the complete code for this project here on Github.

Do follow the commands in the README-md file to get started with setting up your project.


  • Solidity
  • React
  • Tailwind

Step 1: Setting up your environment ✅​

There are two options to setting up your environment.

Using the Template:

Navigate to the Celo Composer repository and follow the step by step guide entailed in the README-md.


Using the CLI:

The Celo Composer CLI is the easiest way to setup your environment because unlike the first option, it only installs dependencies and boilerplate code necessary for the framework you intend to use.

npx @celo/celo-composer create

Running this command throws up a prompt for you to select the framework of your choice and you are fully setup to start using the starter-kit.

To install dependencies locally and setup test wallet, please refer to the README-md.

Step 2: Creating your smart contract ✅

  • Open your project on your text editor and ensure you’re on the root folder and the navigate to the packages/hardhat folder and rename the .envexample file to .env.

  • In the .env file, paste in the private key of your wallet. If you are wondering where to find your private key, please refer to the README-md in the Celo composer repository. If you prefer to setup your metamask wallet to work with this and future projects, please refer to this guide.


  • Navigate to the contracts folder and here we are going to add two contracts. One for the ERC20 token we are going to be using, and our staking contract. So create two new files, Piron.sol and Staking.sol respectively.


  • In the Piron.sol file, paste in this contract and save. This is a basic ERC20 contract compliant with the IERC20 standards. This token whose symbol is PTK will serve as our staking and reward token.


  • In the Staking.sol file, paste in this contract. This is a simple staking contract that accepts the address of our token(Piron token) and assigns it to the pirToken variable. This contract has six functions or methods but we will only be interacting with three, which are the staking function, pause and unpause function.


Step 3: Deploying your smart contract ✅

After setting up and creating our smart contracts, the only thing left to do on the backend of our project is to deploy it to the blockchain. Deploying contracts using the Celo Composer toolkit is an unbelievably seamless process.

  • Navigate to the deploy folder in the hardhat directory and in your 00-deploy.js file, you will see the deployment function for the greeter contract(A sample contract that comes with the starter kit).
await deploy(“Greeter”, {
from: deployer,
args: [“hello world”],
log: true,

Update the function to deploy your PironToken and StakePIR by replacing the function with this

const piron = await deploy(“PironToken”, {
from: deployer,
log: true,

await deploy(“Greeter”, {
from: deployer,
args: [piron.address],
log: true,

module.exports.tags=["PironToken", "StakePIR"];


We have two deploy functions, the first functions (piron) deploys our staking and reward token and the second deploys our staking contract and takes as an argument, the contract address of our token(PironToken).

There you have it! All that is left to do, is to open up your terminal, navigate to the hardhat directory

cd packages/hardhat

and run

yarn deploy

This compiles your contracts and deploys them to the Celo blockchain (Alfajores testnet).

View smart contract

Open the Celo Block Explorer (Alfajores Testnet) and paste the transaction or deployed address to view the transaction or smart contract. You can also check your wallet to confirm that the gas fee has been deducted from your balance.

Step 4: Getting started on the frontend ✅

Now we are done with the hardhat folder, we move on to the react-app folder. Navigate to the react-app folder by running on your terminal

cd ../react-app

or if you are in the root directory

cd packages/react-app


Follow the official tailwind guide to add tailwind to your project.

Note: Ensure you are in the react-app directory before installing tailwind.

After installing tailwind, you will notice two new files has been created for you. In the tailwind.config.js file, replace the boilerplate code with this.


After updating the tailwind config file, create a new folder in the react-app directory called styles.

In this new folder, create a file called global.css and then paste in this code. These are just basic styling and mostly gradients(most of which we are not going to use, so feel free to take advantage of them to customize your frontend).


The final setup for the tailwind configuration is to import the global.css file in the pages/_app.tsx file.

 import “../styles/global.css”;

Add this below the import statements in your _app.tsx file and voila we are done setting up tailwind.



The first file we are going to change is the components/layout/AppLayout.tsx file. We are going to replace it with the following code:

import * as React from “react”;
import Meta from “../meta/Meta”;
import { Header } from “./Header”;
interface Props {
title: string;
description: string;
children: React.ReactNode;
export default function AppLayout({ title, description, children }: Props) {
return (

<div className=”flex-1 h-full bg-gray-800">
<Header />
<Meta title={title} description={description} />



In the utils folder, you are going to add one more utility function to the index.tsx file. Paste in this function below

export function formatTime(timestamp: number) {
const milliseconds = timestamp \* 1000;
const dateObject = new Date(milliseconds);
const humanDateFormat = dateObject.toLocaleDateString()
return humanDateFormat;

Step 5: Interacting with your smart contract from the frontend ✅


Next file we are going to work on, is the index.tsx file. Here, we are going to delete all the placeholder code and replace it with this


Code Walkthrough
const contracts =

The variable contracts is being assigned, every deployed contract which is imported from the deployments folder in the hardhat directory. The variable is in turn, being passed in to the HomePage.tsx.


Navigate to the pages directory and create a new file called HomePage.tsx and in this newly created file, paste in this code. This will serve as as our home screen.


Code walkthrough
const { kit, address } = useCelo();

The useCelo() is gotten from react-celo which is a React hook and the easiest way to access Celo in your React application. Kit is used to query onchain data while address returns the address of the user after the connect function has connected the wallet to the project(Refer to components/layout/Header.tsx to see the connect function in action).

const pironContract = contracts
? (new kit.connection.web3.eth.Contract(
) as any as PironToken)
: null;

This creates a new instance of the contract (In this case our PironToken contract). It leverages kit which was destructured from useCelo() to create a new contract instance by passing in, the abi and address of the contract.

Note: “contracts” was passed in to the HomePage from the Index.tsx and contains all deployed contracts if any.

await pironContract.methods
.approve(contracts.StakePIR.address, “1000000000”)
.send({ from: address });

The above function is contained in the submit function and it is responsible for calling our smart contract methods or functions. This particular function calls the approve function in the pironContract and passes as arguments, the contract address of the staking contract and also a gas amount.

const paused = await stakingContract.methods.paused().call();

The above function, is quite similar to the previous in the sense that they both call methods or functions in the smart contract but do notice that theres is a difference between them. .call() is used in place of .send({…}) this is because this particular function does not create a new transaction on the blockchain.


Navigate to the components directory and create a file called Input.tsx and paste in this code. This component is our custom input field to accept input from the frontend.



Still in the components directory, create another file called ProjectCard.tsx and paste this code in it. The ProjectCard component is a way to display all the data we fetch from the blockchain.

This card will contain relevant information about our stake contract like number of stakers, start date, end date, etc and also allow up to make some contract calls such as pause, unPause and Claim Rewards.



This brings to an end, this session on building with Celo composer. But your learning don’t have to end here as PRs are welcome for those who want to contribute to this project.

Here is a quick recap of everything we covered.

  • ✅ Step 1: Setting up your environment.
  • ✅ Step 2: Creating your smart contract.
  • ✅ Step 3: Deploying your smart contract.
  • ✅ Step 4: Getting started on the frontend.
  • ✅ Step 5: Interacting with your smart contract from the frontend.

For those who have questions or want to be part of our developer community, join us on discord!.

Till next time,

Adios ✌🏾