Proposal Manager Settings

The Proposal Manager supports multi- voting token governance systems, and tokens can be of the ERC20, ERC1155, Item standards, and ETH. This allows for highly customizable, multi-level governmental dynamics. A root layer organization could create a single governance token and use that to vote on all proposals; or, it could utilize multiple tokens, perhaps of different standards and different voting power weights and abilities.

The list of tokens that will be supported by a Proposal Manager must be passed when the Proposal Manager is initialized, although the list can be updated later using the setVotingRules function.

Proposal Governance Composition

A root layer organization's governance rule-set is composed by the following six elements:

  • Supported tokens -> the organization can define how many and which tokens can be used to vote on a proposal

  • weights -> this parameter defines, for each supported token, its voting weight. The weight is calculated as number of votes * weight.

  • creationRules -> this smart contract defines the rules that state who can create a Proposal. You can have it so every wallet in existence can create a Proposal, or just a select few.

  • triggeringRules -> this smart contract defines the rules that state who can terminate a Proposal; i.e, allow the Proposal's code to be executed if the Proposal passes. You can have it so every wallet in existence can terminate a Proposal, or just a select few.

  • canTerminate -> this is one or more contracts that define the rules that state whether or not a Proposal can be voted on; and, if not, whether the Proposal can end. You can define, for example, a number blocks for which votes will be accepted.

  • validators -> this defines one or more contracts that define the rules that state whether a Proposal can be passed or not according to the established parameters, such as majority rule; a quorum; etc.

Vertical Permission Level

In addition to a Horizontal Level, an Organization can have an extendable Vertical (i.e Permission) Level. This allows for it to be managed at both the Root and Governance Layers using these permission structures:

  • Host Management Only the host can make changes. This should only be used for the Root Layer.

  • Host and Holder Management Both the host and governance token holders must approve changes.

  • Holder Management Only governance token holders can make changes.

The Vertical Permission structure is defined by the Proposal Manager, and can be applied to Proposal creation, control, and termination. In particular, it is defined by the combination of the following Proposal Manager rules:

  • creationRules

  • triggeringRules

  • canTerminateAddress

  • validators

Why is it defined by the Proposal Manager?

As the vehicle through which the Organization (at both Layers) can make various decisions, such as adding / replacing the Root Layer core's code, or governing subDAOs at the Governance layer; vertical permission levels can be defined here.

Look here for more info and example of Vertical Permission level configuration.

Proposal Manager Initialization

The six governance elements must be passed when the Proposal Manager is initialized, although they be updated later via the setConfiguration function, which any of the organization's active components can call.

The data to pass are as follows:

function _lazyInit(bytes memory lazyInitData) internal override returns(bytes memory) {
    (_hostIsProposalCommand, lazyInitData) = abi.decode(lazyInitData, (bool, bytes));
    if(lazyInitData.length > 0) {
        ProposalsManagerLibrary.setConfiguration(_configuration, abi.decode(lazyInitData, (ProposalConfiguration)));
    }
    return "";
}

struct ProposalConfiguration {
    address[] collections;
    uint256[] objectIds;
    uint256[] weights;
    address creationRules;
    address triggeringRules;
    address[] canTerminateAddresses;
    address[] validatorsAddresses;
}
  • bool _hostIsProposalCommand -> it defines if the Proposal Manager is hosted (host parameter at LazyInitCapableElement) by a smart contract overriding some Proposal Manager functions (true) or not (false). Look here for more details.

  • collections address[] -> this represents the Item and ERC1155 Collection addresses that are supported for voting on Proposals; for ERC20 tokens, pass it as empty.

  • objectIds uint256[] -> this represents, for each _collection, the object id(s) of the specific ERC1155 and Item tokens of the Collections that can vote on a proposal. For ERC20 tokens, the address must be first converted in a uint parameter, and then passed in this object id one (and, as mentioned above, the collection address must be passed as empty).

  • weights uint256[] -> this array contains the weights of the object ids.

  • creationRules -> this is the creationRules contract address. You can pass it as address(0) to let everyone create a Proposal.

  • triggeringRules -> this is the triggeringRules contract address. You can pass it as address(0) to let everyone terminate a Proposal.

  • canTerminateAddresses address[] -> this array contains all the canTerminate contract addresses. If you pass it empty, the Proposal can be terminated at any time.

  • validators[] -> this array contains all the validators contracts addresses. If you pass it empty, the Proposal ever passes and therefore the code can be executed.

Proposal manager initialization follows the logic explained here, which is used for all organization components.

At the moment of initialization, and later using setConfiguration, the length of the Collection array must match the lengths of the objectIds and weights arrays. This means that the link between a Collection, a token (objectId) and its relative weight is always consistent and defined.

To work properly, the Proposal Manager must have at least: one Collection (empty for ERC20 addresses) with an objectId and a weight.

