Supply Chain Management (Life-Saving Drugs) Through Blockchain: A Case Study
A case study on how blockchain can help overcome challenges in supply chain management of critical drugs.

Tech Lead II at Geekyants!
Search for a command to run...
A case study on how blockchain can help overcome challenges in supply chain management of critical drugs.

Tech Lead II at Geekyants!
No comments yet. Be the first to comment.
Hope you enjoyed reading my beginner's article on what is an NFT, how it works, its advantages, disadvantages and a lot more. Well, you must be eagerly waiting to experience this new technology which is the newest addition to the digital world. Let'...
OpenClaw is a powerful, self-hosted AI assistant that connects to your tools to perform actions. Explore its Gateway architecture, real-world use cases, and security precautions.

Discover how neo-brutalism is shaping 2026 design trends. See how anti-design principles can create distinct, usable, and memorable product experiences.

When code breaks a pipeline, developers have to stop working and figure out why. This blog shows how an AI agent reads the error, finds the fix, and submits it for review all on its own.

GeekyAnts built a 5-agent fraud detection pipeline that makes decisions in under 200ms — 15x cheaper than single-model systems, with full explainability built in.

A deep dive into how GeekyAnts built a real-time AI fraud detection system that evaluates transactions in milliseconds using a hybrid multi-agent approach.

GeekyAnts Tech Blog
348 posts
GeekyAnts is an AI-powered digital product engineering and consulting company helping startups, enterprises, and Fortune 500 brands build scalable, future-ready digital solutions. Since 2006, we have delivered 800+ successful projects for 550+ global clients across healthcare, BFSI, retail, logistics, education, and enterprise technology. We help businesses accelerate digital transformation through strategy, design, engineering, and AI-led innovation.
Blockchain has been the game-changing technology behind cryptocurrencies and everything Decentralised Finance (DeFi). However, the underlying principles of how blockchain works are being leveraged across other sectors these days. One such sector where it holds great value is supply chain management. Blockchain has the potential of improving supply chains by enabling faster and more cost-efficient products delivery, enhancing products traceability, and improving coordination between partners.
Companies such as Walmart, Emerson, Hayward, IBM, and MasterCard operate in varied industries such as retailing, technology, and financial services. They are either conducting pilots or have already moved even further and are working with supply chain partners to develop real-time applications. In this case study we will be creating a supply chain management platform which deals with life saving drugs. In case you need more information on the project's code base, find the same here.
Since the 1990's, there has been a considerable advancement in supply chain information sharing, thanks to the use of enterprise resource planning (ERP) systems. However, visibility remains a challenge in large supply chains that involve complex transactions.
Let us consider a scenario of a simple transaction involving a manufacturer that sources a product to a supplier and a vendor that sources the product from supplier to later sell it to customers. However, in the flow from manufacturer to supplier to vendor, the financial institutions which are key to financing this chain are in a blind spot.
The transaction involves information, inventory, and financial flows. The state-of-the-art ERP systems, manual audits, and inspections cannot connect all these flows reliably. This makes it hard to eliminate execution errors, improve decision-making, and resolve supply chain conflicts. Blockchain can help overcome some of these limitations and create a seamless supply chain management experience for users.
The following diagram depicts the difference between conventional record keeping in supply chain management against blockchain.

During the pandemic, countries around the world faced shortages, interruption, and illegal hoarding of critical life-saving drugs. Sighting this problem, we will be creating an effective supply chain management solution for critical life-saving drugs in this case study. We will be writing a smart contract that brings manufacturers, suppliers, vendors, and customers on a single platform to improve decision-making and resolve supply chain conflicts.
Typically in a supply chain management flow we have 4 key entities i.e., a manufacturer, a supplier, a vendor, and in the end of the chain are customers. The following flow diagram illustrates how the supply chain management would be:

Based on the various entities we discussed above, the product ownership will flow from: Manufacturer-> Supplier-> Vendor-> Customer

