|
| 1 | +// SPDX-License-Identifier: MIT |
| 2 | +pragma solidity ^0.8.19; |
| 3 | + |
| 4 | +contract SmartContractRegistry { |
| 5 | + struct Contract { |
| 6 | + string name; |
| 7 | + string version; |
| 8 | + address owner; |
| 9 | + string sourceCode; |
| 10 | + string documentation; |
| 11 | + bool isVerified; |
| 12 | + uint256 creationTime; |
| 13 | + ContractStatus status; |
| 14 | + address[] authorizedUsers; |
| 15 | + uint256 lastUpdateTime; |
| 16 | + } |
| 17 | + |
| 18 | + struct ContractVersion { |
| 19 | + string version; |
| 20 | + string sourceCode; |
| 21 | + uint256 deploymentTime; |
| 22 | + string changeLog; |
| 23 | + } |
| 24 | + |
| 25 | + enum ContractStatus { |
| 26 | + Draft, |
| 27 | + UnderReview, |
| 28 | + Active, |
| 29 | + Deprecated, |
| 30 | + Paused |
| 31 | + } |
| 32 | + |
| 33 | + // State variables |
| 34 | + mapping(bytes32 => Contract) public contracts; |
| 35 | + mapping(bytes32 => ContractVersion[]) public contractVersions; |
| 36 | + mapping(address => bytes32[]) public userContracts; |
| 37 | + mapping(address => bool) public administrators; |
| 38 | + |
| 39 | + uint256 public totalContracts; |
| 40 | + address public owner; |
| 41 | + bool public paused; |
| 42 | + |
| 43 | + // Events |
| 44 | + event ContractCreated(bytes32 indexed contractId, string name, address owner); |
| 45 | + event ContractUpdated(bytes32 indexed contractId, string version); |
| 46 | + event ContractStatusChanged(bytes32 indexed contractId, ContractStatus newStatus); |
| 47 | + event UserAuthorized(bytes32 indexed contractId, address user); |
| 48 | + event UserRevoked(bytes32 indexed contractId, address user); |
| 49 | + event AdministratorAdded(address administrator); |
| 50 | + event AdministratorRemoved(address administrator); |
| 51 | + |
| 52 | + // Modifiers |
| 53 | + modifier onlyOwner() { |
| 54 | + require(msg.sender == owner, "Only owner can call this function"); |
| 55 | + _; |
| 56 | + } |
| 57 | + |
| 58 | + modifier onlyAdmin() { |
| 59 | + require(administrators[msg.sender], "Only administrators can call this function"); |
| 60 | + _; |
| 61 | + } |
| 62 | + |
| 63 | + modifier contractExists(bytes32 contractId) { |
| 64 | + require(contracts[contractId].owner != address(0), "Contract does not exist"); |
| 65 | + _; |
| 66 | + } |
| 67 | + |
| 68 | + modifier notPaused() { |
| 69 | + require(!paused, "Contract is paused"); |
| 70 | + _; |
| 71 | + } |
| 72 | + |
| 73 | + modifier onlyContractOwner(bytes32 contractId) { |
| 74 | + require(contracts[contractId].owner == msg.sender, "Only contract owner can call this function"); |
| 75 | + _; |
| 76 | + } |
| 77 | + |
| 78 | + constructor() { |
| 79 | + owner = msg.sender; |
| 80 | + administrators[msg.sender] = true; |
| 81 | + paused = false; |
| 82 | + } |
| 83 | + |
| 84 | + // Core functions |
| 85 | + function createContract( |
| 86 | + string memory name, |
| 87 | + string memory version, |
| 88 | + string memory sourceCode, |
| 89 | + string memory documentation |
| 90 | + ) external notPaused returns (bytes32) { |
| 91 | + require(bytes(name).length > 0, "Name cannot be empty"); |
| 92 | + require(bytes(version).length > 0, "Version cannot be empty"); |
| 93 | + require(bytes(sourceCode).length > 0, "Source code cannot be empty"); |
| 94 | + |
| 95 | + bytes32 contractId = keccak256(abi.encodePacked(name, msg.sender, block.timestamp)); |
| 96 | + |
| 97 | + require(contracts[contractId].owner == address(0), "Contract ID already exists"); |
| 98 | + |
| 99 | + Contract storage newContract = contracts[contractId]; |
| 100 | + newContract.name = name; |
| 101 | + newContract.version = version; |
| 102 | + newContract.owner = msg.sender; |
| 103 | + newContract.sourceCode = sourceCode; |
| 104 | + newContract.documentation = documentation; |
| 105 | + newContract.isVerified = false; |
| 106 | + newContract.creationTime = block.timestamp; |
| 107 | + newContract.status = ContractStatus.Draft; |
| 108 | + newContract.lastUpdateTime = block.timestamp; |
| 109 | + |
| 110 | + // Add initial version |
| 111 | + ContractVersion memory initialVersion = ContractVersion({ |
| 112 | + version: version, |
| 113 | + sourceCode: sourceCode, |
| 114 | + deploymentTime: block.timestamp, |
| 115 | + changeLog: "Initial version" |
| 116 | + }); |
| 117 | + contractVersions[contractId].push(initialVersion); |
| 118 | + |
| 119 | + // Update user contracts |
| 120 | + userContracts[msg.sender].push(contractId); |
| 121 | + totalContracts++; |
| 122 | + |
| 123 | + emit ContractCreated(contractId, name, msg.sender); |
| 124 | + return contractId; |
| 125 | + } |
| 126 | + |
| 127 | + function updateContract( |
| 128 | + bytes32 contractId, |
| 129 | + string memory newVersion, |
| 130 | + string memory newSourceCode, |
| 131 | + string memory changeLog |
| 132 | + ) external contractExists(contractId) onlyContractOwner(contractId) notPaused { |
| 133 | + require(bytes(newVersion).length > 0, "Version cannot be empty"); |
| 134 | + require(bytes(newSourceCode).length > 0, "Source code cannot be empty"); |
| 135 | + |
| 136 | + Contract storage contractToUpdate = contracts[contractId]; |
| 137 | + contractToUpdate.version = newVersion; |
| 138 | + contractToUpdate.sourceCode = newSourceCode; |
| 139 | + contractToUpdate.lastUpdateTime = block.timestamp; |
| 140 | + contractToUpdate.isVerified = false; |
| 141 | + |
| 142 | + // Add new version |
| 143 | + ContractVersion memory newContractVersion = ContractVersion({ |
| 144 | + version: newVersion, |
| 145 | + sourceCode: newSourceCode, |
| 146 | + deploymentTime: block.timestamp, |
| 147 | + changeLog: changeLog |
| 148 | + }); |
| 149 | + contractVersions[contractId].push(newContractVersion); |
| 150 | + |
| 151 | + emit ContractUpdated(contractId, newVersion); |
| 152 | + } |
| 153 | + |
| 154 | + function changeContractStatus( |
| 155 | + bytes32 contractId, |
| 156 | + ContractStatus newStatus |
| 157 | + ) external contractExists(contractId) onlyAdmin { |
| 158 | + contracts[contractId].status = newStatus; |
| 159 | + emit ContractStatusChanged(contractId, newStatus); |
| 160 | + } |
| 161 | + |
| 162 | + function authorizeUser( |
| 163 | + bytes32 contractId, |
| 164 | + address user |
| 165 | + ) external contractExists(contractId) onlyContractOwner(contractId) { |
| 166 | + require(user != address(0), "Invalid user address"); |
| 167 | + require(!isUserAuthorized(contractId, user), "User already authorized"); |
| 168 | + |
| 169 | + contracts[contractId].authorizedUsers.push(user); |
| 170 | + emit UserAuthorized(contractId, user); |
| 171 | + } |
| 172 | + |
| 173 | + function revokeUser( |
| 174 | + bytes32 contractId, |
| 175 | + address user |
| 176 | + ) external contractExists(contractId) onlyContractOwner(contractId) { |
| 177 | + require(isUserAuthorized(contractId, user), "User not authorized"); |
| 178 | + |
| 179 | + Contract storage contractToUpdate = contracts[contractId]; |
| 180 | + for (uint i = 0; i < contractToUpdate.authorizedUsers.length; i++) { |
| 181 | + if (contractToUpdate.authorizedUsers[i] == user) { |
| 182 | + // Remove user by replacing with last element and popping |
| 183 | + contractToUpdate.authorizedUsers[i] = contractToUpdate.authorizedUsers[contractToUpdate.authorizedUsers.length - 1]; |
| 184 | + contractToUpdate.authorizedUsers.pop(); |
| 185 | + break; |
| 186 | + } |
| 187 | + } |
| 188 | + |
| 189 | + emit UserRevoked(contractId, user); |
| 190 | + } |
| 191 | + |
| 192 | + // Admin functions |
| 193 | + function addAdministrator(address newAdmin) external onlyOwner { |
| 194 | + require(newAdmin != address(0), "Invalid administrator address"); |
| 195 | + require(!administrators[newAdmin], "Already an administrator"); |
| 196 | + |
| 197 | + administrators[newAdmin] = true; |
| 198 | + emit AdministratorAdded(newAdmin); |
| 199 | + } |
| 200 | + |
| 201 | + function removeAdministrator(address admin) external onlyOwner { |
| 202 | + require(admin != owner, "Cannot remove contract owner"); |
| 203 | + require(administrators[admin], "Not an administrator"); |
| 204 | + |
| 205 | + administrators[admin] = false; |
| 206 | + emit AdministratorRemoved(admin); |
| 207 | + } |
| 208 | + |
| 209 | + function togglePause() external onlyOwner { |
| 210 | + paused = !paused; |
| 211 | + } |
| 212 | + |
| 213 | + // View functions |
| 214 | + function getContract(bytes32 contractId) external view returns ( |
| 215 | + string memory name, |
| 216 | + string memory version, |
| 217 | + address owner, |
| 218 | + bool isVerified, |
| 219 | + uint256 creationTime, |
| 220 | + ContractStatus status, |
| 221 | + uint256 lastUpdateTime |
| 222 | + ) { |
| 223 | + Contract storage contractData = contracts[contractId]; |
| 224 | + require(contractData.owner != address(0), "Contract does not exist"); |
| 225 | + |
| 226 | + return ( |
| 227 | + contractData.name, |
| 228 | + contractData.version, |
| 229 | + contractData.owner, |
| 230 | + contractData.isVerified, |
| 231 | + contractData.creationTime, |
| 232 | + contractData.status, |
| 233 | + contractData.lastUpdateTime |
| 234 | + ); |
| 235 | + } |
| 236 | + |
| 237 | + function getContractVersions(bytes32 contractId) external view returns (ContractVersion[] memory) { |
| 238 | + return contractVersions[contractId]; |
| 239 | + } |
| 240 | + |
| 241 | + function getUserContracts(address user) external view returns (bytes32[] memory) { |
| 242 | + return userContracts[user]; |
| 243 | + } |
| 244 | + |
| 245 | + function isUserAuthorized(bytes32 contractId, address user) public view returns (bool) { |
| 246 | + Contract storage contractData = contracts[contractId]; |
| 247 | + if (contractData.owner == user) return true; |
| 248 | + |
| 249 | + for (uint i = 0; i < contractData.authorizedUsers.length; i++) { |
| 250 | + if (contractData.authorizedUsers[i] == user) return true; |
| 251 | + } |
| 252 | + return false; |
| 253 | + } |
| 254 | + |
| 255 | + function getContractCount() external view returns (uint256) { |
| 256 | + return totalContracts; |
| 257 | + } |
| 258 | +} |
0 commit comments