// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import "./UniversalCCIPBridge.sol"; /** * @title CommodityCCIPBridge * @notice Specialized bridge for commodity-backed tokens (gold, oil, etc.) * @dev Includes certificate validation and physical delivery coordination */ contract CommodityCCIPBridge is UniversalCCIPBridge { // Certificate registry (authenticity verification) mapping(address => mapping(bytes32 => bool)) public validCertificates; mapping(address => mapping(bytes32 => CertificateInfo)) public certificates; struct CertificateInfo { bytes32 certificateHash; address custodian; uint256 quantity; string commodityType; uint256 issuedAt; uint256 expiresAt; bool isValid; } // Custodian registry mapping(address => address) public tokenCustodians; mapping(address => bool) public approvedCustodians; // Physical delivery tracking struct DeliveryRequest { bytes32 messageId; address token; uint256 amount; address requester; string deliveryAddress; uint256 requestedAt; DeliveryStatus status; } enum DeliveryStatus { Requested, Confirmed, InTransit, Delivered, Cancelled } mapping(bytes32 => DeliveryRequest) public deliveryRequests; bytes32[] public deliveryIds; event CertificateRegistered( address indexed token, bytes32 indexed certificateHash, address custodian ); event PhysicalDeliveryRequested( bytes32 indexed deliveryId, bytes32 indexed messageId, address token, uint256 amount ); event DeliveryStatusUpdated( bytes32 indexed deliveryId, DeliveryStatus status ); /** * @notice Bridge commodity token with certificate validation */ function bridgeCommodity( address token, uint256 amount, uint64 destinationChain, address recipient, bytes32 certificateHash, bytes calldata custodianSignature ) external nonReentrant returns (bytes32 messageId) { // Verify asset is Commodity type UniversalAssetRegistry.UniversalAsset memory asset = assetRegistry.getAsset(token); require(asset.assetType == UniversalAssetRegistry.AssetType.Commodity, "Not commodity"); require(asset.isActive, "Asset not active"); // Verify certificate require(validCertificates[token][certificateHash], "Invalid certificate"); CertificateInfo memory cert = certificates[token][certificateHash]; require(cert.isValid, "Certificate not valid"); require(block.timestamp < cert.expiresAt, "Certificate expired"); require(cert.quantity >= amount, "Certificate insufficient"); // Verify custodian require(approvedCustodians[cert.custodian], "Custodian not approved"); // Verify custodian signature _verifyCustodianSignature(token, amount, certificateHash, custodianSignature, cert.custodian); // Execute bridge BridgeOperation memory op = BridgeOperation({ token: token, amount: amount, destinationChain: destinationChain, recipient: recipient, assetType: bytes32(uint256(UniversalAssetRegistry.AssetType.Commodity)), usePMM: true, // Commodities can use PMM useVault: false, complianceProof: abi.encode(certificateHash), vaultInstructions: "" }); messageId = this.bridge(op); return messageId; } /** * @notice Initiate physical delivery of commodity */ function initiatePhysicalDelivery( bytes32 messageId, string calldata deliveryAddress ) external returns (bytes32 deliveryId) { require(messageId != bytes32(0), "Invalid message ID"); deliveryId = keccak256(abi.encode(messageId, msg.sender, block.timestamp)); // This would integrate with off-chain logistics systems deliveryRequests[deliveryId] = DeliveryRequest({ messageId: messageId, token: address(0), // Would be fetched from bridge record amount: 0, // Would be fetched from bridge record requester: msg.sender, deliveryAddress: deliveryAddress, requestedAt: block.timestamp, status: DeliveryStatus.Requested }); deliveryIds.push(deliveryId); emit PhysicalDeliveryRequested(deliveryId, messageId, address(0), 0); return deliveryId; } /** * @notice Update physical delivery status */ function updateDeliveryStatus( bytes32 deliveryId, DeliveryStatus status ) external onlyRole(BRIDGE_OPERATOR_ROLE) { require(deliveryRequests[deliveryId].requestedAt > 0, "Delivery not found"); deliveryRequests[deliveryId].status = status; emit DeliveryStatusUpdated(deliveryId, status); } /** * @notice Verify custodian signature */ function _verifyCustodianSignature( address /* token */, uint256 /* amount */, bytes32 certificateHash, bytes memory signature, address custodian ) internal view { // In production, this would use EIP-712 signature verification // For now, simplified check require(signature.length > 0, "Missing signature"); require(custodian != address(0), "Invalid custodian"); } // Admin functions function registerCertificate( address token, bytes32 certificateHash, address custodian, uint256 quantity, string calldata commodityType, uint256 expiresAt ) external onlyRole(BRIDGE_OPERATOR_ROLE) { validCertificates[token][certificateHash] = true; certificates[token][certificateHash] = CertificateInfo({ certificateHash: certificateHash, custodian: custodian, quantity: quantity, commodityType: commodityType, issuedAt: block.timestamp, expiresAt: expiresAt, isValid: true }); emit CertificateRegistered(token, certificateHash, custodian); } function approveCustodian(address custodian, bool approved) external onlyRole(DEFAULT_ADMIN_ROLE) { approvedCustodians[custodian] = approved; } function revokeCertificate(address token, bytes32 certificateHash) external onlyRole(BRIDGE_OPERATOR_ROLE) { validCertificates[token][certificateHash] = false; certificates[token][certificateHash].isValid = false; } }