Supply Chain Traceability Contract
What it does:
Tracks products and materials throughout the supply chain, recording each event, transfer, or checkpoint on-chain for full traceability.
Why it matters:
Enhances transparency, prevents fraud, ensures compliance with regulations, and allows all stakeholders to verify the origin and journey of products.
How it works:
-
Suppliers, manufacturers, and distributors register on-chain
-
Each product or batch is assigned a unique ID or NFT
-
Events such as shipment, receipt, processing, or inspection are logged on-chain
-
Ownership and custody transfers are recorded immutably
-
Smart contracts can trigger alerts for delays, deviations, or unauthorized handling
-
Public dashboards allow verification of supply chain integrity
-
Integrates with marketplaces, compliance audits, and product authenticity contracts
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/access/Ownable.sol";
/**
* @title SupplyChainTraceability
* @author Nam
* @notice Tracks product batches and supply chain events on-chain
*/
contract SupplyChainTraceability is Ownable {
struct Batch {
string metadataURI; // batch details
address currentOwner;
uint256 creationTimestamp;
bool exists;
}
struct Event {
uint256 timestamp;
string description;
address performedBy;
}
mapping(uint256 => Batch) public batches;
mapping(uint256 => Event[]) public batchEvents;
uint256 public batchCount;
mapping(address => bool) public registeredParticipants;
// -------------------- EVENTS --------------------
event ParticipantRegistered(address indexed participant);
event BatchCreated(uint256 indexed batchId, address owner);
event EventLogged(uint256 indexed batchId, string description, address indexed performer);
event BatchTransferred(uint256 indexed batchId, address from, address to);
// -------------------- PARTICIPANT MANAGEMENT --------------------
function registerParticipant(address _participant) external onlyOwner {
registeredParticipants[_participant] = true;
emit ParticipantRegistered(_participant);
}
function isParticipant(address _participant) public view returns (bool) {
return registeredParticipants[_participant];
}
// -------------------- BATCH MANAGEMENT --------------------
function createBatch(string calldata _metadataURI) external {
require(isParticipant(msg.sender), "Not a registered participant");
batchCount += 1;
batches[batchCount] = Batch({
metadataURI: _metadataURI,
currentOwner: msg.sender,
creationTimestamp: block.timestamp,
exists: true
});
emit BatchCreated(batchCount, msg.sender);
}
function logEvent(uint256 _batchId, string calldata _description) external {
require(isParticipant(msg.sender), "Not a registered participant");
require(batches[_batchId].exists, "Batch does not exist");
batchEvents[_batchId].push(Event({
timestamp: block.timestamp,
description: _description,
performedBy: msg.sender
}));
emit EventLogged(_batchId, _description, msg.sender);
}
function transferBatch(uint256 _batchId, address _to) external {
require(isParticipant(msg.sender), "Not a registered participant");
require(batches[_batchId].currentOwner == msg.sender, "Not batch owner");
require(isParticipant(_to), "Recipient not registered");
address from = batches[_batchId].currentOwner;
batches[_batchId].currentOwner = _to;
emit BatchTransferred(_batchId, from, _to);
}
// -------------------- VIEW FUNCTIONS --------------------
function getBatchEvents(uint256 _batchId) external view returns (Event[] memory) {
return batchEvents[_batchId];
}
function getBatchOwner(uint256 _batchId) external view returns (address) {
return batches[_batchId].currentOwner;
}
}