Untitled

 avatar
unknown
plain_text
a year ago
9.2 kB
4
Indexable
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract ProductSupplyChain {
    address public admin;

    struct Product {
        string productId; // Using string for product ID
        string productName;
        string manufacturerId; // Using string for manufacturer ID
        string details; // Compressed details
        uint256 quantity;
        uint256 quality;
        bool isConfirmed;
        bool isSold;
        bool isReturned;
        string scratchCode; // Store the scratch code
    }

    mapping(string => Product) public products; // Using string keys
    mapping(address => bool) public manufacturers;
    mapping(address => bool) public retailers;
    mapping(address => bool) public wholesalers;

    event ProductProduced(string indexed productId, string productName, string manufacturerId, string details);
    event ProductVerified(string indexed productId, uint256 quantity, uint256 quality, bool isConfirmed);
    event ProductSold(string indexed productId, address indexed buyer);
    event ProductReturned(string indexed productId);
    event ProductAuthenticity(string indexed productId, bool isAuthentic);
    event ScratchCodeGenerated(string indexed productId, string scratchCode);

    constructor() {
        admin = msg.sender;
    }

    modifier onlyAdmin() {
        require(msg.sender == admin, "Only admin can perform this action");
        _;
    }

    modifier onlyManufacturer() {
        require(manufacturers[msg.sender], "Only manufacturers can perform this action");
        _;
    }

    modifier onlyWholesaler() {
        require(wholesalers[msg.sender], "Only wholesalers can perform this action");
        _;
    }

    modifier onlyRetailer() {
        require(retailers[msg.sender], "Only retailers can perform this action");
        _;
    }

    function produceProduct(
        string memory _productId,
        string memory _productName, 
        string memory _manufacturerId,
        string memory _manufactureDate,
        string memory _expirationDate,
        string memory _batchNumber
    ) public onlyManufacturer {
        require(bytes(products[_productId].productId).length == 0, "Product with the same productId already exists");

        string memory details = string(abi.encodePacked(_manufactureDate, "|", _expirationDate, "|", _batchNumber));
        
        products[_productId] = Product(_productId, _productName, _manufacturerId, details, 0, 0, false, false, false, "");
        emit ProductProduced(_productId, _productName, _manufacturerId, details);
    }

    function verifyProduct(string memory _productId, uint256 _quantity, uint256 _quality) public onlyManufacturer {
        require(bytes(products[_productId].productId).length != 0, "Product with the given productId does not exist");

        Product storage product = products[_productId];
        product.quantity = _quantity;
        product.quality = _quality;
        product.isConfirmed = true; // Confirming the product on-chain

        // Generate a random scratch code
        product.scratchCode = generateRandomScratchCode(_productId);

        emit ProductVerified(_productId, _quantity, _quality, true);
        emit ScratchCodeGenerated(_productId, product.scratchCode);
    }

    function sellProduct(string memory _productId) public {
        require(bytes(products[_productId].productId).length != 0, "Product with the given productId does not exist");
        require(products[_productId].isConfirmed, "Product has not been confirmed by the manufacturer");
        require(!products[_productId].isSold, "Product has already been sold");

        if (manufacturers[msg.sender]) {
            // Manufacturer selling to wholesaler or retailer
            products[_productId].isSold = true;
        } else if (wholesalers[msg.sender] || retailers[msg.sender]) {
            // Wholesaler or retailer selling to customer or another wholesaler/retailer
            products[_productId].isSold = true;
        } else {
            revert("Only manufacturers, wholesalers, or retailers can sell products");
        }

        emit ProductSold(_productId, msg.sender);
    }

    function buyProduct(string memory _productId) public {
        require(bytes(products[_productId].productId).length != 0, "Product with the given productId does not exist");
        require(products[_productId].isSold, "Product has not been sold yet");

        products[_productId].isSold = true;

        emit ProductSold(_productId, msg.sender);
    }

    function returnProduct(string memory _productId) public {
        require(bytes(products[_productId].productId).length != 0, "Product with the given productId does not exist");
        require(products[_productId].isSold, "Product has not been sold yet");

        products[_productId].isSold = false;
        products[_productId].isReturned = true;

        emit ProductReturned(_productId);
    }

    function checkAuthenticity(string memory _productId) public view returns (bool, bool) {
        if (bytes(products[_productId].productId).length == 0) {
            return (false, false);
        }
        return (products[_productId].isConfirmed, true);
    }

    function addManufacturer(address _manufacturer) public onlyAdmin {
        require(!manufacturers[_manufacturer], "Manufacturer already exists");
        manufacturers[_manufacturer] = true;
    }

    function addRetailer(address _retailer) public onlyAdmin {
        require(!retailers[_retailer], "Retailer already exists");
        retailers[_retailer] = true;
    }

    function addWholesaler(address _wholesaler) public onlyAdmin {
        require(!wholesalers[_wholesaler], "Wholesaler already exists");
        wholesalers[_wholesaler] = true;
    }

    function getScratchCode(string memory _productId) public view returns (string memory) {
        require(bytes(products[_productId].productId).length != 0, "Product with the given productId does not exist");
        require(products[_productId].isSold, "Product has not been sold yet");
        return products[_productId].scratchCode;
    }

    function isReturned(string memory _productId) public view returns (bool) {
        require(bytes(products[_productId].productId).length != 0, "Product with the given productId does not exist");
        return products[_productId].isReturned;
    }

    function decodeDetails(string memory details) public pure returns (string memory, string memory, string memory) {
        bytes memory detailsBytes = bytes(details);
        uint delimiterCount = 0;
        uint lastDelimiterIndex = 0;
        uint[] memory delimiterIndices = new uint[](2);
        for (uint i = 0; i < detailsBytes.length; i++) {
            if (detailsBytes[i] == '|') {
                delimiterIndices[delimiterCount] = i;
                delimiterCount++;
                lastDelimiterIndex = i;
            }
        }

        require(delimiterCount == 2, "Invalid details format");

        string memory manufactureDate = new string(delimiterIndices[0]);
        string memory expirationDate = new string(delimiterIndices[1] - delimiterIndices[0] - 1);
        string memory batchNumber = new string(detailsBytes.length - lastDelimiterIndex - 1);

        for (uint i = 0; i < delimiterIndices[0]; i++) {
            bytes(manufactureDate)[i] = detailsBytes[i];
        }

        for (uint i = delimiterIndices[0] + 1; i < delimiterIndices[1]; i++) {
            bytes(expirationDate)[i - delimiterIndices[0] - 1] = detailsBytes[i];
        }

        for (uint i = delimiterIndices[1] + 1; i < detailsBytes.length; i++) {
            bytes(batchNumber)[i - delimiterIndices[1] - 1] = detailsBytes[i];
        }

        return (manufactureDate, expirationDate, batchNumber);
    }

    function generateRandomScratchCode(string memory _productId) internal view returns (string memory) {
        bytes32 randomHash = keccak256(abi.encodePacked(block.timestamp, _productId, block.difficulty));
        return toString(randomHash);
    }

    function toString(bytes32 _bytes32) internal pure returns (string memory) {
        bytes memory bytesArray = new bytes(64);
        for (uint256 i = 0; i < 32; i++) {
            bytesArray[i*2] = _toHexChar(uint8(_bytes32[i]) / 16);
            bytesArray[i*2 + 1] = _toHexChar(uint8(_bytes32[i]) % 16);
        }
        return string(bytesArray);
    }

    function _toHexChar(uint8 _char) internal pure returns (bytes1) {
        if (_char < 10) {
            return bytes1(_char + 0x30);
        } else {
            return bytes1(_char + 0x57);
        }
    }

    function verifyScratchCode(string memory _productId, string memory _scratchCode) public view returns (bool) {
        require(bytes(products[_productId].productId).length != 0, "Product with the given productId does not exist");
        Product memory product = products[_productId];
        return keccak256(abi.encodePacked(product.scratchCode)) == keccak256(abi.encodePacked(_scratchCode));
    }
}

      
Editor is loading...
Leave a Comment