How to mint tokens from the backend using Nest.js on Celo Alfajores network
Tags: celo, hardhat, nestjs, erc20
Published: 2023-05-08 10:34:17

## Overview
In this tutorial, you will learn how to
- create an ERC20 token smart contract from openzeppelin wizard with mint functionality
- deploy the contract to the Alfajore's testnet on Celo
- create API to fetch methods from the deployed smart contract to the backend
- use the celo-ethers-wrapper to make ethers compatible with celo network

## Prerequisite
- To follow up this tutorial, a basic understanding on API and [nestjs](https://nestjs.com/) is necessary.

## Generate smart contract from Hardhat
- Open up your terminal and create an empty folder using this command `mkdir my-project && cd my-project` Then run `yarn init -y` to initialize the project and then install hardhat using the command `yarn add --dev hardhat`.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hjljhqtepl8gb2w9gk4n.png)

- After that, run `yarn hardhat` and select Create a typescript project and leave every other option as the default.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ma00h8960qf98slcekjd.png)

- Open up the project folder on [visual studio code](https://code.visualstudio.com/) or any other text editor. It should look like this

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iveg3gifceoc5wembz3n.png)

- In the contracts folder, delete the Lock.sol file and do so with the files in the scripts and tests folder too.

- Next up, we will be using the openzeppelin contract library to generate the ERC20 token, instead of writing the code from 
scratch. But first, we need to install it using this command `yarn add @openzeppelin/contracts` on the terminal. After the installation, go to [openzeppelin wizard](https://docs.openzeppelin.com/contracts/4.x/wizard) to generate the contract for your token. After selecting mintable, it should look like this

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n55c3mqgw87rob2h9crt.png)

- On your text editor, in your contracts folder, create a new file called MyToken.sol and copy the codes from the openzeppelin wizard and paste it into the newly created file. Your file should look like this
```
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract MyToken is ERC20, Ownable {
    constructor() ERC20("MyToken", "MTK") {}

    function mint(address to, uint256 amount) public onlyOwner {
        _mint(to, amount);
    }
}
```
- The mint function in your contract is for creating the total supply of that token to the wallet address specified in the function 

- This is a basic solidity smart contract with the functionality of a GET and POST requests that we can use in the backend.
- On your terminal, run the command `yarn hardhat compile` so as to compile the smart contract and generate the artefact folder, which is the JSON file containing useful informations about the smart contract. We will need this JSON object for the backend interaction with the contract.

## Deploy smart contract to Alfajores network
We will be using hardhat to deploy our smart contract by setting up celo in our local environment.
- In your text editor, open up hardhat.config.ts file and add up this celo configuration code below

```
const config: HardhatUserConfig = {
  defaultNetwork: "alfajores",
  networks: {
    alfajores: {
      url: "https://alfajores-forno.celo-testnet.org",
      accounts: {
        mnemonic: process.env.MNEMONIC,
        path: "m/44'/52752'/0'/0"
      },
    }
  },
  solidity: "0.8.18",
};
```
- In your project root directory, create a .env file where we will be storing our mnemonics. It should look like this

```
MNEMONICS = "paste your 12 mnemonic keys here"
```
- Next up, install the dotenv using this command `yarn add dotenv` so we can access the mnemonic variable in the config file. And then import the dotenv into the file 

```
import * as dotenv from 'dotenv';
dotenv.config()
```
- Your hardhat.config.ts file should look like this

```
import { HardhatUserConfig } from "hardhat/config";
import "@nomicfoundation/hardhat-toolbox";

import * as dotenv from 'dotenv';
dotenv.config()

const config: HardhatUserConfig = {
  defaultNetwork: "alfajores",
  networks: {
    alfajores: {
      url: "https://alfajores-forno.celo-testnet.org",
      accounts: {
        mnemonic: process.env.MNEMONIC,
        path: "m/44'/52752'/0'/0"
      },
    }
  },
  solidity: "0.8.18",
};

export default config;

```

- To deploy our smart contract, go to your scripts folder, create a file and name it deploy.ts , copy & paste the code snippets below into it

```
import { ethers } from "hardhat";
import { MyToken__factory } from "../typechain-types";

async function main() {
  const [signer] =  await ethers.getSigners();
  console.log("Deploying contracts with the account:", signer.address);
  console.log("Account balance:", (await signer.getBalance()).toString()); //get the wallet balance of the signer account
  const contractFactory = new MyToken__factory(signer);
  
  console.log(`Deploying MyToken Contract...\n`);
  const contract = await contractFactory.deploy();
  const contractTxnReceipt = await contract.deployTransaction.wait();
  console.log(`Deployed contract at address ${contractTxnReceipt.contractAddress}`);

 
}

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});

```
- Run this command on your terminal to deploy the contract `yarn hardhat run scripts/deploy.ts --network alfajores` and you should get an output similar to this


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vmrvmn0jfrabvpqv01ys.png)

