How Community Pools Work
Technical explanation of community pool mechanics, governance, yield distribution, and member management on KhipuVault.
How Community Pools Work
Learn the technical details of how community pools operate, from member deposits to governance and yield distribution.
Architecture Overview
Community Pools are smart contracts that enable collective savings with democratic governance.
┌─────────────────────────────────────────┐
│ Community Pool Contract │
├─────────────────────────────────────────┤
│ │
│ 👥 Members │
│ ├─ Member addresses & balances │
│ ├─ Voting weights │
│ └─ Join/leave timestamps │
│ │
│ 💰 Treasury │
│ ├─ Total pooled MUSD │
│ ├─ Individual member balances │
│ └─ Accumulated yields │
│ │
│ 🗳️ Governance │
│ ├─ Active proposals │
│ ├─ Voting records │
│ └─ Execution queue │
│ │
│ ⚙️ Configuration │
│ ├─ Membership rules │
│ ├─ Voting parameters │
│ └─ Withdrawal limits │
│ │
└─────────────────────────────────────────┘Core Mechanics
Pool Creation
When you create a community pool:
Smart Contract Deployment
A new CooperativePool contract is deployed with your specified parameters:
struct PoolConfig {
string name;
string description;
uint256 minimumDeposit; // e.g., 50 MUSD
uint256 votingPeriod; // e.g., 3 days
uint256 quorumPercentage; // e.g., 51%
bool isPublic; // Open vs closed
VotingType votingType; // Democratic or weighted
}Founder Initialization
You become the first member and admin:
- Your address is recorded as founder
- Initial admin privileges granted
- First deposit can be made
Pool Registration
Pool is registered in the YieldAggregator:
- Receives unique pool ID
- Begins earning yields
- Listed on platform (if public)
Member Lifecycle
Joining Process
For open pools:
User clicks "Join" → Deposits minimum → Automatically added → Starts earningFor closed pools:
User requests join → Admin/members vote → If approved, deposit → AddedFor hybrid pools:
User requests join → Provides verification → Admin approves → Deposit → AddedMember States
enum MemberStatus {
ACTIVE, // Contributing and earning
INACTIVE, // Temporarily not contributing
PENDING, // Awaiting approval
REMOVED, // Voted out or left
SUSPENDED // Temporarily restricted
}Deposits & Balances
Individual Tracking
Each member has their own balance:
struct Member {
address wallet;
uint256 totalDeposited; // All-time deposits
uint256 currentBalance; // Current principal
uint256 accruedYields; // Earned yields
uint256 joinedAt; // Timestamp
MemberStatus status; // Current state
uint256 votingWeight; // For weighted voting
}Deposit Flow
Member Initiates Deposit
deposit(amount: bigint)MUSD Transfer
Smart contract receives MUSD from member's wallet:
- Requires prior MUSD approval
- Verifies minimum deposit requirement
- Updates member's balance
Pool Balance Update
poolTotalDeposits += amount;
member.totalDeposited += amount;
member.currentBalance += amount;Yield Aggregator Notification
Pool notifies aggregator of new funds:
- Funds deployed to yield strategies
- Begins earning immediately
Yield Generation & Distribution
How Yields are Earned
Your pooled MUSD is deployed across multiple strategies:
-
Mezo Staking (40-50% allocation)
- Bitcoin staking on Mezo L2
- ~8-12% APY
- Lowest risk
-
DeFi Lending (30-40% allocation)
- Lending protocols (Aave-style)
- ~15-20% APY
- Medium risk
-
Liquidity Provision (10-20% allocation)
- DEX liquidity pools
- ~20-30% APY
- Higher risk, higher return
Diversification across strategies reduces risk while maintaining competitive yields.
The YieldAggregator automatically rebalances based on market conditions.
Yield Calculation
Yields are calculated per block and attributed to each member proportionally:
// Simplified yield calculation
function calculateMemberYield(address member) returns (uint256) {
uint256 memberShare = memberBalance[member] / totalPoolBalance;
uint256 totalYield = getCurrentYields();
uint256 platformFee = totalYield * 10 / 100; // 10% fee
uint256 netYield = totalYield - platformFee;
return netYield * memberShare;
}Every block (~5 seconds):
Total Pool Yields Generated
↓
Subtract 10% Platform Fee
↓
Distribute 90% Proportionally
↓
Update Each Member's BalanceYield Distribution Options
Pools can choose how to handle yields:
-
Auto-compound (default)
- Yields added to principal automatically
- Maximizes compound growth
- No action needed
-
Distribute to members
- Yields sent to member wallets periodically
- Members receive actual MUSD
- Requires gas for distributions
-
Pool treasury
- Yields accumulate in pool
- Used for pool expenses
- Requires governance vote to distribute
Governance System
Proposal Types
Members can propose various actions:
enum ProposalType {
ADD_MEMBER, // Invite new member
REMOVE_MEMBER, // Remove a member
WITHDRAW_FUNDS, // Pool-level withdrawal
CHANGE_PARAMETERS, // Modify pool settings
CHANGE_STRATEGY, // Adjust yield strategy
DISTRIBUTE_YIELDS, // Payout accumulated yields
UPGRADE_CONTRACT // Technical upgrades
}Voting Mechanisms
Democratic (One Person, One Vote)
// Each member gets 1 vote
function vote(uint256 proposalId, bool support) {
require(isMember(msg.sender), "Not a member");
votes[proposalId][msg.sender] = support ? 1 : 0;
}Best for:
- Tight-knit communities
- Equal participation
- Fairness emphasis
Weighted by Contribution
// Vote weight based on deposited amount
function vote(uint256 proposalId, bool support) {
uint256 weight = memberBalance[msg.sender];
votes[proposalId][msg.sender] = support ? weight : 0;
}Best for:
- Large diverse pools
- Proportional risk/reward
- Investment clubs
Proposal Lifecycle
Creation
Any member can create a proposal:
createProposal({
type: ProposalType,
description: string,
data: bytes, // Encoded parameters
votingPeriod: uint256
})Voting Period
Members vote during the configured period (typically 3-7 days):
Proposal Created (Day 0)
↓
Voting Period (3-7 days)
↓
Voting EndsQuorum Check
For proposal to pass:
- ✅ Minimum participation met (quorum)
- ✅ Majority voted in favor
uint256 totalVotes = yesVotes + noVotes;
uint256 participation = totalVotes / totalMembers;
bool quorumMet = participation >= quorumPercentage;
bool majorityApproved = yesVotes > noVotes;Execution
If passed, proposal is executed automatically:
if (quorumMet && majorityApproved) {
executeProposal(proposalId);
emit ProposalExecuted(proposalId);
}Completion
Proposal is marked as executed or rejected:
- State updated on-chain
- Event emitted
- Results visible to all members
Withdrawal Mechanisms
Individual Withdrawals
Members can withdraw their own balance:
// Small withdrawals (< 10% of balance)
withdraw(amount) {
require(amount <= balance * 0.1, "Exceeds limit");
// Instant, no approval needed
}
// Large withdrawals (≥ 10% of balance)
requestWithdrawal(amount) {
// Creates proposal
// Requires member vote
// Protects pool stability
}Large Withdrawal Protection: Withdrawals exceeding 10% of pool balance may require governance approval to prevent sudden liquidity issues.
Emergency Withdrawals
In case of emergencies, members can force withdraw:
emergencyWithdraw() {
// Available after 7-day waiting period
// Withdraws proportional share
// Removes from pool
}Member Management
Adding Members
Open Pools: Automatic
joinPool() payable {
require(msg.value >= minimumDeposit);
members.push(msg.sender);
}Closed Pools: Requires vote
proposeNewMember(address newMember) {
createProposal(ADD_MEMBER, newMember);
// Voting begins
}Removing Members
Members can be removed by vote for:
- Violating pool rules
- Inactivity (not contributing)
- Malicious behavior
- Voluntary exit
proposalRemoveMember(address member, string reason) {
// Creates removal proposal
// Requires 60% approval
// Member can withdraw during vote
}Member Protection: A member under removal proposal can still withdraw their balance during the voting period.
Fee Structure
Platform Fees
Gross Yields: 100 MUSD
↓
Platform Fee (10%): -10 MUSD
↓
Net to Pool: 90 MUSD
↓
Distributed to Members ProportionallyGas Fees
Approximate gas costs on Mezo testnet:
| Action | Gas Cost | USD Equivalent |
|---|---|---|
| Create pool | ~0.0003 BTC | ~$0.03 |
| Join pool | ~0.0001 BTC | ~$0.01 |
| Deposit | ~0.0001 BTC | ~$0.01 |
| Vote | ~0.00005 BTC | ~$0.005 |
| Withdraw | ~0.00015 BTC | ~$0.015 |
Mainnet Costs: Gas fees on mainnet will be higher (~$0.10-$0.50 per transaction) but still significantly cheaper than Ethereum.
Smart Contract Architecture
Core Contracts
CooperativePool.sol
├─ Inherits from ERC20 (pool shares)
├─ Implements governance (proposals, voting)
├─ Manages members (add, remove, balances)
└─ Integrates with YieldAggregator
YieldAggregator.sol
├─ Routes funds to strategies
├─ Calculates yields
├─ Distributes returns
└─ Manages platform fees
GovernanceModule.sol
├─ Proposal creation
├─ Voting logic
├─ Execution queue
└─ Event emissionsKey Functions
// Member actions
function deposit(uint256 amount) external;
function withdraw(uint256 amount) external;
function requestJoin() external;
function leavePool() external;
// Governance
function createProposal(...) external returns (uint256);
function vote(uint256 proposalId, bool support) external;
function executeProposal(uint256 proposalId) external;
// Admin (multi-sig or voted role)
function approveJoinRequest(address member) external;
function updatePoolParameters(...) external;
function emergencyPause() external;
// Views
function getMemberBalance(address member) external view returns (uint256);
function getPoolStats() external view returns (PoolStats);
function getActiveProposals() external view returns (Proposal[]);Security Measures
Access Control
// Role-based permissions
modifier onlyMember() {
require(isMember(msg.sender), "Not a member");
_;
}
modifier onlyAdmin() {
require(isAdmin(msg.sender), "Not an admin");
_;
}
modifier whenNotPaused() {
require(!paused, "Pool is paused");
_;
}Reentrancy Protection
All external calls use OpenZeppelin's ReentrancyGuard:
function withdraw(uint256 amount)
external
nonReentrant
whenNotPaused
{
// Safe withdrawal logic
}Emergency Controls
- Pause mechanism: Stops deposits/withdrawals during emergencies
- Timelock: Major changes require 48-hour delay
- Multi-sig admin: Critical functions require multiple approvals
Data Transparency
On-Chain Events
All important actions emit events:
event Deposited(address indexed member, uint256 amount);
event Withdrawn(address indexed member, uint256 amount);
event MemberAdded(address indexed member);
event MemberRemoved(address indexed member, string reason);
event ProposalCreated(uint256 indexed proposalId);
event Voted(uint256 indexed proposalId, address indexed voter, bool support);
event YieldsDistributed(uint256 amount, uint256 timestamp);Real-Time Queries
Members can query pool state anytime:
// Get pool overview
const stats = await pool.getPoolStats();
// {
// totalMembers: 45,
// totalDeposits: 125000000000, // 125k MUSD
// totalYields: 15000000000, // 15k MUSD
// avgMemberBalance: 2777777778
// }
// Get your balance
const myBalance = await pool.getMemberBalance(address);
// Get active proposals
const proposals = await pool.getActiveProposals();Performance Optimization
Gas Optimization
- Batch operations: Vote on multiple proposals at once
- Lazy yield calculation: Yields calculated on-demand, not every block
- Efficient storage: Packed struct variables
Scalability
- Member limits: Pools capped at 1,000 members for governance efficiency
- Proposal limits: Max 10 active proposals at once
- Vote indexing: Off-chain indexing for fast UI queries
Next Steps
Now that you understand how community pools work technically:
Create Your Pool
Step-by-step pool creation guide
Join Existing Pool
Find and join community pools
Governance Details
Deep dive into voting and proposals
Pool Management
Admin features and best practices