// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/governance/Governor.sol";
import "@openzeppelin/contracts/governance/extensions/GovernorSettings.sol";
import "@openzeppelin/contracts/governance/extensions/GovernorCountingSimple.sol";
import "@openzeppelin/contracts/governance/extensions/GovernorVotes.sol";
import "@openzeppelin/contracts/governance/extensions/GovernorVotesQuorumFraction.sol";
import "@openzeppelin/contracts/governance/extensions/GovernorTimelockControl.sol";
/**
* @title QuadraticVotingGovernor
* @dev DAO yönetişim sözleşmesi - Quadratic Voting mekanizması ile
* @notice Bu sözleşme, token sahiplerinin öneriler üzerinde karesel oy kullanmasına izin verir
*/
contract QuadraticVotingGovernor is
Governor,
GovernorSettings,
GovernorCountingSimple,
GovernorVotes,
GovernorVotesQuorumFraction,
GovernorTimelockControl
{
// Sybil saldırılarına karşı kimlik doğrulama registry'si
IProofOfPersonhood public pohRegistry;
// Her kullanıcının her öneri için kullanabileceği maksimum ses kredisi
uint256 public constant MAX_VOICE_CREDITS = 100;
// Kullanıcının her önerideki harcadığı ses kredileri
mapping(uint256 => mapping(address => uint256)) public voiceCreditsSpent;
// Öneri başına toplam karesel oylar
mapping(uint256 => uint256) public quadraticVotesFor;
mapping(uint256 => uint256) public quadraticVotesAgainst;
mapping(uint256 => uint256) public quadraticVotesAbstain;
event QuadraticVoteCast(
address indexed voter,
uint256 proposalId,
uint8 support,
uint256 voiceCredits,
uint256 effectiveVotes
);
/**
* @dev Constructor
* @param _token Governance token (ERC20Votes)
* @param _timelock Timelock controller adresi
* @param _pohRegistry Proof of Personhood registry
*/
constructor(
IVotes _token,
TimelockController _timelock,
IProofOfPersonhood _pohRegistry
)
Governor("ContentDAO")
GovernorSettings(
1, // voting delay: 1 block
50400, // voting period: ~1 week (assuming 12s blocks)
100e18 // proposal threshold: 100 tokens
)
GovernorVotes(_token)
GovernorVotesQuorumFraction(4) // 4% quorum
GovernorTimelockControl(_timelock)
{
pohRegistry = _pohRegistry;
}
/**
* @dev Karesel oylama fonksiyonu
* @param proposalId Öneri ID
* @param support 0=Against, 1=For, 2=Abstain
* @param voiceCredits Harcanacak ses kredisi sayısı
*/
function castQuadraticVote(
uint256 proposalId,
uint8 support,
uint256 voiceCredits
) public returns (uint256) {
// Sybil kontrolü - kullanıcı gerçek bir insan mı?
require(
pohRegistry.isHuman(msg.sender),
"QuadraticVoting: Proof of Personhood required"
);
// Oylama durumu kontrolü
require(
state(proposalId) == ProposalState.Active,
"QuadraticVoting: voting is closed"
);
// Ses kredisi limiti kontrolü
uint256 spent = voiceCreditsSpent[proposalId][msg.sender];
require(
spent + voiceCredits <= MAX_VOICE_CREDITS,
"QuadraticVoting: exceeds voice credit limit"
);
// Karesel formül: effectiveVotes = sqrt(voiceCredits)
uint256 effectiveVotes = sqrt(voiceCredits);
// Ses kredisini kaydet
voiceCreditsSpent[proposalId][msg.sender] = spent + voiceCredits;
// Oyları ilgili kategoriye ekle
if (support == 0) {
quadraticVotesAgainst[proposalId] += effectiveVotes;
} else if (support == 1) {
quadraticVotesFor[proposalId] += effectiveVotes;
} else if (support == 2) {
quadraticVotesAbstain[proposalId] += effectiveVotes;
}
emit QuadraticVoteCast(
msg.sender,
proposalId,
support,
voiceCredits,
effectiveVotes
);
return effectiveVotes;
}
/**
* @dev Babylon karekök algoritması (gaz-verimli)
* @param x Karekökü alınacak sayı
*/
function sqrt(uint256 x) internal pure returns (uint256 y) {
if (x == 0) return 0;
uint256 z = (x + 1) / 2;
y = x;
while (z < y) {
y = z;
z = (x / z + z) / 2;
}
}
/**
* @dev Önerinin geçip geçmediğini kontrol et
*/
function _quorumReached(uint256 proposalId)
internal
view
override
returns (bool)
{
uint256 totalVotes = quadraticVotesFor[proposalId] +
quadraticVotesAgainst[proposalId] +
quadraticVotesAbstain[proposalId];
return totalVotes >= quorum(proposalSnapshot(proposalId));
}
/**
* @dev Oylama sonucunu hesapla
*/
function _voteSucceeded(uint256 proposalId)
internal
view
override
returns (bool)
{
return quadraticVotesFor[proposalId] > quadraticVotesAgainst[proposalId];
}
// Override fonksiyonları (OpenZeppelin gereksinimi)
function votingDelay()
public
view
override(IGovernor, GovernorSettings)
returns (uint256)
{
return super.votingDelay();
}
function votingPeriod()
public
view
override(IGovernor, GovernorSettings)
returns (uint256)
{
return super.votingPeriod();
}
function quorum(uint256 blockNumber)
public
view
override(IGovernor, GovernorVotesQuorumFraction)
returns (uint256)
{
return super.quorum(blockNumber);
}
function proposalThreshold()
public
view
override(Governor, GovernorSettings)
returns (uint256)
{
return super.proposalThreshold();
}
function state(uint256 proposalId)
public
view
override(Governor, GovernorTimelockControl)
returns (ProposalState)
{
return super.state(proposalId);
}
function propose(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
string memory description
) public override(Governor, IGovernor) returns (uint256) {
return super.propose(targets, values, calldatas, description);
}
function _execute(
uint256 proposalId,
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) internal override(Governor, GovernorTimelockControl) {
super._execute(proposalId, targets, values, calldatas, descriptionHash);
}
function _cancel(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) internal override(Governor, GovernorTimelockControl) returns (uint256) {
return super._cancel(targets, values, calldatas, descriptionHash);
}
function _executor()
internal
view
override(Governor, GovernorTimelockControl)
returns (address)
{
return super._executor();
}
function supportsInterface(bytes4 interfaceId)
public
view
override(Governor, GovernorTimelockControl)
returns (bool)
{
return super.supportsInterface(interfaceId);
}
}
/**
* @dev Proof of Personhood interface
*/
interface IProofOfPersonhood {
function isHuman(address account) external view returns (bool);
}