Hello Humanity

With our environment ready we can now move into developing the application that will allow any user enrolled with an EVM wallet address in Humanity Protocol to prove they are indeed human.

You can download a repository with the code for this section. If you do, do not forget to rename .env.example to .env.local

First let’s create a .env.local file at the root of our directory with the following contents

NEXT_PUBLIC_API_KEY=<YOUR_HP_API_KEY_HERE>
NEXT_PUBLIC_API_ADDRESS=http://api.humanity.org

In order to properly connect our wallet without any Typescript errors we need to create a global.d.ts file with the following contents

interface Window {
  ethereum?: {
    request: (args: { method: string }) => Promise<any>
  }
}

Then in our tsconfig.ts file we need to add our global.d.ts file inside the includes array like this:

...
"include": [
    "next-env.d.ts",
    "global.d.ts",
    ".next/types/**/*.ts",
    "**/*.ts",
    "**/*.tsx"
  ],
  ...

Let’s create a new folder called components inside our app folder, then create a file called MessageCard.tsx with the following content

import React from 'react'
import Confetti from 'react-confetti-boom'

interface MessageCardProps {
  isSuccess: boolean
}

const MessageCard: React.FC<MessageCardProps> = ({ isSuccess }) => {
  return (
    <div
      className="rounded-[2rem] shadow-lg h-1/2 w-full bg-white text-text"
      style={{ height: '24rem', width: '20rem' }}
    >
      <div className="bg-background border-2 border-white h-[15rem] rounded-t-[2rem] flex items-center justify-center align-center w-full card">
        <svg
          width="64"
          height="64"
          viewBox="0 0 64 64"
          fill="none"
          xmlns="http://www.w3.org/2000/svg"
          className="scale-[300%]"
        >
          <path
            d="M39.9617 46.1183C38.9214 46.9141 37.7723 47.5313 36.5457 47.9583L36.4486 47.9933C36.0138 48.1408 35.5209 47.9816 35.2491 47.6012L34.6707 46.7938C34.3408 46.328 33.9021 46.0834 33.3393 46.0407C33.0831 46.0213 32.8385 46.0135 32.5357 46.0135C32.2679 46.0135 32.0155 46.0329 31.7632 46.064C31.2547 46.1261 30.8432 46.3746 30.5482 46.8054L29.8728 47.7875C29.6088 48.1718 29.108 48.3426 28.6616 48.1951C27.5475 47.8302 26.4296 47.2247 25.1447 46.293C24.9079 46.1222 24.574 46.1533 24.4032 46.3668C24.2596 46.5454 24.2557 46.8016 24.3877 46.9879C25.1408 48.0554 25.9016 49.1229 26.6431 50.1555L27.9707 52.011C28.6927 53.028 29.6476 53.5676 30.8898 53.6647C31.4371 53.7074 31.9612 53.7268 32.4736 53.7268C33.1723 53.7268 33.8439 53.688 34.5038 53.6064C35.614 53.4706 36.5068 52.9271 37.159 51.9877C37.6248 51.3161 38.0945 50.6291 38.5487 49.9653L38.7583 49.6625C39.2668 48.9249 39.7831 48.168 40.2994 47.4265C40.443 47.2208 40.5828 47.0151 40.7264 46.8132C40.7924 46.72 40.8235 46.6113 40.8235 46.5027C40.8235 46.3474 40.7458 46.2037 40.6216 46.1067C40.4392 45.9631 40.1635 45.9708 39.9656 46.1222L39.9617 46.1183Z"
            fill="#BA5400"
          />
          <path
            d="M21.8541 43.7659C20.7788 43.0244 19.8356 42.1199 19.0514 41.0874L18.9893 41.002C18.7137 40.6332 18.7137 40.113 18.9893 39.7404L18.8418 39.6317L19.0243 39.6899L19.5794 38.9407C19.9171 38.4827 20.018 37.9897 19.886 37.4384C19.8278 37.19 19.7579 36.9532 19.6609 36.6659C19.5755 36.4175 19.4784 36.1768 19.3736 35.9478C19.1562 35.482 18.7952 35.1714 18.2906 35.02L17.1454 34.6823C16.6951 34.5503 16.3807 34.1272 16.3846 33.6536C16.3846 32.4852 16.6175 31.2352 17.1105 29.7213C17.1998 29.4418 17.0678 29.1352 16.8116 29.042C16.5981 28.9605 16.3535 29.0342 16.2177 29.2167C15.4219 30.2803 14.63 31.3556 13.8653 32.392L13.8342 32.4309C13.4072 33.0131 12.9763 33.5954 12.5454 34.1738C11.804 35.1753 11.5827 36.2506 11.8739 37.4578C12.1806 38.7194 12.561 39.8374 13.0423 40.8778C13.512 41.8948 14.3078 42.5741 15.4025 42.9041C16.1866 43.137 16.9824 43.3738 17.7549 43.6028L18.0383 43.6882C18.9194 43.9483 19.8239 44.2161 20.709 44.484L20.7633 44.3093L20.7517 44.4956C20.9768 44.5655 21.2059 44.6315 21.4271 44.7014C21.4776 44.7169 21.5319 44.7247 21.5863 44.7247C21.6445 44.7247 21.6988 44.7169 21.7532 44.6975C21.9007 44.647 22.0133 44.5306 22.0715 44.3831C22.1491 44.1657 22.0599 43.9056 21.8541 43.7659Z"
            fill="#BA5400"
          />
          <path
            d="M25.1619 21.046C25.5385 20.6966 25.7209 20.2541 25.7093 19.7301L25.6782 18.5383C25.6666 18.0686 25.9694 17.6416 26.4197 17.4941C27.5299 17.137 28.7837 16.9701 30.3636 16.9701H30.383C30.678 16.9701 30.9265 16.7488 30.9381 16.4732C30.9498 16.2442 30.8023 16.0345 30.5849 15.9608C29.397 15.5571 28.2014 15.1573 27.0369 14.7691L26.7884 14.6875C26.1013 14.4585 25.4181 14.2295 24.731 14.0005C23.5471 13.6045 22.4563 13.7287 21.4004 14.377C20.2941 15.0563 19.3508 15.7667 18.5123 16.547C17.6933 17.3078 17.2895 18.2744 17.3167 19.4195C17.3361 20.2386 17.3594 21.0693 17.3788 21.8728L17.3866 22.1912C17.4099 23.1034 17.4371 24.035 17.4565 24.9589C17.4603 25.2112 17.4681 25.4597 17.4681 25.7081C17.4681 25.8207 17.5069 25.9294 17.5729 26.0187C17.6661 26.1429 17.8136 26.2166 17.9689 26.2205H17.9844C18.2095 26.2205 18.4192 26.0536 18.489 25.8207C18.8656 24.5669 19.4284 23.3907 20.1932 22.2921L20.2281 22.2416C20.4959 21.8651 20.9889 21.7059 21.4276 21.8534L21.4858 21.6826V21.8728L22.3709 22.1679C22.9105 22.3503 23.4073 22.2921 23.8926 21.9932C24.11 21.8612 24.3235 21.7176 24.5564 21.5429C24.7737 21.3799 24.9678 21.2207 25.1503 21.046H25.1619Z"
            fill="#BA5400"
          />
          <path
            d="M44.8916 22.0434C45.5787 22.9906 46.126 24.1357 46.6152 25.6497C46.6928 25.8864 46.9063 26.0417 47.1314 26.0417C47.1741 26.0417 47.2168 26.0378 47.2595 26.0262C47.4808 25.9641 47.6361 25.7622 47.6361 25.5332C47.6555 24.2561 47.6633 22.9712 47.6749 21.7251L47.6943 19.3611C47.706 18.115 47.2518 17.1135 46.3046 16.3099C45.3186 15.4676 44.352 14.7882 43.3505 14.2331C42.3723 13.6858 41.3281 13.6043 40.2489 13.9847C39.4764 14.2564 38.6923 14.532 37.9353 14.8038L37.652 14.9047C36.7863 15.2114 35.9012 15.5258 35.0239 15.8325C34.7872 15.914 34.5504 15.9955 34.3175 16.077C34.2088 16.1119 34.1195 16.1818 34.0535 16.2711C33.9642 16.3992 33.9409 16.5584 33.9836 16.7136C34.0496 16.9349 34.2631 17.0863 34.5115 17.0863H34.5271C35.8391 17.0552 37.1279 17.2299 38.3973 17.6142L38.471 17.6375C38.9097 17.7734 39.2125 18.1965 39.2086 18.6584L39.2008 19.6522C39.1969 20.2228 39.4027 20.677 39.8374 21.0458C40.0315 21.2127 40.2295 21.3641 40.4702 21.5388C40.6876 21.6902 40.905 21.8299 41.1262 21.9503C41.5765 22.1987 42.054 22.2375 42.547 22.0667L43.6727 21.6669C44.1114 21.5116 44.616 21.6669 44.8916 22.0512V22.0434Z"
            fill="#BA5400"
          />
          <path
            d="M52.505 34.1095C52.0081 33.4612 51.5035 32.8013 51.0105 32.1608L50.6068 31.6368C50.1177 31.0001 49.6208 30.3519 49.1356 29.7114L49.0968 29.6609C48.957 29.4785 48.8211 29.296 48.6853 29.1136C48.6193 29.0204 48.5261 28.9583 48.4174 28.9272C48.2699 28.8806 48.1108 28.9078 47.9749 28.9971C47.7847 29.1252 47.7071 29.3892 47.7886 29.626C48.2233 30.8604 48.4563 32.1414 48.4834 33.4418V33.5466C48.4912 34.0047 48.1845 34.4239 47.742 34.5676L46.7948 34.8665C46.2514 35.0373 45.8826 35.375 45.6652 35.899C45.5682 36.1319 45.4828 36.3649 45.3896 36.656C45.312 36.9122 45.2498 37.1606 45.1994 37.4052C45.1023 37.9098 45.211 38.3757 45.5293 38.791L46.2552 39.7382C46.5425 40.1108 46.5464 40.6349 46.2669 41.0153C45.5798 41.9625 44.6598 42.8359 43.371 43.7714C43.1342 43.9461 43.0644 44.2722 43.2158 44.4973C43.3128 44.6448 43.4758 44.7264 43.6466 44.7264C43.6971 44.7264 43.7476 44.7186 43.798 44.7031C45.0674 44.311 46.3406 43.9073 47.5712 43.5191L47.6178 43.5036C48.3049 43.2862 48.9958 43.0688 49.6868 42.8514C50.8746 42.4788 51.686 41.7373 52.1595 40.5883C52.6525 39.3888 53.0019 38.2631 53.2232 37.1373C53.4367 36.0388 53.196 35.0217 52.5011 34.1134L52.505 34.1095Z"
            fill="#BA5400"
          />
        </svg>
      </div>

      <div className="text-left font-[300] text-xl w-[90%] mx-auto mt-4">
        <Confetti mode="boom" particleCount={150} />
        <div className="flex items-start align-center font-semibold text-text justify-center mt-[0.85rem]">
          Identity Verified
        </div>
      </div>
      {isSuccess && (
        <div className="flex items-center align-center justify-center mt-[1.5rem]">
          <button className="px-4 py-2 bg-primary rounded text-[0.875rem] font-semibold text-white rounded hover:bg-primaryHover w-[75%] mx-auto uppercase tracking-[0.125rem]">
            access
          </button>
        </div>
      )}
    </div>
  )
}

