Governance

The Eco DeFi protocol is governed and upgraded by ESG token-holders, using three distinct components; the ESG token, governance module (Governor Bravo), and Timelock. Together, these contracts allow the community to propose, vote, and implement changes through the administrative functions of a eToken or the Esgtroller. Proposals can modify system parameters, support new markets, or add entirely new functionality to the protocol.

ESG token-holders can delegate their voting rights to themselves, or an address of their choice. Addresses delegated at least 300,000 ESG can create governance proposals; any address can lock 100 ESG to create an Autonomous Proposal, which becomes a governance proposal after being delegated 300,000 ESG. When a governance proposal is created, it enters a 2 day review period, after which voting weights are recorded and voting begins. Voting lasts for 3 days; if a majority, and at least 500,000 votes are cast for the proposal, it is queued in the Timelock, and can be implemented 2 days later. In total, any change to the protocol takes at least one week.

ESG

ESG is an BEP20 token that allows the owner to delegate voting rights to any address, including their own address. Changes to the owner’s token balance automatically adjust the voting rights of the delegate.

Delegate

Delegate votes from the sender to the delegatee. Users can delegate to 1 address at a time, and the number of votes added to the delegatee’s vote count is equivalent to the balance of ESG in the user’s account. Votes are delegated from the current block and onward, until the sender delegates again, or transfers their ESG.

ESG

function delegate(address delegatee)
  • delegatee: The address in which the sender wishes to delegate their votes to.

  • msg.sender: The address of the ESG token holder that is attempting to delegate their votes.

  • RETURN: No return, reverts on error.

Esg Eco = Esg(0x123...); // contract address

eco.delegate(delegateeAddress);

Delegate By Signature

Delegate votes from the signatory to the delegatee. This method has the same purpose as Delegate but it instead enables offline signatures to participate in Eco DeFi governance vote delegation. For more details on how to create an offline signature, review EIP-712.

ESG

function delegateBySig(address delegatee, uint nonce, uint expiry, uint8 v, bytes32 r, bytes32 s)
  • delegatee: The address in which the sender wishes to delegate their votes to.

  • nonce: The contract state required to match the signature. This can be retrieved from the contract’s public nonces mapping.

  • expiry: The time at which to expire the signature. A block timestamp as seconds since the unix epoch (uint).

  • v: The recovery byte of the signature.

  • r: Half of the ECDSA signature pair.

  • s: Half of the ECDSA signature pair.

  • RETURN: No return, reverts on error.

Eco Eco = Eco(0x123...); // contract address

eco.delegateBySig(delegateeAddress, nonce, expiry, v, r, s);

Get Current Votes

Get the balance of votes for an account as of the current block.

ESG

function getCurrentVotes(address account) returns (uint96)
  • account: Address of the account in which to retrieve the number of votes.

  • RETURN: The number of votes (integer).

Eco Eco = Eco(0x123...); // contract address

uint votes = eco.getCurrentVotes(0xabc...);

Get Prior Votes

Gets the prior number of votes for an account at a specific block number. The block number passed must be a finalized block or the function will revert.

ESG

function getPriorVotes(address account, uint blockNumber) returns (uint96)
  • account: Address of the account in which to retrieve the prior number of votes.

  • blockNumber: The block number at which to retrieve the prior number of votes.

  • RETURN: The number of prior votes.

Eco Eco = Eco(0x123...); // contract address

uint priorVotes = eco.getPriorVotes(account, blockNumber);

Key Events

Governor Bravo

Governor Bravo is the governance module of the protocol; it allows addresses with more than 300,000 ESG to propose changes to the protocol. Addresses that held voting weight, at the start of the proposal, invoked through the get prior votes function, can submit their votes during a 3 day voting period. If a majority, and at least 500,000 votes are cast for the proposal, it is queued in the Timelock, and can be implemented after 2 days.

Quorum Votes

The required minimum number of votes to support the proposal to be succeeded.

Governor Bravo

function quorumVotes() public pure returns (uint)
  • RETURN: The minimum number of votes required for a proposal to succeed.

GovernorBravo gov = GovernorBravo(0x123...); // contract address
uint quorum = gov.quorumVotes();

Proposal Threshold

The minimum number of votes required for an account to create a proposal. This can be changed through governance.

Governor Bravo

function proposalThreshold() returns (uint)
  • RETURN: The minimum number of votes required for an account to create a proposal.

GovernorBravo gov = GovernorBravo(0x123...); // contract address
uint threshold = gov.proposalThreshold();

Proposal Max Operations

The maximum number of actions that can be included in a proposal. Actions are function calls that will be made when a proposal is succeeded and executed.

