Supply Chain Smart Contract Design | by Abhishek Chauhan | Oct, 2022

Simplify the operation of the agriculture supply chain network

supply chain | images by author

Agriculture plays a vital role in feeding and clothing the world’s seven billion citizens. Over 25% of the world’s population is employed in the agricultural sector. Agriculture supply chains have formed as long and complex networks of production, processing, distribution, and marketing channels in order to meet demand in a globalized world. They are made up of farmers, processors, traders, logistics providers, finance companies, consumers, and many others, each with interests that are often widely varied and in conflict.

The agricultural industry is a major contributor to the global economy, with over $6 trillion in revenue every year. This industry provides the world with essential commodities such as rice, corn, wheat, and livestock. However, despite its vital role in providing essential products around the globe, agricultural supply chains face many common challenges across geographies and commodities.

Blockchain technology creates a single source of truth. This is important for supply chains that involve multiple participants in a network who don’t necessarily trust each other.

In this article, we take a practical approach and explore the code. The examples in this article are designed to simplify the operation of the agricultural supply chain. It increases transparency and efficiency between farmers, distributors, retailers, and consumers.

1*3MC 4EjYtl0jrpHuwwHh0A
flow diagram


  1. Farmer creates a product and lists it to be purchased by Distributor
  2. Farmer ships the product
  3. Distributor receives the product, processes it, packages it, and puts it on sale
  4. Retailer buys the product from Distributor
  5. Distributor ships the product to Retailer
  6. Retailer receives the product and put it on sale
  7. Consumer purchase the product

Environment setup

Let’s clone the repo and install the dependencies:

git clone
cd Supply-Chain
yarn or npm install

Now navigate to /contractsfolder and create folders as given:

You will see four folders:

  • The /access folder contains roles for the farmer, distributor, retailer & consumer
  • The /ownership folder contains a solidity file Ownable.solto restrict functions to only selected addresses
  • The /supplyChain folder contains the structure of the chain
  • The /utils folder contains a context file that provides information about the current execution context, including the sender of the transaction and its data

Start from designing a smart contract for roles, for this purpose we use /access folder.

This contract will be used to manage roles on the platform. It provides the functions to add, remove and query information about roles. This will then be imported into our other roles smart contracts.

  1. Let’s begin with creating library Roles:
