Governance Contract
The Governance contract enables token holders to propose and vote on protocol changes.
Contract Overview
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
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/GovernorTimelockControl.sol";
contract UsmeweGovernor is
Governor,
GovernorSettings,
GovernorCountingSimple,
GovernorVotes,
GovernorTimelockControl
{
// Voting power multipliers for levels
mapping(address => uint256) public levelMultipliers;
// Proposal types
enum ProposalType {
ParameterChange,
ContractUpgrade,
TreasurySpend,
EmergencyAction
}
struct ProposalMetadata {
ProposalType proposalType;
string ipfsHash;
address proposer;
}
mapping(uint256 => ProposalMetadata) public proposalMetadata;
}
Key Functions
Create Proposal
function propose(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
string memory description,
ProposalType proposalType,
string memory ipfsHash
) public returns (uint256 proposalId) {
require(
getVotes(msg.sender, block.number - 1) >= proposalThreshold(),
"Below proposal threshold"
);
proposalId = super.propose(targets, values, calldatas, description);
proposalMetadata[proposalId] = ProposalMetadata({
proposalType: proposalType,
ipfsHash: ipfsHash,
proposer: msg.sender
});
emit ProposalCreatedWithMetadata(proposalId, proposalType, ipfsHash);
return proposalId;
}
Cast Vote
function castVote(
uint256 proposalId,
uint8 support
) public override returns (uint256 weight) {
address voter = msg.sender;
// Get base voting power
uint256 baseWeight = getVotes(voter, proposalSnapshot(proposalId));
// Apply level multiplier (Platinum: 1.5x, Diamond: 2x)
uint256 multiplier = levelMultipliers[voter];
if (multiplier == 0) multiplier = 100; // 1x default
weight = (baseWeight * multiplier) / 100;
_countVote(proposalId, voter, support, weight, "");
emit VoteCastWithWeight(voter, proposalId, support, weight, baseWeight);
return weight;
}
Execute Proposal
function execute(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) public payable override returns (uint256) {
uint256 proposalId = hashProposal(targets, values, calldatas, descriptionHash);
require(
state(proposalId) == ProposalState.Succeeded,
"Proposal not successful"
);
// Emergency proposals skip timelock
if (proposalMetadata[proposalId].proposalType == ProposalType.EmergencyAction) {
_executeOperations(proposalId, targets, values, calldatas, descriptionHash);
} else {
super.execute(targets, values, calldatas, descriptionHash);
}
emit ProposalExecuted(proposalId);
return proposalId;
}
Update Level Multiplier
function updateLevelMultiplier(
address user,
uint256 multiplier
) external onlyRole(LEVEL_MANAGER_ROLE) {
require(multiplier >= 100 && multiplier <= 200, "Invalid multiplier");
levelMultipliers[user] = multiplier;
emit LevelMultiplierUpdated(user, multiplier);
}
View Functions
Get Proposal Details
function getProposalDetails(uint256 proposalId) external view returns (
ProposalState currentState,
uint256 forVotes,
uint256 againstVotes,
uint256 abstainVotes,
uint256 startBlock,
uint256 endBlock,
ProposalType proposalType,
string memory ipfsHash
) {
(forVotes, againstVotes, abstainVotes) = proposalVotes(proposalId);
return (
state(proposalId),
forVotes,
againstVotes,
abstainVotes,
proposalSnapshot(proposalId),
proposalDeadline(proposalId),
proposalMetadata[proposalId].proposalType,
proposalMetadata[proposalId].ipfsHash
);
}
Get Voting Power
function getVotingPower(address account) external view returns (
uint256 baseVotes,
uint256 multiplier,
uint256 effectiveVotes
) {
baseVotes = getVotes(account, block.number - 1);
multiplier = levelMultipliers[account];
if (multiplier == 0) multiplier = 100;
effectiveVotes = (baseVotes * multiplier) / 100;
return (baseVotes, multiplier, effectiveVotes);
}
Events
event ProposalCreatedWithMetadata(
uint256 indexed proposalId,
ProposalType proposalType,
string ipfsHash
);
event VoteCastWithWeight(
address indexed voter,
uint256 indexed proposalId,
uint8 support,
uint256 effectiveWeight,
uint256 baseWeight
);
event ProposalExecuted(uint256 indexed proposalId);
event LevelMultiplierUpdated(
address indexed user,
uint256 multiplier
);
Governance Parameters
| Parameter | Value | Description |
|---|
votingDelay | 1 day | Time before voting starts |
votingPeriod | 7 days | Duration of voting |
proposalThreshold | 1000 tmUSDC | Minimum to propose |
quorumNumerator | 4% | Minimum participation |
Proposal Types
Parameter Change
Modify protocol parameters (interest rates, limits, fees)
Contract Upgrade
Upgrade proxy implementations
Treasury Spend
Allocate treasury funds
Emergency Action
Critical fixes (bypasses timelock)
Voting Power by Level
| Level | Base Multiplier | Effective Vote |
|---|
| Bronze | 1.0x | 100 tmUSDC = 100 votes |
| Silver | 1.0x | 100 tmUSDC = 100 votes |
| Gold | 1.0x | 100 tmUSDC = 100 votes |
| Platinum | 1.5x | 100 tmUSDC = 150 votes |
| Diamond | 2.0x | 100 tmUSDC = 200 votes |
Proposal Lifecycle
┌─────────────────────────────────────────────────────────────────┐
│ PROPOSAL LIFECYCLE │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Created → Pending → Active → Succeeded → Queued → Executed │
│ │ │ │ │
│ │ │ └──→ Defeated │
│ │ │ │
│ │ └──→ Defeated (quorum not met) │
│ │ │
│ └──→ Cancelled (by proposer) │
│ │
└─────────────────────────────────────────────────────────────────┘
| State | Description |
|---|
Pending | Waiting for voting delay |
Active | Voting in progress |
Succeeded | Passed, awaiting execution |
Defeated | Failed (votes or quorum) |
Queued | In timelock queue |
Executed | Successfully executed |
Cancelled | Cancelled by proposer |
Integration Example
import { ethers } from 'ethers';
// Check voting power
const power = await governor.getVotingPower(myAddress);
console.log(`Effective votes: ${power.effectiveVotes}`);
// Create proposal
const targets = [trustVaultAddress];
const values = [0];
const calldatas = [
trustVault.interface.encodeFunctionData('updateInterestRate', [500])
];
const description = "Proposal #1: Reduce base interest rate to 5%";
const proposalId = await governor.propose(
targets,
values,
calldatas,
description,
0, // ParameterChange
"QmXxx..." // IPFS hash with details
);
// Vote
await governor.castVote(proposalId, 1); // 1 = For
// Execute after voting period
await governor.execute(targets, values, calldatas, ethers.id(description));
Security Measures
Critical governance security features:
- Timelock: 48-hour delay on non-emergency actions
- Quorum: 4% minimum participation required
- Proposal Threshold: Prevents spam proposals
- Emergency Guardian: Multi-sig can pause in emergencies