Every single day, we engage in numerous activities on the blockchain. Consider a report card that consolidates all these on-chain interactions. Such a report card holds immense value as a means of showcasing reputation, fostering an achievement system, or serving as a personal tracking method within the blockchain community.

Creating such a report card that encompasses all on-chain interactions has become incredibly simple thanks to Helius and the Underdog Protocols APIs. This tutorial blog aims to guide you through a step-by-step process to construct your very own application to generate an on-chain report card as an NFT.

What is Underdog Protocol?

Underdog Protocol is a platform that assists developers and startups in simplifying their interactions with digital collectibles. It allows them to seamlessly integrate web3 into their products or workflows to enhance their user experience.

Here are some useful resources :

Technologies used in this project include:

  • Underdog protocol
  • Helius
  • Next.js
  • Chakra-ui
  • Express
  • Solana blockchain
  • Wallet adapter for Solana

Generating API keys

Visit Underdog Protocol click on 'Get Started', and connect your wallet. Once inside the dashboard, click on 'API keys' and generate your API key to access the Underdog Protocol REST APIs.

visit Helius and repeat the same process.

The app flow is as follows:

There are essentially 3 steps:

  1. The user connects the wallet to the app.
  2. This wallet address is dispatched to Helius APIs to get some useful data.
  3. This data is transformed into valuable metadata and utilized with the Underdog Protocol 'Mint NFT' API to generate NFTs.

Making API calls

Before making all necessary API calls to the tools and protocols, ask the user to connect their wallet. Using the Solana wallet adapter and Solana web3.js packages, implement a 'connect wallet' button that detects installed wallets on the user's browser and prompts them to connect.

After completing this step, we obtain the public key of the user, which is essential for collecting data about that specific address using the Helius APIs.

Why data from Helius?

Similar to how a school report card comprises scores for various subjects, our NFT report card for users will also include scores for different on-chain activities. To calculate this score, we require detailed data pertaining to the user's wallet address.

We will be calculating the score by sending requests to the following APIs:

  • To gather all the NFT-related data of the user, we will initiate a POST request to the following endpoint. This request will provide a response containing all the NFT events associated with the provided wallet address.
  • COPY
  • COPY
  •  https://api.helius.xyz/v1/nft-events
  • To obtain token balances and metadata-related information, separate requests need to be sent to the following APIs accordingly:
  • COPY
  • COPY
  •  https://api.helius.xyz/v0/addresses/{address}/balances
  • COPY
  • COPY
  •  https://api.helius.xyz/v0/token-metadata

In order to determine the wallet age, requires calculating the difference between the date of the user's first transaction and the current date. To find out when the first transaction is made we need to make an RPC call, it would look something like this

COPY

COPY

const { Connection, PublicKey } = require("@solana/web3.js");
require('dotenv').config()

const HELIUS_API_KEY = process.env.HELIUS_API_KEY;

const connection = new Connection(
   `https://rpc.helius.xyz?api-key=${HELIUS_API_KEY}`
);

async function getWalletAge(address, beforeSig) {
   let signatures = ["1"];
   let counter = 0;
   let before = { before: beforeSig };
   let lastSig = { signature: "", slot: 0 };
   let firstSig = { signature: "", slot: 0 };
   while (signatures.length > 0) {
       const sigs = await connection.getSignaturesForAddress(
           new PublicKey(address),
           before
       );
       // console.log(sigs);
       if (sigs.length == 0) {
           break;
       }
       if (counter == 0) {
           firstSig = sigs[0];
       }
       lastSig = sigs[sigs.length - 1];
       before = { before: lastSig.signature };
       counter++;
   }

   if(!firstSig.signature) {
       return {
           firstDate: "0",
           age: "0",
           lastDate: "0",
       }
   }

   const firstTx = await connection.getParsedTransaction(lastSig.signature , { "maxSupportedTransactionVersion": 0});
   const lastTx = await connection.getParsedTransaction(firstSig.signature , { "maxSupportedTransactionVersion": 0});

   const firstDate = new Date(firstTx.blockTime * 1000);
   const lastDate = new Date(lastTx.blockTime * 1000);
   let age = Date.now() - firstDate.getTime();
   age = Math.floor(age / (1000 * 60 * 60 * 24 * 30));

   return {
       firstDate: firstDate.toLocaleDateString(),
       age: age,
       lastDate: lastDate.toLocaleDateString(),
   }
}


