// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import "@openzeppelin/contracts/access/AccessControl.sol"; /** * @title BridgeVaultExtension * @notice Extension to vault for tracking bridge operations * @dev Can be attached to existing vault contracts */ contract BridgeVaultExtension is AccessControl { bytes32 public constant BRIDGE_OPERATOR_ROLE = keccak256("BRIDGE_OPERATOR_ROLE"); enum BridgeStatus { Initiated, Confirmed, Completed, Failed } struct BridgeRecord { bytes32 messageId; address token; uint256 amount; uint64 destinationChain; address recipient; uint256 timestamp; BridgeStatus status; } // Storage mapping(bytes32 => BridgeRecord) public bridgeRecords; bytes32[] public bridgeHistory; mapping(address => bytes32[]) public userBridgeHistory; event BridgeRecorded( bytes32 indexed messageId, address indexed token, uint256 amount, uint64 destinationChain ); event BridgeStatusUpdated( bytes32 indexed messageId, BridgeStatus status ); constructor(address admin) { _grantRole(DEFAULT_ADMIN_ROLE, admin); _grantRole(BRIDGE_OPERATOR_ROLE, admin); } /** * @notice Record bridge operation */ function recordBridgeOperation( bytes32 messageId, address token, uint256 amount, uint64 destinationChain, address recipient ) external onlyRole(BRIDGE_OPERATOR_ROLE) { require(messageId != bytes32(0), "Invalid message ID"); require(bridgeRecords[messageId].timestamp == 0, "Already recorded"); bridgeRecords[messageId] = BridgeRecord({ messageId: messageId, token: token, amount: amount, destinationChain: destinationChain, recipient: recipient, timestamp: block.timestamp, status: BridgeStatus.Initiated }); bridgeHistory.push(messageId); userBridgeHistory[recipient].push(messageId); emit BridgeRecorded(messageId, token, amount, destinationChain); } /** * @notice Update bridge status */ function updateBridgeStatus( bytes32 messageId, BridgeStatus status ) external onlyRole(BRIDGE_OPERATOR_ROLE) { require(bridgeRecords[messageId].timestamp > 0, "Not found"); bridgeRecords[messageId].status = status; emit BridgeStatusUpdated(messageId, status); } // View functions function getBridgeRecord(bytes32 messageId) external view returns (BridgeRecord memory) { return bridgeRecords[messageId]; } function getBridgeHistory(address user) external view returns (BridgeRecord[] memory) { bytes32[] memory userHistory = userBridgeHistory[user]; BridgeRecord[] memory records = new BridgeRecord[](userHistory.length); for (uint256 i = 0; i < userHistory.length; i++) { records[i] = bridgeRecords[userHistory[i]]; } return records; } function getAllBridgeHistory() external view returns (BridgeRecord[] memory) { BridgeRecord[] memory records = new BridgeRecord[](bridgeHistory.length); for (uint256 i = 0; i < bridgeHistory.length; i++) { records[i] = bridgeRecords[bridgeHistory[i]]; } return records; } function getBridgeCount() external view returns (uint256) { return bridgeHistory.length; } function getUserBridgeCount(address user) external view returns (uint256) { return userBridgeHistory[user].length; } }