-
Notifications
You must be signed in to change notification settings - Fork 142
Verifying DAO DAO v1 contracts
If you're planning on doing anything mission critical with a DAO DAO DAO you should consider verifying it is configured correctly. This wiki page covers:
- Verifying that a DAO's instantiate message is valid.
- Verifying that an already instantiated DAO is using the correct smart contracts.
If you intend to follow this guide to verify a DAO DAO DAO, it may be helpful to familiarize yourself with the DAO DAO v1 contracts design here.
There are two steps to verifying that a DAO DAO DAO is being instantiated correctly. First, the instantiate message should be verified to ensure that the DAO's voting configuration is correct. Then, the code IDs in the instantiate message should be verified to ensure that they point to the correct code.
A typical DAO DAO DAO instantiate message looks something like this:
{
"admin": "juno1jv65s3grqf6v6jl3dp4t6c9t9rk99cd83d88wr",
"automatically_add_cw20s": true,
"automatically_add_cw721s": true,
"description": "A Juno SubDAO in charge of organizing and running Hack Juno.",
"image_url": "https://cloudflare-ipfs.com/ipfs/bafybeibnuzc52kmcu4c5pxxwkr3vyp34gsrdomlvw3e66w4ltidr2v4oxi",
"name": "Hack Juno",
"proposal_modules_instantiate_info": [
{
"admin": {
"core_contract": {}
},
"code_id": 427,
"label": "DAO_Hack Juno_cw-proposal-single",
"msg": "eyJhbGxvd19yZXZvdGluZyI6ZmFsc2UsIm1heF92b3RpbmdfcGVyaW9kIjp7InRpbWUiOjI1OTIwMH0sIm9ubHlfbWVtYmVyc19leGVjdXRlIjp0cnVlLCJ0aHJlc2hvbGQiOnsiYWJzb2x1dGVfY291bnQiOnsidGhyZXNob2xkIjoiNSJ9fX0="
}
],
"voting_module_instantiate_info": {
"admin": {
"core_contract": {}
},
"code_id": 429,
"label": "DAO_Hack Juno_cw4-voting",
"msg": "eyJjdzRfZ3JvdXBfY29kZV9pZCI6NzgsImluaXRpYWxfbWVtYmVycyI6W3siYWRkciI6Imp1bm8xand4anpwd2R0Z2xmN2EzNXNhY2t2MGRuMGhyOW5rNmg2Y3RzaDQiLCJ3ZWlnaHQiOjF9LHsiYWRkciI6Imp1bm8xYXowZWh6MmU1ZW11ZHloOGh4MnF3dGZlbHkwbHN0emowOGdnOWoiLCJ3ZWlnaHQiOjF9LHsiYWRkciI6Imp1bm8xd2QwMmt0Y3ZwYW5hbmx2ZDl1NmptM3gzYXAzdm13NTlkM25yem0iLCJ3ZWlnaHQiOjF9LHsiYWRkciI6Imp1bm8xdXNzOGx1ZmE5N3BtZDdlemxzdnRucWNtbXdxd3hrZDB3dGhnZmciLCJ3ZWlnaHQiOjF9LHsiYWRkciI6Imp1bm8xNWV0ZDR0OTM4MG12ZnBzbHJmdHk0djIyNTdmNGxmcnI1MmpndmUiLCJ3ZWlnaHQiOjF9LHsiYWRkciI6Imp1bm8xZ3NqZGgzd3I1ODdrZ3pjYXg1dmF4OXZweWp3a3RtbHpoY244dnQiLCJ3ZWlnaHQiOjF9LHsiYWRkciI6Imp1bm8xOHF3OXlkcGV3aDQwNXc0bHZtdWhsZzlndGFlcDc5dnkyZ210cjIiLCJ3ZWlnaHQiOjF9XX0="
}
}
Here is a description of each field above and their values in the above message:
-
admin
- This is an optional field which sets an admin for the DAO. The admin has the ability to execute messages on the instantiated DAO. For example, the admin could withdraw the DAO's treasury or upgrade its modules in the event of a security vulnerability. In this case the admin is set to the address of the Juno community pool. -
automatically_add_cw20s
- If this field is set to true cw20 tokens sent to the DAO will be automatically added to the DAOs treasury and displayed in the DAO DAO UI when they are sent to the DAO via a cw20 send message. If you are concerned about potential spam this should be toggled off. -
automatically_add_cw721s
- If this field is set to true cw721 tokens (NFTs) sent to the DAO will be automatically added to the treasury. This should be disabled if you are concerned about potential spam. -
name
/description
/image_url
- These fields set the name, description, and image URL that will be displayed in the DAO DAO UI to identify it. -
proposal_modules_instantiate_info
- This field contains the list of proposal modules that will be added to the DAO when it is created. Proposal modules handle the creation of, voting on, and execution of DAO proposals. At this time the only proposal module supported by DAO DAO is cw-proposal-single.-
admin
- This sets the contract admin. The admin of a contract can perform a migration of that contract. This particular message sets the admin of the proposal module to the core contract meaning that the DAO will able to migrate this module via governance. Most DAOs should set the admin like this. -
code_id
- This is an identifier which points towards some code on the Juno blockchain. That code will be the proposal module. -
label
- This is a human readable label for the proposal module. -
msg
- This is a base64 encoded string which contains the instantiate message that will be used to create this module.
-
-
voting_module_instantiate_info
- This object and its fields are identical toproposal_module_instantiate_info
except that it deals with instantiating the DAO's voting power module. Voting power modules are responsible for determining the voting power of members when they vote on proposals.
To verify that proposal configuration of this DAO we can decode the
msg
field of the proposal module instantiate info as follows:
echo eyJhbGxvd19yZXZvdGluZyI6ZmFsc2UsIm1heF92b3RpbmdfcGVyaW9kIjp7InRpbWUiOjI1OTIwMH0sIm9ubHlfbWVtYmVyc19leGVjdXRlIjp0cnVlLCJ0aHJlc2hvbGQiOnsiYWJzb2x1dGVfY291bnQiOnsidGhyZXNob2xkIjoiNSJ9fX0= | base64 --decode
This yields the JSON message that will be used to instantiate that module. In this case it looks like this:
{
"allow_revoting": true,
"max_voting_period": {
"time": 259200
},
"only_members_execute": true,
"threshold": {
"absolute_count": {
"threshold": "5"
}
}
}
Here is a description of the fields:
-
allow_revoting
- If this is set to true members will be allowed to change their votes on proposals. If revoting is enabled it will slow down DAO governance as proposals will not be allowed to close early. -
max_voting_period
- This is the amount of time (in seconds) that proposals in the DAO will be open for voting. -
only_members_execute
- If set to true only DAO members will be able to execute passed proposals. Otherwise, any address can execute a passed proposal. -
threshold
- This is the passing threshold for proposals in the DAO. In this case it is set to an absolute count of 5. This means that if five votes are cast in favor of a proposal it will pass. Other types of voting thresholds includeabsolute_threshold
andthreshold_quorum
. By default DAO DAO DAOs usethreshold_quorum
though for some DAOs with non token based votingabsolute_count
may simplify the voting process.
Giving the same treatment as earlier to the voting module msg
field:
echo eyJjdzRfZ3JvdXBfY29kZV9pZCI6NzgsImluaXRpYWxfbWVtYmVycyI6W3siYWRkciI6Imp1bm8xand4anpwd2R0Z2xmN2EzNXNhY2t2MGRuMGhyOW5rNmg2Y3RzaDQiLCJ3ZWlnaHQiOjF9LHsiYWRkciI6Imp1bm8xYXowZWh6MmU1ZW11ZHloOGh4MnF3dGZlbHkwbHN0emowOGdnOWoiLCJ3ZWlnaHQiOjF9LHsiYWRkciI6Imp1bm8xd2QwMmt0Y3ZwYW5hbmx2ZDl1NmptM3gzYXAzdm13NTlkM25yem0iLCJ3ZWlnaHQiOjF9LHsiYWRkciI6Imp1bm8xdXNzOGx1ZmE5N3BtZDdlemxzdnRucWNtbXdxd3hrZDB3dGhnZmciLCJ3ZWlnaHQiOjF9LHsiYWRkciI6Imp1bm8xNWV0ZDR0OTM4MG12ZnBzbHJmdHk0djIyNTdmNGxmcnI1MmpndmUiLCJ3ZWlnaHQiOjF9LHsiYWRkciI6Imp1bm8xZ3NqZGgzd3I1ODdrZ3pjYXg1dmF4OXZweWp3a3RtbHpoY244dnQiLCJ3ZWlnaHQiOjF9LHsiYWRkciI6Imp1bm8xOHF3OXlkcGV3aDQwNXc0bHZtdWhsZzlndGFlcDc5dnkyZ210cjIiLCJ3ZWlnaHQiOjF9XX0= | base64 --decode
We see that it contains the following JSON data:
{
"cw4_group_code_id": 78,
"initial_members": [
{
"addr": "juno1jwxjzpwdtglf7a35sackv0dn0hr9nk6h6ctsh4",
"weight": 1
},
{
"addr": "juno1az0ehz2e5emudyh8hx2qwtfely0lstzj08gg9j",
"weight": 1
},
{
"addr": "juno1wd02ktcvpananlvd9u6jm3x3ap3vmw59d3nrzm",
"weight": 1
},
{
"addr": "juno1uss8lufa97pmd7ezlsvtnqcmmwqwxkd0wthgfg",
"weight": 1
},
{
"addr": "juno15etd4t9380mvfpslrfty4v2257f4lfrr52jgve",
"weight": 1
},
{
"addr": "juno1gsjdh3wr587kgzcax5vax9vpyjwktmlzhcn8vt",
"weight": 1
},
{
"addr": "juno18qw9ydpewh405w4lvmuhlg9gtaep79vy2gmtr2",
"weight": 1
}
]
}
This is reasonably straightforward. This DAO will have a fixed set of members (there will be no governance token), there will be seven members, and each member will have the same voting power. This message instantiates a cw4_voting voting power module.
Next, we'll want to verify that the code ID fields in the instantiate
message are what we expect. In all cases (at the time of writing)
the code ID field of the proposal module instantiate message should
correspond to cw_proposal_single
. For the voting module, if a
non-token based DAO is being used the voting module should correspond
to cw4_voting
. If a token is being used it should correspond to
cw20_staked_balance_voting
. These contracts can all be found in the
dao-contracts repo.
To verify that a code ID matches:
-
Clone and build the dao-contracts git repository.
To clone, install git, and then run:
git clone https://github.com/DA0-DA0/dao-contracts.git
To build, install docker, and then run:
docker run --rm -v "$(pwd)":/code \ --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ cosmwasm/workspace-optimizer:0.12.6
from inside the dao-contracts directory. Your built wasm contracts will be placed in the
artifacts
subdirectory. -
Download the code for the code ID by running:
junod query wasm code <CODE_ID> check.wasm
This downloads the code being used and places it in a file called check.wasm.
-
Verify that the shasum of the downloaded code matches the shasum of the program you have built locally. To compute the shasum:
shasum <WASM_FILE>
For example, to check if a code ID matches the
cw-proposal-single
contract, run:shasum artifacts/cw_proposal_single.wasm
and then run:
shasum check.wasm
If the output from the commands matches then you have the contract that you want!
Follow these steps for every code ID in the instantiate message to verify that it is creating the DAO that you expect.
-
Write down the address of the DAO.
This can be found either in the "Addresses" section of the DAO page or in the URL.
-
Configure
junod
.This will allow you to query the Juno blockchain directly. You can find instructions for getting
junod
installed here. -
Clone and build the dao-contracts git repository.
To clone, install git, and then run:
git clone https://github.com/DA0-DA0/dao-contracts.git
To build, install docker, and then run:
docker run --rm -v "$(pwd)":/code \ --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ cosmwasm/workspace-optimizer:0.12.6
from inside the dao-contracts directory.
Having finished those first three steps, there should be a number of
.wasm
files in the artifacts/
directory inside dao-contracts. DAO
DAO DAOs are constructed out of three contracts:
- A core contract:
cw_core.wasm
. - A proposal contract:
cw_proposal_single.wasm
. - A voting power contract. If token based voting is being used,
this contract will be
cw20_staked_balance_voting.wasm
. If non-token based voting is being used this contract will becw4_voting.wasm
.
To verify that our DAO is using the contracts from the dao-contracts git repo, for each contract, we will:
- Download the contract's code from the Juno blockchain.
- Compare the contract's code to the code that we built locally.
If the two programs have the same code it means that the contract is what we expect. If they are different, something may be amis.
To start, we'll collect the addresses of all our contracts. The
address from earlier corresponds to the cw-core
contract. We can ask
that contract for the other addresses running:
junod query wasm contract-state smart <DAO_ADDRESS> '{"dump_state":{}}'
Make sure to replace <DAO_ADDRESS>
above with the address you wrote
down earlier. Once you run this you ought to get a response that looks
something like this:
data:
admin: juno1jv65s3grqf6v6jl3dp4t6c9t9rk99cd83d88wr
config:
automatically_add_cw20s: true
automatically_add_cw721s: true
description: A Juno SubDAO in charge of organizing and running Hack Juno.
image_url: https://cloudflare-ipfs.com/ipfs/bafybeibnuzc52kmcu4c5pxxwkr3vyp34gsrdomlvw3e66w4ltidr2v4oxi
name: Hack Juno
pause_info:
Unpaused: {}
proposal_modules:
- juno139tumjnzrsu4td5pk8jz5d4sllcnj7f5ut2ld3ja8q3cd4hudyjsd8uuh4
version:
contract: crates.io:cw-core
version: 0.1.0
voting_module: juno145cessdvpguc8alqwgf27qhny9ysz44n34h7d54z3uc9w6vcr8qsex42tm
Here, the voting_module
field corresponds to the voting power
contract we will verify against and the address under
proposal_modules
corresponds to the cw_proposal_single
contract.
To download the code associated with a contract, first determine the
code ID that contract was instantiated with by checking the value of
the code_id
field after running:
junod query wasm contract <CONTRACT_ADDRESS>
Then, download that contract's code by running:
junod query wasm code <CODE_ID> check.wasm
Finally, compare the shasum of the two wasm files. If the shasums are the same, the contract is the one you expect. To compute a shasum, run:
shasum <WASM_FILE>
To crystallize things, lets walk through an example and verify a test
Hack Juno DAO with the address
juno1g325q3ddaxk943s52z9ejac4envfje98y0pnfw7lpnmxxdwdyw2sum2jgg
. This
example assumes you have already followed steps 1 through 3 above.
First, we'll query the DAO to get its module's addresses:
$ junod query wasm contract-state smart juno1g325q3ddaxk943s52z9ejac4envfje98y0pnfw7lpnmxxdwdyw2sum2jgg '{"dump_state":{}}'
data:
admin: juno1jv65s3grqf6v6jl3dp4t6c9t9rk99cd83d88wr
config:
automatically_add_cw20s: true
automatically_add_cw721s: true
description: A Juno SubDAO in charge of organizing and running Hack Juno.
image_url: https://cloudflare-ipfs.com/ipfs/bafybeibnuzc52kmcu4c5pxxwkr3vyp34gsrdomlvw3e66w4ltidr2v4oxi
name: Hack Juno
pause_info:
Unpaused: {}
proposal_modules:
- juno139tumjnzrsu4td5pk8jz5d4sllcnj7f5ut2ld3ja8q3cd4hudyjsd8uuh4
version:
contract: crates.io:cw-core
version: 0.1.0
voting_module: juno145cessdvpguc8alqwgf27qhny9ysz44n34h7d54z3uc9w6vcr8qsex42tm
From this we determine that our voting module contract address is
juno145cessdvpguc8alqwgf27qhny9ysz44n34h7d54z3uc9w6vcr8qsex42tm
and
our proposal module contract address is
juno139tumjnzrsu4td5pk8jz5d4sllcnj7f5ut2ld3ja8q3cd4hudyjsd8uuh4
.
Next, we'll verify the voting module contract against the
cw4_voting
contract:
$ junod query wasm contract juno145cessdvpguc8alqwgf27qhny9ysz44n34h7d54z3uc9w6vcr8qsex42tm
address: juno145cessdvpguc8alqwgf27qhny9ysz44n34h7d54z3uc9w6vcr8qsex42tm
contract_info:
admin: juno1g325q3ddaxk943s52z9ejac4envfje98y0pnfw7lpnmxxdwdyw2sum2jgg
code_id: "429"
created: null
creator: juno1g325q3ddaxk943s52z9ejac4envfje98y0pnfw7lpnmxxdwdyw2sum2jgg
extension: null
ibc_port_id: ""
label: DAO_Hack Juno_cw4-voting
$ junod query wasm code 429 check.wasm
Downloading wasm code to check.wasm
$ shasum check.wasm
9a33c618ebd172fcb063a55b9c1e2e2afbde36c1 check.wasm
$ shasum dao-contracts/artifacts/cw4_voting.wasm
9a33c618ebd172fcb063a55b9c1e2e2afbde36c1 dao-contracts/artifacts/cw4_voting.wasm
From the above sequence of commands we can see that the two shasums are the same. That means this DAO is using the correct voting module! To finish this verification you should check the proposal module and the DAO contract itself using the same steps as above.
Before we can be sure that our contracts are valid we also need to verify that our voting module is using the correct underlying contract. For token based DAOs, staking is used to determine voting power, this corresponds to the cw20_stake contract. For non-token based DAOs, the cw4_group contract is used.
To verify a cw4_group contract, first find it's address by running:
junod query wasm contract-state smart <VOTING_ADDRESS> '{"group_contract":{}}'
Then, follow the same docker build steps from above but using the
cw-plus git repo. Compare the
contract's code to the cw4_group.wasm
file.
To verify a cw20_stake contract, first find it's address by running:
junod query wasm contract-state smart <VOTING_ADDRESS> '{"staking_contract": {}}'
Then, verify the contract against the cw20_stake.wasm
file in
dao-contracts/artifacts
.