library Roles {

2. Now, we create struct Role to set an address. To do this, you must create a mapping that maps addresses in boolean value to the bearer.

You must create a mapping that maps the address to a boolean value for the bearer.

library Roles {
struct Role {
mapping(address => bool) bearer;

3. Create internal function addwith storage role, and account address to check the address is passing is not connected account and has not been added before:

function add( Role storage role, address account) internal {
require(account != address(0));
require(!has(role, account));

4. Similarly, we create remove function and here we check the passing address is not the connected account and that the address does exist in a given role and the last set role value in storage to false.

function remove( Role storage role, address account) internal {
require(account != address(0));
require(has(role, account));
role.bearer[account] = false;

5. In the final step, create a function hasto check if the account has a role or not, it returns a boolean value:

function remove( Role storage role, address account) 
returns (bool)
require(account != address(0));
return role.bearer[account];

6. Finishing up, your smart contract now looks like the following:


Now we use our previous Roles library contract to create new roles for farmers.

  1. Let’s begin with creating a contract FarmerRolefile and use the struct Role Rolelibrary.
contract FarmerRole is Context {
using Roles for Roles.Role

2. Create two events to capture if farmers are added or removed successfully.

event FarmerAdded(address indexed account); 
event FarmerRemoved(address indexed account);

3. Define a struct farmers by inheriting from Roles library, struct Role.

Roles.Role private farmers;

4. The deploying address of the contract would be added as the first farmer. When using the constructor of a contract, you must perform the initial setup of the contract. Using the constructor will ensure initialization during contract creation.

constructor() public { _addFarmer(_msgSender()); }

5. Define a modifier to check the address that is calling the function has an appropriate role.

modifier onlyFarmer() { 

6. Create a function to check if the address is farmer or not it returns a boolean value.

function isFarmer(address account) 
returns (bool)
return farmers.has(account);

7. Now, let’s work on functions to add and remove the Farmer role.

The Farmer role smart contract is finished, have a look below:


Similarly, we create other roles DistributorRole.sol , RetailerRole.sol and ConsumerRole.sol and we repeat the same process as given in FarmerRole.sol just changing the name of functions and variables according to the role.

Now, let’s move to the main contract SupplyChain.sol.

This smart contract contains all the logic behind the supply chain. A farmer creates a product and put it on sale, a distributor buys it, sells it to a retailer and then the retailer sells it to consumers. The entire process is transparent and visible for everyone on the blockchain so that there are no chances of errors or fraud.

  1. Let’s begin by importing all the necessary contracts that we have created before and create a contract SupplyChain.
supply chain contract schema

2. To store the address of the currently connected wallet. Please save the wallet address after.

address owner;

3. The product code generated by a farmer that goes on the product to be verified by the distributor. The productCode stores that value.

uint256 productCode;

4. To keep a record of the number of stock units which is created by Farmer. The stockUnit stores that value.

uint256 stockUnit;

5. Create a mapping items that map the product code to an Item that will be created in struct later.

mapping(uint256 => Item) items;

6. Define a public mapping itemsHistory that maps the product code to an array of TxHash, which tracks its journey through the supply chain — to be sent from the application.

mapping(uint256 => Txblocks) itemsHistory;

7. Now create a state to track the process of the product with the following values and set the default state to produce by a farmer.

state for supply chain process

6. Create a struct to store all product details that are required.

item structure

7. Create a struct to store the address of blocks for transactions between farmer-distributor, distributor-retailer, and retailer-consumer.

struct Txblocks { 
uint256 FTD; // block of farmerToDistributor
uint256 DTR; // block of DistributorToRetailer
uint256 RTC; // block of RetailerToConsumer

8. Now create events for all functions which will be emitted when the job is done successfully.

events for supply chain

9. Create a modifier that checks to see if the address is the owner of the contract.

modifier only_Owner() {
require(_msgSender() == owner);

10. Define a modifier that verifies the Caller, and after that, define another modifier that checks if the paid amount is sufficient to cover the price.

11. Now, we define modifiers to check if product code exists and update the current status of the product.

12. Create a constructor to set up owner, stock unit, and product code.

constructor() public payable {        
owner = _msgSender();
stockUnit = 1;
productCode = 1;

13. Now, create a function that allows you to convert an address into a payable address.

function _make_payable(address x) internal pure 
returns (address payable) {
return payable(address(uint160(x)));

14. Create a kill function for the owner that sends all remaining Ether stored in the contract to the owner’s address.

function kill() public { 
if (_msgSender() == owner) {
address payable ownerAddressPayable = _make_payable(owner);

15. Now, let’s create a function that allows Farmer to create a product.

create product

16. Let’s create a function that will allow the farmer to sell an item that you create and another function that will allow distributors to buy products from the farmers.

function to sell prod to distributor

17. When the distributor buys the product, the farmer will need to ship it, and the distributor will need to receive it. We can enable them to do so by creating functions for shipping and receiving.

18. The product that the Distributor lists now have to be bought by the Retailer. After the Retailer purchases it, the Distributor ships the product to the Retailer, who then needs to receive it. For this, three functions need to be created: one for the Retailer to purchase the product, one to allow the Distributor to ship the product, and one for the Retailer to receive the product:

functions for processing, packaging, and list for sale

19. The product that the Distributor has listed now be purchased by the Retailer. After the Retailer purchases it, the Distributor will ship the product, and the Retailer will receive it. For this to happen, three functions need to be created: one for the Retailer to purchase the product, one for the Distributor to ship the product, and one for the Retailer to receive the product:

20. Now, we have two steps left. The first step is to place the product on a retailer’s shelf and let it sit until a consumer comes along. The second step is to have consumers purchase that product. Let’s look at how to do it:

21. Now it’s time to create functions to fetch products: we create three functions fetchItemBufferOne , fetchItemBufferTwo , and fetchitemHistory .

fetchItemBufferOne: this function gets only product creation information that is generated at the time of creation.

fetchItemBufferTwo: gets product information including addresses of all participants

fetchitemHistory: gets block data of process involving farmer to distributor, distributor to retailer, retailer to consumer

Let’s have a look at these functions:

function to get data

Finally, our smart contract for the supply chain is completed, you can have a look at the final contract here.

Now we test, compile, and deploy the contract on the polygon network. I have already included a test file that you can use for testing. You also have to check the configuration in truffle-config.js .

To deploy the contracts, run the following:

truffle develop$truffle(develop)> test
$truffle(develop)> deploy

Congratulations🤩! Your contract has been successfully deployed.

News Credit

%d bloggers like this: