Building Role-Based NFTs for Healthcare on the Stacks Blockchain.

Biliqis OnikoyiBiliqis Onikoyi
3 min read

In this tutorial, we’ll build a role-based NFT smart contract using Clarity, the smart contract language on the Stacks blockchain. These NFTs will represent user roles in a decentralized healthcare system.


🔍 Overview

We'll walk through creating a SIP-009 compliant NFT called RoleNFT, which represents roles like Patient, Medical Staff, Executive, and more. This NFT contract:

  • Assigns a unique role to each token

  • Stores a URI to metadata (like an IPFS link)

  • Implements all the required SIP-009 functions


📦 Prerequisites

  • Clarinet (installed via Stacks CLI)

  • Basic familiarity with Clarity and smart contracts

Install Clarinet if you haven't:

npm install -g @hirosystems/clarinet

Create a new project:

clarinet new role-nft
cd role-nft

🧠 Step 1: Define Roles

In contracts/role-nft.clar,start by defining roles as constants:

(define-constant ROLE_PATIENT u0)
(define-constant ROLE_MEDICAL_STAFF u1)
(define-constant ROLE_ADMIN_STAFF u2)
(define-constant ROLE_EXECUTIVE u3)
(define-constant ROLE_NON_MEDICAL_STAFF u4)

These uints will represent different user roles on-chain.


🔗 Step 2: Set Up SIP-009 NFT

SIP-009 is the standard interface for NFTs on Stacks. Implement metadata URI storage, ownership tracking, and SIP-009-required functions like get-owner, get-token-uri, and transfer.

Define the NFT and URI map:

(define-non-fungible-token role-nft uint)
(define-map token-uri ((id uint)) ((uri (string-utf8 256))))
(define-map token-roles ((id uint)) ((role uint)))
(define-data-var token-id-counter uint u0)

🧬 Step 3: Minting Role NFTs

The mint-role function assigns a role and URI to a new token.

(define-public (mint-role (recipient principal) (role uint) (uri (string-utf8 256)))
  (if (not (is-some (get role {
    u0: true, u1: true, u2: true, u3: true, u4: true
  })))
    (err u400)
    (let (
      (id (+ (var-get token-id-counter) u1))
    )
      (var-set token-id-counter id)
      (map-set token-uri ((id id)) ((uri uri)))
      (map-set token-roles ((id id)) ((role role)))
      (nft-mint? role-nft id recipient)
    )
  )
)

This function:

  • Increments the ID counter

  • Stores the URI and role

  • Mints the token to the recipient


🔍 Step 4: Read Token Role

(define-read-only (get-role (id uint))
  (default-to u999 (get role (map-get? token-roles ((id id)))))
)

Returns the role of the NFT or u999 if not found.


🧾 Step 5: SIP-009 Read Functions

(define-read-only (get-owner (id uint))
  (nft-get-owner? role-nft id)
)

(define-read-only (get-token-uri (id uint))
  (match (map-get? token-uri ((id id)))
    uri-data (ok (get uri uri-data))
    (err u404)
  )
)

These functions allow dApps to query token ownership and metadata.


🔁 Step 6: Transfer NFTs

(define-public (transfer (id uint) (sender principal) (recipient principal))
  (nft-transfer? role-nft id sender recipient)
)

Allows token owners to transfer role NFTs.


🧪 Step 7: Testing with Clarinet

Run tests using:

clarinet test

You can create test files in tests/ to simulate minting, transferring, and reading role NFTs.


🧠 Bonus Ideas

  • Create a frontend in Next.js to visualize role NFTs

  • Integrate IPFS for decentralized metadata

  • Use Clarity’s define-trait to enforce interfaces


🧾 Conclusion

You’ve now built a complete role-based NFT contract on Stacks! This approach can be used in healthcare, education, or any field that requires identity + role binding on-chain.

Want the full code? Check the GitHub repo here: http://github.com/BiliqisO/MediVerse


🔗 Resources

Let me know in the comments if you want a frontend for this too!

0
Subscribe to my newsletter

Read articles from Biliqis Onikoyi directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Biliqis Onikoyi
Biliqis Onikoyi

Web3 || FrontEnd Dev