module.exports = getWalletAge;

Calculating the score

Once we have collected all the necessary data, we can proceed with the calculations. You are free to implement your own logic for scoring, but here is a basic idea to get you started.

COPY

COPY

const getNFTStats = require('./getNFTStats');
const getProgramsInteracted = require('./getProgramsInteracted');
const getStakedAccounts = require('./getStakedAccounts');
const getWalletAge = require('./getWalletAge');

const getOnchainData = async (address) => {

   const walletAge = await getWalletAge(address);
   const nftStats = await getNFTStats(address);
   const programsInteracted = await getProgramsInteracted(address);
   const stakedAccounts = await getStakedAccounts(address);

   let nftScore, programScore, stakedScore, walletAgeScore;

   let nftSold = nftStats.nftSold.total
   let nftBought = nftStats.nftBought.total

   let nftTotal = nftStats.nftSold.volume + nftStats.nftBought.volume;

   let uniqueProgramInteractions = Object.keys(programsInteracted.programs).length;
   let totalInteractions = programsInteracted.total;
   let uniqueTransactionTypes = Object.keys(programsInteracted.type).length;

   let stakedSol = stakedAccounts.stakedSol;

   let walletage = walletAge.age;
   let firstInteraction = walletAge.firstDate;
   let lastInteraction = walletAge.lastDate;


   if (nftTotal < 10) {
       nftScore = 1;
   } else if (nftTotal < 100) {
       nftScore = 2;
   } else {
       nftScore = 3;
   }

   if (uniqueProgramInteractions < 10) {
       programScore = 1;
   } else if (uniqueProgramInteractions < 20) {
       programScore = 2;
   } else {
       programScore = 3;
   }

   if (stakedSol < 100) {
       stakedScore = 1;
   } else if (stakedSol < 1000) {
       stakedScore = 2;
   } else {
       stakedScore = 3;
   }

   if (walletage < 12) {
       walletAgeScore = 1;
   } else if (walletage < 24) {
       walletAgeScore = 2;
   } else {
       walletAgeScore = 3;
   }

   if (totalInteractions < 100) {
       programScore += 1;
   } else if (totalInteractions < 1000) {
       programScore += 2;
   } else {
       programScore += 3;
   }

   if (nftTotal == 0) {
       nftScore = 0;
   }
   if (uniqueProgramInteractions == 0) {
       programScore = 0;
   }
   if (stakedSol == 0) {
       stakedScore = 0;
   }
   if (walletage == 0) {
       walletAgeScore = 0;
   }

   let totalScore = (nftScore + programScore + stakedScore + walletAgeScore);
   console.log(totalScore);

   let status = 0;
   if (totalScore < 2.5) {
       status = 1;
   } else if (totalScore < 3.5) {
       status = 2;
   } else {
       status = 3;
   }

   return {
       nftSold,
       nftBought,
       nftTotal,
       uniqueProgramInteractions,
       totalInteractions,
       uniqueTransactionTypes,
       stakedSol,
       walletage,
       firstInteraction,
       lastInteraction,
       status,
       totalScore
   }

}

module.exports = getOnchainData;

Mint the NFT

We're almost finished. The final step is to send the collected comprehensive on-chain data as metadata to the Underdog Protocol API for minting our NFT.

COPY

COPY

https://dev.underdogprotocol.com/v2/collections/{mintAddress}/mint

Here's a possible implementation of the code

COPY

COPY


const axios = require('axios');
const BASE_URL = process.env.BACKEND_URL

export default async function handler(req, res) {

   if (req.method === "GET") {
       console.log("GET")
   }

   if (req.method === "POST") {


       const degenData = req.body.data
       const { wallet } = req.body.params;

       console.log(degenData)
       console.log(wallet)

       const resp = await axios.post(`${BASE_URL}/mint`, {
           params: {
               wallet: wallet,
           },
           data: {
               degenData: degenData
           }
       })

       console.log(resp.data)

       res.status(200).json("success")


   }
}

and there you have it we have successfully built an app to mint a report card for the user based on his on-chain interactions.

Congratulations on this achievement! 🎉

You can experiment with the code of the live application provided in the link. Feel free to contribute to the app. Let's delve into another tutorial soon, and until then, keep buildin.