Spaces:
Running
Running
File size: 7,708 Bytes
963d10b | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 | // 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];
}
}
|