- If you get an error showing insufficient gas, copy your address to the [celo faucet](https://faucet.celo.org/alfajores) and fund your account with test tokens.


- Take note of the deployed contract address because it will be useful when making API calls to the backend

## Setting up the backend using Nest.js
[Nest.js](https://docs.nestjs.com/) is a framework for building Node.js server side applications.
Let's get started
- In your terminal, run this command `npm i -g @nestjs/cli` to install nest cli globally.
- In your my-project directory, run `nest new backend` which will create a new folder consisting of the base structure of Nest files. After installation using yarn, your terminal should look like this

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hdbhk83vtmrr0ixbstyo.png)


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iz2q9yie3v57st1sd00p.png)

- In the terminal, navigate into the backend folder using the command `cd backend` and run `yarn start:dev` to start up the server and keep it in watch mode.


- Open up your browser and run `http://localhost:3000` and you will see the output of Hello World!, which means the server is running successfully.

- Let us install a package that will help in describing our RESTful APIs, [nestjs/swagger](https://docs.nestjs.com/openapi/introduction). Run the command `yarn add @nestjs/swagger` to install.

- After the installation process is complete, in your src folder, open up the main.ts file and paste the code below into it

```
import { NestFactory } from '@nestjs/core';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  const config = new DocumentBuilder()
    .setTitle('Mint ERC20 Token on Celo Blockchain')
    .setDescription('The ERC20 API description')
    .setVersion('1.0')
    .addTag('celo, alfajores testnet')
    .build();
  const document = SwaggerModule.createDocument(app, config);
  SwaggerModule.setup('api', app, document);

  await app.listen(3000);
}
bootstrap();
```

- Open up your browser and enter the url `http://localhost:3000/api`. You will get an output like this


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ay9mm1ndrcncy46gssq5.png)

## Integrating Smart contract to the Backend
- We will be using the [ethers](https://docs.ethers.org/v5/) library. Run the command `yarn add ethers@^5.7.2` to install ethers.
- Remember the artefact folder we generated after we compiled the smart contract? We will copy the contents in MyToken.json file into our backend folder.
- In the `backend/src` folder, create a new folder called assets and create a new file MyToken.json, paste the copied contents into the file.
- Open up app.service.ts, import ethers and the json object. Copy and paste the code below into the file

```
import { Injectable } from '@nestjs/common';
import { ethers } from 'ethers';
import * as MyTokenJson from './assets/MyToken.json';

@Injectable()
export class AppService {
  
}
```
- We have a problem in importing Json, so we have to fix that by adding `"resolveJsonModule": true` to tsconfig.json file.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xrij76j8t74to3a3wecn.png)

- Remember the contract address we got after deploying the smart contract from the script file in hardhat, we will be using that address to interact with the functions of that address. For now, let's store the address in the app.service.ts file. The file should look like this

```
import { Injectable } from '@nestjs/common';
import { ethers } from 'ethers';
import * as MyTokenJson from './assets/MyToken.json';

const CONTRACT_ADDRESS = "0xBEc9321DEDcB18e954cf63BF81A6688bA5E4415E" // paste your own contract address gotten from the terminal

@Injectable()
export class AppService {
  getContractAddress(): string {
    return CONTRACT_ADDRESS;
  }
}
```
- Open your app.controller.ts file, copy and paste the codes below

```
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get('/contract-address')
  getContractAddress(): string {
    return this.appService.getContractAddress();
  }
}

```
- Refresh your swagger UI tab in your browser. You'll see the route to get contract address. Execute it and you will get 200 status response like the image below

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/do6njdl118rlpun1h96f.png)

- Next up, we will be using the ethers library that we installed so we can interact with the contract address.
- First up, we will get the total supply which is a default method generated from an ERC20 token.

- In your app.controller.ts file, add a new route called /total-suppy, which will be an async operation, returning a promise. The updated file will look like the codes below

```
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get('/contract-address')
  getContractAddress(): string {
    return this.appService.getContractAddress();
  }

  @Get('/total-supply')
  async getTotalSupply(): Promise<number> {
    return this.appService.getTotalSupply();
  }
}

```
- Next up, in your app.service.ts, we will be creating an asynchronous function called getTotalSupply. We will be using a package called [celo-ethers-wrapper](https://docs.celo.org/developer/celo-ethers-wrapper) to be able to make ethers.js to be compatible with the celo network. In your backend directory of your project, install the package using `yarn add @celo-tools/celo-ethers-wrapper`. 
Update your app.service.ts with the codes below 

```
import { Injectable } from '@nestjs/common';
import { ethers } from 'ethers';
import * as MyTokenJson from './assets/MyToken.json';
import { CeloProvider } from '@celo-tools/celo-ethers-wrapper'

const CONTRACT_ADDRESS = "0xBEc9321DEDcB18e954cf63BF81A6688bA5E4415E" // paste your own contract address gotten from the terminal

@Injectable()
export class AppService {
  getContractAddress(): string {
    return CONTRACT_ADDRESS;
  }

  async getTotalSupply(): Promise<number> {
    // Connecting to Alfajores testnet
    const provider = new CeloProvider('https://alfajores-forno.celo-testnet.org')
    await provider.ready
    const contract = new ethers.Contract(CONTRACT_ADDRESS, MyTokenJson.abi, provider );
    return contract.totalSupply();
  }
}
```
- Refresh your swagger UI tab on your browser. After executing your /total-supply route, you should get an output like this

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kikt2690r5enn2gwcbbf.png)
The return value we get is a Big Number. How do we convert it to a string?
Update your getTotalSupply async function with the lines of code below