Tokens Weight in a Proposal Vote

The weight of each token is defined at the moment of Proposal Manager initialization. Each token (object id) of a Collection (populated for ERC1155 and Items; empty for ERC20) corresponds to a specific weight in uint256 form. That defined weight is valid for all created Proposals; i.e. it is not possible to define different weights for the same token in the case of multiple Proposals.

For example: token1 -> weight = 1; token 2 -> weight = 5; token 3 > weight = 10.

A vote expressed with 100 token1 is worth 100; a vote expressed with 100 token2 is worth 200; and a vote expressed with 100 token3 is worth 1000.

All supported Collections and objectIds can be used to vote on Proposals with their specific weights, further opening up the possibility for deep granular governmental customization--and, of course, using ERC1155 and Items, you can vote in batch mode using batch transfer methods.

creationRules and triggeringRules

Passing these allows you define who can create and terminate Proposals. What does it mean?

The creationRules can be used to set who can call the batchCreate to create a Proposal and the triggeringRules can be used to set who can call the terminate function to terminate the Proposal. For example, in both cases, you can define a specific address, multiple addresses, or everyone.

A root layer organization that wants very simple governance rules can just have zero or a few basic creationRules and triggeringRules contract(s), whereas a root layer organization that wants complex governance rules will have one or more sophisticated creationRules and triggeringRules contract(s).

Built with entirely customizable smart contracts, these governance rules are entirely customizable.

Both the creationRules and triggeringRules can be passed as address(0) to let everyone creates and terminate Proposals.

canTerminate and validators

A root layer organization that wants very simple governance rules can just have one or a few basic canTerminate and validators contract(s), whereas a root layer organization that wants complex governance rules will have one or more sophisticated canTerminate and validators contract(s).

If the canTerminate ispassed empty, the Proposal can be terminated at any time. If the validators is passed empty, the Proposal can be validated at any time.

canTerminate

canTerminate is a list of rules that tell the ProposalManager whether or not a Proposal can still be voted on.

For example, the concept of an allowed voting duration defined by an end block doesn’t exist in the Proposal Manager's logic, but can be defined in the canTerminate rules.

canTerminate contracts follow an “or” logic, which means that at least one must return true to terminate the Proposal so that it cannot be voted on anymore. For example, if a root layer organization has 5 different contracts composing the canTerminate rules, if only one of these 5 returns true, the Proposal cannot be voted on anymore.

The “or” logic implies that the canTerminate contracts can be composed of multiple contracts containing heterogeneous rules. So, as another example, one canTerminate contract can be written so that a Proposal can be voted on for 100 blocks.

This means that from block 0 to block 100, canTerminate will return false, because the Proposal can still be voted on (and therefore cannot be terminated); but after block 100, canTerminate will return true, because the Proposal can no longer be voted on (and therefore can be terminated).

validators

validators is a list of rules that tell the Proposal Manager whether or not a Proposal has passed.

For example, the concept of a required quorum (i.e a minimum number of votes required for a Proposal to pass) and of a required relative or absolute majority) are not in the Proposal Manager's logic, but can be part of the validators rules.

The validators contracts follow an "and" logic, which means all must return true for the Proposal to pass.

For example, if a Root Layer has five validators contracts, and all return true for a Proposal, the Proposal will pass. But if even only one returns false, it will not pass.

The logic also implies that the validators contracts can be composed of multiple contracts containing homogeneous rules.

Let's say that the contract that returned false was written so that the Proposal had a required quorum of 100k accept votes. Because it returned false, less than 100k accept votes must have been cast; if more than 100k had been, it would have returned true.

Let's also say that one of the contracts that returned true was written so that the Proposal had a required quorum of 80k votes. Because it returned true, but the other contract returned false, at least 80k but less than 100k votes must have been cast.

Of course, governance rule checks are constantly performed by the ProposalManager on all major actions involving a Proposal and vote management, such as:

  • Vote casting

  • Vote withdrawal

  • Vote moving

  • Proposal termination and execution of Proposal code

If passed, the governance rules contracts must integrate the method check(IProposalsManager manager, bytes32 proposalId, IProposalsManager.Proposal calldata proposal, address from, address voter) contained in the IProposalChecker interface provided by the IProposalManager contract.

In this way, checks can be performed by the Proposal Manager.

Why Define the Governance Rules (and Therefore the Vertical Permission Layer) by External Smart Contracts From the ProposalManager ?

This allows you to not be constrained by hardwired governance rules in the Proposal Manager's code, but instead be free to write your own rules according to your unique needs. It also makes easier updating and changing the governance rules.

Additionally, because they are external contracts, they can be reused by different Organizations, or there could be Factories or an application that makes contracts containing governance rules available so that other Organizations can take and use them.

Last updated