Repository: DA0-DA0/dao-contracts
Pull request #344 (2022-06-07 19:36:19)

Update cw-core admin transfer process.
The new process is as follows:

1. The current admin nominates a new admin.
2. For the nomination to take effect the nominated admin must execute
   the `AcceptAdminNomination` method on the core contract.
3. At any point the current admin may withdraw their nomination.
4. The current admin may remove the contract's admin entirely without
   any nomination process.

This was suggested by the Oak Security folks and prevents an admin
from accidentally setting the admin of the contract to an invalid
address.

codecov-commenter: # [Codecov](https://codecov.io/gh/DA0-DA0/dao-contracts/pull/344?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=DA0-DA0) Report
> Merging [#344](https://codecov.io/gh/DA0-DA0/dao-contracts/pull/344?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=DA0-DA0) (87bc9be) into [main](https://codecov.io/gh/DA0-DA0/dao-contracts/commit/3a65522243a83da6459fe1bc8b264eb095582b00?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=DA0-DA0) (3a65522) will **increase** coverage by `0.07%`.
> The diff coverage is `100.00%`.

```diff
@@            Coverage Diff             @@
##             main     #344      +/-   ##
==========================================
+ Coverage   96.90%   96.97%   +0.07%     
==========================================
  Files          37       37              
  Lines        8237     8372     +135     
==========================================
+ Hits         7982     8119     +137     
+ Misses        255      253       -2     
```


| [Impacted Files](https://codecov.io/gh/DA0-DA0/dao-contracts/pull/344?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=DA0-DA0) | Coverage Δ | |
|---|---|---|
| [contracts/cw-core/src/contract.rs](https://codecov.io/gh/DA0-DA0/dao-contracts/pull/344/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=DA0-DA0#diff-Y29udHJhY3RzL2N3LWNvcmUvc3JjL2NvbnRyYWN0LnJz) | `96.79% <100.00%> (+1.03%)` | :arrow_up: |
| [contracts/cw-core/src/tests.rs](https://codecov.io/gh/DA0-DA0/dao-contracts/pull/344/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=DA0-DA0#diff-Y29udHJhY3RzL2N3LWNvcmUvc3JjL3Rlc3RzLnJz) | `100.00% <100.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/DA0-DA0/dao-contracts/pull/344?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/344?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=DA0-DA0). Last update [3a65522...87bc9be](https://codecov.io/gh/DA0-DA0/dao-contracts/pull/344?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.lock b/Cargo.lock
index bf837cfd..2544ea73 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -253,7 +253,7 @@ dependencies = [
 [[package]]
 name = "cw-multi-test"
 version = "0.13.4"
-source = "git+https://github.com/CosmWasm/cw-plus?branch=main#07d1f718a40bb33574f4ac40141aa6a948bc8c24"
+source = "git+https://github.com/CosmWasm/cw-plus?branch=main#44852805788d9c76e3b1faa7a039d5a77c49007b"
 dependencies = [
  "anyhow",
  "cosmwasm-std",
@@ -329,7 +329,6 @@ dependencies = [
  "cw20",
  "cw20-balance-voting",
  "cw20-base",
- "cw20-stake",
  "cw20-staked-balance-voting",
  "cw3",
  "cw4",
@@ -339,6 +338,7 @@ dependencies = [
  "proposal-hooks",
  "schemars",
  "serde",
+ "stake-cw20",
  "testing",
  "thiserror",
  "vote-hooks",
@@ -376,7 +376,7 @@ dependencies = [
 [[package]]
 name = "cw-storage-plus"
 version = "0.13.4"
-source = "git+https://github.com/CosmWasm/cw-plus?branch=main#07d1f718a40bb33574f4ac40141aa6a948bc8c24"
+source = "git+https://github.com/CosmWasm/cw-plus?branch=main#44852805788d9c76e3b1faa7a039d5a77c49007b"
 dependencies = [
  "cosmwasm-std",
  "schemars",
@@ -398,7 +398,7 @@ dependencies = [
 [[package]]
 name = "cw-utils"
 version = "0.13.4"
-source = "git+https://github.com/CosmWasm/cw-plus?branch=main#07d1f718a40bb33574f4ac40141aa6a948bc8c24"
+source = "git+https://github.com/CosmWasm/cw-plus?branch=main#44852805788d9c76e3b1faa7a039d5a77c49007b"
 dependencies = [
  "cosmwasm-std",
  "schemars",
@@ -466,26 +466,6 @@ dependencies = [
  "thiserror",
 ]
 
-[[package]]
-name = "cw20-stake"
-version = "0.2.6"
-dependencies = [
- "anyhow",
- "cosmwasm-schema",
- "cosmwasm-std",
- "cosmwasm-storage",
- "cw-controllers",
- "cw-multi-test 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "cw-storage-plus 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "cw-utils 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "cw2",
- "cw20",
- "cw20-base",
- "schemars",
- "serde",
- "thiserror",
-]
-
 [[package]]
 name = "cw20-staked-balance-voting"
 version = "0.1.0"
@@ -501,9 +481,9 @@ dependencies = [
  "cw2",
  "cw20",
  "cw20-base",
- "cw20-stake",
  "schemars",
  "serde",
+ "stake-cw20",
  "thiserror",
 ]
 
@@ -721,13 +701,13 @@ dependencies = [
 
 [[package]]
 name = "getrandom"
-version = "0.2.7"
+version = "0.2.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
+checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad"
 dependencies = [
  "cfg-if",
  "libc",
- "wasi 0.11.0+wasi-snapshot-preview1",
+ "wasi 0.10.2+wasi-snapshot-preview1",
 ]
 
 [[package]]
@@ -937,7 +917,7 @@ version = "0.6.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
 dependencies = [
- "getrandom 0.2.7",
+ "getrandom 0.2.6",
 ]
 
 [[package]]
@@ -1078,6 +1058,26 @@ dependencies = [
  "der",
 ]
 
+[[package]]
+name = "stake-cw20"
+version = "0.2.6"
+dependencies = [
+ "anyhow",
+ "cosmwasm-schema",
+ "cosmwasm-std",
+ "cosmwasm-storage",
+ "cw-controllers",
+ "cw-multi-test 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cw-storage-plus 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cw-utils 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cw2",
+ "cw20",
+ "cw20-base",
+ "schemars",
+ "serde",
+ "thiserror",
+]
+
 [[package]]
 name = "stake-cw20-external-rewards"
 version = "0.2.6"
@@ -1093,9 +1093,9 @@ dependencies = [
  "cw2",
  "cw20",
  "cw20-base",
- "cw20-stake",
  "schemars",
  "serde",
+ "stake-cw20",
  "thiserror",
 ]
 
@@ -1112,9 +1112,9 @@ dependencies = [
  "cw2",
  "cw20",
  "cw20-base",
- "cw20-stake",
  "schemars",
  "serde",
+ "stake-cw20",
  "thiserror",
 ]
 
@@ -1191,9 +1191,9 @@ dependencies = [
 
 [[package]]
 name = "unicode-ident"
-version = "1.0.1"
+version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c"
+checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee"
 
 [[package]]
 name = "version_check"
@@ -1229,9 +1229,9 @@ checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
 
 [[package]]
 name = "wasi"
-version = "0.11.0+wasi-snapshot-preview1"
+version = "0.10.2+wasi-snapshot-preview1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
 
 [[package]]
 name = "zeroize"
diff --git a/Cargo.toml b/Cargo.toml
index 64f2b8ea..264663d3 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -15,6 +15,5 @@ lto = true
 debug-assertions = false
 panic = 'abort'
 incremental = false
-# Please do not disable these. Doing so will cause overflow checks in
-# all workspace members to stop working. Overflows should be errors. 
 overflow-checks = true
+ 
\ No newline at end of file
diff --git a/README.md b/README.md
index 67da4bf0..26d5f8af 100644
--- a/README.md
+++ b/README.md
@@ -1,23 +1,23 @@
 # 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)
 
-| Audited contracts                                                        | Description                                                |
-| :----------------------------------------------------------------------- | :--------------------------------------------------------- |
-| [cw-core](contracts/cw-core)                                             | The core module for a DAO DAO DAO.                         |
-| [cw-proposal-single](contracts/cw-proposal-single)                       | A proposal module for single choice (yes / no) voting.     |
-| [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.                        |
-
-
-| 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.        |
+| Audited contracts                                                  | Description                                            |
+| :----------------------------------------------------------------- | :----------------------------------------------------- |
+| [cw-core](contracts/cw-core)                                       | The core module for a DAO DAO DAO.                     |
+| [cw-proposal-single](contracts/cw-proposal-single)                 | A proposal module for single choice (yes / no) voting. |
+| [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.       |
+| [stake-cw20](contracts/stake-cw20)                                 | A contract for staking cw20 tokens.                    |
+
+
+| 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/stake-cw20-external-rewards)        | A contract for providing external stakinig rewards.      |
+| [stake-cw20-reward-distributor](contracts/stake-cw20-external-rewards) | A contract for distributing rewards via stake-cw20.      |
 
 Audited contracts have completed [an
 audit](https://github.com/securityDAO/audits/blob/7bb8e4910baaea89fddfc025591658f44adbc27c/cosmwasm/dao-contracts/v0.3%20DAO%20DAO%20audit.pdf)
diff --git a/contracts/cw-core/examples/schema.rs b/contracts/cw-core/examples/schema.rs
index 2b27dc67..5d0616c5 100644
--- a/contracts/cw-core/examples/schema.rs
+++ b/contracts/cw-core/examples/schema.rs
@@ -5,7 +5,10 @@ use cosmwasm_schema::{export_schema, export_schema_with_title, remove_schemas, s
 use cosmwasm_std::Addr;
 use cw_core::{
     msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg},
-    query::{Cw20BalanceResponse, DumpStateResponse, GetItemResponse, PauseInfoResponse},
+    query::{
+        AdminNominationResponse, Cw20BalanceResponse, DumpStateResponse, GetItemResponse,
+        PauseInfoResponse,
+    },
     state::Config,
 };
 use cw_core_interface::voting::{
@@ -29,6 +32,7 @@ fn main() {
     export_schema(&schema_for!(InfoResponse), &out_dir);
     export_schema(&schema_for!(TotalPowerAtHeightResponse), &out_dir);
     export_schema(&schema_for!(VotingPowerAtHeightResponse), &out_dir);
+    export_schema(&schema_for!(AdminNominationResponse), &out_dir);
 
     // Auto TS code generation expects the query return type as QueryNameResponse
     // Here we map query resonses to the correct name
diff --git a/contracts/cw-core/schema/admin_nomination_response.json b/contracts/cw-core/schema/admin_nomination_response.json
new file mode 100644
index 00000000..7e2b4af9
--- /dev/null
+++ b/contracts/cw-core/schema/admin_nomination_response.json
@@ -0,0 +1,25 @@
+{
+  "$schema": "http://json-schema.org/draft-07/schema#",
+  "title": "AdminNominationResponse",
+  "description": "Returned by the `AdminNomination` query.",
+  "type": "object",
+  "properties": {
+    "nomination": {
+      "description": "The currently nominated admin or None if no nomination is pending.",
+      "anyOf": [
+        {
+          "$ref": "#/definitions/Addr"
+        },
+        {
+          "type": "null"
+        }
+      ]
+    }
+  },
+  "definitions": {
+    "Addr": {
+      "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
+      "type": "string"
+    }
+  }
+}
diff --git a/contracts/cw-core/schema/cw20_balances_response.json b/contracts/cw-core/schema/cw20_balances_response.json
index 3dd46383..34300995 100644
--- a/contracts/cw-core/schema/cw20_balances_response.json
+++ b/contracts/cw-core/schema/cw20_balances_response.json
@@ -1,7 +1,7 @@
 {
   "$schema": "http://json-schema.org/draft-07/schema#",
   "title": "Cw20BalancesResponse",
-  "description": "Returned by Cw20Balances query.",
+  "description": "Returned by the `Cw20Balances` query.",
   "type": "object",
   "required": [
     "addr",
diff --git a/contracts/cw-core/schema/dump_state_response.json b/contracts/cw-core/schema/dump_state_response.json
index 232555e1..e363b55b 100644
--- a/contracts/cw-core/schema/dump_state_response.json
+++ b/contracts/cw-core/schema/dump_state_response.json
@@ -4,6 +4,7 @@
   "description": "Relevant state for the governance module. Returned by the `DumpState` query.",
   "type": "object",
   "required": [
+    "admin",
     "config",
     "pause_info",
     "proposal_modules",
@@ -13,12 +14,9 @@
   "properties": {
     "admin": {
       "description": "Optional DAO Admin",
-      "anyOf": [
+      "allOf": [
         {
           "$ref": "#/definitions/Addr"
-        },
-        {
-          "type": "null"
         }
       ]
     },
diff --git a/contracts/cw-core/schema/execute_msg.json b/contracts/cw-core/schema/execute_msg.json
index 7aee7c6e..de05dbbe 100644
--- a/contracts/cw-core/schema/execute_msg.json
+++ b/contracts/cw-core/schema/execute_msg.json
@@ -144,23 +144,19 @@
       "additionalProperties": false
     },
     {
-      "description": "Callable by the Admin, if one is configured, changes the current admin. Setting to None removes the Admin.",
+      "description": "Callable by the admin of the contract. If ADMIN is None the admin is set as the contract itself so that it may be updated later by vote. If ADMIN is Some a new admin is proposed and that new admin may become the admin by executing the `AcceptAdminNomination` message.\n\nIf there is already a pending admin nomination the `WithdrawAdminNomination` message must be executed before a new admin may be nominated.",
       "type": "object",
       "required": [
-        "update_admin"
+        "nominate_admin"
       ],
       "properties": {
-        "update_admin": {
+        "nominate_admin": {
           "type": "object",
           "properties": {
             "admin": {
-              "anyOf": [
-                {
-                  "$ref": "#/definitions/Addr"
-                },
-                {
-                  "type": "null"
-                }
+              "type": [
+                "string",
+                "null"
               ]
             }
           }
@@ -168,6 +164,32 @@
       },
       "additionalProperties": false
     },
+    {
+      "description": "Callable by a nominated admin. Admins are nominated via the `NominateAdmin` message. Accepting a nomination will make the nominated address the new admin.\n\nRequiring that the new admin accepts the nomination before becoming the admin protects against a typo causing the admin to change to an invalid address.",
+      "type": "object",
+      "required": [
+        "accept_admin_nomination"
+      ],
+      "properties": {
+        "accept_admin_nomination": {
+          "type": "object"
+        }
+      },
+      "additionalProperties": false
+    },
+    {
+      "description": "Callable by the current admin. Withdraws the current admin nomination.",
+      "type": "object",
+      "required": [
+        "withdraw_admin_nomination"
+      ],
+      "properties": {
+        "withdraw_admin_nomination": {
+          "type": "object"
+        }
+      },
+      "additionalProperties": false
+    },
     {
       "description": "Callable by the core contract. Replaces the current governance contract config with the provided config.",
       "type": "object",
@@ -305,10 +327,6 @@
     }
   ],
   "definitions": {
-    "Addr": {
-      "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
-      "type": "string"
-    },
     "Admin": {
       "description": "Information about the admin of a contract.",
       "oneOf": [
diff --git a/contracts/cw-core/schema/instantiate_msg.json b/contracts/cw-core/schema/instantiate_msg.json
index 41f7da75..df235663 100644
--- a/contracts/cw-core/schema/instantiate_msg.json
+++ b/contracts/cw-core/schema/instantiate_msg.json
@@ -12,7 +12,7 @@
   ],
   "properties": {
     "admin": {
-      "description": "Optional Admin with the ability to execute DAO messages directly Useful for building SubDAOs completely controlled by a parent DAO.",
+      "description": "Optional Admin with the ability to execute DAO messages directly. Useful for building SubDAOs controlled by a parent DAO. If no admin is specified the contract is set as its own admin so that the admin may be updated later by governance.",
       "type": [
         "string",
         "null"
diff --git a/contracts/cw-core/schema/query_msg.json b/contracts/cw-core/schema/query_msg.json
index 9ce26da7..560f1c7f 100644
--- a/contracts/cw-core/schema/query_msg.json
+++ b/contracts/cw-core/schema/query_msg.json
@@ -3,7 +3,7 @@
   "title": "QueryMsg",
   "oneOf": [
     {
-      "description": "Get's the DAO's admin (if configured).",
+      "description": "Get's the DAO's admin. Returns `Addr`.",
       "type": "object",
       "required": [
         "admin"
@@ -15,6 +15,19 @@
       },
       "additionalProperties": false
     },
+    {
+      "description": "Get's the currently nominated admin (if any). Returns `AdminNominationResponse`.",
+      "type": "object",
+      "required": [
+        "admin_nomination"
+      ],
+      "properties": {
+        "admin_nomination": {
+          "type": "object"
+        }
+      },
+      "additionalProperties": false
+    },
     {
       "description": "Gets the contract's config. Returns Config.",
       "type": "object",
diff --git a/contracts/cw-core/src/contract.rs b/contracts/cw-core/src/contract.rs
index b2ff2eae..2c5d3e5c 100644
--- a/contracts/cw-core/src/contract.rs
+++ b/contracts/cw-core/src/contract.rs
@@ -15,9 +15,13 @@ use crate::error::ContractError;
 use crate::msg::{
     ExecuteMsg, InitialItem, InstantiateMsg, MigrateMsg, ModuleInstantiateInfo, QueryMsg,
 };
-use crate::query::{Cw20BalanceResponse, DumpStateResponse, GetItemResponse, PauseInfoResponse};
+use crate::query::{
+    AdminNominationResponse, Cw20BalanceResponse, DumpStateResponse, GetItemResponse,
+    PauseInfoResponse,
+};
 use crate::state::{
-    Config, ADMIN, CONFIG, CW20_LIST, CW721_LIST, ITEMS, PAUSED, PROPOSAL_MODULES, VOTING_MODULE,
+    Config, ADMIN, CONFIG, CW20_LIST, CW721_LIST, ITEMS, NOMINATED_ADMIN, PAUSED, PROPOSAL_MODULES,
+    VOTING_MODULE,
 };
 
 // version info for migration info
@@ -49,7 +53,9 @@ pub fn instantiate(
     let admin = msg
         .admin
         .map(|human| deps.api.addr_validate(&human))
-        .transpose()?;
+        .transpose()?
+        // If no admin is specified, the contract is its own admin.
+        .unwrap_or_else(|| env.contract.address.clone());
     ADMIN.save(deps.storage, &admin)?;
 
     let vote_module_msg = msg
@@ -105,7 +111,6 @@ pub fn execute(
         ExecuteMsg::ReceiveNft(_) => execute_receive_cw721(deps, info.sender),
         ExecuteMsg::RemoveItem { key } => execute_remove_item(deps, env, info.sender, key),
         ExecuteMsg::SetItem { key, addr } => execute_set_item(deps, env, info.sender, key, addr),
-        ExecuteMsg::UpdateAdmin { admin } => execute_update_admin(deps, info.sender, admin),
         ExecuteMsg::UpdateConfig { config } => {
             execute_update_config(deps, env, info.sender, config)
         }
@@ -121,6 +126,13 @@ pub fn execute(
         ExecuteMsg::UpdateProposalModules { to_add, to_remove } => {
             execute_update_proposal_modules(deps, env, info.sender, to_add, to_remove)
         }
+        ExecuteMsg::NominateAdmin { admin } => {
+            execute_nominate_admin(deps, env, info.sender, admin)
+        }
+        ExecuteMsg::AcceptAdminNomination {} => execute_accept_admin_nomination(deps, info.sender),
+        ExecuteMsg::WithdrawAdminNomination {} => {
+            execute_withdraw_admin_nomination(deps, info.sender)
+        }
     }
 }
 
@@ -152,19 +164,14 @@ pub fn execute_admin_msgs(
 ) -> Result<Response, ContractError> {
     let admin = ADMIN.load(deps.storage)?;
 
-    match admin {
-        Some(admin) => {
-            // Check if the sender is the DAO Admin
-            if sender != admin {
-                return Err(ContractError::Unauthorized {});
-            }
-
-            Ok(Response::default()
-                .add_attribute("action", "execute_admin_msgs")
-                .add_messages(msgs))
-        }
-        None => Err(ContractError::NoAdmin {}),
+    // Check if the sender is the DAO Admin
+    if sender != admin {
+        return Err(ContractError::Unauthorized {});
     }
+
+    Ok(Response::default()
+        .add_attribute("action", "execute_admin_msgs")
+        .add_messages(msgs))
 }
 
 pub fn execute_proposal_hook(
@@ -182,37 +189,79 @@ pub fn execute_proposal_hook(
         .add_messages(msgs))
 }
 
-pub fn execute_update_admin(
+pub fn execute_nominate_admin(
     deps: DepsMut,
+    env: Env,
     sender: Addr,
-    admin: Option<Addr>,
+    nomination: Option<String>,
 ) -> Result<Response, ContractError> {
+    let nomination = nomination.map(|h| deps.api.addr_validate(&h)).transpose()?;
+
     let current_admin = ADMIN.load(deps.storage)?;
+    if current_admin != sender {
+        return Err(ContractError::Unauthorized {});
+    }
 
-    match current_admin {
-        Some(current_admin) => {
-            // Check sender is the DAO Admin
-            if sender != current_admin {
-                return Err(ContractError::Unauthorized {});
-            }
+    let current_nomination = NOMINATED_ADMIN.may_load(deps.storage)?;
+    if current_nomination.is_some() {
+        return Err(ContractError::PendingNomination {});
+    }
 
-            // Save the new DAO Admin (which may be set to None)
-            ADMIN.save(deps.storage, &admin)?;
-
-            Ok(Response::default()
-                .add_attribute("action", "execute_update_admin")
-                .add_attribute(
-                    "new_admin",
-                    admin
-                        .map(|a| a.into_string())
-                        .unwrap_or_else(|| "None".to_string()),
-                ))
-        }
-        None => {
-            // If no DAO admin is configured, return unauthorized
-            Err(ContractError::Unauthorized {})
-        }
+    match &nomination {
+        Some(nomination) => NOMINATED_ADMIN.save(deps.storage, nomination)?,
+        // If no admin set to default of the contract. This allows the
+        // contract to later set a new admin via governance.
+        None => ADMIN.save(deps.storage, &env.contract.address)?,
+    }
+
+    Ok(Response::default()
+        .add_attribute("action", "execute_nominate_admin")
+        .add_attribute(
+            "nomination",
+            nomination
+                .map(|n| n.to_string())
+                .unwrap_or_else(|| "None".to_string()),
+        ))
+}
+
+pub fn execute_accept_admin_nomination(
+    deps: DepsMut,
+    sender: Addr,
+) -> Result<Response, ContractError> {
+    let nomination = NOMINATED_ADMIN
+        .may_load(deps.storage)?
+        .ok_or(ContractError::NoAdminNomination {})?;
+    if sender != nomination {
+        return Err(ContractError::Unauthorized {});
     }
+    NOMINATED_ADMIN.remove(deps.storage);
+    ADMIN.save(deps.storage, &nomination)?;
+
+    Ok(Response::default()
+        .add_attribute("action", "execute_accept_admin_nomination")
+        .add_attribute("new_admin", sender))
+}
+
+pub fn execute_withdraw_admin_nomination(
+    deps: DepsMut,
+    sender: Addr,
+) -> Result<Response, ContractError> {
+    let admin = ADMIN.load(deps.storage)?;
+    if admin != sender {
+        return Err(ContractError::Unauthorized {});
+    }
+
+    // Check that there is indeed a nomination to withdraw.
+    let current_nomination = NOMINATED_ADMIN.may_load(deps.storage)?;
+    if current_nomination.is_none() {
+        return Err(ContractError::NoAdminNomination {});
+    }
+
+    NOMINATED_ADMIN.remove(deps.storage);
+
+    Ok(Response::default()
+        .add_attribute("action", "execute_withdraw_admin_nomination")
+        .add_attribute("sender", sender))
 }
 
 pub fn execute_update_config(
@@ -417,6 +466,7 @@ pub fn execute_receive_cw721(deps: DepsMut, sender: Addr) -> Result<Response, Co
 pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult<Binary> {
     match msg {
         QueryMsg::Admin {} => query_admin(deps),
+        QueryMsg::AdminNomination {} => query_admin_nomination(deps),
         QueryMsg::Config {} => query_config(deps),
         QueryMsg::Cw20TokenList { start_at, limit } => query_cw20_list(deps, start_at, limit),
         QueryMsg::Cw20Balances { start_at, limit } => {
@@ -444,6 +494,11 @@ pub fn query_admin(deps: Deps) -> StdResult<Binary> {
     to_binary(&admin)
 }
 
+pub fn query_admin_nomination(deps: Deps) -> StdResult<Binary> {
+    let nomination = NOMINATED_ADMIN.may_load(deps.storage)?;
+    to_binary(&AdminNominationResponse { nomination })
+}
+
 pub fn query_config(deps: Deps) -> StdResult<Binary> {
     let config = CONFIG.load(deps.storage)?;
     to_binary(&config)
diff --git a/contracts/cw-core/src/error.rs b/contracts/cw-core/src/error.rs
index d9a551d1..dfb6fc66 100644
--- a/contracts/cw-core/src/error.rs
+++ b/contracts/cw-core/src/error.rs
@@ -10,9 +10,6 @@ pub enum ContractError {
     #[error("Unauthorized.")]
     Unauthorized {},
 
-    #[error("DAO does not have an admin configured.")]
-    NoAdmin {},
-
     #[error("The contract is paused.")]
     Paused {},
 
@@ -34,9 +31,14 @@ pub enum ContractError {
     #[error("Unsigned integer overflow.")]
     Overflow {},
 
-    #[error("You can only instantiate {0} items during instantiation, but you tried to instantiate {1}.")]
-    TooManyItems(u64, usize),
-
     #[error("Key is missing from storage")]
     KeyMissing {},
+
+    #[error("No pending admin nomination.")]
+    NoAdminNomination {},
+
+    #[error(
+        "The pending admin nomination must be withdrawn before a new nomination can be created."
+    )]
+    PendingNomination {},
 }
diff --git a/contracts/cw-core/src/msg.rs b/contracts/cw-core/src/msg.rs
index 22e61114..5ef1991f 100644
--- a/contracts/cw-core/src/msg.rs
+++ b/contracts/cw-core/src/msg.rs
@@ -1,4 +1,4 @@
-use cosmwasm_std::{Addr, Binary, CosmosMsg, Empty};
+use cosmwasm_std::{Binary, CosmosMsg, Empty};
 use cw_utils::Duration;
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
@@ -43,8 +43,10 @@ pub struct InitialItem {
 
 #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
 pub struct InstantiateMsg {
-    /// Optional Admin with the ability to execute DAO messages directly
-    /// Useful for building SubDAOs completely controlled by a parent DAO.
+    /// Optional Admin with the ability to execute DAO messages
+    /// directly. Useful for building SubDAOs controlled by a parent
+    /// DAO. If no admin is specified the contract is set as its own
+    /// admin so that the admin may be updated later by governance.
     pub admin: Option<String>,
     /// The name of the core contract.
     pub name: String,
@@ -100,9 +102,27 @@ pub enum ExecuteMsg {
     /// item already exists the existing value is overriden. If the
     /// item does not exist a new item is added.
     SetItem { key: String, addr: String },
-    /// Callable by the Admin, if one is configured, changes the
-    /// current admin. Setting to None removes the Admin.
-    UpdateAdmin { admin: Option<Addr> },
+    /// Callable by the admin of the contract. If ADMIN is None the
+    /// admin is set as the contract itself so that it may be updated
+    /// later by vote. If ADMIN is Some a new admin is proposed and
+    /// that new admin may become the admin by executing the
+    /// `AcceptAdminNomination` message.
+    ///
+    /// If there is already a pending admin nomination the
+    /// `WithdrawAdminNomination` message must be executed before a
+    /// new admin may be nominated.
+    NominateAdmin { admin: Option<String> },
+    /// Callable by a nominated admin. Admins are nominated via the
+    /// `NominateAdmin` message. Accepting a nomination will make the
+    /// nominated address the new admin.
+    ///
+    /// Requiring that the new admin accepts the nomination before
+    /// becoming the admin protects against a typo causing the admin
+    /// to change to an invalid address.
+    AcceptAdminNomination {},
+    /// Callable by the current admin. Withdraws the current admin
+    /// nomination.
+    WithdrawAdminNomination {},
     /// Callable by the core contract. Replaces the current
     /// governance contract config with the provided config.
     UpdateConfig { config: Config },
@@ -133,8 +153,11 @@ pub enum ExecuteMsg {
 #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
 #[serde(rename_all = "snake_case")]
 pub enum QueryMsg {
-    /// Get's the DAO's admin (if configured).
+    /// Get's the DAO's admin. Returns `Addr`.
     Admin {},
+    /// Get's the currently nominated admin (if any). Returns
+    /// `AdminNominationResponse`.
+    AdminNomination {},
     /// Gets the contract's config. Returns Config.
     Config {},
     /// Gets the token balance for each cw20 registered with the
diff --git a/contracts/cw-core/src/query.rs b/contracts/cw-core/src/query.rs
index ee10975d..2aa4bf97 100644
--- a/contracts/cw-core/src/query.rs
+++ b/contracts/cw-core/src/query.rs
@@ -11,7 +11,7 @@ use crate::state::Config;
 #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
 pub struct DumpStateResponse {
     /// Optional DAO Admin
-    pub admin: Option<Addr>,
+    pub admin: Addr,
     /// The governance contract's config.
     pub config: Config,
     // True if the contract is currently paused.
@@ -40,7 +40,7 @@ pub struct GetItemResponse {
     pub item: Option<String>,
 }
 
-/// Returned by Cw20Balances query.
+/// Returned by the `Cw20Balances` query.
 #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
 pub struct Cw20BalanceResponse {
     /// The address of the token.
@@ -48,3 +48,11 @@ pub struct Cw20BalanceResponse {
     /// The contract's balance.
     pub balance: Uint128,
 }
+
+/// Returned by the `AdminNomination` query.
+#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
+pub struct AdminNominationResponse {
+    /// The currently nominated admin or None if no nomination is
+    /// pending.
+    pub nomination: Option<Addr>,
+}
diff --git a/contracts/cw-core/src/state.rs b/contracts/cw-core/src/state.rs
index 705ebb0b..f2c267dc 100644
--- a/contracts/cw-core/src/state.rs
+++ b/contracts/cw-core/src/state.rs
@@ -22,7 +22,23 @@ pub struct Config {
     pub automatically_add_cw721s: bool,
 }
 
-pub const ADMIN: Item<Option<Addr>> = Item::new("admin");
+/// The admin of the contract. Typically a DAO. The contract admin may
+/// unilaterally execute messages on this contract.
+///
+/// In cases where no admin is wanted the admin should be set to the
+/// contract itself. This will happen by default if no admin is
+/// specified in `NominateAdmin` and instantiate messages.
+pub const ADMIN: Item<Addr> = Item::new("admin");
+
+/// A new admin that has been nominated by the current admin. The
+/// nominated admin must accept the proposal before becoming the admin
+/// themselves.
+///
+/// NOTE: If no admin is currently nominated this will not have a
+/// value set. To load this value, use
+/// `NOMINATED_ADMIN.may_load(deps.storage)`.
+pub const NOMINATED_ADMIN: Item<Addr> = Item::new("nominated_admin");
+
 pub const CONFIG: Item<Config> = Item::new("config");
 
 pub const PAUSED: Item<Expiration> = Item::new("paused");
diff --git a/contracts/cw-proposal-single/Cargo.toml b/contracts/cw-proposal-single/Cargo.toml
index a43d4584..1b5d0b51 100644
--- a/contracts/cw-proposal-single/Cargo.toml
+++ b/contracts/cw-proposal-single/Cargo.toml
@@ -54,7 +54,7 @@ cw4-voting = { path = "../cw4-voting", version = "*" }
 cw20-balance-voting = { path = "../../debug/cw20-balance-voting", version = "*" }
 cw20-staked-balance-voting = { path = "../cw20-staked-balance-voting", version = "*" }
 testing = { version = "*", path = "../../packages/testing" }
-cw20-stake = { path= "../cw20-stake", version = "*" }
+stake-cw20 = { path="../stake-cw20", version = "*" }
 cw20-base = "0.13"
 cw4 = "0.13"
 cw4-group = "0.13"
diff --git a/contracts/cw-proposal-single/schema/config_response.json b/contracts/cw-proposal-single/schema/config_response.json
index 4cf3647c..3bc0e0fa 100644
--- a/contracts/cw-proposal-single/schema/config_response.json
+++ b/contracts/cw-proposal-single/schema/config_response.json
@@ -42,17 +42,6 @@
         }
       ]
     },
-    "min_voting_period": {
-      "description": "The minimum amount of time a proposal must be open before passing. A proposal may fail before this amount of time has elapsed, but it will not pass. This can be useful for preventing governance attacks wherein an attacker aquires a large number of tokens and forces a proposal through.",
-      "anyOf": [
-        {
-          "$ref": "#/definitions/Duration"
-        },
-        {
-          "type": "null"
-        }
-      ]
-    },
     "only_members_execute": {
       "description": "If set to true only members may execute passed proposals. Otherwise, any address may execute a passed proposal.",
       "type": "boolean"
diff --git a/contracts/cw-proposal-single/schema/execute_msg.json b/contracts/cw-proposal-single/schema/execute_msg.json
index cd45e394..16b12b46 100644
--- a/contracts/cw-proposal-single/schema/execute_msg.json
+++ b/contracts/cw-proposal-single/schema/execute_msg.json
@@ -162,17 +162,6 @@
                 }
               ]
             },
-            "min_voting_period": {
-              "description": "The minimum amount of time a proposal must be open before passing. A proposal may fail before this amount of time has elapsed, but it will not pass. This can be useful for preventing governance attacks wherein an attacker aquires a large number of tokens and forces a proposal through.",
-              "anyOf": [
-                {
-                  "$ref": "#/definitions/Duration"
-                },
-                {
-                  "type": "null"
-                }
-              ]
-            },
             "only_members_execute": {
               "description": "If set to true only members may execute passed proposals. Otherwise, any address may execute a passed proposal. Applies to all outstanding and future proposals.",
               "type": "boolean"
diff --git a/contracts/cw-proposal-single/schema/instantiate_msg.json b/contracts/cw-proposal-single/schema/instantiate_msg.json
index 3fcee7ab..a33699b6 100644
--- a/contracts/cw-proposal-single/schema/instantiate_msg.json
+++ b/contracts/cw-proposal-single/schema/instantiate_msg.json
@@ -32,17 +32,6 @@
         }
       ]
     },
-    "min_voting_period": {
-      "description": "The minimum amount of time a proposal must be open before passing. A proposal may fail before this amount of time has elapsed, but it will not pass. This can be useful for preventing governance attacks wherein an attacker aquires a large number of tokens and forces a proposal through.",
-      "anyOf": [
-        {
-          "$ref": "#/definitions/Duration"
-        },
-        {
-          "type": "null"
-        }
-      ]
-    },
     "only_members_execute": {
       "description": "If set to true only members may execute passed proposals. Otherwise, any address may execute a passed proposal.",
       "type": "boolean"
diff --git a/contracts/cw-proposal-single/schema/list_proposals_response.json b/contracts/cw-proposal-single/schema/list_proposals_response.json
index acc5ab41..ade96a90 100644
--- a/contracts/cw-proposal-single/schema/list_proposals_response.json
+++ b/contracts/cw-proposal-single/schema/list_proposals_response.json
@@ -591,41 +591,18 @@
           "type": "string"
         },
         "expiration": {
-          "description": "The the time at which this proposal will expire and close for additional votes.",
-          "allOf": [
-            {
-              "$ref": "#/definitions/Expiration"
-            }
-          ]
-        },
-        "min_voting_period": {
-          "description": "The minimum amount of time this proposal must remain open for voting. The proposal may not pass unless this is expired or None.",
-          "anyOf": [
-            {
-              "$ref": "#/definitions/Expiration"
-            },
-            {
-              "type": "null"
-            }
-          ]
+          "$ref": "#/definitions/Expiration"
         },
         "msgs": {
-          "description": "The messages that will be executed should this proposal pass.",
           "type": "array",
           "items": {
             "$ref": "#/definitions/CosmosMsg_for_Empty"
           }
         },
         "proposer": {
-          "description": "The address that created this proposal.",
-          "allOf": [
-            {
-              "$ref": "#/definitions/Addr"
-            }
-          ]
+          "$ref": "#/definitions/Addr"
         },
         "start_height": {
-          "description": "The block height at which this proposal was created. Voting power queries should query for voting power at this block height.",
           "type": "integer",
           "format": "uint64",
           "minimum": 0.0
@@ -634,23 +611,13 @@
           "$ref": "#/definitions/Status"
         },
         "threshold": {
-          "description": "The threshold at which this proposal will pass.",
-          "allOf": [
-            {
-              "$ref": "#/definitions/Threshold"
-            }
-          ]
+          "$ref": "#/definitions/Threshold"
         },
         "title": {
           "type": "string"
         },
         "total_power": {
-          "description": "The total amount of voting power at the time of this proposal's creation.",
-          "allOf": [
-            {
-              "$ref": "#/definitions/Uint128"
-            }
-          ]
+          "$ref": "#/definitions/Uint128"
         },
         "votes": {
           "$ref": "#/definitions/Votes"
diff --git a/contracts/cw-proposal-single/schema/proposal_response.json b/contracts/cw-proposal-single/schema/proposal_response.json
index c2c77390..a0ef8ca5 100644
--- a/contracts/cw-proposal-single/schema/proposal_response.json
+++ b/contracts/cw-proposal-single/schema/proposal_response.json
@@ -594,41 +594,18 @@
           "type": "string"
         },
         "expiration": {
-          "description": "The the time at which this proposal will expire and close for additional votes.",
-          "allOf": [
-            {
-              "$ref": "#/definitions/Expiration"
-            }
-          ]
-        },
-        "min_voting_period": {
-          "description": "The minimum amount of time this proposal must remain open for voting. The proposal may not pass unless this is expired or None.",
-          "anyOf": [
-            {
-              "$ref": "#/definitions/Expiration"
-            },
-            {
-              "type": "null"
-            }
-          ]
+          "$ref": "#/definitions/Expiration"
         },
         "msgs": {
-          "description": "The messages that will be executed should this proposal pass.",
           "type": "array",
           "items": {
             "$ref": "#/definitions/CosmosMsg_for_Empty"
           }
         },
         "proposer": {
-          "description": "The address that created this proposal.",
-          "allOf": [
-            {
-              "$ref": "#/definitions/Addr"
-            }
-          ]
+          "$ref": "#/definitions/Addr"
         },
         "start_height": {
-          "description": "The block height at which this proposal was created. Voting power queries should query for voting power at this block height.",
           "type": "integer",
           "format": "uint64",
           "minimum": 0.0
@@ -637,23 +614,13 @@
           "$ref": "#/definitions/Status"
         },
         "threshold": {
-          "description": "The threshold at which this proposal will pass.",
-          "allOf": [
-            {
-              "$ref": "#/definitions/Threshold"
-            }
-          ]
+          "$ref": "#/definitions/Threshold"
         },
         "title": {
           "type": "string"
         },
         "total_power": {
-          "description": "The total amount of voting power at the time of this proposal's creation.",
-          "allOf": [
-            {
-              "$ref": "#/definitions/Uint128"
-            }
-          ]
+          "$ref": "#/definitions/Uint128"
         },
         "votes": {
           "$ref": "#/definitions/Votes"
diff --git a/contracts/cw-proposal-single/schema/reverse_proposals_response.json b/contracts/cw-proposal-single/schema/reverse_proposals_response.json
index 3c2e3daf..fa543295 100644
--- a/contracts/cw-proposal-single/schema/reverse_proposals_response.json
+++ b/contracts/cw-proposal-single/schema/reverse_proposals_response.json
@@ -591,41 +591,18 @@
           "type": "string"
         },
         "expiration": {
-          "description": "The the time at which this proposal will expire and close for additional votes.",
-          "allOf": [
-            {
-              "$ref": "#/definitions/Expiration"
-            }
-          ]
-        },
-        "min_voting_period": {
-          "description": "The minimum amount of time this proposal must remain open for voting. The proposal may not pass unless this is expired or None.",
-          "anyOf": [
-            {
-              "$ref": "#/definitions/Expiration"
-            },
-            {
-              "type": "null"
-            }
-          ]
+          "$ref": "#/definitions/Expiration"
         },
         "msgs": {
-          "description": "The messages that will be executed should this proposal pass.",
           "type": "array",
           "items": {
             "$ref": "#/definitions/CosmosMsg_for_Empty"
           }
         },
         "proposer": {
-          "description": "The address that created this proposal.",
-          "allOf": [
-            {
-              "$ref": "#/definitions/Addr"
-            }
-          ]
+          "$ref": "#/definitions/Addr"
         },
         "start_height": {
-          "description": "The block height at which this proposal was created. Voting power queries should query for voting power at this block height.",
           "type": "integer",
           "format": "uint64",
           "minimum": 0.0
@@ -634,23 +611,13 @@
           "$ref": "#/definitions/Status"
         },
         "threshold": {
-          "description": "The threshold at which this proposal will pass.",
-          "allOf": [
-            {
-              "$ref": "#/definitions/Threshold"
-            }
-          ]
+          "$ref": "#/definitions/Threshold"
         },
         "title": {
           "type": "string"
         },
         "total_power": {
-          "description": "The total amount of voting power at the time of this proposal's creation.",
-          "allOf": [
-            {
-              "$ref": "#/definitions/Uint128"
-            }
-          ]
+          "$ref": "#/definitions/Uint128"
         },
         "votes": {
           "$ref": "#/definitions/Votes"
diff --git a/contracts/cw-proposal-single/src/contract.rs b/contracts/cw-proposal-single/src/contract.rs
index 194e5dba..a410e8af 100644
--- a/contracts/cw-proposal-single/src/contract.rs
+++ b/contracts/cw-proposal-single/src/contract.rs
@@ -24,7 +24,7 @@ use crate::{
         get_deposit_msg, get_return_deposit_msg, Ballot, Config, BALLOTS, CONFIG, PROPOSALS,
         PROPOSAL_COUNT, PROPOSAL_HOOKS, VOTE_HOOKS,
     },
-    utils::{get_total_power, get_voting_power, validate_voting_period},
+    utils::{get_total_power, get_voting_power},
 };
 
 const CONTRACT_NAME: &str = "crates.io:cw-govmod-single";
@@ -51,13 +51,9 @@ pub fn instantiate(
         .map(|info| info.into_checked(deps.as_ref(), dao.clone()))
         .transpose()?;
 
-    let (min_voting_period, max_voting_period) =
-        validate_voting_period(msg.min_voting_period, msg.max_voting_period)?;
-
     let config = Config {
         threshold: msg.threshold,
-        max_voting_period,
-        min_voting_period,
+        max_voting_period: msg.max_voting_period,
         only_members_execute: msg.only_members_execute,
         dao: dao.clone(),
         deposit_info,
@@ -93,7 +89,6 @@ pub fn execute(
         ExecuteMsg::UpdateConfig {
             threshold,
             max_voting_period,
-            min_voting_period,
             only_members_execute,
             allow_revoting,
             dao,
@@ -103,7 +98,6 @@ pub fn execute(
             info,
             threshold,
             max_voting_period,
-            min_voting_period,
             only_members_execute,
             allow_revoting,
             dao,
@@ -172,7 +166,6 @@ pub fn execute_propose(
             description,
             proposer: sender.clone(),
             start_height: env.block.height,
-            min_voting_period: config.min_voting_period.map(|min| min.after(&env.block)),
             expiration,
             threshold: config.threshold,
             total_power,
@@ -431,7 +424,6 @@ pub fn execute_update_config(
     info: MessageInfo,
     threshold: Threshold,
     max_voting_period: Duration,
-    min_voting_period: Option<Duration>,
     only_members_execute: bool,
     allow_revoting: bool,
     dao: String,
@@ -450,15 +442,11 @@ pub fn execute_update_config(
         .map(|info| info.into_checked(deps.as_ref(), dao.clone()))
         .transpose()?;
 
-    let (min_voting_period, max_voting_period) =
-        validate_voting_period(min_voting_period, max_voting_period)?;
-
     CONFIG.save(
         deps.storage,
         &Config {
             threshold,
             max_voting_period,
-            min_voting_period,
             only_members_execute,
             allow_revoting,
             dao,
diff --git a/contracts/cw-proposal-single/src/error.rs b/contracts/cw-proposal-single/src/error.rs
index 297bd6b4..e1edd83a 100644
--- a/contracts/cw-proposal-single/src/error.rs
+++ b/contracts/cw-proposal-single/src/error.rs
@@ -56,10 +56,4 @@ pub enum ContractError {
 
     #[error("The DAO is currently inactive, you cannot create proposals")]
     InactiveDao {},
-
-    #[error("min_voting_period and max_voting_period must have the same units (height or time)")]
-    DurationUnitsConflict {},
-
-    #[error("Min voting period must be less than or equal to max voting period")]
-    InvalidMinVotingPeriod {},
 }
diff --git a/contracts/cw-proposal-single/src/msg.rs b/contracts/cw-proposal-single/src/msg.rs
index f281f850..23d38328 100644
--- a/contracts/cw-proposal-single/src/msg.rs
+++ b/contracts/cw-proposal-single/src/msg.rs
@@ -13,12 +13,6 @@ pub struct InstantiateMsg {
     /// The default maximum amount of time a proposal may be voted on
     /// before expiring.
     pub max_voting_period: Duration,
-    /// The minimum amount of time a proposal must be open before
-    /// passing. A proposal may fail before this amount of time has
-    /// elapsed, but it will not pass. This can be useful for
-    /// preventing governance attacks wherein an attacker aquires a
-    /// large number of tokens and forces a proposal through.
-    pub min_voting_period: Option<Duration>,
     /// If set to true only members may execute passed
     /// proposals. Otherwise, any address may execute a passed
     /// proposal.
@@ -105,12 +99,6 @@ pub enum ExecuteMsg {
         /// on before expiring. This will only apply to proposals
         /// created after the config update.
         max_voting_period: Duration,
-        /// The minimum amount of time a proposal must be open before
-        /// passing. A proposal may fail before this amount of time has
-        /// elapsed, but it will not pass. This can be useful for
-        /// preventing governance attacks wherein an attacker aquires a
-        /// large number of tokens and forces a proposal through.
-        min_voting_period: Option<Duration>,
         /// If set to true only members may execute passed
         /// proposals. Otherwise, any address may execute a passed
         /// proposal. Applies to all outstanding and future proposals.
diff --git a/contracts/cw-proposal-single/src/proposal.rs b/contracts/cw-proposal-single/src/proposal.rs
index b054600b..b0266302 100644
--- a/contracts/cw-proposal-single/src/proposal.rs
+++ b/contracts/cw-proposal-single/src/proposal.rs
@@ -13,25 +13,14 @@ use crate::{
 pub struct Proposal {
     pub title: String,
     pub description: String,
-    /// The address that created this proposal.
     pub proposer: Addr,
-    /// The block height at which this proposal was created. Voting
-    /// power queries should query for voting power at this block
-    /// height.
+
     pub start_height: u64,
-    /// The minimum amount of time this proposal must remain open for
-    /// voting. The proposal may not pass unless this is expired or
-    /// None.
-    pub min_voting_period: Option<Expiration>,
-    /// The the time at which this proposal will expire and close for
-    /// additional votes.
     pub expiration: Expiration,
-    /// The threshold at which this proposal will pass.
+
     pub threshold: Threshold,
-    /// The total amount of voting power at the time of this
-    /// proposal's creation.
     pub total_power: Uint128,
-    /// The messages that will be executed should this proposal pass.
+
     pub msgs: Vec<CosmosMsg<Empty>>,
 
     pub status: Status,
@@ -129,16 +118,6 @@ impl Proposal {
         if self.allow_revoting && !self.expiration.is_expired(block) {
             return 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
-        // single actor accumulates enough tokens to unilaterally pass
-        // proposals.
-        if let Some(min) = self.min_voting_period {
-            if !min.is_expired(block) {
-                return false;
-            }
-        }
 
         match self.threshold {
             Threshold::AbsolutePercentage { percentage } => {
@@ -290,7 +269,6 @@ mod test {
         votes: Votes,
         total_power: Uint128,
         is_expired: bool,
-        min_voting_period_elapsed: bool,
         allow_revoting: bool,
     ) -> (Proposal, BlockInfo) {
         let block = mock_env().block;
@@ -298,17 +276,12 @@ mod test {
             true => Expiration::AtHeight(block.height - 5),
             false => Expiration::AtHeight(block.height + 100),
         };
-        let min_voting_period = match min_voting_period_elapsed {
-            true => Expiration::AtHeight(block.height - 5),
-            false => Expiration::AtHeight(block.height + 5),
-        };
         let prop = Proposal {
             title: "Demo".to_string(),
             description: "Info".to_string(),
             proposer: Addr::unchecked("test"),
             start_height: 100,
             expiration,
-            min_voting_period: Some(min_voting_period),
             allow_revoting,
             msgs: vec![],
             status: Status::Open,
@@ -325,17 +298,9 @@ mod test {
         votes: Votes,
         total_power: Uint128,
         is_expired: bool,
-        min_voting_period_elapsed: bool,
         allow_revoting: bool,
     ) -> bool {
-        let (prop, block) = setup_prop(
-            threshold,
-            votes,
-            total_power,
-            is_expired,
-            min_voting_period_elapsed,
-            allow_revoting,
-        );
+        let (prop, block) = setup_prop(threshold, votes, total_power, is_expired, allow_revoting);
         prop.is_passed(&block)
     }
 
@@ -344,17 +309,9 @@ mod test {
         votes: Votes,
         total_power: Uint128,
         is_expired: bool,
-        min_voting_period_elapsed: bool,
         allow_revoting: bool,
     ) -> bool {
-        let (prop, block) = setup_prop(
-            threshold,
-            votes,
-            total_power,
-            is_expired,
-            min_voting_period_elapsed,
-            allow_revoting,
-        );
+        let (prop, block) = setup_prop(threshold, votes, total_power, is_expired, allow_revoting);
         prop.is_rejected(&block)
     }
 
@@ -376,7 +333,6 @@ mod test {
             votes.clone(),
             Uint128::new(15),
             false,
-            true,
             false,
         ));
         // Proposal being expired should not effect those results.
@@ -385,7 +341,6 @@ mod test {
             votes.clone(),
             Uint128::new(15),
             true,
-            true,
             false
         ));
 
@@ -395,84 +350,10 @@ mod test {
             votes,
             Uint128::new(17),
             false,
-            true,
-            false
-        ));
-    }
-
-    #[test]
-    fn test_min_voting_period_no_early_pass() {
-        let threshold = Threshold::AbsolutePercentage {
-            percentage: PercentageThreshold::Majority {},
-        };
-        let votes = Votes {
-            yes: Uint128::new(7),
-            no: Uint128::new(4),
-            abstain: Uint128::new(2),
-        };
-
-        // Does not pass if min voting period is not expired.
-        assert!(!check_is_passed(
-            threshold.clone(),
-            votes.clone(),
-            Uint128::new(15),
-            false,
-            false,
-            false,
-        ));
-        // Should not be rejected either.
-        assert!(!check_is_rejected(
-            threshold.clone(),
-            votes.clone(),
-            Uint128::new(15),
-            false,
-            false,
-            false,
-        ));
-
-        // Min voting period being expired makes this pass.
-        assert!(check_is_passed(
-            threshold,
-            votes,
-            Uint128::new(15),
-            false,
-            true,
             false
         ));
     }
 
-    #[test]
-    fn test_min_voting_period_early_rejection() {
-        let threshold = Threshold::AbsolutePercentage {
-            percentage: PercentageThreshold::Majority {},
-        };
-        let votes = Votes {
-            yes: Uint128::new(4),
-            no: Uint128::new(7),
-            abstain: Uint128::new(2),
-        };
-
-        // Proposal has not passed.
-        assert!(!check_is_passed(
-            threshold.clone(),
-            votes.clone(),
-            Uint128::new(15),
-            false,
-            false,
-            false,
-        ));
-        // Should be rejected despite the min voting period not being
-        // passed.
-        assert!(check_is_rejected(
-            threshold,
-            votes,
-            Uint128::new(15),
-            false,
-            false,
-            false,
-        ));
-    }
-
     #[test]
     fn test_revoting_majority_no_pass() {
         // Revoting being allowed means that proposals may not be
@@ -494,7 +375,6 @@ mod test {
             Uint128::new(15),
             false,
             true,
-            true,
         ));
         // Proposal being expired should cause the proposal to be
         // passed as votes may no longer be cast.
@@ -503,7 +383,6 @@ mod test {
             votes,
             Uint128::new(15),
             true,
-            true,
             true
         ));
     }
@@ -527,7 +406,6 @@ mod test {
             votes.clone(),
             Uint128::new(15),
             false,
-            true,
             true
         ));
 
@@ -537,7 +415,6 @@ mod test {
             votes,
             Uint128::new(15),
             true,
-            true,
             true
         ));
     }
@@ -559,7 +436,6 @@ mod test {
             },
             Uint128::new(100),
             false,
-            true,
             false
         ));
 
@@ -572,7 +448,6 @@ mod test {
             },
             Uint128::new(10),
             false,
-            true,
             false
         ));
 
@@ -585,7 +460,6 @@ mod test {
             },
             Uint128::new(11),
             false,
-            true,
             false
         ));
 
@@ -598,7 +472,6 @@ mod test {
             },
             Uint128::new(11),
             false,
-            true,
             false
         ));
     }
@@ -620,7 +493,6 @@ mod test {
             },
             Uint128::new(100),
             false,
-            true,
             true
         ));
         assert!(check_is_passed(
@@ -632,7 +504,6 @@ mod test {
             },
             Uint128::new(100),
             true,
-            true,
             true
         ));
 
@@ -645,7 +516,6 @@ mod test {
             },
             Uint128::new(10),
             false,
-            true,
             true
         ));
         assert!(check_is_rejected(
@@ -657,7 +527,6 @@ mod test {
             },
             Uint128::new(10),
             true,
-            true,
             true
         ));
     }
@@ -677,7 +546,6 @@ mod test {
             votes,
             Uint128::new(13),
             false,
-            true,
             false
         ))
     }
@@ -697,7 +565,6 @@ mod test {
             votes.clone(),
             Uint128::new(13),
             false,
-            true,
             false
         ));
         assert!(!check_is_rejected(
@@ -705,7 +572,6 @@ mod test {
             votes,
             Uint128::new(13),
             false,
-            true,
             false
         ));
     }
@@ -725,7 +591,6 @@ mod test {
             votes.clone(),
             Uint128::new(13),
             false,
-            true,
             false
         ));
         assert!(!check_is_passed(
@@ -733,7 +598,6 @@ mod test {
             votes,
             Uint128::new(14),
             false,
-            true,
             false
         ))
     }
@@ -759,7 +623,6 @@ mod test {
             votes.clone(),
             Uint128::new(15),
             false,
-            true,
             false,
         ));
         assert!(check_is_rejected(
@@ -767,7 +630,6 @@ mod test {
             votes.clone(),
             Uint128::new(15),
             true,
-            true,
             false
         ));
 
@@ -778,7 +640,6 @@ mod test {
             votes.clone(),
             Uint128::new(17),
             false,
-            true,
             false
         ));
         assert!(!check_is_rejected(
@@ -786,7 +647,6 @@ mod test {
             votes.clone(),
             Uint128::new(17),
             true,
-            true,
             false
         ));
 
@@ -796,7 +656,6 @@ mod test {
             votes.clone(),
             Uint128::new(14),
             false,
-            true,
             false
         ));
         assert!(check_is_rejected(
@@ -804,7 +663,6 @@ mod test {
             votes,
             Uint128::new(14),
             true,
-            true,
             false
         ));
     }
@@ -841,7 +699,6 @@ mod test {
             passing.clone(),
             Uint128::new(30),
             true,
-            true,
             false,
         ));
         // under quorum it is not passing (40% of 33 = 13.2 > 13)
@@ -850,7 +707,6 @@ mod test {
             passing.clone(),
             Uint128::new(33),
             true,
-            true,
             false
         ));
         // over quorum, threshold passes if we ignore abstain
@@ -861,7 +717,6 @@ mod test {
             passes_ignoring_abstain.clone(),
             Uint128::new(40),
             true,
-            true,
             false,
         ));
         // over quorum, but under threshold fails also
@@ -870,7 +725,6 @@ mod test {
             failing,
             Uint128::new(20),
             true,
-            true,
             false
         ));
 
@@ -881,7 +735,6 @@ mod test {
             passing.clone(),
             Uint128::new(30),
             false,
-            true,
             false
         ));
         assert!(!check_is_passed(
@@ -889,7 +742,6 @@ mod test {
             passes_ignoring_abstain.clone(),
             Uint128::new(40),
             false,
-            true,
             false
         ));
         // if we have threshold * total_weight as yes votes this must pass
@@ -898,7 +750,6 @@ mod test {
             passing.clone(),
             Uint128::new(14),
             false,
-            true,
             false
         ));
         // all votes have been cast, some abstain
@@ -907,7 +758,6 @@ mod test {
             passes_ignoring_abstain,
             Uint128::new(17),
             false,
-            true,
             false
         ));
         // 3 votes uncast, if they all vote no, we have 7 yes, 7 no+veto, 2 abstain (out of 16)
@@ -916,7 +766,6 @@ mod test {
             passing,
             Uint128::new(16),
             false,
-            true,
             false
         ));
     }
@@ -953,7 +802,6 @@ mod test {
             rejecting.clone(),
             Uint128::new(30),
             true,
-            true,
             false
         ));
         // Total power of 33. 13 total votes. 8 no votes, 3 yes, 2
@@ -965,7 +813,6 @@ mod test {
             rejecting.clone(),
             Uint128::new(33),
             true,
-            true,
             false
         ));
 
@@ -977,7 +824,6 @@ mod test {
             rejected_ignoring_abstain.clone(),
             Uint128::new(40),
             true,
-            true,
             false
         ));
 
@@ -990,7 +836,6 @@ mod test {
             failing.clone(),
             Uint128::new(20),
             true,
-            true,
             false
         ));
         assert!(!check_is_rejected(
@@ -998,7 +843,6 @@ mod test {
             failing,
             Uint128::new(20),
             false,
-            true,
             false
         ));
 
@@ -1009,7 +853,6 @@ mod test {
             rejecting.clone(),
             Uint128::new(30),
             false,
-            true,
             false
         ));
         assert!(!check_is_rejected(
@@ -1017,7 +860,6 @@ mod test {
             rejected_ignoring_abstain.clone(),
             Uint128::new(40),
             false,
-            true,
             false
         ));
         // if we have threshold * total_weight as no votes this must reject
@@ -1026,7 +868,6 @@ mod test {
             rejecting.clone(),
             Uint128::new(14),
             false,
-            true,
             false
         ));
         // all votes have been cast, some abstain
@@ -1035,7 +876,6 @@ mod test {
             rejected_ignoring_abstain,
             Uint128::new(17),
             false,
-            true,
             false
         ));
         // 3 votes uncast, if they all vote yes, we have 7 no, 7 yes+veto, 2 abstain (out of 16)
@@ -1044,7 +884,6 @@ mod test {
             rejecting,
             Uint128::new(16),
             false,
-            true,
             false
         ));
     }
@@ -1071,7 +910,6 @@ mod test {
             missing_voters.clone(),
             Uint128::new(15),
             false,
-            true,
             false
         ));
         assert!(!check_is_passed(
@@ -1079,7 +917,6 @@ mod test {
             missing_voters,
             Uint128::new(15),
             true,
-            true,
             false
         ));
 
@@ -1094,7 +931,6 @@ mod test {
             wait_til_expired.clone(),
             Uint128::new(15),
             false,
-            true,
             false
         ));
         assert!(check_is_passed(
@@ -1102,7 +938,6 @@ mod test {
             wait_til_expired,
             Uint128::new(15),
             true,
-            true,
             false
         ));
 
@@ -1117,7 +952,6 @@ mod test {
             passes_early.clone(),
             Uint128::new(15),
             false,
-            true,
             false
         ));
         assert!(check_is_passed(
@@ -1125,7 +959,6 @@ mod test {
             passes_early,
             Uint128::new(15),
             true,
-            true,
             false
         ));
     }
diff --git a/contracts/cw-proposal-single/src/state.rs b/contracts/cw-proposal-single/src/state.rs
index 0afd661f..0546096b 100644
--- a/contracts/cw-proposal-single/src/state.rs
+++ b/contracts/cw-proposal-single/src/state.rs
@@ -34,12 +34,6 @@ pub struct Config {
     /// The default maximum amount of time a proposal may be voted on
     /// before expiring.
     pub max_voting_period: Duration,
-    /// The minimum amount of time a proposal must be open before
-    /// passing. A proposal may fail before this amount of time has
-    /// elapsed, but it will not pass. This can be useful for
-    /// preventing governance attacks wherein an attacker aquires a
-    /// large number of tokens and forces a proposal through.
-    pub min_voting_period: Option<Duration>,
     /// If set to true only members may execute passed
     /// proposals. Otherwise, any address may execute a passed
     /// proposal.
diff --git a/contracts/cw-proposal-single/src/utils.rs b/contracts/cw-proposal-single/src/utils.rs
index a31b96b8..80c0cbab 100644
--- a/contracts/cw-proposal-single/src/utils.rs
+++ b/contracts/cw-proposal-single/src/utils.rs
@@ -1,9 +1,6 @@
 use cosmwasm_std::{Addr, Deps, StdResult, Uint128};
 
 use cw_core_interface::voting;
-use cw_utils::Duration;
-
-use crate::ContractError;
 
 pub fn get_voting_power(
     deps: Deps,
@@ -27,27 +24,3 @@ pub fn get_total_power(deps: Deps, dao: Addr, height: Option<u64>) -> StdResult<
         .query_wasm_smart(dao, &voting::Query::TotalPowerAtHeight { height })?;
     Ok(response.power)
 }
-
-/// Validates that the min voting period is less than the max voting
-/// period. Passes arguments through the function.
-pub fn validate_voting_period(
-    min: Option<Duration>,
-    max: Duration,
-) -> Result<(Option<Duration>, Duration), ContractError> {
-    let min = min
-        .map(|min| {
-            let valid = match (min, max) {
-                (Duration::Time(min), Duration::Time(max)) => min <= max,
-                (Duration::Height(min), Duration::Height(max)) => min <= max,
-                _ => return Err(ContractError::DurationUnitsConflict {}),
-            };
-            if valid {
-                Ok(min)
-            } else {
-                Err(ContractError::InvalidMinVotingPeriod {})
-            }
-        })
-        .transpose()?;
-
-    Ok((min, max))
-}
diff --git a/contracts/cw20-staked-balance-voting/Cargo.toml b/contracts/cw20-staked-balance-voting/Cargo.toml
index f50a8634..8b8aca4a 100644
--- a/contracts/cw20-staked-balance-voting/Cargo.toml
+++ b/contracts/cw20-staked-balance-voting/Cargo.toml
@@ -40,7 +40,7 @@ serde = { version = "1.0", default-features = false, features = ["derive"] }
 thiserror = { version = "1.0" }
 cw-core-macros = { version = "*", path = "../../packages/cw-core-macros" }
 cw-core-interface = { version = "*", path = "../../packages/cw-core-interface" }
-cw20-stake = { path = "../cw20-stake" }
+stake-cw20 = { path = "../stake-cw20" }
 cw20-base = {  version = "0.13", features = ["library"] }
 
 [dev-dependencies]
diff --git a/contracts/cw20-staked-balance-voting/schema/migrate_msg.json b/contracts/cw20-staked-balance-voting/schema/migrate_msg.json
deleted file mode 100644
index 87b18ea7..00000000
--- a/contracts/cw20-staked-balance-voting/schema/migrate_msg.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-  "$schema": "http://json-schema.org/draft-07/schema#",
-  "title": "MigrateMsg",
-  "type": "object"
-}
diff --git a/contracts/cw20-staked-balance-voting/src/contract.rs b/contracts/cw20-staked-balance-voting/src/contract.rs
index 4d55c990..8c8b589b 100644
--- a/contracts/cw20-staked-balance-voting/src/contract.rs
+++ b/contracts/cw20-staked-balance-voting/src/contract.rs
@@ -65,9 +65,9 @@ pub fn instantiate(
                 } => {
                     let staking_contract_address =
                         deps.api.addr_validate(&staking_contract_address)?;
-                    let resp: cw20_stake::msg::GetConfigResponse = deps.querier.query_wasm_smart(
+                    let resp: stake_cw20::msg::GetConfigResponse = deps.querier.query_wasm_smart(
                         &staking_contract_address,
-                        &cw20_stake::msg::QueryMsg::GetConfig {},
+                        &stake_cw20::msg::QueryMsg::GetConfig {},
                     )?;
 
                     if address != resp.token_address {
@@ -90,7 +90,7 @@ pub fn instantiate(
                         funds: vec![],
                         admin: Some(info.sender.to_string()),
                         label: env.contract.address.to_string(),
-                        msg: to_binary(&cw20_stake::msg::InstantiateMsg {
+                        msg: to_binary(&stake_cw20::msg::InstantiateMsg {
                             owner: Some(info.sender.to_string()),
                             unstaking_duration,
                             token_address: address.to_string(),
@@ -258,9 +258,9 @@ pub fn query_voting_power_at_height(
 ) -> StdResult<Binary> {
     let staking_contract = STAKING_CONTRACT.load(deps.storage)?;
     let address = deps.api.addr_validate(&address)?;
-    let res: cw20_stake::msg::StakedBalanceAtHeightResponse = deps.querier.query_wasm_smart(
+    let res: stake_cw20::msg::StakedBalanceAtHeightResponse = deps.querier.query_wasm_smart(
         staking_contract,
-        &cw20_stake::msg::QueryMsg::StakedBalanceAtHeight {
+        &stake_cw20::msg::QueryMsg::StakedBalanceAtHeight {
             address: address.to_string(),
             height,
         },
@@ -277,9 +277,9 @@ pub fn query_total_power_at_height(
     height: Option<u64>,
 ) -> StdResult<Binary> {
     let staking_contract = STAKING_CONTRACT.load(deps.storage)?;
-    let res: cw20_stake::msg::TotalStakedAtHeightResponse = deps.querier.query_wasm_smart(
+    let res: stake_cw20::msg::TotalStakedAtHeightResponse = deps.querier.query_wasm_smart(
         staking_contract,
-        &cw20_stake::msg::QueryMsg::TotalStakedAtHeight { height },
+        &stake_cw20::msg::QueryMsg::TotalStakedAtHeight { height },
     )?;
     to_binary(&cw_core_interface::voting::TotalPowerAtHeightResponse {
         power: res.total,
@@ -302,10 +302,10 @@ pub fn query_is_active(deps: Deps) -> StdResult<Binary> {
     if let Some(threshold) = threshold {
         let token_contract = TOKEN.load(deps.storage)?;
         let staking_contract = STAKING_CONTRACT.load(deps.storage)?;
-        let actual_power: cw20_stake::msg::TotalStakedAtHeightResponse =
+        let actual_power: stake_cw20::msg::TotalStakedAtHeightResponse =
             deps.querier.query_wasm_smart(
                 staking_contract,
-                &cw20_stake::msg::QueryMsg::TotalStakedAtHeight { height: None },
+                &stake_cw20::msg::QueryMsg::TotalStakedAtHeight { height: None },
             )?;
         match threshold {
             ActiveThreshold::AbsoluteCount { count } => to_binary(&IsActiveResponse {
@@ -375,7 +375,7 @@ pub fn reply(deps: DepsMut, env: Env, msg: Reply) -> Result<Response, ContractEr
                         funds: vec![],
                         admin: Some(dao.to_string()),
                         label: env.contract.address.to_string(),
-                        msg: to_binary(&cw20_stake::msg::InstantiateMsg {
+                        msg: to_binary(&stake_cw20::msg::InstantiateMsg {
                             owner: Some(dao.to_string()),
                             unstaking_duration,
                             token_address: token.to_string(),
diff --git a/contracts/cw4-voting/schema/migrate_msg.json b/contracts/cw4-voting/schema/migrate_msg.json
deleted file mode 100644
index 87b18ea7..00000000
--- a/contracts/cw4-voting/schema/migrate_msg.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-  "$schema": "http://json-schema.org/draft-07/schema#",
-  "title": "MigrateMsg",
-  "type": "object"
-}
diff --git a/contracts/cw20-stake-external-rewards/.cargo/config b/contracts/stake-cw20-external-rewards/.cargo/config
similarity index 100%
rename from contracts/cw20-stake-external-rewards/.cargo/config
rename to contracts/stake-cw20-external-rewards/.cargo/config
diff --git a/contracts/cw20-stake-external-rewards/Cargo.lock b/contracts/stake-cw20-external-rewards/Cargo.lock
similarity index 100%
rename from contracts/cw20-stake-external-rewards/Cargo.lock
rename to contracts/stake-cw20-external-rewards/Cargo.lock
diff --git a/contracts/cw20-stake-external-rewards/Cargo.toml b/contracts/stake-cw20-external-rewards/Cargo.toml
similarity index 94%
rename from contracts/cw20-stake-external-rewards/Cargo.toml
rename to contracts/stake-cw20-external-rewards/Cargo.toml
index 109a0db4..2fd233fb 100644
--- a/contracts/cw20-stake-external-rewards/Cargo.toml
+++ b/contracts/stake-cw20-external-rewards/Cargo.toml
@@ -27,7 +27,7 @@ cw2 = "0.13"
 schemars = "0.8.8"
 serde = { version = "1.0.132", default-features = false, features = ["derive"] }
 thiserror = { version = "1.0.30" }
-cw20-stake = { path = "../cw20-stake", features = ["library"]}
+stake-cw20 = { path = "../stake-cw20", features = ["library"]}
 
 [dev-dependencies]
 cosmwasm-schema = { version = "1.0.0" }
diff --git a/contracts/cw20-stake-external-rewards/LICENSE b/contracts/stake-cw20-external-rewards/LICENSE
similarity index 100%
rename from contracts/cw20-stake-external-rewards/LICENSE
rename to contracts/stake-cw20-external-rewards/LICENSE
diff --git a/contracts/cw20-stake-external-rewards/NOTICE b/contracts/stake-cw20-external-rewards/NOTICE
similarity index 100%
rename from contracts/cw20-stake-external-rewards/NOTICE
rename to contracts/stake-cw20-external-rewards/NOTICE
diff --git a/contracts/cw20-stake-external-rewards/README.md b/contracts/stake-cw20-external-rewards/README.md
similarity index 100%
rename from contracts/cw20-stake-external-rewards/README.md
rename to contracts/stake-cw20-external-rewards/README.md
diff --git a/contracts/cw20-stake-external-rewards/examples/schema.rs b/contracts/stake-cw20-external-rewards/examples/schema.rs
similarity index 100%
rename from contracts/cw20-stake-external-rewards/examples/schema.rs
rename to contracts/stake-cw20-external-rewards/examples/schema.rs
diff --git a/contracts/cw20-stake-external-rewards/schema/execute_msg.json b/contracts/stake-cw20-external-rewards/schema/execute_msg.json
similarity index 100%
rename from contracts/cw20-stake-external-rewards/schema/execute_msg.json
rename to contracts/stake-cw20-external-rewards/schema/execute_msg.json
diff --git a/contracts/cw20-stake-external-rewards/schema/info_response.json b/contracts/stake-cw20-external-rewards/schema/info_response.json
similarity index 100%
rename from contracts/cw20-stake-external-rewards/schema/info_response.json
rename to contracts/stake-cw20-external-rewards/schema/info_response.json
diff --git a/contracts/cw20-stake-external-rewards/schema/instantiate_msg.json b/contracts/stake-cw20-external-rewards/schema/instantiate_msg.json
similarity index 100%
rename from contracts/cw20-stake-external-rewards/schema/instantiate_msg.json
rename to contracts/stake-cw20-external-rewards/schema/instantiate_msg.json
diff --git a/contracts/cw20-stake-external-rewards/schema/pending_rewards_response.json b/contracts/stake-cw20-external-rewards/schema/pending_rewards_response.json
similarity index 100%
rename from contracts/cw20-stake-external-rewards/schema/pending_rewards_response.json
rename to contracts/stake-cw20-external-rewards/schema/pending_rewards_response.json
diff --git a/contracts/cw20-stake-external-rewards/schema/query_msg.json b/contracts/stake-cw20-external-rewards/schema/query_msg.json
similarity index 100%
rename from contracts/cw20-stake-external-rewards/schema/query_msg.json
rename to contracts/stake-cw20-external-rewards/schema/query_msg.json
diff --git a/contracts/cw20-stake-external-rewards/src/contract.rs b/contracts/stake-cw20-external-rewards/src/contract.rs
similarity index 98%
rename from contracts/cw20-stake-external-rewards/src/contract.rs
rename to contracts/stake-cw20-external-rewards/src/contract.rs
index b6d02f16..7d92bb7c 100644
--- a/contracts/cw20-stake-external-rewards/src/contract.rs
+++ b/contracts/stake-cw20-external-rewards/src/contract.rs
@@ -19,13 +19,13 @@ use cosmwasm_std::{
 };
 use cw2::set_contract_version;
 use cw20::{Cw20ReceiveMsg, Denom};
-use cw20_stake::hooks::StakeChangedHookMsg;
+use stake_cw20::hooks::StakeChangedHookMsg;
 
 use cw20::Denom::Cw20;
 use std::cmp::min;
 use std::convert::TryInto;
 
-const CONTRACT_NAME: &str = "crates.io:cw20-stake-external-rewards";
+const CONTRACT_NAME: &str = "crates.io:stake-cw20-external-rewards";
 const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");
 
 #[cfg_attr(not(feature = "library"), entry_point)]
@@ -49,9 +49,9 @@ pub fn instantiate(
     };
 
     // Verify contract provided is a staking contract
-    let _: cw20_stake::msg::TotalStakedAtHeightResponse = deps.querier.query_wasm_smart(
+    let _: stake_cw20::msg::TotalStakedAtHeightResponse = deps.querier.query_wasm_smart(
         &msg.staking_contract,
-        &cw20_stake::msg::QueryMsg::TotalStakedAtHeight { height: None },
+        &stake_cw20::msg::QueryMsg::TotalStakedAtHeight { height: None },
     )?;
 
     let config = Config {
@@ -343,18 +343,18 @@ fn get_last_time_reward_applicable(deps: Deps, env: &Env) -> StdResult<u64> {
 }
 
 fn get_total_staked(deps: Deps, contract_addr: &Addr) -> StdResult<Uint128> {
-    let msg = cw20_stake::msg::QueryMsg::TotalStakedAtHeight { height: None };
-    let resp: cw20_stake::msg::TotalStakedAtHeightResponse =
+    let msg = stake_cw20::msg::QueryMsg::TotalStakedAtHeight { height: None };
+    let resp: stake_cw20::msg::TotalStakedAtHeightResponse =
         deps.querier.query_wasm_smart(contract_addr, &msg)?;
     Ok(resp.total)
 }
 
 fn get_staked_balance(deps: Deps, contract_addr: &Addr, addr: &Addr) -> StdResult<Uint128> {
-    let msg = cw20_stake::msg::QueryMsg::StakedBalanceAtHeight {
+    let msg = stake_cw20::msg::QueryMsg::StakedBalanceAtHeight {
         address: addr.into(),
         height: None,
     };
-    let resp: cw20_stake::msg::StakedBalanceAtHeightResponse =
+    let resp: stake_cw20::msg::StakedBalanceAtHeightResponse =
         deps.querier.query_wasm_smart(contract_addr, &msg)?;
     Ok(resp.balance)
 }
@@ -527,9 +527,9 @@ mod tests {
 
     pub fn contract_staking() -> Box<dyn Contract<Empty>> {
         let contract = ContractWrapper::new(
-            cw20_stake::contract::execute,
-            cw20_stake::contract::instantiate,
-            cw20_stake::contract::query,
+            stake_cw20::contract::execute,
+            stake_cw20::contract::instantiate,
+            stake_cw20::contract::query,
         );
         Box::new(contract)
     }
@@ -568,7 +568,7 @@ mod tests {
         unstaking_duration: Option<Duration>,
     ) -> Addr {
         let staking_code_id = app.store_code(contract_staking());
-        let msg = cw20_stake::msg::InstantiateMsg {
+        let msg = stake_cw20::msg::InstantiateMsg {
             owner: Some(OWNER.to_string()),
             manager: Some("manager".to_string()),
             token_address: cw20.to_string(),
@@ -595,14 +595,14 @@ mod tests {
         let msg = cw20::Cw20ExecuteMsg::Send {
             contract: staking_addr.to_string(),
             amount: Uint128::new(amount),
-            msg: to_binary(&cw20_stake::msg::ReceiveMsg::Stake {}).unwrap(),
+            msg: to_binary(&stake_cw20::msg::ReceiveMsg::Stake {}).unwrap(),
         };
         app.execute_contract(Addr::unchecked(sender), cw20_addr.clone(), &msg, &[])
             .unwrap();
     }
 
     fn unstake_tokens(app: &mut App, staking_addr: &Addr, address: &str, amount: u128) {
-        let msg = cw20_stake::msg::ExecuteMsg::Unstake {
+        let msg = stake_cw20::msg::ExecuteMsg::Unstake {
             amount: Uint128::new(amount),
         };
         app.execute_contract(Addr::unchecked(address), staking_addr.clone(), &msg, &[])
@@ -646,7 +646,7 @@ mod tests {
         let reward_addr = app
             .instantiate_contract(reward_code_id, owner, &msg, &[], "reward", None)
             .unwrap();
-        let msg = cw20_stake::msg::ExecuteMsg::AddHook {
+        let msg = stake_cw20::msg::ExecuteMsg::AddHook {
             addr: reward_addr.to_string(),
         };
         let _result = app
diff --git a/contracts/cw20-stake-external-rewards/src/error.rs b/contracts/stake-cw20-external-rewards/src/error.rs
similarity index 100%
rename from contracts/cw20-stake-external-rewards/src/error.rs
rename to contracts/stake-cw20-external-rewards/src/error.rs
diff --git a/contracts/cw20-stake-external-rewards/src/lib.rs b/contracts/stake-cw20-external-rewards/src/lib.rs
similarity index 100%
rename from contracts/cw20-stake-external-rewards/src/lib.rs
rename to contracts/stake-cw20-external-rewards/src/lib.rs
diff --git a/contracts/cw20-stake-external-rewards/src/msg.rs b/contracts/stake-cw20-external-rewards/src/msg.rs
similarity index 97%
rename from contracts/cw20-stake-external-rewards/src/msg.rs
rename to contracts/stake-cw20-external-rewards/src/msg.rs
index fa1f94c0..8d7df9ba 100644
--- a/contracts/cw20-stake-external-rewards/src/msg.rs
+++ b/contracts/stake-cw20-external-rewards/src/msg.rs
@@ -1,8 +1,8 @@
 use cosmwasm_std::Uint128;
 use cw20::{Cw20ReceiveMsg, Denom};
-use cw20_stake::hooks::StakeChangedHookMsg;
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
+use stake_cw20::hooks::StakeChangedHookMsg;
 
 use crate::state::{Config, RewardConfig};
 pub use cw_controllers::ClaimsResponse;
diff --git a/contracts/cw20-stake-external-rewards/src/state.rs b/contracts/stake-cw20-external-rewards/src/state.rs
similarity index 100%
rename from contracts/cw20-stake-external-rewards/src/state.rs
rename to contracts/stake-cw20-external-rewards/src/state.rs
diff --git a/contracts/cw20-stake-reward-distributor/.cargo/config b/contracts/stake-cw20-reward-distributor/.cargo/config
similarity index 100%
rename from contracts/cw20-stake-reward-distributor/.cargo/config
rename to contracts/stake-cw20-reward-distributor/.cargo/config
diff --git a/contracts/cw20-stake-reward-distributor/Cargo.toml b/contracts/stake-cw20-reward-distributor/Cargo.toml
similarity index 95%
rename from contracts/cw20-stake-reward-distributor/Cargo.toml
rename to contracts/stake-cw20-reward-distributor/Cargo.toml
index a012240a..4556d179 100644
--- a/contracts/cw20-stake-reward-distributor/Cargo.toml
+++ b/contracts/stake-cw20-reward-distributor/Cargo.toml
@@ -37,7 +37,7 @@ cw20 = { version = "0.13" }
 cw-utils = "0.13"
 schemars = "0.8"
 cw20-base = {  version = "0.13", features = ["library"] }
-cw20-stake = { path = "../cw20-stake", features = ["library"]}
+stake-cw20 = { path = "../stake-cw20", features = ["library"]}
 serde = { version = "1.0", default-features = false, features = ["derive"] }
 thiserror = { version = "1.0" }
 
diff --git a/contracts/cw20-stake-reward-distributor/README.md b/contracts/stake-cw20-reward-distributor/README.md
similarity index 100%
rename from contracts/cw20-stake-reward-distributor/README.md
rename to contracts/stake-cw20-reward-distributor/README.md
diff --git a/contracts/cw20-stake-reward-distributor/examples/schema.rs b/contracts/stake-cw20-reward-distributor/examples/schema.rs
similarity index 100%
rename from contracts/cw20-stake-reward-distributor/examples/schema.rs
rename to contracts/stake-cw20-reward-distributor/examples/schema.rs
diff --git a/contracts/cw20-stake-reward-distributor/schema/execute_msg.json b/contracts/stake-cw20-reward-distributor/schema/execute_msg.json
similarity index 100%
rename from contracts/cw20-stake-reward-distributor/schema/execute_msg.json
rename to contracts/stake-cw20-reward-distributor/schema/execute_msg.json
diff --git a/contracts/cw20-stake-reward-distributor/schema/instantiate_msg.json b/contracts/stake-cw20-reward-distributor/schema/instantiate_msg.json
similarity index 100%
rename from contracts/cw20-stake-reward-distributor/schema/instantiate_msg.json
rename to contracts/stake-cw20-reward-distributor/schema/instantiate_msg.json
diff --git a/contracts/cw20-stake-reward-distributor/schema/query_msg.json b/contracts/stake-cw20-reward-distributor/schema/query_msg.json
similarity index 100%
rename from contracts/cw20-stake-reward-distributor/schema/query_msg.json
rename to contracts/stake-cw20-reward-distributor/schema/query_msg.json
diff --git a/contracts/cw20-stake-reward-distributor/src/contract.rs b/contracts/stake-cw20-reward-distributor/src/contract.rs
similarity index 89%
rename from contracts/cw20-stake-reward-distributor/src/contract.rs
rename to contracts/stake-cw20-reward-distributor/src/contract.rs
index 76d145a4..a3facc88 100644
--- a/contracts/cw20-stake-reward-distributor/src/contract.rs
+++ b/contracts/stake-cw20-reward-distributor/src/contract.rs
@@ -10,7 +10,7 @@ use crate::state::{Config, CONFIG, LAST_PAYMENT_BLOCK};
 use cosmwasm_std::{Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult};
 use cw2::set_contract_version;
 
-const CONTRACT_NAME: &str = "crates.io:cw20-stake-reward-distributor";
+const CONTRACT_NAME: &str = "crates.io:stake-cw20-reward-distributor";
 const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");
 
 #[cfg_attr(not(feature = "library"), entry_point)]
@@ -93,6 +93,7 @@ pub fn execute_update_config(
         return Err(ContractError::Unauthorized {});
     }
 
+    let distribution_msg = get_distribution_msg(deps.as_ref(), &env)?;
     LAST_PAYMENT_BLOCK.save(deps.storage, &env.block.height)?;
 
     let owner = deps.api.addr_validate(&owner)?;
@@ -114,14 +115,8 @@ pub fn execute_update_config(
     };
     CONFIG.save(deps.storage, &config)?;
 
-    let resp = match get_distribution_msg(deps.as_ref(), &env) {
-        // distribution succeeded
-        Ok(msg) => Response::new().add_message(msg),
-        // distribution failed (either zero rewards or already distributed for block)
-        _ => Response::new(),
-    };
-
-    Ok(resp
+    Ok(Response::new()
+        .add_messages(distribution_msg)
         .add_attribute("action", "update_config")
         .add_attribute("owner", owner.into_string())
         .add_attribute("staking_addr", staking_addr.into_string())
@@ -137,22 +132,18 @@ pub fn validate_cw20(deps: Deps, cw20_addr: Addr) -> bool {
 }
 
 pub fn validate_staking(deps: Deps, staking_addr: Addr) -> bool {
-    let response: Result<cw20_stake::msg::TotalStakedAtHeightResponse, StdError> =
+    let response: Result<stake_cw20::msg::TotalStakedAtHeightResponse, StdError> =
         deps.querier.query_wasm_smart(
             staking_addr,
-            &cw20_stake::msg::QueryMsg::TotalStakedAtHeight { height: None },
+            &stake_cw20::msg::QueryMsg::TotalStakedAtHeight { height: None },
         );
     response.is_ok()
 }
 
-fn get_distribution_msg(deps: Deps, env: &Env) -> Result<CosmosMsg, ContractError> {
+fn get_distribution_msg(deps: Deps, env: &Env) -> Result<Option<CosmosMsg>, ContractError> {
     let config = CONFIG.load(deps.storage)?;
     let last_payment_block = LAST_PAYMENT_BLOCK.load(deps.storage)?;
-    if last_payment_block >= env.block.height {
-        return Err(ContractError::RewardsDistributedForBlock {});
-    }
     let block_diff = env.block.height - last_payment_block;
-
     let pending_rewards: Uint128 = config.reward_rate * Uint128::new(block_diff.into());
 
     let balance_info: cw20::BalanceResponse = deps.querier.query_wasm_smart(
@@ -165,13 +156,13 @@ fn get_distribution_msg(deps: Deps, env: &Env) -> Result<CosmosMsg, ContractErro
     let amount = min(balance_info.balance, pending_rewards);
 
     if amount == Uint128::zero() {
-        return Err(ContractError::ZeroRewards {});
+        return Ok(None);
     }
 
     let msg = to_binary(&cw20::Cw20ExecuteMsg::Send {
         contract: config.staking_addr.clone().into_string(),
         amount,
-        msg: to_binary(&cw20_stake::msg::ReceiveMsg::Fund {}).unwrap(),
+        msg: to_binary(&stake_cw20::msg::ReceiveMsg::Fund {}).unwrap(),
     })?;
     let send_msg: CosmosMsg = WasmMsg::Execute {
         contract_addr: config.reward_token.into(),
@@ -180,14 +171,14 @@ fn get_distribution_msg(deps: Deps, env: &Env) -> Result<CosmosMsg, ContractErro
     }
     .into();
 
-    Ok(send_msg)
+    Ok(Some(send_msg))
 }
 
 pub fn execute_distribute(deps: DepsMut, env: Env) -> Result<Response, ContractError> {
     let msg = get_distribution_msg(deps.as_ref(), &env)?;
     LAST_PAYMENT_BLOCK.save(deps.storage, &env.block.height)?;
     Ok(Response::new()
-        .add_message(msg)
+        .add_messages(msg)
         .add_attribute("action", "distribute"))
 }
 
diff --git a/contracts/cw20-stake-reward-distributor/src/error.rs b/contracts/stake-cw20-reward-distributor/src/error.rs
similarity index 67%
rename from contracts/cw20-stake-reward-distributor/src/error.rs
rename to contracts/stake-cw20-reward-distributor/src/error.rs
index 6c0bb37e..44fdc288 100644
--- a/contracts/cw20-stake-reward-distributor/src/error.rs
+++ b/contracts/stake-cw20-reward-distributor/src/error.rs
@@ -14,10 +14,4 @@ pub enum ContractError {
 
     #[error("Invalid Staking Contract")]
     InvalidStakingContract {},
-
-    #[error("Zero eligible rewards")]
-    ZeroRewards {},
-
-    #[error("Rewards have already been distributed for this block")]
-    RewardsDistributedForBlock {},
 }
diff --git a/contracts/cw20-stake/.cargo/config b/contracts/stake-cw20/.cargo/config
similarity index 100%
rename from contracts/cw20-stake/.cargo/config
rename to contracts/stake-cw20/.cargo/config
diff --git a/contracts/cw20-stake/Cargo.lock b/contracts/stake-cw20/Cargo.lock
similarity index 100%
rename from contracts/cw20-stake/Cargo.lock
rename to contracts/stake-cw20/Cargo.lock
diff --git a/contracts/cw20-stake/Cargo.toml b/contracts/stake-cw20/Cargo.toml
similarity index 98%
rename from contracts/cw20-stake/Cargo.toml
rename to contracts/stake-cw20/Cargo.toml
index 54e3311c..67a0ebf3 100644
--- a/contracts/cw20-stake/Cargo.toml
+++ b/contracts/stake-cw20/Cargo.toml
@@ -1,5 +1,5 @@
 [package]
-name = "cw20-stake"
+name = "stake-cw20"
 version = "0.2.6"
 authors = ["Ben2x4 <Ben2x4@tutanota.com>"]
 edition = "2018"
diff --git a/contracts/cw20-stake/LICENSE b/contracts/stake-cw20/LICENSE
similarity index 100%
rename from contracts/cw20-stake/LICENSE
rename to contracts/stake-cw20/LICENSE
diff --git a/contracts/cw20-stake/NOTICE b/contracts/stake-cw20/NOTICE
similarity index 100%
rename from contracts/cw20-stake/NOTICE
rename to contracts/stake-cw20/NOTICE
diff --git a/contracts/cw20-stake/README.md b/contracts/stake-cw20/README.md
similarity index 100%
rename from contracts/cw20-stake/README.md
rename to contracts/stake-cw20/README.md
diff --git a/contracts/cw20-stake/examples/schema.rs b/contracts/stake-cw20/examples/schema.rs
similarity index 98%
rename from contracts/cw20-stake/examples/schema.rs
rename to contracts/stake-cw20/examples/schema.rs
index 354bc936..bf77c2c8 100644
--- a/contracts/cw20-stake/examples/schema.rs
+++ b/contracts/stake-cw20/examples/schema.rs
@@ -7,7 +7,7 @@ use cw20::{
     AllAccountsResponse, AllAllowancesResponse, AllowanceResponse, BalanceResponse,
     TokenInfoResponse,
 };
-use cw20_stake::msg::{
+use stake_cw20::msg::{
     ClaimsResponse, ExecuteMsg, GetConfigResponse, GetHooksResponse, InstantiateMsg, QueryMsg,
     StakedBalanceAtHeightResponse, StakedValueResponse, TotalStakedAtHeightResponse,
     TotalValueResponse,
diff --git a/contracts/cw20-stake/schema/all_accounts_response.json b/contracts/stake-cw20/schema/all_accounts_response.json
similarity index 100%
rename from contracts/cw20-stake/schema/all_accounts_response.json
rename to contracts/stake-cw20/schema/all_accounts_response.json
diff --git a/contracts/cw20-stake/schema/all_allowances_response.json b/contracts/stake-cw20/schema/all_allowances_response.json
similarity index 100%
rename from contracts/cw20-stake/schema/all_allowances_response.json
rename to contracts/stake-cw20/schema/all_allowances_response.json
diff --git a/contracts/cw20-stake/schema/allowance_response.json b/contracts/stake-cw20/schema/allowance_response.json
similarity index 100%
rename from contracts/cw20-stake/schema/allowance_response.json
rename to contracts/stake-cw20/schema/allowance_response.json
diff --git a/contracts/cw20-stake/schema/balance_response.json b/contracts/stake-cw20/schema/balance_response.json
similarity index 100%
rename from contracts/cw20-stake/schema/balance_response.json
rename to contracts/stake-cw20/schema/balance_response.json
diff --git a/contracts/cw20-stake/schema/claims_response.json b/contracts/stake-cw20/schema/claims_response.json
similarity index 100%
rename from contracts/cw20-stake/schema/claims_response.json
rename to contracts/stake-cw20/schema/claims_response.json
diff --git a/contracts/cw20-stake/schema/execute_msg.json b/contracts/stake-cw20/schema/execute_msg.json
similarity index 100%
rename from contracts/cw20-stake/schema/execute_msg.json
rename to contracts/stake-cw20/schema/execute_msg.json
diff --git a/contracts/cw20-stake/schema/get_config_response.json b/contracts/stake-cw20/schema/get_config_response.json
similarity index 100%
rename from contracts/cw20-stake/schema/get_config_response.json
rename to contracts/stake-cw20/schema/get_config_response.json
diff --git a/contracts/cw20-stake/schema/get_hooks_response.json b/contracts/stake-cw20/schema/get_hooks_response.json
similarity index 100%
rename from contracts/cw20-stake/schema/get_hooks_response.json
rename to contracts/stake-cw20/schema/get_hooks_response.json
diff --git a/contracts/cw20-stake/schema/instantiate_msg.json b/contracts/stake-cw20/schema/instantiate_msg.json
similarity index 100%
rename from contracts/cw20-stake/schema/instantiate_msg.json
rename to contracts/stake-cw20/schema/instantiate_msg.json
diff --git a/contracts/cw20-stake/schema/query_msg.json b/contracts/stake-cw20/schema/query_msg.json
similarity index 100%
rename from contracts/cw20-stake/schema/query_msg.json
rename to contracts/stake-cw20/schema/query_msg.json
diff --git a/contracts/cw20-stake/schema/staked_balance_at_height_response.json b/contracts/stake-cw20/schema/staked_balance_at_height_response.json
similarity index 100%
rename from contracts/cw20-stake/schema/staked_balance_at_height_response.json
rename to contracts/stake-cw20/schema/staked_balance_at_height_response.json
diff --git a/contracts/cw20-stake/schema/staked_value_response.json b/contracts/stake-cw20/schema/staked_value_response.json
similarity index 100%
rename from contracts/cw20-stake/schema/staked_value_response.json
rename to contracts/stake-cw20/schema/staked_value_response.json
diff --git a/contracts/cw20-stake/schema/token_info_response.json b/contracts/stake-cw20/schema/token_info_response.json
similarity index 100%
rename from contracts/cw20-stake/schema/token_info_response.json
rename to contracts/stake-cw20/schema/token_info_response.json
diff --git a/contracts/cw20-stake/schema/total_staked_at_height_response.json b/contracts/stake-cw20/schema/total_staked_at_height_response.json
similarity index 100%
rename from contracts/cw20-stake/schema/total_staked_at_height_response.json
rename to contracts/stake-cw20/schema/total_staked_at_height_response.json
diff --git a/contracts/cw20-stake/schema/total_value_response.json b/contracts/stake-cw20/schema/total_value_response.json
similarity index 100%
rename from contracts/cw20-stake/schema/total_value_response.json
rename to contracts/stake-cw20/schema/total_value_response.json
diff --git a/contracts/cw20-stake/src/contract.rs b/contracts/stake-cw20/src/contract.rs
similarity index 99%
rename from contracts/cw20-stake/src/contract.rs
rename to contracts/stake-cw20/src/contract.rs
index 8a8f2e77..5ab97131 100644
--- a/contracts/cw20-stake/src/contract.rs
+++ b/contracts/stake-cw20/src/contract.rs
@@ -32,7 +32,7 @@ pub use cw20_base::enumerable::{query_all_accounts, query_all_allowances};
 use cw_controllers::ClaimsResponse;
 use cw_utils::Duration;
 
-const CONTRACT_NAME: &str = "crates.io:cw20-stake";
+const CONTRACT_NAME: &str = "crates.io:stake_cw20";
 const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");
 
 #[cfg_attr(not(feature = "library"), entry_point)]
diff --git a/legacy/cw3-dao/Cargo.toml b/legacy/cw3-dao/Cargo.toml
index 148193c0..bebff6a8 100644
--- a/legacy/cw3-dao/Cargo.toml
+++ b/legacy/cw3-dao/Cargo.toml
@@ -21,7 +21,7 @@ cw2 = { version = "0.11" }
 cw3 = { version = "0.11" }
 cw20 = "0.11"
 cw20-base = {  version = "0.11", features = ["library"] }
-stake-cw20 = { path = "../../contracts/cw20-stake" }
+stake-cw20 = { path = "../../contracts/stake-cw20" } 
 cw-storage-plus = {  version = "0.11" }
 cosmwasm-std = { version = "1.0.0" }
 schemars = "0.8.8"


Test patch:
diff --git a/contracts/cw-core/src/tests.rs b/contracts/cw-core/src/tests.rs
index 68cf1578..cf1f9f9c 100644
--- a/contracts/cw-core/src/tests.rs
+++ b/contracts/cw-core/src/tests.rs
@@ -8,7 +8,10 @@ use crate::{
     msg::{
         Admin, ExecuteMsg, InitialItem, InstantiateMsg, MigrateMsg, ModuleInstantiateInfo, QueryMsg,
     },
-    query::{Cw20BalanceResponse, DumpStateResponse, GetItemResponse, PauseInfoResponse},
+    query::{
+        AdminNominationResponse, Cw20BalanceResponse, DumpStateResponse, GetItemResponse,
+        PauseInfoResponse,
+    },
     state::Config,
     ContractError,
 };
@@ -890,22 +893,23 @@ fn test_admin_permissions() {
     let res = app.execute_contract(
         Addr::unchecked("rando"),
         core_addr.clone(),
-        &ExecuteMsg::UpdateAdmin {
-            admin: Some(Addr::unchecked("rando")),
+        &ExecuteMsg::NominateAdmin {
+            admin: Some("rando".to_string()),
         },
         &[],
     );
     assert!(res.is_err());
 
-    // Update Admin can't be called, even by the core module
+    // Nominate admin can be called by core contract as no admin was
+    // specified so the admin defaulted to the core contract.
     let res = app.execute_contract(
-        core_addr.clone(),
+        proposal_module.clone(),
         core_addr.clone(),
         &ExecuteMsg::ExecuteProposalHook {
             msgs: vec![WasmMsg::Execute {
                 contract_addr: core_addr.to_string(),
-                msg: to_binary(&ExecuteMsg::UpdateAdmin {
-                    admin: Some(Addr::unchecked("meow")),
+                msg: to_binary(&ExecuteMsg::NominateAdmin {
+                    admin: Some("meow".to_string()),
                 })
                 .unwrap(),
                 funds: vec![],
@@ -914,7 +918,7 @@ fn test_admin_permissions() {
         },
         &[],
     );
-    assert!(res.is_err());
+    assert!(res.is_ok());
 
     // Instantiate new DAO with an admin
     let (core_with_admin_addr, mut app) =
@@ -972,23 +976,295 @@ fn test_admin_permissions() {
     // DAO unpauses after 10 blocks
     app.update_block(|mut block| block.height += 11);
 
-    // Admin can update the admin.
+    // Admin can nominate a new admin.
     let res = app.execute_contract(
         Addr::unchecked("admin"),
         core_with_admin_addr.clone(),
-        &ExecuteMsg::UpdateAdmin {
-            admin: Some(Addr::unchecked("meow")),
+        &ExecuteMsg::NominateAdmin {
+            admin: Some("meow".to_string()),
         },
         &[],
     );
     assert!(res.is_ok());
 
+    let nomination: AdminNominationResponse = app
+        .wrap()
+        .query_wasm_smart(core_with_admin_addr.clone(), &QueryMsg::AdminNomination {})
+        .unwrap();
+    assert_eq!(
+        nomination,
+        AdminNominationResponse {
+            nomination: Some(Addr::unchecked("meow"))
+        }
+    );
+
+    // Check that admin has not yet been updated
+    let res: Addr = app
+        .wrap()
+        .query_wasm_smart(core_with_admin_addr.clone(), &QueryMsg::Admin {})
+        .unwrap();
+    assert_eq!(res, Addr::unchecked("admin"));
+
+    // Only the nominated address may accept the nomination.
+    let err: ContractError = app
+        .execute_contract(
+            Addr::unchecked("random"),
+            core_with_admin_addr.clone(),
+            &ExecuteMsg::AcceptAdminNomination {},
+            &[],
+        )
+        .unwrap_err()
+        .downcast()
+        .unwrap();
+    assert_eq!(err, ContractError::Unauthorized {});
+
+    // Accept the nomination.
+    app.execute_contract(
+        Addr::unchecked("meow"),
+        core_with_admin_addr.clone(),
+        &ExecuteMsg::AcceptAdminNomination {},
+        &[],
+    )
+    .unwrap();
+
     // Check that admin has been updated
-    let res: Option<Addr> = app
+    let res: Addr = app
+        .wrap()
+        .query_wasm_smart(core_with_admin_addr.clone(), &QueryMsg::Admin {})
+        .unwrap();
+    assert_eq!(res, Addr::unchecked("meow"));
+
+    // Check that the pending admin has been cleared.
+    let nomination: AdminNominationResponse = app
+        .wrap()
+        .query_wasm_smart(core_with_admin_addr, &QueryMsg::AdminNomination {})
+        .unwrap();
+    assert_eq!(nomination, AdminNominationResponse { nomination: None });
+}
+
+#[test]
+fn test_admin_nomination() {
+    let (core_addr, mut app) = do_standard_instantiate(true, Some("admin".to_string()));
+
+    // Check that there is no pending nominations.
+    let nomination: AdminNominationResponse = app
+        .wrap()
+        .query_wasm_smart(core_addr.clone(), &QueryMsg::AdminNomination {})
+        .unwrap();
+    assert_eq!(nomination, AdminNominationResponse { nomination: None });
+
+    // Nominate a new admin.
+    app.execute_contract(
+        Addr::unchecked("admin"),
+        core_addr.clone(),
+        &ExecuteMsg::NominateAdmin {
+            admin: Some("ekez".to_string()),
+        },
+        &[],
+    )
+    .unwrap();
+
+    // Check that the nomination is in place.
+    let nomination: AdminNominationResponse = app
+        .wrap()
+        .query_wasm_smart(core_addr.clone(), &QueryMsg::AdminNomination {})
+        .unwrap();
+    assert_eq!(
+        nomination,
+        AdminNominationResponse {
+            nomination: Some(Addr::unchecked("ekez"))
+        }
+    );
+
+    // Non-admin can not withdraw.
+    let err: ContractError = app
+        .execute_contract(
+            Addr::unchecked("ekez"),
+            core_addr.clone(),
+            &ExecuteMsg::WithdrawAdminNomination {},
+            &[],
+        )
+        .unwrap_err()
+        .downcast()
+        .unwrap();
+    assert_eq!(err, ContractError::Unauthorized {});
+
+    // Admin can withdraw.
+    app.execute_contract(
+        Addr::unchecked("admin"),
+        core_addr.clone(),
+        &ExecuteMsg::WithdrawAdminNomination {},
+        &[],
+    )
+    .unwrap();
+
+    // Check that the nomination is withdrawn.
+    let nomination: AdminNominationResponse = app
+        .wrap()
+        .query_wasm_smart(core_addr.clone(), &QueryMsg::AdminNomination {})
+        .unwrap();
+    assert_eq!(nomination, AdminNominationResponse { nomination: None });
+
+    // Can not withdraw if no nomination is pending.
+    let err: ContractError = app
+        .execute_contract(
+            Addr::unchecked("admin"),
+            core_addr.clone(),
+            &ExecuteMsg::WithdrawAdminNomination {},
+            &[],
+        )
+        .unwrap_err()
+        .downcast()
+        .unwrap();
+    assert_eq!(err, ContractError::NoAdminNomination {});
+
+    // Can not claim nomination b/c it has been withdrawn.
+    let err: ContractError = app
+        .execute_contract(
+            Addr::unchecked("ekez"),
+            core_addr.clone(),
+            &ExecuteMsg::AcceptAdminNomination {},
+            &[],
+        )
+        .unwrap_err()
+        .downcast()
+        .unwrap();
+    assert_eq!(err, ContractError::NoAdminNomination {});
+
+    // Nominate a new admin.
+    app.execute_contract(
+        Addr::unchecked("admin"),
+        core_addr.clone(),
+        &ExecuteMsg::NominateAdmin {
+            admin: Some("meow".to_string()),
+        },
+        &[],
+    )
+    .unwrap();
+
+    // A new nomination can not be created if there is already a
+    // pending nomination.
+    let err: ContractError = app
+        .execute_contract(
+            Addr::unchecked("admin"),
+            core_addr.clone(),
+            &ExecuteMsg::NominateAdmin {
+                admin: Some("arthur".to_string()),
+            },
+            &[],
+        )
+        .unwrap_err()
+        .downcast()
+        .unwrap();
+    assert_eq!(err, ContractError::PendingNomination {});
+
+    // Only nominated admin may accept.
+    let err: ContractError = app
+        .execute_contract(
+            Addr::unchecked("ekez"),
+            core_addr.clone(),
+            &ExecuteMsg::AcceptAdminNomination {},
+            &[],
+        )
+        .unwrap_err()
+        .downcast()
+        .unwrap();
+    assert_eq!(err, ContractError::Unauthorized {});
+
+    app.execute_contract(
+        Addr::unchecked("meow"),
+        core_addr.clone(),
+        &ExecuteMsg::AcceptAdminNomination {},
+        &[],
+    )
+    .unwrap();
+
+    // Check that meow is the new admin.
+    let admin: Addr = app
+        .wrap()
+        .query_wasm_smart(core_addr.clone(), &QueryMsg::Admin {})
+        .unwrap();
+    assert_eq!(admin, Addr::unchecked("meow".to_string()));
+
+    let start_height = app.block_info().height;
+    // Check that the new admin can do admin things and the old can not.
+    let err: ContractError = app
+        .execute_contract(
+            Addr::unchecked("admin"),
+            core_addr.clone(),
+            &ExecuteMsg::ExecuteAdminMsgs {
+                msgs: vec![WasmMsg::Execute {
+                    contract_addr: core_addr.to_string(),
+                    msg: to_binary(&ExecuteMsg::Pause {
+                        duration: Duration::Height(10),
+                    })
+                    .unwrap(),
+                    funds: vec![],
+                }
+                .into()],
+            },
+            &[],
+        )
+        .unwrap_err()
+        .downcast()
+        .unwrap();
+    assert_eq!(err, ContractError::Unauthorized {});
+
+    let res = app.execute_contract(
+        Addr::unchecked("meow"),
+        core_addr.clone(),
+        &ExecuteMsg::ExecuteAdminMsgs {
+            msgs: vec![WasmMsg::Execute {
+                contract_addr: core_addr.to_string(),
+                msg: to_binary(&ExecuteMsg::Pause {
+                    duration: Duration::Height(10),
+                })
+                .unwrap(),
+                funds: vec![],
+            }
+            .into()],
+        },
+        &[],
+    );
+    assert!(res.is_ok());
+
+    let paused: PauseInfoResponse = app
+        .wrap()
+        .query_wasm_smart(core_addr.clone(), &QueryMsg::PauseInfo {})
+        .unwrap();
+    assert_eq!(
+        paused,
+        PauseInfoResponse::Paused {
+            expiration: Expiration::AtHeight(start_height + 10)
+        }
+    );
+
+    // DAO unpauses after 10 blocks
+    app.update_block(|mut block| block.height += 11);
+
+    // Remove the admin.
+    app.execute_contract(
+        Addr::unchecked("meow"),
+        core_addr.clone(),
+        &ExecuteMsg::NominateAdmin { admin: None },
+        &[],
+    )
+    .unwrap();
+
+    // Check that this has not caused an admin to be nominated.
+    let nomination: AdminNominationResponse = app
+        .wrap()
+        .query_wasm_smart(core_addr.clone(), &QueryMsg::AdminNomination {})
+        .unwrap();
+    assert_eq!(nomination, AdminNominationResponse { nomination: None });
+
+    // Check that admin has been updated. As there was no admin
+    // nominated the admin should revert back to the contract address.
+    let res: Addr = app
         .wrap()
-        .query_wasm_smart(core_with_admin_addr, &QueryMsg::Admin {})
+        .query_wasm_smart(core_addr.clone(), &QueryMsg::Admin {})
         .unwrap();
-    assert_eq!(res, Some(Addr::unchecked("meow")));
+    assert_eq!(res, core_addr);
 }
 
 #[test]
diff --git a/contracts/cw-proposal-single/src/staking_tests.rs b/contracts/cw-proposal-single/src/staking_tests.rs
index c6e1d3fd..2c0d59d7 100644
--- a/contracts/cw-proposal-single/src/staking_tests.rs
+++ b/contracts/cw-proposal-single/src/staking_tests.rs
@@ -39,11 +39,11 @@ fn staked_balances_voting() -> Box<dyn Contract<Empty>> {
     Box::new(contract)
 }
 
-fn cw20_stake() -> Box<dyn Contract<Empty>> {
+fn stake_cw20() -> Box<dyn Contract<Empty>> {
     let contract = ContractWrapper::new(
-        cw20_stake::contract::execute,
-        cw20_stake::contract::instantiate,
-        cw20_stake::contract::query,
+        stake_cw20::contract::execute,
+        stake_cw20::contract::instantiate,
+        stake_cw20::contract::query,
     );
     Box::new(contract)
 }
@@ -64,7 +64,7 @@ fn instantiate_with_staked_balances_voting() {
 
     let govmod_id = app.store_code(single_govmod_contract());
     let cw20_id = app.store_code(cw20_contract());
-    let cw20_stake_id = app.store_code(cw20_stake());
+    let stake_cw20_id = app.store_code(stake_cw20());
     let core_contract_id = app.store_code(core_contract());
     let staked_balances_voting_id = app.store_code(staked_balances_voting());
 
@@ -90,7 +90,7 @@ fn instantiate_with_staked_balances_voting() {
                         amount: Uint128::new(100),
                     }],
                     marketing: None,
-                    staking_code_id: cw20_stake_id,
+                    staking_code_id: stake_cw20_id,
                     unstaking_duration: Some(Duration::Height(10u64)),
                     initial_dao_balance: Some(Uint128::new(100)),
                 },
@@ -109,7 +109,6 @@ fn instantiate_with_staked_balances_voting() {
                     quorum: voting::PercentageThreshold::Percent(Decimal::percent(30)),
                 },
                 max_voting_period: Duration::Height(10u64),
-                min_voting_period: None,
                 only_members_execute: true,
                 allow_revoting: false,
                 deposit_info: None,
diff --git a/contracts/cw-proposal-single/src/tests.rs b/contracts/cw-proposal-single/src/tests.rs
index 2258d92d..e7eb702f 100644
--- a/contracts/cw-proposal-single/src/tests.rs
+++ b/contracts/cw-proposal-single/src/tests.rs
@@ -34,9 +34,9 @@ fn cw20_contract() -> Box<dyn Contract<Empty>> {
 
 fn cw20_stake_contract() -> Box<dyn Contract<Empty>> {
     let contract = ContractWrapper::new(
-        cw20_stake::contract::execute,
-        cw20_stake::contract::instantiate,
-        cw20_stake::contract::query,
+        stake_cw20::contract::execute,
+        stake_cw20::contract::instantiate,
+        stake_cw20::contract::query,
     );
     Box::new(contract)
 }
@@ -92,11 +92,11 @@ fn staked_balances_voting() -> Box<dyn Contract<Empty>> {
     Box::new(contract)
 }
 
-fn cw20_stake() -> Box<dyn Contract<Empty>> {
+fn stake_cw20() -> Box<dyn Contract<Empty>> {
     let contract = ContractWrapper::new(
-        cw20_stake::contract::execute,
-        cw20_stake::contract::instantiate,
-        cw20_stake::contract::query,
+        stake_cw20::contract::execute,
+        stake_cw20::contract::instantiate,
+        stake_cw20::contract::query,
     );
     Box::new(contract)
 }
@@ -150,7 +150,7 @@ fn instantiate_with_staked_balances_governance(
     };
 
     let cw20_id = app.store_code(cw20_contract());
-    let cw20_stake_id = app.store_code(cw20_stake());
+    let stake_cw20_id = app.store_code(stake_cw20());
     let staked_balances_voting_id = app.store_code(staked_balances_voting());
     let core_contract_id = app.store_code(cw_gov_contract());
 
@@ -173,7 +173,7 @@ fn instantiate_with_staked_balances_governance(
                     decimals: 6,
                     initial_balances: initial_balances.clone(),
                     marketing: None,
-                    staking_code_id: cw20_stake_id,
+                    staking_code_id: stake_cw20_id,
                     unstaking_duration: Some(Duration::Height(6)),
                     initial_dao_balance: None,
                 },
@@ -231,7 +231,7 @@ fn instantiate_with_staked_balances_governance(
             &cw20::Cw20ExecuteMsg::Send {
                 contract: staking_contract.to_string(),
                 amount,
-                msg: to_binary(&cw20_stake::msg::ReceiveMsg::Stake {}).unwrap(),
+                msg: to_binary(&stake_cw20::msg::ReceiveMsg::Stake {}).unwrap(),
             },
             &[],
         )
@@ -550,7 +550,6 @@ where
     let instantiate = InstantiateMsg {
         threshold,
         max_voting_period,
-        min_voting_period: None,
         only_members_execute: false,
         allow_revoting: false,
         deposit_info,
@@ -706,7 +705,6 @@ fn test_propose() {
     let instantiate = InstantiateMsg {
         threshold: threshold.clone(),
         max_voting_period,
-        min_voting_period: None,
         only_members_execute: false,
         allow_revoting: false,
         deposit_info: None,
@@ -736,7 +734,6 @@ fn test_propose() {
     let expected = Config {
         threshold: threshold.clone(),
         max_voting_period,
-        min_voting_period: None,
         only_members_execute: false,
         allow_revoting: false,
         dao: governance_addr,
@@ -768,7 +765,6 @@ fn test_propose() {
         proposer: Addr::unchecked(CREATOR_ADDR),
         start_height: current_block.height,
         expiration: max_voting_period.after(&current_block),
-        min_voting_period: None,
         threshold,
         allow_revoting: false,
         total_power: Uint128::new(100_000_000),
@@ -794,7 +790,6 @@ fn test_propose_supports_stargate_message() {
     let instantiate = InstantiateMsg {
         threshold: threshold.clone(),
         max_voting_period,
-        min_voting_period: None,
         only_members_execute: false,
         allow_revoting: false,
         deposit_info: None,
@@ -843,7 +838,6 @@ fn test_propose_supports_stargate_message() {
         proposer: Addr::unchecked(CREATOR_ADDR),
         start_height: current_block.height,
         expiration: max_voting_period.after(&current_block),
-        min_voting_period: None,
         threshold,
         allow_revoting: false,
         total_power: Uint128::new(100_000_000),
@@ -964,7 +958,6 @@ fn test_voting_module_token_proposal_deposit_instantiate() {
     let instantiate = InstantiateMsg {
         threshold,
         max_voting_period,
-        min_voting_period: None,
         only_members_execute: false,
         allow_revoting: false,
         deposit_info: Some(DepositInfo {
@@ -1041,7 +1034,6 @@ fn test_different_token_proposal_deposit() {
     let instantiate = InstantiateMsg {
         threshold,
         max_voting_period,
-        min_voting_period: None,
         only_members_execute: false,
         allow_revoting: false,
         deposit_info: Some(DepositInfo {
@@ -1098,7 +1090,6 @@ fn test_bad_token_proposal_deposit() {
     let instantiate = InstantiateMsg {
         threshold,
         max_voting_period,
-        min_voting_period: None,
         only_members_execute: false,
         allow_revoting: false,
         deposit_info: Some(DepositInfo {
@@ -1125,7 +1116,6 @@ fn test_take_proposal_deposit() {
     let instantiate = InstantiateMsg {
         threshold,
         max_voting_period,
-        min_voting_period: None,
         only_members_execute: false,
         allow_revoting: false,
         deposit_info: Some(DepositInfo {
@@ -1475,7 +1465,6 @@ fn test_execute_expired_proposal() {
                 quorum: PercentageThreshold::Percent(Decimal::percent(10)),
             },
             max_voting_period: Duration::Height(10),
-            min_voting_period: None,
             only_members_execute: true,
             allow_revoting: false,
             deposit_info: None,
@@ -1636,7 +1625,6 @@ fn test_update_config() {
                 percentage: PercentageThreshold::Majority {},
             },
             max_voting_period: cw_utils::Duration::Height(10),
-            min_voting_period: None,
             only_members_execute: false,
             allow_revoting: false,
             dao: CREATOR_ADDR.to_string(),
@@ -1655,7 +1643,6 @@ fn test_update_config() {
                 percentage: PercentageThreshold::Majority {},
             },
             max_voting_period: cw_utils::Duration::Height(10),
-            min_voting_period: None,
             only_members_execute: false,
             allow_revoting: false,
             dao: CREATOR_ADDR.to_string(),
@@ -1675,7 +1662,6 @@ fn test_update_config() {
             percentage: PercentageThreshold::Majority {},
         },
         max_voting_period: cw_utils::Duration::Height(10),
-        min_voting_period: None,
         only_members_execute: false,
         allow_revoting: false,
         dao: Addr::unchecked(CREATOR_ADDR),
@@ -1693,7 +1679,6 @@ fn test_update_config() {
                 percentage: PercentageThreshold::Majority {},
             },
             max_voting_period: cw_utils::Duration::Height(10),
-            min_voting_period: None,
             only_members_execute: false,
             allow_revoting: false,
             dao: CREATOR_ADDR.to_string(),
@@ -1776,7 +1761,6 @@ fn test_query_list_proposals() {
                 quorum: PercentageThreshold::Percent(Decimal::percent(0)),
             },
             max_voting_period: cw_utils::Duration::Height(100),
-            min_voting_period: None,
             only_members_execute: true,
             allow_revoting: false,
             deposit_info: None,
@@ -1848,7 +1832,6 @@ fn test_query_list_proposals() {
             proposer: Addr::unchecked(CREATOR_ADDR),
             start_height: app.block_info().height,
             expiration: cw_utils::Expiration::AtHeight(app.block_info().height + 100),
-            min_voting_period: None,
             threshold: Threshold::ThresholdQuorum {
                 threshold: PercentageThreshold::Majority {},
                 quorum: PercentageThreshold::Percent(Decimal::percent(0)),
@@ -1893,7 +1876,6 @@ fn test_query_list_proposals() {
             proposer: Addr::unchecked(CREATOR_ADDR),
             start_height: app.block_info().height,
             expiration: cw_utils::Expiration::AtHeight(app.block_info().height + 100),
-            min_voting_period: None,
             threshold: Threshold::ThresholdQuorum {
                 threshold: PercentageThreshold::Majority {},
                 quorum: PercentageThreshold::Percent(Decimal::percent(0)),
@@ -1925,7 +1907,6 @@ fn test_hooks() {
     let instantiate = InstantiateMsg {
         threshold,
         max_voting_period,
-        min_voting_period: None,
         only_members_execute: false,
         allow_revoting: false,
         deposit_info: None,
@@ -2099,7 +2080,6 @@ fn test_active_threshold_absolute() {
     let instantiate = InstantiateMsg {
         threshold,
         max_voting_period,
-        min_voting_period: None,
         only_members_execute: false,
         allow_revoting: false,
         deposit_info: None,
@@ -2170,7 +2150,7 @@ fn test_active_threshold_absolute() {
     let msg = cw20::Cw20ExecuteMsg::Send {
         contract: staking_contract.to_string(),
         amount: Uint128::new(100),
-        msg: to_binary(&cw20_stake::msg::ReceiveMsg::Stake {}).unwrap(),
+        msg: to_binary(&stake_cw20::msg::ReceiveMsg::Stake {}).unwrap(),
     };
     app.execute_contract(Addr::unchecked(CREATOR_ADDR), token_contract, &msg, &[])
         .unwrap();
@@ -2191,7 +2171,7 @@ fn test_active_threshold_absolute() {
         .unwrap();
 
     // Unstake some tokens to make it inactive again
-    let msg = cw20_stake::msg::ExecuteMsg::Unstake {
+    let msg = stake_cw20::msg::ExecuteMsg::Unstake {
         amount: Uint128::new(50),
     };
     app.execute_contract(Addr::unchecked(CREATOR_ADDR), staking_contract, &msg, &[])
@@ -2225,7 +2205,6 @@ fn test_active_threshold_percent() {
     let instantiate = InstantiateMsg {
         threshold,
         max_voting_period,
-        min_voting_period: None,
         only_members_execute: false,
         allow_revoting: false,
         deposit_info: None,
@@ -2297,7 +2276,7 @@ fn test_active_threshold_percent() {
     let msg = cw20::Cw20ExecuteMsg::Send {
         contract: staking_contract.to_string(),
         amount: Uint128::new(20000000),
-        msg: to_binary(&cw20_stake::msg::ReceiveMsg::Stake {}).unwrap(),
+        msg: to_binary(&stake_cw20::msg::ReceiveMsg::Stake {}).unwrap(),
     };
     app.execute_contract(Addr::unchecked(CREATOR_ADDR), token_contract, &msg, &[])
         .unwrap();
@@ -2318,7 +2297,7 @@ fn test_active_threshold_percent() {
         .unwrap();
 
     // Unstake some tokens to make it inactive again
-    let msg = cw20_stake::msg::ExecuteMsg::Unstake {
+    let msg = stake_cw20::msg::ExecuteMsg::Unstake {
         amount: Uint128::new(1000),
     };
     app.execute_contract(Addr::unchecked(CREATOR_ADDR), staking_contract, &msg, &[])
@@ -2352,7 +2331,6 @@ fn test_active_threshold_none() {
     let instantiate = InstantiateMsg {
         threshold,
         max_voting_period,
-        min_voting_period: None,
         only_members_execute: false,
         allow_revoting: false,
         deposit_info: None,
@@ -2402,7 +2380,7 @@ fn test_active_threshold_none() {
     let msg = cw20::Cw20ExecuteMsg::Send {
         contract: staking_contract.to_string(),
         amount: Uint128::new(2000),
-        msg: to_binary(&cw20_stake::msg::ReceiveMsg::Stake {}).unwrap(),
+        msg: to_binary(&stake_cw20::msg::ReceiveMsg::Stake {}).unwrap(),
     };
     app.execute_contract(Addr::unchecked(CREATOR_ADDR), token_contract, &msg, &[])
         .unwrap();
@@ -2431,7 +2409,6 @@ fn test_active_threshold_none() {
     let instantiate = InstantiateMsg {
         threshold,
         max_voting_period,
-        min_voting_period: None,
         only_members_execute: false,
         allow_revoting: false,
         deposit_info: None,
@@ -2482,7 +2459,6 @@ fn test_revoting() {
                 quorum: PercentageThreshold::Percent(Decimal::percent(10)),
             },
             max_voting_period: Duration::Height(10),
-            min_voting_period: None,
             only_members_execute: true,
             allow_revoting: true,
             deposit_info: None,
@@ -2608,7 +2584,6 @@ fn test_allow_revoting_config_changes() {
                 quorum: PercentageThreshold::Percent(Decimal::percent(10)),
             },
             max_voting_period: Duration::Height(10),
-            min_voting_period: None,
             only_members_execute: true,
             allow_revoting: true,
             deposit_info: None,
@@ -2654,7 +2629,6 @@ fn test_allow_revoting_config_changes() {
                 quorum: PercentageThreshold::Percent(Decimal::percent(10)),
             },
             max_voting_period: Duration::Height(10),
-            min_voting_period: None,
             only_members_execute: true,
             allow_revoting: false,
             deposit_info: None,
@@ -2751,7 +2725,6 @@ fn test_revoting_same_vote_twice() {
                 quorum: PercentageThreshold::Percent(Decimal::percent(10)),
             },
             max_voting_period: Duration::Height(10),
-            min_voting_period: None,
             only_members_execute: true,
             allow_revoting: true,
             deposit_info: None,
@@ -2853,7 +2826,6 @@ fn test_three_of_five_multisig() {
                 threshold: Uint128::new(3),
             },
             max_voting_period: Duration::Height(10),
-            min_voting_period: None,
             only_members_execute: true,
             allow_revoting: false,
             deposit_info: None,
@@ -2979,7 +2951,6 @@ fn test_three_of_five_multisig_reject() {
                 threshold: Uint128::new(3),
             },
             max_voting_period: Duration::Height(10),
-            min_voting_period: None,
             only_members_execute: true,
             allow_revoting: false,
             deposit_info: None,
@@ -3111,7 +3082,6 @@ fn test_voting_module_token_with_multisig_style_voting() {
                 threshold: Uint128::new(3),
             },
             max_voting_period: Duration::Height(10),
-            min_voting_period: None,
             only_members_execute: true,
             allow_revoting: false,
             deposit_info: Some(DepositInfo {
@@ -3150,7 +3120,6 @@ fn test_three_of_five_multisig_revoting() {
                 threshold: Uint128::new(3),
             },
             max_voting_period: Duration::Height(10),
-            min_voting_period: None,
             only_members_execute: true,
             allow_revoting: true,
             deposit_info: None,
@@ -3383,7 +3352,6 @@ fn test_migrate() {
     let instantiate = InstantiateMsg {
         threshold,
         max_voting_period,
-        min_voting_period: None,
         only_members_execute: false,
         allow_revoting: false,
         deposit_info: None,
@@ -3441,7 +3409,6 @@ fn test_proposal_count_initialized_to_zero() {
                 quorum: PercentageThreshold::Percent(Decimal::percent(10)),
             },
             max_voting_period: Duration::Height(10),
-            min_voting_period: None,
             only_members_execute: true,
             allow_revoting: false,
             deposit_info: None,
@@ -3473,251 +3440,3 @@ fn test_proposal_count_initialized_to_zero() {
         .unwrap();
     assert_eq!(proposal_count, 0);
 }
-
-#[test]
-fn test_no_early_pass_with_min_duration() {
-    let mut app = App::default();
-    let govmod_id = app.store_code(single_proposal_contract());
-    let core_addr = instantiate_with_staked_balances_governance(
-        &mut app,
-        govmod_id,
-        InstantiateMsg {
-            threshold: Threshold::ThresholdQuorum {
-                threshold: PercentageThreshold::Majority {},
-                quorum: PercentageThreshold::Percent(Decimal::percent(10)),
-            },
-            max_voting_period: Duration::Height(10),
-            min_voting_period: Some(Duration::Height(2)),
-            only_members_execute: true,
-            allow_revoting: false,
-            deposit_info: None,
-        },
-        Some(vec![
-            Cw20Coin {
-                address: "ekez".to_string(),
-                amount: Uint128::new(10),
-            },
-            Cw20Coin {
-                address: "wale".to_string(),
-                amount: Uint128::new(90),
-            },
-        ]),
-    );
-
-    let gov_state: cw_core::query::DumpStateResponse = app
-        .wrap()
-        .query_wasm_smart(core_addr, &cw_core::msg::QueryMsg::DumpState {})
-        .unwrap();
-    let proposal_modules = gov_state.proposal_modules;
-
-    assert_eq!(proposal_modules.len(), 1);
-    let proposal_single = proposal_modules.into_iter().next().unwrap();
-
-    app.execute_contract(
-        Addr::unchecked("wale"),
-        proposal_single.clone(),
-        &ExecuteMsg::Propose {
-            title: "A simple text proposal".to_string(),
-            description: "This is a simple text proposal".to_string(),
-            msgs: vec![],
-        },
-        &[],
-    )
-    .unwrap();
-
-    // Wale votes yes which under normal curcumstances would cause the
-    // proposal to pass. Because there is a min duration it does not.
-    app.execute_contract(
-        Addr::unchecked("wale"),
-        proposal_single.clone(),
-        &ExecuteMsg::Vote {
-            proposal_id: 1,
-            vote: Vote::Yes,
-        },
-        &[],
-    )
-    .unwrap();
-
-    let proposal: ProposalResponse = app
-        .wrap()
-        .query_wasm_smart(
-            proposal_single.clone(),
-            &QueryMsg::Proposal { proposal_id: 1 },
-        )
-        .unwrap();
-
-    assert_eq!(proposal.proposal.status, Status::Open);
-
-    // Let the min voting period pass.
-    app.update_block(|b| b.height += 2);
-
-    let proposal: ProposalResponse = app
-        .wrap()
-        .query_wasm_smart(proposal_single, &QueryMsg::Proposal { proposal_id: 1 })
-        .unwrap();
-
-    assert_eq!(proposal.proposal.status, Status::Passed);
-}
-
-#[test]
-#[should_panic(
-    expected = "min_voting_period and max_voting_period must have the same units (height or time)"
-)]
-fn test_min_duration_units_missmatch() {
-    let mut app = App::default();
-    let govmod_id = app.store_code(single_proposal_contract());
-    instantiate_with_staked_balances_governance(
-        &mut app,
-        govmod_id,
-        InstantiateMsg {
-            threshold: Threshold::ThresholdQuorum {
-                threshold: PercentageThreshold::Majority {},
-                quorum: PercentageThreshold::Percent(Decimal::percent(10)),
-            },
-            max_voting_period: Duration::Height(10),
-            min_voting_period: Some(Duration::Time(2)),
-            only_members_execute: true,
-            allow_revoting: false,
-            deposit_info: None,
-        },
-        Some(vec![
-            Cw20Coin {
-                address: "ekez".to_string(),
-                amount: Uint128::new(10),
-            },
-            Cw20Coin {
-                address: "wale".to_string(),
-                amount: Uint128::new(90),
-            },
-        ]),
-    );
-}
-
-#[test]
-#[should_panic(expected = "Min voting period must be less than or equal to max voting period")]
-fn test_min_duration_larger_than_proposal_duration() {
-    let mut app = App::default();
-    let govmod_id = app.store_code(single_proposal_contract());
-    instantiate_with_staked_balances_governance(
-        &mut app,
-        govmod_id,
-        InstantiateMsg {
-            threshold: Threshold::ThresholdQuorum {
-                threshold: PercentageThreshold::Majority {},
-                quorum: PercentageThreshold::Percent(Decimal::percent(10)),
-            },
-            max_voting_period: Duration::Height(10),
-            min_voting_period: Some(Duration::Height(11)),
-            only_members_execute: true,
-            allow_revoting: false,
-            deposit_info: None,
-        },
-        Some(vec![
-            Cw20Coin {
-                address: "ekez".to_string(),
-                amount: Uint128::new(10),
-            },
-            Cw20Coin {
-                address: "wale".to_string(),
-                amount: Uint128::new(90),
-            },
-        ]),
-    );
-}
-
-#[test]
-fn test_min_duration_same_as_proposal_duration() {
-    let mut app = App::default();
-    let govmod_id = app.store_code(single_proposal_contract());
-    let core_addr = instantiate_with_staked_balances_governance(
-        &mut app,
-        govmod_id,
-        InstantiateMsg {
-            threshold: Threshold::ThresholdQuorum {
-                threshold: PercentageThreshold::Majority {},
-                quorum: PercentageThreshold::Percent(Decimal::percent(10)),
-            },
-            max_voting_period: Duration::Time(10),
-            min_voting_period: Some(Duration::Time(10)),
-            only_members_execute: true,
-            allow_revoting: false,
-            deposit_info: None,
-        },
-        Some(vec![
-            Cw20Coin {
-                address: "ekez".to_string(),
-                amount: Uint128::new(10),
-            },
-            Cw20Coin {
-                address: "wale".to_string(),
-                amount: Uint128::new(90),
-            },
-        ]),
-    );
-
-    let gov_state: cw_core::query::DumpStateResponse = app
-        .wrap()
-        .query_wasm_smart(core_addr, &cw_core::msg::QueryMsg::DumpState {})
-        .unwrap();
-    let proposal_modules = gov_state.proposal_modules;
-
-    assert_eq!(proposal_modules.len(), 1);
-    let proposal_single = proposal_modules.into_iter().next().unwrap();
-
-    app.execute_contract(
-        Addr::unchecked("wale"),
-        proposal_single.clone(),
-        &ExecuteMsg::Propose {
-            title: "A simple text proposal".to_string(),
-            description: "This is a simple text proposal".to_string(),
-            msgs: vec![],
-        },
-        &[],
-    )
-    .unwrap();
-
-    // Wale votes yes which under normal curcumstances would cause the
-    // proposal to pass. Because there is a min duration it does not.
-    app.execute_contract(
-        Addr::unchecked("wale"),
-        proposal_single.clone(),
-        &ExecuteMsg::Vote {
-            proposal_id: 1,
-            vote: Vote::Yes,
-        },
-        &[],
-    )
-    .unwrap();
-
-    let proposal: ProposalResponse = app
-        .wrap()
-        .query_wasm_smart(
-            proposal_single.clone(),
-            &QueryMsg::Proposal { proposal_id: 1 },
-        )
-        .unwrap();
-
-    assert_eq!(proposal.proposal.status, Status::Open);
-
-    // ekez can vote no.
-    app.execute_contract(
-        Addr::unchecked("ekez"),
-        proposal_single.clone(),
-        &ExecuteMsg::Vote {
-            proposal_id: 1,
-            vote: Vote::No,
-        },
-        &[],
-    )
-    .unwrap();
-
-    // Let the min voting period pass.
-    app.update_block(|b| b.time = b.time.plus_seconds(10));
-
-    let proposal: ProposalResponse = app
-        .wrap()
-        .query_wasm_smart(proposal_single, &QueryMsg::Proposal { proposal_id: 1 })
-        .unwrap();
-
-    assert_eq!(proposal.proposal.status, Status::Passed);
-}
diff --git a/contracts/cw20-staked-balance-voting/src/tests.rs b/contracts/cw20-staked-balance-voting/src/tests.rs
index 5d87c153..b583d1b7 100644
--- a/contracts/cw20-staked-balance-voting/src/tests.rs
+++ b/contracts/cw20-staked-balance-voting/src/tests.rs
@@ -23,9 +23,9 @@ fn cw20_contract() -> Box<dyn Contract<Empty>> {
 
 fn staking_contract() -> Box<dyn Contract<Empty>> {
     let contract = ContractWrapper::new(
-        cw20_stake::contract::execute,
-        cw20_stake::contract::instantiate,
-        cw20_stake::contract::query,
+        stake_cw20::contract::execute,
+        stake_cw20::contract::instantiate,
+        stake_cw20::contract::query,
     );
     Box::new(contract)
 }
@@ -57,7 +57,7 @@ fn stake_tokens(app: &mut App, staking_addr: Addr, cw20_addr: Addr, sender: &str
     let msg = cw20::Cw20ExecuteMsg::Send {
         contract: staking_addr.to_string(),
         amount: Uint128::new(amount),
-        msg: to_binary(&cw20_stake::msg::ReceiveMsg::Stake {}).unwrap(),
+        msg: to_binary(&stake_cw20::msg::ReceiveMsg::Stake {}).unwrap(),
     };
     app.execute_contract(Addr::unchecked(sender), cw20_addr, &msg, &[])
         .unwrap();
diff --git a/contracts/cw20-stake-reward-distributor/src/lib.rs b/contracts/stake-cw20-reward-distributor/src/lib.rs
similarity index 100%
rename from contracts/cw20-stake-reward-distributor/src/lib.rs
rename to contracts/stake-cw20-reward-distributor/src/lib.rs
diff --git a/contracts/cw20-stake-reward-distributor/src/msg.rs b/contracts/stake-cw20-reward-distributor/src/msg.rs
similarity index 100%
rename from contracts/cw20-stake-reward-distributor/src/msg.rs
rename to contracts/stake-cw20-reward-distributor/src/msg.rs
diff --git a/contracts/cw20-stake-reward-distributor/src/state.rs b/contracts/stake-cw20-reward-distributor/src/state.rs
similarity index 100%
rename from contracts/cw20-stake-reward-distributor/src/state.rs
rename to contracts/stake-cw20-reward-distributor/src/state.rs
diff --git a/contracts/cw20-stake-reward-distributor/src/tests.rs b/contracts/stake-cw20-reward-distributor/src/tests.rs
similarity index 94%
rename from contracts/cw20-stake-reward-distributor/src/tests.rs
rename to contracts/stake-cw20-reward-distributor/src/tests.rs
index 26ff3a79..55ab6bfd 100644
--- a/contracts/cw20-stake-reward-distributor/src/tests.rs
+++ b/contracts/stake-cw20-reward-distributor/src/tests.rs
@@ -22,9 +22,9 @@ pub fn cw20_contract() -> Box<dyn Contract<Empty>> {
 
 fn staking_contract() -> Box<dyn Contract<Empty>> {
     let contract = ContractWrapper::new(
-        cw20_stake::contract::execute,
-        cw20_stake::contract::instantiate,
-        cw20_stake::contract::query,
+        stake_cw20::contract::execute,
+        stake_cw20::contract::instantiate,
+        stake_cw20::contract::query,
     );
     Box::new(contract)
 }
@@ -55,7 +55,7 @@ fn instantiate_cw20(app: &mut App, initial_balances: Vec<Cw20Coin>) -> Addr {
 
 fn instantiate_staking(app: &mut App, cw20_addr: Addr) -> Addr {
     let staking_id = app.store_code(staking_contract());
-    let msg = cw20_stake::msg::InstantiateMsg {
+    let msg = stake_cw20::msg::InstantiateMsg {
         owner: Some(OWNER.to_string()),
         manager: Some(MANAGER.to_string()),
         token_address: cw20_addr.to_string(),
@@ -268,43 +268,24 @@ fn test_distribute() {
     let distributor_info = get_info(&app, distributor_addr.clone());
     assert_eq!(distributor_info.balance, Uint128::new(0));
     assert_eq!(distributor_info.last_payment_block, app.block_info().height);
-    let last_payment_block = distributor_info.last_payment_block;
 
     // Pays out nothing
     app.update_block(|mut block| block.height += 1100);
-    let err: ContractError = app
+    let _err = app
         .execute_contract(
             Addr::unchecked(OWNER),
             distributor_addr.clone(),
             &ExecuteMsg::Distribute {},
             &[],
         )
-        .unwrap_err()
-        .downcast()
         .unwrap();
 
-    assert!(matches!(err, ContractError::ZeroRewards {}));
-
     let staking_balance = get_balance_cw20(&app, cw20_addr, staking_addr);
     assert_eq!(staking_balance, Uint128::new(1000));
 
-    let distributor_info = get_info(&app, distributor_addr.clone());
+    let distributor_info = get_info(&app, distributor_addr);
     assert_eq!(distributor_info.balance, Uint128::new(0));
-    assert_eq!(distributor_info.last_payment_block, last_payment_block);
-
-    // go to a block before the last payment
-    app.update_block(|mut block| block.height -= 2000);
-    let err: ContractError = app
-        .execute_contract(
-            Addr::unchecked(OWNER),
-            distributor_addr,
-            &ExecuteMsg::Distribute {},
-            &[],
-        )
-        .unwrap_err()
-        .downcast()
-        .unwrap();
-    assert!(matches!(err, ContractError::RewardsDistributedForBlock {}));
+    assert_eq!(distributor_info.last_payment_block, app.block_info().height);
 }
 
 #[test]
diff --git a/contracts/cw20-stake/src/error.rs b/contracts/stake-cw20/src/error.rs
similarity index 100%
rename from contracts/cw20-stake/src/error.rs
rename to contracts/stake-cw20/src/error.rs
diff --git a/contracts/cw20-stake/src/hooks.rs b/contracts/stake-cw20/src/hooks.rs
similarity index 100%
rename from contracts/cw20-stake/src/hooks.rs
rename to contracts/stake-cw20/src/hooks.rs
diff --git a/contracts/cw20-stake/src/lib.rs b/contracts/stake-cw20/src/lib.rs
similarity index 100%
rename from contracts/cw20-stake/src/lib.rs
rename to contracts/stake-cw20/src/lib.rs
diff --git a/contracts/cw20-stake/src/msg.rs b/contracts/stake-cw20/src/msg.rs
similarity index 100%
rename from contracts/cw20-stake/src/msg.rs
rename to contracts/stake-cw20/src/msg.rs
diff --git a/contracts/cw20-stake/src/state.rs b/contracts/stake-cw20/src/state.rs
similarity index 100%
rename from contracts/cw20-stake/src/state.rs
rename to contracts/stake-cw20/src/state.rs
diff --git a/debug/proposal-hooks-counter/src/tests.rs b/debug/proposal-hooks-counter/src/tests.rs
index 29d240b8..1c75ffab 100644
--- a/debug/proposal-hooks-counter/src/tests.rs
+++ b/debug/proposal-hooks-counter/src/tests.rs
@@ -136,7 +136,6 @@ fn test_counters() {
     let instantiate = cw_proposal_single::msg::InstantiateMsg {
         threshold,
         max_voting_period,
-        min_voting_period: None,
         only_members_execute: false,
         allow_revoting: false,
         deposit_info: None,