```
const totalSupplyBigNumber = await contract.totalSupply();
    const totalSupplyString = ethers.utils.formatEther(totalSupplyBigNumber);
    const totalSupplyNumber = parseFloat(totalSupplyString);
    return totalSupplyNumber;
```
- Head to the swagger UI tab in browser, refresh the page and execute the total supply route. You'll get a return value of 0 at the response body because we have not minted any token on our contract.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ghg5zkrnrldbhi7oen92.png)

- Minting tokens requires a signer to sign the transaction and that signer has to be the owner or the deployer of the contract. We will use the celoWallet from the [celo-ethers-wrapper](https://github.com/celo-tools/celo-ethers-wrapper) to be able to sign transactions and make calls.
To mint tokens in the backend, we will need to make a POST request.
In the app.controller.ts file, we need to first import Post and create a new async method route called mintTokens.
Your updated file should look like that

```
import { Controller, Get, Post } from '@nestjs/common';
import { AppService } from './app.service';


@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}m

  @Get('/contract-address')
  getContractAddress(): string {
    return this.appService.getContractAddress();
  }

  @Get('/total-supply')
  async getTotalSupply(): Promise<number> {
    return this.appService.getTotalSupply();
  }

  @Post('/mint-tokens')
  async mintTokens(){
    return this.appService.mintTokens();
  }
}

```
- In the app.service.ts file, import the CeloWallet from celo-ethers-wrapper and create an .env file in the backend directory which stores the private key of the account you used to deploy the smart contract because that is the signer account. Make sure you install dotenv with `yarn add dotenv`.
Your updated file should look like this

```
import { Injectable } from '@nestjs/common';
import { ethers } from 'ethers';
import * as MyTokenJson from './assets/MyToken.json';
import { CeloProvider } from '@celo-tools/celo-ethers-wrapper'
import { CeloWallet } from "@celo-tools/celo-ethers-wrapper";
import * as dotenv from 'dotenv';
dotenv.config()


const CONTRACT_ADDRESS = "0xBEc9321DEDcB18e954cf63BF81A6688bA5E4415E" // paste your own contract address gotten from the terminal
const WALLET_ADDRESS = "0x122851EB3915cc769dECBf95a566e7fC8aAc2125" // paste your own wallet address

@Injectable()
export class AppService {
  getContractAddress(): string {
    return CONTRACT_ADDRESS;
  }

  async getTotalSupply(): Promise<number> {
    // Connecting to Alfajores testnet
    const provider = new CeloProvider('https://alfajores-forno.celo-testnet.org')
    await provider.ready
    const contract = new ethers.Contract(CONTRACT_ADDRESS, MyTokenJson.abi, provider );
    const totalSupplyBigNumber = await contract.totalSupply();
    const totalSupplyString = ethers.utils.formatEther(totalSupplyBigNumber);
    const totalSupplyNumber = parseFloat(totalSupplyString);
    return totalSupplyNumber;
  }

  async mintTokens(){
    // Connecting to Alfajores testnet
    const provider = new CeloProvider('https://alfajores-forno.celo-testnet.org')
    await provider.ready
    const wallet = new CeloWallet(process.env.PRIVATE_KEY, provider);
    const contract = new ethers.Contract(CONTRACT_ADDRESS, MyTokenJson.abi, wallet );
    const mint = await contract.mint(WALLET_ADDRESS, 100000); 
    return mint;
  }
}
```
- In your swagger UI tab, refresh it and execute the mintTokens route which is a POST request. You'll get an output like this

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ja2vwsojvlquzpebzsic.png)

This indicates that the POST request was successful and your tokens are minted based on the parameter you supplied in your mintFunction method.

- Next up, let's check the number of tokens we have by executing the /total-supply GET request. You should get an output like this base on the amount of times you execute the mintTokens POST request.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ny3e4cjoxxdgnpbxnu42.png)

In my response body, I got the value of 3e+13 which is the E- notation version of 3*10^13.

You can view the details of your contract by searching up the contract address in the [celo alfajores explorer](https://alfajores.celoscan.io/)

This is the transaction details from my contract address [0xbec9321dedcb18e954cf63bf81a6688ba5e4415e](https://alfajores.celoscan.io/address/0xbec9321dedcb18e954cf63bf81a6688ba5e4415e).

- This is the end of this tutorial and I hope you learned one or two from this tutorial and it helped in one way or the other. Let me know what you think. Cheers