How To make an ERC- 721 NFT Participation Certificate for your event/webinar etc.

Nowadays most of the offline events are happening online, like the guest lectures, an college event or an webinar by an expert. Most of the event organisers issue certificates to the people who have attended the session throughout, They just take the template from internet and paste the names, event name, event heads etc, without knowing that it could be forged easily as they mostly don’t have any identity to prove that certificate is given to this person. So for this, we would make an NFT Certificate of participation which would not just look cool, but would eliminate any chances for forgery of certificates. So lets start.

Pre- requisites-

  1. preferably An Linux/ macOS installed system
  2. basic knowledge of smart contracts and ERC-20 protocol
  3. Metamask installed into chrome browser.

Setting up the environment-

Open terminal and create a new project.

$ mkdir NFTCert&& cd NFTCert
$ npm init -y

You would see something like this..

Then we install OpenZeppelin Contracts 88 which has an implementation of ERC721.

npm install --save-dev @openzeppelin/contracts

Next we install a development tool for deployment, for this tutorial we will use Truffle 70 but we could use any other tools such as Buidler, Remix or OpenZeppelin CLI 28.

npm install truffle

Getting the contract artifacts

We will setup our Solidity project using truffle init to create a contracts directory and configuration to connect to a network.

$ npx truffle init

Upon success-full setup, we would see

Unbox successful, sweet!

now we need to copy the artifacts to the build/contracts directory.

