Interacting with the Referenda Precompile¶
Introduction¶
As a Polkadot parachain and decentralized network, Moonbeam features native on-chain governance that enables stakeholders to participate in the direction of the network. With the introduction of OpenGov, also referred to as Governance v2, the Referenda Pallet allows token holders to get information on existing referenda, submit a proposal to be put forth for referenda, and manage actions related to the Decision Deposit, which is required for a referenda to be decided on. To learn more about Moonbeam's governance system, such as an overview of related terminology, principles, mechanics, and more, please refer to the Governance on Moonbeam page.
The Referenda Precompile interacts directly with Substrate's Referenda Pallet. This pallet is coded in Rust and is normally not accessible from the Ethereum side of Moonbeam. However, the Referenda Precompile allows you to access functions needed to view referenda, submit referenda, and manage the required Decision Deposit, all of which are part of the Substrate Referenda Pallet, directly from a Solidity interface.
The Referenda Precompile is located at the following address:
0x0000000000000000000000000000000000000811
0x0000000000000000000000000000000000000811
0x0000000000000000000000000000000000000811
Note
There can be some unintended consequences when using the precompiled contracts on Moonbeam. Please refer to the Security Considerations page for more information.
The Referenda Solidity Interface¶
Referenda.sol
is a Solidity interface that allows developers to interact with the precompile's methods. The methods are as follows:
The methods are as follows:
referendumCount() - a read-only function that returns the total referendum count
None.
uint256
total count of referenda
submissionDeposit() - a read-only function that returns the Submission Deposit required for each referendum
None.
uint256
amount of the required Submission Deposit
decidingCount(uint16 trackId) - a read-only function that returns the total count of deciding referenda for a given Track
trackId
- uint16 Track ID to query the deciding count for
uint256
count of deciding referenda for the specified Track
trackIds() - a read-only function that returns a list of the Track IDs for all Tracks (and Origins)
None.
uint16[]
array of Track IDs
trackInfo(uint16 trackId) - a read-only function that returns the following governance parameters configured for a given Track ID
trackId
- uint16 Track ID to query the parameters for
string
name - the name of the Trackuint256
maxDeciding - the maximum number of referenda that can be decided on at onceuint256
decisionDeposit - the amount of the Decision Deposituint256
preparePeriod - the duration of the Prepare Perioduint256
decisionPeriod - the duration of the Decide Perioduint256
confirmPeriod - the duration of the Confirm Perioduint256
minEnactmentPeriod - the minimum amount of time the Enactment Period must bebytes
minApproval - the minimum "Aye" votes as a percentage of overall Conviction-weighted votes needed for an approvalbytes
minSupport - minimum number of "Aye" votes, not taking into consideration Conviction-weighted votes, needed as a percent of the total supply needed for an approval
referendumStatus(uint32 referendumIndex) - a read-only function that returns the status for a given referendum
referendumIndex
- uint32 index of the referendum to query the status for
ReferendumStatus enum:
enum ReferendumStatus {
Ongoing,
Approved,
Rejected,
Cancelled,
TimedOut,
Killed
}
ongoingReferendumInfo(uint32 referendumIndex) - a read-only function that returns information pertaining to an ongoing referendum
referendumIndex
- uint32 index of the ongoing referendum to query
uint16
trackId - the Track of this referendumbytes
origin - the Origin for this referendumbytes
proposal - the hash of the proposal up for referendumbool
enactmentType -true
if the proposal is scheduled to be dispatched at enactment time andfalse
if after enactment timeuint256
enactmentTime - the time the proposal should be scheduled for enactmentuint256
submissionTime - the time of submissionaddress
submissionDepositor - the address of the depositor for the Submission Deposituint256
submissionDeposit - the amount of the Submission Depositaddress
decisionDepositor - the address of the depositor for the Decision Deposituint256
decisionDeposit - the amount of the Decision Deposituint256
decidingSince - when this referendum entered the Decide Perioduint256
decidingConfirmingEnd - when this referendum is scheduled to leave the Confirm Perioduint256
ayes - the number of "Aye" votes, expressed in terms of post-conviction lock-voteuint32
support - percent of "Aye" votes, expressed pre-conviction, over total votes in the classuint32
approval - percent of "Aye" votes over "Aye" and "Nay" votesbool
inQueue -true
if this referendum has been placed in the queue for being decideduint256
alarmTime - the next scheduled wake-upbytes
taskAddress - scheduler task address if scheduled
closedReferendumInfo(uint32 referendumIndex) - a read-only function that returns information pertaining to a closed referendum
referendumIndex
- uint32 index of the closed referendum to query
uint256
end - when the referendum endedaddress
submissionDepositor - the address of the depositor for the Submission Deposituint256
submissionDeposit - the amount of the Submission Depositaddress
decisionDepositor - the address of the depositor for the Decision Deposituint256
decisionDeposit - the amount of the Decision Deposit
killedReferendumBlock(uint32 referendumIndex) - a read-only function that returns the block a given referendum was killed
referendumIndex
- uint32 index of the killed referendum to query
uint256
block number at which the referendum was killed
submitAt(uint16 trackId, bytes32 proposalHash, uint32 proposalLen, uint32 block) - submits a referendum given a Track ID corresponding to the origin from which the proposal is to be dispatched. Returns the referendum index of the submitted referendum
trackId
- uint16 Track ID corresponding to the origin from which the proposal is to be dispatchedproposalHash
- bytes32 preimage hash of the proposed runtime callproposalLen
- uint32 length of the proposalblock
- uint32 block number at which this will be executed
uint32
index of the submitted referendum
submitAfter(uint16 trackId, bytes32 proposalHash, uint32 proposalLen, uint32 block) - submits a referendum given a Track ID corresponding to the origin from which the proposal is to be dispatched. Returns the referendum index of the submitted referendum
trackId
- uint16 Track ID corresponding to the origin from which the proposal is to be dispatchedproposalHash
- bytes32 preimage hash of the proposed runtime callproposalLen
- uint32 length of the proposalblock
- uint32 block number after which this will be executed
uint32
index of the submitted referendum
placeDecisionDeposit(uint32 index) - posts the Decision Deposit for a referendum given the index of the going referendum
index
- uint32 index of the ongoing referendum to place the Decision Deposit for
None.
refundDecisionDeposit(uint32 index) - refunds the Decision Deposit for a closed referendum back to the depositor
index
- uint32 index of the closed referendum in which the Decision Deposit is still locked
None.
refundSubmissionDeposit(uint32 index) - refunds the Submission Deposit for a closed referendum back to the depositor
index
- uint32 index of the closed referendum to refund the Submission Deposit for
None.
The interface also includes the following events:
- SubmittedAt(uint16 indexed trackId, uint32 referendumIndex, bytes32 hash) - emitted when a referenda has been submitted at a given block
- SubmittedAfter(uint16 indexed trackId, uint32 referendumIndex, bytes32 hash) - emitted when a referenda has been submitted after a given block
- DecisionDepositPlaced(uint32 index, address caller, uint256 depositedAmount) - emitted when a Decision Deposit for a referendum has been placed
- DecisionDepositRefunded(uint32 index, address caller, uint256 refundedAmount) - emitted when a Decision Deposit for a closed referendum has been refunded
- SubmissionDepositRefunded(uint32 index, address caller, uint256 refundedAmount) - emitted when a Submission Deposit for a valid referendum has been refunded
Interact with the Solidity Interface¶
Checking Prerequisites¶
The below example is demonstrated on Moonbase Alpha, however, similar steps can be taken for Moonriver. To follow the steps in this guide, you'll need to have the following:
- MetaMask installed and connected to Moonbase Alpha
- An account with some DEV tokens. You can get DEV tokens for testing on Moonbase Alpha once every 24 hours from the Moonbase Alpha Faucet
Remix Set Up¶
- Click on the File explorer tab
- Paste a copy of
Referenda.sol
into a Remix file namedReferenda.sol
Compile the Contract¶
- Click on the Compile tab, second from top
- Then to compile the interface, click on Compile Referenda.sol
Access the Contract¶
- Click on the Deploy and Run tab, directly below the Compile tab in Remix. Note: you are not deploying a contract here, instead you are accessing a precompiled contract that is already deployed
- Make sure Injected Provider - Metamask is selected in the ENVIRONMENT drop down
- Ensure Referenda.sol is selected in the CONTRACT dropdown. Since this is a precompiled contract there is no need to deploy, instead you are going to provide the address of the precompile in the At Address field
- Provide the address of the Referenda Precompile for Moonbase Alpha:
0x0000000000000000000000000000000000000811
and click At Address - The Referenda Precompile will appear in the list of Deployed Contracts
Submit a Proposal¶
In order to submit a proposal, you should have already submitted the preimage hash for the proposal. If you have not done so, please follow the steps outlined in the Preimage Precompile documentation. There are two methods that can be used to submit a proposal: submitAt
and submitAfter
. The submitAt
function submits a proposal to be executed at a given block and the submitAfter
function submits a proposal to be executed after a specific block. For this example, submitAt
will be used, but the same steps can be applied if you want to use submitAfter
instead.
To submit the proposal, you'll need to determine which Track your proposal belongs to and the Track ID of that Track. For help with these requirements, you can refer to the OpenGov section of the governance overview page.
You'll also need to make sure you have the preimage hash and the length of the preimage handy, both of which you should have received from following the steps in the Preimage Precompile documentation. If you're unsure, you can find your preimage from the Preimage page of Polkadot.js Apps and copy the preimage hash. To get the length of the preimage, you can then query the preimage
pallet using the preimageFor
method from the Polkadot.js Apps Chain State page.
Once you have the Track ID, preimage hash, and preimage length, you can go ahead and submit the proposal using the Referenda Precompile. From Remix, you can take the following steps:
- Expand the Referenda Precompile contract to see the available functions
- Find the submitAt function and press the button to expand the section
- Enter the track ID that your proposal will be processed through
- Enter the preimage hash. You should have received this from following the steps in the Preimage Precompile documentation
- Enter the length of the preimage
- Enter the block you want the proposal to be executed at
- Press transact and confirm the transaction in MetaMask
After your transaction has been confirmed you'll be able to see the proposal listed on the Referenda page of Polkadot.js Apps. You can also check out your proposal on Polkassembly, which sorts proposals by the Track they belong to.
Submit Decision Deposit¶
Now that you've submitted your proposal, the next step is to submit the Decision Deposit. The Decision Deposit is the minimum deposit amount required for a referendum to progress to the decision phase at the end of the Lead-in Period. For more information on the Decision Deposit, please refer to the OpenGov section of the governance overview page.
You can submit the Decision Deposit using the placeDecisionDeposit
function of the Referenda Precompile. You'll just need to have the index of the referendum and enough funds to do so. The Decision Deposit varies by Track, to find the minimum amount required you can take a look at the General Parameters by Track table on the governance overview page.
To submit the deposit, you can take the following steps:
- Find the placeDecisionDeposit function and press the button to expand the section
- Enter the index of the referendum
- Press transact and confirm the transaction in MetaMask
Now that the Decision Deposit has been placed, the referendum is one step closer to moving to the Decide Period. There will also need to be enough Capacity in the designated Track and the duration of the Prepare Period must pass for it to move to the Decide Period.
To vote on referenda, you can follow the steps outlined in the Conviction Voting Precompile documentation.
Refund Decision Deposit¶
Once a referendum has either been approved or rejected, the Decision Deposit can be refunded. This holds true as long as the referendum wasn't cancelled due to the proposal being malicious. If the proposal is deemed malicious and killed via the Root Track or the Emergency Killer Track, the Decision Deposit will be slashed.
To refund the Decision Deposit, you can use the refundDecisionDeposit
function of the Referenda Precompile. To do so, you can take the following steps:
- Find the placeDecisionDeposit function and press the button to expand the section
- Enter the index of the referendum
- Press transact and confirm the transaction in MetaMask
And that's it! You've completed your introduction to the Referenda Precompile. There are a few more functions that are documented in Referenda.sol
— feel free to reach out on Discord if you have any questions about those functions or any other aspect of the Referenda Precompile.
| Created: February 15, 2023