export default MessageCard

This is a simple component in charge of displaying a successful verification graphic for users with an EVM wallet address associated with their Humanity Protocol identity. The component accepts a simple property isSuccess then just returns styled HTML alongside a confetti effect when the component is rendered on our app.

Let’s jump into our page.tsx and create our logic. We will add the following content at the beginning of the file

'use client'

import { useState } from 'react'
import toast from 'react-hot-toast'
import './styles/globals.css'
import MessageCard from './components/MessageCard'

inside our export function Page we will include a set of state variables to control the reactivity of our application. We need to keep track of the connected wallet address, a trimmed version of that address for displaying purposes, a boolean reference for our API call and, finally a loading state for when our fetching to the API is taking place.

const [walletAddress, setWalletAddress] = useState()
const [normalizedAddress, setNormalizedAddress] = useState('Connect Wallet')
const [isSuccess, setIsSuccess] = useState(false)
const [loading, setLoading] = useState(false)

Then for our return statement we will replace our previous content with the following

return (
    <div className="layout-container bg-background">
      <div className="layout-header fixed right-[3rem] top-[3rem]">
        <button
          onClick={connectWallet}
          className="px-4 py-2 bg-primary rounded-full text-[0.875rem] font-semibold text-white rounded hover:bg-primaryHover"
        >
          {normalizedAddress}
        </button>
      </div>
      <div className="mx-auto layout-content h-screen w-full flex align-center justify-around items-center">
        <div className="layout-left">
          <h1 className="text-[3.5rem] font-[200] text-text mb-8">
            Hello Humanity Auth
          </h1>
          <button
            onClick={fetchData}
            className="px-4 py-2 bg-secondary text-black rounded hover:bg-secondaryHover flex justify-around items-center"
            disabled={loading}
          >
            {loading ? 'Loading...' : 'Check your human credential'}
            <span className="ml-[1rem]">
              <svg
                xmlns="http://www.w3.org/2000/svg"
                width="24"
                height="24"
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                strokeWidth="2"
                strokeLinecap="round"
                strokeLinejoin="round"
                className="lucide lucide-key-icon lucide-key"
              >
                <path d="m15.5 7.5 2.3 2.3a1 1 0 0 0 1.4 0l2.1-2.1a1 1 0 0 0 0-1.4L19 4" />
                <path d="m21 2-9.6 9.6" />
                <circle cx="7.5" cy="15.5" r="5.5" />
              </svg>
            </span>
          </button>
        </div>
        <div className="layout-right">
         
        </div>
      </div>
    </div>
  )