$ mkdir -p build/contracts/
$ cp node_modules/@openzeppelin/contracts/build/contracts/* build/contracts/

Now go to migrations folder in NFTCert and create an javascript file called 2_deploy.js using any editor and paste the following there-

// migrations/2_deploy.js
// SPDX-License-Identifier: MIT
const ERC721PresetMinterPauserAutoId = artifacts.require("ERC721PresetMinterPauserAutoId");
module.exports = function(deployer) {
deployer.deploy(ERC721PresetMinterPauserAutoId, "My NFT","NFT", "");

Now the above URL- is the URL For the metadata of the NFT we are creating. To create this metadata on my-json-server, follow this link-

Upon setting up metadata, it would look something like this-


  1. “id” is the ID for the NFT you have published, it would be useful in minting the NFT.
  2. “description” is the description of the NFT, It would be shown as what this NFT is for etc.
  3. “external_url” points to the website if you want to add.
  4. “image” is the image an NFT would be showing. pinata is preferred for external image. In this project, our certificate is the image.
  5. “name” is the name of the NFT you want to give

for adding more NFTs, just copy this 5 things and paste it below id 0, but rename it to id 1 and make the preferred changes in image and name. for image, we are using pinata as it uses ipfs to store the image. To learn how to set up, you can follow the link here-

Deploying to the Rinkeby testnet

For deploying to the Testnet, we need following pre-requisites-

  • An Infura project ID (or a public node provider of your choice). follow link here to make your new project ID
  • @truffle/hdwallet-provider installed (To install, simply write npm install @truffle/hdwallet-provider)
  • Configure truffle-config.js for Rinkeby network
  • A funded testnet account and mnemonic

To configure truffle-config.js simply paste the following code WITH YOUR PERSONAL MNEMONIC AND INFURA PROJECTID

const fs = require('fs');const HDWalletProvider = require('@truffle/hdwallet-provider');var mnemonic = ""; // Your personal mnemonicvar projectId = ""; // your Personal projectIDmodule.exports = {networks: {rinkeby: {provider: () => new HDWalletProvider(mnemonic, `${projectId}`),network_id: 4,       // Rinkeby's idgas: 8500000,gasPrice: 1000000000,  // 1 gwei (in wei) (default: 100 gwei)confirmations: 2,    // # of confs to wait between deployments. (default: 0)timeoutBlocks: 200,  // # of blocks before a deployment times out  (minimum/default: 50)skipDryRun: true     // Skip dry run before migrations? (default: false for public nets )},},mocha: {// timeout: 100000},compilers: {solc: {// version: "0.5.1",    // Fetch exact version from solc-bin (default: truffle's version)// docker: true,        // Use "0.5.1" you've installed locally with docker (default: false)// settings: {          // See the solidity docs for advice about optimization and evmVersion//  optimizer: {//    enabled: false,//    runs: 200//  },//  evmVersion: "byzantium"// }}},db: {enabled: false}};

Deploy to Rinkeby

Now after successful setup, we would deploy the smart contract to the testnet

$ npx truffle console --network rinkeby
truffle(rinkeby)> migrate
Compiling your contracts...
> Everything is up to date, there is nothing to compile.
Starting migrations...
> Network name: 'rinkeby'
> Network id: 4
> Block gas limit: 10000000 (0x989680)
1_initial_migration.js=====================Deploying 'Migrations'---------------------
> transaction hash: 0x9008f27679da63b92289ae4e4eacfbd5558ceed7c7c7fe2a56ef4218a2d11fb5
> Blocks: 0 Seconds: 13
> contract address: 0x1CD15A3D13FA90F9607359F0D667C8f3c99D822a
> block number: 8875873
> block timestamp: 1625375314
> account: 0x993E6B1Cf39135Cd6BCdb87Ff9E6bab538035FF3
> balance: 16.035155549
> gas used: 193219 (0x2f2c3)
> gas price: 1 gwei
> value sent: 0 ETH
> total cost: 0.000193219 ETH
Pausing for 2 confirmations...
> confirmation number: 1 (block: 8875874)
> confirmation number: 2 (block: 8875875)
> Saving migration to chain.
> Saving artifacts
-------------------------------------> Total cost: 0.000193219 ETH2_deploy.js===========Deploying 'ERC721PresetMinterPauserAutoId'
> transaction hash: 0xece55d4302e9de76195318bdfa14be7ca36f2527a26ef1f2635290cd5f361cbe
> Blocks: 2 Seconds: 21
> contract address: 0xF3255e17dD5d9DD8885C3E25eF72bbdc13974F82
> block number: 8875878
> block timestamp: 1625375389
> account: 0x993E6B1Cf39135Cd6BCdb87Ff9E6bab538035FF3
> balance: 16.032520751
> gas used: 2589060 (0x278184)
> gas price: 1 gwei
> value sent: 0 ETH
> total cost: 0.00258906 ETH
Pausing for 2 confirmations...------------------------------> confirmation number: 1 (block: 8875879)
> confirmation number: 2 (block: 8875880)
> Saving migration to chain.
> Saving artifacts
-------------------------------------> Total cost: 0.00258906 ETHSummary=======> Total deployments: 2
> Final cost: 0.002782279 ETH

Minting our new NFT

We can send a transaction to mint tokens to a given account, from an account with the minter role.

truffle(rinkeby)> nft = await ERC721PresetMinterPauserAutoId.deployed()

In our case we are minting from the account which deployed the token, which is given the minter role.

To see configured accounts run the command accounts.

truffle(rinkeby)> accounts

We will mint 1 NFT with token ID 0. Specify the address that you want to be the token holder (0x993E6B1Cf39135Cd6BCdb87Ff9E6bab538035FF3 is one of my test accounts)

We will mint 1 NFT with token ID 0.

truffle(develop)> await"0x993E6B1Cf39135Cd6BCdb87Ff9E6bab538035FF3")

Now we have minted our first NFT participation certificate and it is on the address “0x993E6B1Cf39135Cd6BCdb87Ff9E6bab538035FF3”. Now we would open metamask

goto “add token” and add your newly created contract address

you would see your NFTcert here

Now to interact with your NFT, we would have to go to (this version of opensea is testnet as we are using Rinkeby testnet)

select your metamask account where your NFTCert is located, then you would see the NFTs in your account

Here we can see our certificate of recognisation for Ankur Kumar(which I have made)

Finishing up…

Now the last step is to make an QR Code so that the participant could see the NFT on the opensea.

the link is

After making QR code for above URL, The final certificate which could be given to the participant is-

Last Thoughts…

ERC-721 is an ethereum protocol which distinguishes one token from another, simple ERC-20 Tokens could be exchanged one to one, but ERC-721 tokens could not. These tokens are known as NFT or non-fungible tokens and could be used in variety of ways- making a game character, giving a distinguishable characterstic to any digital thing and many more. This project would help not to make an cool certificate to give to participants, but would also help to reduce counterfeit certificates

The github link for the project is


Deep dive into blockchain

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

NODE-JS Authentication Master Series using MongoDB.

Getting Started With React

React logo

Simplify Your Life With React!

Vert.x as Backend for Single-Page Apps

From Reflux To Redux (part 1/2)

How To Properly Use Frontend UI Component Libraries

Adding Video Chat To Your Laravel App

Image Search Application using ReactJS, Unsplash API and cloudflare worker

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store


Deep dive into blockchain

More from Medium

Web3.0: How to Make a Cryptocurrency, Smart Contract, or Blockchain dApp

Beginners Guide to Blockchain Part V: What is an NFT?

Starting an NFT project from scratch

Understanding NFTs