// SPDX-License-Identifier: MIT pragma solidity ^0.8.21; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/security/Pausable.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/math/SafeMath.sol"; contract NAVIS is Ownable, Pausable { using SafeMath for uint256; IERC20 public token; enum ShipmentStatus { Created, Accepted, InTransit, Delivered } struct Shipment { uint trackingNumber; ShipmentStatus status; address dispatcher; address courier; address packageReceiver; string paymentForm; string destination; string uniquePasscode; string wazeRoute; string[] alternativeRoutes; uint expirationTimestamp; uint256 reward; uint weightInPounds; bool isMultiPackage; } mapping(uint => Shipment) public shipments; uint public totalShipments; mapping(address => string) public courierProfilePictures; mapping(address => string) public dispatcherProfilePictures; event ShipmentCreated(uint indexed trackingNumber, address indexed dispatcher, uint256 reward); event ShipmentAccepted(uint indexed trackingNumber, address indexed courier); event ShipmentInTransit(uint indexed trackingNumber); event ShipmentDelivered(uint indexed trackingNumber, address indexed packageReceiver, uint256 reward); modifier onlyDispatcher() { require(msg.sender == owner(), "Only dispatcher can perform this action"); _; } modifier onlyCourier(uint _trackingNumber) { require(shipments[_trackingNumber].courier == msg.sender, "Only courier can perform this action"); _; } modifier onlyPackageReceiver(uint _trackingNumber) { require(shipments[_trackingNumber].packageReceiver == msg.sender, "Only package receiver can perform this action"); _; } modifier onlyBeforeExpiration(uint _trackingNumber) { require(block.timestamp < shipments[_trackingNumber].expirationTimestamp, "Shipment has expired"); _; } constructor(address _tokenAddress) { token = IERC20(_tokenAddress); } function createShipment( address _packageReceiver, string memory _paymentForm, string memory _destination, uint256 _reward, uint _weightInPounds, bool _isMultiPackage ) external onlyDispatcher { totalShipments++; shipments[totalShipments] = Shipment({ trackingNumber: totalShipments, status: ShipmentStatus.Created, dispatcher: msg.sender, courier: address(0), packageReceiver: _packageReceiver, paymentForm: _paymentForm, destination: _destination, uniquePasscode: "", wazeRoute: "", expirationTimestamp: 0, reward: _reward, weightInPounds: _weightInPounds, isMultiPackage: _isMultiPackage }); emit ShipmentCreated(totalShipments, msg.sender, _reward); } function acceptShipment(uint _trackingNumber) external onlyCourier(_trackingNumber) { Shipment storage shipment = shipments[_trackingNumber]; require(shipment.status == ShipmentStatus.Created, "Shipment must be in Created state"); shipment.status = ShipmentStatus.Accepted; shipment.courier = msg.sender; emit ShipmentAccepted(_trackingNumber, msg.sender); } function startShipment(uint _trackingNumber, string memory _uniquePasscode, string memory _wazeRoute, string[] memory _alternativeRoutes) external onlyCourier(_trackingNumber) onlyBeforeExpiration(_trackingNumber) { Shipment storage shipment = shipments[_trackingNumber]; require(shipment.status == ShipmentStatus.Accepted, "Shipment must be in Accepted state"); shipment.status = ShipmentStatus.InTransit; shipment.uniquePasscode = _uniquePasscode; shipment.wazeRoute = _wazeRoute; shipment.alternativeRoutes = _alternativeRoutes; emit ShipmentInTransit(_trackingNumber); } function completeShipment(uint _trackingNumber) external onlyCourier(_trackingNumber) onlyBeforeExpiration(_trackingNumber) { Shipment storage shipment = shipments[_trackingNumber]; require(shipment.status == ShipmentStatus.InTransit, "Shipment must be in InTransit state"); shipment.status = ShipmentStatus.Delivered; // Distribute rewards to dispatcher, courier, and package receiver distributeRewards(shipment.dispatcher, shipment.courier, shipment.packageReceiver, shipment.reward); emit ShipmentDelivered(_trackingNumber, shipment.packageReceiver, shipment.reward); } function distributeRewards(address _dispatcher, address _courier, address _packageReceiver, uint256 _reward) internal { uint256 dispatcherReward = _reward.mul(60).div(100); // 60% for dispatcher uint256 courierReward = _reward.mul(30).div(100); // 30% for courier uint256 packageReceiverReward = _reward.mul(10).div(100); // 10% for package receiver // Distribute rewards token.transfer(_dispatcher, dispatcherReward); token.transfer(_courier, courierReward); token.transfer(_packageReceiver, packageReceiverReward); } // Function to get user's NAVIS token balance function getUserNAVISBalance(address user) external view returns (uint256) { return token.balanceOf(user); } // Function to set courier profile picture function setCourierProfilePicture(string memory _profilePicture) external { courierProfilePictures[msg.sender] = _profilePicture; } // Function to set dispatcher profile picture function setDispatcherProfilePicture(string memory _profilePicture) external onlyDispatcher { dispatcherProfilePictures[msg.sender] = _profilePicture; } // Function to get user profile picture function getUserProfilePicture(address user) external view returns (string memory) { if (user == owner()) { // Dispatcher profile picture return dispatcherProfilePictures[user]; } else if (courierProfilePictures[user] != "") { // Courier profile picture return courierProfilePictures[user]; } else { // Guest profile picture (random color or image URL) // Replace this with actual logic to generate or retrieve a random color or image URL return "https://www.gigacalculator.com/randomizers/random-color-generator.php/NAVISAppLogo.pdf"; } } // Pause and unpause functions for emergency scenarios function pauseContract() external onlyOwner { _pause(); } function unpauseContract() external onlyOwner { _unpause(); } }