Exploring the Supply Chain Smart Contract: An In-Depth Guide

Exploring the Supply Chain Smart Contract: An In-Depth Guide

The SupplyChain smart contract, written in Solidity, is designed to manage and track the different stages in an agricultural supply chain. From raw materials to consumer sales, this contract provides a transparent and verifiable system for handling the movement of crops. Below, we’ll break down each section and function to help you understand how this contract operates.


1. Overview and Roles in the Contract

This contract keeps track of the lifecycle of a crop through various roles, including:

  • Raw Material Supplier: Provides the initial materials.

  • Manufacturer: Follows specific guidelines to create products.

  • Distributor: Delivers products to retailers.

  • Retailer: Sells products to the end customer.

The Owner of the contract, defined at deployment, is the only one allowed to add these roles.

2. Setting Up the Owner and Basic Permissions

The Owner address is set in the constructor function, which is called when the contract is deployed. This ensures that only the contract deployer has exclusive permissions to assign roles.

address public Owner;

constructor() public {
    Owner = msg.sender;
}

The onlyByOwner modifier restricts certain functions to the contract’s owner:

modifier onlyByOwner() {
    require(msg.sender == Owner);
    _;
}

3. Supply Chain Stages Using Enum

The contract uses an enum called STAGE to represent different stages in the supply chain. These stages reflect the real-world flow of the crop from inception to sale.

enum STAGE {
    Init,
    RawMaterialSupply,
    Manufacture,
    Distribution,
    Retail,
    Sold
}

Each stage is sequential, allowing each crop to move through a logical path from the supplier to the consumer.


4. Crop Tracking and Storage

Each crop has a unique ID and other details like its name, description, and current stage. It’s stored within the CropStock mapping for quick access.

struct crop {
    uint256 id;
    string name;
    string description;
    uint256 RMSid;
    uint256 MANid;
    uint256 DISid;
    uint256 RETid;
    STAGE stage;
}

mapping(uint256 => crop) public CropStock;
  • ID – A unique identifier for each crop.

  • RMSid, MANid, DISid, RETid – IDs of the roles involved with the crop.

5. Viewing the Crop’s Current Stage

The showStage function returns the current stage of a crop based on its ID:

function showStage(uint256 _cropID) public view returns (string memory) {
    require(CropCtr > 0);
    if (CropStock[_cropID].stage == STAGE.Init)
        return "Crop Ordered";
    // Additional conditions follow for each stage
}

This is helpful for external applications to monitor the crop’s progress.


6. Adding Roles in the Supply Chain

The owner has exclusive access to add new entities to the supply chain. Here’s how it works for each role:

Adding a Raw Material Supplier

function addRMS(address _address, string memory _name, string memory _place) public onlyByOwner() {
    rmsCtr++;
    RMS[rmsCtr] = rawMaterialSupplier(_address, rmsCtr, _name, _place);
}

The addRMS function creates a new supplier with details like their address, name, and location. The rmsCtr counter keeps track of how many suppliers have been added.

Similarly, manufacturers, distributors, and retailers are added using addManufacturer, addDistributor, and addRetailer functions, respectively.


7. Tracking Crop Stages Through the Supply Chain

Each stage is handled by a different function, restricted to specific roles:

Supply Raw Materials

The RMSsupply function sets the supplier for the crop:

function RMSsupply(uint256 _cropID) public {
    require(_cropID > 0 && _cropID <= CropCtr);
    uint256 _id = findRMS(msg.sender);
    require(_id > 0);
    require(CropStock[_cropID].stage == STAGE.Init);
    CropStock[_cropID].RMSid = _id;
    CropStock[_cropID].stage = STAGE.RawMaterialSupply;
}

The findRMS helper function verifies the supplier’s ID:

function findRMS(address _address) private view returns (uint256) {
    require(rmsCtr > 0);
    for (uint256 i = 1; i <= rmsCtr; i++) {
        if (RMS[i].addr == _address) return RMS[i].id;
    }
    return 0;
}

Manufacturing Stage

The Manufacturing function allows the manufacturer to process the crop once raw materials are supplied.

function Manufacturing(uint256 _cropID) public {
    require(_cropID > 0 && _cropID <= CropCtr);
    uint256 _id = findMAN(msg.sender);
    require(_id > 0);
    require(CropStock[_cropID].stage == STAGE.RawMaterialSupply);
    CropStock[_cropID].MANid = _id;
    CropStock[_cropID].stage = STAGE.Manufacture;
}

Distribution Stage

The Distribute function transfers the crop to a distributor once manufacturing is complete.

function Distribute(uint256 _cropID) public {
    require(_cropID > 0 && _cropID <= CropCtr);
    uint256 _id = findDIS(msg.sender);
    require(_id > 0);
    require(CropStock[_cropID].stage == STAGE.Manufacture);
    CropStock[_cropID].DISid = _id;
    CropStock[_cropID].stage = STAGE.Distribution;
}

Retail Stage

The Retail function assigns the crop to a retailer after distribution.

function Retail(uint256 _cropID) public {
    require(_cropID > 0 && _cropID <= CropCtr);
    uint256 _id = findRET(msg.sender);
    require(_id > 0);
    require(CropStock[_cropID].stage == STAGE.Distribution);
    CropStock[_cropID].RETid = _id;
    CropStock[_cropID].stage = STAGE.Retail;
}

Marking as Sold

The sold function marks a crop as sold, ending its lifecycle in the supply chain.

function sold(uint256 _cropID) public {
    require(_cropID > 0 && _cropID <= CropCtr);
    uint256 _id = findRET(msg.sender);
    require(_id > 0);
    require(_id == CropStock[_cropID].RETid);
    require(CropStock[_cropID].stage == STAGE.Retail);
    CropStock[_cropID].stage = STAGE.sold;
}

Only the retailer associated with the crop can mark it as sold.


8. Adding New Crops

The owner can add new crops to the system using the addcrop function:

function addcrop(string memory _name, string memory _description) public onlyByOwner() {
    require((rmsCtr > 0) && (manCtr > 0) && (disCtr > 0) && (retCtr > 0));
    CropCtr++;
    CropStock[CropCtr] = crop(CropCtr, _name, _description, 0, 0, 0, 0, STAGE.Init);
}

This ensures all necessary roles are defined before crops can enter the supply chain.


9. Conclusion

The SupplyChain smart contract provides a transparent and verifiable way to manage and track the lifecycle of crops in the agricultural sector. Each stage is carefully regulated by role-based permissions, allowing only authorized entities to interact with the crops at different stages. This contract can be deployed to Ethereum or other EVM-compatible blockchains, providing a reliable and immutable tracking system for agricultural products from farm to consumer.

With features like stage tracking and role verification, this contract is a solid foundation for building a reliable and efficient supply chain solution on the blockchain.