Skip to main content
Version: V2

Collect a badge

In this section you'll learn how to implement the Collect Essence feature in both gasless and gas modes. Previously you've learned that creating a badge means registering an essence, but the process of minting and transferring the SBT is actually executed when a user collects the badge by attending an event.

To keep things simple we will only focus on the actual implementation of users collecting badges. Linking them to an actual event and checking whether users have attended the event falls beyond the scope of the tutorial.

Gasless Mode

GraphQL mutations

To collect an essence, meaning to collect a SBT badge, is a two step process and requires two GraphQL mutations: CreateCollectEssenceTypedData and Relay.

  1. CreateCollectEssenceTypedData is used to present data to the user in a readable format:
import { gql } from "@apollo/client";

mutation CreateCollectEssenceTypedData(
$input: CreateCollectEssenceTypedDataInput!
) {
createCollectEssenceTypedData(input: $input) {
typedData {
  1. Relay is responsible for broadcasting the transaction, minting and transferring the NFT:
import { gql } from "@apollo/client";

export const RELAY = gql`
mutation Relay($input: RelayInput!) {
relay(input: $input) {
  1. RelayActionStatus is used to get the relaying status:
import { gql } from "@apollo/client";

export const RELAY_ACTION_STATUS = gql`
query RelayActionStatus($relayActionId: ID!) {
relayActionStatus(relayActionId: $relayActionId) {
... on RelayActionStatusResult {
... on RelayActionError {
... on RelayActionQueued {

Collect a Badge

Now that you set up the APIs required, you can implement the Collect feature. The approach is similar to the approach from Create a Badge:

  1. Get data in a readable format and the typedDataID for it;
  2. Get the user to sign the message data and get its signature;
  3. Call the relay and pass it the typedDataID and signature;
/* Create typed data in a readable format */
const typedDataResult = await createCollectEssenceTypedData({
variables: {
input: {
collector: account,
profileID: profileID,
essenceID: essenceID,

const typedData =;
const message =;
const typedDataID =;

/* Get the signature for the message signed with the wallet */
const params = [account, message];
const method = "eth_signTypedData_v4";
const signature = await signer.provider.send(method, params);

/* Call the relay to broadcast the transaction */
const relayResult = await relay({
variables: {
input: {
typedDataID: typedDataID,
signature: signature,
const txHash =;

Gas Mode

To collect a badge using gas mode, you need to call the ProfileNFT contract directly:

  1. import ProfileNFTABI from GitHub
  2. Pass the abi, signer and contract address PROFILE_NFT_CONTRACT which is 0x57e12b7a5f38a7f9c23ebd0400e6e53f2a45f271 to get a contract instance
  3. call the collect method from the contract
const contract = new ethers.Contract(

const tx = await contract.collect(
collector: address,
profileId: profileID,
essenceId: essenceID,

console.log("tx", tx);

If the collect process was successful, you can verify the transaction hash on BscScan.

You can also view the NFT when a user collects a badge on You'll notice that the image for the NFT and all other details about it correspond to the details passed to the Metadata Schema fields (e.g. image_data, name, attributes, etc).

nft essence

Awesome! You've completed the How to Build Badge app guide! Now you can build your own application that issues baadges. Share your work on our Discord channel. We would love to see what you've built!

Designed by