How to change a pre-propose module
The proposal module system enables DAOs to support any type of execution flow that can be coded in a smart contract, and the pre-propose module is the first step in the proposal process. It determines the process for submitting a proposal: who can propose something, whether or not there's a deposit, etc.
Proposal modules can either set a pre-propose module, or not use one, which effectively removes all restrictions (meaning anyone can submit a proposal).
There are two pre-propose module types:
base: manages deposits and submission policy
approval: same as base, plus the ability to set an approver that must approve pending proposals before they open for voting on the proposal module
Each proposal module has corresponding pre-propose modules (e.g. the dao-proposal-single
module has dao-pre-propose-single
and dao-pre-propose-approval-single
, the dao-proposal-multiple
module has dao-pre-propose-multiple
and dao-pre-propose-approval-multiple
, etc.).
You can always add, remove, or replace the pre-propose module for a proposal module.
Adding and disabling proposal modules is not common, and can be dangerous, and thus is not easily supported by the UI, but it is not hard to do with a little bit of time.
Be careful, the DAO will be bricked and all assets will be locked if the pre-propose module is misconfigured. It is best practice to ensure at least one is functioning before disabling another. Ideally, add an entirely new proposal module with the pre-propose module configured how you want it, test that it works by passing and executing a proposal, and then disable the old one (instead of trying to change the pre-propose module on your only working proposal module).
Guide
First you need the address of the proposal module and the code ID of the pre-propose module. You can find all uploaded DAO DAO code IDs here: https://github.com/DA0-DA0/dao-dao-ui/blob/development/packages/utils/constants/codeIds.json
The following guide will update a v2.6.0
single choice proposal module to use the v2.6.0
base pre-propose module on Osmosis mainnet (osmosis-1
), so we need the dao-proposal-single
address and dao-pre-propose-single
code ID.
You can use the DAO DAO indexer to query for the active proposal modules in a DAO and find the dao-proposal-single
address:
DAO_ADDRESS=INSERT_DAO_ADDRESS curl -s https://indexer.daodao.zone/osmosis-1/contract/$DAO_ADDRESS/daoCore/activeProposalModules | jq
Or query the chain directly:
osmosisd query contract-state smart $DAO_ADDRESS '{"active_proposal_modules":{}}' --output json | jq
The output will look like this:
[
{
"prefix": "A",
"status": "enabled",
"address": "DAO_PROPOSAL_SINGLE_ADDRESS",
"info": { "version": "2.6.0", "contract": "crates.io:dao-proposal-single" }
},
{
"prefix": "B",
"status": "enabled",
"address": "DAO_PROPOSAL_MULTIPLE_ADDRESS",
"info": {
"version": "2.6.0",
"contract": "crates.io:dao-proposal-multiple"
}
}
]
You can see the dao-proposal-single
address is DAO_PROPOSAL_SINGLE_ADDRESS
.
You can also find the dao-pre-propose-single
code ID in the codeIds.json
file linked above: 1250
.
A few steps involve base64 encoding, which base64encode.org can help with.
Here is the configuration we will use:
submission policy: DAO members only
no proposal deposit
You can look at the JSON schemas' instantiate
definitions for the pre-propose module to see the configuration fields you need to set, as well as the dao-proposal-single
contract's JSON schema to see the update_pre_propose_info
execution message that performs the actual update:
You can also look at the Rust source code, which contains the corresponding InstantiateMsg
and ExecuteMsg
structs, though you will need to know how serde serializes various types and enums which can be tricky. The JSON schemas above contain all the information you need, but here is the source code for reference:
dao-pre-propose-single
InstantiateMsg
struct which just extends the dao-pre-propose-baseInstantiateMsg
struct
Putting that all together, the full message we need to execute on the proposal module via a proposal, with everything decoded for readability, follows:
{
"update_pre_propose_info": {
"info": {
"module_may_propose": {
"info": {
"code_id": 1250,
"msg": {
"submission_policy": {
"specific": {
"dao_members": true,
"allowlist": [],
"denylist": []
}
},
"extension": {}
},
"admin": { "core_module": {} },
"funds": [],
"label": "dao-pre-propose-single-v2.6.0"
}
}
}
}
}
Now we have to encode the msg
field with base64:
{
"submission_policy": {
"specific": {
"dao_members": true,
"allowlist": [],
"denylist": []
}
},
"extension": {}
}
This gets base64-encoded to:
ewogICJzdWJtaXNzaW9uX3BvbGljeSI6IHsKICAgICJzcGVjaWZpYyI6IHsKICAgICAgImRhb19tZW1iZXJzIjogdHJ1ZSwKICAgICAgImFsbG93bGlzdCI6IFtdLAogICAgICAiZGVueWxpc3QiOiBbXQogICAgfQogIH0sCiAgImV4dGVuc2lvbiI6IHt9Cn0=
Now replace the pre-propose module instantiation msg
field with the base64-encoded instantiation message:
{
"update_pre_propose_info": {
"info": {
"module_may_propose": {
"info": {
"code_id": 1250,
"msg": "ewogICJzdWJtaXNzaW9uX3BvbGljeSI6IHsKICAgICJzcGVjaWZpYyI6IHsKICAgICAgImRhb19tZW1iZXJzIjogdHJ1ZSwKICAgICAgImFsbG93bGlzdCI6IFtdLAogICAgICAiZGVueWxpc3QiOiBbXQogICAgfQogIH0sCiAgImV4dGVuc2lvbiI6IHt9Cn0=",
"admin": { "core_module": {} },
"funds": [],
"label": "dao-pre-propose-single-v2.6.0"
}
}
}
}
}
And you have the full message to execute on the proposal module via a proposal. You can now use the DAO DAO UI to create a proposal with the Execute Smart Contract
action, with the contract address being the dao-proposal-single
module's address, and the message field containing the above text. Once you pass and execute the proposal, the pre-propose module will be updated.
Last updated