Content Moderation DAO
What it does:
Enables decentralized, community-driven moderation of published content through on-chain proposals, voting, and enforcement actions.
Why it matters:
Replaces opaque, centralized moderation with transparent rules, reduces arbitrary censorship, aligns platform standards with community values, and creates accountable moderation decisions.
How it works:
-
Content is flagged by users for review (spam, abuse, plagiarism, misinformation)
-
Moderators or token holders stake tokens to participate in moderation votes
-
Each moderation case becomes an on-chain proposal
-
Community members vote to keep, restrict, label, or remove content
-
Decisions are executed automatically by smart contracts
-
Staked tokens can be slashed for malicious or bad-faith moderation
-
All flags, votes, and outcomes are publicly auditable
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
/**
* @title ContentModerationDAO
* @author Nam
* @notice DAO-based content moderation with staking and voting
*/
contract ContentModerationDAO is Ownable {
IERC20 public moderationToken;
uint256 public votingDuration;
uint256 public minimumStake;
enum Decision {
None,
Keep,
Label,
Restrict,
Remove
}
struct Case {
uint256 contentId;
string reason;
uint256 startTime;
uint256 endTime;
uint256 votesKeep;
uint256 votesRemove;
bool executed;
Decision result;
}
uint256 public caseCount;
mapping(uint256 => Case) public cases;
mapping(uint256 => mapping(address => bool)) public hasVoted;
mapping(address => uint256) public stakedBalance;
// -------------------- EVENTS --------------------
event CaseOpened(uint256 indexed caseId, uint256 indexed contentId, string reason);
event VoteCast(uint256 indexed caseId, address indexed voter, Decision decision, uint256 weight);
event CaseExecuted(uint256 indexed caseId, Decision result);
// -------------------- CONSTRUCTOR --------------------
constructor(
address _moderationToken,
uint256 _votingDuration,
uint256 _minimumStake
) {
moderationToken = IERC20(_moderationToken);
votingDuration = _votingDuration;
minimumStake = _minimumStake;
}
// -------------------- STAKING --------------------
function stake(uint256 _amount) external {
require(_amount >= minimumStake, "Stake too low");
moderationToken.transferFrom(msg.sender, address(this), _amount);
stakedBalance[msg.sender] += _amount;
}
// -------------------- CASE MANAGEMENT --------------------
function openCase(uint256 _contentId, string calldata _reason) external {
caseCount++;
cases[caseCount] = Case({
contentId: _contentId,
reason: _reason,
startTime: block.timestamp,
endTime: block.timestamp + votingDuration,
votesKeep: 0,
votesRemove: 0,
executed: false,
result: Decision.None
});
emit CaseOpened(caseCount, _contentId, _reason);
}
// -------------------- VOTING --------------------
function vote(uint256 _caseId, Decision _decision) external {
Case storage c = cases[_caseId];
require(block.timestamp = minimumStake, "Insufficient stake");
uint256 weight = stakedBalance[msg.sender];
hasVoted[_caseId][msg.sender] = true;
if (_decision == Decision.Keep) {
c.votesKeep += weight;
} else if (_decision == Decision.Remove) {
c.votesRemove += weight;
}
emit VoteCast(_caseId, msg.sender, _decision, weight);
}
// -------------------- EXECUTION --------------------
function executeCase(uint256 _caseId) external {
Case storage c = cases[_caseId];
require(block.timestamp > c.endTime, "Voting active");
require(!c.executed, "Already executed");
c.executed = true;
if (c.votesRemove > c.votesKeep) {
c.result = Decision.Remove;
} else {
c.result = Decision.Keep;
}
emit CaseExecuted(_caseId, c.result);
}
// -------------------- ADMIN --------------------
function updateVotingDuration(uint256 _duration) external onlyOwner {
votingDuration = _duration;
}
function updateMinimumStake(uint256 _stake) external onlyOwner {
minimumStake = _stake;
}
}