Add ERC721Votes for NFT-based governance (#2944)
Co-authored-by: Francisco Giordano <frangio.1@gmail.com> Co-authored-by: Hadrien Croubois <hadrien@openzeppelin.com>
This commit is contained in:
@ -1,6 +1,6 @@
|
||||
= ERC721
|
||||
|
||||
We've discussed how you can make a _fungible_ token using xref:erc20.adoc[ERC20], but what if not all tokens are alike? This comes up in situations like *real estate* or *collectibles*, where some items are valued more than others, due to their usefulness, rarity, etc. ERC721 is a standard for representing ownership of xref:tokens.adoc#different-kinds-of-tokens[_non-fungible_ tokens], that is, where each token is unique.
|
||||
We've discussed how you can make a _fungible_ token using xref:erc20.adoc[ERC20], but what if not all tokens are alike? This comes up in situations like *real estate*, *voting rights*, or *collectibles*, where some items are valued more than others, due to their usefulness, rarity, etc. ERC721 is a standard for representing ownership of xref:tokens.adoc#different-kinds-of-tokens[_non-fungible_ tokens], that is, where each token is unique.
|
||||
|
||||
ERC721 is a more complex standard than ERC20, with multiple optional extensions, and is split across a number of contracts. The OpenZeppelin Contracts provide flexibility regarding how these are combined, along with custom useful extensions. Check out the xref:api:token/ERC721.adoc[API Reference] to learn more about these.
|
||||
|
||||
|
||||
@ -119,13 +119,13 @@ contract MyToken is ERC20, ERC20Permit, ERC20Votes, ERC20Wrapper {
|
||||
}
|
||||
```
|
||||
|
||||
NOTE: Voting power could be determined in different ways: multiple ERC20 tokens, ERC721 tokens, sybil resistant identities, etc. All of these options are potentially supported by writing a custom Votes module for your Governor.
|
||||
NOTE: Voting power could be determined in different ways: multiple ERC20 tokens, ERC721 tokens, sybil resistant identities, etc. All of these options are potentially supported by writing a custom Votes module for your Governor. The only other source of voting power available in OpenZeppelin Contracts currently is xref:api:token/ERC721.adoc#ERC721Votes[`ERC721Votes`].
|
||||
|
||||
=== Governor
|
||||
|
||||
Initially, we will build a Governor without a timelock. The core logic is given by the Governor contract, but we still need to choose: 1) how voting power is determined, 2) how many votes are needed for quorum, and 3) what options people have when casting a vote and how those votes are counted. Each of these aspects is customizable by writing your own module, or more easily choosing one from OpenZeppelin Contracts.
|
||||
Initially, we will build a Governor without a timelock. The core logic is given by the Governor contract, but we still need to choose: 1) how voting power is determined, 2) how many votes are needed for quorum, 3) what options people have when casting a vote and how those votes are counted, and 4) what type of token should be used to vote. Each of these aspects is customizable by writing your own module, or more easily choosing one from OpenZeppelin Contracts.
|
||||
|
||||
For 1) we will use the GovernorVotes module, which hooks to an ERC20Votes instance to determine the voting power of an account based on the token balance they hold when a proposal becomes active. This module requires as a constructor parameter the address of the token.
|
||||
For 1) we will use the GovernorVotes module, which hooks to an IVotes instance to determine the voting power of an account based on the token balance they hold when a proposal becomes active. This module requires as a constructor parameter the address of the token.
|
||||
|
||||
For 2) we will use GovernorVotesQuorumFraction which works together with ERC20Votes to define quorum as a percentage of the total supply at the block a proposal’s voting power is retrieved. This requires a constructor parameter to set the percentage. Most Governors nowadays use 4%, so we will initialize the module with parameter 4 (this indicates the percentage, resulting in 4%).
|
||||
|
||||
@ -152,7 +152,7 @@ import "./governance/extensions/GovernorVotesQuorumFraction.sol";
|
||||
import "./governance/extensions/GovernorTimelockControl.sol";
|
||||
|
||||
contract MyGovernor is Governor, GovernorCompatibilityBravo, GovernorVotes, GovernorVotesQuorumFraction, GovernorTimelockControl {
|
||||
constructor(ERC20Votes _token, TimelockController _timelock)
|
||||
constructor(IVotes _token, TimelockController _timelock)
|
||||
Governor("MyGovernor")
|
||||
GovernorVotes(_token)
|
||||
GovernorVotesQuorumFraction(4)
|
||||
@ -294,7 +294,7 @@ This will create a new proposal, with a proposal id that is obtained by hashing
|
||||
|
||||
=== Cast a Vote
|
||||
|
||||
Once a proposal is active, stakeholders can cast their vote. This is done through a function in the Governor contract that users can invoke directly from a governance UI such as Tally.
|
||||
Once a proposal is active, stakeholders can cast their vote. This is done through a function in the Governor contract that users can invoke directly from a governance UI such as Tally.
|
||||
|
||||
image::tally-vote.png[Voting in Tally]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user