Governor Bravo

function proposalMaxOperations() returns (uint)
  • RETURN: The maximum number of actions that can be included in a proposal.

GovernorBravo gov = GovernorBravo(0x123...); // contract address

uint operations = gov.proposalMaxOperations();

Voting Delay

The number of BSC blocks to wait before voting on a proposal may begin. This value is added to the current block number when a proposal is created. This can be changed through governance.

Governor Bravo

function votingDelay() returns (uint)
  • RETURN: Number of blocks to wait before voting on a proposal may begin.

GovernorBravo gov = GovernorBravo(0x123...); // contract address
uint blocks = gov.votingDelay();

Voting Period

The duration of voting on a proposal, in BSC blocks. This can be changed through governance.

Governor Bravo

function votingPeriod() returns (uint)
  • RETURN: The duration of voting on a proposal, in BSC blocks.

GovernorBravo gov = GovernorBravo(0x123...); // contract address

uint blocks = gov.votingPeriod();

Propose

Create a Proposal to change the protocol. E.g., A proposal can set a eToken's interest rate model or risk parameters on the Esgtroller.

Proposals will be voted on by delegated voters. If there is sufficient support before the voting period ends, the proposal shall be automatically enacted. Enacted proposals are queued and executed in the Eco DeFi Timelock contract. The sender must hold more ESG than the current proposal threshold (proposalThreshold()) as of the immediately previous block. If the threshold is 300,000 ESG, the sender must have been delegated more than 1% of all Eco in order to create a proposal. The proposal can have up to 10 actions (based on proposalMaxOperations()).

The proposer cannot create another proposal if they currently have a pending or active proposal. It is not possible to queue two identical actions in the same block (due to a restriction in the Timelock), therefore actions in a single proposal must be unique, and unique proposals that share an identical action must be queued in different blocks.

Governor Bravo

function propose(address[] memory targets, uint[] memory values, string[] memory signatures, bytes[] memory calldatas, string memory description) returns (uint)
  • targets: The ordered list of target addresses for calls to be made during proposal execution. This array must be the same length as all other array parameters in this function.

  • values: The ordered list of values (i.e. msg.value) to be passed to the calls made during proposal execution. This array must be the same length as all other array parameters in this function.

  • signatures: The ordered list of function signatures to be passed during execution. This array must be the same length as all other array parameters in this function.

  • calldatas: The ordered list of data to be passed to each individual function call during proposal execution. This array must be the same length as all other array parameters in this function.

  • description: A human readable description of the proposal and the changes it will enact.

  • RETURN: The ID of the newly created proposal.

GovernorBravo gov = GovernorBravo(0x123...); // contract address

uint proposalId = gov.propose(targets, values, signatures, calldatas, description);

Queue

After a proposal has succeeded, it is moved into the Timelock waiting period using this function. The waiting period (e.g. 2 days) begins when this function is called. The queue function can be called by any BSC address.

Governor Bravo

function queue(uint proposalId)
  • proposalId: ID of a proposal that has succeeded.

  • RETURN: No return, reverts on error.

GovernorBravo gov = GovernorBravo(0x123...); // contract address

gov.queue(proposalId);

Execute

After the Timelock waiting period has elapsed, a proposal can be executed using this function, which applies the proposal changes to the target contracts. This will invoke each of the actions described in the proposal.

The execute function can be called by any BSC address.

Note: this function is payable, so the Timelock contract can invoke payable functions that were selected in the proposal.

Governor Bravo

function execute(uint proposalId) payable
  • proposalId: ID of a succeeded proposal to execute.

  • RETURN: No return, reverts on error.

GovernorBravo gov = GovernorBravo(0x123...); // contract address

gov.execute(proposalId).value(999).gas(999)();

Cancel

A proposal is eligible to be cancelled at any time prior to its execution, including while queued in the Timelock, using this function.

The cancel function can be called by the proposal creator, or any BSC address, if the proposal creator fails to maintain more delegated votes than the proposal threshold (e.g. 100,000).

Governor Bravo

function cancel(uint proposalId)
  • proposalId: ID of a proposal to cancel. The proposal cannot have already been executed.

  • RETURN: No return, reverts on error.

GovernorBravo gov = GovernorBravo(0x123...); // contract address

gov.cancel(proposalId);

Get Actions

Gets the actions of a selected proposal. Pass a proposal ID and get the targets, values, signatures and calldatas of that proposal.

Governor Bravo

