From f74892b429732bdef676e8eccd857e09100015eb Mon Sep 17 00:00:00 2001 From: Sara Sorici Date: Mon, 1 Sep 2025 10:42:55 +0300 Subject: [PATCH 01/19] docs(governance): update proposal close/query interaction and overview --- docs/governance/governance-interaction.md | 111 ++++---- docs/governance/overview.md | 102 +++++--- .../updated_governance-interaction.md | 247 ++++++++++++++++++ docs/governance/updated_overview.md | 102 ++++++++ 4 files changed, 473 insertions(+), 89 deletions(-) create mode 100644 docs/governance/updated_governance-interaction.md create mode 100644 docs/governance/updated_overview.md diff --git a/docs/governance/governance-interaction.md b/docs/governance/governance-interaction.md index fb838278e..83beded32 100644 --- a/docs/governance/governance-interaction.md +++ b/docs/governance/governance-interaction.md @@ -103,26 +103,32 @@ The `vote_balance` is the amount of stake the address has in the smart contract. ### Closing a proposal -A proposal can be closed only in an epoch that is strictly higher than the end epoch value provided when the proposal was open. +A proposal can be closed only in an epoch that is strictly higher than the end epoch value provided when the proposal was opened. +Closing can only be performed by the account that created the proposal (the issuer). ```rust CloseProposalTransaction { - Sender: + Sender: Receiver: erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrlllsrujgla - Value: 0 EGLD + Value: 0 EGLD GasLimit: 51000000 - Data: "closeProposal" + - "@" + Data: "closeProposal" + + "@" } ``` - -Only the address that created the proposal can call the `closeProposal` function that will also trigger the funds unlocking. As stated in the overview page, if the proposal does not pass, the amount returned will be less with 10 EGLD. - +#### Rules for closing +- Only the issuer can call `closeProposal`. +- If the proposal **passes** → the full proposal fee is refunded. +- If the proposal **fails** or is **vetoed** → the refund is reduced by the `LostProposalFee`. +- Once a proposal is closed, it cannot be reopened. +- - Closing also finalizes the vote tally (the proposal is marked as `Passed` or not, based on the results). + + [comment]: # (mx-context-auto) ### Querying the status of a proposal -The status of a certain proposal can be queried at any time by using the `vm-values/query` REST API endpoints provided by the gateway/API. +The status of any proposal can be queried at any time through `vm-values/query` REST API endpoints provided by the gateway/API. ```bash https://.multiversx.com/vm-values/query @@ -136,48 +142,50 @@ https://.multiversx.com/vm-values/query } ``` -the `nonce` represents the proposal nonce in hex format. The response will contain the following json definition where all fields are base64-encoded: +- The argument `nonce` is the proposal nonce in hex format. +- The response will contain the following json definition where all fields are base64-encoded: ```json { "returnData": [ - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", + "", (amount locked by proposer) + "", (unique identifier of the proposal) + "", (proposal number) + "", (address of the proposer) + "", (epoch when voting starts) + "", (epoch when voting ends) + "", (minimum stake required to reach quorum) + "", (total stake voting YES) + "", (total stake voting NO) + "", (total stake vetoing the proposal) + "", (total stake abstaining) "", "" ] } ``` -Example: +Example response: ```json { "returnData": [ "NjXJrcXeoAAA", (proposal locked amount: 1000 EGLD denominated = 1000 * 10^18) "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABg==", (commit hash: 0x0000...006) "AQ==", (nonce: 1) - "aj88GtqHy9ibm5ePPQlG4aqLhpgqsQWygoTppckLa4M=", (address: erd1dglncxk6sl9a3xumj78n6z2xux4ghp5c92cstv5zsn56tjgtdwpsk46qrs) + "aj88GtqHy9ibm5ePPQlG4aqLhpgqsQWygoTppckLa4M=", (proposer address) "bQ==", (starting epoch: 109) "bg==", (ending epoch: 110) - "ntGU2xmyOMAAAA==", (quorum: 750000 EGLD = 7500000 * 10^18) - "", (yes value: 0) - "", (no value: 0) - "ntGU2xmyOMAAAA==", (veto value: 7500000 * 10^18) - "", (abstain value: 0) + "ntGU2xmyOMAAAA==", (quorum: 7,500,000 EGLD denominated) + "", (yes votes: 0) + "", (no votes: 0) + "ntGU2xmyOMAAAA==", (veto votes: 7,500,000 EGLD denominated) + "", (abstain votes: 0) "dHJ1ZQ==", (proposal closed: true) "ZmFsc2U=" (proposal passed: false) ] } -``` + + [comment]: # (mx-context-auto) @@ -192,51 +200,48 @@ https://.multiversx.com/vm-values/query ```json { "scAddress": "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrlllsrujgla", - "funcName": "viewUserVoteHistory", - "args": ["
"] + "funcName": "viewDelegatedVoteInfo", + "args": ["", "
"] } ``` -the `address` represents the address in hex format. The response will contain the following json definition where all fields are base64-encoded: +- `proposal_nonce` → the proposal identifier (nonce, hex-encoded). +- `address` → the bech32 address of the account to check. + + The response will contain the following json definition where all fields are base64-encoded: ```json { "returnData": [ - "", - "", - "", - ... - "", - "", - "", - "", - ... - "" + "", (voting power already used by this address on the proposal) + "", (stake associated with the used power) + "", (total available voting power for the address) + "" (total stake considered in governance for the address) ] } ``` -Example for an address that cast votes on 5 proposals: +Example for an address that voted: ```json { "returnData": [ - "Aw==", (number of delegated nonces: 3) - "AQ==", (nonce: 1) - "Ag==", (nonce: 2) - "Aw==", (nonce: 3) - "Ag==", (number of direct nonces: 2) - "BA==", (nonce: 4) - "BQ==", (nonce: 5) + "Cg==", (used power: 10) + "ZAA=", (used stake: 100) + "A+g=", (total power: 1000) + "Gg4M=", (total stake: 10000) ] } ``` +In this example, the queried address voted on this proposal with **100 stake**, which translated into **10 voting power**. The proposal overall had **10000 total stake** and **1000 total voting power** recorded. -Example for an address that did not cast votes on any proposals: +Example for an address that did not vote: ```json { "returnData": [ - "AA==", (number of delegated nonces: 0) - "AA==", (number of direct nonces: 0) + "AA==", (used power: 0) + "AA==", (used stake: 0) + "A+g=", (total power: 1000) + "Gg4M=", (total stake: 10000) ] } ``` diff --git a/docs/governance/overview.md b/docs/governance/overview.md index a8c7d2994..361e27a03 100644 --- a/docs/governance/overview.md +++ b/docs/governance/overview.md @@ -19,54 +19,84 @@ This page provides an overview of the On-chain Governance module that will be av ## Overview -The MultiversX network is able to handle on-chain governance proposes by issuing special types of transactions. This was implemented as a mean for further increasing the decentralization of the decision-making process. +The MultiversX network enables on-chain governance by issuing special types of transactions. This mechanism increases decentralization by allowing community members to directly propose and vote on changes, such as protocol upgrades or configuration adjustments. -Anyone can create a proposal using their wallet and issuing a special type of transaction specifying an identifier, a starting epoch and an end epoch. - -Users that staked EGLD will be able to cast votes upon opened proposals. The voting power is proportional with the staked value and is computed as `voting_power = staked_value` (linear voting). +- **Anyone** can create a proposal by paying the proposal fee and specifying: + - a **commit hash** (unique identifier, usually a GitHub commit or spec reference) + - a **start epoch** and an **end epoch** (the voting window). +- **Any staked user** (direct or delegated) can vote during the active period. +- Voting power is proportional to stake: + `voting_power = staked_value` (linear voting). [comment]: # (mx-context-auto) ## Implementation details -Each proposal costs 1000 EGLD that will be locked during the voting of the proposal. If the proposal is either rejected or accepted, the entire locked sum can be unlocked & withdrawn. If the proposal do not pass, 10 EGLD will remain locked in the governance smart contract as a penalty fee. The proposal costs around 51 million of gas units. +### Proposals +- Each proposal requires paying a `ProposalFee` (currently **500 EGLD**). +- If the proposal passes or fails normally, the fee is refunded to the issuer. +- If the proposal is vetoed, the fee is slashed (transferred to the **Community Governance Pool**) or reduced by the configured `LostProposalFee`. +- Proposals cannot be duplicated: the same commit hash cannot be submitted twice. + +### Voting +- There are four vote types: **Yes**, **No**, **Abstain**, and **Veto**. +- Voting consumes gas (approx. 6 million units). +- Voting power is derived from staked and delegated EGLD. +- Delegation and liquid staking contracts can cast votes on behalf of users. +- Voting power is **linear** with stake: the more EGLD staked or delegated, the higher the voting power. +- The contract tracks both **used voting power/stake** (applied in a proposal) and **total available voting power/stake** for each address. + +### Quorum and thresholds +A proposal can pass only if all conditions are met: -There are 4 types of votes: **Yes**, **No**, **Abstain** and **Veto**. Each of the vote costs around 6 million of gas units. +- **Quorum**: at least `MinQuorum%` of the total voting power must participate. +- **Majority**: the **Yes** votes must outnumber **No** votes. +- **Pass threshold**: the **Yes** votes must represent at least `MinPassThreshold%` of all votes cast. +- **Veto threshold**: if **Veto** votes exceed `MinVetoThreshold%`, the proposal is rejected and penalized. -A user can create any number of proposals as long as it pays the locking fee & the gas used for the transaction. The proposal can be closed in any epoch following the provided end epoch. +### Closing and cleanup +- Proposals can only be closed after the end epoch has passed. +- Only the issuer can close their proposal. +- Closing finalizes the outcome (passed / failed / vetoed) and unlocks the funds. +- Expired but unclosed proposals can also be cleaned up via `clearEndedProposals` to maintain efficiency. -The quorum is computed as the sum of the staked EGLD for all addresses that cast votes. +### Governance configuration +All thresholds and fees are defined in `GovernanceConfigV2`: +- `MinQuorum` +- `MinPassThreshold` +- `MinVetoThreshold` +- `ProposalFee` +- `LostProposalFee` +- `LastProposalNonce` -The votes will be added for each category (**Yes**, **No**, **Abstain** and **Veto**). The vote is computed as `vote = total_staked_value` for each address that cast a vote. - -A proposal can pass only if all conditions are met: -- the quorum value is at least the minimumQuorumThresholdPercentage * total staked value held by the staking contracts; -- the **Yes** value > **No** value (simple majority); -- the **Yes** value is at least the minimumPassThresholdPercentage * sum of votes on all 4 categories. -- the **Veto** value did not reach the minimumVetoThresholdPercentage * sum of votes on all 4 categories; +These values are set by the system and can be updated by contract owner calls. [comment]: # (mx-context-auto) ### Example Let's suppose we have the following addresses that cast the following votes: -- `alice`: staked value 2000 EGLD that vote **Yes** -- `bob`: staked value 3000 EGLD that vote **Yes** -- `charlie`: staked value 4000 EGLD that vote **Yes** -- `delta`: staked value 1500 EGLD that vote **No** - -The quorum in this case will be a value `(2000+3000+4000+1500) * 10^18 = 10500 * 10^18`. - -The **Yes** category will hold the value `2000 * 10^18 + 3000 * 10^18 + 4000 * 10^18 = 9000 * 10^18` -The **No** category will hold the value `1500 * 10^18` -The **Abstain** and **Veto** categories will both hold 0. -The total voted value is `9000 * 10^18 + 1500 * 10^18 + 0 + 0 = 10500 * 10^18` - -Supposing the total staked value in the system is `20000 EGLD` and the minimum quorum threshold percentage is `20%`, then the minimum quorum value is `20% * 20000 = 4000 EGLD`. - -The following list contains true sentences: -- the quorum value (`10500 EGLD`) is larger than the minimum quorum (`4000 EGLD`) -- **Yes** value (`9000 * 10^18`) is larger than the **No** value (`1500 * 10^18`) -- **Yes** value (`9000 * 10^18`) is larger than the pass threshold (`50%`) \* total voted value (`10500 * 10^18`) which is `5250 * 10^18` -- the **Veto** did not reach `33%` of the total vote value because it was `0` - -To sum it all, **the proposal passed**. +- `alice`: staked value **2000 EGLD** that vote **Yes** +- `bob`: staked value **3000 EGLD** that vote **Yes** +- `charlie`: staked value **4000 EGLD** that vote **Yes** +- `delta`: staked value **1500 EGLD** that vote **No** + +The totals are: +- Quorum = `(2000+3000+4000+1500) = 10,500 EGLD` +- **Yes** = `9000 EGLD` +- **No** = `1500 EGLD` +- **Abstain** = `0` +- **Veto** = `0` + +Assume: +- total staked in the system = `20,000 EGLD` +- `MinQuorum` = 20% +- `MinPassThreshold` = 50% +- `MinVetoThreshold` = 33% + +Evaluation: +- Quorum is satisfied: `10,500 > 4000` +- Yes > No: `9000 > 1500` +- Yes is ≥ 50% of votes: `9000 / 10,500 = 85.7%` +- Veto is below 33%: `0 < 3465` + +To sum it all, the proposal **passes**. diff --git a/docs/governance/updated_governance-interaction.md b/docs/governance/updated_governance-interaction.md new file mode 100644 index 000000000..83beded32 --- /dev/null +++ b/docs/governance/updated_governance-interaction.md @@ -0,0 +1,247 @@ +--- +id: governance-interaction +title: Governance interaction +--- + +[comment]: # (mx-context-auto) + +### Introduction + +The interaction with the governance system smartcontract is done through correctly formatted transactions to submit actions and through the usage of the vm-query REST API calls for reading the proposal(s) status. + +[comment]: # (mx-context-auto) + +### Creating a proposal + +The proposal creation transaction has the following parameters: + +```rust +GovernanceProposalTransaction { + Sender: + Receiver: erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrlllsrujgla + Value: 1000 EGLD + GasLimit: 51000000 + Data: "proposal" + + "@" + + "@" + + "@" +} +``` + +The proposal identifier is a hex string containing exactly 40 characters. Usually, this can be a git commit hash on which the proposal is made but can be any other identifier string. + +The starting & ending epochs should be an even-length hex string containing the starting epoch and the ending epoch. During this time frame the votes can be cast. + +After issuing the proposal, there is a log event generated having the `proposal` identifier that will contain the following encoded topics: + +- `nonce` as encoded integer which uniquely identifies the proposals +- `identifier` the provided 40 hex characters longs identifier +- `start epoch` as encoded integer for the starting epoch +- `end epoch` as encoded integer for the ending epoch + +[comment]: # (mx-context-auto) + +### Voting a proposal using the direct staked or delegation-system amount + +Any wallet that has staked EGLD (either direct staked or through the delegation sub-system) can cast a vote for a proposal. +```rust +GovernanceVoteTransaction { + Sender: + Receiver: erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrlllsrujgla + Value: 0 EGLD + GasLimit: 6000000 + Data: "vote" + + "@" + + "@" +} +``` + +The `nonce` is the hex encoded value of the proposal's unique nonce and the `vote_type` can be one of the following values: +- for **Yes**: `796573`; +- for **No**: `6e6f`; +- for **Abstain**: `6162737461696e`; +- for **Veto**: `7665746f`. + +The vote value for the account that will vote a proposal is the sum of all staked values along with the sum of all delegated values in the delegation sub-system. + +[comment]: # (mx-context-auto) + +### Voting a proposal through smart contracts (delegation voting) + +Whenever we deal with a smart contract that delegated through the delegation sub-system or owns staked nodes it is the out of scope of the metachain's governance contract to track each address that sent EGLD how much is really staked (if any EGLD is staked). +That is why we offered an endpoint to the governance smart contact that can be called **only by a shard smart contract** and the governance contract will record the address provided, the vote type and vote value. +This is very useful whenever implementing liquid-staking-like smart contracts. The liquid-staking contract knows the balance for each user, so it will delegate the call to the governance contract providing the value. + +:::important +The maximum value for the staked value & the voting power for the liquid-staking contract is known by the governance contract, so it can not counterfeit the voting. If, due to a bug the liquid-staking contract allows, for example, for a user to vote with a larger quota, the liquid-staking contract will deplete its maximum allowance making other voting transactions (on his behalf) to fail. +::: + +```rust +GovernanceVoteThourghDelegationTransaction { + Sender: + Receiver: erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrlllsrujgla + Value: 0 EGLD + GasLimit: 51000000 + Data: "delegateVote" + + "@" + + "@" + + "@" + + "@" +} +``` + +The `nonce` is the hex encoded value of the proposal's unique nonce and the `vote_type` can be one of the following values: +- for **Yes**: `796573`; +- for **No**: `6e6f`; +- for **Abstain**: `6162737461696e`; +- for **Veto**: `7665746f`. + +The `account address handled by the smart contract` is the address handled by the smart contract that will delegate the vote towards the governance smart contract. This address will be recorded for casting the vote. +The `vote_balance` is the amount of stake the address has in the smart contract. The governance contract will "believe" that this is the right amount as it impossible to verify the information. The balance will diminish the total voting power the smart contract has. + +[comment]: # (mx-context-auto) + +### Closing a proposal + +A proposal can be closed only in an epoch that is strictly higher than the end epoch value provided when the proposal was opened. +Closing can only be performed by the account that created the proposal (the issuer). + +```rust +CloseProposalTransaction { + Sender: + Receiver: erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrlllsrujgla + Value: 0 EGLD + GasLimit: 51000000 + Data: "closeProposal" + + "@" +} +``` +#### Rules for closing +- Only the issuer can call `closeProposal`. +- If the proposal **passes** → the full proposal fee is refunded. +- If the proposal **fails** or is **vetoed** → the refund is reduced by the `LostProposalFee`. +- Once a proposal is closed, it cannot be reopened. +- - Closing also finalizes the vote tally (the proposal is marked as `Passed` or not, based on the results). + + +[comment]: # (mx-context-auto) + +### Querying the status of a proposal + +The status of any proposal can be queried at any time through `vm-values/query` REST API endpoints provided by the gateway/API. + +```bash +https://.multiversx.com/vm-values/query +``` + +```json +{ + "scAddress": "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrlllsrujgla", + "funcName": "viewProposal", + "args": [""] +} +``` + +- The argument `nonce` is the proposal nonce in hex format. +- The response will contain the following json definition where all fields are base64-encoded: + +```json +{ + "returnData": [ + "", (amount locked by proposer) + "", (unique identifier of the proposal) + "", (proposal number) + "", (address of the proposer) + "", (epoch when voting starts) + "", (epoch when voting ends) + "", (minimum stake required to reach quorum) + "", (total stake voting YES) + "", (total stake voting NO) + "", (total stake vetoing the proposal) + "", (total stake abstaining) + "", + "" + ] +} +``` + +Example response: +```json +{ + "returnData": [ + "NjXJrcXeoAAA", (proposal locked amount: 1000 EGLD denominated = 1000 * 10^18) + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABg==", (commit hash: 0x0000...006) + "AQ==", (nonce: 1) + "aj88GtqHy9ibm5ePPQlG4aqLhpgqsQWygoTppckLa4M=", (proposer address) + "bQ==", (starting epoch: 109) + "bg==", (ending epoch: 110) + "ntGU2xmyOMAAAA==", (quorum: 7,500,000 EGLD denominated) + "", (yes votes: 0) + "", (no votes: 0) + "ntGU2xmyOMAAAA==", (veto votes: 7,500,000 EGLD denominated) + "", (abstain votes: 0) + "dHJ1ZQ==", (proposal closed: true) + "ZmFsc2U=" (proposal passed: false) + ] +} + + + +[comment]: # (mx-context-auto) + +### Querying an address voting status (direct staking or system delegation) + +The voting status of a certain address can be queried at any time by using the `vm-values/query` REST API endpoints provided by the gateway/API. + +```bash +https://.multiversx.com/vm-values/query +``` + +```json +{ + "scAddress": "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrlllsrujgla", + "funcName": "viewDelegatedVoteInfo", + "args": ["", "
"] +} +``` + +- `proposal_nonce` → the proposal identifier (nonce, hex-encoded). +- `address` → the bech32 address of the account to check. + + The response will contain the following json definition where all fields are base64-encoded: + +```json +{ + "returnData": [ + "", (voting power already used by this address on the proposal) + "", (stake associated with the used power) + "", (total available voting power for the address) + "" (total stake considered in governance for the address) + ] +} +``` + +Example for an address that voted: +```json +{ + "returnData": [ + "Cg==", (used power: 10) + "ZAA=", (used stake: 100) + "A+g=", (total power: 1000) + "Gg4M=", (total stake: 10000) + ] +} +``` +In this example, the queried address voted on this proposal with **100 stake**, which translated into **10 voting power**. The proposal overall had **10000 total stake** and **1000 total voting power** recorded. + +Example for an address that did not vote: +```json +{ + "returnData": [ + "AA==", (used power: 0) + "AA==", (used stake: 0) + "A+g=", (total power: 1000) + "Gg4M=", (total stake: 10000) + ] +} +``` diff --git a/docs/governance/updated_overview.md b/docs/governance/updated_overview.md new file mode 100644 index 000000000..361e27a03 --- /dev/null +++ b/docs/governance/updated_overview.md @@ -0,0 +1,102 @@ +--- +id: overview +title: Governance - Overview +--- + +[comment]: # (mx-abstract) + +This page provides an overview of the On-chain Governance module that will be available on the `v1.6.x` release. + +[comment]: # (mx-context-auto) + +## Table of contents + +| Name | Description | +|-----------------------------------------------------------------------------------|--------------------------------------------------------------------------| +| [Interacting with the on-chain governance SC](/governance/governance-interaction) | Interacting with the governance system smart contract. | + +[comment]: # (mx-context-auto) + +## Overview + +The MultiversX network enables on-chain governance by issuing special types of transactions. This mechanism increases decentralization by allowing community members to directly propose and vote on changes, such as protocol upgrades or configuration adjustments. + +- **Anyone** can create a proposal by paying the proposal fee and specifying: + - a **commit hash** (unique identifier, usually a GitHub commit or spec reference) + - a **start epoch** and an **end epoch** (the voting window). +- **Any staked user** (direct or delegated) can vote during the active period. +- Voting power is proportional to stake: + `voting_power = staked_value` (linear voting). + +[comment]: # (mx-context-auto) + +## Implementation details + +### Proposals +- Each proposal requires paying a `ProposalFee` (currently **500 EGLD**). +- If the proposal passes or fails normally, the fee is refunded to the issuer. +- If the proposal is vetoed, the fee is slashed (transferred to the **Community Governance Pool**) or reduced by the configured `LostProposalFee`. +- Proposals cannot be duplicated: the same commit hash cannot be submitted twice. + +### Voting +- There are four vote types: **Yes**, **No**, **Abstain**, and **Veto**. +- Voting consumes gas (approx. 6 million units). +- Voting power is derived from staked and delegated EGLD. +- Delegation and liquid staking contracts can cast votes on behalf of users. +- Voting power is **linear** with stake: the more EGLD staked or delegated, the higher the voting power. +- The contract tracks both **used voting power/stake** (applied in a proposal) and **total available voting power/stake** for each address. + +### Quorum and thresholds +A proposal can pass only if all conditions are met: + +- **Quorum**: at least `MinQuorum%` of the total voting power must participate. +- **Majority**: the **Yes** votes must outnumber **No** votes. +- **Pass threshold**: the **Yes** votes must represent at least `MinPassThreshold%` of all votes cast. +- **Veto threshold**: if **Veto** votes exceed `MinVetoThreshold%`, the proposal is rejected and penalized. + +### Closing and cleanup +- Proposals can only be closed after the end epoch has passed. +- Only the issuer can close their proposal. +- Closing finalizes the outcome (passed / failed / vetoed) and unlocks the funds. +- Expired but unclosed proposals can also be cleaned up via `clearEndedProposals` to maintain efficiency. + +### Governance configuration +All thresholds and fees are defined in `GovernanceConfigV2`: +- `MinQuorum` +- `MinPassThreshold` +- `MinVetoThreshold` +- `ProposalFee` +- `LostProposalFee` +- `LastProposalNonce` + +These values are set by the system and can be updated by contract owner calls. + +[comment]: # (mx-context-auto) + +### Example +Let's suppose we have the following addresses that cast the following votes: +- `alice`: staked value **2000 EGLD** that vote **Yes** +- `bob`: staked value **3000 EGLD** that vote **Yes** +- `charlie`: staked value **4000 EGLD** that vote **Yes** +- `delta`: staked value **1500 EGLD** that vote **No** + +The totals are: +- Quorum = `(2000+3000+4000+1500) = 10,500 EGLD` +- **Yes** = `9000 EGLD` +- **No** = `1500 EGLD` +- **Abstain** = `0` +- **Veto** = `0` + +Assume: +- total staked in the system = `20,000 EGLD` +- `MinQuorum` = 20% +- `MinPassThreshold` = 50% +- `MinVetoThreshold` = 33% + +Evaluation: +- Quorum is satisfied: `10,500 > 4000` +- Yes > No: `9000 > 1500` +- Yes is ≥ 50% of votes: `9000 / 10,500 = 85.7%` +- Veto is below 33%: `0 < 3465` + +To sum it all, the proposal **passes**. From dda38d85f721128270605ea957f48b3fcf3678bc Mon Sep 17 00:00:00 2001 From: Sara Sorici Date: Mon, 1 Sep 2025 11:08:29 +0300 Subject: [PATCH 02/19] chore(docs): remove duplicate governance files --- .../updated_governance-interaction.md | 247 ------------------ docs/governance/updated_overview.md | 102 -------- 2 files changed, 349 deletions(-) delete mode 100644 docs/governance/updated_governance-interaction.md delete mode 100644 docs/governance/updated_overview.md diff --git a/docs/governance/updated_governance-interaction.md b/docs/governance/updated_governance-interaction.md deleted file mode 100644 index 83beded32..000000000 --- a/docs/governance/updated_governance-interaction.md +++ /dev/null @@ -1,247 +0,0 @@ ---- -id: governance-interaction -title: Governance interaction ---- - -[comment]: # (mx-context-auto) - -### Introduction - -The interaction with the governance system smartcontract is done through correctly formatted transactions to submit actions and through the usage of the vm-query REST API calls for reading the proposal(s) status. - -[comment]: # (mx-context-auto) - -### Creating a proposal - -The proposal creation transaction has the following parameters: - -```rust -GovernanceProposalTransaction { - Sender: - Receiver: erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrlllsrujgla - Value: 1000 EGLD - GasLimit: 51000000 - Data: "proposal" + - "@" + - "@" + - "@" -} -``` - -The proposal identifier is a hex string containing exactly 40 characters. Usually, this can be a git commit hash on which the proposal is made but can be any other identifier string. - -The starting & ending epochs should be an even-length hex string containing the starting epoch and the ending epoch. During this time frame the votes can be cast. - -After issuing the proposal, there is a log event generated having the `proposal` identifier that will contain the following encoded topics: - -- `nonce` as encoded integer which uniquely identifies the proposals -- `identifier` the provided 40 hex characters longs identifier -- `start epoch` as encoded integer for the starting epoch -- `end epoch` as encoded integer for the ending epoch - -[comment]: # (mx-context-auto) - -### Voting a proposal using the direct staked or delegation-system amount - -Any wallet that has staked EGLD (either direct staked or through the delegation sub-system) can cast a vote for a proposal. -```rust -GovernanceVoteTransaction { - Sender: - Receiver: erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrlllsrujgla - Value: 0 EGLD - GasLimit: 6000000 - Data: "vote" + - "@" + - "@" -} -``` - -The `nonce` is the hex encoded value of the proposal's unique nonce and the `vote_type` can be one of the following values: -- for **Yes**: `796573`; -- for **No**: `6e6f`; -- for **Abstain**: `6162737461696e`; -- for **Veto**: `7665746f`. - -The vote value for the account that will vote a proposal is the sum of all staked values along with the sum of all delegated values in the delegation sub-system. - -[comment]: # (mx-context-auto) - -### Voting a proposal through smart contracts (delegation voting) - -Whenever we deal with a smart contract that delegated through the delegation sub-system or owns staked nodes it is the out of scope of the metachain's governance contract to track each address that sent EGLD how much is really staked (if any EGLD is staked). -That is why we offered an endpoint to the governance smart contact that can be called **only by a shard smart contract** and the governance contract will record the address provided, the vote type and vote value. -This is very useful whenever implementing liquid-staking-like smart contracts. The liquid-staking contract knows the balance for each user, so it will delegate the call to the governance contract providing the value. - -:::important -The maximum value for the staked value & the voting power for the liquid-staking contract is known by the governance contract, so it can not counterfeit the voting. If, due to a bug the liquid-staking contract allows, for example, for a user to vote with a larger quota, the liquid-staking contract will deplete its maximum allowance making other voting transactions (on his behalf) to fail. -::: - -```rust -GovernanceVoteThourghDelegationTransaction { - Sender: - Receiver: erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrlllsrujgla - Value: 0 EGLD - GasLimit: 51000000 - Data: "delegateVote" + - "@" + - "@" + - "@" + - "@" -} -``` - -The `nonce` is the hex encoded value of the proposal's unique nonce and the `vote_type` can be one of the following values: -- for **Yes**: `796573`; -- for **No**: `6e6f`; -- for **Abstain**: `6162737461696e`; -- for **Veto**: `7665746f`. - -The `account address handled by the smart contract` is the address handled by the smart contract that will delegate the vote towards the governance smart contract. This address will be recorded for casting the vote. -The `vote_balance` is the amount of stake the address has in the smart contract. The governance contract will "believe" that this is the right amount as it impossible to verify the information. The balance will diminish the total voting power the smart contract has. - -[comment]: # (mx-context-auto) - -### Closing a proposal - -A proposal can be closed only in an epoch that is strictly higher than the end epoch value provided when the proposal was opened. -Closing can only be performed by the account that created the proposal (the issuer). - -```rust -CloseProposalTransaction { - Sender: - Receiver: erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrlllsrujgla - Value: 0 EGLD - GasLimit: 51000000 - Data: "closeProposal" + - "@" -} -``` -#### Rules for closing -- Only the issuer can call `closeProposal`. -- If the proposal **passes** → the full proposal fee is refunded. -- If the proposal **fails** or is **vetoed** → the refund is reduced by the `LostProposalFee`. -- Once a proposal is closed, it cannot be reopened. -- - Closing also finalizes the vote tally (the proposal is marked as `Passed` or not, based on the results). - - -[comment]: # (mx-context-auto) - -### Querying the status of a proposal - -The status of any proposal can be queried at any time through `vm-values/query` REST API endpoints provided by the gateway/API. - -```bash -https://.multiversx.com/vm-values/query -``` - -```json -{ - "scAddress": "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrlllsrujgla", - "funcName": "viewProposal", - "args": [""] -} -``` - -- The argument `nonce` is the proposal nonce in hex format. -- The response will contain the following json definition where all fields are base64-encoded: - -```json -{ - "returnData": [ - "", (amount locked by proposer) - "", (unique identifier of the proposal) - "", (proposal number) - "", (address of the proposer) - "", (epoch when voting starts) - "", (epoch when voting ends) - "", (minimum stake required to reach quorum) - "", (total stake voting YES) - "", (total stake voting NO) - "", (total stake vetoing the proposal) - "", (total stake abstaining) - "", - "" - ] -} -``` - -Example response: -```json -{ - "returnData": [ - "NjXJrcXeoAAA", (proposal locked amount: 1000 EGLD denominated = 1000 * 10^18) - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABg==", (commit hash: 0x0000...006) - "AQ==", (nonce: 1) - "aj88GtqHy9ibm5ePPQlG4aqLhpgqsQWygoTppckLa4M=", (proposer address) - "bQ==", (starting epoch: 109) - "bg==", (ending epoch: 110) - "ntGU2xmyOMAAAA==", (quorum: 7,500,000 EGLD denominated) - "", (yes votes: 0) - "", (no votes: 0) - "ntGU2xmyOMAAAA==", (veto votes: 7,500,000 EGLD denominated) - "", (abstain votes: 0) - "dHJ1ZQ==", (proposal closed: true) - "ZmFsc2U=" (proposal passed: false) - ] -} - - - -[comment]: # (mx-context-auto) - -### Querying an address voting status (direct staking or system delegation) - -The voting status of a certain address can be queried at any time by using the `vm-values/query` REST API endpoints provided by the gateway/API. - -```bash -https://.multiversx.com/vm-values/query -``` - -```json -{ - "scAddress": "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrlllsrujgla", - "funcName": "viewDelegatedVoteInfo", - "args": ["", "
"] -} -``` - -- `proposal_nonce` → the proposal identifier (nonce, hex-encoded). -- `address` → the bech32 address of the account to check. - - The response will contain the following json definition where all fields are base64-encoded: - -```json -{ - "returnData": [ - "", (voting power already used by this address on the proposal) - "", (stake associated with the used power) - "", (total available voting power for the address) - "" (total stake considered in governance for the address) - ] -} -``` - -Example for an address that voted: -```json -{ - "returnData": [ - "Cg==", (used power: 10) - "ZAA=", (used stake: 100) - "A+g=", (total power: 1000) - "Gg4M=", (total stake: 10000) - ] -} -``` -In this example, the queried address voted on this proposal with **100 stake**, which translated into **10 voting power**. The proposal overall had **10000 total stake** and **1000 total voting power** recorded. - -Example for an address that did not vote: -```json -{ - "returnData": [ - "AA==", (used power: 0) - "AA==", (used stake: 0) - "A+g=", (total power: 1000) - "Gg4M=", (total stake: 10000) - ] -} -``` diff --git a/docs/governance/updated_overview.md b/docs/governance/updated_overview.md deleted file mode 100644 index 361e27a03..000000000 --- a/docs/governance/updated_overview.md +++ /dev/null @@ -1,102 +0,0 @@ ---- -id: overview -title: Governance - Overview ---- - -[comment]: # (mx-abstract) - -This page provides an overview of the On-chain Governance module that will be available on the `v1.6.x` release. - -[comment]: # (mx-context-auto) - -## Table of contents - -| Name | Description | -|-----------------------------------------------------------------------------------|--------------------------------------------------------------------------| -| [Interacting with the on-chain governance SC](/governance/governance-interaction) | Interacting with the governance system smart contract. | - -[comment]: # (mx-context-auto) - -## Overview - -The MultiversX network enables on-chain governance by issuing special types of transactions. This mechanism increases decentralization by allowing community members to directly propose and vote on changes, such as protocol upgrades or configuration adjustments. - -- **Anyone** can create a proposal by paying the proposal fee and specifying: - - a **commit hash** (unique identifier, usually a GitHub commit or spec reference) - - a **start epoch** and an **end epoch** (the voting window). -- **Any staked user** (direct or delegated) can vote during the active period. -- Voting power is proportional to stake: - `voting_power = staked_value` (linear voting). - -[comment]: # (mx-context-auto) - -## Implementation details - -### Proposals -- Each proposal requires paying a `ProposalFee` (currently **500 EGLD**). -- If the proposal passes or fails normally, the fee is refunded to the issuer. -- If the proposal is vetoed, the fee is slashed (transferred to the **Community Governance Pool**) or reduced by the configured `LostProposalFee`. -- Proposals cannot be duplicated: the same commit hash cannot be submitted twice. - -### Voting -- There are four vote types: **Yes**, **No**, **Abstain**, and **Veto**. -- Voting consumes gas (approx. 6 million units). -- Voting power is derived from staked and delegated EGLD. -- Delegation and liquid staking contracts can cast votes on behalf of users. -- Voting power is **linear** with stake: the more EGLD staked or delegated, the higher the voting power. -- The contract tracks both **used voting power/stake** (applied in a proposal) and **total available voting power/stake** for each address. - -### Quorum and thresholds -A proposal can pass only if all conditions are met: - -- **Quorum**: at least `MinQuorum%` of the total voting power must participate. -- **Majority**: the **Yes** votes must outnumber **No** votes. -- **Pass threshold**: the **Yes** votes must represent at least `MinPassThreshold%` of all votes cast. -- **Veto threshold**: if **Veto** votes exceed `MinVetoThreshold%`, the proposal is rejected and penalized. - -### Closing and cleanup -- Proposals can only be closed after the end epoch has passed. -- Only the issuer can close their proposal. -- Closing finalizes the outcome (passed / failed / vetoed) and unlocks the funds. -- Expired but unclosed proposals can also be cleaned up via `clearEndedProposals` to maintain efficiency. - -### Governance configuration -All thresholds and fees are defined in `GovernanceConfigV2`: -- `MinQuorum` -- `MinPassThreshold` -- `MinVetoThreshold` -- `ProposalFee` -- `LostProposalFee` -- `LastProposalNonce` - -These values are set by the system and can be updated by contract owner calls. - -[comment]: # (mx-context-auto) - -### Example -Let's suppose we have the following addresses that cast the following votes: -- `alice`: staked value **2000 EGLD** that vote **Yes** -- `bob`: staked value **3000 EGLD** that vote **Yes** -- `charlie`: staked value **4000 EGLD** that vote **Yes** -- `delta`: staked value **1500 EGLD** that vote **No** - -The totals are: -- Quorum = `(2000+3000+4000+1500) = 10,500 EGLD` -- **Yes** = `9000 EGLD` -- **No** = `1500 EGLD` -- **Abstain** = `0` -- **Veto** = `0` - -Assume: -- total staked in the system = `20,000 EGLD` -- `MinQuorum` = 20% -- `MinPassThreshold` = 50% -- `MinVetoThreshold` = 33% - -Evaluation: -- Quorum is satisfied: `10,500 > 4000` -- Yes > No: `9000 > 1500` -- Yes is ≥ 50% of votes: `9000 / 10,500 = 85.7%` -- Veto is below 33%: `0 < 3465` - -To sum it all, the proposal **passes**. From a90f71188ebf051d799faee1316cff3741052ebd Mon Sep 17 00:00:00 2001 From: Sara Sorici Date: Mon, 1 Sep 2025 11:17:13 +0300 Subject: [PATCH 03/19] WIP: save local changes --- docs/governance/governance-interaction.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/governance/governance-interaction.md b/docs/governance/governance-interaction.md index 83beded32..36c10267d 100644 --- a/docs/governance/governance-interaction.md +++ b/docs/governance/governance-interaction.md @@ -121,7 +121,7 @@ CloseProposalTransaction { - If the proposal **passes** → the full proposal fee is refunded. - If the proposal **fails** or is **vetoed** → the refund is reduced by the `LostProposalFee`. - Once a proposal is closed, it cannot be reopened. -- - Closing also finalizes the vote tally (the proposal is marked as `Passed` or not, based on the results). +- Closing also finalizes the vote tally (the proposal is marked as `Passed` or not, based on the results). [comment]: # (mx-context-auto) From 65ddd1e227a017dc64f2baaac4bc9a25b25ef847 Mon Sep 17 00:00:00 2001 From: Sara Sorici Date: Tue, 2 Sep 2025 10:13:40 +0300 Subject: [PATCH 04/19] Update governance docs: clarified querying voting status --- docs/governance/governance-interaction.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/governance/governance-interaction.md b/docs/governance/governance-interaction.md index 36c10267d..f0c7304ff 100644 --- a/docs/governance/governance-interaction.md +++ b/docs/governance/governance-interaction.md @@ -148,7 +148,7 @@ https://.multiversx.com/vm-values/query ```json { "returnData": [ - "", (amount locked by proposer) + "", (amount locked by proposer) "", (unique identifier of the proposal) "", (proposal number) "", (address of the proposer) From 749d2b773d7ca9d97826c39eeb7003ac5dd0e1d1 Mon Sep 17 00:00:00 2001 From: Sara Sorici Date: Tue, 2 Sep 2025 10:37:22 +0300 Subject: [PATCH 05/19] Update governance docs: clarified older viewUserVoteHistory function --- docs/governance/governance-interaction.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/governance/governance-interaction.md b/docs/governance/governance-interaction.md index f0c7304ff..d8aa425a9 100644 --- a/docs/governance/governance-interaction.md +++ b/docs/governance/governance-interaction.md @@ -138,7 +138,7 @@ https://.multiversx.com/vm-values/query { "scAddress": "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrlllsrujgla", "funcName": "viewProposal", - "args": [""] + "args": [""] } ``` @@ -154,7 +154,7 @@ https://.multiversx.com/vm-values/query "", (address of the proposer) "", (epoch when voting starts) "", (epoch when voting ends) - "", (minimum stake required to reach quorum) + "", (current quorum stake: sum of stake that participated) "", (total stake voting YES) "", (total stake voting NO) "", (total stake vetoing the proposal) @@ -201,14 +201,17 @@ https://.multiversx.com/vm-values/query { "scAddress": "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrlllsrujgla", "funcName": "viewDelegatedVoteInfo", - "args": ["", "
"] + "args": ["", "
"] } ``` - `proposal_nonce` → the proposal identifier (nonce, hex-encoded). - `address` → the bech32 address of the account to check. - The response will contain the following json definition where all fields are base64-encoded: +> **Note:** The older function `viewUserVoteHistory` (which returned lists of proposal nonces) is now considered legacy. +> Use `viewDelegatedVoteInfo` for detailed voting power and stake information. + +The response will contain the following json definition where all fields are base64-encoded: ```json { From bbcd78def88fd0e8fca238188837587a84a4d350 Mon Sep 17 00:00:00 2001 From: Sara Sorici Date: Tue, 2 Sep 2025 10:51:49 +0300 Subject: [PATCH 06/19] Update governance overview: clarified quorum and thresholds with config references --- docs/governance/overview.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/governance/overview.md b/docs/governance/overview.md index 361e27a03..457566daf 100644 --- a/docs/governance/overview.md +++ b/docs/governance/overview.md @@ -37,6 +37,9 @@ The MultiversX network enables on-chain governance by issuing special types of t - If the proposal passes or fails normally, the fee is refunded to the issuer. - If the proposal is vetoed, the fee is slashed (transferred to the **Community Governance Pool**) or reduced by the configured `LostProposalFee`. - Proposals cannot be duplicated: the same commit hash cannot be submitted twice. +- Proposals can be **cancelled** by the issuer **before the voting period starts**. (introduced in v1.10.0 Barnard release) +- Lost proposal fees may either be accumulated in the governance contract (retrievable by the contract owner) or redirected to a **Community Governance Pool**, depending on configuration. + ### Voting - There are four vote types: **Yes**, **No**, **Abstain**, and **Veto**. @@ -49,10 +52,9 @@ The MultiversX network enables on-chain governance by issuing special types of t ### Quorum and thresholds A proposal can pass only if all conditions are met: -- **Quorum**: at least `MinQuorum%` of the total voting power must participate. -- **Majority**: the **Yes** votes must outnumber **No** votes. -- **Pass threshold**: the **Yes** votes must represent at least `MinPassThreshold%` of all votes cast. -- **Veto threshold**: if **Veto** votes exceed `MinVetoThreshold%`, the proposal is rejected and penalized. +- **Quorum**: at least `MinQuorum%` of the total voting power must participate. (currently at least **20%** of total voting power) +- **Acceptance threshold**: YES / (YES + NO + VETO) ≥ **66.67%** (`MinPassThreshold`) +- **Veto threshold**: if VETO votes exceed **33%** of total participating voting power, the proposal is rejected and the proposal fee is slashed. ### Closing and cleanup - Proposals can only be closed after the end epoch has passed. From 1b33881b746f1f2f188d94cda026ee6b919618d6 Mon Sep 17 00:00:00 2001 From: LaraTifui <121812597+LaraTifui@users.noreply.github.com> Date: Thu, 4 Sep 2025 04:47:30 +0200 Subject: [PATCH 07/19] update - governance creation of proposal and voting --- docs/governance/governance-interaction.md | 31 ++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/docs/governance/governance-interaction.md b/docs/governance/governance-interaction.md index fb838278e..3d46607e0 100644 --- a/docs/governance/governance-interaction.md +++ b/docs/governance/governance-interaction.md @@ -19,7 +19,7 @@ The proposal creation transaction has the following parameters: GovernanceProposalTransaction { Sender: Receiver: erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrlllsrujgla - Value: 1000 EGLD + Value: 500 EGLD GasLimit: 51000000 Data: "proposal" + "@" + @@ -28,9 +28,13 @@ GovernanceProposalTransaction { } ``` +The value parameter represents the mandatory proposal submission deposit of 500 EGLD. + The proposal identifier is a hex string containing exactly 40 characters. Usually, this can be a git commit hash on which the proposal is made but can be any other identifier string. -The starting & ending epochs should be an even-length hex string containing the starting epoch and the ending epoch. During this time frame the votes can be cast. +The starting & ending epochs should be an even-length hex string containing the starting epoch and the ending epoch. During this time frame, lasting 10 days, the votes can be cast. + +>**Note:** When providing the starting epoch, it should be taken into consideration that the governance contract enforcing a configured maximum gap of 30 epochs between the current epoch and the proposal’s starting epoch. After issuing the proposal, there is a log event generated having the `proposal` identifier that will contain the following encoded topics: @@ -43,7 +47,7 @@ After issuing the proposal, there is a log event generated having the `proposal` ### Voting a proposal using the direct staked or delegation-system amount -Any wallet that has staked EGLD (either direct staked or through the delegation sub-system) can cast a vote for a proposal. +Any wallet that has staked EGLD (either direct staked or through the delegation sub-system) can cast a vote for a proposal. ```rust GovernanceVoteTransaction { Sender: @@ -56,6 +60,8 @@ GovernanceVoteTransaction { } ``` +The value parameter for the voting transaction must be set to 0, since the function is non-payable. + The `nonce` is the hex encoded value of the proposal's unique nonce and the `vote_type` can be one of the following values: - for **Yes**: `796573`; - for **No**: `6e6f`; @@ -64,11 +70,19 @@ The `nonce` is the hex encoded value of the proposal's unique nonce and the `vot The vote value for the account that will vote a proposal is the sum of all staked values along with the sum of all delegated values in the delegation sub-system. +After issuing the vote, a log event is generated containing the `proposal` identifier and the following encoded topics: + +- `nonce` as encoded integer which uniquely identifies the proposals +- `vote_type` as encoded string representing the vote +- `total_stake` total staked EGLD for the sender address +- `total_voting_power` total available voting power for the sender address + [comment]: # (mx-context-auto) ### Voting a proposal through smart contracts (delegation voting) Whenever we deal with a smart contract that delegated through the delegation sub-system or owns staked nodes it is the out of scope of the metachain's governance contract to track each address that sent EGLD how much is really staked (if any EGLD is staked). + That is why we offered an endpoint to the governance smart contact that can be called **only by a shard smart contract** and the governance contract will record the address provided, the vote type and vote value. This is very useful whenever implementing liquid-staking-like smart contracts. The liquid-staking contract knows the balance for each user, so it will delegate the call to the governance contract providing the value. @@ -90,6 +104,8 @@ GovernanceVoteThourghDelegationTransaction { } ``` +The value parameter for the voting transaction must be set to 0, since the function is non-payable. + The `nonce` is the hex encoded value of the proposal's unique nonce and the `vote_type` can be one of the following values: - for **Yes**: `796573`; - for **No**: `6e6f`; @@ -97,8 +113,17 @@ The `nonce` is the hex encoded value of the proposal's unique nonce and the `vot - for **Veto**: `7665746f`. The `account address handled by the smart contract` is the address handled by the smart contract that will delegate the vote towards the governance smart contract. This address will be recorded for casting the vote. + The `vote_balance` is the amount of stake the address has in the smart contract. The governance contract will "believe" that this is the right amount as it impossible to verify the information. The balance will diminish the total voting power the smart contract has. +After issuing the vote, a log event is generated containing the `proposal` identifier and the following encoded topics: + +- `nonce` as encoded integer which uniquely identifies the proposals +- `vote_type` as encoded string representing the vote +- `voter` account address handled by the smart contract +- `total_stake` total staked EGLD for the sender address +- `total_voting_power` total available voting power for the sender address + [comment]: # (mx-context-auto) ### Closing a proposal From 2f6f6a024d67700e0c7a4b765458d7d8348b8303 Mon Sep 17 00:00:00 2001 From: LaraTifui <121812597+LaraTifui@users.noreply.github.com> Date: Thu, 4 Sep 2025 05:10:36 +0200 Subject: [PATCH 08/19] update - gov interaction --- docs/governance/governance-interaction.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/governance/governance-interaction.md b/docs/governance/governance-interaction.md index be17221e6..12c0bd9f9 100644 --- a/docs/governance/governance-interaction.md +++ b/docs/governance/governance-interaction.md @@ -7,7 +7,7 @@ title: Governance interaction ### Introduction -The interaction with the governance system smartcontract is done through correctly formatted transactions to submit actions and through the usage of the vm-query REST API calls for reading the proposal(s) status. +The interaction with the governance system smartcontract is done through correctly formatted transactions to submit actions and through the usage of the vm-query REST API calls for reading the proposal(s) status. [comment]: # (mx-context-auto) @@ -84,6 +84,7 @@ After issuing the vote, a log event is generated containing the `proposal` ident Whenever we deal with a smart contract that delegated through the delegation sub-system or owns staked nodes it is the out of scope of the metachain's governance contract to track each address that sent EGLD how much is really staked (if any EGLD is staked). That is why we offered an endpoint to the governance smart contact that can be called **only by a shard smart contract** and the governance contract will record the address provided, the vote type and vote value. + This is very useful whenever implementing liquid-staking-like smart contracts. The liquid-staking contract knows the balance for each user, so it will delegate the call to the governance contract providing the value. :::important From eb77ec41319cf44a7d6b702bd49620abbec5db0a Mon Sep 17 00:00:00 2001 From: Sara Sorici Date: Mon, 1 Sep 2025 10:42:55 +0300 Subject: [PATCH 09/19] docs(governance): update proposal close/query interaction and overview --- docs/governance/governance-interaction.md | 111 ++++---- docs/governance/overview.md | 102 +++++--- .../updated_governance-interaction.md | 247 ++++++++++++++++++ docs/governance/updated_overview.md | 102 ++++++++ 4 files changed, 473 insertions(+), 89 deletions(-) create mode 100644 docs/governance/updated_governance-interaction.md create mode 100644 docs/governance/updated_overview.md diff --git a/docs/governance/governance-interaction.md b/docs/governance/governance-interaction.md index fb838278e..83beded32 100644 --- a/docs/governance/governance-interaction.md +++ b/docs/governance/governance-interaction.md @@ -103,26 +103,32 @@ The `vote_balance` is the amount of stake the address has in the smart contract. ### Closing a proposal -A proposal can be closed only in an epoch that is strictly higher than the end epoch value provided when the proposal was open. +A proposal can be closed only in an epoch that is strictly higher than the end epoch value provided when the proposal was opened. +Closing can only be performed by the account that created the proposal (the issuer). ```rust CloseProposalTransaction { - Sender: + Sender: Receiver: erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrlllsrujgla - Value: 0 EGLD + Value: 0 EGLD GasLimit: 51000000 - Data: "closeProposal" + - "@" + Data: "closeProposal" + + "@" } ``` - -Only the address that created the proposal can call the `closeProposal` function that will also trigger the funds unlocking. As stated in the overview page, if the proposal does not pass, the amount returned will be less with 10 EGLD. - +#### Rules for closing +- Only the issuer can call `closeProposal`. +- If the proposal **passes** → the full proposal fee is refunded. +- If the proposal **fails** or is **vetoed** → the refund is reduced by the `LostProposalFee`. +- Once a proposal is closed, it cannot be reopened. +- - Closing also finalizes the vote tally (the proposal is marked as `Passed` or not, based on the results). + + [comment]: # (mx-context-auto) ### Querying the status of a proposal -The status of a certain proposal can be queried at any time by using the `vm-values/query` REST API endpoints provided by the gateway/API. +The status of any proposal can be queried at any time through `vm-values/query` REST API endpoints provided by the gateway/API. ```bash https://.multiversx.com/vm-values/query @@ -136,48 +142,50 @@ https://.multiversx.com/vm-values/query } ``` -the `nonce` represents the proposal nonce in hex format. The response will contain the following json definition where all fields are base64-encoded: +- The argument `nonce` is the proposal nonce in hex format. +- The response will contain the following json definition where all fields are base64-encoded: ```json { "returnData": [ - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", + "", (amount locked by proposer) + "", (unique identifier of the proposal) + "", (proposal number) + "", (address of the proposer) + "", (epoch when voting starts) + "", (epoch when voting ends) + "", (minimum stake required to reach quorum) + "", (total stake voting YES) + "", (total stake voting NO) + "", (total stake vetoing the proposal) + "", (total stake abstaining) "", "" ] } ``` -Example: +Example response: ```json { "returnData": [ "NjXJrcXeoAAA", (proposal locked amount: 1000 EGLD denominated = 1000 * 10^18) "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABg==", (commit hash: 0x0000...006) "AQ==", (nonce: 1) - "aj88GtqHy9ibm5ePPQlG4aqLhpgqsQWygoTppckLa4M=", (address: erd1dglncxk6sl9a3xumj78n6z2xux4ghp5c92cstv5zsn56tjgtdwpsk46qrs) + "aj88GtqHy9ibm5ePPQlG4aqLhpgqsQWygoTppckLa4M=", (proposer address) "bQ==", (starting epoch: 109) "bg==", (ending epoch: 110) - "ntGU2xmyOMAAAA==", (quorum: 750000 EGLD = 7500000 * 10^18) - "", (yes value: 0) - "", (no value: 0) - "ntGU2xmyOMAAAA==", (veto value: 7500000 * 10^18) - "", (abstain value: 0) + "ntGU2xmyOMAAAA==", (quorum: 7,500,000 EGLD denominated) + "", (yes votes: 0) + "", (no votes: 0) + "ntGU2xmyOMAAAA==", (veto votes: 7,500,000 EGLD denominated) + "", (abstain votes: 0) "dHJ1ZQ==", (proposal closed: true) "ZmFsc2U=" (proposal passed: false) ] } -``` + + [comment]: # (mx-context-auto) @@ -192,51 +200,48 @@ https://.multiversx.com/vm-values/query ```json { "scAddress": "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrlllsrujgla", - "funcName": "viewUserVoteHistory", - "args": ["
"] + "funcName": "viewDelegatedVoteInfo", + "args": ["", "
"] } ``` -the `address` represents the address in hex format. The response will contain the following json definition where all fields are base64-encoded: +- `proposal_nonce` → the proposal identifier (nonce, hex-encoded). +- `address` → the bech32 address of the account to check. + + The response will contain the following json definition where all fields are base64-encoded: ```json { "returnData": [ - "", - "", - "", - ... - "", - "", - "", - "", - ... - "" + "", (voting power already used by this address on the proposal) + "", (stake associated with the used power) + "", (total available voting power for the address) + "" (total stake considered in governance for the address) ] } ``` -Example for an address that cast votes on 5 proposals: +Example for an address that voted: ```json { "returnData": [ - "Aw==", (number of delegated nonces: 3) - "AQ==", (nonce: 1) - "Ag==", (nonce: 2) - "Aw==", (nonce: 3) - "Ag==", (number of direct nonces: 2) - "BA==", (nonce: 4) - "BQ==", (nonce: 5) + "Cg==", (used power: 10) + "ZAA=", (used stake: 100) + "A+g=", (total power: 1000) + "Gg4M=", (total stake: 10000) ] } ``` +In this example, the queried address voted on this proposal with **100 stake**, which translated into **10 voting power**. The proposal overall had **10000 total stake** and **1000 total voting power** recorded. -Example for an address that did not cast votes on any proposals: +Example for an address that did not vote: ```json { "returnData": [ - "AA==", (number of delegated nonces: 0) - "AA==", (number of direct nonces: 0) + "AA==", (used power: 0) + "AA==", (used stake: 0) + "A+g=", (total power: 1000) + "Gg4M=", (total stake: 10000) ] } ``` diff --git a/docs/governance/overview.md b/docs/governance/overview.md index a8c7d2994..361e27a03 100644 --- a/docs/governance/overview.md +++ b/docs/governance/overview.md @@ -19,54 +19,84 @@ This page provides an overview of the On-chain Governance module that will be av ## Overview -The MultiversX network is able to handle on-chain governance proposes by issuing special types of transactions. This was implemented as a mean for further increasing the decentralization of the decision-making process. +The MultiversX network enables on-chain governance by issuing special types of transactions. This mechanism increases decentralization by allowing community members to directly propose and vote on changes, such as protocol upgrades or configuration adjustments. -Anyone can create a proposal using their wallet and issuing a special type of transaction specifying an identifier, a starting epoch and an end epoch. - -Users that staked EGLD will be able to cast votes upon opened proposals. The voting power is proportional with the staked value and is computed as `voting_power = staked_value` (linear voting). +- **Anyone** can create a proposal by paying the proposal fee and specifying: + - a **commit hash** (unique identifier, usually a GitHub commit or spec reference) + - a **start epoch** and an **end epoch** (the voting window). +- **Any staked user** (direct or delegated) can vote during the active period. +- Voting power is proportional to stake: + `voting_power = staked_value` (linear voting). [comment]: # (mx-context-auto) ## Implementation details -Each proposal costs 1000 EGLD that will be locked during the voting of the proposal. If the proposal is either rejected or accepted, the entire locked sum can be unlocked & withdrawn. If the proposal do not pass, 10 EGLD will remain locked in the governance smart contract as a penalty fee. The proposal costs around 51 million of gas units. +### Proposals +- Each proposal requires paying a `ProposalFee` (currently **500 EGLD**). +- If the proposal passes or fails normally, the fee is refunded to the issuer. +- If the proposal is vetoed, the fee is slashed (transferred to the **Community Governance Pool**) or reduced by the configured `LostProposalFee`. +- Proposals cannot be duplicated: the same commit hash cannot be submitted twice. + +### Voting +- There are four vote types: **Yes**, **No**, **Abstain**, and **Veto**. +- Voting consumes gas (approx. 6 million units). +- Voting power is derived from staked and delegated EGLD. +- Delegation and liquid staking contracts can cast votes on behalf of users. +- Voting power is **linear** with stake: the more EGLD staked or delegated, the higher the voting power. +- The contract tracks both **used voting power/stake** (applied in a proposal) and **total available voting power/stake** for each address. + +### Quorum and thresholds +A proposal can pass only if all conditions are met: -There are 4 types of votes: **Yes**, **No**, **Abstain** and **Veto**. Each of the vote costs around 6 million of gas units. +- **Quorum**: at least `MinQuorum%` of the total voting power must participate. +- **Majority**: the **Yes** votes must outnumber **No** votes. +- **Pass threshold**: the **Yes** votes must represent at least `MinPassThreshold%` of all votes cast. +- **Veto threshold**: if **Veto** votes exceed `MinVetoThreshold%`, the proposal is rejected and penalized. -A user can create any number of proposals as long as it pays the locking fee & the gas used for the transaction. The proposal can be closed in any epoch following the provided end epoch. +### Closing and cleanup +- Proposals can only be closed after the end epoch has passed. +- Only the issuer can close their proposal. +- Closing finalizes the outcome (passed / failed / vetoed) and unlocks the funds. +- Expired but unclosed proposals can also be cleaned up via `clearEndedProposals` to maintain efficiency. -The quorum is computed as the sum of the staked EGLD for all addresses that cast votes. +### Governance configuration +All thresholds and fees are defined in `GovernanceConfigV2`: +- `MinQuorum` +- `MinPassThreshold` +- `MinVetoThreshold` +- `ProposalFee` +- `LostProposalFee` +- `LastProposalNonce` -The votes will be added for each category (**Yes**, **No**, **Abstain** and **Veto**). The vote is computed as `vote = total_staked_value` for each address that cast a vote. - -A proposal can pass only if all conditions are met: -- the quorum value is at least the minimumQuorumThresholdPercentage * total staked value held by the staking contracts; -- the **Yes** value > **No** value (simple majority); -- the **Yes** value is at least the minimumPassThresholdPercentage * sum of votes on all 4 categories. -- the **Veto** value did not reach the minimumVetoThresholdPercentage * sum of votes on all 4 categories; +These values are set by the system and can be updated by contract owner calls. [comment]: # (mx-context-auto) ### Example Let's suppose we have the following addresses that cast the following votes: -- `alice`: staked value 2000 EGLD that vote **Yes** -- `bob`: staked value 3000 EGLD that vote **Yes** -- `charlie`: staked value 4000 EGLD that vote **Yes** -- `delta`: staked value 1500 EGLD that vote **No** - -The quorum in this case will be a value `(2000+3000+4000+1500) * 10^18 = 10500 * 10^18`. - -The **Yes** category will hold the value `2000 * 10^18 + 3000 * 10^18 + 4000 * 10^18 = 9000 * 10^18` -The **No** category will hold the value `1500 * 10^18` -The **Abstain** and **Veto** categories will both hold 0. -The total voted value is `9000 * 10^18 + 1500 * 10^18 + 0 + 0 = 10500 * 10^18` - -Supposing the total staked value in the system is `20000 EGLD` and the minimum quorum threshold percentage is `20%`, then the minimum quorum value is `20% * 20000 = 4000 EGLD`. - -The following list contains true sentences: -- the quorum value (`10500 EGLD`) is larger than the minimum quorum (`4000 EGLD`) -- **Yes** value (`9000 * 10^18`) is larger than the **No** value (`1500 * 10^18`) -- **Yes** value (`9000 * 10^18`) is larger than the pass threshold (`50%`) \* total voted value (`10500 * 10^18`) which is `5250 * 10^18` -- the **Veto** did not reach `33%` of the total vote value because it was `0` - -To sum it all, **the proposal passed**. +- `alice`: staked value **2000 EGLD** that vote **Yes** +- `bob`: staked value **3000 EGLD** that vote **Yes** +- `charlie`: staked value **4000 EGLD** that vote **Yes** +- `delta`: staked value **1500 EGLD** that vote **No** + +The totals are: +- Quorum = `(2000+3000+4000+1500) = 10,500 EGLD` +- **Yes** = `9000 EGLD` +- **No** = `1500 EGLD` +- **Abstain** = `0` +- **Veto** = `0` + +Assume: +- total staked in the system = `20,000 EGLD` +- `MinQuorum` = 20% +- `MinPassThreshold` = 50% +- `MinVetoThreshold` = 33% + +Evaluation: +- Quorum is satisfied: `10,500 > 4000` +- Yes > No: `9000 > 1500` +- Yes is ≥ 50% of votes: `9000 / 10,500 = 85.7%` +- Veto is below 33%: `0 < 3465` + +To sum it all, the proposal **passes**. diff --git a/docs/governance/updated_governance-interaction.md b/docs/governance/updated_governance-interaction.md new file mode 100644 index 000000000..83beded32 --- /dev/null +++ b/docs/governance/updated_governance-interaction.md @@ -0,0 +1,247 @@ +--- +id: governance-interaction +title: Governance interaction +--- + +[comment]: # (mx-context-auto) + +### Introduction + +The interaction with the governance system smartcontract is done through correctly formatted transactions to submit actions and through the usage of the vm-query REST API calls for reading the proposal(s) status. + +[comment]: # (mx-context-auto) + +### Creating a proposal + +The proposal creation transaction has the following parameters: + +```rust +GovernanceProposalTransaction { + Sender: + Receiver: erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrlllsrujgla + Value: 1000 EGLD + GasLimit: 51000000 + Data: "proposal" + + "@" + + "@" + + "@" +} +``` + +The proposal identifier is a hex string containing exactly 40 characters. Usually, this can be a git commit hash on which the proposal is made but can be any other identifier string. + +The starting & ending epochs should be an even-length hex string containing the starting epoch and the ending epoch. During this time frame the votes can be cast. + +After issuing the proposal, there is a log event generated having the `proposal` identifier that will contain the following encoded topics: + +- `nonce` as encoded integer which uniquely identifies the proposals +- `identifier` the provided 40 hex characters longs identifier +- `start epoch` as encoded integer for the starting epoch +- `end epoch` as encoded integer for the ending epoch + +[comment]: # (mx-context-auto) + +### Voting a proposal using the direct staked or delegation-system amount + +Any wallet that has staked EGLD (either direct staked or through the delegation sub-system) can cast a vote for a proposal. +```rust +GovernanceVoteTransaction { + Sender: + Receiver: erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrlllsrujgla + Value: 0 EGLD + GasLimit: 6000000 + Data: "vote" + + "@" + + "@" +} +``` + +The `nonce` is the hex encoded value of the proposal's unique nonce and the `vote_type` can be one of the following values: +- for **Yes**: `796573`; +- for **No**: `6e6f`; +- for **Abstain**: `6162737461696e`; +- for **Veto**: `7665746f`. + +The vote value for the account that will vote a proposal is the sum of all staked values along with the sum of all delegated values in the delegation sub-system. + +[comment]: # (mx-context-auto) + +### Voting a proposal through smart contracts (delegation voting) + +Whenever we deal with a smart contract that delegated through the delegation sub-system or owns staked nodes it is the out of scope of the metachain's governance contract to track each address that sent EGLD how much is really staked (if any EGLD is staked). +That is why we offered an endpoint to the governance smart contact that can be called **only by a shard smart contract** and the governance contract will record the address provided, the vote type and vote value. +This is very useful whenever implementing liquid-staking-like smart contracts. The liquid-staking contract knows the balance for each user, so it will delegate the call to the governance contract providing the value. + +:::important +The maximum value for the staked value & the voting power for the liquid-staking contract is known by the governance contract, so it can not counterfeit the voting. If, due to a bug the liquid-staking contract allows, for example, for a user to vote with a larger quota, the liquid-staking contract will deplete its maximum allowance making other voting transactions (on his behalf) to fail. +::: + +```rust +GovernanceVoteThourghDelegationTransaction { + Sender: + Receiver: erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrlllsrujgla + Value: 0 EGLD + GasLimit: 51000000 + Data: "delegateVote" + + "@" + + "@" + + "@" + + "@" +} +``` + +The `nonce` is the hex encoded value of the proposal's unique nonce and the `vote_type` can be one of the following values: +- for **Yes**: `796573`; +- for **No**: `6e6f`; +- for **Abstain**: `6162737461696e`; +- for **Veto**: `7665746f`. + +The `account address handled by the smart contract` is the address handled by the smart contract that will delegate the vote towards the governance smart contract. This address will be recorded for casting the vote. +The `vote_balance` is the amount of stake the address has in the smart contract. The governance contract will "believe" that this is the right amount as it impossible to verify the information. The balance will diminish the total voting power the smart contract has. + +[comment]: # (mx-context-auto) + +### Closing a proposal + +A proposal can be closed only in an epoch that is strictly higher than the end epoch value provided when the proposal was opened. +Closing can only be performed by the account that created the proposal (the issuer). + +```rust +CloseProposalTransaction { + Sender: + Receiver: erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrlllsrujgla + Value: 0 EGLD + GasLimit: 51000000 + Data: "closeProposal" + + "@" +} +``` +#### Rules for closing +- Only the issuer can call `closeProposal`. +- If the proposal **passes** → the full proposal fee is refunded. +- If the proposal **fails** or is **vetoed** → the refund is reduced by the `LostProposalFee`. +- Once a proposal is closed, it cannot be reopened. +- - Closing also finalizes the vote tally (the proposal is marked as `Passed` or not, based on the results). + + +[comment]: # (mx-context-auto) + +### Querying the status of a proposal + +The status of any proposal can be queried at any time through `vm-values/query` REST API endpoints provided by the gateway/API. + +```bash +https://.multiversx.com/vm-values/query +``` + +```json +{ + "scAddress": "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrlllsrujgla", + "funcName": "viewProposal", + "args": [""] +} +``` + +- The argument `nonce` is the proposal nonce in hex format. +- The response will contain the following json definition where all fields are base64-encoded: + +```json +{ + "returnData": [ + "", (amount locked by proposer) + "", (unique identifier of the proposal) + "", (proposal number) + "", (address of the proposer) + "", (epoch when voting starts) + "", (epoch when voting ends) + "", (minimum stake required to reach quorum) + "", (total stake voting YES) + "", (total stake voting NO) + "", (total stake vetoing the proposal) + "", (total stake abstaining) + "", + "" + ] +} +``` + +Example response: +```json +{ + "returnData": [ + "NjXJrcXeoAAA", (proposal locked amount: 1000 EGLD denominated = 1000 * 10^18) + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABg==", (commit hash: 0x0000...006) + "AQ==", (nonce: 1) + "aj88GtqHy9ibm5ePPQlG4aqLhpgqsQWygoTppckLa4M=", (proposer address) + "bQ==", (starting epoch: 109) + "bg==", (ending epoch: 110) + "ntGU2xmyOMAAAA==", (quorum: 7,500,000 EGLD denominated) + "", (yes votes: 0) + "", (no votes: 0) + "ntGU2xmyOMAAAA==", (veto votes: 7,500,000 EGLD denominated) + "", (abstain votes: 0) + "dHJ1ZQ==", (proposal closed: true) + "ZmFsc2U=" (proposal passed: false) + ] +} + + + +[comment]: # (mx-context-auto) + +### Querying an address voting status (direct staking or system delegation) + +The voting status of a certain address can be queried at any time by using the `vm-values/query` REST API endpoints provided by the gateway/API. + +```bash +https://.multiversx.com/vm-values/query +``` + +```json +{ + "scAddress": "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrlllsrujgla", + "funcName": "viewDelegatedVoteInfo", + "args": ["", "
"] +} +``` + +- `proposal_nonce` → the proposal identifier (nonce, hex-encoded). +- `address` → the bech32 address of the account to check. + + The response will contain the following json definition where all fields are base64-encoded: + +```json +{ + "returnData": [ + "", (voting power already used by this address on the proposal) + "", (stake associated with the used power) + "", (total available voting power for the address) + "" (total stake considered in governance for the address) + ] +} +``` + +Example for an address that voted: +```json +{ + "returnData": [ + "Cg==", (used power: 10) + "ZAA=", (used stake: 100) + "A+g=", (total power: 1000) + "Gg4M=", (total stake: 10000) + ] +} +``` +In this example, the queried address voted on this proposal with **100 stake**, which translated into **10 voting power**. The proposal overall had **10000 total stake** and **1000 total voting power** recorded. + +Example for an address that did not vote: +```json +{ + "returnData": [ + "AA==", (used power: 0) + "AA==", (used stake: 0) + "A+g=", (total power: 1000) + "Gg4M=", (total stake: 10000) + ] +} +``` diff --git a/docs/governance/updated_overview.md b/docs/governance/updated_overview.md new file mode 100644 index 000000000..361e27a03 --- /dev/null +++ b/docs/governance/updated_overview.md @@ -0,0 +1,102 @@ +--- +id: overview +title: Governance - Overview +--- + +[comment]: # (mx-abstract) + +This page provides an overview of the On-chain Governance module that will be available on the `v1.6.x` release. + +[comment]: # (mx-context-auto) + +## Table of contents + +| Name | Description | +|-----------------------------------------------------------------------------------|--------------------------------------------------------------------------| +| [Interacting with the on-chain governance SC](/governance/governance-interaction) | Interacting with the governance system smart contract. | + +[comment]: # (mx-context-auto) + +## Overview + +The MultiversX network enables on-chain governance by issuing special types of transactions. This mechanism increases decentralization by allowing community members to directly propose and vote on changes, such as protocol upgrades or configuration adjustments. + +- **Anyone** can create a proposal by paying the proposal fee and specifying: + - a **commit hash** (unique identifier, usually a GitHub commit or spec reference) + - a **start epoch** and an **end epoch** (the voting window). +- **Any staked user** (direct or delegated) can vote during the active period. +- Voting power is proportional to stake: + `voting_power = staked_value` (linear voting). + +[comment]: # (mx-context-auto) + +## Implementation details + +### Proposals +- Each proposal requires paying a `ProposalFee` (currently **500 EGLD**). +- If the proposal passes or fails normally, the fee is refunded to the issuer. +- If the proposal is vetoed, the fee is slashed (transferred to the **Community Governance Pool**) or reduced by the configured `LostProposalFee`. +- Proposals cannot be duplicated: the same commit hash cannot be submitted twice. + +### Voting +- There are four vote types: **Yes**, **No**, **Abstain**, and **Veto**. +- Voting consumes gas (approx. 6 million units). +- Voting power is derived from staked and delegated EGLD. +- Delegation and liquid staking contracts can cast votes on behalf of users. +- Voting power is **linear** with stake: the more EGLD staked or delegated, the higher the voting power. +- The contract tracks both **used voting power/stake** (applied in a proposal) and **total available voting power/stake** for each address. + +### Quorum and thresholds +A proposal can pass only if all conditions are met: + +- **Quorum**: at least `MinQuorum%` of the total voting power must participate. +- **Majority**: the **Yes** votes must outnumber **No** votes. +- **Pass threshold**: the **Yes** votes must represent at least `MinPassThreshold%` of all votes cast. +- **Veto threshold**: if **Veto** votes exceed `MinVetoThreshold%`, the proposal is rejected and penalized. + +### Closing and cleanup +- Proposals can only be closed after the end epoch has passed. +- Only the issuer can close their proposal. +- Closing finalizes the outcome (passed / failed / vetoed) and unlocks the funds. +- Expired but unclosed proposals can also be cleaned up via `clearEndedProposals` to maintain efficiency. + +### Governance configuration +All thresholds and fees are defined in `GovernanceConfigV2`: +- `MinQuorum` +- `MinPassThreshold` +- `MinVetoThreshold` +- `ProposalFee` +- `LostProposalFee` +- `LastProposalNonce` + +These values are set by the system and can be updated by contract owner calls. + +[comment]: # (mx-context-auto) + +### Example +Let's suppose we have the following addresses that cast the following votes: +- `alice`: staked value **2000 EGLD** that vote **Yes** +- `bob`: staked value **3000 EGLD** that vote **Yes** +- `charlie`: staked value **4000 EGLD** that vote **Yes** +- `delta`: staked value **1500 EGLD** that vote **No** + +The totals are: +- Quorum = `(2000+3000+4000+1500) = 10,500 EGLD` +- **Yes** = `9000 EGLD` +- **No** = `1500 EGLD` +- **Abstain** = `0` +- **Veto** = `0` + +Assume: +- total staked in the system = `20,000 EGLD` +- `MinQuorum` = 20% +- `MinPassThreshold` = 50% +- `MinVetoThreshold` = 33% + +Evaluation: +- Quorum is satisfied: `10,500 > 4000` +- Yes > No: `9000 > 1500` +- Yes is ≥ 50% of votes: `9000 / 10,500 = 85.7%` +- Veto is below 33%: `0 < 3465` + +To sum it all, the proposal **passes**. From 37ebcdb2dd648b2cdfe18acf15cda7d7bf35cc0f Mon Sep 17 00:00:00 2001 From: Sara Sorici Date: Mon, 1 Sep 2025 11:08:29 +0300 Subject: [PATCH 10/19] chore(docs): remove duplicate governance files --- .../updated_governance-interaction.md | 247 ------------------ docs/governance/updated_overview.md | 102 -------- 2 files changed, 349 deletions(-) delete mode 100644 docs/governance/updated_governance-interaction.md delete mode 100644 docs/governance/updated_overview.md diff --git a/docs/governance/updated_governance-interaction.md b/docs/governance/updated_governance-interaction.md deleted file mode 100644 index 83beded32..000000000 --- a/docs/governance/updated_governance-interaction.md +++ /dev/null @@ -1,247 +0,0 @@ ---- -id: governance-interaction -title: Governance interaction ---- - -[comment]: # (mx-context-auto) - -### Introduction - -The interaction with the governance system smartcontract is done through correctly formatted transactions to submit actions and through the usage of the vm-query REST API calls for reading the proposal(s) status. - -[comment]: # (mx-context-auto) - -### Creating a proposal - -The proposal creation transaction has the following parameters: - -```rust -GovernanceProposalTransaction { - Sender: - Receiver: erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrlllsrujgla - Value: 1000 EGLD - GasLimit: 51000000 - Data: "proposal" + - "@" + - "@" + - "@" -} -``` - -The proposal identifier is a hex string containing exactly 40 characters. Usually, this can be a git commit hash on which the proposal is made but can be any other identifier string. - -The starting & ending epochs should be an even-length hex string containing the starting epoch and the ending epoch. During this time frame the votes can be cast. - -After issuing the proposal, there is a log event generated having the `proposal` identifier that will contain the following encoded topics: - -- `nonce` as encoded integer which uniquely identifies the proposals -- `identifier` the provided 40 hex characters longs identifier -- `start epoch` as encoded integer for the starting epoch -- `end epoch` as encoded integer for the ending epoch - -[comment]: # (mx-context-auto) - -### Voting a proposal using the direct staked or delegation-system amount - -Any wallet that has staked EGLD (either direct staked or through the delegation sub-system) can cast a vote for a proposal. -```rust -GovernanceVoteTransaction { - Sender: - Receiver: erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrlllsrujgla - Value: 0 EGLD - GasLimit: 6000000 - Data: "vote" + - "@" + - "@" -} -``` - -The `nonce` is the hex encoded value of the proposal's unique nonce and the `vote_type` can be one of the following values: -- for **Yes**: `796573`; -- for **No**: `6e6f`; -- for **Abstain**: `6162737461696e`; -- for **Veto**: `7665746f`. - -The vote value for the account that will vote a proposal is the sum of all staked values along with the sum of all delegated values in the delegation sub-system. - -[comment]: # (mx-context-auto) - -### Voting a proposal through smart contracts (delegation voting) - -Whenever we deal with a smart contract that delegated through the delegation sub-system or owns staked nodes it is the out of scope of the metachain's governance contract to track each address that sent EGLD how much is really staked (if any EGLD is staked). -That is why we offered an endpoint to the governance smart contact that can be called **only by a shard smart contract** and the governance contract will record the address provided, the vote type and vote value. -This is very useful whenever implementing liquid-staking-like smart contracts. The liquid-staking contract knows the balance for each user, so it will delegate the call to the governance contract providing the value. - -:::important -The maximum value for the staked value & the voting power for the liquid-staking contract is known by the governance contract, so it can not counterfeit the voting. If, due to a bug the liquid-staking contract allows, for example, for a user to vote with a larger quota, the liquid-staking contract will deplete its maximum allowance making other voting transactions (on his behalf) to fail. -::: - -```rust -GovernanceVoteThourghDelegationTransaction { - Sender: - Receiver: erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrlllsrujgla - Value: 0 EGLD - GasLimit: 51000000 - Data: "delegateVote" + - "@" + - "@" + - "@" + - "@" -} -``` - -The `nonce` is the hex encoded value of the proposal's unique nonce and the `vote_type` can be one of the following values: -- for **Yes**: `796573`; -- for **No**: `6e6f`; -- for **Abstain**: `6162737461696e`; -- for **Veto**: `7665746f`. - -The `account address handled by the smart contract` is the address handled by the smart contract that will delegate the vote towards the governance smart contract. This address will be recorded for casting the vote. -The `vote_balance` is the amount of stake the address has in the smart contract. The governance contract will "believe" that this is the right amount as it impossible to verify the information. The balance will diminish the total voting power the smart contract has. - -[comment]: # (mx-context-auto) - -### Closing a proposal - -A proposal can be closed only in an epoch that is strictly higher than the end epoch value provided when the proposal was opened. -Closing can only be performed by the account that created the proposal (the issuer). - -```rust -CloseProposalTransaction { - Sender: - Receiver: erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrlllsrujgla - Value: 0 EGLD - GasLimit: 51000000 - Data: "closeProposal" + - "@" -} -``` -#### Rules for closing -- Only the issuer can call `closeProposal`. -- If the proposal **passes** → the full proposal fee is refunded. -- If the proposal **fails** or is **vetoed** → the refund is reduced by the `LostProposalFee`. -- Once a proposal is closed, it cannot be reopened. -- - Closing also finalizes the vote tally (the proposal is marked as `Passed` or not, based on the results). - - -[comment]: # (mx-context-auto) - -### Querying the status of a proposal - -The status of any proposal can be queried at any time through `vm-values/query` REST API endpoints provided by the gateway/API. - -```bash -https://.multiversx.com/vm-values/query -``` - -```json -{ - "scAddress": "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrlllsrujgla", - "funcName": "viewProposal", - "args": [""] -} -``` - -- The argument `nonce` is the proposal nonce in hex format. -- The response will contain the following json definition where all fields are base64-encoded: - -```json -{ - "returnData": [ - "", (amount locked by proposer) - "", (unique identifier of the proposal) - "", (proposal number) - "", (address of the proposer) - "", (epoch when voting starts) - "", (epoch when voting ends) - "", (minimum stake required to reach quorum) - "", (total stake voting YES) - "", (total stake voting NO) - "", (total stake vetoing the proposal) - "", (total stake abstaining) - "", - "" - ] -} -``` - -Example response: -```json -{ - "returnData": [ - "NjXJrcXeoAAA", (proposal locked amount: 1000 EGLD denominated = 1000 * 10^18) - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABg==", (commit hash: 0x0000...006) - "AQ==", (nonce: 1) - "aj88GtqHy9ibm5ePPQlG4aqLhpgqsQWygoTppckLa4M=", (proposer address) - "bQ==", (starting epoch: 109) - "bg==", (ending epoch: 110) - "ntGU2xmyOMAAAA==", (quorum: 7,500,000 EGLD denominated) - "", (yes votes: 0) - "", (no votes: 0) - "ntGU2xmyOMAAAA==", (veto votes: 7,500,000 EGLD denominated) - "", (abstain votes: 0) - "dHJ1ZQ==", (proposal closed: true) - "ZmFsc2U=" (proposal passed: false) - ] -} - - - -[comment]: # (mx-context-auto) - -### Querying an address voting status (direct staking or system delegation) - -The voting status of a certain address can be queried at any time by using the `vm-values/query` REST API endpoints provided by the gateway/API. - -```bash -https://.multiversx.com/vm-values/query -``` - -```json -{ - "scAddress": "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrlllsrujgla", - "funcName": "viewDelegatedVoteInfo", - "args": ["", "
"] -} -``` - -- `proposal_nonce` → the proposal identifier (nonce, hex-encoded). -- `address` → the bech32 address of the account to check. - - The response will contain the following json definition where all fields are base64-encoded: - -```json -{ - "returnData": [ - "", (voting power already used by this address on the proposal) - "", (stake associated with the used power) - "", (total available voting power for the address) - "" (total stake considered in governance for the address) - ] -} -``` - -Example for an address that voted: -```json -{ - "returnData": [ - "Cg==", (used power: 10) - "ZAA=", (used stake: 100) - "A+g=", (total power: 1000) - "Gg4M=", (total stake: 10000) - ] -} -``` -In this example, the queried address voted on this proposal with **100 stake**, which translated into **10 voting power**. The proposal overall had **10000 total stake** and **1000 total voting power** recorded. - -Example for an address that did not vote: -```json -{ - "returnData": [ - "AA==", (used power: 0) - "AA==", (used stake: 0) - "A+g=", (total power: 1000) - "Gg4M=", (total stake: 10000) - ] -} -``` diff --git a/docs/governance/updated_overview.md b/docs/governance/updated_overview.md deleted file mode 100644 index 361e27a03..000000000 --- a/docs/governance/updated_overview.md +++ /dev/null @@ -1,102 +0,0 @@ ---- -id: overview -title: Governance - Overview ---- - -[comment]: # (mx-abstract) - -This page provides an overview of the On-chain Governance module that will be available on the `v1.6.x` release. - -[comment]: # (mx-context-auto) - -## Table of contents - -| Name | Description | -|-----------------------------------------------------------------------------------|--------------------------------------------------------------------------| -| [Interacting with the on-chain governance SC](/governance/governance-interaction) | Interacting with the governance system smart contract. | - -[comment]: # (mx-context-auto) - -## Overview - -The MultiversX network enables on-chain governance by issuing special types of transactions. This mechanism increases decentralization by allowing community members to directly propose and vote on changes, such as protocol upgrades or configuration adjustments. - -- **Anyone** can create a proposal by paying the proposal fee and specifying: - - a **commit hash** (unique identifier, usually a GitHub commit or spec reference) - - a **start epoch** and an **end epoch** (the voting window). -- **Any staked user** (direct or delegated) can vote during the active period. -- Voting power is proportional to stake: - `voting_power = staked_value` (linear voting). - -[comment]: # (mx-context-auto) - -## Implementation details - -### Proposals -- Each proposal requires paying a `ProposalFee` (currently **500 EGLD**). -- If the proposal passes or fails normally, the fee is refunded to the issuer. -- If the proposal is vetoed, the fee is slashed (transferred to the **Community Governance Pool**) or reduced by the configured `LostProposalFee`. -- Proposals cannot be duplicated: the same commit hash cannot be submitted twice. - -### Voting -- There are four vote types: **Yes**, **No**, **Abstain**, and **Veto**. -- Voting consumes gas (approx. 6 million units). -- Voting power is derived from staked and delegated EGLD. -- Delegation and liquid staking contracts can cast votes on behalf of users. -- Voting power is **linear** with stake: the more EGLD staked or delegated, the higher the voting power. -- The contract tracks both **used voting power/stake** (applied in a proposal) and **total available voting power/stake** for each address. - -### Quorum and thresholds -A proposal can pass only if all conditions are met: - -- **Quorum**: at least `MinQuorum%` of the total voting power must participate. -- **Majority**: the **Yes** votes must outnumber **No** votes. -- **Pass threshold**: the **Yes** votes must represent at least `MinPassThreshold%` of all votes cast. -- **Veto threshold**: if **Veto** votes exceed `MinVetoThreshold%`, the proposal is rejected and penalized. - -### Closing and cleanup -- Proposals can only be closed after the end epoch has passed. -- Only the issuer can close their proposal. -- Closing finalizes the outcome (passed / failed / vetoed) and unlocks the funds. -- Expired but unclosed proposals can also be cleaned up via `clearEndedProposals` to maintain efficiency. - -### Governance configuration -All thresholds and fees are defined in `GovernanceConfigV2`: -- `MinQuorum` -- `MinPassThreshold` -- `MinVetoThreshold` -- `ProposalFee` -- `LostProposalFee` -- `LastProposalNonce` - -These values are set by the system and can be updated by contract owner calls. - -[comment]: # (mx-context-auto) - -### Example -Let's suppose we have the following addresses that cast the following votes: -- `alice`: staked value **2000 EGLD** that vote **Yes** -- `bob`: staked value **3000 EGLD** that vote **Yes** -- `charlie`: staked value **4000 EGLD** that vote **Yes** -- `delta`: staked value **1500 EGLD** that vote **No** - -The totals are: -- Quorum = `(2000+3000+4000+1500) = 10,500 EGLD` -- **Yes** = `9000 EGLD` -- **No** = `1500 EGLD` -- **Abstain** = `0` -- **Veto** = `0` - -Assume: -- total staked in the system = `20,000 EGLD` -- `MinQuorum` = 20% -- `MinPassThreshold` = 50% -- `MinVetoThreshold` = 33% - -Evaluation: -- Quorum is satisfied: `10,500 > 4000` -- Yes > No: `9000 > 1500` -- Yes is ≥ 50% of votes: `9000 / 10,500 = 85.7%` -- Veto is below 33%: `0 < 3465` - -To sum it all, the proposal **passes**. From 17d787b4df23ed5291e9b0bcfd8a7bcdba76de24 Mon Sep 17 00:00:00 2001 From: Sara Sorici Date: Mon, 1 Sep 2025 11:17:13 +0300 Subject: [PATCH 11/19] WIP: save local changes --- docs/governance/governance-interaction.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/governance/governance-interaction.md b/docs/governance/governance-interaction.md index 83beded32..36c10267d 100644 --- a/docs/governance/governance-interaction.md +++ b/docs/governance/governance-interaction.md @@ -121,7 +121,7 @@ CloseProposalTransaction { - If the proposal **passes** → the full proposal fee is refunded. - If the proposal **fails** or is **vetoed** → the refund is reduced by the `LostProposalFee`. - Once a proposal is closed, it cannot be reopened. -- - Closing also finalizes the vote tally (the proposal is marked as `Passed` or not, based on the results). +- Closing also finalizes the vote tally (the proposal is marked as `Passed` or not, based on the results). [comment]: # (mx-context-auto) From 283e1b7a44761dc0d10e8249ba7cdb77829f2b1b Mon Sep 17 00:00:00 2001 From: Sara Sorici Date: Tue, 2 Sep 2025 10:13:40 +0300 Subject: [PATCH 12/19] Update governance docs: clarified querying voting status --- docs/governance/governance-interaction.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/governance/governance-interaction.md b/docs/governance/governance-interaction.md index 36c10267d..f0c7304ff 100644 --- a/docs/governance/governance-interaction.md +++ b/docs/governance/governance-interaction.md @@ -148,7 +148,7 @@ https://.multiversx.com/vm-values/query ```json { "returnData": [ - "", (amount locked by proposer) + "", (amount locked by proposer) "", (unique identifier of the proposal) "", (proposal number) "", (address of the proposer) From d63b7ffe5c9defd2ff8b9d2993485b6e610bb5fd Mon Sep 17 00:00:00 2001 From: Sara Sorici Date: Tue, 2 Sep 2025 10:37:22 +0300 Subject: [PATCH 13/19] Update governance docs: clarified older viewUserVoteHistory function --- docs/governance/governance-interaction.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/governance/governance-interaction.md b/docs/governance/governance-interaction.md index f0c7304ff..d8aa425a9 100644 --- a/docs/governance/governance-interaction.md +++ b/docs/governance/governance-interaction.md @@ -138,7 +138,7 @@ https://.multiversx.com/vm-values/query { "scAddress": "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrlllsrujgla", "funcName": "viewProposal", - "args": [""] + "args": [""] } ``` @@ -154,7 +154,7 @@ https://.multiversx.com/vm-values/query "", (address of the proposer) "", (epoch when voting starts) "", (epoch when voting ends) - "", (minimum stake required to reach quorum) + "", (current quorum stake: sum of stake that participated) "", (total stake voting YES) "", (total stake voting NO) "", (total stake vetoing the proposal) @@ -201,14 +201,17 @@ https://.multiversx.com/vm-values/query { "scAddress": "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrlllsrujgla", "funcName": "viewDelegatedVoteInfo", - "args": ["", "
"] + "args": ["", "
"] } ``` - `proposal_nonce` → the proposal identifier (nonce, hex-encoded). - `address` → the bech32 address of the account to check. - The response will contain the following json definition where all fields are base64-encoded: +> **Note:** The older function `viewUserVoteHistory` (which returned lists of proposal nonces) is now considered legacy. +> Use `viewDelegatedVoteInfo` for detailed voting power and stake information. + +The response will contain the following json definition where all fields are base64-encoded: ```json { From 4208eaf36bf0d9233e4f4112793859bf379f2feb Mon Sep 17 00:00:00 2001 From: Sara Sorici Date: Tue, 2 Sep 2025 10:51:49 +0300 Subject: [PATCH 14/19] Update governance overview: clarified quorum and thresholds with config references --- docs/governance/overview.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/governance/overview.md b/docs/governance/overview.md index 361e27a03..457566daf 100644 --- a/docs/governance/overview.md +++ b/docs/governance/overview.md @@ -37,6 +37,9 @@ The MultiversX network enables on-chain governance by issuing special types of t - If the proposal passes or fails normally, the fee is refunded to the issuer. - If the proposal is vetoed, the fee is slashed (transferred to the **Community Governance Pool**) or reduced by the configured `LostProposalFee`. - Proposals cannot be duplicated: the same commit hash cannot be submitted twice. +- Proposals can be **cancelled** by the issuer **before the voting period starts**. (introduced in v1.10.0 Barnard release) +- Lost proposal fees may either be accumulated in the governance contract (retrievable by the contract owner) or redirected to a **Community Governance Pool**, depending on configuration. + ### Voting - There are four vote types: **Yes**, **No**, **Abstain**, and **Veto**. @@ -49,10 +52,9 @@ The MultiversX network enables on-chain governance by issuing special types of t ### Quorum and thresholds A proposal can pass only if all conditions are met: -- **Quorum**: at least `MinQuorum%` of the total voting power must participate. -- **Majority**: the **Yes** votes must outnumber **No** votes. -- **Pass threshold**: the **Yes** votes must represent at least `MinPassThreshold%` of all votes cast. -- **Veto threshold**: if **Veto** votes exceed `MinVetoThreshold%`, the proposal is rejected and penalized. +- **Quorum**: at least `MinQuorum%` of the total voting power must participate. (currently at least **20%** of total voting power) +- **Acceptance threshold**: YES / (YES + NO + VETO) ≥ **66.67%** (`MinPassThreshold`) +- **Veto threshold**: if VETO votes exceed **33%** of total participating voting power, the proposal is rejected and the proposal fee is slashed. ### Closing and cleanup - Proposals can only be closed after the end epoch has passed. From c0e65eb4300df505c7aaf8085c3c98215a097d21 Mon Sep 17 00:00:00 2001 From: LaraTifui <121812597+LaraTifui@users.noreply.github.com> Date: Thu, 4 Sep 2025 05:10:36 +0200 Subject: [PATCH 15/19] update - gov interaction --- docs/governance/governance-interaction.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/governance/governance-interaction.md b/docs/governance/governance-interaction.md index be17221e6..12c0bd9f9 100644 --- a/docs/governance/governance-interaction.md +++ b/docs/governance/governance-interaction.md @@ -7,7 +7,7 @@ title: Governance interaction ### Introduction -The interaction with the governance system smartcontract is done through correctly formatted transactions to submit actions and through the usage of the vm-query REST API calls for reading the proposal(s) status. +The interaction with the governance system smartcontract is done through correctly formatted transactions to submit actions and through the usage of the vm-query REST API calls for reading the proposal(s) status. [comment]: # (mx-context-auto) @@ -84,6 +84,7 @@ After issuing the vote, a log event is generated containing the `proposal` ident Whenever we deal with a smart contract that delegated through the delegation sub-system or owns staked nodes it is the out of scope of the metachain's governance contract to track each address that sent EGLD how much is really staked (if any EGLD is staked). That is why we offered an endpoint to the governance smart contact that can be called **only by a shard smart contract** and the governance contract will record the address provided, the vote type and vote value. + This is very useful whenever implementing liquid-staking-like smart contracts. The liquid-staking contract knows the balance for each user, so it will delegate the call to the governance contract providing the value. :::important From 20d57a6a213ba1cae843c9f37762e64387517cba Mon Sep 17 00:00:00 2001 From: Lara Tifui Date: Thu, 4 Sep 2025 11:39:05 +0200 Subject: [PATCH 16/19] your message for docs/governance/overview.md --- docs/governance/overview.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/docs/governance/overview.md b/docs/governance/overview.md index 457566daf..cc4ee8e86 100644 --- a/docs/governance/overview.md +++ b/docs/governance/overview.md @@ -33,7 +33,13 @@ The MultiversX network enables on-chain governance by issuing special types of t ## Implementation details ### Proposals -- Each proposal requires paying a `ProposalFee` (currently **500 EGLD**). + +In order for a proposal to be submitted, the following requierments need to be met: + - For a period of at least 15 days, the proposal needs to be published on Agora for public debate and analysis; + - Each proposal requires paying a `ProposalFee` (currently **500 EGLD**); + - Each proposal costs around 51 million of gas units to be submitted. + - The starting epoch of the proposal's voting period needs to be set inside an interval of 30 epochs from the epoch in which the proposal was submitted; + - If the proposal passes or fails normally, the fee is refunded to the issuer. - If the proposal is vetoed, the fee is slashed (transferred to the **Community Governance Pool**) or reduced by the configured `LostProposalFee`. - Proposals cannot be duplicated: the same commit hash cannot be submitted twice. @@ -42,10 +48,11 @@ The MultiversX network enables on-chain governance by issuing special types of t ### Voting +- The voting starts from the provided starting epoch and lasts exactly *10 days*. - There are four vote types: **Yes**, **No**, **Abstain**, and **Veto**. - Voting consumes gas (approx. 6 million units). - Voting power is derived from staked and delegated EGLD. -- Delegation and liquid staking contracts can cast votes on behalf of users. +- Delegation and liquid staking () contracts can cast votes on behalf of users. - Voting power is **linear** with stake: the more EGLD staked or delegated, the higher the voting power. - The contract tracks both **used voting power/stake** (applied in a proposal) and **total available voting power/stake** for each address. From ac32312dad877fe30aaf82036a7b356487c0962b Mon Sep 17 00:00:00 2001 From: Lara Tifui Date: Thu, 4 Sep 2025 11:45:25 +0200 Subject: [PATCH 17/19] update - gov overview - alignment --- docs/governance/overview.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/governance/overview.md b/docs/governance/overview.md index cc4ee8e86..7cec06f44 100644 --- a/docs/governance/overview.md +++ b/docs/governance/overview.md @@ -35,10 +35,11 @@ The MultiversX network enables on-chain governance by issuing special types of t ### Proposals In order for a proposal to be submitted, the following requierments need to be met: - - For a period of at least 15 days, the proposal needs to be published on Agora for public debate and analysis; - - Each proposal requires paying a `ProposalFee` (currently **500 EGLD**); - - Each proposal costs around 51 million of gas units to be submitted. - - The starting epoch of the proposal's voting period needs to be set inside an interval of 30 epochs from the epoch in which the proposal was submitted; + - For a period of at least 15 days, the proposal needs to be published on Agora for public debate and analysis; + - Each proposal requires paying a `ProposalFee` (currently **500 EGLD**); + - Each proposal costs around 51 million of gas units to be submitted. + - The starting epoch of the proposal's voting period needs to be set inside an interval of 30 epochs from the epoch in which the proposal was submitted; + - If the proposal passes or fails normally, the fee is refunded to the issuer. - If the proposal is vetoed, the fee is slashed (transferred to the **Community Governance Pool**) or reduced by the configured `LostProposalFee`. From cc973059be4ea2aecfd0e8218d4d29944b46723f Mon Sep 17 00:00:00 2001 From: Lara Tifui Date: Thu, 4 Sep 2025 12:14:40 +0200 Subject: [PATCH 18/19] update - gov overview - fix alignment issue --- docs/governance/overview.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/governance/overview.md b/docs/governance/overview.md index 7cec06f44..db53fabca 100644 --- a/docs/governance/overview.md +++ b/docs/governance/overview.md @@ -35,12 +35,12 @@ The MultiversX network enables on-chain governance by issuing special types of t ### Proposals In order for a proposal to be submitted, the following requierments need to be met: - - For a period of at least 15 days, the proposal needs to be published on Agora for public debate and analysis; - - Each proposal requires paying a `ProposalFee` (currently **500 EGLD**); - - Each proposal costs around 51 million of gas units to be submitted. - - The starting epoch of the proposal's voting period needs to be set inside an interval of 30 epochs from the epoch in which the proposal was submitted; - +- For a period of at least 15 days, the proposal needs to be published on Agora for public debate and analysis; +- Each proposal requires paying a `ProposalFee` (currently **500 EGLD**); +- Each proposal costs around 51 million of gas units to be submitted. +- The starting epoch of the proposal's voting period needs to be set inside an interval of 30 epochs from the epoch in which the proposal was submitted; +After a proposal is created, the following rules will apply: - If the proposal passes or fails normally, the fee is refunded to the issuer. - If the proposal is vetoed, the fee is slashed (transferred to the **Community Governance Pool**) or reduced by the configured `LostProposalFee`. - Proposals cannot be duplicated: the same commit hash cannot be submitted twice. From d64eff3be25582c0927c68d8ce019f78255bd720 Mon Sep 17 00:00:00 2001 From: LaraTifui <121812597+LaraTifui@users.noreply.github.com> Date: Thu, 4 Sep 2025 12:52:27 +0200 Subject: [PATCH 19/19] Update overview.md spelling error --- docs/governance/overview.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/governance/overview.md b/docs/governance/overview.md index db53fabca..cb20d3eea 100644 --- a/docs/governance/overview.md +++ b/docs/governance/overview.md @@ -34,7 +34,7 @@ The MultiversX network enables on-chain governance by issuing special types of t ### Proposals -In order for a proposal to be submitted, the following requierments need to be met: +In order for a proposal to be submitted, the following requirements need to be met: - For a period of at least 15 days, the proposal needs to be published on Agora for public debate and analysis; - Each proposal requires paying a `ProposalFee` (currently **500 EGLD**); - Each proposal costs around 51 million of gas units to be submitted.