Cold Chain Monitoring Contract
What it does:
Monitors temperature-sensitive products throughout the supply chain, recording events and deviations on-chain to ensure quality and compliance.
Why it matters:
Prevents spoilage, ensures regulatory compliance, increases consumer trust, and provides auditable data for perishable or sensitive goods like vaccines, food, or pharmaceuticals.
How it works:
-
Each batch/product is registered on-chain with metadata and temperature thresholds
-
IoT sensors or authorized participants log temperature, humidity, or other environmental data
-
Smart contracts validate data against predefined thresholds
-
Alerts are triggered for deviations or breaches in cold chain conditions
-
Ownership and custody changes are recorded immutably
-
Public dashboards provide real-time monitoring and historical audit trails
-
Integrates with Supply Chain Traceability, logistics, and regulatory reporting
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/access/Ownable.sol";
/**
* @title ColdChainMonitoring
* @author Nam
* @notice Tracks temperature-sensitive products and logs environmental data on-chain
*/
contract ColdChainMonitoring is Ownable {
struct Batch {
string metadataURI;
address currentOwner;
uint256 creationTimestamp;
uint256 minTemp; // minimum allowed temperature
uint256 maxTemp; // maximum allowed temperature
bool exists;
}
struct SensorEvent {
uint256 timestamp;
int256 temperature;
int256 humidity;
address reporter;
}
mapping(uint256 => Batch) public batches;
mapping(uint256 => SensorEvent[]) public batchEvents;
uint256 public batchCount;
mapping(address => bool) public authorizedReporters;
// -------------------- EVENTS --------------------
event BatchCreated(uint256 indexed batchId, address owner, uint256 minTemp, uint256 maxTemp);
event SensorEventLogged(uint256 indexed batchId, int256 temperature, int256 humidity, address reporter);
event BatchTransferred(uint256 indexed batchId, address from, address to);
event ReporterApproved(address reporter);
// -------------------- REPORTER MANAGEMENT --------------------
function approveReporter(address _reporter) external onlyOwner {
authorizedReporters[_reporter] = true;
emit ReporterApproved(_reporter);
}
function revokeReporter(address _reporter) external onlyOwner {
authorizedReporters[_reporter] = false;
}
// -------------------- BATCH MANAGEMENT --------------------
function createBatch(string calldata _metadataURI, uint256 _minTemp, uint256 _maxTemp) external {
require(_minTemp <= _maxTemp, "Invalid temperature range");
batchCount += 1;
batches[batchCount] = Batch({
metadataURI: _metadataURI,
currentOwner: msg.sender,
creationTimestamp: block.timestamp,
minTemp: _minTemp,
maxTemp: _maxTemp,
exists: true
});
emit BatchCreated(batchCount, msg.sender, _minTemp, _maxTemp);
}
function transferBatch(uint256 _batchId, address _to) external {
require(batches[_batchId].currentOwner == msg.sender, "Not batch owner");
require(_to != address(0), "Invalid recipient");
address from = batches[_batchId].currentOwner;
batches[_batchId].currentOwner = _to;
emit BatchTransferred(_batchId, from, _to);
}
// -------------------- SENSOR DATA LOGGING --------------------
function logSensorData(uint256 _batchId, int256 _temperature, int256 _humidity) external {
require(authorizedReporters[msg.sender], "Not an authorized reporter");
require(batches[_batchId].exists, "Batch does not exist");
batchEvents[_batchId].push(SensorEvent({
timestamp: block.timestamp,
temperature: _temperature,
humidity: _humidity,
reporter: msg.sender
}));
// Optional: emit alert if temperature out of bounds
if (_temperature int256(batches[_batchId].maxTemp)) {
// Could integrate with alert system
}
emit SensorEventLogged(_batchId, _temperature, _humidity, msg.sender);
}
// -------------------- VIEW FUNCTIONS --------------------
function getBatchEvents(uint256 _batchId) external view returns (SensorEvent[] memory) {
return batchEvents[_batchId];
}
function getBatchOwner(uint256 _batchId) external view returns (address) {
return batches[_batchId].currentOwner;
}
}