This will generate the right HTML we are going to need in order to connect our wallet and make our call to the Humanity Protocol API

Now let’s connect our app to Metamask. Add the following function after your state variables declarations

const connectWallet = async () => {
    if (typeof window.ethereum !== 'undefined') {
      try {
        const accounts = await window.ethereum.request({
          method: 'eth_requestAccounts',
        })
        setNormalizedAddress(
          accounts[0].substr(0, 4) +
            '...' +
            accounts[0].substr(accounts[0].length - 3, accounts[0].length)
        )
        setWalletAddress(accounts[0])
      } catch (error) {
        console.error('Error connecting to wallet:', error)
      }
    } else {
      alert('Please install MetaMask')
    }
  }

This function will handle the connection to Metamask from our HTML markup through our “Connect Wallet” button. It will look for an instance of Metamask in our browser and if one is found will create a connection with our currently selected account asking you to allow to connect to the application through a dialog.

It will also change our “Connect Wallet” text to a trimmed version of our wallet address as well as taking care of errors when trying to connect to Metamask or if no instance of Metamask is found in the browser (the extension is not installed or enabled).

Now let’s focus on the connection to Humanity Protocol API. Add the following code below our previous connectWallet function

const fetchData = async () => {
    if (!walletAddress) {
      toast.error('Please connect your wallet first')
      return
    }

    setLoading(true)
    try {
      const response = await fetch(
        `${process.env.NEXT_PUBLIC_API_ADDRESS}/v1/human/verify?walletAddress=${walletAddress}`,
        {
          headers: {
            'X-HP-API-Key': `${process.env.NEXT_PUBLIC_API_KEY}`,
          },
        }
      )

      const data = await response.json()

      
      if (data.is_human) {
        setIsSuccess(true)
      } else {
        setIsSuccess(false)
        toast.error('Sorry, you are not a verified human')
      }
    } catch (error) {
      console.error('Error fetching data:', error)
      toast.error('Error fetching data')
    } finally {
      setLoading(false)
    }
  }

