Product Lifecycle Tracking

What it does:
Tracks a product from raw materials to manufacturing, distribution, retail, and end-of-life, ensuring full transparency and traceability on-chain.

Why it matters:
Prevents counterfeiting, improves supply chain accountability, enables recalls or warranty claims, and provides consumers with verified product history.

How it works:

  • Manufacturers and suppliers register raw materials, production batches, and product IDs on-chain

  • Each lifecycle stage is logged with timestamp, location, and responsible party

  • Smart contracts maintain immutable records of transfers, ownership changes, and processing steps

  • Integrates with IoT sensors for automated condition reporting (e.g., temperature, humidity)

  • Consumers and regulators can verify product origin and lifecycle history

  • Enables integration with Product Authenticity Verification, Supply Chain Traceability, and Smart Warranty Contracts

  • Dashboards visualize product journey, custody, and condition events

      // SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/access/Ownable.sol";

/**
 * @title ProductLifecycleTracking
 * @author Nam
 * @notice Tracks product lifecycle stages and ownership on-chain
 */
contract ProductLifecycleTracking is Ownable {

    struct Stage {
        string description;
        uint256 timestamp;
        address operator;
    }

    struct Product {
        string productName;
        address currentOwner;
        Stage[] stages;
        bool exists;
    }

    mapping(uint256 => Product) public products;
    uint256 public productCount;

    mapping(address => bool) public authorizedOperators;

    // -------------------- EVENTS --------------------

    event ProductRegistered(uint256 indexed productId, string productName, address owner);
    event StageLogged(uint256 indexed productId, string description, address operator);
    event OwnershipTransferred(uint256 indexed productId, address from, address to);
    event OperatorApproved(address operator);

    // -------------------- OPERATOR MANAGEMENT --------------------

    function approveOperator(address _operator) external onlyOwner {
        authorizedOperators[_operator] = true;
        emit OperatorApproved(_operator);
    }

    function revokeOperator(address _operator) external onlyOwner {
        authorizedOperators[_operator] = false;
    }

    modifier onlyOperator() {
        require(authorizedOperators[msg.sender], "Not an authorized operator");
        _;
    }

    // -------------------- PRODUCT REGISTRATION --------------------

    function registerProduct(string calldata _productName, address _owner) external onlyOperator {
        require(_owner != address(0), "Invalid owner");

        productCount += 1;
        Product storage p = products[productCount];
        p.productName = _productName;
        p.currentOwner = _owner;
        p.exists = true;

        emit ProductRegistered(productCount, _productName, _owner);
    }

    // -------------------- LIFECYCLE STAGE LOGGING --------------------

    function logStage(uint256 _productId, string calldata _description) external onlyOperator {
        Product storage p = products[_productId];
        require(p.exists, "Product does not exist");

        p.stages.push(Stage({
            description: _description,
            timestamp: block.timestamp,
            operator: msg.sender
        }));

        emit StageLogged(_productId, _description, msg.sender);
    }

    // -------------------- OWNERSHIP MANAGEMENT --------------------

    function transferOwnership(uint256 _productId, address _newOwner) external onlyOperator {
        Product storage p = products[_productId];
        require(p.exists, "Product does not exist");
        require(_newOwner != address(0), "Invalid new owner");

        address oldOwner = p.currentOwner;
        p.currentOwner = _newOwner;

        emit OwnershipTransferred(_productId, oldOwner, _newOwner);
    }

    // -------------------- VIEW FUNCTIONS --------------------

    function getStages(uint256 _productId) external view returns (Stage[] memory) {
        return products[_productId].stages;
    }

    function getCurrentOwner(uint256 _productId) external view returns (address) {
        return products[_productId].currentOwner;
    }
}