This proposal introduces the Soulbound Degradable Governance (SDG) standard, where governance power should be granted as non-transferable tokens that decay over time unless renewed through participation. SDG enables young DAOs to implement merit-based governance by detaching governance power from economic power while on early stages of development.
Motivation
Traditional DAO governance models rely heavily on economic tokens, where voting power is proportional to token holdings. While effective for some use cases, this model risks concentrating power among wealthy members, leading to plutocracy and discouraging participation from smaller stakeholders. Furthermore, it fosters a treasury-centric culture that attracts contributors primarily focused on financial gain, rather than long-term governance or community well-being.
Young DAOs, in particular, need governance models that incentivize active contributions without relying on economic power. This proposal addresses these issues by detaching governance power from economic power and ensuring political power decays if not maintained through ongoing participation. This approach creates a merit-based structure that reflects continuous involvement and reduces the risk of early-stage centralization or dependent on heavy inflationary policies.
Specification
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”,
“RECOMMENDED”, “NOT RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as
described in RFC 2119 and RFC 8174.
This system MUST operate with two distinct tokens types, one representing political power and another representing economic power:
The political power token SHOULD be non-transferable with over-time decayment.
The economic power token supports liquidity and trade, providing the financial utility needed for the DAO’s operations and is RECOMMENDED to be a standard ERC-20 token.
The implementer of this standard MUST:
Override the transfer(...) function for the governance token to block transfers between addresses.
Create a decay mechanism by overriding the getVotes(...) function on parent contract, reducing the token’s voting power over time. This is RECOMMENDED to be a linear or exponential decay formula.
Create the respective Event emissions that track the voting power of addresses.
Contract Interface:
// SPDX-License-Identifier: CC0-1.0
pragmasolidity^0.8.20;interfaceSDG{/**
* @dev Returns the grace period duration before the voting units begins decaying. This period is
* fixed to 90 days. But it can be overridden in derived contracts.
* @return The duration of the grace period in seconds.
*/functiongracePeriod()publicviewvirtualreturns(uint256);/**
* @dev Returns the duration of the decay period during which the voting units decreases. This
* period is fixed to 90 days. But it can be overridden in derived contracts.
* @return The duration of the decay period in seconds.
*/functiondecayPeriod()publicviewvirtualreturns(uint256);/**
* @dev Should be implemented by derived contracts to return the current voting units of an account.
* This function calculates the voting units based on the last time it was updated and decays it
* over time.
* @param account The address to check for voting units.
* @return The current voting units of the account.
*/functiongetVotes(addressaccount)publicviewvirtualreturns(uint256);}
Rationale
The SDG standard ensures flexibility by not being tied to any specific token type, allowing DAOs to implement it with ERC-20, ERC-721, ERC-1155, or other future token standards. This decision maximizes the compatibility and adaptability of the framework across different governance models.
The choice to decouple governance power from economic power aims to provide a practical governance model for young DAOs seeking to prevent early centralization while fostering active participation. Non-transferable governance tokens ensure that only engaged members retain influence, as political power decays over time if not renewed through contributions.
We deliberately avoided incorporating mechanisms like “Game Master mode” for early stages or fixed decayment strategy within the standard to keep the specification minimal and modular. These governance structures should be implemented by individual DAOs if needed, without burdening the core SDG standard with additional complexity. The goal is to provide DAOs with the essential tools to build sustainable, merit-based governance, while leaving room for experimentation and customization at the implementation level.
The inclusion of grace periods and decay periods balances fairness with fluidity, incentivizing active participation while preventing governance stagnation. These mechanics ensure that governance power reflects recent contributions, phasing out inactive members naturally, and maintaining a dynamic, merit-based structure.
Backwards Compatibility
No backward compatibility issues found.
Reference Implementation
// SPDX-License-Identifier: CC0-1.0
pragmasolidity^0.8.20;import{SDG}from"./SDG.sol";// SDG implementation
import{SOULERC721}from"./ERC721/SOULERC721.sol";// Soulbounded ERC721 implementation
import{Ownable}from"@openzeppelin/contracts/access/Ownable.sol";// Oz Ownable contract
/**
* @title Valocracy
* @dev Implements the SDG governance model where governance power decays over time if not actively maintained.
* Only the DAO governance contract, as the owner, can mint new tokens or grant additional governance power.
*/contractValocracyisSDG,SOULERC721,Ownable{// Event emitted when voting units are updated
eventVotingUnitsUpdated(addressindexedaccount,uint256oldVotingUnits,uint256newVotingUnits);// Sequential ID and total supply of tokens
uint256publictotalSupply;// Mapping of addresses to their token IDs
mapping(address=>uint256)private_tokens;/**
* @param _name The name of the ERC721 token.
* @param _symbol The symbols of the ERC721 token.
*/constructor(stringmemory_name,stringmemory_symbol)Ownable(_msgSender())SOULERC721(_name,_symbol){}/**
* @dev See {IERC721Metadata-tokenURI}.
* @notice This function returns a static string as the token URI. In a real implementation, this
* function should return an URI that points to a JSON file with metadata about the token. Or even
* better, a dynamic SVG that displays the governance power of the token holder.
*/functiontokenURI(uint256tokenId)publicpureoverridereturns(stringmemory){return"Custom images or Dynamic NFT that displays the governance power";}/**
* @dev See {ISDG-getVotes}.
*/functiongetVotes(addressaccount)publicviewoverridereturns(uint256){uint256grantedTime=_lastUpdateOf(account);// If no voting units was granted or still in grace period, return all voting units
if(grantedTime==0||block.timestamp<grantedTime+gracePeriod()){return_votingUnitsOf(account);}// Calculate time passed since grace period ended
uint256timeSinceGracePeriod=block.timestamp-(grantedTime+gracePeriod());// If decay period is over, return 0
if(timeSinceGracePeriod>=decayPeriod()){return0;}// Linear decay: Calculate remaining voting units during decay period
uint256decayPercentage=(timeSinceGracePeriod*1e18)/decayPeriod();// Percentage in 18 decimals
uint256remainingVotes=(_votingUnitsOf(account)*(1e18-decayPercentage))/1e18;returnremainingVotes;}/**
* @dev Grants voting units to the specified account by `amount`. Only the contract owner can
* mint new tokens and grant additional votings units. If the users doesn't have a token, one
* will be minted for them.
* @param to The address to mint the new token or grant additional voting units.
* @param amount The amount of voting units to grant alongside the token.
*/functiongrantVotingUnits(addressto,uint256amount)publicvirtualonlyOwner{if(_tokens[to]==0){_mint(to,++totalSupply);_tokens[to]=totalSupply;}uint256votingUnits=getVotes(to);_setVotingUnits(to,votingUnits+amount);emitVotingUnitsUpdated(to,votingUnits,amount);}/**
* @notice Burns an ERC721 token and erases the voting units associated with the token holder.
* @dev The token must exist and be burnable by the token holder or authorized entity.
* @param tokenId The ID of the token to burn.
*/functionburn(uint256tokenId)publicvirtual{addressfrom=ownerOf(tokenId);uint256votingUnits=getVotes(from);_tokens[from]=0;_setVotingUnits(from,0);_burn(tokenId);emitVotingUnitsUpdated(from,votingUnits,0);}}