Repository: DA0-DA0/dao-contracts
Pull request #410 (2022-07-20 04:00:59)

Initial integration tests
Adds some DAO DAO integration tests that are run with `cargo test` using [cosm-orc](https://github.com/de-husk/cosm-orc).

First step in https://github.com/DA0-DA0/dao-contracts/issues/403. Ill do the gas diff in another PR, but this is uploading the gas report file as a github artifact.

How to add new integration tests:

```rust
#[test_context(Chain)]
#[test]
fn new_dao_has_no_items(chain: &mut Chain) {
    let res = create_dao(
        chain, 
        Some(chain.user.addr.clone()), 
        "ex_create_dao", 
       chain.user.addr.clone()
    );
    let dao = res.unwrap();

    // use the native rust types to interact with the contract
     let res = chain
        .orc
        .query(
            "cw_core",
            "exc_items_get",
            &cw_core::msg::QueryMsg::GetItem {
                key: "meme".to_string(),
            },
        )
        .unwrap();
    let res: GetItemResponse = res.data().unwrap();

    assert_eq!(res.item, None);
}
```

codecov-commenter: # [Codecov](https://codecov.io/gh/DA0-DA0/dao-contracts/pull/410?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=DA0-DA0) Report
> Merging [#410](https://codecov.io/gh/DA0-DA0/dao-contracts/pull/410?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=DA0-DA0) (02934ef) into [main](https://codecov.io/gh/DA0-DA0/dao-contracts/commit/6dc92d9cba7f00d570f97fbceadd334fb61d6ccf?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=DA0-DA0) (6dc92d9) will **not change** coverage.
> The diff coverage is `n/a`.

```diff
@@           Coverage Diff           @@
##             main     #410   +/-   ##
=======================================
  Coverage   95.39%   95.39%           
=======================================
  Files          33       33           
  Lines        4954     4954           
=======================================
  Hits         4726     4726           
  Misses        228      228           
```



------

[Continue to review full report at Codecov](https://codecov.io/gh/DA0-DA0/dao-contracts/pull/410?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=DA0-DA0).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=DA0-DA0)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/DA0-DA0/dao-contracts/pull/410?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=DA0-DA0). Last update [6dc92d9...02934ef](https://codecov.io/gh/DA0-DA0/dao-contracts/pull/410?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=DA0-DA0). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=DA0-DA0).


Patch:
diff --git a/.cargo/config b/.cargo/config
index 71deaf22..e857f471 100644
--- a/.cargo/config
+++ b/.cargo/config
@@ -1,4 +1,5 @@
 [alias]
 wasm = "build --release --target wasm32-unknown-unknown"
 wasm-debug = "build --target wasm32-unknown-unknown"
-unit-test = "test --lib"
+t = "test --workspace --exclude integration_tests"
+unit-test = "test --workspace --exclude integration_tests"
\ No newline at end of file
diff --git a/.github/workflows/basic.yml b/.github/workflows/basic.yml
index 4fe3b6a1..ee347565 100644
--- a/.github/workflows/basic.yml
+++ b/.github/workflows/basic.yml
@@ -24,7 +24,7 @@ jobs:
         uses: actions-rs/cargo@v1
         with:
           toolchain: nightly
-          command: test
+          command: unit-test
           args: --locked
         env:
           RUST_BACKTRACE: 1
diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml
index 9b3d3a12..b2547b17 100644
--- a/.github/workflows/codecov.yml
+++ b/.github/workflows/codecov.yml
@@ -13,7 +13,7 @@ jobs:
         uses: actions/checkout@v2
       - name: Generate code coverage
         run: >
-          cargo +nightly tarpaulin --verbose --workspace --out Xml --exclude-files debug/* *test*.rs packages/testing/* packages/cw-core-macros/* legacy/*
+          cargo +nightly tarpaulin --verbose --workspace --out Xml --exclude integration_tests --exclude-files debug/* *test*.rs packages/testing/* packages/cw-core-macros/* legacy/* ci/*
       - name: Upload to codecov.io
         uses: codecov/codecov-action@v3
         with:
diff --git a/.gitignore b/.gitignore
index ca8ada31..6cf19d83 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,3 +24,6 @@ artifacts/
 
 # code coverage
 tarpaulin-report.*
+
+# integration tests
+gas_reports/
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
index 5276e3ce..9da9951a 100644
--- a/CODE_OF_CONDUCT.md
+++ b/CODE_OF_CONDUCT.md
@@ -1,4 +1,4 @@
-# Code of conduct
+# Code of conduct 
 
 Be kind to one another.
 
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index f170b65c..a398f707 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,58 +1,121 @@
-# Welcome to the DAO DAO contributing guide <!-- omit in toc -->
+# Contributing to DAO DAO
 
-Thank you for investing your time in contributing to our project!
+Thanks for your interest in contributing. We're excited to welcome you
+into our community. :)
 
-Read our [Code of Conduct](./CODE_OF_CONDUCT.md) to keep our community approachable and respectable.
+We value kindness, humility, and low-ego communication. You can read
+our [Code of Conduct](./CODE_OF_CONDUCT.md) for a poetic version of
+this.
 
-All contributions will fall under our [our open-source license agreement](./LICENSE).
+There are many ways you can contribute.
 
-In this guide you will get an overview of the contribution workflow from opening an issue, creating a PR, reviewing, and merging the PR.
+- If you're interested in doing smart contract work, you're in the
+  right place.
+- If you want to work on our UI, check out the [dao-dao-ui
+  repo](https://github.com/DA0-DA0/dao-dao-ui).
+- If you want to contribute documentation, check out our [docs
+  repo](https://github.com/DA0-DA0/docs).
 
-## New contributor guide
+Getting started contributing to an open source project can be
+daunting. A great place to ask questions is the [DAO DAO
+Discord](https://discord.gg/sAaGuyW3D2).
 
-To get an overview of the project, read the [README](README.md). Here are some resources to help you get started with open source contributions:
+## Getting started
 
-- [Finding ways to contribute to open source on GitHub](https://docs.github.com/en/get-started/exploring-projects-on-github/finding-ways-to-contribute-to-open-source-on-github)
-- [Set up Git](https://docs.github.com/en/get-started/quickstart/set-up-git)
-- [GitHub flow](https://docs.github.com/en/get-started/quickstart/github-flow)
-- [Collaborating with pull requests](https://docs.github.com/en/github/collaborating-with-pull-requests)
+To work on the DAO DAO smart contracts you'll need a reasonable
+understanding of the Rust programming language. The [Rust
+book](https://doc.rust-lang.org/book/) is a really good place to pick
+this up.
 
-### Issues
+Before picking up any issues, you may also want to read our [design
+wiki
+page](https://github.com/DA0-DA0/dao-contracts/wiki/DAO-DAO-Contracts-Design)
+which gives an overview of how all of the DAO DAO smart contracts fit
+together.
 
-#### Create a new issue
+Our development workflow is just like a regular Rust project:
 
-If you spot a problem, [search if an issue already exists](https://docs.github.com/en/github/searching-for-information-on-github/searching-on-github/searching-issues-and-pull-requests#search-by-the-title-body-or-comments). If a related issue doesn't exist, you can open a new issue using a relevant [issue form](https://github.com/DA0-DA0/dao-contracts/issues/new/choose).
+- `cargo build` from the repository root to build the contracts.
+- `cargo test` from the repository root to test the contracts.
 
-#### Solve an issue
+To build WASM files for deploying on a blockchain, run:
 
-Scan through our [existing issues](https://github.com/DA0-DA0/dao-contracts/issues) to find one that interests you. You can narrow down the search using `labels` as filters. See [Labels](/contributing/how-to-use-labels.md) for more information. As a general rule, we don’t assign issues to anyone. If you find an issue to work on, you are welcome to open a PR with a fix.
+```
+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 \
+  --platform linux/amd64 \
+  cosmwasm/workspace-optimizer:0.12.6
+```
 
-### Make Changes
+From the repository root.
 
-#### Make changes in the UI
+## Getting ready to make a PR
 
-1. [Install Git LFS](https://docs.github.com/en/github/managing-large-files/versioning-large-files/installing-git-large-file-storage).
+Before making a PR, you'll need to do two things to get CI passing:
 
-2. Fork the repository.
+1. Generate schema files for the contracts.
+2. Generate typescript interfaces from those schemas.
 
-3. Create a working branch and start with your changes!
+To generate schema files, run:
 
-### Commit your update
+```
+./scripts/schema.sh
+```
 
-Commit the changes once you are happy with them. See [Atom's contributing guide](https://github.com/atom/atom/blob/master/CONTRIBUTING.md#git-commit-messages) to know how to use emoji for commit messages.
+To generate the typescript interface, after generating the schema
+files, run:
 
-### Pull Request
+```
+yarn --cwd ./types install --frozen-lockfile
+yarn --cwd ./types build
+yarn --cwd ./types codegen
+```
 
-When you're finished with the changes, create a pull request, also known as a PR.
+To do this you'll need [yarn](https://yarnpkg.com/) installed.
 
-- Don't forget to [link the PR to an issue](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue) if you are solving one.
-  Once you submit your PR, a team member will review your proposal. We may ask questions or request for additional information.
-- We may ask for changes to be made before a PR can be merged, either using [suggested changes](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/incorporating-feedback-in-your-pull-request) or pull request comments. You can apply suggested changes directly through the UI. You can make any other changes in your fork, then commit them to your branch.
-- As you update your PR and apply changes, mark each conversation as [resolved](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/commenting-on-a-pull-request#resolving-conversations).
-- If you run into any merge issues, checkout this [git tutorial](https://lab.github.com/githubtraining/managing-merge-conflicts) to help you resolve merge conflicts and other issues.
+## Deploying in a development environment
 
-### Your PR is merged!
+Build and deploy the contracts to a local chain running in Docker with:
 
-Congratulations :tada::tada: DAO DAO thanks you :sparkles:.
+```sh
+bash scripts/deploy_local.sh juno10j9gpw9t4jsz47qgnkvl5n3zlm2fz72k67rxsg
+```
 
-Once your PR is merged, your contributions will be publicly visible.
+> Note: This Wasm account is from the [default
+> account](default-account.txt), which you can use for testing (DO NOT
+> store any real funds with this account). You can pass in any wasm
+> account address you want to use.
+
+This will run a chain locally in a docker container, then build and
+deploy the contracts to that chain.
+
+The script will output something like:
+
+```sh
+NEXT_PUBLIC_CW20_CODE_ID=1
+NEXT_PUBLIC_CW4GROUP_CODE_ID=2
+NEXT_PUBLIC_CWCORE_CODE_ID=7
+NEXT_PUBLIC_CWPROPOSALSINGLE_CODE_ID=11
+NEXT_PUBLIC_CW4VOTING_CODE_ID=5
+NEXT_PUBLIC_CW20STAKEDBALANCEVOTING_CODE_ID=4
+NEXT_PUBLIC_STAKECW20_CODE_ID=13
+NEXT_PUBLIC_DAO_CONTRACT_ADDRESS=juno1zlmaky7753d2fneyhduwz0rn3u9ns8rse3tudhze8rc2g54w9ysqgjt23l
+NEXT_PUBLIC_V1_FACTORY_CONTRACT_ADDRESS=juno1pvrwmjuusn9wh34j7y520g8gumuy9xtl3gvprlljfdpwju3x7ucssml9ug
+```
+
+This output can be directly copy and pased into a `.env` file in the
+[DAO DAO UI](https://github.com/DA0-DA0/dao-dao-ui) for local
+development.
+
+Note, to send commands to the docker container:
+
+```sh
+docker exec -i cosmwasm junod status
+```
+
+Some commands require a password which defaults to `xxxxxxxxx`. You can use them like so:
+
+```sh
+echo xxxxxxxxx | docker exec -i cosmwasm  junod keys show validator -a
+```
diff --git a/Cargo.lock b/Cargo.lock
index 866aeae5..8b6ebfdf 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2,11 +2,95 @@
 # It is not intended for manual editing.
 version = 3
 
+[[package]]
+name = "addr2line"
+version = "0.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b"
+dependencies = [
+ "gimli",
+]
+
+[[package]]
+name = "adler"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+
+[[package]]
+name = "ahash"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
+dependencies = [
+ "getrandom 0.2.7",
+ "once_cell",
+ "version_check",
+]
+
+[[package]]
+name = "aho-corasick"
+version = "0.7.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
+dependencies = [
+ "memchr",
+]
+
 [[package]]
 name = "anyhow"
-version = "1.0.58"
+version = "1.0.59"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c91f1f46651137be86f3a2b9a8359f9ab421d04d941c62b5982e1ca21113adf9"
+
+[[package]]
+name = "assert_matches"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9"
+
+[[package]]
+name = "async-stream"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dad5c83079eae9969be7fadefe640a1c566901f05ff91ab221de4b6f68d9507e"
+dependencies = [
+ "async-stream-impl",
+ "futures-core",
+]
+
+[[package]]
+name = "async-stream-impl"
+version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bb07d2053ccdbe10e2af2995a2f116c1330396493dc1269f6a91d0ae82e19704"
+checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "async-trait"
+version = "0.1.57"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76464446b8bc32758d7e88ee1a804d9914cd9b1cb264c029899680b0be29826f"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "atty"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
+dependencies = [
+ "hermit-abi",
+ "libc",
+ "winapi",
+]
 
 [[package]]
 name = "autocfg"
@@ -14,6 +98,64 @@ version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
 
+[[package]]
+name = "axum"
+version = "0.5.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6b9496f0c1d1afb7a2af4338bbe1d969cddfead41d87a9fb3aaa6d0bbc7af648"
+dependencies = [
+ "async-trait",
+ "axum-core",
+ "bitflags",
+ "bytes",
+ "futures-util",
+ "http",
+ "http-body",
+ "hyper",
+ "itoa",
+ "matchit",
+ "memchr",
+ "mime",
+ "percent-encoding",
+ "pin-project-lite",
+ "serde",
+ "sync_wrapper",
+ "tokio",
+ "tower",
+ "tower-http",
+ "tower-layer",
+ "tower-service",
+]
+
+[[package]]
+name = "axum-core"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e4f44a0e6200e9d11a1cdc989e4b358f6e3d354fbf48478f345a17f4e43f8635"
+dependencies = [
+ "async-trait",
+ "bytes",
+ "futures-util",
+ "http",
+ "http-body",
+ "mime",
+]
+
+[[package]]
+name = "backtrace"
+version = "0.3.66"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7"
+dependencies = [
+ "addr2line",
+ "cc",
+ "cfg-if",
+ "libc",
+ "miniz_oxide",
+ "object",
+ "rustc-demangle",
+]
+
 [[package]]
 name = "base16ct"
 version = "0.1.1"
@@ -32,15 +174,70 @@ version = "1.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3bdca834647821e0b13d9539a8634eb62d3501b6b6c2cec1722786ee6671b851"
 
+[[package]]
+name = "bip32"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "873faa4363bfc54c36a48321da034c92a0645a363eed34d948683ffc1706e37f"
+dependencies = [
+ "bs58",
+ "hmac",
+ "k256",
+ "once_cell",
+ "pbkdf2",
+ "rand_core 0.6.3",
+ "ripemd160",
+ "sha2",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
 [[package]]
 name = "block-buffer"
 version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
 dependencies = [
+ "block-padding",
  "generic-array",
 ]
 
+[[package]]
+name = "block-buffer"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "block-padding"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae"
+
+[[package]]
+name = "bs58"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3"
+dependencies = [
+ "sha2",
+]
+
+[[package]]
+name = "bumpalo"
+version = "3.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3"
+
 [[package]]
 name = "byteorder"
 version = "1.4.3"
@@ -49,9 +246,15 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
 
 [[package]]
 name = "bytes"
-version = "1.2.0"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db"
+
+[[package]]
+name = "cc"
+version = "1.0.73"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f0b3de4a0c5e67e16066a0715723abd91edc2f9001d09c46e1dca929351e130e"
+checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
 
 [[package]]
 name = "cfg-if"
@@ -59,19 +262,108 @@ version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
+[[package]]
+name = "config"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "11f1667b8320afa80d69d8bbe40830df2c8a06003d86f73d8e003b2c48df416d"
+dependencies = [
+ "async-trait",
+ "json5",
+ "lazy_static",
+ "nom",
+ "pathdiff",
+ "ron",
+ "rust-ini",
+ "serde",
+ "serde_json",
+ "toml",
+ "yaml-rust",
+]
+
 [[package]]
 name = "const-oid"
 version = "0.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3"
 
+[[package]]
+name = "core-foundation"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
+
+[[package]]
+name = "cosm-orc"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "718327a8f492823d82433323f1f62290c041e98faf3cf670ae8bc2e658faf1e7"
+dependencies = [
+ "backtrace",
+ "config",
+ "cosmos-sdk-proto",
+ "cosmrs",
+ "log",
+ "prost 0.10.4",
+ "serde",
+ "serde_json",
+ "tendermint-rpc",
+ "thiserror",
+ "tokio",
+]
+
+[[package]]
+name = "cosmos-sdk-proto"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24ca04d3795c18023c221a2143b29de9c70668ecb22d17783bc02ee780c6c404"
+dependencies = [
+ "prost 0.10.4",
+ "prost-types",
+ "tendermint-proto",
+ "tonic",
+]
+
+[[package]]
+name = "cosmrs"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6989fdb6267eccb52762530b79ce0b385f4eaeb8b786522a95512e9bebb268c2"
+dependencies = [
+ "bip32",
+ "cosmos-sdk-proto",
+ "ecdsa",
+ "eyre",
+ "getrandom 0.2.7",
+ "k256",
+ "prost 0.10.4",
+ "prost-types",
+ "rand_core 0.6.3",
+ "serde",
+ "serde_json",
+ "subtle-encoding",
+ "tendermint",
+ "tendermint-rpc",
+ "thiserror",
+]
+
 [[package]]
 name = "cosmwasm-crypto"
 version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5eb0afef2325df81aadbf9be1233f522ed8f6e91df870c764bc44cca2b1415bd"
 dependencies = [
- "digest",
+ "digest 0.9.0",
  "ed25519-zebra",
  "k256",
  "rand_core 0.6.3",
@@ -151,6 +443,16 @@ dependencies = [
  "zeroize",
 ]
 
+[[package]]
+name = "crypto-common"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
+dependencies = [
+ "generic-array",
+ "typenum",
+]
+
 [[package]]
 name = "crypto-mac"
 version = "0.11.1"
@@ -161,6 +463,25 @@ dependencies = [
  "subtle",
 ]
 
+[[package]]
+name = "ct-logs"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1a816186fa68d9e426e3cb4ae4dff1fcd8e4a2c34b781bf7a822574a0d0aac8"
+dependencies = [
+ "sct",
+]
+
+[[package]]
+name = "ctor"
+version = "0.1.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cdffe87e1d521a10f9696f833fe502293ea446d7f256c06128293a4119bdf4cb"
+dependencies = [
+ "quote",
+ "syn",
+]
+
 [[package]]
 name = "curve25519-dalek"
 version = "3.2.1"
@@ -168,7 +489,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "90f9d052967f590a76e62eb387bd0bbb1b000182c3cefe5364db6b7211651bc0"
 dependencies = [
  "byteorder",
- "digest",
+ "digest 0.9.0",
  "rand_core 0.5.1",
  "subtle",
  "zeroize",
@@ -268,7 +589,7 @@ dependencies = [
  "cw-utils 0.13.4",
  "derivative",
  "itertools",
- "prost",
+ "prost 0.9.0",
  "schemars",
  "serde",
  "thiserror",
@@ -277,7 +598,7 @@ dependencies = [
 [[package]]
 name = "cw-multi-test"
 version = "0.14.0"
-source = "git+https://github.com/CosmWasm/cw-plus?branch=main#d67face967d2a7f0f59ed4c29b5bbf8f00ce1523"
+source = "git+https://github.com/CosmWasm/cw-plus?branch=main#0a3f52dc2c8b5bdc79be7077a8a877a6c495604e"
 dependencies = [
  "anyhow",
  "cosmwasm-std",
@@ -286,7 +607,7 @@ dependencies = [
  "cw-utils 0.14.0",
  "derivative",
  "itertools",
- "prost",
+ "prost 0.9.0",
  "schemars",
  "serde",
  "thiserror",
@@ -466,7 +787,7 @@ dependencies = [
 [[package]]
 name = "cw-storage-plus"
 version = "0.14.0"
-source = "git+https://github.com/CosmWasm/cw-plus?branch=main#d67face967d2a7f0f59ed4c29b5bbf8f00ce1523"
+source = "git+https://github.com/CosmWasm/cw-plus?branch=main#0a3f52dc2c8b5bdc79be7077a8a877a6c495604e"
 dependencies = [
  "cosmwasm-std",
  "schemars",
@@ -518,7 +839,7 @@ dependencies = [
 [[package]]
 name = "cw-utils"
 version = "0.14.0"
-source = "git+https://github.com/CosmWasm/cw-plus?branch=main#d67face967d2a7f0f59ed4c29b5bbf8f00ce1523"
+source = "git+https://github.com/CosmWasm/cw-plus?branch=main#0a3f52dc2c8b5bdc79be7077a8a877a6c495604e"
 dependencies = [
  "cosmwasm-std",
  "cw2 0.14.0",
@@ -543,7 +864,7 @@ dependencies = [
 [[package]]
 name = "cw2"
 version = "0.14.0"
-source = "git+https://github.com/CosmWasm/cw-plus?branch=main#d67face967d2a7f0f59ed4c29b5bbf8f00ce1523"
+source = "git+https://github.com/CosmWasm/cw-plus?branch=main#0a3f52dc2c8b5bdc79be7077a8a877a6c495604e"
 dependencies = [
  "cosmwasm-std",
  "cw-storage-plus 0.14.0",
@@ -806,11 +1127,27 @@ dependencies = [
  "generic-array",
 ]
 
+[[package]]
+name = "digest"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506"
+dependencies = [
+ "block-buffer 0.10.2",
+ "crypto-common",
+]
+
+[[package]]
+name = "dlv-list"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257"
+
 [[package]]
 name = "dyn-clone"
-version = "1.0.8"
+version = "1.0.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9d07a982d1fb29db01e5a59b1918e03da4df7297eaeee7686ac45542fd4e59c8"
+checksum = "4f94fa09c2aeea5b8839e414b7b841bf429fd25b9c522116ac97ee87856d88b2"
 
 [[package]]
 name = "ecdsa"
@@ -824,6 +1161,27 @@ dependencies = [
  "signature",
 ]
 
+[[package]]
+name = "ed25519"
+version = "1.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e9c280362032ea4203659fc489832d0204ef09f247a0506f170dafcac08c369"
+dependencies = [
+ "signature",
+]
+
+[[package]]
+name = "ed25519-dalek"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d"
+dependencies = [
+ "curve25519-dalek",
+ "ed25519",
+ "sha2",
+ "zeroize",
+]
+
 [[package]]
 name = "ed25519-zebra"
 version = "3.0.0"
@@ -863,6 +1221,29 @@ dependencies = [
  "zeroize",
 ]
 
+[[package]]
+name = "env_logger"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3"
+dependencies = [
+ "atty",
+ "humantime",
+ "log",
+ "regex",
+ "termcolor",
+]
+
+[[package]]
+name = "eyre"
+version = "0.6.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb"
+dependencies = [
+ "indenter",
+ "once_cell",
+]
+
 [[package]]
 name = "ff"
 version = "0.11.1"
@@ -873,6 +1254,32 @@ dependencies = [
  "subtle",
 ]
 
+[[package]]
+name = "flex-error"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c606d892c9de11507fa0dcffc116434f94e105d0bbdc4e405b61519464c49d7b"
+dependencies = [
+ "eyre",
+ "paste",
+]
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "form_urlencoded"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191"
+dependencies = [
+ "matches",
+ "percent-encoding",
+]
+
 [[package]]
 name = "forward_ref"
 version = "1.0.0"
@@ -880,348 +1287,1673 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c8cbd1169bd7b4a0a20d92b9af7a7e0422888bd38a6f5ec29c1fd8c1558a272e"
 
 [[package]]
-name = "generic-array"
-version = "0.14.5"
+name = "futures"
+version = "0.3.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803"
+checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e"
 dependencies = [
- "typenum",
- "version_check",
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-io",
+ "futures-sink",
+ "futures-task",
+ "futures-util",
 ]
 
 [[package]]
-name = "getrandom"
-version = "0.1.16"
+name = "futures-channel"
+version = "0.3.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
+checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010"
 dependencies = [
- "cfg-if",
- "libc",
- "wasi 0.9.0+wasi-snapshot-preview1",
+ "futures-core",
+ "futures-sink",
 ]
 
 [[package]]
-name = "getrandom"
-version = "0.2.7"
+name = "futures-core"
+version = "0.3.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
-dependencies = [
- "cfg-if",
- "libc",
- "wasi 0.11.0+wasi-snapshot-preview1",
-]
+checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3"
 
 [[package]]
-name = "group"
-version = "0.11.0"
+name = "futures-executor"
+version = "0.3.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bc5ac374b108929de78460075f3dc439fa66df9d8fc77e8f12caa5165fcf0c89"
+checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6"
 dependencies = [
- "ff",
- "rand_core 0.6.3",
- "subtle",
+ "futures-core",
+ "futures-task",
+ "futures-util",
 ]
 
 [[package]]
-name = "hashbrown"
-version = "0.12.3"
+name = "futures-io"
+version = "0.3.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b"
 
 [[package]]
-name = "hex"
-version = "0.4.3"
+name = "futures-macro"
+version = "0.3.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
-
+checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "futures-sink"
+version = "0.3.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868"
+
+[[package]]
+name = "futures-task"
+version = "0.3.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a"
+
+[[package]]
+name = "futures-util"
+version = "0.3.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-macro",
+ "futures-sink",
+ "futures-task",
+ "memchr",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+]
+
+[[package]]
+name = "generic-array"
+version = "0.14.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9"
+dependencies = [
+ "typenum",
+ "version_check",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi 0.9.0+wasi-snapshot-preview1",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "libc",
+ "wasi 0.11.0+wasi-snapshot-preview1",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "gimli"
+version = "0.26.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d"
+
+[[package]]
+name = "group"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc5ac374b108929de78460075f3dc439fa66df9d8fc77e8f12caa5165fcf0c89"
+dependencies = [
+ "ff",
+ "rand_core 0.6.3",
+ "subtle",
+]
+
+[[package]]
+name = "h2"
+version = "0.3.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37a82c6d637fc9515a4694bbf1cb2457b79d81ce52b3108bdeea58b07dd34a57"
+dependencies = [
+ "bytes",
+ "fnv",
+ "futures-core",
+ "futures-sink",
+ "futures-util",
+ "http",
+ "indexmap",
+ "slab",
+ "tokio",
+ "tokio-util",
+ "tracing",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+dependencies = [
+ "ahash",
+]
+
+[[package]]
+name = "headers"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4cff78e5788be1e0ab65b04d306b2ed5092c815ec97ec70f4ebd5aee158aa55d"
+dependencies = [
+ "base64",
+ "bitflags",
+ "bytes",
+ "headers-core",
+ "http",
+ "httpdate",
+ "mime",
+ "sha-1",
+]
+
+[[package]]
+name = "headers-core"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429"
+dependencies = [
+ "http",
+]
+
+[[package]]
+name = "hermit-abi"
+version = "0.1.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "hex"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
+
+[[package]]
+name = "hmac"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b"
+dependencies = [
+ "crypto-mac",
+ "digest 0.9.0",
+]
+
+[[package]]
+name = "http"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399"
+dependencies = [
+ "bytes",
+ "fnv",
+ "itoa",
+]
+
+[[package]]
+name = "http-body"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1"
+dependencies = [
+ "bytes",
+ "http",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "http-range-header"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29"
+
+[[package]]
+name = "httparse"
+version = "1.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c"
+
+[[package]]
+name = "httpdate"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
+
+[[package]]
+name = "humantime"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
+
+[[package]]
+name = "hyper"
+version = "0.14.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac"
+dependencies = [
+ "bytes",
+ "futures-channel",
+ "futures-core",
+ "futures-util",
+ "h2",
+ "http",
+ "http-body",
+ "httparse",
+ "httpdate",
+ "itoa",
+ "pin-project-lite",
+ "socket2",
+ "tokio",
+ "tower-service",
+ "tracing",
+ "want",
+]
+
+[[package]]
+name = "hyper-proxy"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca815a891b24fdfb243fa3239c86154392b0953ee584aa1a2a1f66d20cbe75cc"
+dependencies = [
+ "bytes",
+ "futures",
+ "headers",
+ "http",
+ "hyper",
+ "hyper-rustls",
+ "rustls-native-certs",
+ "tokio",
+ "tokio-rustls",
+ "tower-service",
+ "webpki",
+]
+
+[[package]]
+name = "hyper-rustls"
+version = "0.22.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f9f7a97316d44c0af9b0301e65010573a853a9fc97046d7331d7f6bc0fd5a64"
+dependencies = [
+ "ct-logs",
+ "futures-util",
+ "hyper",
+ "log",
+ "rustls",
+ "rustls-native-certs",
+ "tokio",
+ "tokio-rustls",
+ "webpki",
+ "webpki-roots",
+]
+
+[[package]]
+name = "hyper-timeout"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1"
+dependencies = [
+ "hyper",
+ "pin-project-lite",
+ "tokio",
+ "tokio-io-timeout",
+]
+
+[[package]]
+name = "idna"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8"
+dependencies = [
+ "matches",
+ "unicode-bidi",
+ "unicode-normalization",
+]
+
+[[package]]
+name = "indenter"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
+
+[[package]]
+name = "indexable-hooks"
+version = "0.1.0"
+dependencies = [
+ "cosmwasm-std",
+ "cw-storage-plus 0.13.4",
+ "schemars",
+ "serde",
+ "thiserror",
+]
+
+[[package]]
+name = "indexmap"
+version = "1.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
+dependencies = [
+ "autocfg",
+ "hashbrown",
+ "serde",
+]
+
+[[package]]
+name = "integration_tests"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "assert_matches",
+ "cosm-orc",
+ "cosmwasm-std",
+ "ctor",
+ "cw-core",
+ "cw-core-interface",
+ "cw-proposal-single",
+ "cw-utils 0.13.4",
+ "cw20 0.13.4",
+ "cw20-base",
+ "cw20-stake",
+ "cw20-staked-balance-voting",
+ "env_logger",
+ "once_cell",
+ "rand",
+ "serde",
+ "serde_json",
+ "test-context",
+ "voting",
+]
+
+[[package]]
+name = "itertools"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754"
+
+[[package]]
+name = "js-sys"
+version = "0.3.59"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "258451ab10b34f8af53416d1fdab72c22e805f0c92a1136d59470ec0b11138b2"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "json5"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1"
+dependencies = [
+ "pest",
+ "pest_derive",
+ "serde",
+]
+
+[[package]]
+name = "k256"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19c3a5e0a0b8450278feda242592512e09f61c72e018b8cd5c859482802daf2d"
+dependencies = [
+ "cfg-if",
+ "ecdsa",
+ "elliptic-curve",
+ "sec1",
+ "sha2",
+ "sha3",
+]
+
+[[package]]
+name = "keccak"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f9b7d56ba4a8344d6be9729995e6b06f928af29998cdf79fe390cbf6b1fee838"
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "libc"
+version = "0.2.127"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "505e71a4706fa491e9b1b55f51b95d4037d0821ee40131190475f692b35b009b"
+
+[[package]]
+name = "linked-hash-map"
+version = "0.5.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
+
+[[package]]
+name = "lock_api"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "matches"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
+
+[[package]]
+name = "matchit"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73cbba799671b762df5a175adf59ce145165747bb891505c43d09aefbbf38beb"
+
+[[package]]
+name = "memchr"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+
+[[package]]
+name = "mime"
+version = "0.3.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
+
+[[package]]
+name = "minimal-lexical"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+
+[[package]]
+name = "miniz_oxide"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc"
+dependencies = [
+ "adler",
+]
+
+[[package]]
+name = "mio"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf"
+dependencies = [
+ "libc",
+ "log",
+ "wasi 0.11.0+wasi-snapshot-preview1",
+ "windows-sys",
+]
+
+[[package]]
+name = "nom"
+version = "7.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36"
+dependencies = [
+ "memchr",
+ "minimal-lexical",
+]
+
+[[package]]
+name = "num-derive"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "num_cpus"
+version = "1.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
+dependencies = [
+ "hermit-abi",
+ "libc",
+]
+
+[[package]]
+name = "num_threads"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "object"
+version = "0.29.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1"
+
+[[package]]
+name = "opaque-debug"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
+
+[[package]]
+name = "openssl-probe"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
+
+[[package]]
+name = "ordered-multimap"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ccd746e37177e1711c20dd619a1620f34f5c8b569c53590a72dedd5344d8924a"
+dependencies = [
+ "dlv-list",
+ "hashbrown",
+]
+
+[[package]]
+name = "parking_lot"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
+dependencies = [
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "windows-sys",
+]
+
+[[package]]
+name = "paste"
+version = "1.0.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9423e2b32f7a043629287a536f21951e8c6a82482d0acb1eeebfc90bc2225b22"
+
+[[package]]
+name = "pathdiff"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd"
+
+[[package]]
+name = "pbkdf2"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f05894bce6a1ba4be299d0c5f29563e08af2bc18bb7d48313113bed71e904739"
+dependencies = [
+ "crypto-mac",
+]
+
+[[package]]
+name = "peg"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07c0b841ea54f523f7aa556956fbd293bcbe06f2e67d2eb732b7278aaf1d166a"
+dependencies = [
+ "peg-macros",
+ "peg-runtime",
+]
+
+[[package]]
+name = "peg-macros"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5aa52829b8decbef693af90202711348ab001456803ba2a98eb4ec8fb70844c"
+dependencies = [
+ "peg-runtime",
+ "proc-macro2",
+ "quote",
+]
+
+[[package]]
+name = "peg-runtime"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c719dcf55f09a3a7e764c6649ab594c18a177e3599c467983cdf644bfc0a4088"
+
+[[package]]
+name = "percent-encoding"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
+
+[[package]]
+name = "pest"
+version = "2.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69486e2b8c2d2aeb9762db7b4e00b0331156393555cff467f4163ff06821eef8"
+dependencies = [
+ "thiserror",
+ "ucd-trie",
+]
+
+[[package]]
+name = "pest_derive"
+version = "2.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b13570633aff33c6d22ce47dd566b10a3b9122c2fe9d8e7501895905be532b91"
+dependencies = [
+ "pest",
+ "pest_generator",
+]
+
+[[package]]
+name = "pest_generator"
+version = "2.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b3c567e5702efdc79fb18859ea74c3eb36e14c43da7b8c1f098a4ed6514ec7a0"
+dependencies = [
+ "pest",
+ "pest_meta",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "pest_meta"
+version = "2.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5eb32be5ee3bbdafa8c7a18b0a8a8d962b66cfa2ceee4037f49267a50ee821fe"
+dependencies = [
+ "once_cell",
+ "pest",
+ "sha-1",
+]
+
+[[package]]
+name = "pin-project"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78203e83c48cffbe01e4a2d35d566ca4de445d79a85372fc64e378bfc812a260"
+dependencies = [
+ "pin-project-internal",
+]
+
+[[package]]
+name = "pin-project-internal"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "710faf75e1b33345361201d36d04e98ac1ed8909151a017ed384700836104c74"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "pkcs8"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7cabda3fb821068a9a4fab19a683eac3af12edf0f34b94a8be53c4972b8149d0"
+dependencies = [
+ "der",
+ "spki",
+ "zeroize",
+]
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.43"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "proposal-hooks"
+version = "0.1.0"
+dependencies = [
+ "cosmwasm-std",
+ "indexable-hooks",
+ "schemars",
+ "serde",
+ "voting",
+]
+
+[[package]]
+name = "proposal-hooks-counter"
+version = "0.1.0"
+dependencies = [
+ "cosmwasm-schema",
+ "cosmwasm-std",
+ "cosmwasm-storage",
+ "cw-core",
+ "cw-multi-test 0.13.4",
+ "cw-proposal-single",
+ "cw-storage-plus 0.13.4",
+ "cw-utils 0.13.4",
+ "cw2 0.13.4",
+ "cw20 0.13.4",
+ "cw20-balance-voting",
+ "cw20-base",
+ "indexable-hooks",
+ "proposal-hooks",
+ "schemars",
+ "serde",
+ "thiserror",
+ "vote-hooks",
+ "voting",
+]
+
+[[package]]
+name = "prost"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "444879275cb4fd84958b1a1d5420d15e6fcf7c235fe47f053c9c2a80aceb6001"
+dependencies = [
+ "bytes",
+ "prost-derive 0.9.0",
+]
+
+[[package]]
+name = "prost"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "71adf41db68aa0daaefc69bb30bcd68ded9b9abaad5d1fbb6304c4fb390e083e"
+dependencies = [
+ "bytes",
+ "prost-derive 0.10.1",
+]
+
+[[package]]
+name = "prost-derive"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f9cc1a3263e07e0bf68e96268f37665207b49560d98739662cdfaae215c720fe"
+dependencies = [
+ "anyhow",
+ "itertools",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "prost-derive"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b670f45da57fb8542ebdbb6105a925fe571b67f9e7ed9f47a06a84e72b4e7cc"
+dependencies = [
+ "anyhow",
+ "itertools",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "prost-types"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2d0a014229361011dc8e69c8a1ec6c2e8d0f2af7c91e3ea3f5b2170298461e68"
+dependencies = [
+ "bytes",
+ "prost 0.10.4",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "libc",
+ "rand_chacha",
+ "rand_core 0.6.3",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core 0.6.3",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
+dependencies = [
+ "getrandom 0.1.16",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
+dependencies = [
+ "getrandom 0.2.7",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "regex"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
+
+[[package]]
+name = "rfc6979"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96ef608575f6392792f9ecf7890c00086591d29a83910939d430753f7c050525"
+dependencies = [
+ "crypto-bigint",
+ "hmac",
+ "zeroize",
+]
+
+[[package]]
+name = "ring"
+version = "0.16.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
+dependencies = [
+ "cc",
+ "libc",
+ "once_cell",
+ "spin",
+ "untrusted",
+ "web-sys",
+ "winapi",
+]
+
+[[package]]
+name = "ripemd160"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2eca4ecc81b7f313189bf73ce724400a07da2a6dac19588b03c8bd76a2dcc251"
+dependencies = [
+ "block-buffer 0.9.0",
+ "digest 0.9.0",
+ "opaque-debug",
+]
+
+[[package]]
+name = "ron"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "88073939a61e5b7680558e6be56b419e208420c2adb92be54921fa6b72283f1a"
+dependencies = [
+ "base64",
+ "bitflags",
+ "serde",
+]
+
+[[package]]
+name = "rust-ini"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6d5f2436026b4f6e79dc829837d467cc7e9a55ee40e750d716713540715a2df"
+dependencies = [
+ "cfg-if",
+ "ordered-multimap",
+]
+
+[[package]]
+name = "rustc-demangle"
+version = "0.1.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
+
+[[package]]
+name = "rustls"
+version = "0.19.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7"
+dependencies = [
+ "base64",
+ "log",
+ "ring",
+ "sct",
+ "webpki",
+]
+
+[[package]]
+name = "rustls-native-certs"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a07b7c1885bd8ed3831c289b7870b13ef46fe0e856d288c30d9cc17d75a2092"
+dependencies = [
+ "openssl-probe",
+ "rustls",
+ "schannel",
+ "security-framework",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
+
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "schannel"
+version = "0.1.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2"
+dependencies = [
+ "lazy_static",
+ "windows-sys",
+]
+
+[[package]]
+name = "schemars"
+version = "0.8.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1847b767a3d62d95cbf3d8a9f0e421cf57a0d8aa4f411d4b16525afb0284d4ed"
+dependencies = [
+ "dyn-clone",
+ "schemars_derive",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "schemars_derive"
+version = "0.8.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af4d7e1b012cb3d9129567661a63755ea4b8a7386d339dc945ae187e403c6743"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "serde_derive_internals",
+ "syn",
+]
+
+[[package]]
+name = "scopeguard"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
+
+[[package]]
+name = "sct"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce"
+dependencies = [
+ "ring",
+ "untrusted",
+]
+
+[[package]]
+name = "sec1"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08da66b8b0965a5555b6bd6639e68ccba85e1e2506f5fbb089e93f8a04e1a2d1"
+dependencies = [
+ "der",
+ "generic-array",
+ "pkcs8",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "security-framework"
+version = "2.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dc14f172faf8a0194a3aded622712b0de276821addc574fa54fc0a1167e10dc"
+dependencies = [
+ "bitflags",
+ "core-foundation",
+ "core-foundation-sys",
+ "libc",
+ "security-framework-sys",
+]
+
+[[package]]
+name = "security-framework-sys"
+version = "2.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "semver"
+version = "1.0.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93f6841e709003d68bb2deee8c343572bf446003ec20a583e76f7b15cebf3711"
+
+[[package]]
+name = "serde"
+version = "1.0.142"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e590c437916fb6b221e1d00df6e3294f3fccd70ca7e92541c475d6ed6ef5fee2"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde-json-wasm"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "479b4dbc401ca13ee8ce902851b834893251404c4f3c65370a49e047a6be09a5"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "serde_bytes"
+version = "0.11.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cfc50e8183eeeb6178dcb167ae34a8051d63535023ae38b5d8d12beae193d37b"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.142"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34b5b8d809babe02f538c2cfec6f2c1ed10804c0e5a6a041a049a4f5588ccc2e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_derive_internals"
+version = "0.26.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
 [[package]]
-name = "hmac"
-version = "0.11.0"
+name = "serde_json"
+version = "1.0.83"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b"
+checksum = "38dd04e3c8279e75b31ef29dbdceebfe5ad89f4d0937213c53f7d49d01b3d5a7"
 dependencies = [
- "crypto-mac",
- "digest",
+ "itoa",
+ "ryu",
+ "serde",
 ]
 
 [[package]]
-name = "indexable-hooks"
-version = "0.1.0"
+name = "serde_repr"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fe39d9fbb0ebf5eb2c7cb7e2a47e4f462fad1379f1166b8ae49ad9eae89a7ca"
 dependencies = [
- "cosmwasm-std",
- "cw-storage-plus 0.13.4",
- "schemars",
- "serde",
- "thiserror",
+ "proc-macro2",
+ "quote",
+ "syn",
 ]
 
 [[package]]
-name = "indexmap"
-version = "1.9.1"
+name = "sha-1"
+version = "0.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
+checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f"
 dependencies = [
- "autocfg",
- "hashbrown",
- "serde",
+ "cfg-if",
+ "cpufeatures",
+ "digest 0.10.3",
 ]
 
 [[package]]
-name = "itertools"
-version = "0.10.3"
+name = "sha2"
+version = "0.9.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3"
+checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800"
 dependencies = [
- "either",
+ "block-buffer 0.9.0",
+ "cfg-if",
+ "cpufeatures",
+ "digest 0.9.0",
+ "opaque-debug",
 ]
 
 [[package]]
-name = "itoa"
-version = "1.0.2"
+name = "sha3"
+version = "0.9.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
+checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809"
+dependencies = [
+ "block-buffer 0.9.0",
+ "digest 0.9.0",
+ "keccak",
+ "opaque-debug",
+]
 
 [[package]]
-name = "k256"
-version = "0.10.4"
+name = "signal-hook-registry"
+version = "1.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "19c3a5e0a0b8450278feda242592512e09f61c72e018b8cd5c859482802daf2d"
+checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0"
 dependencies = [
- "cfg-if",
- "ecdsa",
- "elliptic-curve",
- "sec1",
- "sha2",
+ "libc",
 ]
 
 [[package]]
-name = "libc"
-version = "0.2.126"
+name = "signature"
+version = "1.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
+checksum = "02658e48d89f2bec991f9a78e69cfa4c316f8d6a6c4ec12fae1aeb263d486788"
+dependencies = [
+ "digest 0.9.0",
+ "rand_core 0.6.3",
+]
 
 [[package]]
-name = "opaque-debug"
-version = "0.3.0"
+name = "slab"
+version = "0.4.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
+checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef"
+dependencies = [
+ "autocfg",
+]
 
 [[package]]
-name = "pkcs8"
-version = "0.8.0"
+name = "smallvec"
+version = "1.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7cabda3fb821068a9a4fab19a683eac3af12edf0f34b94a8be53c4972b8149d0"
+checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1"
+
+[[package]]
+name = "socket2"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0"
 dependencies = [
- "der",
- "spki",
- "zeroize",
+ "libc",
+ "winapi",
 ]
 
 [[package]]
-name = "ppv-lite86"
-version = "0.2.16"
+name = "spin"
+version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
+checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
 
 [[package]]
-name = "proc-macro2"
-version = "1.0.42"
+name = "spki"
+version = "0.5.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c278e965f1d8cf32d6e0e96de3d3e79712178ae67986d9cf9151f51e95aac89b"
+checksum = "44d01ac02a6ccf3e07db148d2be087da624fea0221a16152ed01f0496a6b0a27"
 dependencies = [
- "unicode-ident",
+ "base64ct",
+ "der",
 ]
 
 [[package]]
-name = "proposal-hooks"
-version = "0.1.0"
+name = "stake-cw20-external-rewards"
+version = "0.2.6"
 dependencies = [
+ "anyhow",
+ "cosmwasm-schema",
  "cosmwasm-std",
- "indexable-hooks",
+ "cosmwasm-storage",
+ "cw-controllers",
+ "cw-multi-test 0.13.4",
+ "cw-storage-plus 0.13.4",
+ "cw-utils 0.13.4",
+ "cw2 0.13.4",
+ "cw20 0.13.4",
+ "cw20-base",
+ "cw20-stake",
  "schemars",
  "serde",
- "voting",
+ "thiserror",
 ]
 
 [[package]]
-name = "proposal-hooks-counter"
+name = "stake-cw20-reward-distributor"
 version = "0.1.0"
 dependencies = [
  "cosmwasm-schema",
  "cosmwasm-std",
  "cosmwasm-storage",
- "cw-core",
  "cw-multi-test 0.13.4",
- "cw-proposal-single",
  "cw-storage-plus 0.13.4",
  "cw-utils 0.13.4",
  "cw2 0.13.4",
  "cw20 0.13.4",
- "cw20-balance-voting",
  "cw20-base",
- "indexable-hooks",
- "proposal-hooks",
+ "cw20-stake",
  "schemars",
  "serde",
  "thiserror",
- "vote-hooks",
- "voting",
 ]
 
 [[package]]
-name = "prost"
-version = "0.9.0"
+name = "static_assertions"
+version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "444879275cb4fd84958b1a1d5420d15e6fcf7c235fe47f053c9c2a80aceb6001"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
+[[package]]
+name = "subtle"
+version = "2.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
+
+[[package]]
+name = "subtle-encoding"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7dcb1ed7b8330c5eed5441052651dd7a12c75e2ed88f2ec024ae1fa3a5e59945"
 dependencies = [
- "bytes",
- "prost-derive",
+ "zeroize",
 ]
 
 [[package]]
-name = "prost-derive"
-version = "0.9.0"
+name = "syn"
+version = "1.0.99"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f9cc1a3263e07e0bf68e96268f37665207b49560d98739662cdfaae215c720fe"
+checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13"
 dependencies = [
- "anyhow",
- "itertools",
  "proc-macro2",
  "quote",
- "syn",
+ "unicode-ident",
 ]
 
 [[package]]
-name = "quote"
-version = "1.0.20"
+name = "sync_wrapper"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "20518fe4a4c9acf048008599e464deb21beeae3d3578418951a189c235a7a9a8"
+
+[[package]]
+name = "synstructure"
+version = "0.12.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804"
+checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f"
 dependencies = [
  "proc-macro2",
+ "quote",
+ "syn",
+ "unicode-xid",
 ]
 
 [[package]]
-name = "rand"
-version = "0.8.5"
+name = "tendermint"
+version = "0.23.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+checksum = "3ca881fa4dedd2b46334f13be7fbc8cc1549ba4be5a833fe4e73d1a1baaf7949"
 dependencies = [
- "libc",
- "rand_chacha",
- "rand_core 0.6.3",
+ "async-trait",
+ "bytes",
+ "ed25519",
+ "ed25519-dalek",
+ "flex-error",
+ "futures",
+ "k256",
+ "num-traits",
+ "once_cell",
+ "prost 0.10.4",
+ "prost-types",
+ "ripemd160",
+ "serde",
+ "serde_bytes",
+ "serde_json",
+ "serde_repr",
+ "sha2",
+ "signature",
+ "subtle",
+ "subtle-encoding",
+ "tendermint-proto",
+ "time",
+ "zeroize",
 ]
 
 [[package]]
-name = "rand_chacha"
-version = "0.3.1"
+name = "tendermint-config"
+version = "0.23.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+checksum = "f6c56ee93f4e9b7e7daba86d171f44572e91b741084384d0ae00df7991873dfd"
 dependencies = [
- "ppv-lite86",
- "rand_core 0.6.3",
+ "flex-error",
+ "serde",
+ "serde_json",
+ "tendermint",
+ "toml",
+ "url",
 ]
 
 [[package]]
-name = "rand_core"
-version = "0.5.1"
+name = "tendermint-proto"
+version = "0.23.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
+checksum = "b71f925d74903f4abbdc4af0110635a307b3cb05b175fdff4a7247c14a4d0874"
 dependencies = [
- "getrandom 0.1.16",
+ "bytes",
+ "flex-error",
+ "num-derive",
+ "num-traits",
+ "prost 0.10.4",
+ "prost-types",
+ "serde",
+ "serde_bytes",
+ "subtle-encoding",
+ "time",
 ]
 
 [[package]]
-name = "rand_core"
-version = "0.6.3"
+name = "tendermint-rpc"
+version = "0.23.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
+checksum = "a13e63f57ee05a1e927887191c76d1b139de9fa40c180b9f8727ee44377242a6"
 dependencies = [
+ "async-trait",
+ "bytes",
+ "flex-error",
+ "futures",
  "getrandom 0.2.7",
+ "http",
+ "hyper",
+ "hyper-proxy",
+ "hyper-rustls",
+ "peg",
+ "pin-project",
+ "serde",
+ "serde_bytes",
+ "serde_json",
+ "subtle-encoding",
+ "tendermint",
+ "tendermint-config",
+ "tendermint-proto",
+ "thiserror",
+ "time",
+ "tokio",
+ "tracing",
+ "url",
+ "uuid",
+ "walkdir",
 ]
 
 [[package]]
-name = "rfc6979"
-version = "0.1.0"
+name = "termcolor"
+version = "1.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "96ef608575f6392792f9ecf7890c00086591d29a83910939d430753f7c050525"
+checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
 dependencies = [
- "crypto-bigint",
- "hmac",
- "zeroize",
+ "winapi-util",
 ]
 
 [[package]]
-name = "ryu"
-version = "1.0.10"
+name = "test-context"
+version = "0.1.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695"
+checksum = "055831a02a4f5aa28fede67f2902014273eb8c21b958ac5ebbd59b71ef30dbc3"
+dependencies = [
+ "async-trait",
+ "futures",
+ "test-context-macros",
+]
 
 [[package]]
-name = "schemars"
-version = "0.8.10"
+name = "test-context-macros"
+version = "0.1.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1847b767a3d62d95cbf3d8a9f0e421cf57a0d8aa4f411d4b16525afb0284d4ed"
+checksum = "8901a55b0a7a06ebc4a674dcca925170da8e613fa3b163a1df804ed10afb154d"
 dependencies = [
- "dyn-clone",
- "schemars_derive",
- "serde",
- "serde_json",
+ "quote",
+ "syn",
 ]
 
 [[package]]
-name = "schemars_derive"
-version = "0.8.10"
+name = "testing"
+version = "0.1.0"
+dependencies = [
+ "cosmwasm-schema",
+ "cosmwasm-std",
+ "cw-core",
+ "cw-core-interface",
+ "cw-multi-test 0.13.4",
+ "cw-utils 0.13.4",
+ "cw2 0.13.4",
+ "cw20 0.13.4",
+ "cw20-balance-voting",
+ "cw20-base",
+ "cw20-stake",
+ "cw20-staked-balance-voting",
+ "cw4",
+ "cw4-group",
+ "cw4-voting",
+ "indexable-hooks",
+ "rand",
+ "voting",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.32"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af4d7e1b012cb3d9129567661a63755ea4b8a7386d339dc945ae187e403c6743"
+checksum = "f5f6586b7f764adc0231f4c79be7b920e766bb2f3e51b3661cdb263828f19994"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "12bafc5b54507e0149cdf1b145a5d80ab80a90bcd9275df43d4fff68460f6c21"
 dependencies = [
  "proc-macro2",
  "quote",
- "serde_derive_internals",
  "syn",
 ]
 
 [[package]]
-name = "sec1"
-version = "0.2.1"
+name = "time"
+version = "0.3.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "08da66b8b0965a5555b6bd6639e68ccba85e1e2506f5fbb089e93f8a04e1a2d1"
+checksum = "74b7cc93fc23ba97fde84f7eea56c55d1ba183f495c6715defdfc7b9cb8c870f"
 dependencies = [
- "der",
- "generic-array",
- "pkcs8",
- "subtle",
- "zeroize",
+ "js-sys",
+ "libc",
+ "num_threads",
+ "time-macros",
 ]
 
 [[package]]
-name = "semver"
-version = "1.0.12"
+name = "time-macros"
+version = "0.2.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a2333e6df6d6598f2b1974829f853c2b4c5f4a6e503c10af918081aa6f8564e1"
+checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792"
 
 [[package]]
-name = "serde"
-version = "1.0.140"
+name = "tinyvec"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
+dependencies = [
+ "tinyvec_macros",
+]
+
+[[package]]
+name = "tinyvec_macros"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
+
+[[package]]
+name = "tokio"
+version = "1.20.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fc855a42c7967b7c369eb5860f7164ef1f6f81c20c7cc1141f2a604e18723b03"
+checksum = "7a8325f63a7d4774dd041e363b2409ed1c5cbbd0f867795e661df066b2b0a581"
 dependencies = [
- "serde_derive",
+ "autocfg",
+ "bytes",
+ "libc",
+ "memchr",
+ "mio",
+ "num_cpus",
+ "once_cell",
+ "parking_lot",
+ "pin-project-lite",
+ "signal-hook-registry",
+ "socket2",
+ "tokio-macros",
+ "winapi",
 ]
 
 [[package]]
-name = "serde-json-wasm"
-version = "0.4.1"
+name = "tokio-io-timeout"
+version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "479b4dbc401ca13ee8ce902851b834893251404c4f3c65370a49e047a6be09a5"
+checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf"
 dependencies = [
- "serde",
+ "pin-project-lite",
+ "tokio",
 ]
 
 [[package]]
-name = "serde_derive"
-version = "1.0.140"
+name = "tokio-macros"
+version = "1.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f2122636b9fe3b81f1cb25099fcf2d3f542cdb1d45940d56c713158884a05da"
+checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1229,166 +2961,181 @@ dependencies = [
 ]
 
 [[package]]
-name = "serde_derive_internals"
-version = "0.26.0"
+name = "tokio-rustls"
+version = "0.22.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c"
+checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6"
 dependencies = [
- "proc-macro2",
- "quote",
- "syn",
+ "rustls",
+ "tokio",
+ "webpki",
 ]
 
 [[package]]
-name = "serde_json"
-version = "1.0.82"
+name = "tokio-stream"
+version = "0.1.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7"
+checksum = "df54d54117d6fdc4e4fea40fe1e4e566b3505700e148a6827e59b34b0d2600d9"
 dependencies = [
- "itoa",
- "ryu",
- "serde",
+ "futures-core",
+ "pin-project-lite",
+ "tokio",
 ]
 
 [[package]]
-name = "sha2"
-version = "0.9.9"
+name = "tokio-util"
+version = "0.7.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800"
+checksum = "cc463cd8deddc3770d20f9852143d50bf6094e640b485cb2e189a2099085ff45"
 dependencies = [
- "block-buffer",
- "cfg-if",
- "cpufeatures",
- "digest",
- "opaque-debug",
+ "bytes",
+ "futures-core",
+ "futures-sink",
+ "pin-project-lite",
+ "tokio",
+ "tracing",
 ]
 
 [[package]]
-name = "signature"
-version = "1.4.0"
+name = "toml"
+version = "0.5.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "02658e48d89f2bec991f9a78e69cfa4c316f8d6a6c4ec12fae1aeb263d486788"
+checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7"
 dependencies = [
- "digest",
- "rand_core 0.6.3",
+ "serde",
 ]
 
 [[package]]
-name = "spki"
-version = "0.5.4"
+name = "tonic"
+version = "0.7.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "44d01ac02a6ccf3e07db148d2be087da624fea0221a16152ed01f0496a6b0a27"
+checksum = "5be9d60db39854b30b835107500cf0aca0b0d14d6e1c3de124217c23a29c2ddb"
 dependencies = [
- "base64ct",
- "der",
+ "async-stream",
+ "async-trait",
+ "axum",
+ "base64",
+ "bytes",
+ "futures-core",
+ "futures-util",
+ "h2",
+ "http",
+ "http-body",
+ "hyper",
+ "hyper-timeout",
+ "percent-encoding",
+ "pin-project",
+ "prost 0.10.4",
+ "prost-derive 0.10.1",
+ "tokio",
+ "tokio-stream",
+ "tokio-util",
+ "tower",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+ "tracing-futures",
+]
+
+[[package]]
+name = "tower"
+version = "0.4.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
+dependencies = [
+ "futures-core",
+ "futures-util",
+ "indexmap",
+ "pin-project",
+ "pin-project-lite",
+ "rand",
+ "slab",
+ "tokio",
+ "tokio-util",
+ "tower-layer",
+ "tower-service",
+ "tracing",
 ]
 
 [[package]]
-name = "stake-cw20-external-rewards"
-version = "0.2.6"
+name = "tower-http"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c530c8675c1dbf98facee631536fa116b5fb6382d7dd6dc1b118d970eafe3ba"
 dependencies = [
- "anyhow",
- "cosmwasm-schema",
- "cosmwasm-std",
- "cosmwasm-storage",
- "cw-controllers",
- "cw-multi-test 0.13.4",
- "cw-storage-plus 0.13.4",
- "cw-utils 0.13.4",
- "cw2 0.13.4",
- "cw20 0.13.4",
- "cw20-base",
- "cw20-stake",
- "schemars",
- "serde",
- "thiserror",
+ "bitflags",
+ "bytes",
+ "futures-core",
+ "futures-util",
+ "http",
+ "http-body",
+ "http-range-header",
+ "pin-project-lite",
+ "tower",
+ "tower-layer",
+ "tower-service",
 ]
 
 [[package]]
-name = "stake-cw20-reward-distributor"
-version = "0.1.0"
-dependencies = [
- "cosmwasm-schema",
- "cosmwasm-std",
- "cosmwasm-storage",
- "cw-multi-test 0.13.4",
- "cw-storage-plus 0.13.4",
- "cw-utils 0.13.4",
- "cw2 0.13.4",
- "cw20 0.13.4",
- "cw20-base",
- "cw20-stake",
- "schemars",
- "serde",
- "thiserror",
-]
+name = "tower-layer"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "343bc9466d3fe6b0f960ef45960509f84480bf4fd96f92901afe7ff3df9d3a62"
 
 [[package]]
-name = "static_assertions"
-version = "1.1.0"
+name = "tower-service"
+version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
 
 [[package]]
-name = "subtle"
-version = "2.4.1"
+name = "tracing"
+version = "0.1.36"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
+checksum = "2fce9567bd60a67d08a16488756721ba392f24f29006402881e43b19aac64307"
+dependencies = [
+ "cfg-if",
+ "log",
+ "pin-project-lite",
+ "tracing-attributes",
+ "tracing-core",
+]
 
 [[package]]
-name = "syn"
-version = "1.0.98"
+name = "tracing-attributes"
+version = "0.1.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd"
+checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2"
 dependencies = [
  "proc-macro2",
  "quote",
- "unicode-ident",
+ "syn",
 ]
 
 [[package]]
-name = "testing"
-version = "0.1.0"
+name = "tracing-core"
+version = "0.1.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5aeea4303076558a00714b823f9ad67d58a3bbda1df83d8827d21193156e22f7"
 dependencies = [
- "cosmwasm-schema",
- "cosmwasm-std",
- "cw-core",
- "cw-core-interface",
- "cw-multi-test 0.13.4",
- "cw-utils 0.13.4",
- "cw2 0.13.4",
- "cw20 0.13.4",
- "cw20-balance-voting",
- "cw20-base",
- "cw20-stake",
- "cw20-staked-balance-voting",
- "cw4",
- "cw4-group",
- "cw4-voting",
- "indexable-hooks",
- "rand",
- "voting",
+ "once_cell",
 ]
 
 [[package]]
-name = "thiserror"
-version = "1.0.31"
+name = "tracing-futures"
+version = "0.2.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a"
+checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2"
 dependencies = [
- "thiserror-impl",
+ "pin-project",
+ "tracing",
 ]
 
 [[package]]
-name = "thiserror-impl"
-version = "1.0.31"
+name = "try-lock"
+version = "0.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
+checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
 
 [[package]]
 name = "typenum"
@@ -1396,6 +3143,12 @@ version = "1.15.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
 
+[[package]]
+name = "ucd-trie"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89570599c4fe5585de2b388aab47e99f7fa4e9238a1399f707a02e356058141c"
+
 [[package]]
 name = "uint"
 version = "0.9.3"
@@ -1408,11 +3161,56 @@ dependencies = [
  "static_assertions",
 ]
 
+[[package]]
+name = "unicode-bidi"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
+
 [[package]]
 name = "unicode-ident"
-version = "1.0.2"
+version = "1.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "15c61ba63f9235225a22310255a29b806b907c9b8c964bcbd0a2c70f3f2deea7"
+checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf"
+
+[[package]]
+name = "unicode-normalization"
+version = "0.1.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "854cbdc4f7bc6ae19c820d44abdc3277ac3e1b2b93db20a636825d9322fb60e6"
+dependencies = [
+ "tinyvec",
+]
+
+[[package]]
+name = "unicode-xid"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04"
+
+[[package]]
+name = "untrusted"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
+
+[[package]]
+name = "url"
+version = "2.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c"
+dependencies = [
+ "form_urlencoded",
+ "idna",
+ "matches",
+ "percent-encoding",
+]
+
+[[package]]
+name = "uuid"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
 
 [[package]]
 name = "version_check"
@@ -1446,6 +3244,27 @@ dependencies = [
  "thiserror",
 ]
 
+[[package]]
+name = "walkdir"
+version = "2.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
+dependencies = [
+ "same-file",
+ "winapi",
+ "winapi-util",
+]
+
+[[package]]
+name = "want"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0"
+dependencies = [
+ "log",
+ "try-lock",
+]
+
 [[package]]
 name = "wasi"
 version = "0.9.0+wasi-snapshot-preview1"
@@ -1458,8 +3277,189 @@ version = "0.11.0+wasi-snapshot-preview1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
 
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.82"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc7652e3f6c4706c8d9cd54832c4a4ccb9b5336e2c3bd154d5cccfbf1c1f5f7d"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.82"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "662cd44805586bd52971b9586b1df85cdbbd9112e4ef4d8f41559c334dc6ac3f"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.82"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b260f13d3012071dfb1512849c033b1925038373aea48ced3012c09df952c602"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.82"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5be8e654bdd9b79216c2929ab90721aa82faf65c48cdf08bdc4e7f51357b80da"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.82"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6598dd0bd3c7d51095ff6531a5b23e02acdc81804e30d8f07afb77b7215a140a"
+
+[[package]]
+name = "web-sys"
+version = "0.3.59"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed055ab27f941423197eb86b2035720b1a3ce40504df082cac2ecc6ed73335a1"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "webpki"
+version = "0.21.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea"
+dependencies = [
+ "ring",
+ "untrusted",
+]
+
+[[package]]
+name = "webpki-roots"
+version = "0.21.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940"
+dependencies = [
+ "webpki",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows-sys"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
+dependencies = [
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
+
+[[package]]
+name = "yaml-rust"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
+dependencies = [
+ "linked-hash-map",
+]
+
 [[package]]
 name = "zeroize"
 version = "1.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd"
+dependencies = [
+ "zeroize_derive",
+]
+
+[[package]]
+name = "zeroize_derive"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f8f187641dad4f680d25c4bfc4225b418165984179f26ca76ec4fb6441d3a17"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "synstructure",
+]
diff --git a/Cargo.toml b/Cargo.toml
index 64f2b8ea..66e6faeb 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,5 +1,5 @@
 [workspace]
-members = ["contracts/*", "packages/*", "debug/*"]
+members = ["contracts/*", "packages/*", "debug/*", "ci/*"]
 
 
 [profile.release.package.stake-cw20-external-rewards]
diff --git a/README.md b/README.md
index 7f5c22dc..6770996f 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,13 @@
 # DAO DAO Contracts
+
 [![codecov](https://codecov.io/gh/DA0-DA0/dao-contracts/branch/main/graph/badge.svg?token=SCKOIPYZPV)](https://codecov.io/gh/DA0-DA0/dao-contracts)
 
+This is a collection for smart contracts for building composable,
+modular, and upgradable DAOs.
+
+For an overview of our contract design, see [our
+wiki](https://github.com/DA0-DA0/dao-contracts/wiki/DAO-DAO-Contracts-Design).
+
 | Audited contracts                                                        | Description                                                |
 | :----------------------------------------------------------------------- | :--------------------------------------------------------- |
 | [cw-core](contracts/cw-core)                                             | The core module for a DAO DAO DAO.                         |
@@ -8,75 +15,46 @@
 | [cw20-staked-balance-voting](contracts/cw20-staked-balance-voting)       | A voting power module for staked governance tokens.        |
 | [cw4-voting](contracts/cw4-voting)                                       | A voting power module for multisig-style voting.           |
 | [cw20-stake](contracts/cw20-stake)                                       | A contract for staking cw20 tokens.                        |
+| [stake-external-rewards](contracts/cw20-stake-external-rewards)          | A contract for providing external stakinig rewards.        |
+| [cw20-stake-reward-distributor](contracts/cw20-stake-external-rewards)   | A contract for distributing rewards via stake-cw20.        |
 
 
 | Unaudited contracts                                                      | Description                                                |
 | :----------------------------------------------------------------------- | :--------------------------------------------------------- |
 | [cw-named-groups](contracts/cw-named-groups)                             | A contract for managing named groups of addresses.         |
-| [cw-proposal-sudo](contracts/cw-proposal-sudo)                           | A proposal module that allows an admin to control a DAO.   |
-| [cw20-balance-voting](contracts/cw20-balance-voting)                     | TESTING ONLY - a voting module based on cw20 balances.     |
-| [proposal-hooks-counter](contracts/proposal-hooks-counter)               | TESTING ONLY - a contract for testing proposal hooks.      |
-| [stake-external-rewards](contracts/cw20-stake-external-rewards)          | A contract for providing external stakinig rewards.        |
-| [cw20-stake-reward-distributor](contracts/cw20-stake-external-rewards)   | A contract for distributing rewards via stake-cw20.        |
 | [cw-proposal-multiple](contracts/cw-proposal-multiple)                   | A proposal module for multiple choice proposals.           |
-Audited contracts have completed [an
-audit](https://github.com/securityDAO/audits/blob/7bb8e4910baaea89fddfc025591658f44adbc27c/cosmwasm/dao-contracts/v0.3%20DAO%20DAO%20audit.pdf)
-by security DAO. A second audit is forthcoming.
-
-## Contributing
-
-Interested in contributing to DAO DAO? Check out [CONTRIBUTING.md](./CONTRIBUTING.md).
-
-## Deploying in a development environment
-
-_Note: this will deploy the legacy version of the contracts currently
-running at [daodao.zone](https://daodao.zone)._
-
-Build and deploy the contracts to a local chain running in Docker with:
-
-```sh
-bash scripts/deploy_local.sh juno10j9gpw9t4jsz47qgnkvl5n3zlm2fz72k67rxsg
-```
-
-> Note: This Wasm account is from the [default account](default-account.txt), which you can use for testing (DO NOT store any real funds with this account). You can pass in any wasm account address you want to use.
 
-This will run a chain locally in a docker container, then build and deploy the contracts to that chain.
+Audited contracts have completed audits by
+[securityDAO](https://github.com/securityDAO/audits/blob/7bb8e4910baaea89fddfc025591658f44adbc27c/cosmwasm/dao-contracts/v0.3%20DAO%20DAO%20audit.pdf)
+and [Oak
+Security](https://github.com/oak-security/audit-reports/blob/2377ba8cfcfd505283c789d706311b06771d6db4/DAO%20DAO/2022-06-22%20Audit%20Report%20-%20DAO%20DAO%20v1.0.pdf).
 
-The script will output something like:
+## Developers
 
-```sh
-NEXT_PUBLIC_CW20_CODE_ID=1
-NEXT_PUBLIC_CW4GROUP_CODE_ID=2
-NEXT_PUBLIC_CWCORE_CODE_ID=7
-NEXT_PUBLIC_CWPROPOSALSINGLE_CODE_ID=11
-NEXT_PUBLIC_CW4VOTING_CODE_ID=5
-NEXT_PUBLIC_CW20STAKEDBALANCEVOTING_CODE_ID=4
-NEXT_PUBLIC_STAKECW20_CODE_ID=13
-NEXT_PUBLIC_DAO_CONTRACT_ADDRESS=juno1zlmaky7753d2fneyhduwz0rn3u9ns8rse3tudhze8rc2g54w9ysqgjt23l
-NEXT_PUBLIC_V1_FACTORY_CONTRACT_ADDRESS=juno1pvrwmjuusn9wh34j7y520g8gumuy9xtl3gvprlljfdpwju3x7ucssml9ug
-```
+Information about our development workflow and how to contribute can
+be found in [CONTRIBUTING.md](./CONTRIBUTING.md).
 
-You can then instantiate and interact with contracts.
+## Testing
 
-Note, to send commands to the docker container:
+### Unit tests
 
-```sh
-docker exec -i cosmwasm junod status
-```
+Run `cargo t` or `cargo unit-test` from the project root to run the unit tests.
 
-Some commands require a password which defaults to `xxxxxxxxx`. You can use them like so:
+### Integration tests
 
-```sh
-echo xxxxxxxxx | docker exec -i cosmwasm  junod keys show validator -a
-```
+* `./scripts/deploy_local.sh juno10j9gpw9t4jsz47qgnkvl5n3zlm2fz72k67rxsg`
+* `cd ci/integration_tests`
+* `CONTRACT_DIR="../../artifacts" GAS_OUT_DIR="gas_reports" CONFIG="configs/local.yaml" cargo t`
 
-## Generating schema for all contracts
-As we have a workflow to check schema differences on commit, to quickly run `cargo schema` against all contracts
-simply run the following from the repo root:
-```sh
-./scripts/schema.sh
-```
+See [ci/integration_tests/README.md](ci/integration_tests/README.md) for more information.
 
 ## Disclaimer
 
-DAO DAO TOOLING IS PROVIDED “AS IS”, AT YOUR OWN RISK, AND WITHOUT WARRANTIES OF ANY KIND. No developer or entity involved in creating the DAO DAO UI or smart contracts will be liable for any claims or damages whatsoever associated with your use, inability to use, or your interaction with other users of DAO DAO tooling, including any direct, indirect, incidental, special, exemplary, punitive or consequential damages, or loss of profits, cryptocurrencies, tokens, or anything else of value.
+DAO DAO TOOLING IS PROVIDED “AS IS”, AT YOUR OWN RISK, AND WITHOUT
+WARRANTIES OF ANY KIND. No developer or entity involved in creating
+the DAO DAO UI or smart contracts will be liable for any claims or
+damages whatsoever associated with your use, inability to use, or your
+interaction with other users of DAO DAO tooling, including any direct,
+indirect, incidental, special, exemplary, punitive or consequential
+damages, or loss of profits, cryptocurrencies, tokens, or anything
+else of value.
diff --git a/contracts/cw-proposal-multiple/schema/config_response.json b/contracts/cw-proposal-multiple/schema/config_response.json
index 0a0d59d1..4c87d9d7 100644
--- a/contracts/cw-proposal-multiple/schema/config_response.json
+++ b/contracts/cw-proposal-multiple/schema/config_response.json
@@ -4,6 +4,7 @@
   "description": "The governance module's configuration.",
   "type": "object",
   "required": [
+    "allow_revoting",
     "close_proposal_on_execution_failure",
     "dao",
     "max_voting_period",
@@ -11,6 +12,10 @@
     "voting_strategy"
   ],
   "properties": {
+    "allow_revoting": {
+      "description": "Allows changing votes before the proposal expires. If this is enabled proposals will not be able to complete early as final vote information is not known until the time of proposal expiration.",
+      "type": "boolean"
+    },
     "close_proposal_on_execution_failure": {
       "description": "If set to true proposals will be closed if their execution fails. Otherwise, proposals will remain open after execution failure. For example, with this enabled a proposal to send 5 tokens out of a DAO's treasury with 4 tokens would be closed when it is executed. With this disabled, that same proposal would remain open until the DAO's treasury was large enough for it to be executed.",
       "type": "boolean"
diff --git a/contracts/cw-proposal-multiple/schema/execute_msg.json b/contracts/cw-proposal-multiple/schema/execute_msg.json
index 6eda1c0b..03326979 100644
--- a/contracts/cw-proposal-multiple/schema/execute_msg.json
+++ b/contracts/cw-proposal-multiple/schema/execute_msg.json
@@ -129,6 +129,7 @@
         "update_config": {
           "type": "object",
           "required": [
+            "allow_revoting",
             "close_proposal_on_execution_failure",
             "dao",
             "max_voting_period",
@@ -136,6 +137,10 @@
             "voting_strategy"
           ],
           "properties": {
+            "allow_revoting": {
+              "description": "Allows changing votes before the proposal expires. If this is enabled proposals will not be able to complete early as final vote information is not known until the time of proposal expiration.",
+              "type": "boolean"
+            },
             "close_proposal_on_execution_failure": {
               "description": "If set to true proposals will be closed if their execution fails. Otherwise, proposals will remain open after execution failure. For example, with this enabled a proposal to send 5 tokens out of a DAO's treasury with 4 tokens would be closed when it is executed. With this disabled, that same proposal would remain open until the DAO's treasury was large enough for it to be executed.",
               "type": "boolean"
diff --git a/contracts/cw-proposal-multiple/schema/instantiate_msg.json b/contracts/cw-proposal-multiple/schema/instantiate_msg.json
index 4dfa2967..1529297f 100644
--- a/contracts/cw-proposal-multiple/schema/instantiate_msg.json
+++ b/contracts/cw-proposal-multiple/schema/instantiate_msg.json
@@ -3,12 +3,17 @@
   "title": "InstantiateMsg",
   "type": "object",
   "required": [
+    "allow_revoting",
     "close_proposal_on_execution_failure",
     "max_voting_period",
     "only_members_execute",
     "voting_strategy"
   ],
   "properties": {
+    "allow_revoting": {
+      "description": "Allows changing votes before the proposal expires. If this is enabled proposals will not be able to complete early as final vote information is not known until the time of proposal expiration.",
+      "type": "boolean"
+    },
     "close_proposal_on_execution_failure": {
       "description": "If set to true proposals will be closed if their execution fails. Otherwise, proposals will remain open after execution failure. For example, with this enabled a proposal to send 5 tokens out of a DAO's treasury with 4 tokens would be closed when it is executed. With this disabled, that same proposal would remain open until the DAO's treasury was large enough for it to be executed.",
       "type": "boolean"
diff --git a/contracts/cw-proposal-multiple/schema/list_proposals_response.json b/contracts/cw-proposal-multiple/schema/list_proposals_response.json
index 32a63fef..b31f468d 100644
--- a/contracts/cw-proposal-multiple/schema/list_proposals_response.json
+++ b/contracts/cw-proposal-multiple/schema/list_proposals_response.json
@@ -569,6 +569,7 @@
     "MultipleChoiceProposal": {
       "type": "object",
       "required": [
+        "allow_revoting",
         "choices",
         "description",
         "expiration",
@@ -581,6 +582,9 @@
         "voting_strategy"
       ],
       "properties": {
+        "allow_revoting": {
+          "type": "boolean"
+        },
         "choices": {
           "type": "array",
           "items": {
diff --git a/contracts/cw-proposal-multiple/schema/proposal_response.json b/contracts/cw-proposal-multiple/schema/proposal_response.json
index d12b51df..5fab5168 100644
--- a/contracts/cw-proposal-multiple/schema/proposal_response.json
+++ b/contracts/cw-proposal-multiple/schema/proposal_response.json
@@ -573,6 +573,7 @@
     "MultipleChoiceProposal": {
       "type": "object",
       "required": [
+        "allow_revoting",
         "choices",
         "description",
         "expiration",
@@ -585,6 +586,9 @@
         "voting_strategy"
       ],
       "properties": {
+        "allow_revoting": {
+          "type": "boolean"
+        },
         "choices": {
           "type": "array",
           "items": {
diff --git a/contracts/cw-proposal-multiple/schema/reverse_proposals_response.json b/contracts/cw-proposal-multiple/schema/reverse_proposals_response.json
index 82dedbb6..75e019a9 100644
--- a/contracts/cw-proposal-multiple/schema/reverse_proposals_response.json
+++ b/contracts/cw-proposal-multiple/schema/reverse_proposals_response.json
@@ -569,6 +569,7 @@
     "MultipleChoiceProposal": {
       "type": "object",
       "required": [
+        "allow_revoting",
         "choices",
         "description",
         "expiration",
@@ -581,6 +582,9 @@
         "voting_strategy"
       ],
       "properties": {
+        "allow_revoting": {
+          "type": "boolean"
+        },
         "choices": {
           "type": "array",
           "items": {
diff --git a/contracts/cw-proposal-multiple/src/contract.rs b/contracts/cw-proposal-multiple/src/contract.rs
index f8ff562b..090292cc 100644
--- a/contracts/cw-proposal-multiple/src/contract.rs
+++ b/contracts/cw-proposal-multiple/src/contract.rs
@@ -61,6 +61,7 @@ pub fn instantiate(
         min_voting_period,
         max_voting_period,
         only_members_execute: msg.only_members_execute,
+        allow_revoting: msg.allow_revoting,
         dao,
         deposit_info,
         close_proposal_on_execution_failure: msg.close_proposal_on_execution_failure,
@@ -96,6 +97,7 @@ pub fn execute(
             min_voting_period,
             max_voting_period,
             only_members_execute,
+            allow_revoting,
             dao,
             deposit_info,
             close_proposal_on_execution_failure,
@@ -106,6 +108,7 @@ pub fn execute(
             min_voting_period,
             max_voting_period,
             only_members_execute,
+            allow_revoting,
             dao,
             deposit_info,
             close_proposal_on_execution_failure,
@@ -176,6 +179,7 @@ pub fn execute_propose(
             total_power,
             status: Status::Open,
             votes: MultipleChoiceVotes::zero(checked_multiple_choice_options.len()),
+            allow_revoting: config.allow_revoting,
             deposit_info: config.deposit_info.clone(),
             choices: checked_multiple_choice_options,
         };
@@ -256,7 +260,26 @@ pub fn execute_vote(
         deps.storage,
         (proposal_id, info.sender.clone()),
         |bal| match bal {
-            Some(_) => Err(ContractError::AlreadyVoted {}),
+            Some(current_ballot) => {
+                if prop.allow_revoting {
+                    if current_ballot.vote == vote {
+                        // Don't allow casting the same vote more than
+                        // once. This seems liable to be confusing
+                        // behavior.
+                        Err(ContractError::AlreadyCast {})
+                    } else {
+                        // Remove the old vote if this is a re-vote.
+                        prop.votes
+                            .remove_vote(current_ballot.vote, current_ballot.power)?;
+                        Ok(Ballot {
+                            power: vote_power,
+                            vote,
+                        })
+                    }
+                } else {
+                    Err(ContractError::AlreadyVoted {})
+                }
+            }
             None => Ok(Ballot {
                 vote,
                 power: vote_power,
@@ -265,7 +288,8 @@ pub fn execute_vote(
     )?;
 
     let old_status = prop.status;
-    prop.votes.add_vote(vote, vote_power);
+
+    prop.votes.add_vote(vote, vote_power)?;
     prop.update_status(&env.block)?;
     PROPOSALS.save(deps.storage, proposal_id, &prop)?;
     let new_status = prop.status;
@@ -394,7 +418,7 @@ pub fn execute_close(
     let config = CONFIG.load(deps.storage)?;
 
     prop.update_status(&env.block)?;
-    if prop.status != Status::Rejected && prop.status != Status::ExecutionFailed {
+    if prop.status != Status::Rejected {
         return Err(ContractError::WrongCloseStatus {});
     }
 
@@ -440,6 +464,7 @@ pub fn execute_update_config(
     min_voting_period: Option<Duration>,
     max_voting_period: Duration,
     only_members_execute: bool,
+    allow_revoting: bool,
     dao: String,
     deposit_info: Option<DepositInfo>,
     close_proposal_on_execution_failure: bool,
@@ -468,6 +493,7 @@ pub fn execute_update_config(
             min_voting_period,
             max_voting_period,
             only_members_execute,
+            allow_revoting,
             dao,
             deposit_info,
             close_proposal_on_execution_failure,
diff --git a/contracts/cw-proposal-multiple/src/error.rs b/contracts/cw-proposal-multiple/src/error.rs
index bb5f7516..1adcde14 100644
--- a/contracts/cw-proposal-multiple/src/error.rs
+++ b/contracts/cw-proposal-multiple/src/error.rs
@@ -40,9 +40,12 @@ pub enum ContractError {
     #[error("Not registered to vote (no voting power) at time of proposal creation.")]
     NotRegistered {},
 
-    #[error("Already voted")]
+    #[error("Already voted. This proposal does not support revoting.")]
     AlreadyVoted {},
 
+    #[error("Already cast a vote with that option. Change your vote to revote.")]
+    AlreadyCast {},
+
     #[error("Proposal must be in 'passed' state to be executed.")]
     NotPassed {},
 
diff --git a/contracts/cw-proposal-multiple/src/msg.rs b/contracts/cw-proposal-multiple/src/msg.rs
index d08f4f27..57f91d61 100644
--- a/contracts/cw-proposal-multiple/src/msg.rs
+++ b/contracts/cw-proposal-multiple/src/msg.rs
@@ -22,6 +22,11 @@ pub struct InstantiateMsg {
     /// proposals. Otherwise, any address may execute a passed
     /// proposal.
     pub only_members_execute: bool,
+    /// Allows changing votes before the proposal expires. If this is
+    /// enabled proposals will not be able to complete early as final
+    /// vote information is not known until the time of proposal
+    /// expiration.
+    pub allow_revoting: bool,
     /// Information about the deposit required to create a
     /// proposal. None if there is no deposit requirement, Some
     /// otherwise.
@@ -88,6 +93,11 @@ pub enum ExecuteMsg {
         /// proposals. Otherwise, any address may execute a passed
         /// proposal. Applies to all outstanding and future proposals.
         only_members_execute: bool,
+        /// Allows changing votes before the proposal expires. If this is
+        /// enabled proposals will not be able to complete early as final
+        /// vote information is not known until the time of proposal
+        /// expiration.
+        allow_revoting: bool,
         /// The address if tge DAO that this governance module is
         /// associated with.
         dao: String,
diff --git a/contracts/cw-proposal-multiple/src/proposal.rs b/contracts/cw-proposal-multiple/src/proposal.rs
index d387fa12..5262705f 100644
--- a/contracts/cw-proposal-multiple/src/proposal.rs
+++ b/contracts/cw-proposal-multiple/src/proposal.rs
@@ -32,6 +32,7 @@ pub struct MultipleChoiceProposal {
     /// The total power when the proposal started (used to calculate percentages)
     pub total_power: Uint128,
     pub votes: MultipleChoiceVotes,
+    pub allow_revoting: bool,
     /// Information about the deposit that was sent as part of this
     /// proposal. None if no deposit.
     pub deposit_info: Option<CheckedDepositInfo>,
@@ -93,6 +94,11 @@ impl MultipleChoiceProposal {
     /// one of the options that is not "None of the above"
     /// has won the most votes, and there is no tie.
     pub fn is_passed(&self, block: &BlockInfo) -> StdResult<bool> {
+        // If re-voting is allowed nothing is known until the proposal
+        // has expired.
+        if self.allow_revoting && !self.expiration.is_expired(block) {
+            return Ok(false);
+        }
         // If the min voting period is set and not expired the
         // proposal can not yet be passed. This gives DAO members some
         // time to remove liquidity / scheme on a recovery plan if a
@@ -133,6 +139,12 @@ impl MultipleChoiceProposal {
     }
 
     pub fn is_rejected(&self, block: &BlockInfo) -> StdResult<bool> {
+        // If re-voting is allowed and the proposal is not expired no
+        // information is known.
+        if self.allow_revoting && !self.expiration.is_expired(block) {
+            return Ok(false);
+        }
+
         let vote_result = self.calculate_vote_result()?;
         match vote_result {
             // Proposal is rejected if there is a tie, and either the proposal is expired or
@@ -262,6 +274,7 @@ mod tests {
         votes: MultipleChoiceVotes,
         total_power: Uint128,
         is_expired: bool,
+        allow_revoting: bool,
     ) -> MultipleChoiceProposal {
         // The last option that gets added in into_checked is always the none of the above option
         let options = vec![
@@ -294,6 +307,7 @@ mod tests {
             voting_strategy,
             total_power,
             votes,
+            allow_revoting,
             deposit_info: None,
             min_voting_period: None,
         }
@@ -316,6 +330,7 @@ mod tests {
             votes,
             Uint128::new(1),
             false,
+            false,
         );
 
         // Quorum was met and all votes were cast, should be passed.
@@ -331,6 +346,7 @@ mod tests {
             votes,
             Uint128::new(1),
             false,
+            false,
         );
 
         // Quorum was met but none of the above won, should be rejected.
@@ -346,6 +362,7 @@ mod tests {
             votes,
             Uint128::new(100),
             false,
+            false,
         );
 
         // Quorum was not met and is not expired, should be open.
@@ -361,6 +378,7 @@ mod tests {
             votes,
             Uint128::new(100),
             true,
+            false,
         );
 
         // Quorum was not met and it is expired, should be rejected.
@@ -376,6 +394,7 @@ mod tests {
             votes,
             Uint128::new(100),
             true,
+            false,
         );
 
         // Quorum was met but it is a tie and expired, should be rejected.
@@ -385,7 +404,14 @@ mod tests {
         let votes = MultipleChoiceVotes {
             vote_weights: vec![Uint128::new(50), Uint128::new(50), Uint128::new(0)],
         };
-        let prop = create_proposal(&env.block, voting_strategy, votes, Uint128::new(150), false);
+        let prop = create_proposal(
+            &env.block,
+            voting_strategy,
+            votes,
+            Uint128::new(150),
+            false,
+            false,
+        );
 
         // Quorum was met but it is a tie but not expired and still voting power remains, should be open.
         assert!(!prop.is_passed(&env.block).unwrap());
@@ -411,6 +437,7 @@ mod tests {
             votes,
             Uint128::new(1),
             false,
+            false,
         );
 
         // Quorum was met and all votes were cast, should be passed.
@@ -426,6 +453,7 @@ mod tests {
             votes,
             Uint128::new(1),
             false,
+            false,
         );
 
         // Quorum was met but none of the above won, should be rejected.
@@ -441,6 +469,7 @@ mod tests {
             votes,
             Uint128::new(100),
             false,
+            false,
         );
 
         // Quorum was not met and is not expired, should be open.
@@ -456,6 +485,7 @@ mod tests {
             votes,
             Uint128::new(101),
             true,
+            false,
         );
 
         // Quorum was not met and it is expired, should be rejected.
@@ -471,6 +501,7 @@ mod tests {
             votes,
             Uint128::new(10000),
             true,
+            false,
         );
 
         // Quorum was met but it is a tie and expired, should be rejected.
@@ -480,7 +511,14 @@ mod tests {
         let votes = MultipleChoiceVotes {
             vote_weights: vec![Uint128::new(50), Uint128::new(50), Uint128::new(0)],
         };
-        let prop = create_proposal(&env.block, voting_strategy, votes, Uint128::new(150), false);
+        let prop = create_proposal(
+            &env.block,
+            voting_strategy,
+            votes,
+            Uint128::new(150),
+            false,
+            false,
+        );
 
         // Quorum was met but it is a tie but not expired and still voting power remains, should be open.
         assert!(!prop.is_passed(&env.block).unwrap());
@@ -504,6 +542,7 @@ mod tests {
             votes,
             Uint128::new(1000),
             false,
+            false,
         );
 
         // Quorum was met but none of the above is winning, but it also can't be beat (only a tie at best), should be rejected
@@ -522,7 +561,14 @@ mod tests {
         let votes = MultipleChoiceVotes {
             vote_weights: vec![Uint128::new(10), Uint128::new(0), Uint128::new(0)],
         };
-        let prop = create_proposal(&env.block, voting_strategy, votes, Uint128::new(100), true);
+        let prop = create_proposal(
+            &env.block,
+            voting_strategy,
+            votes,
+            Uint128::new(100),
+            true,
+            false,
+        );
 
         // Quorum was met and proposal expired, should pass
         assert!(prop.is_passed(&env.block).unwrap());
@@ -544,6 +590,7 @@ mod tests {
             votes,
             Uint128::new(1000000),
             true,
+            false,
         );
 
         // Quorum was not met and expired, should reject
@@ -566,6 +613,7 @@ mod tests {
             votes,
             Uint128::new(10000000),
             true,
+            false,
         );
 
         // Quorum was not met and expired, should reject
@@ -590,13 +638,21 @@ mod tests {
             votes.clone(),
             Uint128::new(13),
             true,
+            false,
         );
 
         // Should pass if expired
         assert!(prop.is_passed(&env.block).unwrap());
         assert!(!prop.is_rejected(&env.block).unwrap());
 
-        let prop = create_proposal(&env.block, voting_strategy, votes, Uint128::new(13), false);
+        let prop = create_proposal(
+            &env.block,
+            voting_strategy,
+            votes,
+            Uint128::new(13),
+            false,
+            false,
+        );
 
         // Should pass if not expired
         assert!(prop.is_passed(&env.block).unwrap());
@@ -619,16 +675,178 @@ mod tests {
             votes.clone(),
             Uint128::new(13),
             true,
+            false,
         );
 
         // Should pass if majority voted
         assert!(prop.is_passed(&env.block).unwrap());
         assert!(!prop.is_rejected(&env.block).unwrap());
 
-        let prop = create_proposal(&env.block, voting_strategy, votes, Uint128::new(14), true);
+        let prop = create_proposal(
+            &env.block,
+            voting_strategy,
+            votes,
+            Uint128::new(14),
+            true,
+            false,
+        );
 
         // Shouldn't pass if only half voted
         assert!(!prop.is_passed(&env.block).unwrap());
         assert!(prop.is_rejected(&env.block).unwrap());
     }
+
+    #[test]
+    fn test_majority_revote_pass() {
+        // Revoting being allowed means that proposals may not be
+        // passed or rejected before they expire.
+        let env = mock_env();
+        let voting_strategy = VotingStrategy::SingleChoice {
+            quorum: voting::threshold::PercentageThreshold::Majority {},
+        };
+        let votes = MultipleChoiceVotes {
+            vote_weights: vec![Uint128::new(6), Uint128::new(0), Uint128::new(0)],
+        };
+
+        let prop = create_proposal(
+            &env.block,
+            voting_strategy.clone(),
+            votes.clone(),
+            Uint128::new(10),
+            false,
+            true,
+        );
+        // Quorum reached, but proposal is still active => no pass
+        assert!(!prop.is_passed(&env.block).unwrap());
+
+        let prop = create_proposal(
+            &env.block,
+            voting_strategy,
+            votes,
+            Uint128::new(10),
+            true,
+            true,
+        );
+        // Quorum reached & proposal has expired => pass
+        assert!(prop.is_passed(&env.block).unwrap());
+    }
+
+    #[test]
+    fn test_majority_revote_rejection() {
+        // Revoting being allowed means that proposals may not be
+        // passed or rejected before they expire.
+        let env = mock_env();
+        let voting_strategy = VotingStrategy::SingleChoice {
+            quorum: voting::threshold::PercentageThreshold::Majority {},
+        };
+        let votes = MultipleChoiceVotes {
+            vote_weights: vec![Uint128::new(5), Uint128::new(5), Uint128::new(0)],
+        };
+
+        let prop = create_proposal(
+            &env.block,
+            voting_strategy.clone(),
+            votes.clone(),
+            Uint128::new(10),
+            false,
+            true,
+        );
+        // Everyone voted and proposal is in a tie...
+        assert_eq!(prop.total_power, prop.votes.total());
+        assert_eq!(prop.votes.vote_weights[0], prop.votes.vote_weights[1]);
+        // ... but proposal is still active => no rejection
+        assert!(!prop.is_rejected(&env.block).unwrap());
+
+        let prop = create_proposal(
+            &env.block,
+            voting_strategy,
+            votes,
+            Uint128::new(10),
+            true,
+            true,
+        );
+        // Proposal has expired and ended in a tie => rejection
+        assert_eq!(prop.votes.vote_weights[0], prop.votes.vote_weights[1]);
+        assert!(prop.is_rejected(&env.block).unwrap());
+    }
+
+    #[test]
+    fn test_percentage_revote_pass() {
+        // Revoting being allowed means that proposals may not be
+        // passed or rejected before they expire.
+        let env = mock_env();
+        let voting_strategy = VotingStrategy::SingleChoice {
+            quorum: voting::threshold::PercentageThreshold::Percent(
+                cosmwasm_std::Decimal::percent(80),
+            ),
+        };
+
+        let votes = MultipleChoiceVotes {
+            vote_weights: vec![Uint128::new(81), Uint128::new(0), Uint128::new(0)],
+        };
+
+        let prop = create_proposal(
+            &env.block,
+            voting_strategy.clone(),
+            votes.clone(),
+            Uint128::new(100),
+            false,
+            true,
+        );
+        // Quorum reached, but proposal is still active => no pass
+        assert!(!prop.is_passed(&env.block).unwrap());
+
+        let prop = create_proposal(
+            &env.block,
+            voting_strategy,
+            votes,
+            Uint128::new(100),
+            true,
+            true,
+        );
+        // Quorum reached & proposal has expired => pass
+        assert!(prop.is_passed(&env.block).unwrap());
+    }
+
+    #[test]
+    fn test_percentage_revote_rejection() {
+        // Revoting being allowed means that proposals may not be
+        // passed or rejected before they expire.
+        let env = mock_env();
+        let voting_strategy = VotingStrategy::SingleChoice {
+            quorum: voting::threshold::PercentageThreshold::Percent(
+                cosmwasm_std::Decimal::percent(80),
+            ),
+        };
+
+        let votes = MultipleChoiceVotes {
+            vote_weights: vec![Uint128::new(90), Uint128::new(0), Uint128::new(0)],
+        };
+
+        let prop = create_proposal(
+            &env.block,
+            voting_strategy.clone(),
+            votes,
+            Uint128::new(100),
+            false,
+            true,
+        );
+        // Quorum reached, but proposal is still active => no rejection
+        assert!(!prop.is_rejected(&env.block).unwrap());
+
+        let votes = MultipleChoiceVotes {
+            vote_weights: vec![Uint128::new(50), Uint128::new(0), Uint128::new(0)],
+        };
+
+        let prop = create_proposal(
+            &env.block,
+            voting_strategy,
+            votes,
+            Uint128::new(100),
+            true,
+            true,
+        );
+        // No quorum reached & proposal has expired => rejection
+        assert!(prop.is_rejected(&env.block).unwrap());
+    }
 }
diff --git a/contracts/cw-proposal-multiple/src/state.rs b/contracts/cw-proposal-multiple/src/state.rs
index 6eebf9df..2e685e87 100644
--- a/contracts/cw-proposal-multiple/src/state.rs
+++ b/contracts/cw-proposal-multiple/src/state.rs
@@ -28,6 +28,11 @@ pub struct Config {
     /// proposals. Otherwise, any address may execute a passed
     /// proposal.
     pub only_members_execute: bool,
+    /// Allows changing votes before the proposal expires. If this is
+    /// enabled proposals will not be able to complete early as final
+    /// vote information is not known until the time of proposal
+    /// expiration.
+    pub allow_revoting: bool,
     /// The address of the DAO that this governance module is
     /// associated with.
     pub dao: Addr,
diff --git a/contracts/cw-proposal-single/src/contract.rs b/contracts/cw-proposal-single/src/contract.rs
index 5f473f7e..5dd3b01f 100644
--- a/contracts/cw-proposal-single/src/contract.rs
+++ b/contracts/cw-proposal-single/src/contract.rs
@@ -328,7 +328,6 @@ pub fn execute_vote(
         return Err(ContractError::NotRegistered {});
     }
 
-    let mut previous_ballot = None;
     BALLOTS.update(
         deps.storage,
         (proposal_id, info.sender.clone()),
@@ -341,7 +340,9 @@ pub fn execute_vote(
                         // behavior.
                         Err(ContractError::AlreadyCast {})
                     } else {
-                        previous_ballot = Some(current_ballot);
+                        // Remove the old vote if this is a re-vote.
+                        prop.votes
+                            .remove_vote(current_ballot.vote, current_ballot.power);
                         Ok(Ballot {
                             power: vote_power,
                             vote,
@@ -360,11 +361,6 @@ pub fn execute_vote(
 
     let old_status = prop.status;
 
-    // Remove the old vote if this is a re-vote.
-    if let Some(ballot) = previous_ballot {
-        prop.votes.remove_vote(ballot.vote, ballot.power)
-    }
-
     prop.votes.add_vote(vote, vote_power);
     prop.update_status(&env.block);
 
diff --git a/default-account.txt b/default-account.txt
index 49a0cd34..5119a576 100644
--- a/default-account.txt
+++ b/default-account.txt
@@ -8,4 +8,4 @@
 **Important** write this mnemonic phrase in a safe place.
 It is the only way to recover your account if you ever forget your password.
 
-siren window salt bullet cream letter huge satoshi fade shiver permit offer happy immense wage fitness goose usual aim hammer clap about super trend
\ No newline at end of file
+siren window salt bullet cream letter huge satoshi fade shiver permit offer happy immense wage fitness goose usual aim hammer clap about super trend
diff --git a/packages/voting/src/voting.rs b/packages/voting/src/voting.rs
index 23a4d126..3ae520d2 100644
--- a/packages/voting/src/voting.rs
+++ b/packages/voting/src/voting.rs
@@ -1,4 +1,4 @@
-use cosmwasm_std::{Addr, Decimal, Deps, StdResult, Uint128, Uint256};
+use cosmwasm_std::{Addr, Decimal, Deps, StdError, StdResult, Uint128, Uint256};
 use cw_core_interface::voting;
 use cw_utils::Duration;
 use schemars::JsonSchema;
@@ -55,8 +55,18 @@ impl MultipleChoiceVotes {
         self.vote_weights.iter().sum()
     }
 
-    pub fn add_vote(&mut self, vote: MultipleChoiceVote, weight: Uint128) {
-        self.vote_weights[vote.option_id as usize] += weight;
+    pub fn add_vote(&mut self, vote: MultipleChoiceVote, weight: Uint128) -> StdResult<()> {
+        self.vote_weights[vote.option_id as usize] = self.vote_weights[vote.option_id as usize]
+            .checked_add(weight)
+            .map_err(StdError::overflow)?;
+        Ok(())
+    }
+
+    pub fn remove_vote(&mut self, vote: MultipleChoiceVote, weight: Uint128) -> StdResult<()> {
+        self.vote_weights[vote.option_id as usize] = self.vote_weights[vote.option_id as usize]
+            .checked_sub(weight)
+            .map_err(StdError::overflow)?;
+        Ok(())
     }
 
     pub fn zero(num_choices: usize) -> Self {
diff --git a/types/contracts/cw-admin-factory/CwAdminFactoryContract.ts b/types/contracts/cw-admin-factory/CwAdminFactoryContract.ts
index d9e7f6d2..968393a7 100644
--- a/types/contracts/cw-admin-factory/CwAdminFactoryContract.ts
+++ b/types/contracts/cw-admin-factory/CwAdminFactoryContract.ts
@@ -1,7 +1,7 @@
 /**
-* This file was automatically generated by cosmwasm-typescript-gen@0.3.6.
+* This file was automatically generated by @cosmwasm/ts-codegen@0.5.8.
 * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
-* and run the cosmwasm-typescript-gen generate command to regenerate this file.
+* and run the @cosmwasm/ts-codegen generate command to regenerate this file.
 */
 
 import { CosmWasmClient, ExecuteResult, SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate";
diff --git a/types/contracts/cw-core/CwCoreContract.ts b/types/contracts/cw-core/CwCoreContract.ts
index d8a559a8..95be36ba 100644
--- a/types/contracts/cw-core/CwCoreContract.ts
+++ b/types/contracts/cw-core/CwCoreContract.ts
@@ -1,7 +1,7 @@
 /**
-* This file was automatically generated by cosmwasm-typescript-gen@0.3.6.
+* This file was automatically generated by @cosmwasm/ts-codegen@0.5.8.
 * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
-* and run the cosmwasm-typescript-gen generate command to regenerate this file.
+* and run the @cosmwasm/ts-codegen generate command to regenerate this file.
 */
 
 import { CosmWasmClient, ExecuteResult, SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate";
diff --git a/types/contracts/cw-named-groups/CwNamedGroupsContract.ts b/types/contracts/cw-named-groups/CwNamedGroupsContract.ts
index b8fa93e9..9619f1ff 100644
--- a/types/contracts/cw-named-groups/CwNamedGroupsContract.ts
+++ b/types/contracts/cw-named-groups/CwNamedGroupsContract.ts
@@ -1,7 +1,7 @@
 /**
-* This file was automatically generated by cosmwasm-typescript-gen@0.3.6.
+* This file was automatically generated by @cosmwasm/ts-codegen@0.5.8.
 * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
-* and run the cosmwasm-typescript-gen generate command to regenerate this file.
+* and run the @cosmwasm/ts-codegen generate command to regenerate this file.
 */
 
 import { CosmWasmClient, ExecuteResult, SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate";
diff --git a/types/contracts/cw-names-registry/CwNamesRegistryContract.ts b/types/contracts/cw-names-registry/CwNamesRegistryContract.ts
index 78474733..3dac4b6f 100644
--- a/types/contracts/cw-names-registry/CwNamesRegistryContract.ts
+++ b/types/contracts/cw-names-registry/CwNamesRegistryContract.ts
@@ -1,7 +1,7 @@
 /**
-* This file was automatically generated by cosmwasm-typescript-gen@0.3.6.
+* This file was automatically generated by @cosmwasm/ts-codegen@0.5.8.
 * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
-* and run the cosmwasm-typescript-gen generate command to regenerate this file.
+* and run the @cosmwasm/ts-codegen generate command to regenerate this file.
 */
 
 import { CosmWasmClient, ExecuteResult, SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate";
diff --git a/types/contracts/cw-native-staked-balance-voting/CwNativeStakedBalanceVotingContract.ts b/types/contracts/cw-native-staked-balance-voting/CwNativeStakedBalanceVotingContract.ts
index d564c4aa..e1772ce9 100644
--- a/types/contracts/cw-native-staked-balance-voting/CwNativeStakedBalanceVotingContract.ts
+++ b/types/contracts/cw-native-staked-balance-voting/CwNativeStakedBalanceVotingContract.ts
@@ -1,7 +1,7 @@
 /**
-* This file was automatically generated by cosmwasm-typescript-gen@0.3.6.
+* This file was automatically generated by @cosmwasm/ts-codegen@0.5.8.
 * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
-* and run the cosmwasm-typescript-gen generate command to regenerate this file.
+* and run the @cosmwasm/ts-codegen generate command to regenerate this file.
 */
 
 import { CosmWasmClient, ExecuteResult, SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate";
diff --git a/types/contracts/cw-proposal-multiple/CwProposalMultipleContract.ts b/types/contracts/cw-proposal-multiple/CwProposalMultipleContract.ts
index 253f7567..c79cabfd 100644
--- a/types/contracts/cw-proposal-multiple/CwProposalMultipleContract.ts
+++ b/types/contracts/cw-proposal-multiple/CwProposalMultipleContract.ts
@@ -1,7 +1,7 @@
 /**
-* This file was automatically generated by cosmwasm-typescript-gen@0.3.6.
+* This file was automatically generated by @cosmwasm/ts-codegen@0.5.8.
 * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
-* and run the cosmwasm-typescript-gen generate command to regenerate this file.
+* and run the @cosmwasm/ts-codegen generate command to regenerate this file.
 */
 
 import { CosmWasmClient, ExecuteResult, SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate";
@@ -28,6 +28,7 @@ export type PercentageThreshold = {
 };
 export type Decimal = string;
 export interface ConfigResponse {
+  allow_revoting: boolean;
   close_proposal_on_execution_failure: boolean;
   dao: Addr;
   deposit_info?: CheckedDepositInfo | null;
@@ -68,6 +69,7 @@ export type ExecuteMsg = {
   };
 } | {
   update_config: {
+    allow_revoting: boolean;
     close_proposal_on_execution_failure: boolean;
     dao: string;
     deposit_info?: DepositInfo | null;
@@ -297,6 +299,7 @@ export interface ContractVersion {
   [k: string]: unknown;
 }
 export interface InstantiateMsg {
+  allow_revoting: boolean;
   close_proposal_on_execution_failure: boolean;
   deposit_info?: DepositInfo | null;
   max_voting_period: Duration;
@@ -326,6 +329,7 @@ export interface ProposalResponse {
   [k: string]: unknown;
 }
 export interface MultipleChoiceProposal {
+  allow_revoting: boolean;
   choices: CheckedMultipleChoiceOption[];
   deposit_info?: CheckedDepositInfo | null;
   description: string;
@@ -615,6 +619,7 @@ export interface CwProposalMultipleInterface extends CwProposalMultipleReadOnlyI
     proposalId: number;
   }, fee?: number | StdFee | "auto", memo?: string, funds?: readonly Coin[]) => Promise<ExecuteResult>;
   updateConfig: ({
+    allowRevoting,
     closeProposalOnExecutionFailure,
     dao,
     depositInfo,
@@ -623,6 +628,7 @@ export interface CwProposalMultipleInterface extends CwProposalMultipleReadOnlyI
     onlyMembersExecute,
     votingStrategy
   }: {
+    allowRevoting: boolean;
     closeProposalOnExecutionFailure: boolean;
     dao: string;
     depositInfo?: DepositInfo;
@@ -727,6 +733,7 @@ export class CwProposalMultipleClient extends CwProposalMultipleQueryClient impl
     }, fee, memo, funds);
   };
   updateConfig = async ({
+    allowRevoting,
     closeProposalOnExecutionFailure,
     dao,
     depositInfo,
@@ -735,6 +742,7 @@ export class CwProposalMultipleClient extends CwProposalMultipleQueryClient impl
     onlyMembersExecute,
     votingStrategy
   }: {
+    allowRevoting: boolean;
     closeProposalOnExecutionFailure: boolean;
     dao: string;
     depositInfo?: DepositInfo;
@@ -745,6 +753,7 @@ export class CwProposalMultipleClient extends CwProposalMultipleQueryClient impl
   }, fee: number | StdFee | "auto" = "auto", memo?: string, funds?: readonly Coin[]): Promise<ExecuteResult> => {
     return await this.client.execute(this.sender, this.contractAddress, {
       update_config: {
+        allow_revoting: allowRevoting,
         close_proposal_on_execution_failure: closeProposalOnExecutionFailure,
         dao,
         deposit_info: depositInfo,
diff --git a/types/contracts/cw-proposal-single/CwProposalSingleContract.ts b/types/contracts/cw-proposal-single/CwProposalSingleContract.ts
index 5bef50a6..a3fbc7dd 100644
--- a/types/contracts/cw-proposal-single/CwProposalSingleContract.ts
+++ b/types/contracts/cw-proposal-single/CwProposalSingleContract.ts
@@ -1,7 +1,7 @@
 /**
-* This file was automatically generated by cosmwasm-typescript-gen@0.3.6.
+* This file was automatically generated by @cosmwasm/ts-codegen@0.5.8.
 * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
-* and run the cosmwasm-typescript-gen generate command to regenerate this file.
+* and run the @cosmwasm/ts-codegen generate command to regenerate this file.
 */
 
 import { CosmWasmClient, ExecuteResult, SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate";
diff --git a/types/contracts/cw-token-swap/CwTokenSwapContract.ts b/types/contracts/cw-token-swap/CwTokenSwapContract.ts
index 5de4cf52..7b191431 100644
--- a/types/contracts/cw-token-swap/CwTokenSwapContract.ts
+++ b/types/contracts/cw-token-swap/CwTokenSwapContract.ts
@@ -1,7 +1,7 @@
 /**
-* This file was automatically generated by cosmwasm-typescript-gen@0.3.6.
+* This file was automatically generated by @cosmwasm/ts-codegen@0.5.8.
 * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
-* and run the cosmwasm-typescript-gen generate command to regenerate this file.
+* and run the @cosmwasm/ts-codegen generate command to regenerate this file.
 */
 
 import { CosmWasmClient, ExecuteResult, SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate";
diff --git a/types/contracts/cw20-stake-external-rewards/Cw20StakeExternalRewardsContract.ts b/types/contracts/cw20-stake-external-rewards/Cw20StakeExternalRewardsContract.ts
index fae0ea2d..d4b7386b 100644
--- a/types/contracts/cw20-stake-external-rewards/Cw20StakeExternalRewardsContract.ts
+++ b/types/contracts/cw20-stake-external-rewards/Cw20StakeExternalRewardsContract.ts
@@ -1,7 +1,7 @@
 /**
-* This file was automatically generated by cosmwasm-typescript-gen@0.3.6.
+* This file was automatically generated by @cosmwasm/ts-codegen@0.5.8.
 * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
-* and run the cosmwasm-typescript-gen generate command to regenerate this file.
+* and run the @cosmwasm/ts-codegen generate command to regenerate this file.
 */
 
 import { CosmWasmClient, ExecuteResult, SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate";
diff --git a/types/contracts/cw20-stake-reward-distributor/Cw20StakeRewardDistributorContract.ts b/types/contracts/cw20-stake-reward-distributor/Cw20StakeRewardDistributorContract.ts
index e0830851..a09d5b5e 100644
--- a/types/contracts/cw20-stake-reward-distributor/Cw20StakeRewardDistributorContract.ts
+++ b/types/contracts/cw20-stake-reward-distributor/Cw20StakeRewardDistributorContract.ts
@@ -1,7 +1,7 @@
 /**
-* This file was automatically generated by cosmwasm-typescript-gen@0.3.6.
+* This file was automatically generated by @cosmwasm/ts-codegen@0.5.8.
 * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
-* and run the cosmwasm-typescript-gen generate command to regenerate this file.
+* and run the @cosmwasm/ts-codegen generate command to regenerate this file.
 */
 
 import { CosmWasmClient, ExecuteResult, SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate";
diff --git a/types/contracts/cw20-stake/Cw20StakeContract.ts b/types/contracts/cw20-stake/Cw20StakeContract.ts
index db691bd4..0c00f282 100644
--- a/types/contracts/cw20-stake/Cw20StakeContract.ts
+++ b/types/contracts/cw20-stake/Cw20StakeContract.ts
@@ -1,7 +1,7 @@
 /**
-* This file was automatically generated by cosmwasm-typescript-gen@0.3.6.
+* This file was automatically generated by @cosmwasm/ts-codegen@0.5.8.
 * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
-* and run the cosmwasm-typescript-gen generate command to regenerate this file.
+* and run the @cosmwasm/ts-codegen generate command to regenerate this file.
 */
 
 import { CosmWasmClient, ExecuteResult, SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate";
diff --git a/types/contracts/cw20-staked-balance-voting/Cw20StakedBalanceVotingContract.ts b/types/contracts/cw20-staked-balance-voting/Cw20StakedBalanceVotingContract.ts
index f9a2ae94..bd06ce34 100644
--- a/types/contracts/cw20-staked-balance-voting/Cw20StakedBalanceVotingContract.ts
+++ b/types/contracts/cw20-staked-balance-voting/Cw20StakedBalanceVotingContract.ts
@@ -1,7 +1,7 @@
 /**
-* This file was automatically generated by cosmwasm-typescript-gen@0.3.6.
+* This file was automatically generated by @cosmwasm/ts-codegen@0.5.8.
 * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
-* and run the cosmwasm-typescript-gen generate command to regenerate this file.
+* and run the @cosmwasm/ts-codegen generate command to regenerate this file.
 */
 
 import { CosmWasmClient, ExecuteResult, SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate";
diff --git a/types/contracts/cw4-voting/Cw4VotingContract.ts b/types/contracts/cw4-voting/Cw4VotingContract.ts
index 6bb5b9f2..4d669e12 100644
--- a/types/contracts/cw4-voting/Cw4VotingContract.ts
+++ b/types/contracts/cw4-voting/Cw4VotingContract.ts
@@ -1,7 +1,7 @@
 /**
-* This file was automatically generated by cosmwasm-typescript-gen@0.3.6.
+* This file was automatically generated by @cosmwasm/ts-codegen@0.5.8.
 * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
-* and run the cosmwasm-typescript-gen generate command to regenerate this file.
+* and run the @cosmwasm/ts-codegen generate command to regenerate this file.
 */
 
 import { CosmWasmClient, ExecuteResult, SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate";
diff --git a/types/contracts/cw721-stake/Cw721StakeContract.ts b/types/contracts/cw721-stake/Cw721StakeContract.ts
index f9c43a39..10d4ece1 100644
--- a/types/contracts/cw721-stake/Cw721StakeContract.ts
+++ b/types/contracts/cw721-stake/Cw721StakeContract.ts
@@ -1,7 +1,7 @@
 /**
-* This file was automatically generated by cosmwasm-typescript-gen@0.3.6.
+* This file was automatically generated by @cosmwasm/ts-codegen@0.5.8.
 * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
-* and run the cosmwasm-typescript-gen generate command to regenerate this file.
+* and run the @cosmwasm/ts-codegen generate command to regenerate this file.
 */
 
 import { CosmWasmClient, ExecuteResult, SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate";
diff --git a/types/package.json b/types/package.json
index 03454fcf..b0172f9e 100644
--- a/types/package.json
+++ b/types/package.json
@@ -18,7 +18,7 @@
   },
   "devDependencies": {
     "@types/node": "^15.14.9",
-    "cosmwasm-typescript-gen": "0.3.6",
+    "@cosmwasm/ts-codegen": "0.5.8",
     "husky": "^7.0.4",
     "prettier": "^2.5.0",
     "pretty-quick": "^3.1.2",
@@ -35,4 +35,4 @@
     "@cosmjs/tendermint-rpc": "^0.28.4",
     "dotenv": "^10.0.0"
   }
-}
+}
\ No newline at end of file
diff --git a/types/packages/cw-core-interface/CwCoreInterfaceContract.ts b/types/packages/cw-core-interface/CwCoreInterfaceContract.ts
index 334acf01..1e8e31bf 100644
--- a/types/packages/cw-core-interface/CwCoreInterfaceContract.ts
+++ b/types/packages/cw-core-interface/CwCoreInterfaceContract.ts
@@ -1,7 +1,7 @@
 /**
-* This file was automatically generated by cosmwasm-typescript-gen@0.3.6.
+* This file was automatically generated by @cosmwasm/ts-codegen@0.5.8.
 * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
-* and run the cosmwasm-typescript-gen generate command to regenerate this file.
+* and run the @cosmwasm/ts-codegen generate command to regenerate this file.
 */
 
 import { CosmWasmClient, ExecuteResult, SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate";
diff --git a/types/packages/cw-core-macros/CwCoreMacrosContract.ts b/types/packages/cw-core-macros/CwCoreMacrosContract.ts
index b86e8487..4478f147 100644
--- a/types/packages/cw-core-macros/CwCoreMacrosContract.ts
+++ b/types/packages/cw-core-macros/CwCoreMacrosContract.ts
@@ -1,7 +1,7 @@
 /**
-* This file was automatically generated by cosmwasm-typescript-gen@0.3.6.
+* This file was automatically generated by @cosmwasm/ts-codegen@0.5.8.
 * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
-* and run the cosmwasm-typescript-gen generate command to regenerate this file.
+* and run the @cosmwasm/ts-codegen generate command to regenerate this file.
 */
 
 import { CosmWasmClient, ExecuteResult, SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate";
diff --git a/types/src/codegen.ts b/types/src/codegen.ts
index cf43dec2..9105f1fa 100644
--- a/types/src/codegen.ts
+++ b/types/src/codegen.ts
@@ -1,7 +1,7 @@
-import { exec } from "child_process";
 import dotenv from "dotenv";
 import fs from "fs";
 import path from "path";
+import { readSchemas, generate } from "@cosmwasm/ts-codegen";
 
 enum OutputType {
   contracts = "contracts",
@@ -64,29 +64,12 @@ const DEFAULT_CONFIG = {
   ]
 };
 
-async function run(cmd: string): Promise<boolean> {
-  log(cmd, LogLevels.Verbose);
-  return new Promise((resolve, reject) => {
-    exec(cmd, (error, stdout, stderr) => {
-      if (error) {
-        console.error(`error: ${error.message}`);
-        reject(error);
-      }
-      if (stderr) {
-        console.error(`stderr: ${stderr}`);
-        reject(stderr);
-      }
-      resolve(true);
-    });
-  });
-}
+async function generateTs(spec: CompilationSpec): Promise<void> {
+  const out = `${spec.outputPath}/${spec.outputType}/${spec.contractName}`;
+  const name = spec.contractName;
 
-async function generateTs(spec: CompilationSpec): Promise<boolean> {
-  const script = "node_modules/cosmwasm-typescript-gen/main/cosmwasm-typescript-gen.js";
-  return run(`${script} generate \
-    --schema ${spec.schemaDir} \
-    --out ${spec.outputPath}/${spec.outputType}/${spec.contractName} \
-    --name ${spec.contractName}`)
+  const schemas = readSchemas({ schemaDir: spec.schemaDir, argv: { packed: false } });
+  return await generate(name, schemas, out);
 }
 
 
diff --git a/types/yarn.lock b/types/yarn.lock
index dbd120db..f58bd762 100644
--- a/types/yarn.lock
+++ b/types/yarn.lock
@@ -1158,6 +1158,35 @@
   resolved "https://registry.yarnpkg.com/@cosmjs/utils/-/utils-0.28.10.tgz#8cb8cb3381210bb498aa766ac26c9ec369a7bfbb"
   integrity sha512-S0t2kFRiAo1MvIrbXlfrGdsn2D3PApyO1d+GCze14Cg4QOOGWDcTsd6VhHcxKhETO1Z9ofEhE4soJ8ZldBlnpg==
 
+"@cosmwasm/ts-codegen@0.5.8":
+  version "0.5.8"
+  resolved "https://registry.yarnpkg.com/@cosmwasm/ts-codegen/-/ts-codegen-0.5.8.tgz#8c7efc91bdbb85637342329daa86342082f3b895"
+  integrity sha512-zd1u8YAghunLhkZAmQvoQIbvrnCW62zs0IZoP3VN23IQWVedycjQJxhoSNKGKdfyir693hSsSMAt3NC/1PrAQQ==
+  dependencies:
+    "@babel/core" "7.18.5"
+    "@babel/generator" "7.18.2"
+    "@babel/parser" "7.18.5"
+    "@babel/plugin-proposal-class-properties" "7.17.12"
+    "@babel/plugin-proposal-export-default-from" "7.17.12"
+    "@babel/plugin-proposal-object-rest-spread" "7.18.0"
+    "@babel/plugin-transform-runtime" "7.18.5"
+    "@babel/preset-env" "7.18.2"
+    "@babel/preset-typescript" "^7.17.12"
+    "@babel/runtime" "^7.18.3"
+    "@babel/traverse" "7.18.5"
+    "@babel/types" "7.18.4"
+    case "1.6.3"
+    dargs "7.0.0"
+    fuzzy "0.1.3"
+    glob "8.0.3"
+    inquirerer "0.1.3"
+    json-schema-to-typescript "10.1.5"
+    long "^5.2.0"
+    minimist "1.2.6"
+    mkdirp "1.0.4"
+    shelljs "0.8.5"
+    wasm-ast-types "^0.4.3"
+
 "@cspotcode/source-map-support@^0.8.0":
   version "0.8.1"
   resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1"
@@ -1804,35 +1833,6 @@ cosmjs-types@^0.4.0:
     long "^4.0.0"
     protobufjs "~6.11.2"
 
-cosmwasm-typescript-gen@0.3.6:
-  version "0.3.6"
-  resolved "https://registry.yarnpkg.com/cosmwasm-typescript-gen/-/cosmwasm-typescript-gen-0.3.6.tgz#c863ce01335f53dd02c8ebc2c545be50828dfc3f"
-  integrity sha512-A+VfD9a59Y4LKhuuH2zN2Qdz5Re4P4qkf3DPMsCUaLiCzcdKSxj2QQCpRXsA4lXkJXbuXPop4WTpYGVX01xpjg==
-  dependencies:
-    "@babel/core" "7.18.5"
-    "@babel/generator" "7.18.2"
-    "@babel/parser" "7.18.5"
-    "@babel/plugin-proposal-class-properties" "7.17.12"
-    "@babel/plugin-proposal-export-default-from" "7.17.12"
-    "@babel/plugin-proposal-object-rest-spread" "7.18.0"
-    "@babel/plugin-transform-runtime" "7.18.5"
-    "@babel/preset-env" "7.18.2"
-    "@babel/preset-typescript" "^7.17.12"
-    "@babel/runtime" "^7.18.3"
-    "@babel/traverse" "7.18.5"
-    "@babel/types" "7.18.4"
-    case "1.6.3"
-    dargs "7.0.0"
-    fuzzy "0.1.3"
-    glob "8.0.3"
-    inquirerer "0.1.3"
-    json-schema-to-typescript "10.1.5"
-    long "^5.2.0"
-    minimist "1.2.6"
-    mkdirp "1.0.4"
-    shelljs "0.8.5"
-    wasm-ast-types "^0.3.5"
-
 create-require@^1.1.0:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333"
@@ -3466,10 +3466,15 @@ util@^0.10.3:
   dependencies:
     inherits "2.0.3"
 
-wasm-ast-types@^0.3.5:
-  version "0.3.5"
-  resolved "https://registry.yarnpkg.com/wasm-ast-types/-/wasm-ast-types-0.3.5.tgz#b46d7b5f4f9312f45278f731f9eb496fdf2f4ca3"
-  integrity sha512-rzpP86SqVV3Rs24hG0ZSsFFrxp34J4U65z/3KWLZOxhe6ljuh3FFECbC6p0iyIkmG6hN5F6mQ+4Y2+QNP5GnAg==
+v8-compile-cache-lib@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf"
+  integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==
+
+wasm-ast-types@^0.4.3:
+  version "0.4.3"
+  resolved "https://registry.yarnpkg.com/wasm-ast-types/-/wasm-ast-types-0.4.3.tgz#bfc6de5ba1c8cb5aed8d41503904372490fbccb0"
+  integrity sha512-AbQe1uwzwyev3VR93coP3hAAIGqZ0ccNeyBHYtUFckHtfrsLwDF5wf3AMmnZ/PmQpxvC8PE/1aiYG8twcg9aGQ==
   dependencies:
     "@babel/runtime" "^7.18.3"
     "@babel/types" "7.18.4"


Test patch:
diff --git a/.github/workflows/integration_tests.yml b/.github/workflows/integration_tests.yml
new file mode 100644
index 00000000..8a9f3d46
--- /dev/null
+++ b/.github/workflows/integration_tests.yml
@@ -0,0 +1,111 @@
+name: Integration Tests
+
+on:
+  pull_request:
+  push:
+    branches:
+      - main
+
+jobs:
+  compile-contracts:
+    name: Compile contracts using optimized rust compiler
+    runs-on: ubuntu-latest
+    container:
+      image: cosmwasm/workspace-optimizer:0.12.6
+    steps:
+      - name: Checkout sources
+        uses: actions/checkout@v3
+      - name: Install tar for cache upload
+        run: apk add --no-cache tar
+      - name: Rust Dependencies Cache
+        uses: actions/cache@v3
+        with:
+          path: |
+            ~/.cargo/bin/
+            ~/.cargo/registry/index/
+            ~/.cargo/registry/cache/
+            ~/.cargo/git/db/
+            target/
+          key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
+      - name: Optimize
+        run: optimize_workspace.sh
+      - name: Upload contracts
+        uses: actions/upload-artifact@v3
+        with:
+          name: dao-dao-contract-artifacts-${{ github.event.number }}
+          path: artifacts
+          retention-days: 1
+
+  test:
+    name: Run integration tests against local juno node
+    runs-on: ubuntu-latest
+    needs: compile-contracts
+    env:
+      ADMIN_ADDR: juno10j9gpw9t4jsz47qgnkvl5n3zlm2fz72k67rxsg
+      GAS_OUT_DIR: gas_reports
+      CONFIG: configs/ci.yaml
+      CONTRACT_DIR: artifacts
+    steps:
+      - name: Checkout sources
+        uses: actions/checkout@v3
+
+      - name: Run Local Juno Node
+        run: |
+          docker run --rm -d --name juno \
+            -e PASSWORD=xxxxxxxxx \
+            -e STAKE_TOKEN=ujunox \
+            -e GAS_LIMIT=100000000 \
+            -e MAX_BYTES=22020096 \
+            -e UNSAFE_CORS=true \
+            -p 1317:1317 \
+            -p 26656:26656 \
+            -p 26657:26657 \
+            ghcr.io/cosmoscontracts/juno:v9.0.0 /opt/setup_and_run.sh $ADMIN_ADDR
+
+      - name: Let Juno catch up
+        run: sleep 30
+
+      - name: Restore optimized contracts
+        uses: actions/download-artifact@v3
+        with:
+          name: dao-dao-contract-artifacts-${{ github.event.number }}
+          path: ci/integration_tests/artifacts
+
+      - name: Download smart contract dependencies
+        run: |
+          wget https://github.com/CosmWasm/cw-plus/releases/download/v0.11.1/cw20_base.wasm \
+            -O ci/integration_tests/artifacts/cw20_base.wasm
+          wget https://github.com/CosmWasm/cw-plus/releases/download/v0.11.1/cw4_group.wasm \
+            -O ci/integration_tests/artifacts/cw4_group.wasm
+
+      - name: Rust Dependencies Cache
+        uses: actions/cache@v3
+        with:
+          path: |
+            ~/.cargo/bin/
+            ~/.cargo/registry/index/
+            ~/.cargo/registry/cache/
+            ~/.cargo/git/db/
+            target/
+          key: ${{ runner.os }}-cargo-integration-${{ hashFiles('ci/integration_tests/Cargo.lock') }}
+
+      - name: Run Integration Tests
+        run: cd ci/integration_tests/ && cargo t
+
+      - name: Combine Test Gas Reports
+        run: cd ci/integration_tests/ && jq -rs 'reduce .[] as $item ({}; . * $item)' gas_reports/*.json > gas_report.json
+
+      - name: Raw Gas Report
+        run: cat ci/integration_tests/gas_report.json
+
+      - name: Set GIT_BRANCH
+        run: echo "GIT_BRANCH=$(echo ${{ github.ref }} | sed 's|/|-|g')" >> $GITHUB_ENV
+
+      - name: Upload Gas Report
+        uses: actions/upload-artifact@v3
+        with:
+          name: dao-dao-gas-report-${{ env.GIT_BRANCH }}
+          path: ci/integration_tests/gas_report.json
+          retention-days: 90
+
+      # TODO: Download main's gas_report artifact and diff
diff --git a/ci/integration_tests/.cargo/config b/ci/integration_tests/.cargo/config
new file mode 100644
index 00000000..2c55b254
--- /dev/null
+++ b/ci/integration_tests/.cargo/config
@@ -0,0 +1,3 @@
+[alias]
+c = "check --tests"
+t = "test -- --test-threads 1"
\ No newline at end of file
diff --git a/ci/integration_tests/Cargo.lock b/ci/integration_tests/Cargo.lock
new file mode 100644
index 00000000..caa29a35
--- /dev/null
+++ b/ci/integration_tests/Cargo.lock
@@ -0,0 +1,1577 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "addr2line"
+version = "0.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b"
+dependencies = [
+ "gimli",
+]
+
+[[package]]
+name = "adler"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+
+[[package]]
+name = "ahash"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
+dependencies = [
+ "getrandom 0.2.7",
+ "once_cell",
+ "version_check",
+]
+
+[[package]]
+name = "aho-corasick"
+version = "0.7.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.58"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb07d2053ccdbe10e2af2995a2f116c1330396493dc1269f6a91d0ae82e19704"
+
+[[package]]
+name = "async-trait"
+version = "0.1.56"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96cf8829f67d2eab0b2dfa42c5d0ef737e0724e4a82b01b3e292456202b19716"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "atty"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
+dependencies = [
+ "hermit-abi",
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "backtrace"
+version = "0.3.66"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7"
+dependencies = [
+ "addr2line",
+ "cc",
+ "cfg-if",
+ "libc",
+ "miniz_oxide",
+ "object",
+ "rustc-demangle",
+]
+
+[[package]]
+name = "base16ct"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce"
+
+[[package]]
+name = "base64"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
+
+[[package]]
+name = "base64ct"
+version = "1.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3bdca834647821e0b13d9539a8634eb62d3501b6b6c2cec1722786ee6671b851"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "block-buffer"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
+dependencies = [
+ "block-padding",
+ "byte-tools",
+ "byteorder",
+ "generic-array 0.12.4",
+]
+
+[[package]]
+name = "block-buffer"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
+dependencies = [
+ "generic-array 0.14.5",
+]
+
+[[package]]
+name = "block-padding"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5"
+dependencies = [
+ "byte-tools",
+]
+
+[[package]]
+name = "byte-tools"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
+
+[[package]]
+name = "byteorder"
+version = "1.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
+
+[[package]]
+name = "cc"
+version = "1.0.73"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "config"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3ea917b74b6edfb5024e3b55d3c8f710b5f4ed92646429601a42e96f0812b31b"
+dependencies = [
+ "async-trait",
+ "json5",
+ "lazy_static",
+ "nom",
+ "pathdiff",
+ "ron",
+ "rust-ini",
+ "serde",
+ "serde_json",
+ "toml",
+ "yaml-rust",
+]
+
+[[package]]
+name = "const-oid"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3"
+
+[[package]]
+name = "cosm-orc"
+version = "0.5.8"
+dependencies = [
+ "anyhow",
+ "backtrace",
+ "config",
+ "log",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "cosmwasm-crypto"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5eb0afef2325df81aadbf9be1233f522ed8f6e91df870c764bc44cca2b1415bd"
+dependencies = [
+ "digest 0.9.0",
+ "ed25519-zebra",
+ "k256",
+ "rand_core 0.6.3",
+ "thiserror",
+]
+
+[[package]]
+name = "cosmwasm-derive"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4b36e527620a2a3e00e46b6e731ab6c9b68d11069c986f7d7be8eba79ef081a4"
+dependencies = [
+ "syn",
+]
+
+[[package]]
+name = "cosmwasm-std"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "875994993c2082a6fcd406937bf0fca21c349e4a624f3810253a14fa83a3a195"
+dependencies = [
+ "base64",
+ "cosmwasm-crypto",
+ "cosmwasm-derive",
+ "forward_ref",
+ "schemars",
+ "serde",
+ "serde-json-wasm",
+ "thiserror",
+ "uint",
+]
+
+[[package]]
+name = "cosmwasm-storage"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d18403b07304d15d304dad11040d45bbcaf78d603b4be3fb5e2685c16f9229b5"
+dependencies = [
+ "cosmwasm-std",
+ "serde",
+]
+
+[[package]]
+name = "cpufeatures"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "crunchy"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
+
+[[package]]
+name = "crypto-bigint"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03c6a1d5fa1de37e071642dfa44ec552ca5b299adb128fab16138e24b548fd21"
+dependencies = [
+ "generic-array 0.14.5",
+ "rand_core 0.6.3",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "crypto-mac"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714"
+dependencies = [
+ "generic-array 0.14.5",
+ "subtle",
+]
+
+[[package]]
+name = "ctor"
+version = "0.1.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f877be4f7c9f246b183111634f75baa039715e3f46ce860677d3b19a69fb229c"
+dependencies = [
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "curve25519-dalek"
+version = "3.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90f9d052967f590a76e62eb387bd0bbb1b000182c3cefe5364db6b7211651bc0"
+dependencies = [
+ "byteorder",
+ "digest 0.9.0",
+ "rand_core 0.5.1",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "cw-controllers"
+version = "0.13.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4f0bc6019b4d3d81e11f5c384bcce7173e2210bd654d75c6c9668e12cca05dfa"
+dependencies = [
+ "cosmwasm-std",
+ "cw-storage-plus 0.13.4",
+ "cw-utils 0.13.4",
+ "schemars",
+ "serde",
+ "thiserror",
+]
+
+[[package]]
+name = "cw-core"
+version = "0.1.0"
+dependencies = [
+ "cosmwasm-std",
+ "cosmwasm-storage",
+ "cw-core-interface",
+ "cw-core-macros",
+ "cw-paginate",
+ "cw-storage-plus 0.13.4",
+ "cw-utils 0.13.4",
+ "cw2",
+ "cw20 0.13.4",
+ "cw721",
+ "schemars",
+ "serde",
+ "thiserror",
+]
+
+[[package]]
+name = "cw-core-interface"
+version = "0.1.0"
+dependencies = [
+ "cosmwasm-std",
+ "cw-core-macros",
+ "cw2",
+ "schemars",
+ "serde",
+]
+
+[[package]]
+name = "cw-core-macros"
+version = "0.1.0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "cw-paginate"
+version = "0.1.0"
+dependencies = [
+ "cosmwasm-std",
+ "cosmwasm-storage",
+ "cw-storage-plus 0.13.4",
+ "serde",
+]
+
+[[package]]
+name = "cw-proposal-single"
+version = "0.1.0"
+dependencies = [
+ "cosmwasm-std",
+ "cosmwasm-storage",
+ "cw-core",
+ "cw-core-interface",
+ "cw-core-macros",
+ "cw-storage-plus 0.13.4",
+ "cw-utils 0.13.4",
+ "cw2",
+ "cw20 0.13.4",
+ "cw3",
+ "indexable-hooks",
+ "proposal-hooks",
+ "schemars",
+ "serde",
+ "thiserror",
+ "vote-hooks",
+ "voting",
+]
+
+[[package]]
+name = "cw-storage-plus"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c087ff98fb0475db4c2b5298a5fd12b2848d2854b39d1115d930ee6da24d1eed"
+dependencies = [
+ "cosmwasm-std",
+ "schemars",
+ "serde",
+]
+
+[[package]]
+name = "cw-storage-plus"
+version = "0.13.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "648b1507290bbc03a8d88463d7cd9b04b1fa0155e5eef366c4fa052b9caaac7a"
+dependencies = [
+ "cosmwasm-std",
+ "schemars",
+ "serde",
+]
+
+[[package]]
+name = "cw-utils"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3396c7aff5a0e3fb6dcc6cc89f56862c1d212b40d93ed725a6962955b1887ff"
+dependencies = [
+ "cosmwasm-std",
+ "schemars",
+ "serde",
+ "thiserror",
+]
+
+[[package]]
+name = "cw-utils"
+version = "0.13.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9dbaecb78c8e8abfd6b4258c7f4fbeb5c49a5e45ee4d910d3240ee8e1d714e1b"
+dependencies = [
+ "cosmwasm-std",
+ "schemars",
+ "serde",
+ "thiserror",
+]
+
+[[package]]
+name = "cw2"
+version = "0.13.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04cf4639517490dd36b333bbd6c4fbd92e325fd0acf4683b41753bc5eb63bfc1"
+dependencies = [
+ "cosmwasm-std",
+ "cw-storage-plus 0.13.4",
+ "schemars",
+ "serde",
+]
+
+[[package]]
+name = "cw20"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "413911037f6b0106ae37ca82352f9131939ccc995aade435df47a5baf2c815f2"
+dependencies = [
+ "cosmwasm-std",
+ "cw-utils 0.12.1",
+ "schemars",
+ "serde",
+]
+
+[[package]]
+name = "cw20"
+version = "0.13.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4cb782b8f110819a4eb5dbbcfed25ffba49ec16bbe32b4ad8da50a5ce68fec05"
+dependencies = [
+ "cosmwasm-std",
+ "cw-utils 0.13.4",
+ "schemars",
+ "serde",
+]
+
+[[package]]
+name = "cw20-base"
+version = "0.13.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0306e606581f4fb45e82bcbb7f0333179ed53dd949c6523f01a99b4bfc1475a0"
+dependencies = [
+ "cosmwasm-std",
+ "cw-storage-plus 0.13.4",
+ "cw-utils 0.13.4",
+ "cw2",
+ "cw20 0.13.4",
+ "schemars",
+ "serde",
+ "thiserror",
+]
+
+[[package]]
+name = "cw20-stake"
+version = "0.2.6"
+dependencies = [
+ "cosmwasm-std",
+ "cosmwasm-storage",
+ "cw-controllers",
+ "cw-storage-plus 0.13.4",
+ "cw-utils 0.13.4",
+ "cw2",
+ "cw20 0.13.4",
+ "cw20-base",
+ "schemars",
+ "serde",
+ "thiserror",
+]
+
+[[package]]
+name = "cw20-staked-balance-voting"
+version = "0.1.0"
+dependencies = [
+ "cosmwasm-std",
+ "cosmwasm-storage",
+ "cw-core-interface",
+ "cw-core-macros",
+ "cw-storage-plus 0.13.4",
+ "cw-utils 0.13.4",
+ "cw2",
+ "cw20 0.13.4",
+ "cw20-base",
+ "cw20-stake",
+ "schemars",
+ "serde",
+ "thiserror",
+]
+
+[[package]]
+name = "cw3"
+version = "0.13.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe19462a7f644ba60c19d3443cb90d00c50d9b6b3b0a3a7fca93df8261af979b"
+dependencies = [
+ "cosmwasm-std",
+ "cw-utils 0.13.4",
+ "schemars",
+ "serde",
+]
+
+[[package]]
+name = "cw721"
+version = "0.13.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "035818368a74c07dd9ed5c5a93340199ba251530162010b9f34c3809e3b97df1"
+dependencies = [
+ "cosmwasm-std",
+ "cw-utils 0.13.4",
+ "schemars",
+ "serde",
+]
+
+[[package]]
+name = "der"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6919815d73839e7ad218de758883aae3a257ba6759ce7a9992501efbb53d705c"
+dependencies = [
+ "const-oid",
+]
+
+[[package]]
+name = "digest"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
+dependencies = [
+ "generic-array 0.12.4",
+]
+
+[[package]]
+name = "digest"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
+dependencies = [
+ "generic-array 0.14.5",
+]
+
+[[package]]
+name = "dlv-list"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257"
+
+[[package]]
+name = "dyn-clone"
+version = "1.0.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d07a982d1fb29db01e5a59b1918e03da4df7297eaeee7686ac45542fd4e59c8"
+
+[[package]]
+name = "ecdsa"
+version = "0.13.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0d69ae62e0ce582d56380743515fefaf1a8c70cec685d9677636d7e30ae9dc9"
+dependencies = [
+ "der",
+ "elliptic-curve",
+ "rfc6979",
+ "signature",
+]
+
+[[package]]
+name = "ed25519-zebra"
+version = "3.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "403ef3e961ab98f0ba902771d29f842058578bb1ce7e3c59dad5a6a93e784c69"
+dependencies = [
+ "curve25519-dalek",
+ "hex",
+ "rand_core 0.6.3",
+ "serde",
+ "sha2",
+ "thiserror",
+ "zeroize",
+]
+
+[[package]]
+name = "elliptic-curve"
+version = "0.11.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "25b477563c2bfed38a3b7a60964c49e058b2510ad3f12ba3483fd8f62c2306d6"
+dependencies = [
+ "base16ct",
+ "crypto-bigint",
+ "der",
+ "ff",
+ "generic-array 0.14.5",
+ "group",
+ "rand_core 0.6.3",
+ "sec1",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "env_logger"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3"
+dependencies = [
+ "atty",
+ "humantime",
+ "log",
+ "regex",
+ "termcolor",
+]
+
+[[package]]
+name = "fake-simd"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
+
+[[package]]
+name = "ff"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "131655483be284720a17d74ff97592b8e76576dc25563148601df2d7c9080924"
+dependencies = [
+ "rand_core 0.6.3",
+ "subtle",
+]
+
+[[package]]
+name = "forward_ref"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8cbd1169bd7b4a0a20d92b9af7a7e0422888bd38a6f5ec29c1fd8c1558a272e"
+
+[[package]]
+name = "futures"
+version = "0.3.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-io",
+ "futures-sink",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3"
+
+[[package]]
+name = "futures-executor"
+version = "0.3.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-io"
+version = "0.3.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b"
+
+[[package]]
+name = "futures-macro"
+version = "0.3.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "futures-sink"
+version = "0.3.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868"
+
+[[package]]
+name = "futures-task"
+version = "0.3.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a"
+
+[[package]]
+name = "futures-util"
+version = "0.3.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-macro",
+ "futures-sink",
+ "futures-task",
+ "memchr",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+]
+
+[[package]]
+name = "generic-array"
+version = "0.12.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd"
+dependencies = [
+ "typenum",
+]
+
+[[package]]
+name = "generic-array"
+version = "0.14.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803"
+dependencies = [
+ "typenum",
+ "version_check",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi 0.9.0+wasi-snapshot-preview1",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi 0.11.0+wasi-snapshot-preview1",
+]
+
+[[package]]
+name = "gimli"
+version = "0.26.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d"
+
+[[package]]
+name = "group"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc5ac374b108929de78460075f3dc439fa66df9d8fc77e8f12caa5165fcf0c89"
+dependencies = [
+ "ff",
+ "rand_core 0.6.3",
+ "subtle",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+dependencies = [
+ "ahash",
+]
+
+[[package]]
+name = "hermit-abi"
+version = "0.1.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "hex"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
+
+[[package]]
+name = "hmac"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b"
+dependencies = [
+ "crypto-mac",
+ "digest 0.9.0",
+]
+
+[[package]]
+name = "humantime"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
+
+[[package]]
+name = "indexable-hooks"
+version = "0.1.0"
+dependencies = [
+ "cosmwasm-std",
+ "cw-storage-plus 0.13.4",
+ "schemars",
+ "serde",
+ "thiserror",
+]
+
+[[package]]
+name = "integration_tests"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "cosm-orc",
+ "cosmwasm-std",
+ "ctor",
+ "cw-core",
+ "cw-core-interface",
+ "cw-proposal-single",
+ "cw-utils 0.13.4",
+ "cw20 0.13.4",
+ "cw20-base",
+ "cw20-stake",
+ "cw20-staked-balance-voting",
+ "env_logger",
+ "once_cell",
+ "rand",
+ "serde",
+ "serde_json",
+ "test-context",
+ "voting",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
+
+[[package]]
+name = "json5"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1"
+dependencies = [
+ "pest",
+ "pest_derive",
+ "serde",
+]
+
+[[package]]
+name = "k256"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19c3a5e0a0b8450278feda242592512e09f61c72e018b8cd5c859482802daf2d"
+dependencies = [
+ "cfg-if",
+ "ecdsa",
+ "elliptic-curve",
+ "sec1",
+ "sha2",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "libc"
+version = "0.2.126"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
+
+[[package]]
+name = "linked-hash-map"
+version = "0.5.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
+
+[[package]]
+name = "log"
+version = "0.4.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "maplit"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
+
+[[package]]
+name = "memchr"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+
+[[package]]
+name = "minimal-lexical"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+
+[[package]]
+name = "miniz_oxide"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc"
+dependencies = [
+ "adler",
+]
+
+[[package]]
+name = "nom"
+version = "7.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36"
+dependencies = [
+ "memchr",
+ "minimal-lexical",
+]
+
+[[package]]
+name = "object"
+version = "0.29.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1"
+
+[[package]]
+name = "opaque-debug"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
+
+[[package]]
+name = "opaque-debug"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
+
+[[package]]
+name = "ordered-multimap"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ccd746e37177e1711c20dd619a1620f34f5c8b569c53590a72dedd5344d8924a"
+dependencies = [
+ "dlv-list",
+ "hashbrown",
+]
+
+[[package]]
+name = "pathdiff"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd"
+
+[[package]]
+name = "pest"
+version = "2.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53"
+dependencies = [
+ "ucd-trie",
+]
+
+[[package]]
+name = "pest_derive"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0"
+dependencies = [
+ "pest",
+ "pest_generator",
+]
+
+[[package]]
+name = "pest_generator"
+version = "2.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55"
+dependencies = [
+ "pest",
+ "pest_meta",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "pest_meta"
+version = "2.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d"
+dependencies = [
+ "maplit",
+ "pest",
+ "sha-1",
+]
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "pkcs8"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7cabda3fb821068a9a4fab19a683eac3af12edf0f34b94a8be53c4972b8149d0"
+dependencies = [
+ "der",
+ "spki",
+ "zeroize",
+]
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.42"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c278e965f1d8cf32d6e0e96de3d3e79712178ae67986d9cf9151f51e95aac89b"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "proposal-hooks"
+version = "0.1.0"
+dependencies = [
+ "cosmwasm-std",
+ "indexable-hooks",
+ "schemars",
+ "serde",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "libc",
+ "rand_chacha",
+ "rand_core 0.6.3",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core 0.6.3",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
+dependencies = [
+ "getrandom 0.1.16",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
+dependencies = [
+ "getrandom 0.2.7",
+]
+
+[[package]]
+name = "regex"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
+
+[[package]]
+name = "rfc6979"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96ef608575f6392792f9ecf7890c00086591d29a83910939d430753f7c050525"
+dependencies = [
+ "crypto-bigint",
+ "hmac",
+ "zeroize",
+]
+
+[[package]]
+name = "ron"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "88073939a61e5b7680558e6be56b419e208420c2adb92be54921fa6b72283f1a"
+dependencies = [
+ "base64",
+ "bitflags",
+ "serde",
+]
+
+[[package]]
+name = "rust-ini"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6d5f2436026b4f6e79dc829837d467cc7e9a55ee40e750d716713540715a2df"
+dependencies = [
+ "cfg-if",
+ "ordered-multimap",
+]
+
+[[package]]
+name = "rustc-demangle"
+version = "0.1.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
+
+[[package]]
+name = "ryu"
+version = "1.0.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695"
+
+[[package]]
+name = "schemars"
+version = "0.8.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1847b767a3d62d95cbf3d8a9f0e421cf57a0d8aa4f411d4b16525afb0284d4ed"
+dependencies = [
+ "dyn-clone",
+ "schemars_derive",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "schemars_derive"
+version = "0.8.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af4d7e1b012cb3d9129567661a63755ea4b8a7386d339dc945ae187e403c6743"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "serde_derive_internals",
+ "syn",
+]
+
+[[package]]
+name = "sec1"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08da66b8b0965a5555b6bd6639e68ccba85e1e2506f5fbb089e93f8a04e1a2d1"
+dependencies = [
+ "der",
+ "generic-array 0.14.5",
+ "pkcs8",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "serde"
+version = "1.0.140"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc855a42c7967b7c369eb5860f7164ef1f6f81c20c7cc1141f2a604e18723b03"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde-json-wasm"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "479b4dbc401ca13ee8ce902851b834893251404c4f3c65370a49e047a6be09a5"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.140"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f2122636b9fe3b81f1cb25099fcf2d3f542cdb1d45940d56c713158884a05da"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_derive_internals"
+version = "0.26.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.82"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "sha-1"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df"
+dependencies = [
+ "block-buffer 0.7.3",
+ "digest 0.8.1",
+ "fake-simd",
+ "opaque-debug 0.2.3",
+]
+
+[[package]]
+name = "sha2"
+version = "0.9.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800"
+dependencies = [
+ "block-buffer 0.9.0",
+ "cfg-if",
+ "cpufeatures",
+ "digest 0.9.0",
+ "opaque-debug 0.3.0",
+]
+
+[[package]]
+name = "signature"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02658e48d89f2bec991f9a78e69cfa4c316f8d6a6c4ec12fae1aeb263d486788"
+dependencies = [
+ "digest 0.9.0",
+ "rand_core 0.6.3",
+]
+
+[[package]]
+name = "slab"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "spki"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44d01ac02a6ccf3e07db148d2be087da624fea0221a16152ed01f0496a6b0a27"
+dependencies = [
+ "base64ct",
+ "der",
+]
+
+[[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
+[[package]]
+name = "subtle"
+version = "2.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
+
+[[package]]
+name = "syn"
+version = "1.0.98"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "termcolor"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "test-context"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "055831a02a4f5aa28fede67f2902014273eb8c21b958ac5ebbd59b71ef30dbc3"
+dependencies = [
+ "async-trait",
+ "futures",
+ "test-context-macros",
+]
+
+[[package]]
+name = "test-context-macros"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8901a55b0a7a06ebc4a674dcca925170da8e613fa3b163a1df804ed10afb154d"
+dependencies = [
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "toml"
+version = "0.5.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "typenum"
+version = "1.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
+
+[[package]]
+name = "ucd-trie"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89570599c4fe5585de2b388aab47e99f7fa4e9238a1399f707a02e356058141c"
+
+[[package]]
+name = "uint"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0"
+dependencies = [
+ "byteorder",
+ "crunchy",
+ "hex",
+ "static_assertions",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "15c61ba63f9235225a22310255a29b806b907c9b8c964bcbd0a2c70f3f2deea7"
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
+name = "vote-hooks"
+version = "0.1.0"
+dependencies = [
+ "cosmwasm-std",
+ "indexable-hooks",
+ "schemars",
+ "serde",
+]
+
+[[package]]
+name = "voting"
+version = "0.1.0"
+dependencies = [
+ "cosmwasm-std",
+ "cw-core",
+ "cw-core-interface",
+ "cw-storage-plus 0.12.1",
+ "cw-utils 0.13.4",
+ "cw20 0.12.1",
+ "schemars",
+ "serde",
+ "thiserror",
+]
+
+[[package]]
+name = "wasi"
+version = "0.9.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "yaml-rust"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
+dependencies = [
+ "linked-hash-map",
+]
+
+[[package]]
+name = "zeroize"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd"
diff --git a/ci/integration_tests/Cargo.toml b/ci/integration_tests/Cargo.toml
new file mode 100644
index 00000000..b99abf9a
--- /dev/null
+++ b/ci/integration_tests/Cargo.toml
@@ -0,0 +1,33 @@
+[package]
+name = "integration_tests"
+version = "0.1.0"
+edition = "2021"
+
+# This crate depends on rand. These are not features in
+# wasm builds of cosmwasm. Despite this crate only being used as a dev
+# dependency, because it is part of the workspace it will always be
+# compiled. There is no good way to remove a member from a workspace
+# conditionally. As such, we don't compile anything here if we're
+# targeting wasm.
+[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
+cosm-orc = { version = "1.3.2" }
+cw20 = "0.13"
+cw20-base = "0.13"
+cw-utils = "0.13"
+cosmwasm-std = { version = "1.0.0", features = ["ibc3"] }
+cw-core = { version = "0.1.0", path = "../../contracts/cw-core" }
+cw20-stake = { version = "*", path = "../../contracts/cw20-stake" }
+cw20-staked-balance-voting = { version = "0.1.0", path = "../../contracts/cw20-staked-balance-voting" }
+cw-proposal-single = { version = "0.1.0", path = "../../contracts/cw-proposal-single" }
+cw-core-interface = { version = "0.1.0", path = "../../packages/cw-core-interface" }
+voting = { version = "*", path = "../../packages/voting" }
+
+assert_matches = "1.5"
+anyhow = { version = "1.0.51"}
+serde = { version = "1.0", default-features = false, features = ["derive"] }
+serde_json = "1.0"
+once_cell = "1.13.0"
+env_logger = "0.9.0"
+test-context = "0.1.4"
+ctor = "0.1.22"
+rand = "0.8.5"
diff --git a/ci/integration_tests/README.md b/ci/integration_tests/README.md
new file mode 100644
index 00000000..10dbf6a6
--- /dev/null
+++ b/ci/integration_tests/README.md
@@ -0,0 +1,68 @@
+# Dao Dao Integration Tests
+
+Dao Dao e2e integration tests with gas profiling.
+
+`cd ci/integration_tests && cargo t` to run all tests.
+
+`cargo t fn_test_name` to run individual integration tests.
+
+## Running Locally
+
+### Hitting Local Juno
+
+* `./scripts/deploy_local.sh juno10j9gpw9t4jsz47qgnkvl5n3zlm2fz72k67rxsg`
+* `cd ci/integration_tests`
+* `CONTRACT_DIR="../../artifacts" GAS_OUT_DIR="gas_reports" CONFIG="configs/local.yaml" cargo t`
+
+### Hitting Testnet
+
+* `cd ci/integration_tests`
+* Change `src/helpers/chain.rs::test_account()` with your testnet account
+* `CONTRACT_DIR="../../artifacts" GAS_OUT_DIR="gas_reports" CONFIG="configs/testnet.yaml" cargo t`
+
+
+### Skipping Contract Storage
+
+By default all of the smart contracts are stored on-chain once before all of the tests are run. 
+This is time consuming when writing tests. If you want to skip this step you can use the `SKIP_CONTRACT_STORE=true` flag like so:
+
+`SKIP_CONTRACT_STORE=true GAS_OUT_DIR="gas_reports" CONFIG="configs/local.yaml" cargo t`
+
+This requires the `code ids` stored in [`configs/local.yaml`](configs/local.yaml) to be set to the correct, up to date value.
+For now you can see the output from `scripts/deploy_local.sh` and manually copy them over.
+These values change as contracts are added, so this is likely out of date, and a more robust solution for this is needed.
+
+
+## Adding New Integration Tests
+
+Add new tests in `src/tests`:
+```rust
+#[test_context(Chain)]
+#[test]
+fn new_dao_has_no_items(chain: &mut Chain) {
+    let res = create_dao(
+        chain, 
+        Some(chain.user.addr.clone()),
+        "ex_create_dao", 
+       chain.user.addr.clone()
+    );
+    let dao = res.unwrap();
+
+    // use the native rust types to interact with the contract
+     let res = chain
+        .orc
+        .query(
+            "cw_core",
+            "exc_items_get",
+            &cw_core::msg::QueryMsg::GetItem {
+                key: "meme".to_string(),
+            },
+        )
+        .unwrap();
+    let res: GetItemResponse = res.data().unwrap();
+
+    assert_eq!(res.item, None);
+}
+```
+
+Run `cargo c` to compile the tests.
diff --git a/ci/integration_tests/configs/ci.yaml b/ci/integration_tests/configs/ci.yaml
new file mode 100644
index 00000000..2cc4c6a1
--- /dev/null
+++ b/ci/integration_tests/configs/ci.yaml
@@ -0,0 +1,7 @@
+chain_cfg: 
+  denom: "ujunox"
+  prefix: "juno"
+  chain_id: "testing"
+  rpc_endpoint: "http://localhost:26657/"
+  gas_prices: 0.1
+  gas_adjustment: 1.5
diff --git a/ci/integration_tests/configs/local.yaml b/ci/integration_tests/configs/local.yaml
new file mode 100644
index 00000000..3a3f01e9
--- /dev/null
+++ b/ci/integration_tests/configs/local.yaml
@@ -0,0 +1,20 @@
+chain_cfg: 
+  denom: "ujunox"
+  prefix: "juno"
+  chain_id: "testing"
+  rpc_endpoint: "http://localhost:26657/"
+  gas_prices: 0.1
+  gas_adjustment: 1.5
+
+# Add code ids if you dont need to store them:
+code_ids:
+  "cw20_base": 1
+  "cw4_group": 2
+  # NOTE: If you are using `SKIP_CONTRACT_STORE` you will need to put the up-to-date
+  # code ids for the contracts you need here.
+  # Something like this:
+  # "cw_core": 9
+  # "cw_proposal_single": 14
+  # "cw4_voting": 6
+  # "cw20_staked_balance_voting": 5
+  # "cw20_stake": 4
\ No newline at end of file
diff --git a/ci/integration_tests/configs/testnet.yaml b/ci/integration_tests/configs/testnet.yaml
new file mode 100644
index 00000000..dd6c1089
--- /dev/null
+++ b/ci/integration_tests/configs/testnet.yaml
@@ -0,0 +1,12 @@
+chain_cfg: 
+  denom: "ujunox"
+  prefix: "juno"
+  chain_id: "uni-3"
+  rpc_endpoint: "https://rpc.uni.juno.deuslabs.fi:443"
+  gas_prices: 0.1
+  gas_adjustment: 1.5
+
+# Add code ids if you dont need to store them:
+code_ids:
+  "cw20_base": 77
+  "cw4_group": 78
\ No newline at end of file
diff --git a/ci/integration_tests/src/helpers/chain.rs b/ci/integration_tests/src/helpers/chain.rs
new file mode 100644
index 00000000..f5a60b63
--- /dev/null
+++ b/ci/integration_tests/src/helpers/chain.rs
@@ -0,0 +1,117 @@
+use cosm_orc::config::key::Key;
+use cosm_orc::orchestrator::cosm_orc::CosmOrc;
+use cosm_orc::{
+    config::cfg::Config, config::key::SigningKey, profilers::gas_profiler::GasProfiler,
+};
+use ctor::ctor;
+use once_cell::sync::OnceCell;
+use rand::Rng;
+use serde_json::Value;
+use std::env;
+use std::fs;
+use std::path::Path;
+use test_context::TestContext;
+
+static CONFIG: OnceCell<Cfg> = OnceCell::new();
+
+#[derive(Debug)]
+pub struct Cfg {
+    cfg: Config,
+    gas_report_dir: String,
+}
+
+pub struct Chain {
+    pub cfg: Config,
+    pub orc: CosmOrc,
+    pub user: Account,
+}
+
+pub struct Account {
+    pub addr: String,
+    pub key: SigningKey,
+}
+
+// NOTE: we have to run the integration tests in one thread right now.
+// We get `account sequence mismatch` CosmosSDK error when we run in parallel.
+// We could either serialize the `account.sequence` per key, or use a different key per test.
+
+impl TestContext for Chain {
+    fn setup() -> Self {
+        let cfg = CONFIG.get().unwrap().cfg.clone();
+        let orc = CosmOrc::new(cfg.clone())
+            .unwrap()
+            .add_profiler(Box::new(GasProfiler::new()));
+
+        let user = test_account(&cfg.chain_cfg.prefix);
+
+        Self { cfg, orc, user }
+    }
+
+    fn teardown(self) {
+        let cfg = CONFIG.get().unwrap();
+        save_gas_report(&self.orc, &cfg.gas_report_dir);
+    }
+}
+
+fn test_account(prefix: &str) -> Account {
+    // TODO: Make this configurable + bootstrap the local env with many test accounts
+    let key = SigningKey {
+        name: "localval".to_string(),
+        key: Key::Mnemonic("siren window salt bullet cream letter huge satoshi fade shiver permit offer happy immense wage fitness goose usual aim hammer clap about super trend".to_string()),
+    };
+    let addr = key.to_account(prefix).unwrap().to_string();
+
+    Account { addr, key }
+}
+
+// global_setup() runs once before anything else
+#[ctor]
+fn global_setup() {
+    env_logger::init();
+    let config = env::var("CONFIG").expect("missing yaml CONFIG env var");
+    let gas_report_dir = env::var("GAS_OUT_DIR").expect("missing GAS_OUT_DIR env var");
+
+    let mut cfg = Config::from_yaml(&config).unwrap();
+    let mut orc = CosmOrc::new(cfg.clone())
+        .unwrap()
+        .add_profiler(Box::new(GasProfiler::new()));
+
+    let account = test_account(&cfg.chain_cfg.prefix);
+
+    let skip_storage = env::var("SKIP_CONTRACT_STORE").unwrap_or_else(|_| "false".to_string());
+    if !skip_storage.parse::<bool>().unwrap() {
+        let contract_dir = env::var("CONTRACT_DIR").expect("missing CONTRACT_DIR env var");
+        orc.store_contracts(&contract_dir, &account.key).unwrap();
+        save_gas_report(&orc, &gas_report_dir);
+        // persist stored code_ids in CONFIG, so we can reuse for all tests
+        cfg.code_ids = orc
+            .contract_map
+            .deploy_info()
+            .iter()
+            .map(|(k, v)| (k.clone(), v.code_id))
+            .collect();
+    }
+
+    let config = Cfg {
+        cfg,
+        gas_report_dir,
+    };
+    CONFIG.set(config).expect("error initializing Config");
+}
+
+fn save_gas_report(orc: &CosmOrc, gas_report_dir: &str) {
+    let reports = orc
+        .profiler_reports()
+        .expect("error fetching profile reports");
+
+    let j: Value = serde_json::from_slice(&reports[0].json_data).unwrap();
+
+    let p = Path::new(gas_report_dir);
+    if !p.exists() {
+        fs::create_dir(p).unwrap();
+    }
+
+    let mut rng = rand::thread_rng();
+    let file_name = format!("test-{}.json", rng.gen::<u32>());
+    fs::write(p.join(file_name), j.to_string()).unwrap();
+}
diff --git a/ci/integration_tests/src/helpers/helper.rs b/ci/integration_tests/src/helpers/helper.rs
new file mode 100644
index 00000000..ece3a0a6
--- /dev/null
+++ b/ci/integration_tests/src/helpers/helper.rs
@@ -0,0 +1,96 @@
+use super::chain::Chain;
+use anyhow::Result;
+use cosmwasm_std::{to_binary, Decimal, Uint128};
+use cw20::Cw20Coin;
+use cw_core::{
+    msg::{Admin, ModuleInstantiateInfo},
+    query::DumpStateResponse,
+};
+use cw_utils::Duration;
+use voting::{
+    deposit::DepositInfo, deposit::DepositToken, threshold::PercentageThreshold,
+    threshold::Threshold,
+};
+
+#[derive(Debug)]
+pub struct DaoState {
+    pub addr: String,
+    pub state: DumpStateResponse,
+}
+
+pub fn create_dao(
+    chain: &mut Chain,
+    admin: Option<String>,
+    op_name: &str,
+    user_addr: String,
+) -> Result<DaoState> {
+    let msg = cw_core::msg::InstantiateMsg {
+        admin,
+        name: "DAO DAO".to_string(),
+        description: "A DAO that makes DAO tooling".to_string(),
+        image_url: None,
+        automatically_add_cw20s: false,
+        automatically_add_cw721s: false,
+        voting_module_instantiate_info: ModuleInstantiateInfo {
+            code_id: chain
+                .orc
+                .contract_map
+                .code_id("cw20_staked_balance_voting")?,
+            msg: to_binary(&cw20_staked_balance_voting::msg::InstantiateMsg {
+                token_info: cw20_staked_balance_voting::msg::TokenInfo::New {
+                    code_id: chain.orc.contract_map.code_id("cw20_base")?,
+                    label: "DAO DAO Gov token".to_string(),
+                    name: "DAO".to_string(),
+                    symbol: "DAO".to_string(),
+                    decimals: 6,
+                    initial_balances: vec![Cw20Coin {
+                        address: user_addr,
+                        amount: Uint128::new(100_000_000),
+                    }],
+                    marketing: None,
+                    staking_code_id: chain.orc.contract_map.code_id("cw20_stake")?,
+                    unstaking_duration: Some(Duration::Time(1209600)),
+                    initial_dao_balance: None,
+                },
+                active_threshold: None,
+            })?,
+            admin: Admin::CoreContract {},
+            label: "DAO DAO Voting Module".to_string(),
+        },
+        proposal_modules_instantiate_info: vec![ModuleInstantiateInfo {
+            code_id: chain.orc.contract_map.code_id("cw_proposal_single")?,
+            msg: to_binary(&cw_proposal_single::msg::InstantiateMsg {
+                min_voting_period: None,
+                threshold: Threshold::ThresholdQuorum {
+                    threshold: PercentageThreshold::Majority {},
+                    quorum: PercentageThreshold::Percent(Decimal::percent(35)),
+                },
+                max_voting_period: Duration::Time(432000),
+                allow_revoting: false,
+                only_members_execute: true,
+                deposit_info: Some(DepositInfo {
+                    token: DepositToken::VotingModuleToken {},
+                    deposit: Uint128::new(1000000000),
+                    refund_failed_proposals: true,
+                }),
+                close_proposal_on_execution_failure: false,
+            })?,
+            admin: Admin::CoreContract {},
+            label: "DAO DAO Proposal Module".to_string(),
+        }],
+        initial_items: None,
+    };
+
+    chain
+        .orc
+        .instantiate("cw_core", op_name, &msg, &chain.user.key)?;
+
+    let res = chain
+        .orc
+        .query("cw_core", op_name, &cw_core::msg::QueryMsg::DumpState {})?;
+
+    Ok(DaoState {
+        addr: chain.orc.contract_map.address("cw_core")?,
+        state: res.data()?,
+    })
+}
diff --git a/ci/integration_tests/src/helpers/mod.rs b/ci/integration_tests/src/helpers/mod.rs
new file mode 100644
index 00000000..5cc5aff6
--- /dev/null
+++ b/ci/integration_tests/src/helpers/mod.rs
@@ -0,0 +1,3 @@
+pub mod helper;
+
+pub mod chain;
diff --git a/ci/integration_tests/src/lib.rs b/ci/integration_tests/src/lib.rs
new file mode 100644
index 00000000..3d559812
--- /dev/null
+++ b/ci/integration_tests/src/lib.rs
@@ -0,0 +1,7 @@
+#![allow(dead_code)]
+
+#[cfg(not(target_arch = "wasm32"))]
+mod tests;
+
+#[cfg(not(target_arch = "wasm32"))]
+mod helpers;
diff --git a/ci/integration_tests/src/tests/cw20_stake_test.rs b/ci/integration_tests/src/tests/cw20_stake_test.rs
new file mode 100644
index 00000000..4d2ecc93
--- /dev/null
+++ b/ci/integration_tests/src/tests/cw20_stake_test.rs
@@ -0,0 +1,118 @@
+use core::time;
+use std::thread;
+
+use crate::helpers::{chain::Chain, helper::create_dao};
+use cosmwasm_std::{to_binary, Uint128};
+use cw20_stake::{msg::StakedValueResponse, state::Config};
+use cw_core_interface::voting::VotingPowerAtHeightResponse;
+use test_context::test_context;
+
+// #### ExecuteMsg #####
+
+#[test_context(Chain)]
+#[test]
+fn execute_stake_tokens(chain: &mut Chain) {
+    let voting_contract = "cw20_staked_balance_voting";
+
+    let res = create_dao(chain, None, "exc_stake_create_dao", chain.user.addr.clone());
+    let dao = res.unwrap();
+
+    let voting_addr = dao.state.voting_module.as_str();
+
+    // stake dao tokens:
+    chain
+        .orc
+        .contract_map
+        .add_address(voting_contract, voting_addr)
+        .unwrap();
+    let staking_addr: String = chain
+        .orc
+        .query(
+            voting_contract,
+            "exc_stake_q_stake",
+            &cw20_staked_balance_voting::msg::QueryMsg::StakingContract {},
+        )
+        .unwrap()
+        .data()
+        .unwrap();
+
+    chain
+        .orc
+        .contract_map
+        .add_address("cw20_stake", staking_addr.to_string())
+        .unwrap();
+    let res = chain
+        .orc
+        .query(
+            "cw20_stake",
+            "exc_stake_q_staked_value",
+            &cw20_stake::msg::QueryMsg::StakedValue {
+                address: chain.user.addr.clone(),
+            },
+        )
+        .unwrap();
+    let staked_value: StakedValueResponse = res.data().unwrap();
+
+    assert_eq!(staked_value.value, Uint128::new(0));
+
+    let res = chain
+        .orc
+        .query(
+            "cw20_stake",
+            "exc_stake_q_cfg",
+            &cw20_stake::msg::QueryMsg::GetConfig {},
+        )
+        .unwrap();
+    let config: Config = res.data().unwrap();
+
+    chain
+        .orc
+        .contract_map
+        .add_address("cw20_base", config.token_address.as_str())
+        .unwrap();
+    chain
+        .orc
+        .execute(
+            "cw20_base",
+            "exc_stake_stake_tokens",
+            &cw20_base::msg::ExecuteMsg::Send {
+                contract: staking_addr,
+                amount: Uint128::new(100),
+                msg: to_binary(&cw20_stake::msg::ReceiveMsg::Stake {}).unwrap(),
+            },
+            &chain.user.key,
+        )
+        .unwrap();
+
+    let res = chain
+        .orc
+        .query(
+            "cw20_stake",
+            "exc_stake_q_stake",
+            &cw20_stake::msg::QueryMsg::StakedValue {
+                address: chain.user.addr.clone(),
+            },
+        )
+        .unwrap();
+    let staked_value: StakedValueResponse = res.data().unwrap();
+
+    assert_eq!(staked_value.value, Uint128::new(100));
+
+    // Sleep to let staking block process, so we have voting power:
+    thread::sleep(time::Duration::from_millis(5000));
+
+    let res = chain
+        .orc
+        .query(
+            "cw_core",
+            "exc_stake_q_power",
+            &cw_core::msg::QueryMsg::VotingPowerAtHeight {
+                address: chain.user.addr.clone(),
+                height: None,
+            },
+        )
+        .unwrap();
+    let power: VotingPowerAtHeightResponse = res.data().unwrap();
+
+    assert_eq!(power.power, Uint128::new(100));
+}
diff --git a/ci/integration_tests/src/tests/cw_core_test.rs b/ci/integration_tests/src/tests/cw_core_test.rs
new file mode 100644
index 00000000..6c103a85
--- /dev/null
+++ b/ci/integration_tests/src/tests/cw_core_test.rs
@@ -0,0 +1,367 @@
+use crate::helpers::chain::Chain;
+use crate::helpers::helper::create_dao;
+use assert_matches::assert_matches;
+use cosm_orc::client::error::ClientError;
+use cosm_orc::orchestrator::error::ProcessError;
+use cosmwasm_std::{to_binary, Addr, CosmosMsg, Decimal, Uint128};
+use cw20_stake::msg::{StakedValueResponse, TotalValueResponse};
+use cw_core::query::{GetItemResponse, PauseInfoResponse};
+use cw_utils::Duration;
+use test_context::test_context;
+use voting::{deposit::CheckedDepositInfo, threshold::PercentageThreshold, threshold::Threshold};
+
+// #### ExecuteMsg #####
+
+// TODO: Add tests for all cw-core execute msgs
+
+#[test_context(Chain)]
+#[test]
+fn execute_execute_admin_msgs(chain: &mut Chain) {
+    // if you are not the admin, you cant execute admin msgs:
+    let res = create_dao(
+        chain,
+        None,
+        "exc_admin_msgs_create_dao",
+        chain.user.addr.clone(),
+    );
+    let dao = res.unwrap();
+
+    let res = chain.orc.execute(
+        "cw_core",
+        "exc_admin_msgs_pause_dao_fail",
+        &cw_core::msg::ExecuteMsg::ExecuteAdminMsgs {
+            msgs: vec![CosmosMsg::Wasm(cosmwasm_std::WasmMsg::Execute {
+                contract_addr: dao.addr,
+                msg: to_binary(&cw_core::msg::ExecuteMsg::Pause {
+                    duration: Duration::Time(100),
+                })
+                .unwrap(),
+                funds: vec![],
+            })],
+        },
+        &chain.user.key,
+    );
+
+    assert_matches!(
+        res.unwrap_err(),
+        ProcessError::ClientError(ClientError::CosmosSdk { .. })
+    );
+
+    let res = chain
+        .orc
+        .query(
+            "cw_core",
+            "exc_admin_msgs_pause_dao_query",
+            &cw_core::msg::QueryMsg::PauseInfo {},
+        )
+        .unwrap();
+    let res: PauseInfoResponse = res.data().unwrap();
+
+    assert_eq!(res, PauseInfoResponse::Unpaused {});
+
+    // if you are the admin you can execute admin msgs:
+    let res = create_dao(
+        chain,
+        Some(chain.user.addr.clone()),
+        "exc_admin_msgs_create_dao_with_admin",
+        chain.user.addr.clone(),
+    );
+    let dao = res.unwrap();
+
+    chain
+        .orc
+        .execute(
+            "cw_core",
+            "exc_admin_msgs_pause_dao",
+            &cw_core::msg::ExecuteMsg::ExecuteAdminMsgs {
+                msgs: vec![CosmosMsg::Wasm(cosmwasm_std::WasmMsg::Execute {
+                    contract_addr: dao.addr,
+                    msg: to_binary(&cw_core::msg::ExecuteMsg::Pause {
+                        duration: Duration::Height(100),
+                    })
+                    .unwrap(),
+                    funds: vec![],
+                })],
+            },
+            &chain.user.key,
+        )
+        .unwrap();
+
+    let res = chain
+        .orc
+        .query(
+            "cw_core",
+            "exc_admin_msgs_pause_dao",
+            &cw_core::msg::QueryMsg::PauseInfo {},
+        )
+        .unwrap();
+
+    let res: PauseInfoResponse = res.data().unwrap();
+    assert_ne!(res, PauseInfoResponse::Unpaused {});
+}
+
+#[test_context(Chain)]
+#[test]
+fn execute_items(chain: &mut Chain) {
+    // add item:
+    let res = create_dao(
+        chain,
+        Some(chain.user.addr.clone()),
+        "exc_items_create_dao",
+        chain.user.addr.clone(),
+    );
+
+    let dao = res.unwrap();
+
+    let res = chain
+        .orc
+        .query(
+            "cw_core",
+            "exc_items_get",
+            &cw_core::msg::QueryMsg::GetItem {
+                key: "meme".to_string(),
+            },
+        )
+        .unwrap();
+    let res: GetItemResponse = res.data().unwrap();
+
+    assert_eq!(res.item, None);
+
+    chain
+        .orc
+        .execute(
+            "cw_core",
+            "exc_items_set",
+            &cw_core::msg::ExecuteMsg::ExecuteAdminMsgs {
+                msgs: vec![CosmosMsg::Wasm(cosmwasm_std::WasmMsg::Execute {
+                    contract_addr: dao.addr.clone(),
+                    msg: to_binary(&cw_core::msg::ExecuteMsg::SetItem {
+                        key: "meme".to_string(),
+                        addr: "foobar".to_string(),
+                    })
+                    .unwrap(),
+                    funds: vec![],
+                })],
+            },
+            &chain.user.key,
+        )
+        .unwrap();
+
+    let res = chain
+        .orc
+        .query(
+            "cw_core",
+            "exc_items_set",
+            &cw_core::msg::QueryMsg::GetItem {
+                key: "meme".to_string(),
+            },
+        )
+        .unwrap();
+    let res: GetItemResponse = res.data().unwrap();
+
+    assert_eq!(res.item, Some("foobar".to_string()));
+
+    // remove item:
+    chain
+        .orc
+        .execute(
+            "cw_core",
+            "exc_items_rm",
+            &cw_core::msg::ExecuteMsg::ExecuteAdminMsgs {
+                msgs: vec![CosmosMsg::Wasm(cosmwasm_std::WasmMsg::Execute {
+                    contract_addr: dao.addr,
+                    msg: to_binary(&cw_core::msg::ExecuteMsg::RemoveItem {
+                        key: "meme".to_string(),
+                    })
+                    .unwrap(),
+                    funds: vec![],
+                })],
+            },
+            &chain.user.key,
+        )
+        .unwrap();
+
+    let res = chain
+        .orc
+        .query(
+            "cw_core",
+            "exc_items_rm",
+            &cw_core::msg::QueryMsg::GetItem {
+                key: "meme".to_string(),
+            },
+        )
+        .unwrap();
+    let res: GetItemResponse = res.data().unwrap();
+
+    assert_eq!(res.item, None);
+}
+
+// #### InstantiateMsg #####
+
+#[test_context(Chain)]
+#[test]
+fn instantiate_with_no_admin(chain: &mut Chain) {
+    let res = create_dao(chain, None, "inst_dao_no_admin", chain.user.addr.clone());
+    let dao = res.unwrap();
+
+    // ensure the dao is the admin:
+    assert_eq!(dao.state.admin, dao.addr);
+    assert_eq!(dao.state.pause_info, PauseInfoResponse::Unpaused {});
+    assert_eq!(
+        dao.state.config,
+        cw_core::state::Config {
+            name: "DAO DAO".to_string(),
+            description: "A DAO that makes DAO tooling".to_string(),
+            image_url: None,
+            automatically_add_cw20s: false,
+            automatically_add_cw721s: false
+        }
+    );
+}
+
+#[test_context(Chain)]
+#[test]
+fn instantiate_with_admin(chain: &mut Chain) {
+    let voting_contract = "cw20_staked_balance_voting";
+    let proposal_contract = "cw_proposal_single";
+
+    let res = create_dao(
+        chain,
+        Some(chain.user.addr.clone()),
+        "inst_admin_create_dao",
+        chain.user.addr.clone(),
+    );
+    let dao = res.unwrap();
+
+    // general dao info is valid:
+    assert_eq!(dao.state.admin, chain.user.addr);
+    assert_eq!(dao.state.pause_info, PauseInfoResponse::Unpaused {});
+    assert_eq!(
+        dao.state.config,
+        cw_core::state::Config {
+            name: "DAO DAO".to_string(),
+            description: "A DAO that makes DAO tooling".to_string(),
+            image_url: None,
+            automatically_add_cw20s: false,
+            automatically_add_cw721s: false
+        }
+    );
+
+    let voting_addr = dao.state.voting_module.as_str();
+    let prop_addr = dao.state.proposal_modules[0].as_str();
+
+    // voting module config is valid:
+    chain
+        .orc
+        .contract_map
+        .add_address(voting_contract, voting_addr)
+        .unwrap();
+    let res = &chain
+        .orc
+        .query(
+            voting_contract,
+            "inst_admin_q_stake",
+            &cw20_staked_balance_voting::msg::QueryMsg::StakingContract {},
+        )
+        .unwrap();
+    let staking_addr: &str = res.data().unwrap();
+
+    chain
+        .orc
+        .contract_map
+        .add_address("cw20_stake", staking_addr)
+        .unwrap();
+    let res = chain
+        .orc
+        .query(
+            "cw20_stake",
+            "inst_admin_q_val",
+            &cw20_stake::msg::QueryMsg::StakedValue {
+                address: chain.user.addr.clone(),
+            },
+        )
+        .unwrap();
+    let staked_res: StakedValueResponse = res.data().unwrap();
+    assert_eq!(staked_res.value, Uint128::new(0));
+
+    let res = chain
+        .orc
+        .query(
+            "cw20_stake",
+            "inst_admin_q_cfg",
+            &cw20_stake::msg::QueryMsg::GetConfig {},
+        )
+        .unwrap();
+    let config_res: cw20_stake::state::Config = res.data().unwrap();
+    assert_eq!(
+        config_res.owner,
+        Some(Addr::unchecked(
+            chain.orc.contract_map.address("cw_core").unwrap()
+        ))
+    );
+    assert_eq!(config_res.manager, None);
+
+    let res = &chain
+        .orc
+        .query(
+            voting_contract,
+            "inst_admin_q_tok",
+            &cw20_staked_balance_voting::msg::QueryMsg::TokenContract {},
+        )
+        .unwrap();
+    let token_addr: &str = res.data().unwrap();
+    assert_eq!(config_res.token_address, token_addr);
+
+    assert_eq!(config_res.unstaking_duration, Some(Duration::Time(1209600)));
+
+    let res = chain
+        .orc
+        .query(
+            "cw20_stake",
+            "inst_admin_q_val",
+            &cw20_stake::msg::QueryMsg::TotalValue {},
+        )
+        .unwrap();
+    let total_res: TotalValueResponse = res.data().unwrap();
+    assert_eq!(total_res.total, Uint128::new(0));
+
+    // proposal module config is valid:
+    chain
+        .orc
+        .contract_map
+        .add_address(proposal_contract, prop_addr)
+        .unwrap();
+    let res = chain
+        .orc
+        .query(
+            proposal_contract,
+            "inst_admin_q_cfg",
+            &cw_proposal_single::msg::QueryMsg::Config {},
+        )
+        .unwrap();
+    let config_res: cw_proposal_single::state::Config = res.data().unwrap();
+
+    assert_eq!(config_res.min_voting_period, None);
+    assert_eq!(config_res.max_voting_period, Duration::Time(432000));
+    assert!(!config_res.allow_revoting);
+    assert!(config_res.only_members_execute);
+    assert_eq!(
+        config_res.deposit_info,
+        Some(CheckedDepositInfo {
+            token: Addr::unchecked(token_addr),
+            deposit: Uint128::new(1000000000),
+            refund_failed_proposals: true,
+        })
+    );
+    assert_eq!(
+        config_res.threshold,
+        Threshold::ThresholdQuorum {
+            threshold: PercentageThreshold::Majority {},
+            quorum: PercentageThreshold::Percent(Decimal::percent(35)),
+        }
+    );
+    assert_eq!(
+        config_res.dao,
+        chain.orc.contract_map.address("cw_core").unwrap()
+    );
+}
diff --git a/ci/integration_tests/src/tests/mod.rs b/ci/integration_tests/src/tests/mod.rs
new file mode 100644
index 00000000..758582f3
--- /dev/null
+++ b/ci/integration_tests/src/tests/mod.rs
@@ -0,0 +1,3 @@
+pub mod cw_core_test;
+
+pub mod cw20_stake_test;
diff --git a/contracts/cw-proposal-multiple/src/tests.rs b/contracts/cw-proposal-multiple/src/tests.rs
index 4f1fc66f..9e051f78 100644
--- a/contracts/cw-proposal-multiple/src/tests.rs
+++ b/contracts/cw-proposal-multiple/src/tests.rs
@@ -149,6 +149,7 @@ where
         min_voting_period: None,
         max_voting_period,
         only_members_execute: false,
+        allow_revoting: false,
         deposit_info,
         voting_strategy,
         close_proposal_on_execution_failure: true,
@@ -765,6 +766,7 @@ fn test_propose() {
     let instantiate = InstantiateMsg {
         max_voting_period,
         only_members_execute: false,
+        allow_revoting: false,
         deposit_info: None,
         voting_strategy: voting_strategy.clone(),
         min_voting_period: None,
@@ -801,6 +803,7 @@ fn test_propose() {
     let expected = Config {
         max_voting_period,
         only_members_execute: false,
+        allow_revoting: false,
         dao: governance_addr,
         deposit_info: None,
         voting_strategy: voting_strategy.clone(),
@@ -855,6 +858,7 @@ fn test_propose() {
         votes: MultipleChoiceVotes {
             vote_weights: vec![Uint128::zero(); 3],
         },
+        allow_revoting: false,
         deposit_info: None,
         min_voting_period: None,
     };
@@ -878,6 +882,7 @@ fn test_propose_wrong_num_choices() {
         close_proposal_on_execution_failure: true,
         max_voting_period,
         only_members_execute: false,
+        allow_revoting: false,
         deposit_info: None,
         voting_strategy: voting_strategy.clone(),
     };
@@ -914,6 +919,7 @@ fn test_propose_wrong_num_choices() {
         close_proposal_on_execution_failure: true,
         max_voting_period,
         only_members_execute: false,
+        allow_revoting: false,
         dao: governance_addr,
         deposit_info: None,
         voting_strategy,
@@ -1072,6 +1078,7 @@ fn test_migrate() {
         min_voting_period: None,
         close_proposal_on_execution_failure: true,
         only_members_execute: true,
+        allow_revoting: false,
         deposit_info: None,
     };
 
@@ -1130,6 +1137,7 @@ fn test_proposal_count_initialized_to_zero() {
         min_voting_period: None,
         close_proposal_on_execution_failure: true,
         only_members_execute: true,
+        allow_revoting: false,
         deposit_info: None,
     };
     let core_addr = instantiate_with_staked_balances_governance(
@@ -1167,6 +1175,7 @@ fn test_no_early_pass_with_min_duration() {
         max_voting_period: Duration::Height(10),
         min_voting_period: Some(Duration::Height(2)),
         only_members_execute: true,
+        allow_revoting: false,
         deposit_info: None,
         close_proposal_on_execution_failure: true,
     };
@@ -1264,6 +1273,7 @@ fn test_propose_with_messages() {
         min_voting_period: None,
         close_proposal_on_execution_failure: true,
         only_members_execute: true,
+        allow_revoting: false,
         deposit_info: None,
     };
 
@@ -1300,6 +1310,7 @@ fn test_propose_with_messages() {
         close_proposal_on_execution_failure: true,
         max_voting_period: cw_utils::Duration::Height(20),
         only_members_execute: false,
+        allow_revoting: false,
         dao: "dao".to_string(),
         deposit_info: None,
     };
@@ -1384,6 +1395,7 @@ fn test_min_duration_units_missmatch() {
         max_voting_period: Duration::Height(10),
         min_voting_period: Some(Duration::Time(2)),
         only_members_execute: true,
+        allow_revoting: false,
         deposit_info: None,
         close_proposal_on_execution_failure: true,
     };
@@ -1416,6 +1428,7 @@ fn test_min_duration_larger_than_proposal_duration() {
         max_voting_period: Duration::Height(10),
         min_voting_period: Some(Duration::Height(11)),
         only_members_execute: true,
+        allow_revoting: false,
         deposit_info: None,
         close_proposal_on_execution_failure: true,
     };
@@ -1447,6 +1460,7 @@ fn test_min_duration_same_as_proposal_duration() {
         max_voting_period: Duration::Time(10),
         min_voting_period: Some(Duration::Time(10)),
         only_members_execute: true,
+        allow_revoting: false,
         deposit_info: None,
         close_proposal_on_execution_failure: true,
     };
@@ -1565,6 +1579,7 @@ fn test_voting_module_token_proposal_deposit_instantiate() {
         close_proposal_on_execution_failure: true,
         max_voting_period,
         only_members_execute: false,
+        allow_revoting: false,
         deposit_info,
         voting_strategy,
     };
@@ -1641,6 +1656,7 @@ fn test_different_token_proposal_deposit() {
         close_proposal_on_execution_failure: true,
         max_voting_period,
         only_members_execute: false,
+        allow_revoting: false,
         deposit_info: Some(DepositInfo {
             token: DepositToken::Token {
                 address: cw20_addr.to_string(),
@@ -1710,6 +1726,7 @@ fn test_bad_token_proposal_deposit() {
         close_proposal_on_execution_failure: true,
         max_voting_period,
         only_members_execute: false,
+        allow_revoting: false,
         deposit_info,
         voting_strategy,
     };
@@ -1741,6 +1758,7 @@ fn test_take_proposal_deposit() {
         close_proposal_on_execution_failure: true,
         max_voting_period,
         only_members_execute: false,
+        allow_revoting: false,
         deposit_info,
         voting_strategy,
     };
@@ -2165,6 +2183,7 @@ fn test_cant_propose_zero_power() {
         close_proposal_on_execution_failure: true,
         max_voting_period,
         only_members_execute: false,
+        allow_revoting: false,
         deposit_info: Some(DepositInfo {
             token: DepositToken::VotingModuleToken {},
             deposit: Uint128::new(1),
@@ -2331,6 +2350,7 @@ fn test_cant_execute_not_member() {
         close_proposal_on_execution_failure: true,
         max_voting_period,
         only_members_execute: true,
+        allow_revoting: false,
         deposit_info: None,
         voting_strategy,
     };
@@ -2669,6 +2689,7 @@ fn test_execute_expired_proposal() {
         close_proposal_on_execution_failure: true,
         max_voting_period,
         only_members_execute: false,
+        allow_revoting: false,
         deposit_info: None,
         voting_strategy,
     };
@@ -2844,6 +2865,7 @@ fn test_update_config() {
             close_proposal_on_execution_failure: true,
             max_voting_period: cw_utils::Duration::Height(10),
             only_members_execute: false,
+            allow_revoting: false,
             dao: dao.to_string(),
             deposit_info: None,
         },
@@ -2863,6 +2885,7 @@ fn test_update_config() {
             close_proposal_on_execution_failure: true,
             max_voting_period: cw_utils::Duration::Height(10),
             only_members_execute: false,
+            allow_revoting: false,
             dao: Addr::unchecked(CREATOR_ADDR).to_string(),
             deposit_info: None,
         },
@@ -2883,6 +2906,7 @@ fn test_update_config() {
         close_proposal_on_execution_failure: true,
         max_voting_period: cw_utils::Duration::Height(10),
         only_members_execute: false,
+        allow_revoting: false,
         dao: Addr::unchecked(CREATOR_ADDR),
         deposit_info: None,
     };
@@ -2901,6 +2925,7 @@ fn test_update_config() {
             close_proposal_on_execution_failure: true,
             max_voting_period: cw_utils::Duration::Height(10),
             only_members_execute: false,
+            allow_revoting: false,
             dao: Addr::unchecked(CREATOR_ADDR).to_string(),
             deposit_info: None,
         },
@@ -2981,6 +3006,7 @@ fn test_query_list_proposals() {
         close_proposal_on_execution_failure: true,
         max_voting_period,
         only_members_execute: false,
+        allow_revoting: false,
         deposit_info: None,
         voting_strategy: voting_strategy.clone(),
     };
@@ -3076,6 +3102,7 @@ fn test_query_list_proposals() {
             votes: MultipleChoiceVotes {
                 vote_weights: vec![Uint128::zero(); 3],
             },
+            allow_revoting: false,
             deposit_info: None,
             min_voting_period: None,
         },
@@ -3119,6 +3146,7 @@ fn test_query_list_proposals() {
             votes: MultipleChoiceVotes {
                 vote_weights: vec![Uint128::zero(); 3],
             },
+            allow_revoting: false,
             deposit_info: None,
             min_voting_period: None,
         },
@@ -3143,6 +3171,7 @@ fn test_hooks() {
         close_proposal_on_execution_failure: true,
         max_voting_period,
         only_members_execute: false,
+        allow_revoting: false,
         deposit_info: None,
         voting_strategy,
     };
@@ -3300,6 +3329,7 @@ fn test_active_threshold_absolute() {
         close_proposal_on_execution_failure: true,
         max_voting_period,
         only_members_execute: false,
+        allow_revoting: false,
         deposit_info: None,
         voting_strategy,
     };
@@ -3437,6 +3467,7 @@ fn test_active_threshold_percent() {
         close_proposal_on_execution_failure: true,
         max_voting_period,
         only_members_execute: false,
+        allow_revoting: false,
         deposit_info: None,
         voting_strategy,
     };
@@ -3575,6 +3606,7 @@ fn test_active_threshold_none() {
         close_proposal_on_execution_failure: true,
         max_voting_period,
         only_members_execute: false,
+        allow_revoting: false,
         deposit_info: None,
         voting_strategy,
     };
@@ -3703,6 +3735,559 @@ fn test_active_threshold_none() {
         .unwrap();
 }
 
+/// Basic test for revoting on prop-multiple
+#[test]
+fn test_revoting() {
+    let mut app = App::default();
+    let govmod_id = app.store_code(proposal_contract());
+    let core_addr = instantiate_with_staked_balances_governance(
+        &mut app,
+        govmod_id,
+        to_binary(&InstantiateMsg {
+            min_voting_period: None,
+            max_voting_period: Duration::Height(6),
+            only_members_execute: false,
+            allow_revoting: true,
+            deposit_info: None,
+            voting_strategy: VotingStrategy::SingleChoice {
+                quorum: PercentageThreshold::Majority {},
+            },
+            close_proposal_on_execution_failure: false,
+        })
+        .unwrap(),
+        Some(vec![
+            Cw20Coin {
+                address: "a-1".to_string(),
+                amount: Uint128::new(100_000_000),
+            },
+            Cw20Coin {
+                address: "a-2".to_string(),
+                amount: Uint128::new(100_000_000),
+            },
+        ]),
+    );
+
+    let governance_modules: Vec<Addr> = app
+        .wrap()
+        .query_wasm_smart(
+            core_addr,
+            &cw_core::msg::QueryMsg::ProposalModules {
+                start_after: None,
+                limit: None,
+            },
+        )
+        .unwrap();
+    let proposal_module = governance_modules.into_iter().next().unwrap();
+
+    let options = vec![
+        MultipleChoiceOption {
+            description: "multiple choice option 1".to_string(),
+            msgs: None,
+        },
+        MultipleChoiceOption {
+            description: "multiple choice option 2".to_string(),
+            msgs: None,
+        },
+    ];
+    let mc_options = MultipleChoiceOptions { options };
+
+    // Create a basic proposal with 2 options
+    app.execute_contract(
+        Addr::unchecked("a-1"),
+        proposal_module.clone(),
+        &ExecuteMsg::Propose {
+            title: "A simple text proposal".to_string(),
+            description: "A simple text proposal".to_string(),
+            choices: mc_options,
+        },
+        &[],
+    )
+    .unwrap();
+
+    // a-1 votes, vote_weights: [100_000_000, 0]
+    app.execute_contract(
+        Addr::unchecked("a-1"),
+        proposal_module.clone(),
+        &ExecuteMsg::Vote {
+            proposal_id: 1,
+            vote: MultipleChoiceVote { option_id: 0 },
+        },
+        &[],
+    )
+    .unwrap();
+
+    // a-2 votes, vote_weights: [100_000_000, 100_000_000]
+    app.execute_contract(
+        Addr::unchecked("a-2"),
+        proposal_module.clone(),
+        &ExecuteMsg::Vote {
+            proposal_id: 1,
+            vote: MultipleChoiceVote { option_id: 1 },
+        },
+        &[],
+    )
+    .unwrap();
+
+    // Time passes..
+    app.update_block(|b| b.height += 2);
+
+    // Assert that both vote options have equal vote weights at some block
+    let proposal: ProposalResponse = app
+        .wrap()
+        .query_wasm_smart(
+            proposal_module.clone(),
+            &QueryMsg::Proposal { proposal_id: 1 },
+        )
+        .unwrap();
+    assert_eq!(proposal.proposal.status, Status::Open);
+    assert_eq!(
+        proposal.proposal.votes.vote_weights[0],
+        Uint128::new(100_000_000),
+    );
+    assert_eq!(
+        proposal.proposal.votes.vote_weights[1],
+        Uint128::new(100_000_000),
+    );
+
+    // More time passes..
+    app.update_block(|b| b.height += 3);
+
+    // Last moment a-2 has a change of mind,
+    // votes shift to [200_000_000, 0]
+    app.execute_contract(
+        Addr::unchecked("a-2"),
+        proposal_module.clone(),
+        &ExecuteMsg::Vote {
+            proposal_id: 1,
+            vote: MultipleChoiceVote { option_id: 0 },
+        },
+        &[],
+    )
+    .unwrap();
+
+    app.update_block(next_block);
+
+    // Assert that revote succeeded
+    let proposal: ProposalResponse = app
+        .wrap()
+        .query_wasm_smart(proposal_module, &QueryMsg::Proposal { proposal_id: 1 })
+        .unwrap();
+    assert_eq!(proposal.proposal.status, Status::Passed);
+    assert_eq!(
+        proposal.proposal.votes.vote_weights[0],
+        Uint128::new(200_000_000),
+    );
+    assert_eq!(proposal.proposal.votes.vote_weights[1], Uint128::new(0),);
+}
+
+/// Tests that revoting is stored at a per-proposal level.
+/// Proposals created while revoting is enabled should not
+/// have it disabled if a config change turns if off.
+#[test]
+fn test_allow_revoting_config_changes() {
+    let mut app = App::default();
+    let govmod_id = app.store_code(proposal_contract());
+    let core_addr = instantiate_with_staked_balances_governance(
+        &mut app,
+        govmod_id,
+        to_binary(&InstantiateMsg {
+            min_voting_period: None,
+            max_voting_period: Duration::Height(6),
+            only_members_execute: false,
+            allow_revoting: true,
+            deposit_info: None,
+            voting_strategy: VotingStrategy::SingleChoice {
+                quorum: PercentageThreshold::Majority {},
+            },
+            close_proposal_on_execution_failure: false,
+        })
+        .unwrap(),
+        Some(vec![
+            Cw20Coin {
+                address: "a-1".to_string(),
+                amount: Uint128::new(100_000_000),
+            },
+            Cw20Coin {
+                address: "a-2".to_string(),
+                amount: Uint128::new(100_000_000),
+            },
+        ]),
+    );
+
+    let governance_modules: Vec<Addr> = app
+        .wrap()
+        .query_wasm_smart(
+            core_addr.clone(),
+            &cw_core::msg::QueryMsg::ProposalModules {
+                start_after: None,
+                limit: None,
+            },
+        )
+        .unwrap();
+    let proposal_module = governance_modules.into_iter().next().unwrap();
+
+    let options = vec![
+        MultipleChoiceOption {
+            description: "multiple choice option 1".to_string(),
+            msgs: None,
+        },
+        MultipleChoiceOption {
+            description: "multiple choice option 2".to_string(),
+            msgs: None,
+        },
+    ];
+    let mc_options = MultipleChoiceOptions { options };
+
+    // Create a basic proposal with 2 options that allows revoting
+    app.execute_contract(
+        Addr::unchecked("a-1"),
+        proposal_module.clone(),
+        &ExecuteMsg::Propose {
+            title: "A simple text proposal".to_string(),
+            description: "A simple text proposal".to_string(),
+            choices: mc_options.clone(),
+        },
+        &[],
+    )
+    .unwrap();
+
+    // Disable revoting
+    app.execute_contract(
+        core_addr.clone(),
+        proposal_module.clone(),
+        &ExecuteMsg::UpdateConfig {
+            min_voting_period: None,
+            max_voting_period: Duration::Height(6),
+            only_members_execute: false,
+            allow_revoting: false,
+            dao: core_addr.to_string(),
+            deposit_info: None,
+            voting_strategy: VotingStrategy::SingleChoice {
+                quorum: PercentageThreshold::Majority {},
+            },
+            close_proposal_on_execution_failure: false,
+        },
+        &[],
+    )
+    .unwrap();
+
+    // Assert that proposal_id: 1 still allows revoting
+    let proposal: ProposalResponse = app
+        .wrap()
+        .query_wasm_smart(
+            proposal_module.clone(),
+            &QueryMsg::Proposal { proposal_id: 1 },
+        )
+        .unwrap();
+    assert!(proposal.proposal.allow_revoting);
+
+    app.execute_contract(
+        Addr::unchecked("a-1"),
+        proposal_module.clone(),
+        &ExecuteMsg::Vote {
+            proposal_id: 1,
+            vote: MultipleChoiceVote { option_id: 0 },
+        },
+        &[],
+    )
+    .unwrap();
+    app.execute_contract(
+        Addr::unchecked("a-1"),
+        proposal_module.clone(),
+        &ExecuteMsg::Vote {
+            proposal_id: 1,
+            vote: MultipleChoiceVote { option_id: 1 },
+        },
+        &[],
+    )
+    .unwrap();
+
+    // New proposals should not allow revoting
+    app.execute_contract(
+        Addr::unchecked("a-2"),
+        proposal_module.clone(),
+        &ExecuteMsg::Propose {
+            title: "A very complex text proposal".to_string(),
+            description: "A very complex text proposal".to_string(),
+            choices: mc_options,
+        },
+        &[],
+    )
+    .unwrap();
+
+    app.execute_contract(
+        Addr::unchecked("a-2"),
+        proposal_module.clone(),
+        &ExecuteMsg::Vote {
+            proposal_id: 2,
+            vote: MultipleChoiceVote { option_id: 0 },
+        },
+        &[],
+    )
+    .unwrap();
+
+    let err: ContractError = app
+        .execute_contract(
+            Addr::unchecked("a-2"),
+            proposal_module,
+            &ExecuteMsg::Vote {
+                proposal_id: 2,
+                vote: MultipleChoiceVote { option_id: 1 },
+            },
+            &[],
+        )
+        .unwrap_err()
+        .downcast()
+        .unwrap();
+
+    assert!(matches!(err, ContractError::AlreadyVoted {}));
+}
+
+/// Tests that we error if a revote casts the same vote as the
+/// previous vote.
+#[test]
+fn test_revoting_same_vote_twice() {
+    let mut app = App::default();
+    let govmod_id = app.store_code(proposal_contract());
+    let core_addr = instantiate_with_staked_balances_governance(
+        &mut app,
+        govmod_id,
+        to_binary(&InstantiateMsg {
+            min_voting_period: None,
+            max_voting_period: Duration::Height(6),
+            only_members_execute: false,
+            allow_revoting: true,
+            deposit_info: None,
+            voting_strategy: VotingStrategy::SingleChoice {
+                quorum: PercentageThreshold::Majority {},
+            },
+            close_proposal_on_execution_failure: false,
+        })
+        .unwrap(),
+        Some(vec![
+            Cw20Coin {
+                address: "a-1".to_string(),
+                amount: Uint128::new(100_000_000),
+            },
+            Cw20Coin {
+                address: "a-2".to_string(),
+                amount: Uint128::new(100_000_000),
+            },
+        ]),
+    );
+
+    let governance_modules: Vec<Addr> = app
+        .wrap()
+        .query_wasm_smart(
+            core_addr,
+            &cw_core::msg::QueryMsg::ProposalModules {
+                start_after: None,
+                limit: None,
+            },
+        )
+        .unwrap();
+    let proposal_module = governance_modules.into_iter().next().unwrap();
+
+    let options = vec![
+        MultipleChoiceOption {
+            description: "multiple choice option 1".to_string(),
+            msgs: None,
+        },
+        MultipleChoiceOption {
+            description: "multiple choice option 2".to_string(),
+            msgs: None,
+        },
+    ];
+    let mc_options = MultipleChoiceOptions { options };
+
+    // Create a basic proposal with 2 options that allows revoting
+    app.execute_contract(
+        Addr::unchecked("a-1"),
+        proposal_module.clone(),
+        &ExecuteMsg::Propose {
+            title: "A simple text proposal".to_string(),
+            description: "A simple text proposal".to_string(),
+            choices: mc_options,
+        },
+        &[],
+    )
+    .unwrap();
+
+    // Cast a vote
+    app.execute_contract(
+        Addr::unchecked("a-1"),
+        proposal_module.clone(),
+        &ExecuteMsg::Vote {
+            proposal_id: 1,
+            vote: MultipleChoiceVote { option_id: 0 },
+        },
+        &[],
+    )
+    .unwrap();
+
+    // Revote for the same option as currently voted
+    let err: ContractError = app
+        .execute_contract(
+            Addr::unchecked("a-1"),
+            proposal_module,
+            &ExecuteMsg::Vote {
+                proposal_id: 1,
+                vote: MultipleChoiceVote { option_id: 0 },
+            },
+            &[],
+        )
+        .unwrap_err()
+        .downcast()
+        .unwrap();
+
+    // Can't cast the same vote twice.
+    assert!(matches!(err, ContractError::AlreadyCast {}));
+}
+
+/// Tests that revoting into a non-existing vote option
+/// does not invalidate the initial vote
+#[test]
+fn test_invalid_revote_does_not_invalidate_initial_vote() {
+    let mut app = App::default();
+    let govmod_id = app.store_code(proposal_contract());
+    let core_addr = instantiate_with_staked_balances_governance(
+        &mut app,
+        govmod_id,
+        to_binary(&InstantiateMsg {
+            min_voting_period: None,
+            max_voting_period: Duration::Height(6),
+            only_members_execute: false,
+            allow_revoting: true,
+            deposit_info: None,
+            voting_strategy: VotingStrategy::SingleChoice {
+                quorum: PercentageThreshold::Majority {},
+            },
+            close_proposal_on_execution_failure: false,
+        })
+        .unwrap(),
+        Some(vec![
+            Cw20Coin {
+                address: "a-1".to_string(),
+                amount: Uint128::new(100_000_000),
+            },
+            Cw20Coin {
+                address: "a-2".to_string(),
+                amount: Uint128::new(100_000_000),
+            },
+        ]),
+    );
+
+    let governance_modules: Vec<Addr> = app
+        .wrap()
+        .query_wasm_smart(
+            core_addr,
+            &cw_core::msg::QueryMsg::ProposalModules {
+                start_after: None,
+                limit: None,
+            },
+        )
+        .unwrap();
+    let proposal_module = governance_modules.into_iter().next().unwrap();
+
+    let options = vec![
+        MultipleChoiceOption {
+            description: "multiple choice option 1".to_string(),
+            msgs: None,
+        },
+        MultipleChoiceOption {
+            description: "multiple choice option 2".to_string(),
+            msgs: None,
+        },
+    ];
+    let mc_options = MultipleChoiceOptions { options };
+
+    // Create a basic proposal with 2 options
+    app.execute_contract(
+        Addr::unchecked("a-1"),
+        proposal_module.clone(),
+        &ExecuteMsg::Propose {
+            title: "A simple text proposal".to_string(),
+            description: "A simple text proposal".to_string(),
+            choices: mc_options,
+        },
+        &[],
+    )
+    .unwrap();
+
+    // a-1 votes, vote_weights: [100_000_000, 0]
+    app.execute_contract(
+        Addr::unchecked("a-1"),
+        proposal_module.clone(),
+        &ExecuteMsg::Vote {
+            proposal_id: 1,
+            vote: MultipleChoiceVote { option_id: 0 },
+        },
+        &[],
+    )
+    .unwrap();
+
+    // a-2 votes, vote_weights: [100_000_000, 100_000_000]
+    app.execute_contract(
+        Addr::unchecked("a-2"),
+        proposal_module.clone(),
+        &ExecuteMsg::Vote {
+            proposal_id: 1,
+            vote: MultipleChoiceVote { option_id: 1 },
+        },
+        &[],
+    )
+    .unwrap();
+
+    app.update_block(next_block);
+
+    // Assert that both vote options have equal vote weights at some block
+    let proposal: ProposalResponse = app
+        .wrap()
+        .query_wasm_smart(
+            proposal_module.clone(),
+            &QueryMsg::Proposal { proposal_id: 1 },
+        )
+        .unwrap();
+    assert_eq!(proposal.proposal.status, Status::Open);
+    assert_eq!(
+        proposal.proposal.votes.vote_weights[0],
+        Uint128::new(100_000_000),
+    );
+    assert_eq!(
+        proposal.proposal.votes.vote_weights[1],
+        Uint128::new(100_000_000),
+    );
+
+    // Time passes..
+    app.update_block(|b| b.height += 3);
+
+    // Last moment a-2 has a change of mind and attempts
+    // to vote for a non-existing option
+    let err: ContractError = app
+        .execute_contract(
+            Addr::unchecked("a-2"),
+            proposal_module,
+            &ExecuteMsg::Vote {
+                proposal_id: 1,
+                vote: MultipleChoiceVote { option_id: 99 },
+            },
+            &[],
+        )
+        .unwrap_err()
+        .downcast()
+        .unwrap();
+    // Assert that prior votes remained the same
+    assert_eq!(
+        proposal.proposal.votes.vote_weights[0],
+        Uint128::new(100_000_000),
+    );
+    assert_eq!(
+        proposal.proposal.votes.vote_weights[1],
+        Uint128::new(100_000_000),
+    );
+    assert!(matches!(err, ContractError::InvalidVote {}));
+}
+
 #[test]
 fn test_return_deposit_to_dao_on_proposal_failure() {
     let (mut app, core_addr) = do_test_votes_cw20_balances(
@@ -3780,6 +4365,7 @@ fn test_close_failed_proposal() {
         voting_strategy,
         min_voting_period: None,
         only_members_execute: false,
+        allow_revoting: false,
         deposit_info: None,
         close_proposal_on_execution_failure: true,
     };
@@ -3911,25 +4497,6 @@ fn test_close_failed_proposal() {
 
     assert_eq!(failed.proposal.status, Status::ExecutionFailed);
 
-    // Check we can close it after it failed
-    app.execute_contract(
-        Addr::unchecked(CREATOR_ADDR),
-        govmod_single.clone(),
-        &ExecuteMsg::Close { proposal_id: 1 },
-        &[],
-    )
-    .unwrap();
-
-    let closed_after_fail: ProposalResponse = app
-        .wrap()
-        .query_wasm_smart(
-            govmod_single.clone(),
-            &QueryMsg::Proposal { proposal_id: 1 },
-        )
-        .unwrap();
-
-    assert_eq!(closed_after_fail.proposal.status, Status::Closed);
-
     // With disabled feature
     // Disable feature first
     {
@@ -3955,6 +4522,7 @@ fn test_close_failed_proposal() {
                                     max_voting_period: original.max_voting_period,
                                     min_voting_period: original.min_voting_period,
                                     only_members_execute: original.only_members_execute,
+                                    allow_revoting: false,
                                     dao: original.dao.to_string(),
                                     deposit_info: None,
                                     close_proposal_on_execution_failure: false,
@@ -4040,3 +4608,227 @@ fn test_close_failed_proposal() {
     // not reverted
     assert_eq!(updated.proposal.status, Status::Passed);
 }
+
+#[test]
+fn test_no_double_refund_on_execute_fail_and_close() {
+    let mut app = App::default();
+    let proposal_module_id = app.store_code(proposal_contract());
+
+    let voting_strategy = VotingStrategy::SingleChoice {
+        quorum: PercentageThreshold::Majority {},
+    };
+    let max_voting_period = cw_utils::Duration::Height(6);
+    let instantiate = InstantiateMsg {
+        voting_strategy,
+        max_voting_period,
+        min_voting_period: None,
+        only_members_execute: false,
+        allow_revoting: false,
+        deposit_info: Some(DepositInfo {
+            token: DepositToken::VotingModuleToken {},
+            deposit: Uint128::new(1),
+            // Important to set to true here as we want to be sure
+            // that we don't get a second refund on close. Refunds on
+            // close only happen if this is true.
+            refund_failed_proposals: true,
+        }),
+        close_proposal_on_execution_failure: true,
+    };
+
+    let core_addr = instantiate_with_staking_active_threshold(
+        &mut app,
+        proposal_module_id,
+        to_binary(&instantiate).unwrap(),
+        Some(vec![Cw20Coin {
+            address: CREATOR_ADDR.to_string(),
+            // One token for sending to the DAO treasury, one token
+            // for staking, one token for paying the proposal deposit.
+            amount: Uint128::new(3),
+        }]),
+        None,
+    );
+    let proposal_modules: Vec<Addr> = app
+        .wrap()
+        .query_wasm_smart(
+            core_addr,
+            &cw_core::msg::QueryMsg::ProposalModules {
+                start_after: None,
+                limit: None,
+            },
+        )
+        .unwrap();
+
+    assert_eq!(proposal_modules.len(), 1);
+    let proposal_single = proposal_modules.into_iter().next().unwrap();
+
+    let proposal_config: Config = app
+        .wrap()
+        .query_wasm_smart(proposal_single.clone(), &QueryMsg::Config {})
+        .unwrap();
+    let dao = proposal_config.dao;
+    let voting_module: Addr = app
+        .wrap()
+        .query_wasm_smart(dao, &cw_core::msg::QueryMsg::VotingModule {})
+        .unwrap();
+    let staking_contract: Addr = app
+        .wrap()
+        .query_wasm_smart(
+            voting_module.clone(),
+            &cw20_staked_balance_voting::msg::QueryMsg::StakingContract {},
+        )
+        .unwrap();
+    let token_contract: Addr = app
+        .wrap()
+        .query_wasm_smart(
+            voting_module,
+            &cw_core_interface::voting::Query::TokenContract {},
+        )
+        .unwrap();
+
+    // Stake a token so we can propose.
+    let msg = cw20::Cw20ExecuteMsg::Send {
+        contract: staking_contract.to_string(),
+        amount: Uint128::new(1),
+        msg: to_binary(&cw20_stake::msg::ReceiveMsg::Stake {}).unwrap(),
+    };
+    app.execute_contract(
+        Addr::unchecked(CREATOR_ADDR),
+        token_contract.clone(),
+        &msg,
+        &[],
+    )
+    .unwrap();
+    app.update_block(next_block);
+
+    // Send some tokens to the proposal module so it has the ability
+    // to double refund if the code is buggy.
+    let msg = cw20::Cw20ExecuteMsg::Transfer {
+        recipient: proposal_single.to_string(),
+        amount: Uint128::new(1),
+    };
+    app.execute_contract(
+        Addr::unchecked(CREATOR_ADDR),
+        token_contract.clone(),
+        &msg,
+        &[],
+    )
+    .unwrap();
+
+    let msg = cw20::Cw20ExecuteMsg::Burn {
+        amount: Uint128::new(2000),
+    };
+    let binary_msg = to_binary(&msg).unwrap();
+
+    // Increase allowance to pay the proposal deposit.
+    app.execute_contract(
+        Addr::unchecked(CREATOR_ADDR),
+        token_contract.clone(),
+        &cw20_base::msg::ExecuteMsg::IncreaseAllowance {
+            spender: proposal_single.to_string(),
+            amount: Uint128::new(1),
+            expires: None,
+        },
+        &[],
+    )
+    .unwrap();
+
+    let choices = MultipleChoiceOptions {
+        options: vec![
+            MultipleChoiceOption {
+                description: "Burning more tokens, than dao treasury have".to_string(),
+                msgs: Some(vec![WasmMsg::Execute {
+                    contract_addr: token_contract.to_string(),
+                    msg: binary_msg,
+                    funds: vec![],
+                }
+                .into()]),
+            },
+            MultipleChoiceOption {
+                description: "hi there".to_string(),
+                msgs: None,
+            },
+        ],
+    };
+    // proposal to overburn tokens
+    app.execute_contract(
+        Addr::unchecked(CREATOR_ADDR),
+        proposal_single.clone(),
+        &ExecuteMsg::Propose {
+            title: "A simple burn tokens proposal".to_string(),
+            description: "Burning more tokens, than dao treasury have".to_string(),
+            choices,
+        },
+        &[],
+    )
+    .unwrap();
+
+    // Vote on proposal
+    app.execute_contract(
+        Addr::unchecked(CREATOR_ADDR),
+        proposal_single.clone(),
+        &ExecuteMsg::Vote {
+            proposal_id: 1,
+            vote: MultipleChoiceVote { option_id: 0 },
+        },
+        &[],
+    )
+    .unwrap();
+
+    // Execute proposal
+    app.execute_contract(
+        Addr::unchecked(CREATOR_ADDR),
+        proposal_single.clone(),
+        &ExecuteMsg::Execute { proposal_id: 1 },
+        &[],
+    )
+    .unwrap();
+
+    let failed: ProposalResponse = app
+        .wrap()
+        .query_wasm_smart(
+            proposal_single.clone(),
+            &QueryMsg::Proposal { proposal_id: 1 },
+        )
+        .unwrap();
+    assert_eq!(failed.proposal.status, Status::ExecutionFailed);
+
+    // Check that our deposit has been refunded.
+    let balance: cw20::BalanceResponse = app
+        .wrap()
+        .query_wasm_smart(
+            token_contract.to_string(),
+            &cw20::Cw20QueryMsg::Balance {
+                address: CREATOR_ADDR.to_string(),
+            },
+        )
+        .unwrap();
+
+    assert_eq!(balance.balance, Uint128::new(1));
+
+    // Close the proposal - this should fail as it was executed.
+    let err: ContractError = app
+        .execute_contract(
+            Addr::unchecked(CREATOR_ADDR),
+            proposal_single,
+            &ExecuteMsg::Close { proposal_id: 1 },
+            &[],
+        )
+        .unwrap_err()
+        .downcast()
+        .unwrap();
+
+    assert!(matches!(err, ContractError::WrongCloseStatus {}));
+
+    // Check that our deposit was not refunded a second time on close.
+    let balance: cw20::BalanceResponse = app
+        .wrap()
+        .query_wasm_smart(
+            token_contract.to_string(),
+            &cw20::Cw20QueryMsg::Balance {
+                address: CREATOR_ADDR.to_string(),
+            },
+        )
+        .unwrap();
+
+    assert_eq!(balance.balance, Uint128::new(1));
+}