// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract PureBi_Jun11 { // --- 0. BRANDING --- string public constant name = "PureBilingual Versation June 11, 2026 Research Registry"; string public constant symbol = "VERSATION"; // --- 1. IDENTITY & REPUTATION (PER DIALECT) --- mapping(address => bool) public isRegistered; // user -> dialect -> xp mapping(address => mapping(string => uint256)) public userXP; // user -> dialect -> role mapping(address => mapping(string => string)) public userRole; // Global admin checking mapping(address => bool) public isAdmin; address public labAdmin; // The Gasless Relayer (Python Backend) // --- 2. REGISTRY & LICENSING --- 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; // Consensus Tracking (entry_id -> user -> bool) mapping(uint256 => mapping(address => bool)) public hasVoted; // --- 3. BACKUP SYSTEM --- struct DataSnapshot { uint256 timestamp; string dataType; string ipfsCid; string description; } mapping(string => DataSnapshot[]) public backups; // --- 4. EVENTS --- 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; // The Python Backend Wallet isAdmin[msg.sender] = true; } // --- MODIFIERS --- modifier onlyRelayer() { require(msg.sender == labAdmin, "Only the Python Relayer can execute transactions"); _; } // --- CORE FUNCTIONS --- 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; } // Auto-register brand new React users as Initiates in this dialect if they have no role 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); // +10 XP for doing the audit if (isGod) { _applyVerification(_id); } else { entry.verificationCount++; if (entry.verificationCount >= 3) { _applyVerification(_id); } } } function _applyVerification(uint256 _id) internal { entries[_id].isVerified = true; _mintReputation(entries[_id].contributor, entries[_id].dialect, 50); // +50 XP to the original submitter 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); // +10 XP for doing the audit 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; // Burn -10 XP from the contributor 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); } // --- INTERNAL REPUTATION & RANKING LOGIC --- 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); } } // --- HELPER QUERIES --- 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]; } }