This ERC proposes a standard interface for implementing dynamic, context-aware governance systems on Ethereum. The Dynamic Governance Optimization Protocol (DGOP) enables DAOs to adapt their voting mechanisms based on proposal context, stakeholder expertise, and historical governance data. By standardizing these interfaces, DGOP allows for interoperable governance components while preserving flexibility for DAO-specific customization.
Current DAO governance systems typically rely on static voting mechanisms that apply uniformly across all decision types. This one-size-fits-all approach fails to account for the varying complexities, stakeholder impacts, and expertise requirements of different governance decisions.
DGOP addresses these limitations by:
- Enabling context-aware governance where voting mechanisms adapt to proposal types
- Incorporating reputation and expertise into voting weight calculations when appropriate
- Using historical governance data to optimize voting parameters over time
- Balancing technical expertise with community inclusivity
- Maintaining transparency while supporting varying levels of voter privacy
- Reducing governance overhead through intelligent optimization
This standard creates a framework for more nuanced, efficient, and effective DAO governance without sacrificing decentralization or security.
The main interface that defines the core functionality of the protocol.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
interface IDGOP {
/**
* @dev Emitted when a new proposal is created
* @param proposalId The unique identifier of the proposal
* @param creator Address that created the proposal
* @param contextId Identifier for the proposal context
* @param moduleId Identifier for the voting module assigned
*/
event ProposalCreated(
bytes32 indexed proposalId,
address indexed creator,
bytes32 indexed contextId,
bytes32 moduleId
);
/**
* @dev Emitted when a proposal's state changes
* @param proposalId The unique identifier of the proposal
* @param newState The new state of the proposal
*/
event ProposalStateChanged(
bytes32 indexed proposalId,
uint8 newState
);
/**
* @dev Emitted when a vote is cast on a proposal
* @param proposalId The proposal identifier
* @param voter The address of the voter
* @param support Whether the vote supports the proposal
* @param weight The weight of the vote (calculated by the voting module)
*/
event VoteCast(
bytes32 indexed proposalId,
address indexed voter,
bool support,
uint256 weight
);
/**
* @dev Creates a new governance proposal
* @param metadataURI URI pointing to proposal metadata
* @param contextParams Parameters defining the proposal context
* @param targets Target addresses for execution
* @param values Ether values for execution
* @param calldatas Function call data for execution
* @return proposalId The unique identifier of the created proposal
*/
function createProposal(
string calldata metadataURI,
bytes calldata contextParams,
address[] calldata targets,
uint256[] calldata values,
bytes[] calldata calldatas
) external returns (bytes32 proposalId);
/**
* @dev Casts a vote on a proposal
* @param proposalId The proposal identifier
* @param support Whether the vote supports the proposal
* @param metadata Additional vote metadata (e.g., expertise proofs)
* @return weight The weight assigned to the vote
*/
function castVote(
bytes32 proposalId,
bool support,
bytes calldata metadata
) external returns (uint256 weight);
/**
* @dev Executes a successful proposal
* @param proposalId The identifier of the proposal to execute
* @return success Whether the execution was successful
*/
function executeProposal(bytes32 proposalId) external returns (bool success);
/**
* @dev Gets the current state of a proposal
* @param proposalId The proposal identifier
* @return state The current state (0: Pending, 1: Active, 2: Succeeded, 3: Failed, 4: Executed, 5: Canceled)
*/
function getProposalState(bytes32 proposalId) external view returns (uint8 state);
/**
* @dev Gets the voting module assigned to a proposal
* @param proposalId The proposal identifier
* @return moduleId The identifier of the voting module
*/
function getProposalVotingModule(bytes32 proposalId) external view returns (bytes32 moduleId);
/**
* @dev Gets the voting power of an address for a specific proposal
* @param voter The address to check
* @param proposalId The proposal identifier
* @return votingPower The voting power for the given proposal
*/
function getVotingPower(address voter, bytes32 proposalId) external view returns (uint256 votingPower);
}Interface for defining and managing proposal contexts.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
interface IProposalContext {
/**
* @dev Structure defining a proposal context
* @param categoryId Unique identifier for the proposal category
* @param urgencyLevel Time sensitivity of the proposal (0-10)
* @param requiredExpertise Array of expertise domains relevant to the proposal
* @param stakeholders Directly affected stakeholders
* @param metadataURI URI to extended context definition
*/
struct Context {
bytes32 categoryId;
uint8 urgencyLevel;
bytes32[] requiredExpertise;
address[] stakeholders;
string metadataURI;
}
/**
* @dev Emitted when a new context category is created
* @param categoryId The unique identifier of the category
* @param name Human-readable name for the category
*/
event CategoryCreated(bytes32 indexed categoryId, string name);
/**
* @dev Emitted when a context is created for a proposal
* @param proposalId The unique identifier of the proposal
* @param contextId The unique identifier of the created context
*/
event ContextCreated(bytes32 indexed proposalId, bytes32 indexed contextId);
/**
* @dev Creates a new proposal context
* @param proposalId The proposal identifier
* @param contextParams Encoded context parameters
* @return contextId The unique identifier of the created context
*/
function createContext(bytes32 proposalId, bytes calldata contextParams)
external returns (bytes32 contextId);
/**
* @dev Gets the context for a proposal
* @param proposalId The proposal identifier
* @return context The proposal context
*/
function getContext(bytes32 proposalId) external view returns (Context memory context);
/**
* @dev Creates a new context category
* @param name Human-readable name for the category
* @param description Category description
* @return categoryId The unique identifier of the created category
*/
function createCategory(string calldata name, string calldata description)
external returns (bytes32 categoryId);
/**
* @dev Checks if a voter has required expertise for a proposal
* @param voter The address to check
* @param proposalId The proposal identifier
* @return hasExpertise Whether the voter has the required expertise
*/
function hasRequiredExpertise(address voter, bytes32 proposalId)
external view returns (bool hasExpertise);
}Interface for registering and managing voting modules.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
interface IVotingModuleRegistry {
/**
* @dev Structure defining a voting module
* @param moduleId Unique identifier for the module
* @param name Human-readable name
* @param description Module description
* @param implementation Address implementing voting logic
* @param suitableCategories Categories where this module works well
* @param minUrgency Minimum urgency level for this module
* @param maxUrgency Maximum urgency level for this module
* @param expertiseWeighted Whether this module uses expertise weighting
* @param stakeholderWeighted Whether this module prioritizes stakeholders
*/
struct VotingModule {
bytes32 moduleId;
string name;
string description;
address implementation;
bytes32[] suitableCategories;
uint8 minUrgency;
uint8 maxUrgency;
bool expertiseWeighted;
bool stakeholderWeighted;
}
/**
* @dev Emitted when a new voting module is registered
* @param moduleId The unique identifier of the module
* @param implementation The address implementing the module
*/
event ModuleRegistered(bytes32 indexed moduleId, address indexed implementation);
/**
* @dev Registers a new voting module
* @param name Human-readable name
* @param description Module description
* @param implementation Address implementing voting logic
* @param suitableCategories Categories where this module works well
* @param minUrgency Minimum urgency level for this module
* @param maxUrgency Maximum urgency level for this module
* @param expertiseWeighted Whether this module uses expertise weighting
* @param stakeholderWeighted Whether this module prioritizes stakeholders
* @return moduleId The unique identifier of the registered module
*/
function registerModule(
string calldata name,
string calldata description,
address implementation,
bytes32[] calldata suitableCategories,
uint8 minUrgency,
uint8 maxUrgency,
bool expertiseWeighted,
bool stakeholderWeighted
) external returns (bytes32 moduleId);
/**
* @dev Gets a voting module by its ID
* @param moduleId The module identifier
* @return module The voting module
*/
function getModule(bytes32 moduleId) external view returns (VotingModule memory module);
/**
* @dev Gets the optimal voting module for a proposal context
* @param contextId The context identifier
* @return moduleId The identifier of the optimal voting module
*/
function getOptimalModule(bytes32 contextId) external view returns (bytes32 moduleId);
/**
* @dev Updates the performance metrics for a voting module
* @param moduleId The module identifier
* @param contextId The context identifier
* @param participationRate The participation rate achieved
* @param executionTime Time from proposal creation to execution
* @param outcome Outcome of the proposal (succeeded or failed)
*/
function updateModulePerformance(
bytes32 moduleId,
bytes32 contextId,
uint256 participationRate,
uint256 executionTime,
bool outcome
) external;
}Interface that all voting modules must implement.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
interface IVotingModule {
/**
* @dev Initializes a module for a specific proposal
* @param proposalId The proposal identifier
* @param contextId The context identifier
* @return success Whether initialization was successful
*/
function initialize(bytes32 proposalId, bytes32 contextId) external returns (bool success);
/**
* @dev Calculates voting power for a voter on a specific proposal
* @param voter The address of the voter
* @param proposalId The proposal identifier
* @param voteMetadata Additional metadata for vote weight calculation
* @return votingPower The calculated voting power
*/
function calculateVotingPower(
address voter,
bytes32 proposalId,
bytes calldata voteMetadata
) external view returns (uint256 votingPower);
/**
* @dev Checks if a proposal has reached quorum
* @param proposalId The proposal identifier
* @return hasQuorum Whether quorum has been reached
*/
function hasQuorum(bytes32 proposalId) external view returns (bool hasQuorum);
/**
* @dev Checks if a proposal has passed
* @param proposalId The proposal identifier
* @return hasPassed Whether the proposal has passed
*/
function hasPassed(bytes32 proposalId) external view returns (bool hasPassed);
/**
* @dev Gets the current quorum requirement for a proposal
* @param proposalId The proposal identifier
* @return quorumRequired The required quorum
*/
function getQuorumRequirement(bytes32 proposalId) external view returns (uint256 quorumRequired);
/**
* @dev Gets the current approval threshold for a proposal
* @param proposalId The proposal identifier
* @return threshold The approval threshold (e.g., 51% = 5100, for precision)
*/
function getApprovalThreshold(bytes32 proposalId) external view returns (uint256 threshold);
}Interface for managing expertise and reputation.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
interface IExpertiseRegistry {
/**
* @dev Structure defining an expertise domain
* @param domainId Unique identifier for the domain
* @param name Human-readable name
* @param description Domain description
* @param verifiers Addresses that can verify expertise in this domain
*/
struct ExpertiseDomain {
bytes32 domainId;
string name;
string description;
address[] verifiers;
}
/**
* @dev Emitted when expertise is verified for an address
* @param account The address receiving verification
* @param domainId The expertise domain identifier
* @param score The expertise score (0-100)
* @param verifier The address of the verifier
*/
event ExpertiseVerified(
address indexed account,
bytes32 indexed domainId,
uint8 score,
address indexed verifier
);
/**
* @dev Creates a new expertise domain
* @param name Human-readable name
* @param description Domain description
* @param verifiers Initial addresses that can verify expertise
* @return domainId The unique identifier of the created domain
*/
function createDomain(
string calldata name,
string calldata description,
address[] calldata verifiers
) external returns (bytes32 domainId);
/**
* @dev Verifies expertise for an address
* @param account The address receiving verification
* @param domainId The expertise domain identifier
* @param score The expertise score (0-100)
* @param validUntil Timestamp when the expertise verification expires
* @param metadata Additional verification metadata
* @return success Whether verification was successful
*/
function verifyExpertise(
address account,
bytes32 domainId,
uint8 score,
uint256 validUntil,
bytes calldata metadata
) external returns (bool success);
/**
* @dev Gets the expertise score for an address in a specific domain
* @param account The address to check
* @param domainId The expertise domain identifier
* @return score The expertise score (0-100)
* @return validUntil Timestamp when the expertise verification expires
*/
function getExpertise(address account, bytes32 domainId)
external view returns (uint8 score, uint256 validUntil);
/**
* @dev Gets all expertise domains for an address
* @param account The address to check
* @return domainIds Array of expertise domain identifiers
*/
function getExpertiseDomains(address account) external view returns (bytes32[] memory domainIds);
/**
* @dev Checks if an address is a verifier for a domain
* @param account The address to check
* @param domainId The expertise domain identifier
* @return isVerifier Whether the address is a verifier
*/
function isVerifier(address account, bytes32 domainId) external view returns (bool isVerifier);
}Interface for storing and querying historical governance data.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
interface IHistoricalDataRegistry {
/**
* @dev Structure defining historical data for a proposal
* @param proposalId The proposal identifier
* @param contextId The context identifier
* @param moduleId The voting module identifier
* @param participationRate The participation rate achieved (in basis points)
* @param executionTime Time from proposal creation to execution (in seconds)
* @param succeeded Whether the proposal succeeded
* @param executed Whether the proposal was executed
*/
struct ProposalHistory {
bytes32 proposalId;
bytes32 contextId;
bytes32 moduleId;
uint256 participationRate;
uint256 executionTime;
bool succeeded;
bool executed;
}
/**
* @dev Emitted when historical data is recorded for a proposal
* @param proposalId The proposal identifier
* @param dataRoot The Merkle root of the historical data
*/
event HistoricalDataRecorded(bytes32 indexed proposalId, bytes32 dataRoot);
/**
* @dev Records historical data for a proposal
* @param proposalId The proposal identifier
* @param contextId The context identifier
* @param moduleId The voting module identifier
* @param participationRate The participation rate achieved
* @param executionTime Time from proposal creation to execution
* @param succeeded Whether the proposal succeeded
* @param executed Whether the proposal was executed
* @return dataRoot The Merkle root of the recorded data
*/
function recordHistory(
bytes32 proposalId,
bytes32 contextId,
bytes32 moduleId,
uint256 participationRate,
uint256 executionTime,
bool succeeded,
bool executed
) external returns (bytes32 dataRoot);
/**
* @dev Gets historical data for a proposal
* @param proposalId The proposal identifier
* @return history The proposal history
*/
function getHistory(bytes32 proposalId) external view returns (ProposalHistory memory history);
/**
* @dev Gets the average performance metrics for a voting module
* @param moduleId The voting module identifier
* @param categoryId The proposal category identifier (optional, 0 for all categories)
* @return avgParticipation Average participation rate
* @return avgExecutionTime Average execution time
* @return successRate Success rate (percentage of successful proposals)
*/
function getModulePerformance(bytes32 moduleId, bytes32 categoryId)
external view returns (
uint256 avgParticipation,
uint256 avgExecutionTime,
uint256 successRate
);
/**
* @dev Verifies if a piece of historical data belongs to a proposal
* @param proposalId The proposal identifier
* @param historyData The historical data to verify
* @param proof The Merkle proof
* @return isValid Whether the data is valid
*/
function verifyHistoricalData(
bytes32 proposalId,
bytes calldata historyData,
bytes32[] calldata proof
) external view returns (bool isValid);
}Interface for privacy-preserving voting mechanisms.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
interface IPrivacyModule {
/**
* @dev Emitted when a commitment is made for a private vote
* @param proposalId The proposal identifier
* @param voter The address of the voter
* @param commitment The vote commitment
*/
event VoteCommitmentMade(
bytes32 indexed proposalId,
address indexed voter,
bytes32 commitment
);
/**
* @dev Emitted when a vote is revealed
* @param proposalId The proposal identifier
* @param voter The address of the voter
* @param support Whether the vote supports the proposal
* @param weight The weight of the vote
*/
event VoteRevealed(
bytes32 indexed proposalId,
address indexed voter,
bool support,
uint256 weight
);
/**
* @dev Commits to a vote without revealing the choice
* @param proposalId The proposal identifier
* @param commitment The vote commitment (keccak256 hash)
* @return success Whether the commitment was successful
*/
function commitVote(bytes32 proposalId, bytes32 commitment) external returns (bool success);
/**
* @dev Reveals a previously committed vote
* @param proposalId The proposal identifier
* @param support Whether the vote supports the proposal
* @param salt The salt used in the commitment
* @param expertiseProof Proof of expertise (if applicable)
* @return weight The weight assigned to the vote
*/
function revealVote(
bytes32 proposalId,
bool support,
bytes32 salt,
bytes calldata expertiseProof
) external returns (uint256 weight);
/**
* @dev Checks if the commit phase for a proposal is active
* @param proposalId The proposal identifier
* @return isActive Whether the commit phase is active
*/
function isCommitPhaseActive(bytes32 proposalId) external view returns (bool isActive);
/**
* @dev Checks if the reveal phase for a proposal is active
* @param proposalId The proposal identifier
* @return isActive Whether the reveal phase is active
*/
function isRevealPhaseActive(bytes32 proposalId) external view returns (bool isActive);
/**
* @dev Gets the number of committed votes for a proposal
* @param proposalId The proposal identifier
* @return count The number of committed votes
*/
function getCommitCount(bytes32 proposalId) external view returns (uint256 count);
/**
* @dev Gets the number of revealed votes for a proposal
* @param proposalId The proposal identifier
* @return count The number of revealed votes
*/
function getRevealCount(bytes32 proposalId) external view returns (uint256 count);
}The standard defines several events to enable clients to track governance activities:
ProposalCreated: Emitted when a new proposal is createdProposalStateChanged: Emitted when a proposal's state changesVoteCast: Emitted when a vote is cast on a proposalCategoryCreated: Emitted when a new context category is createdContextCreated: Emitted when a context is created for a proposalModuleRegistered: Emitted when a new voting module is registeredExpertiseVerified: Emitted when expertise is verified for an addressHistoricalDataRecorded: Emitted when historical data is recorded for a proposalVoteCommitmentMade: Emitted when a commitment is made for a private voteVoteRevealed: Emitted when a vote is revealed
The standard defines key data structures to represent governance concepts:
Context: Defines the context of a proposal, including category, urgency, required expertise, and stakeholdersVotingModule: Defines a voting module, including suitable categories, urgency levels, and whether it uses expertise weightingExpertiseDomain: Defines an expertise domain, including name, description, and verifiersProposalHistory: Defines historical data for a proposal, including participation rate, execution time, and outcome
To minimize gas costs, the standard includes:
- Merkle tree storage for historical data
- Lazy evaluation of voting power
- Batch processing of related proposals
- Optimistic execution for low-risk actions
- Optional off-chain computation with on-chain verification
The DGOP standard uses a three-layer architecture to balance on-chain security with off-chain efficiency:
- Core Protocol Layer (On-Chain): Essential governance functions that must be secure and trustless
- Optimization Layer (Hybrid): Components that benefit from off-chain computation with on-chain verification
- User Experience Layer (Off-Chain): Components that enhance usability without requiring on-chain operations
This architecture allows DAOs to:
- Maintain secure on-chain governance for critical operations
- Leverage off-chain computation for complex optimization tasks
- Provide user-friendly interfaces without incurring excessive gas costs
The standard uses proposal contexts to enable adaptive governance:
- Each proposal is categorized based on its nature (e.g., technical, financial, community)
- Additional context parameters like urgency and required expertise are defined
- The optimal voting module is selected based on the context
- Historical performance data is used to improve module selection over time
This approach ensures that voting mechanisms match the specific needs of each proposal.
The standard incorporates expertise and reputation while preserving inclusivity:
- Expertise domains are clearly defined with transparent verification processes
- Expertise scores can influence voting power when appropriate
- Expertise verification requires multiple attestations to prevent gaming
- Expertise scores naturally decay over time to encourage ongoing contribution
- Maximum influence caps prevent expertise-based capture
The standard supports varying levels of voter privacy:
- Commit-reveal voting for sensitive proposals
- Zero-knowledge proofs for expertise verification without revealing identity
- Delayed revelation of voter identity to prevent influence
The standard uses historical governance data to improve over time:
- Governance outcomes are recorded with cryptographic integrity
- Performance metrics are analyzed to optimize module selection
- Feedback loops ensure continuous improvement
- Challenge periods allow for dispute resolution
DGOP is designed to be compatible with existing Ethereum standards and governance systems:
- ERC-20 Compatibility: Works with standard token-based voting
- ERC-721 Compatibility: Supports NFT-based governance rights
- ERC-1155 Compatibility: Works with multi-token governance
- Governor Compatibility: Can extend OpenZeppelin Governor pattern
- Upgrade Paths: Existing DAOs can migrate incrementally
The reference implementation includes:
DGOPCore.sol: Main contract implementing IDGOPProposalContextManager.sol: Implements IProposalContextVotingModuleRegistry.sol: Implements IVotingModuleRegistryBasicVotingModule.sol: Simple implementation of IVotingModuleExpertiseRegistry.sol: Implements IExpertiseRegistryHistoricalDataRegistry.sol: Implements IHistoricalDataRegistryCommitRevealPrivacyModule.sol: Implements IPrivacyModule
The reference implementation includes these sample voting modules:
TokenWeightedVoting.sol: Standard one-token-one-voteQuadraticVoting.sol: Square root of token balance for voting powerExpertiseWeightedVoting.sol: Voting power weighted by expertiseHybridVoting.sol: Combination of token and expertise weightingStakeholderPriorityVoting.sol: Prioritizes directly affected stakeholders
The standard addresses several security concerns:
- Module Isolation: Each voting module is isolated to prevent cross-module attacks
- Immutable Registry: Core interfaces are immutable to prevent governance attacks
- Fail-Safe Execution: Proposals use timelock mechanisms to prevent flash attacks
- Reentrancy Protection: All state-changing functions are protected against reentrancy
- Integer Overflow Prevention: Using Solidity 0.8+ with built-in overflow checks
- Multiple Attestations: Expertise claims require multiple attestations
- Temporal Decay: Expertise scores decay over time to prevent gaming
- Verifier Rotation: Verifiers are regularly rotated to prevent collusion
- Challenge Mechanism: Expertise claims can be challenged by community members
- Bounded Influence: Maximum influence caps prevent expertise-based capture
- Merkle Tree Storage: Historical data is stored using Merkle trees for integrity
- Challenge Period: Disputed data can be challenged during a verification period
- Cryptographic Commitments: Off-chain data is committed on-chain for verification
- Archivist Selection: Verifiable random functions select data archivists to prevent manipulation
The standard acknowledges these limitations and areas for future work:
- Gas Cost Optimization: Further optimization for large DAOs
- Layer 2 Integration: Adapting the standard for specific L2 environments
- Cross-Chain Governance: Extending the standard for cross-chain operations
- AI-Assisted Optimization: Exploring AI for governance optimization
- Formal Verification: Formal verification of critical components
| Feature | DGOP | ERC-20 | ERC-721 | ERC-1155 | ERC-3000 | EIP-4824 |
|---|---|---|---|---|---|---|
| Token-based Voting | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Context-aware Voting | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Expertise Weighting | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Historical Optimization | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Privacy Preservation | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Gas Optimization | ✅ | N/A | N/A | N/A | ✅ | N/A |
| Interoperability | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Adaptability | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
The Dynamic Governance Optimization Protocol (DGOP) addresses a critical gap in DAO governance by enabling adaptive, context-aware voting mechanisms. By standardizing the interfaces for this approach, DGOP facilitates interoperability while preserving flexibility for DAO-specific customization. This standard will enable DAOs to make more effective decisions by matching voting mechanisms to the specific needs of each proposal, incorporating appropriate expertise, and continuously optimizing based on historical performance.