| |
| pragma solidity ^0.8.0; |
|
|
| contract PureBi_Jun13 { |
| |
| |
| string public constant name = "PureBilingual Versation June 13, 2026 Research Registry"; |
| string public constant symbol = "VERSATION"; |
|
|
| |
| mapping(address => bool) public isRegistered; |
| |
| mapping(address => mapping(string => uint256)) public userXP; |
| |
| mapping(address => mapping(string => string)) public userRole; |
| |
| mapping(address => bool) public isAdmin; |
| |
| address public labAdmin; |
|
|
| |
| struct DialectEntry { |
| uint256 id; |
| string phrase; |
| string dialect; |
| string ipfsCid; |
| address contributor; |
| string licenseType; |
| uint256 verificationCount; |
| uint256 rejectionCount; |
| bool isVerified; |
| bool isBurned; |
| } |
|
|
| uint256 public entryCounter; |
| mapping(uint256 => DialectEntry) public entries; |
|
|
| |
| mapping(uint256 => mapping(address => bool)) public hasVoted; |
|
|
| |
| struct DataSnapshot { |
| uint256 timestamp; |
| string dataType; |
| string ipfsCid; |
| string description; |
| } |
|
|
| mapping(string => DataSnapshot[]) public backups; |
|
|
| |
| event EntryProposed(uint256 indexed id, string phrase, address indexed proposer); |
| event EntryVerified(uint256 indexed id, address indexed verifier); |
| event EntryRejected(uint256 indexed id, address indexed rejector); |
| event EntryBurned(uint256 indexed id, address indexed contributor); |
| event ReputationMinted(address indexed researcher, string dialect, uint256 amount); |
| event RankUpgraded(address indexed researcher, string dialect, string newRole); |
| event BackupCreated(string indexed dataKey, string ipfsCid, uint256 timestamp); |
|
|
| constructor() { |
| labAdmin = msg.sender; |
| isAdmin[msg.sender] = true; |
| } |
|
|
| |
| modifier onlyRelayer() { |
| require(msg.sender == labAdmin, "Only the Python Relayer can execute transactions"); |
| _; |
| } |
|
|
| |
| function proposeEntry(string memory _phrase, string memory _dialect, string memory _ipfsCid, string memory _license, address _contributor) public onlyRelayer returns (uint256) { |
| entryCounter++; |
| |
| entries[entryCounter] = DialectEntry(entryCounter, _phrase, _dialect, _ipfsCid, _contributor, _license, 0, 0, false, false); |
| emit EntryProposed(entryCounter, _phrase, _contributor); |
| |
| if (!isRegistered[_contributor]) { |
| isRegistered[_contributor] = true; |
| } |
| |
| |
| if (bytes(userRole[_contributor][_dialect]).length == 0 && !isAdmin[_contributor]) { |
| userRole[_contributor][_dialect] = "Initiate"; |
| } |
| |
| return entryCounter; |
| } |
|
|
| function verifyEntry(uint256 _id, address _verifier) public onlyRelayer { |
| DialectEntry storage entry = entries[_id]; |
| require(!entry.isVerified && !entry.isBurned, "Entry already resolved"); |
| require(entry.contributor != _verifier, "Cannot verify own entry"); |
| require(!hasVoted[_id][_verifier], "User already voted"); |
|
|
| string memory role = userRole[_verifier][entry.dialect]; |
| bool isGod = isAdmin[_verifier]; |
| bool isOracle = keccak256(bytes(role)) == keccak256(bytes("Oracle")); |
|
|
| require(isGod || isOracle, "Only Admins or Oracles can verify"); |
|
|
| hasVoted[_id][_verifier] = true; |
| _mintReputation(_verifier, entry.dialect, 10); |
|
|
| if (isGod) { |
| _applyVerification(_id); |
| } else { |
| entry.verificationCount++; |
| if (entry.verificationCount >= 2) { |
| _applyVerification(_id); |
| } |
| } |
| } |
|
|
| function _applyVerification(uint256 _id) internal { |
| entries[_id].isVerified = true; |
| _mintReputation(entries[_id].contributor, entries[_id].dialect, 50); |
| emit EntryVerified(_id, msg.sender); |
| } |
|
|
| function rejectEntry(uint256 _id, address _rejector) public onlyRelayer { |
| DialectEntry storage entry = entries[_id]; |
| require(!entry.isVerified && !entry.isBurned, "Entry already resolved"); |
| require(entry.contributor != _rejector, "Cannot reject own entry"); |
| require(!hasVoted[_id][_rejector], "User already voted"); |
|
|
| string memory role = userRole[_rejector][entry.dialect]; |
| bool isGod = isAdmin[_rejector]; |
| bool isOracle = keccak256(bytes(role)) == keccak256(bytes("Oracle")); |
|
|
| require(isGod || isOracle, "Only Admins or Oracles can reject"); |
|
|
| hasVoted[_id][_rejector] = true; |
| _mintReputation(_rejector, entry.dialect, 10); |
|
|
| if (isGod) { |
| _applyBurn(_id); |
| } else { |
| entry.rejectionCount++; |
| if (entry.rejectionCount >= 3) { |
| _applyBurn(_id); |
| } |
| } |
| } |
|
|
| function _applyBurn(uint256 _id) internal { |
| entries[_id].isBurned = true; |
| address badActor = entries[_id].contributor; |
| string memory dialect = entries[_id].dialect; |
| |
| |
| if (userXP[badActor][dialect] >= 10) { |
| userXP[badActor][dialect] -= 10; |
| } else { |
| userXP[badActor][dialect] = 0; |
| } |
| |
| _updateRank(badActor, dialect); |
| emit EntryBurned(_id, badActor); |
| } |
|
|
| function createBackup(string memory _dataKey, string memory _dataType, string memory _ipfsCid, string memory _description) public onlyRelayer { |
| DataSnapshot memory newSnap = DataSnapshot(block.timestamp, _dataType, _ipfsCid, _description); |
| backups[_dataKey].push(newSnap); |
| emit BackupCreated(_dataKey, _ipfsCid, block.timestamp); |
| } |
|
|
| |
| function _mintReputation(address _user, string memory _dialect, uint256 _amount) internal { |
| userXP[_user][_dialect] += _amount; |
| emit ReputationMinted(_user, _dialect, _amount); |
| _updateRank(_user, _dialect); |
| } |
|
|
| function _updateRank(address _user, string memory _dialect) internal { |
| if (isAdmin[_user]) return; |
|
|
| uint256 score = userXP[_user][_dialect]; |
| string memory newRole; |
|
|
| if (score >= 1500) { newRole = "Oracle"; } |
| else if (score >= 800) { newRole = "Archivist"; } |
| else if (score >= 300) { newRole = "Linguist"; } |
| else if (score >= 100) { newRole = "Scout"; } |
| else { newRole = "Initiate"; } |
|
|
| if (keccak256(abi.encodePacked(userRole[_user][_dialect])) != keccak256(abi.encodePacked(newRole))) { |
| userRole[_user][_dialect] = newRole; |
| emit RankUpgraded(_user, _dialect, newRole); |
| } |
| } |
|
|
| |
| function batchMigrateXP(address[] memory _users, string[] memory _dialects, uint256[] memory _xps) public onlyRelayer { |
| require(_users.length == _dialects.length && _users.length == _xps.length, "Array lengths mismatch"); |
| for(uint i=0; i<_users.length; i++){ |
| userXP[_users[i]][_dialects[i]] += _xps[i]; |
| _updateRank(_users[i], _dialects[i]); |
| emit ReputationMinted(_users[i], _dialects[i], _xps[i]); |
| } |
| } |
|
|
| |
| function getRole(address _user, string memory _dialect) public view returns (string memory) { |
| if (isAdmin[_user]) return "Admin"; |
| if (bytes(userRole[_user][_dialect]).length == 0) return "Initiate"; |
| return userRole[_user][_dialect]; |
| } |
|
|
| function getXP(address _user, string memory _dialect) public view returns (uint256) { |
| return userXP[_user][_dialect]; |
| } |
| } |
|
|