|
| 1 | +--- |
| 2 | +sidebar_position: 5 |
| 3 | +--- |
| 4 | + |
| 5 | +# How to add a proposal module |
| 6 | + |
| 7 | +The proposal module system enables DAOs to support any type of execution flow |
| 8 | +that can be coded in a smart contract. |
| 9 | + |
| 10 | +DAO DAO provides two modules: |
| 11 | + |
| 12 | +- single choice (e.g. Yes/No/Abstain) |
| 13 | +- multiple choice (e.g. A/B/C/None of the Above) |
| 14 | + |
| 15 | +You can always add new modules or disable existing ones, though there must |
| 16 | +always be at least one module enabled (or else the DAO will not function). The |
| 17 | +most likely reason one would want to add a new module is to use a new version |
| 18 | +with new features. Because the proposal module interface is standardized and |
| 19 | +unchanging, newer versions of proposal modules should be backwards compatible. |
| 20 | + |
| 21 | +Adding and disabling proposal modules is not common, and can be dangerous, and |
| 22 | +thus is not easily supported by the UI, but it is not hard to do with a little |
| 23 | +bit of time. |
| 24 | + |
| 25 | +> ⚠️ Be careful to verify all proposal modules function correctly before |
| 26 | +> disabling one you no longer need, because if the only enabled proposal modules |
| 27 | +> are broken or the addresses are incorrect, the DAO will be bricked and all |
| 28 | +> assets will be locked. |
| 29 | +
|
| 30 | +## Guide |
| 31 | + |
| 32 | +First you need the code IDs of the proposal module and its corresponding |
| 33 | +pre-propose module. You can find all uploaded DAO DAO code IDs here: |
| 34 | +https://github.com/DA0-DA0/dao-dao-ui/blob/development/packages/utils/constants/codeIds.json |
| 35 | + |
| 36 | +The following guide will add the `v2.6.0` single choice proposal module to a DAO |
| 37 | +on Osmosis mainnet (`osmosis-1`), so we need the `dao-proposal-single` and |
| 38 | +`dao-pre-propose-single` code IDs. |
| 39 | + |
| 40 | +Here is the configuration we will use: |
| 41 | + |
| 42 | +- threshold: majority |
| 43 | +- quorum: 20% |
| 44 | +- max voting period: 1 week |
| 45 | +- only members can execute: true |
| 46 | +- close proposal on execution failure: true |
| 47 | +- allow revoting: false |
| 48 | +- submission policy: DAO members only |
| 49 | +- no proposal deposit |
| 50 | + |
| 51 | +You can look at the JSON schemas' `instantiate` definitions for the proposal and |
| 52 | +pre-propose modules to see the configuration fields you need to set, as well as |
| 53 | +the `dao-dao-core` contract's JSON schema to see the `update_proposal_modules` |
| 54 | +execution message that performs the actual update: |
| 55 | + |
| 56 | +- [dao-proposal-single JSON schema](https://github.com/DA0-DA0/dao-contracts/blob/development/contracts/proposal/dao-proposal-single/schema/dao-proposal-single.json) |
| 57 | +- [dao-pre-propose-single JSON |
| 58 | + schema](https://github.com/DA0-DA0/dao-contracts/blob/development/contracts/pre-propose/dao-pre-propose-single/schema/dao-pre-propose-single.json) |
| 59 | +- [dao-dao-core JSON schema](https://github.com/DA0-DA0/dao-contracts/blob/development/contracts/dao-dao-core/schema/dao-dao-core.json) |
| 60 | + |
| 61 | +You can also look at the Rust source code, which contains the corresponding |
| 62 | +`InstantiateMsg` and `ExecuteMsg` structs, though you will need to know how |
| 63 | +serde serializes various types and enums which can be tricky. The JSON schemas |
| 64 | +above contain all the information you need, but here is the source code for |
| 65 | +reference: |
| 66 | + |
| 67 | +- [dao-proposal-single `InstantiateMsg` |
| 68 | + struct](https://github.com/DA0-DA0/dao-contracts/blob/development/contracts/proposal/dao-proposal-single/src/msg.rs) |
| 69 | +- [dao-pre-propose-single `InstantiateMsg` |
| 70 | + struct](https://github.com/DA0-DA0/dao-contracts/blob/development/contracts/pre-propose/dao-pre-propose-single/src/contract.rs) |
| 71 | + which just extends the [dao-pre-propose-base `InstantiateMsg` |
| 72 | + struct](https://github.com/DA0-DA0/dao-contracts/blob/development/packages/dao-pre-propose-base/src/msg.rs) |
| 73 | +- [dao-dao-core `ExecuteMsg` |
| 74 | + struct](https://github.com/DA0-DA0/dao-contracts/blob/development/packages/dao-interface/src/msg.rs) |
| 75 | + defined in the `dao-interface` crate. |
| 76 | + |
| 77 | +Putting that all together, the full message we need to execute on the DAO via a |
| 78 | +proposal, with everything decoded for readability, follows: |
| 79 | + |
| 80 | +``` |
| 81 | +{ |
| 82 | + "update_proposal_modules": { |
| 83 | + "to_add": [{ |
| 84 | + "admin": { "core_module": {} }, |
| 85 | + "code_id": 1252, |
| 86 | + "label": "dao-proposal-single-v2.6.0", |
| 87 | + "msg": { |
| 88 | + "threshold": { |
| 89 | + "threshold_quorum": { |
| 90 | + "threshold": { "majority": {} }, |
| 91 | + "quorum": { "percent": "0.2" } |
| 92 | + } |
| 93 | + }, |
| 94 | + "max_voting_period": { "time": 604800 }, |
| 95 | + "only_members_execute": true, |
| 96 | + "close_proposal_on_execution_failure": true, |
| 97 | + "allow_revoting": false, |
| 98 | + "pre_propose_info": { |
| 99 | + "module_may_propose": { |
| 100 | + "info": { |
| 101 | + "code_id": 1250, |
| 102 | + "msg": { |
| 103 | + "submission_policy": { |
| 104 | + "specific": { |
| 105 | + "dao_members": true, |
| 106 | + "allowlist": [], |
| 107 | + "denylist": [] |
| 108 | + } |
| 109 | + }, |
| 110 | + "extension": {} |
| 111 | + }, |
| 112 | + "admin": { "core_module": {} }, |
| 113 | + "funds": [], |
| 114 | + "label": "dao-pre-propose-single-v2.6.0" |
| 115 | + } |
| 116 | + } |
| 117 | + } |
| 118 | + }, |
| 119 | + "funds": [] |
| 120 | + }], |
| 121 | + "to_disable": [] |
| 122 | + } |
| 123 | +} |
| 124 | +``` |
| 125 | + |
| 126 | +Now we have to recursively encode the `msg` fields with base64, so starting from |
| 127 | +the innermost `msg` field for the pre-propose module instantiation message: |
| 128 | + |
| 129 | +``` |
| 130 | +{ |
| 131 | + "submission_policy": { |
| 132 | + "specific": { |
| 133 | + "dao_members": true, |
| 134 | + "allowlist": [], |
| 135 | + "denylist": [] |
| 136 | + } |
| 137 | + }, |
| 138 | + "extension": {} |
| 139 | +} |
| 140 | +``` |
| 141 | + |
| 142 | +This gets base64-encoded to: |
| 143 | + |
| 144 | +``` |
| 145 | +ewogICJzdWJtaXNzaW9uX3BvbGljeSI6IHsKICAgICJzcGVjaWZpYyI6IHsKICAgICAgImRhb19tZW1iZXJzIjogdHJ1ZSwKICAgICAgImFsbG93bGlzdCI6IFtdLAogICAgICAiZGVueWxpc3QiOiBbXQogICAgfQogIH0sCiAgImV4dGVuc2lvbiI6IHt9Cn0= |
| 146 | +``` |
| 147 | + |
| 148 | +Now isolate the proposal module instantiation `msg`, and replace the pre-propose |
| 149 | +module instantiation `msg` field with the base64-encoded pre-propose module |
| 150 | +instantiation message from above: |
| 151 | + |
| 152 | +``` |
| 153 | +{ |
| 154 | + "threshold": { |
| 155 | + "threshold_quorum": { |
| 156 | + "threshold": { "majority": {} }, |
| 157 | + "quorum": { "percent": "0.2" } |
| 158 | + } |
| 159 | + }, |
| 160 | + "max_voting_period": { "time": 604800 }, |
| 161 | + "only_members_execute": true, |
| 162 | + "close_proposal_on_execution_failure": true, |
| 163 | + "allow_revoting": false, |
| 164 | + "pre_propose_info": { |
| 165 | + "module_may_propose": { |
| 166 | + "info": { |
| 167 | + "code_id": 1250, |
| 168 | + "msg": "ewogICJzdWJtaXNzaW9uX3BvbGljeSI6IHsKICAgICJzcGVjaWZpYyI6IHsKICAgICAgImRhb19tZW1iZXJzIjogdHJ1ZSwKICAgICAgImFsbG93bGlzdCI6IFtdLAogICAgICAiZGVueWxpc3QiOiBbXQogICAgfQogIH0sCiAgImV4dGVuc2lvbiI6IHt9Cn0=", |
| 169 | + "admin": { "core_module": {} }, |
| 170 | + "funds": [], |
| 171 | + "label": "dao-pre-propose-single-v2.6.0" |
| 172 | + } |
| 173 | + } |
| 174 | + } |
| 175 | +} |
| 176 | +``` |
| 177 | + |
| 178 | +This gets base64-encoded to: |
| 179 | + |
| 180 | +``` |
| 181 | +ewogICJ0aHJlc2hvbGQiOiB7CiAgICAidGhyZXNob2xkX3F1b3J1bSI6IHsKICAgICAgInRocmVzaG9sZCI6IHsgIm1ham9yaXR5Ijoge30gfSwKICAgICAgInF1b3J1bSI6IHsgInBlcmNlbnQiOiAiMC4yIiB9CiAgICB9CiAgfSwKICAibWF4X3ZvdGluZ19wZXJpb2QiOiB7ICJ0aW1lIjogNjA0ODAwIH0sCiAgIm9ubHlfbWVtYmVyc19leGVjdXRlIjogdHJ1ZSwKICAiY2xvc2VfcHJvcG9zYWxfb25fZXhlY3V0aW9uX2ZhaWx1cmUiOiB0cnVlLAogICJhbGxvd19yZXZvdGluZyI6IGZhbHNlLAogICJwcmVfcHJvcG9zZV9pbmZvIjogewogICAgIm1vZHVsZV9tYXlfcHJvcG9zZSI6IHsKICAgICAgImluZm8iOiB7CiAgICAgICAgImNvZGVfaWQiOiAxMjUwLAogICAgICAgICJtc2ciOiAiZXdvZ0lDSnpkV0p0YVhOemFXOXVYM0J2YkdsamVTSTZJSHNLSUNBZ0lDSnpjR1ZqYVdacFl5STZJSHNLSUNBZ0lDQWdJbVJoYjE5dFpXMWlaWEp6SWpvZ2RISjFaU3dLSUNBZ0lDQWdJbUZzYkc5M2JHbHpkQ0k2SUZ0ZExBb2dJQ0FnSUNBaVpHVnVlV3hwYzNRaU9pQmJYUW9nSUNBZ2ZRb2dJSDBzQ2lBZ0ltVjRkR1Z1YzJsdmJpSTZJSHQ5Q24wPSIsCiAgICAgICAgImFkbWluIjogeyAiY29yZV9tb2R1bGUiOiB7fSB9LAogICAgICAgICJmdW5kcyI6IFtdLAogICAgICAgICJsYWJlbCI6ICJkYW8tcHJlLXByb3Bvc2Utc2luZ2xlLXYyLjYuMCIKICAgICAgfQogICAgfQogIH0KfQ== |
| 182 | +``` |
| 183 | + |
| 184 | +Now replace the proposal module instantiation `msg` field in the |
| 185 | +`update_proposal_modules` execution message with the base64-encoded proposal |
| 186 | +module instantiation message from above: |
| 187 | + |
| 188 | +``` |
| 189 | +{ |
| 190 | + "update_proposal_modules": { |
| 191 | + "to_add": [{ |
| 192 | + "admin": { "core_module": {} }, |
| 193 | + "code_id": 1252, |
| 194 | + "label": "dao-proposal-single-v2.6.0", |
| 195 | + "msg": "ewogICJ0aHJlc2hvbGQiOiB7CiAgICAidGhyZXNob2xkX3F1b3J1bSI6IHsKICAgICAgInRocmVzaG9sZCI6IHsgIm1ham9yaXR5Ijoge30gfSwKICAgICAgInF1b3J1bSI6IHsgInBlcmNlbnQiOiAiMC4yIiB9CiAgICB9CiAgfSwKICAibWF4X3ZvdGluZ19wZXJpb2QiOiB7ICJ0aW1lIjogNjA0ODAwIH0sCiAgIm9ubHlfbWVtYmVyc19leGVjdXRlIjogdHJ1ZSwKICAiY2xvc2VfcHJvcG9zYWxfb25fZXhlY3V0aW9uX2ZhaWx1cmUiOiB0cnVlLAogICJhbGxvd19yZXZvdGluZyI6IGZhbHNlLAogICJwcmVfcHJvcG9zZV9pbmZvIjogewogICAgIm1vZHVsZV9tYXlfcHJvcG9zZSI6IHsKICAgICAgImluZm8iOiB7CiAgICAgICAgImNvZGVfaWQiOiAxMjUwLAogICAgICAgICJtc2ciOiAiZXdvZ0lDSnpkV0p0YVhOemFXOXVYM0J2YkdsamVTSTZJSHNLSUNBZ0lDSnpjR1ZqYVdacFl5STZJSHNLSUNBZ0lDQWdJbVJoYjE5dFpXMWlaWEp6SWpvZ2RISjFaU3dLSUNBZ0lDQWdJbUZzYkc5M2JHbHpkQ0k2SUZ0ZExBb2dJQ0FnSUNBaVpHVnVlV3hwYzNRaU9pQmJYUW9nSUNBZ2ZRb2dJSDBzQ2lBZ0ltVjRkR1Z1YzJsdmJpSTZJSHQ5Q24wPSIsCiAgICAgICAgImFkbWluIjogeyAiY29yZV9tb2R1bGUiOiB7fSB9LAogICAgICAgICJmdW5kcyI6IFtdLAogICAgICAgICJsYWJlbCI6ICJkYW8tcHJlLXByb3Bvc2Utc2luZ2xlLXYyLjYuMCIKICAgICAgfQogICAgfQogIH0KfQ==", |
| 196 | + "funds": [] |
| 197 | + }], |
| 198 | + "to_disable": [] |
| 199 | + } |
| 200 | +} |
| 201 | +``` |
| 202 | + |
| 203 | +And you have the full message to execute on the DAO via a proposal. You can now |
| 204 | +use the DAO DAO UI to create a proposal with the `Execute Smart Contract` |
| 205 | +action, with the contract address being the DAO's `dao-dao-core` contract, and |
| 206 | +the message field containing the above text. |
| 207 | + |
| 208 | +Once you execute this, test the new proposal module by creating a proposal, |
| 209 | +voting on it, and executing it. If everything seems to work, you can now disable |
| 210 | +the old single choice proposal module by executing the following message in |
| 211 | +another proposal: |
| 212 | + |
| 213 | +``` |
| 214 | +{ |
| 215 | + "update_proposal_modules": { |
| 216 | + "to_add": [], |
| 217 | + "to_disable": ["EXISTING PROPOSAL MODULE ADDRESS"] |
| 218 | + } |
| 219 | +} |
| 220 | +``` |
| 221 | + |
| 222 | +Ideally, wait a while to see the new proposal module in action before disabling |
| 223 | +the old one. |
0 commit comments