Now that our business flow is ready, let's get started with the smart contract.
A user on the contract will be identified by their address and role type. We created a library to define common types used.
library Types {
enum UserRole {
Manufacturer, // 0
Supplier, // 1
Vendor, // 2
Customer // 3
}
struct User {
UserRole role;
address id_;
// Further attributes can be added for an entity such as name, email, address ...
}
}
Within the types library we have a struct for product, defining all it's attributes.
struct Product {
string name;
string manufacturerName;
address manufacturer;
uint256 manDateEpoch;
uint256 expDateEpoch;
bool isInBatch; // few products will be packed & sold in batches
uint256 batchCount; // QTY that were packed in single batch
string barcodeId;
string productImage;
ProductType productType;
// Further attributes goes here.....
}
In supply chain, each product will move to a different entity layer. Hence, we need to keep track of its history across each stage. We will be creating a struct for product and entity relationship and later storing the product's history in the supply chain against the product ID as key.
struct ProductHistory {
UserHistory manufacturer;
UserHistory supplier;
UserHistory vendor;
UserHistory[] customers;
}
Now that our types are set, let's write all the crud required at the user layer in a contract named Users. These include adding a user as Manufacturer, Supplier, and so on, updating user, deleting the user, as well as getting an information specific to a user entity.
contract Users {
mapping(address => Types.UserDetails) internal users; // contains all users.
mapping(address => Types.UserDetails[]) internal manufacturerSuppliersList; // manufacturer -> suppliers list
mapping(address => Types.UserDetails[]) internal supplierVendorsList; // supplier -> vendors list
mapping(address => Types.UserDetails[]) internal vendorCustomersList; // vendor -> customers list
}
The following method adds a user entity based on its type. Every time a user is added, we emit an event NewUser on the blockchain, which can be listened by client to update the view while front-end integration is being done.
/**
* @dev To add a particular user to a particular role
* @param user UserDetails that need to be added
*/
function add(Types.UserDetails memory user) internal {
require(user.id_ != address(0));
require(!has(user.role, user.id_), "Same user with same role exists");
users[user.id_] = user;
emit NewUser(user.name, user.email, user.role);
}
/**
* @dev To add a particular user to a current logged-in user's correspondence list
* @param user UserDetails that need to be added
* @param myAccount User address who is trying to add the other user
*/
function addparty(Types.UserDetails memory user, address myAccount)
internal
{
require(myAccount != address(0));
require(user.id_ != address(0));
if (
get(myAccount).role == Types.UserRole.Manufacturer &&
user.role == Types.UserRole.Supplier
) {
// Only manufacturers are allowed to add suppliers
manufacturerSuppliersList[myAccount].push(user);
add(user); // To add user to global list
} else if (
get(myAccount).role == Types.UserRole.Supplier &&
user.role == Types.UserRole.Vendor
) {
// Only suppliers are allowed to add vendors
supplierVendorsList[myAccount].push(user);
add(user); // To add user to global list
} else if (
get(myAccount).role == Types.UserRole.Vendor &&
user.role == Types.UserRole.Customer
) {
// Only vendors are allowed to add customers
vendorCustomersList[myAccount].push(user);
add(user); // To add user to global list
} else {
revert("Not valid operation");
}
}
/**
* @dev To get details of the user
* @param id_ User Id for whom the details were needed
* @return user_ Details of the current logged-in User
*/
function getPartyDetails(address id_)
internal
view
returns (Types.UserDetails memory)
{
require(id_ != address(0));
require(get(id_).id_ != address(0));
return get(id_);
}
/**
* @dev To get user details based on the address
* @param account User address that need to be linked to user details
* @return user_ Details of a registered user
*/
function get(address account)
internal
view
returns (Types.UserDetails memory)
{
require(account != address(0));
return users[account];
}
/**
* @dev To remove a particular user from a particular role
* @param role User role for which he/she has to be dismissed for
* @param account User Address that need to be removed
*/
function remove(Types.UserRole role, address account) internal {
require(account != address(0));
require(has(role, account));
string memory name_ = users[account].name;
string memory email_ = users[account].email;
delete users[account];
emit lostUser(name_, email_, role);
}
There are further helper methods in the Users contract, check out the entire contract in details here.
Product contract has all the methods and state variables associated to all products such as adding a product, getting a product based on product ID, product user ownership details, selling a product down the user hierarchy, and so on.
contract Products {
Types.Product[] internal products; // list of products added by manufacturer.
mapping(string => Types.Product) internal product; // product mapped to it's product id.
mapping(address => string[]) internal userLinkedProducts; // Owner product mapping.
mapping(string => Types.ProductHistory) internal productHistory; // Product mapping across entities.
}
/**
* @dev Adds new product to the products list
* @param product_ unique ID of the product
* @param currentTime_ Current Date, Time in epoch (To store when it got added)
*/
function addAProduct(Types.Product memory product_, uint256 currentTime_)
internal
productNotExists(product_.barcodeId)
{
require(
product_.manufacturer == msg.sender,
"Only manufacturer can add"
);
products.push(product_);
product[product_.barcodeId] = product_;
productHistory[product_.barcodeId].manufacturer = Types.UserHistory({
id_: msg.sender,
date: currentTime_
});
userLinkedProducts[msg.sender].push(product_.barcodeId);
emit NewProduct(
product_.name,
product_.manufacturerName,
product_.scientificName,
product_.barcodeId,
product_.manDateEpoch,
product_.expDateEpoch
);
}
While the product is sold down the user hierarchy, its ownership gets transferred accordingly. Further relevant events are emitted at each step of the product lifecycle.
/**
* @dev Transfer the ownership
* @param partyId_ Purchase user's account address
* @param barcodeId_ unique ID of the product
* @param currentTime_ Current Date Time in epoch to keep track of the history
*/
function sell(
address partyId_,
string memory barcodeId_,
Types.UserDetails memory party_,
uint256 currentTime_
) internal productExists(barcodeId_) {
Types.Product memory product_ = product[barcodeId_];
// Updating product history
Types.UserHistory memory userHistory_ = Types.UserHistory({
id_: party_.id_,
date: currentTime_
});
if (Types.UserRole(party_.role) == Types.UserRole.Supplier) {
productHistory[barcodeId_].supplier = userHistory_;
} else if (Types.UserRole(party_.role) == Types.UserRole.Vendor) {
productHistory[barcodeId_].vendor = userHistory_;
} else if (Types.UserRole(party_.role) == Types.UserRole.Customer) {
productHistory[barcodeId_].customers.push(userHistory_);
} else {
// Not in the assumption scope
revert("Not valid operation");
}
transferOwnership(msg.sender, partyId_, barcodeId_); // To transfer ownership from seller to buyer
// Emiting event
emit ProductOwnershipTransfer(
product_.name,
product_.manufacturerName,
product_.scientificName,
product_.barcodeId,
party_.name,
party_.email
);
}
The function to transfer ownership of the product is an internal function that is to be used while the product is being sold.
/**
* @dev To remove the product from current list once sold
* @param sellerId_ Seller's metamask address
* @param buyerId_ Buyer's metamask address
* @param productId_ unique ID of the product
*/
function transferOwnership(
address sellerId_,
address buyerId_,
string memory productId_
) internal {
userLinkedProducts[buyerId_].push(productId_);
string[] memory sellerProducts_ = userLinkedProducts[sellerId_];
uint256 matchIndex_ = (sellerProducts_.length + 1);
for (uint256 i = 0; i < sellerProducts_.length; i++) {
if (compareStrings(sellerProducts_[i], productId_)) {
matchIndex_ = i;
break;
}
}
assert(matchIndex_ < sellerProducts_.length); // Match found
if (sellerProducts_.length == 1) {
delete userLinkedProducts[sellerId_];
} else {
userLinkedProducts[sellerId_][matchIndex_] = userLinkedProducts[
sellerId_
][sellerProducts_.length - 1];
delete userLinkedProducts[sellerId_][sellerProducts_.length - 1];
userLinkedProducts[sellerId_].pop();
}
}
There are further helper methods, function, and modifiers in the Products contract. Check out the entire contract in details here.
Now that we have created separate contracts for products and users to handle all the required crud operations, we will be creating our main contract SupplyChain which will in turn inherit Users and Products contract. The constructor method sets the creator of the contract as the Manufacturer. The contract consist of all the methods required for our supply chain management flow.
// SPDX-License-Identifier: GPL-3.0
pragma experimental ABIEncoderV2;
pragma solidity >=0.4.25 <0.9.0;
import "./Products.sol";
import "./Users.sol";
/**
* @title SupplyChain
* @author Suresh Konakanchi | GeekyAnts
* @dev Implements the transparency in the supply chain to
* understand the actual user & the flow of the products/medicines
*/
contract SupplyChain is Users, Products {
/**
* @dev Create a new SupplyChain with the provided 'manufacturer'
* @param name_ Name of the manufacturer
* @param email_ Email of the manufacturer
*/
constructor(string memory name_, string memory email_) {
Types.UserDetails memory mn_ = Types.UserDetails({
role: Types.UserRole.Manufacturer,
id_: msg.sender,
name: name_,
email: email_
});
add(mn_);
}
Some of the key methods in the supply chain contract are as follows:
/**
* @dev Adds new product to the products list
* @param product_ unique ID of the product
* @param currentTime_ Current Date, Time in epoch (To store when it got added)
*/
function addProduct(Types.Product memory product_, uint256 currentTime_)
public
onlyManufacturer
{
addAProduct(product_, currentTime_);
}
/**
* @dev Transfer the ownership
* @param partyId_ Purchase user's account address
* @param barcodeId_ unique ID of the product
* @param currentTime_ Current Date Time in epoch to keep track of the history
*/
function sellProduct(
address partyId_,
string memory barcodeId_,
uint256 currentTime_
) public {
require(isPartyExists(partyId_), "Party not found");
Types.UserDetails memory party_ = users[partyId_];
sell(partyId_, barcodeId_, party_, currentTime_);
}
/**
* @dev To add an user to my account, which can be used in future at the time of selling
* @param user_ Details of the user that need to be added
*/
function addParty(Types.UserDetails memory user_) public {
addparty(user_, msg.sender);
}
The SupplyChain contracts consist of functions as public, internal, and external in order to facilitate data required both at the contract level as well as one for front-end. Check out the entire contract in details here.
Increasing efficiency, speed, and reduced disruptions.
Any counterfeit in the process can be traced to its source using the blockchain trail.
Members of a supply chain can always ascertain the source and quality of their inventory since each unit of the supply chain is firmly coupled with the identity of its particular owner at every step along the way.
Even though blockchain record is secure, there is still the risk of a contaminated or counterfeit product being tagged and introduced into the supply chain, either in error or by a corrupt actor.
Inaccurate inventory data resulting from mistakes in scanning, tagging, and data entry, since flow is prone to errors.
Transactions on a blockchain network are accepted by the majority of participants (proof of work), but unfortunately, it also limits the speed at which new blocks can be added. Consequently, it is too slow to handle the speed and volume of transactions in supply chains.
Blockchain improves supply chains in terms of end-to-end traceability, speed of product delivery, level and quality of coordination, and eliminating counterfeit. It's time for supply chain managers across industry sectors to explore and conduct pilots with various blockchain platforms and build an ecosystem with other firms. The investment will definitely generate handsome returns.
You can find the full source code here
We started with blockchain, the underlying mechanism of how it works, and wrote our first smart contract and a case study around electronic voting using blockchain. You can also checkout our E-Voting Case Study. In the next article, we will go through the case study of blockchain in KYC Chain.
This article is part of Research & Development work being done by Pushkar Kumar, Suresh konakanchi, and Ruchika Gupta. We will be covering a series of articles along with open source projects around blockchain, smart contracts, and web3 in general. Here is the list of all the articles that you can follow to start with Blockchain and write your first contract.