# Governor simplifie
GOVERNOR_CONTRACT = '''
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
interface IGovernanceToken {
function getVotes(address account) external view returns (uint256);
function getPastVotes(address account, uint256 blockNumber) external view returns (uint256);
}
contract SimpleGovernor {
IGovernanceToken public immutable token;
uint256 public constant VOTING_DELAY = 1;
uint256 public constant VOTING_PERIOD = 50400;
uint256 public constant PROPOSAL_THRESHOLD = 100 * 1e18;
uint256 public constant QUORUM = 4_000_000 * 1e18;
enum ProposalState { Pending, Active, Canceled, Defeated, Succeeded, Queued, Expired, Executed }
struct Proposal {
uint256 id;
address proposer;
address[] targets;
uint256[] values;
bytes[] calldatas;
string description;
uint256 voteStart;
uint256 voteEnd;
uint256 forVotes;
uint256 againstVotes;
uint256 abstainVotes;
bool executed;
}
mapping(uint256 => Proposal) public proposals;
mapping(uint256 => mapping(address => bool)) public hasVoted;
uint256 private _proposalCount;
event ProposalCreated(uint256 indexed id, address indexed proposer, address[] targets, uint256[] values, bytes[] calldatas, string description);
event VoteCast(address indexed voter, uint256 indexed proposalId, uint8 support, uint256 weight);
constructor(address token_) { token = IGovernanceToken(token_); }
function propose(address[] memory targets, uint256[] memory values, bytes[] memory calldatas, string memory description) public returns (uint256) {
require(token.getPastVotes(msg.sender, block.number - 1) >= PROPOSAL_THRESHOLD, "Below proposal threshold");
uint256 proposalId = ++_proposalCount;
proposals[proposalId] = Proposal({
id: proposalId, proposer: msg.sender, targets: targets, values: values,
calldatas: calldatas, description: description,
voteStart: block.number + VOTING_DELAY, voteEnd: block.number + VOTING_DELAY + VOTING_PERIOD,
forVotes: 0, againstVotes: 0, abstainVotes: 0, executed: false
});
emit ProposalCreated(proposalId, msg.sender, targets, values, calldatas, description);
return proposalId;
}
function castVote(uint256 proposalId, uint8 support) public {
require(state(proposalId) == ProposalState.Active, "Voting not active");
require(!hasVoted[proposalId][msg.sender], "Already voted");
Proposal storage proposal = proposals[proposalId];
uint256 weight = token.getPastVotes(msg.sender, proposal.voteStart);
hasVoted[proposalId][msg.sender] = true;
if (support == 0) proposal.againstVotes += weight;
else if (support == 1) proposal.forVotes += weight;
else proposal.abstainVotes += weight;
emit VoteCast(msg.sender, proposalId, support, weight);
}
function state(uint256 proposalId) public view returns (ProposalState) {
Proposal storage p = proposals[proposalId];
if (p.executed) return ProposalState.Executed;
if (block.number < p.voteStart) return ProposalState.Pending;
if (block.number < p.voteEnd) return ProposalState.Active;
uint256 total = p.forVotes + p.againstVotes + p.abstainVotes;
if (total < QUORUM || p.againstVotes >= p.forVotes) return ProposalState.Defeated;
return ProposalState.Succeeded;
}
}
'''
# Deployer le Governor
governor, _ = compile_and_deploy(w3, GOVERNOR_CONTRACT, deployer, gov_token.address)
# Miner des blocs pour que les checkpoints de delegation soient bien dans le passe
for _ in range(3):
w3.provider.make_request("evm_mine", [])
# Creer une proposition
tx = governor.functions.propose(
[deployer], [0], [b""],
"Proposition #1: Augmenter le budget R&D de 10%"
).transact({"from": deployer})
receipt = w3.eth.wait_for_transaction_receipt(tx)
proposal_events = governor.events.ProposalCreated().process_receipt(receipt)
proposal_id = proposal_events[0]["args"]["id"]
state_names = ["Pending", "Active", "Canceled", "Defeated", "Succeeded", "Queued", "Expired", "Executed"]
print(f"\nGovernor:")
print(f" Proposal ID : {proposal_id}")
print(f" Etat : {state_names[governor.functions.state(proposal_id).call()]}")
# Avancer de 2 blocs : 1 pour VOTING_DELAY, 1 pour que voteStart soit dans le passe
# (castVote appelle getPastVotes(voter, voteStart) qui exige voteStart < block.number)
w3.provider.make_request("evm_mine", [])
w3.provider.make_request("evm_mine", [])
print(f" Etat : {state_names[governor.functions.state(proposal_id).call()]}")
# Voter
tx = governor.functions.castVote(proposal_id, 1).transact({"from": deployer})
w3.eth.wait_for_transaction_receipt(tx)
tx = governor.functions.castVote(proposal_id, 1).transact({"from": voter2})
w3.eth.wait_for_transaction_receipt(tx)
# Resultats
proposal_data = governor.functions.proposals(proposal_id).call()
print(f"\nVotes:")
print(f" Pour : {proposal_data[5] / 10**18:,.0f} GOV")
print(f" Contre : {proposal_data[6] / 10**18:,.0f} GOV")
print(f" Abstain : {proposal_data[7] / 10**18:,.0f} GOV")