function getActions(uint proposalId) returns (uint proposalId) public view returns (address[] memory targets, uint[] memory values, string[] memory signatures, bytes[] memory calldatas)
  • proposalId: ID of a proposal in which to get its actions.

  • RETURN: Reverts if the proposal ID is invalid. If successful, the following 4 references are returned.

  1. Array of addresses of contracts the proposal calls.

  2. Array of unsigned integers the proposal uses as values.

  3. Array of strings of the proposal’s signatures.

  4. Array of calldata bytes of the proposal.

GovernorBravo gov = GovernorBravo(0x123...); // contract address

uint proposalId = 123;

Get Receipt

Gets a proposal ballot receipt of the indicated voter.

Governor Bravo

function getReceipt(uint proposalId, address voter) returns (Receipt memory)
  • proposalId: ID of the proposal in which to get a voter’s ballot receipt.

  • voter: Address of the account of a proposal voter.

  • RETURN: Reverts on error. If successful, returns a Receipt struct for the ballot of the voter address.

GovernorBravo gov = GovernorBravo(0x123...); // contract address

Receipt ballot = gov.getReceipt(proposalId, voterAddress);

State

Gets the proposal state for the specified proposal. The return value, ProposalState is an enumerated type defined in the Governor Bravo contract.

Governor Bravo

function state(uint proposalId) returns (ProposalState)
  • proposalId: ID of a proposal in which to get its state.

  • RETURN: Enumerated type ProposalState. The types are Pending, Active, Canceled, Defeated, Succeeded, Queued, Expired, andExecuted.

GovernorBravo gov = GovernorBravo(0x123...); // contract address

GovernorBravo.ProposalState state = gov.state(123);

Cast Vote

Cast a vote on a proposal. The account's voting weight is determined by the number of votes the account had delegated to it at the time the proposal state became active.

Governor Bravo

function castVote(uint proposalId, uint8 support)
  • proposalId: ID of a proposal in which to cast a vote.

  • support: An integer of 0 for against, 1 for in-favor, and 2 for abstain.

  • RETURN: No return, reverts on error.

GovernorBravo gov = GovernorBravo(0x123...); // contract address

gov.castVote(proposalId, 1);

Cast Vote With Reason

Cast a vote on a proposal with a reason attached to the vote.

Governor Bravo

function castVoteWithReason(uint proposalId, uint8 support, string calldata reason)
  • proposalId: ID of a proposal in which to cast a vote.

  • support: An integer of 0 for against, 1 for in-favor, and 2 for abstain.

  • reason: A string containing the voter's reason for their vote selection.

  • RETURN: No return, reverts on error.

GovernorBravo gov = GovernorBravo(0x123...); // contract address

gov.castVoteWithReason(proposalId, 2, "I think...");

Cast Vote By Signature

Cast a vote on a proposal. The account's voting weight is determined by the number of votes the account had delegated at the time that proposal state became active. This method has the same purpose as Cast Vote but it instead enables offline signatures to participate in Eco DeFi governance voting. For more details on how to create an offline signature, review EIP-712.

Governor Bravo

function castVoteBySig(uint proposalId, uint8 support, uint8 v, bytes32 r, bytes32 s)
  • proposalId: ID of a proposal in which to cast a vote.

  • support: An integer of 0 for against, 1 for in-favor, and 2 for abstain.

  • v: The recovery byte of the signature.

  • r: Half of the ECDSA signature pair.

  • s: Half of the ECDSA signature pair.

  • RETURN: No return, reverts on error.

GovernorBravo gov = GovernorBravo(0x123...); // contract address

gov.castVoteBySig(proposalId, 0, v, r, s);

Timelock

Each eToken contract and the Esgtroller contract allow the Timelock address to modify them. The Timelock contract can modify system parameters, logic, and contracts in a 'time-delayed, opt-out' upgrade pattern.

The Timelock has a hard-coded minimum delay of 2 days, which is the least amount of notice possible for a governance action. Each proposed action will be published at a minimum of 2 days in the future from the time of announcement.

Major upgrades, such as changing the risk system, may have a 14 day delay. The Timelock is controlled by the governance module; pending and completed governance actions can be monitored on the Timelock Dashboard.

Pause Guardian

The Esgtroller contract designates a Pause Guardian address capable of disabling protocol functionality. Used only in the event of an unforeseen vulnerability, the Pause Guardian has one and only one ability: to disable a select set of functions: Mint, Borrow, Transfer, and Liquidate. The Pause Guardian cannot unpause an action, nor can it ever prevent users from calling Redeem, or Repay Borrow to close positions and exit the protocol.

ESG token-holders designate the Pause Guardian address, which is currently held by Eco DeFi Labs, Inc.

Last updated