The above function will only work if we successfully connected our wallet using one of our state variables: walletAddress. If not we will use react-toaster installed library to display an error.

If a wallet is connected, we will proceed with the request to the API using the base URL stored in our .env.local file plus the right endpoint v1/human/verify? plus the connected wallet address.

In the call we need to attach our API key in the headers section declaring it as X-HP-API-Key

We await for the json response coming from the API and if it does not contain a property called is_human set to true we will display a react-toast message informing the user they are not a verified human within Humanity Protocol.

If the is_human property is true we set our isSuccess state variable as true as well.

Finally let’s add this following line to our return statement inside our <div className=”layout-right”> HTML node.

 ...
 <div className="layout-right">
  {
    isSuccess ? <MessageCard isSuccess={isSuccess} /> : null 
</div>
...

This will trigger our MessageCard component to show up once our isSuccess variable changes its state to true.

Your Page.tsx file should look like this

'use client'

import { useState } from 'react'
import toast from 'react-hot-toast'
import './styles/globals.css'
import MessageCard from './components/MessageCard'

export default function Page() {
  const [walletAddress, setWalletAddress] = useState()
  const [normalizedAddress, setNormalizedAddress] = useState('Connect Wallet')
  const [isSuccess, setIsSuccess] = useState(false)
  const [loading, setLoading] = useState(false)

  const connectWallet = async () => {
    if (typeof window.ethereum !== 'undefined') {
      try {
        const accounts = await window.ethereum.request({
          method: 'eth_requestAccounts',
        })
        setNormalizedAddress(
          accounts[0].substr(0, 4) +
            '...' +
            accounts[0].substr(accounts[0].length - 3, accounts[0].length)
        )
        setWalletAddress(accounts[0])
      } catch (error) {
        console.error('Error connecting to wallet:', error)
      }
    } else {
      alert('Please install MetaMask')
    }
  }

  const fetchData = async () => {
    if (!walletAddress) {
      toast.error('Please connect your wallet first')
      return
    }

    setLoading(true)
    try {
      const response = await fetch(
        `${process.env.NEXT_PUBLIC_API_ADDRESS}/v1/human/verify?walletAddress=${walletAddress}`,
        {
          headers: {
            'X-HP-API-Key': `${process.env.NEXT_PUBLIC_API_KEY}`,
          },
        }
      )

      const data = await response.json()

      if (data.is_human) {
        setIsSuccess(true)
      } else {
        setIsSuccess(false)
        toast.error('Sorry, you are not a verified human')
      }
    } catch (error) {
      console.error('Error fetching data:', error)
      toast.error('Error fetching data')
    } finally {
      setLoading(false)
    }
  }

  return (
    <div className="layout-container bg-background">
      <div className="layout-header fixed right-[3rem] top-[3rem]">
        <button
          onClick={connectWallet}
          className="px-4 py-2 bg-primary rounded-full text-[0.875rem] font-semibold text-white rounded hover:bg-primaryHover"
        >
          {normalizedAddress}
        </button>
      </div>
      <div className="mx-auto layout-content h-screen w-full flex align-center justify-around items-center">
        <div className="layout-left">
          <h1 className="text-[3.5rem] font-[200] text-text mb-8">
            Hello Humanity Auth
          </h1>
          <button
            onClick={fetchData}
            className="px-4 py-2 bg-secondary text-black rounded hover:bg-secondaryHover flex justify-around items-center"
            disabled={loading}
          >
            {loading ? 'Loading...' : 'Check your human credential'}
            <span className="ml-[1rem]">
              <svg
                xmlns="http://www.w3.org/2000/svg"
                width="24"
                height="24"
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                strokeWidth="2"
                strokeLinecap="round"
                strokeLinejoin="round"
                className="lucide lucide-key-icon lucide-key"
              >
                <path d="m15.5 7.5 2.3 2.3a1 1 0 0 0 1.4 0l2.1-2.1a1 1 0 0 0 0-1.4L19 4" />
                <path d="m21 2-9.6 9.6" />
                <circle cx="7.5" cy="15.5" r="5.5" />
              </svg>
            </span>
          </button>
        </div>
        <div className="layout-right">
          {
            isSuccess ? <MessageCard isSuccess={isSuccess} /> : null 
        </div>
      </div>
    </div>
  )
}

And your folder structure should look like this

app
├── components
│   └── MessageCard.tsx
├── styles
│   └── globals.css
├── layout.tsx
└── page.tsx
node_modules
.env.local
.gitignore
global.d.ts
next-env.d.ts
next.config.mjs
package-lock.json
package.json
postcss.config.js
README.md
tailwind.config.js
tsconfig.json

If our app is not already running make sure it is by running npm run dev If you visit http://localhost:3000 you should see something like this

Click on the Connect Wallet button and it should ask for Metamask to connect to the app

Finally click on Check your human credential and after a few second you should see the following page including our MessageCard component

Congratulations you successfully build an online application that implements a verification process for any individual enrolled in Humanity Protocol with an EVM wallet address!

Next Steps

You are now equipped to develop more sophisticated applications and incorporate Humanity Protocol into your existing systems. To facilitate this process, please visit our Developers Portal where you can access comprehensive resources to create innovative applications that ensure each user is a verified unique individual.

Last updated