From fb3e104840d971e68920462889473d755f5405bc Mon Sep 17 00:00:00 2001 From: Noah Saso Date: Mon, 22 Jul 2024 13:37:35 -0400 Subject: [PATCH] added pre-propose v2.4.1 migration tests --- Cargo.lock | 292 ++++-- Cargo.toml | 5 + .../Cargo.toml | 9 + .../src/contract.rs | 6 +- .../src/tests.rs | 783 +++++++++++++++- .../dao-pre-propose-approver/src/contract.rs | 6 +- .../dao-pre-propose-multiple/Cargo.toml | 9 + .../dao-pre-propose-multiple/src/contract.rs | 6 +- .../dao-pre-propose-multiple/src/tests.rs | 855 +++++++++++++++++- .../dao-pre-propose-single/Cargo.toml | 9 + .../dao-pre-propose-single/src/contract.rs | 6 +- .../dao-pre-propose-single/src/tests.rs | 725 ++++++++++++++- packages/dao-pre-propose-base/Cargo.toml | 2 +- 13 files changed, 2610 insertions(+), 103 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6abb0e65f..228aafb09 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -272,10 +272,10 @@ dependencies = [ "cw-utils 1.0.3", "cw20 1.1.2", "cw20-stake 2.5.0", - "dao-dao-core", + "dao-dao-core 2.5.0", "dao-interface 2.5.0", - "dao-pre-propose-single", - "dao-proposal-single", + "dao-pre-propose-single 2.5.0", + "dao-proposal-single 2.5.0", "dao-voting 2.5.0", "dao-voting-cw20-staked", "env_logger", @@ -304,9 +304,9 @@ dependencies = [ "cw-storage-plus 1.2.0", "cw-utils 1.0.3", "cw2 1.1.2", - "dao-dao-core", + "dao-dao-core 2.5.0", "dao-interface 2.5.0", - "dao-proposal-single", + "dao-proposal-single 2.5.0", "dao-testing", "dao-voting 2.5.0", "dao-voting-token-staked", @@ -700,12 +700,12 @@ dependencies = [ "cw2 1.1.2", "cw20-base 1.1.2", "cw4 1.1.2", - "dao-dao-core", + "dao-dao-core 2.5.0", "dao-interface 2.5.0", - "dao-proposal-single", + "dao-proposal-single 2.5.0", "dao-testing", "dao-voting 2.5.0", - "dao-voting-cw4", + "dao-voting-cw4 2.5.0", "osmosis-test-tube", "thiserror", ] @@ -858,7 +858,7 @@ dependencies = [ "cw20 1.1.2", "cw20-base 1.1.2", "cw20-stake 2.5.0", - "dao-dao-core", + "dao-dao-core 2.5.0", "dao-interface 2.5.0", "dao-voting-cw20-staked", "thiserror", @@ -944,6 +944,17 @@ dependencies = [ "serde", ] +[[package]] +name = "cw-paginate-storage" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5f050a143e90eac20071c039e735c277866b89cbf1d5ef8a9ab4a0b6f9d9d4" +dependencies = [ + "cosmwasm-std", + "cw-storage-plus 1.2.0", + "serde", +] + [[package]] name = "cw-paginate-storage" version = "2.5.0" @@ -1366,7 +1377,7 @@ dependencies = [ "cw20 1.1.2", "cw20-base 1.1.2", "cw20-stake 0.2.6", - "dao-hooks", + "dao-hooks 2.5.0", "dao-voting 2.5.0", "thiserror", ] @@ -1388,7 +1399,7 @@ dependencies = [ "cw20 1.1.2", "cw20-base 1.1.2", "cw20-stake 2.5.0", - "dao-hooks", + "dao-hooks 2.5.0", "stake-cw20-external-rewards", "thiserror", ] @@ -1640,6 +1651,26 @@ dependencies = [ "cw4 1.1.2", ] +[[package]] +name = "dao-dao-core" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbd16c5f6f2760c66546e1e2f3781106dd796c8920847e78e5984922767cbc68" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-core", + "cw-paginate-storage 2.4.1", + "cw-storage-plus 1.2.0", + "cw-utils 1.0.3", + "cw2 1.1.2", + "cw20 1.1.2", + "cw721 0.18.0", + "dao-dao-macros 2.4.1", + "dao-interface 2.4.1", + "thiserror", +] + [[package]] name = "dao-dao-core" version = "2.5.0" @@ -1689,6 +1720,20 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "dao-hooks" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c85abbadefe491d571f709464a8cfd2fb78b63b0cb6e6ef49104df249e28acc" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-hooks 2.4.1", + "cw4 1.1.2", + "dao-pre-propose-base 2.4.1", + "dao-voting 2.4.1", +] + [[package]] name = "dao-hooks" version = "2.5.0" @@ -1752,14 +1797,31 @@ dependencies = [ "cw20-staked-balance-voting", "cw4 0.13.4", "cw4-voting", - "dao-dao-core", + "dao-dao-core 2.5.0", "dao-interface 2.5.0", - "dao-proposal-single", + "dao-proposal-single 2.5.0", "dao-testing", "dao-voting 0.1.0", "dao-voting 2.5.0", "dao-voting-cw20-staked", - "dao-voting-cw4", + "dao-voting-cw4 2.5.0", + "thiserror", +] + +[[package]] +name = "dao-pre-propose-approval-single" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4fe4e5b50b3081272557069dbecaf1e0984d1f2932d2e61418712460f8fd313" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-paginate-storage 2.4.1", + "cw-storage-plus 1.2.0", + "cw2 1.1.2", + "dao-interface 2.4.1", + "dao-pre-propose-base 2.4.1", + "dao-voting 2.4.1", "thiserror", ] @@ -1777,16 +1839,23 @@ dependencies = [ "cw2 1.1.2", "cw20 1.1.2", "cw20-base 1.1.2", + "cw4 1.1.2", "cw4-group 1.1.2", - "dao-dao-core", - "dao-hooks", + "dao-dao-core 2.4.1", + "dao-dao-core 2.5.0", + "dao-hooks 2.5.0", + "dao-interface 2.4.1", "dao-interface 2.5.0", + "dao-pre-propose-approval-single 2.4.1", "dao-pre-propose-base 2.5.0", - "dao-proposal-single", + "dao-proposal-single 2.4.1", + "dao-proposal-single 2.5.0", "dao-testing", + "dao-voting 2.4.1", "dao-voting 2.5.0", "dao-voting-cw20-staked", - "dao-voting-cw4", + "dao-voting-cw4 2.4.1", + "dao-voting-cw4 2.5.0", "thiserror", ] @@ -1804,16 +1873,16 @@ dependencies = [ "cw20 1.1.2", "cw20-base 1.1.2", "cw4-group 1.1.2", - "dao-dao-core", - "dao-hooks", + "dao-dao-core 2.5.0", + "dao-hooks 2.5.0", "dao-interface 2.5.0", - "dao-pre-propose-approval-single", + "dao-pre-propose-approval-single 2.5.0", "dao-pre-propose-base 2.5.0", - "dao-proposal-single", + "dao-proposal-single 2.5.0", "dao-testing", "dao-voting 2.5.0", "dao-voting-cw20-staked", - "dao-voting-cw4", + "dao-voting-cw4 2.5.0", ] [[package]] @@ -1857,6 +1926,19 @@ dependencies = [ "thiserror", ] +[[package]] +name = "dao-pre-propose-multiple" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d6158aeddd2e080c730f1a2ac3814351cbcd38d61c38209dcf1e203d0e554a5" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw2 1.1.2", + "dao-pre-propose-base 2.4.1", + "dao-voting 2.4.1", +] + [[package]] name = "dao-pre-propose-multiple" version = "2.5.0" @@ -1869,16 +1951,36 @@ dependencies = [ "cw2 1.1.2", "cw20 1.1.2", "cw20-base 1.1.2", + "cw4 1.1.2", "cw4-group 1.1.2", - "dao-dao-core", - "dao-hooks", + "dao-dao-core 2.4.1", + "dao-dao-core 2.5.0", + "dao-hooks 2.5.0", + "dao-interface 2.4.1", "dao-interface 2.5.0", "dao-pre-propose-base 2.5.0", - "dao-proposal-multiple", + "dao-pre-propose-multiple 2.4.1", + "dao-proposal-multiple 2.4.1", + "dao-proposal-multiple 2.5.0", "dao-testing", + "dao-voting 2.4.1", "dao-voting 2.5.0", "dao-voting-cw20-staked", - "dao-voting-cw4", + "dao-voting-cw4 2.4.1", + "dao-voting-cw4 2.5.0", +] + +[[package]] +name = "dao-pre-propose-single" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30f96fb1898f94ea4fe5771f8ab5c2bc6237782e06bb599f070713d11d38c19e" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw2 1.1.2", + "dao-pre-propose-base 2.4.1", + "dao-voting 2.4.1", ] [[package]] @@ -1894,16 +1996,23 @@ dependencies = [ "cw2 1.1.2", "cw20 1.1.2", "cw20-base 1.1.2", + "cw4 1.1.2", "cw4-group 1.1.2", - "dao-dao-core", - "dao-hooks", + "dao-dao-core 2.4.1", + "dao-dao-core 2.5.0", + "dao-hooks 2.5.0", + "dao-interface 2.4.1", "dao-interface 2.5.0", "dao-pre-propose-base 2.5.0", - "dao-proposal-single", + "dao-pre-propose-single 2.4.1", + "dao-proposal-single 2.4.1", + "dao-proposal-single 2.5.0", "dao-testing", + "dao-voting 2.4.1", "dao-voting 2.5.0", "dao-voting-cw20-staked", - "dao-voting-cw4", + "dao-voting-cw4 2.4.1", + "dao-voting-cw4 2.5.0", ] [[package]] @@ -1919,12 +2028,12 @@ dependencies = [ "cw2 1.1.2", "cw4 1.1.2", "cw4-group 1.1.2", - "dao-dao-core", + "dao-dao-core 2.5.0", "dao-dao-macros 2.5.0", "dao-interface 2.5.0", "dao-testing", "dao-voting 2.5.0", - "dao-voting-cw4", + "dao-voting-cw4 2.5.0", "thiserror", ] @@ -1941,15 +2050,38 @@ dependencies = [ "cw2 1.1.2", "cw20 1.1.2", "cw20-base 1.1.2", - "dao-dao-core", - "dao-hooks", + "dao-dao-core 2.5.0", + "dao-hooks 2.5.0", "dao-interface 2.5.0", - "dao-proposal-single", + "dao-proposal-single 2.5.0", "dao-voting 2.5.0", "dao-voting-cw20-balance", "thiserror", ] +[[package]] +name = "dao-proposal-multiple" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51fac47816150063ef09b555f3466f8be99612b76860a20fba7c85bd1854beba" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-hooks 2.4.1", + "cw-storage-plus 1.2.0", + "cw-utils 1.0.3", + "cw2 1.1.2", + "cw20 1.1.2", + "dao-dao-macros 2.4.1", + "dao-hooks 2.4.1", + "dao-interface 2.4.1", + "dao-pre-propose-base 2.4.1", + "dao-pre-propose-multiple 2.4.1", + "dao-voting 0.1.0", + "dao-voting 2.4.1", + "thiserror", +] + [[package]] name = "dao-proposal-multiple" version = "2.5.0" @@ -1970,22 +2102,46 @@ dependencies = [ "cw4-group 1.1.2", "cw721-base 0.18.0", "dao-dao-macros 2.5.0", - "dao-hooks", + "dao-hooks 2.5.0", "dao-interface 2.5.0", "dao-pre-propose-base 2.5.0", - "dao-pre-propose-multiple", + "dao-pre-propose-multiple 2.5.0", "dao-testing", "dao-voting 0.1.0", "dao-voting 2.5.0", "dao-voting-cw20-balance", "dao-voting-cw20-staked", - "dao-voting-cw4", + "dao-voting-cw4 2.5.0", "dao-voting-cw721-staked", "dao-voting-token-staked", "rand", "thiserror", ] +[[package]] +name = "dao-proposal-single" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ce91710cfcff1af520cd0e885eee6972aeefbefc1c9da18349e66ab959269bb" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-hooks 2.4.1", + "cw-proposal-single", + "cw-storage-plus 1.2.0", + "cw-utils 0.13.4", + "cw-utils 1.0.3", + "cw2 1.1.2", + "cw20 1.1.2", + "dao-dao-macros 2.4.1", + "dao-hooks 2.4.1", + "dao-interface 2.4.1", + "dao-pre-propose-base 2.4.1", + "dao-voting 0.1.0", + "dao-voting 2.4.1", + "thiserror", +] + [[package]] name = "dao-proposal-single" version = "2.5.0" @@ -2008,18 +2164,18 @@ dependencies = [ "cw4 1.1.2", "cw4-group 1.1.2", "cw721-base 0.18.0", - "dao-dao-core", + "dao-dao-core 2.5.0", "dao-dao-macros 2.5.0", - "dao-hooks", + "dao-hooks 2.5.0", "dao-interface 2.5.0", "dao-pre-propose-base 2.5.0", - "dao-pre-propose-single", + "dao-pre-propose-single 2.5.0", "dao-testing", "dao-voting 0.1.0", "dao-voting 2.5.0", "dao-voting-cw20-balance", "dao-voting-cw20-staked", - "dao-voting-cw4", + "dao-voting-cw4 2.5.0", "dao-voting-cw721-staked", "dao-voting-token-staked", "thiserror", @@ -2058,12 +2214,12 @@ dependencies = [ "cw4 1.1.2", "cw4-group 1.1.2", "cw721-base 0.18.0", - "dao-hooks", + "dao-hooks 2.5.0", "dao-interface 2.5.0", "dao-testing", "dao-voting 2.5.0", "dao-voting-cw20-staked", - "dao-voting-cw4", + "dao-voting-cw4 2.5.0", "dao-voting-cw721-staked", "dao-voting-token-staked", "thiserror", @@ -2111,18 +2267,18 @@ dependencies = [ "cw4-group 1.1.2", "cw721-base 0.18.0", "cw721-roles", - "dao-dao-core", + "dao-dao-core 2.5.0", "dao-interface 2.5.0", - "dao-pre-propose-multiple", - "dao-pre-propose-single", + "dao-pre-propose-multiple 2.5.0", + "dao-pre-propose-single 2.5.0", "dao-proposal-condorcet", - "dao-proposal-single", + "dao-proposal-single 2.5.0", "dao-test-custom-factory", "dao-voting 0.1.0", "dao-voting 2.5.0", "dao-voting-cw20-balance", "dao-voting-cw20-staked", - "dao-voting-cw4", + "dao-voting-cw4 2.5.0", "dao-voting-cw721-roles", "dao-voting-cw721-staked", "dao-voting-onft-staked", @@ -2215,6 +2371,24 @@ dependencies = [ "thiserror", ] +[[package]] +name = "dao-voting-cw4" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba59e19abd4d51d6c3a37a84fb0c8cfe90e2f2ab551a610ec6749fcd09fc9e86" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 1.2.0", + "cw-utils 1.0.3", + "cw2 1.1.2", + "cw4 1.1.2", + "cw4-group 1.1.2", + "dao-dao-macros 2.4.1", + "dao-interface 2.4.1", + "thiserror", +] + [[package]] name = "dao-voting-cw4" version = "2.5.0" @@ -2273,10 +2447,10 @@ dependencies = [ "cw721-base 0.18.0", "cw721-controllers", "dao-dao-macros 2.5.0", - "dao-hooks", + "dao-hooks 2.5.0", "dao-interface 2.5.0", "dao-proposal-hook-counter", - "dao-proposal-single", + "dao-proposal-single 2.5.0", "dao-test-custom-factory", "dao-testing", "dao-voting 2.5.0", @@ -2301,10 +2475,10 @@ dependencies = [ "cw2 1.1.2", "cw721-controllers", "dao-dao-macros 2.5.0", - "dao-hooks", + "dao-hooks 2.5.0", "dao-interface 2.5.0", "dao-proposal-hook-counter", - "dao-proposal-single", + "dao-proposal-single 2.5.0", "dao-test-custom-factory", "dao-testing", "dao-voting 2.5.0", @@ -2331,10 +2505,10 @@ dependencies = [ "cw-utils 1.0.3", "cw2 1.1.2", "dao-dao-macros 2.5.0", - "dao-hooks", + "dao-hooks 2.5.0", "dao-interface 2.5.0", "dao-proposal-hook-counter", - "dao-proposal-single", + "dao-proposal-single 2.5.0", "dao-test-custom-factory", "dao-testing", "dao-voting 2.5.0", @@ -3029,10 +3203,10 @@ dependencies = [ "cw721 0.18.0", "cw721-base 0.18.0", "cw721-roles", - "dao-dao-core", + "dao-dao-core 2.5.0", "dao-interface 2.5.0", - "dao-pre-propose-single", - "dao-proposal-single", + "dao-pre-propose-single 2.5.0", + "dao-proposal-single 2.5.0", "dao-test-custom-factory", "dao-voting 2.5.0", "dao-voting-cw20-staked", diff --git a/Cargo.toml b/Cargo.toml index 135bbf99c..0624265f3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -140,7 +140,12 @@ voting-v1 = { package = "dao-voting", version = "0.1.0" } # v2.4.1 dependencies. used for state migrations. cw-denom-v241 = { package = "cw-denom", version = "2.4.1" } dao-dao-core-v241 = { package = "dao-dao-core", version = "2.4.1" } +dao-interface-v241 = { package = "dao-interface", version = "2.4.1" } dao-pre-propose-base-v241 = { package = "dao-pre-propose-base", version = "2.4.1" } +dao-pre-propose-approval-single-v241 = { package = "dao-pre-propose-approval-single", version = "2.4.1" } +dao-pre-propose-multiple-v241 = { package = "dao-pre-propose-multiple", version = "2.4.1" } dao-pre-propose-single-v241 = { package = "dao-pre-propose-single", version = "2.4.1" } +dao-proposal-multiple-v241 = { package = "dao-proposal-multiple", version = "2.4.1" } +dao-proposal-single-v241 = { package = "dao-proposal-single", version = "2.4.1" } dao-voting-cw4-v241 = { package = "dao-voting-cw4", version = "2.4.1" } dao-voting-v241 = { package = "dao-voting", version = "2.4.1" } diff --git a/contracts/pre-propose/dao-pre-propose-approval-single/Cargo.toml b/contracts/pre-propose/dao-pre-propose-approval-single/Cargo.toml index c38ce7f64..0b44a0621 100644 --- a/contracts/pre-propose/dao-pre-propose-approval-single/Cargo.toml +++ b/contracts/pre-propose/dao-pre-propose-approval-single/Cargo.toml @@ -31,6 +31,7 @@ dao-interface = { workspace = true } cw-denom = { workspace = true } cw-multi-test = { workspace = true } cw-utils = { workspace = true } +cw4 = { workspace = true } cw4-group = { workspace = true } cw20 = { workspace = true } cw20-base = { workspace = true } @@ -41,3 +42,11 @@ dao-voting = { workspace = true } dao-voting-cw4 = { workspace = true } dao-voting-cw20-staked = { workspace = true } dao-proposal-single = { workspace = true } + +# v2.4.1 migration +dao-dao-core-v241 = { workspace = true } +dao-interface-v241 = { workspace = true } +dao-pre-propose-approval-single-v241 = { workspace = true } +dao-proposal-single-v241 = { workspace = true } +dao-voting-cw4-v241 = { workspace = true } +dao-voting-v241 = { workspace = true } diff --git a/contracts/pre-propose/dao-pre-propose-approval-single/src/contract.rs b/contracts/pre-propose/dao-pre-propose-approval-single/src/contract.rs index b062fdcf6..091e91e2e 100644 --- a/contracts/pre-propose/dao-pre-propose-approval-single/src/contract.rs +++ b/contracts/pre-propose/dao-pre-propose-approval-single/src/contract.rs @@ -405,6 +405,8 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { } #[cfg_attr(not(feature = "library"), entry_point)] -pub fn migrate(deps: DepsMut, _env: Env, msg: MigrateMsg) -> Result { - PrePropose::default().migrate(deps, msg) +pub fn migrate(mut deps: DepsMut, _env: Env, msg: MigrateMsg) -> Result { + let res = PrePropose::default().migrate(deps.branch(), msg); + set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; + res } diff --git a/contracts/pre-propose/dao-pre-propose-approval-single/src/tests.rs b/contracts/pre-propose/dao-pre-propose-approval-single/src/tests.rs index 9c0748b6c..8c9093997 100644 --- a/contracts/pre-propose/dao-pre-propose-approval-single/src/tests.rs +++ b/contracts/pre-propose/dao-pre-propose-approval-single/src/tests.rs @@ -1,4 +1,6 @@ -use cosmwasm_std::{coins, from_json, to_json_binary, Addr, Coin, Empty, Uint128}; +use cosmwasm_std::{ + coins, from_json, to_json_binary, Addr, Coin, CosmosMsg, Empty, Uint128, WasmMsg, +}; use cw2::ContractVersion; use cw20::Cw20Coin; use cw_denom::UncheckedDenom; @@ -9,20 +11,28 @@ use dao_interface::state::ProposalModule; use dao_interface::state::{Admin, ModuleInstantiateInfo}; use dao_pre_propose_base::{error::PreProposeError, msg::DepositInfoResponse, state::Config}; use dao_proposal_single::query::ProposalResponse; -use dao_testing::helpers::instantiate_with_cw4_groups_governance; +use dao_testing::{contracts::cw4_group_contract, helpers::instantiate_with_cw4_groups_governance}; use dao_voting::pre_propose::{PreProposeSubmissionPolicy, PreProposeSubmissionPolicyError}; use dao_voting::{ deposit::{CheckedDepositInfo, DepositRefundPolicy, DepositToken, UncheckedDepositInfo}, pre_propose::{PreProposeInfo, ProposalCreationPolicy}, status::Status, threshold::{PercentageThreshold, Threshold}, - voting::Vote, + voting::{SingleChoiceAutoVote, Vote}, }; +// test v2.4.1 migration +use dao_dao_core_v241 as core_v241; +use dao_interface_v241 as di_v241; +use dao_pre_propose_approval_single_v241 as dppas_v241; +use dao_proposal_single_v241 as dps_v241; +use dao_voting_cw4_v241 as dvcw4_v241; +use dao_voting_v241 as dv_v241; + use crate::state::{Proposal, ProposalStatus}; use crate::{contract::*, msg::*}; -fn cw_dao_proposal_single_contract() -> Box> { +fn dao_proposal_single_contract() -> Box> { let contract = ContractWrapper::new( dao_proposal_single::contract::execute, dao_proposal_single::contract::instantiate, @@ -33,8 +43,8 @@ fn cw_dao_proposal_single_contract() -> Box> { Box::new(contract) } -fn cw_pre_propose_base_proposal_single() -> Box> { - let contract = ContractWrapper::new(execute, instantiate, query); +fn dao_pre_propose_approval_single_contract() -> Box> { + let contract = ContractWrapper::new(execute, instantiate, query).with_migrate(migrate); Box::new(contract) } @@ -52,7 +62,7 @@ fn get_default_proposal_module_instantiate( deposit_info: Option, open_proposal_submission: bool, ) -> dao_proposal_single::msg::InstantiateMsg { - let pre_propose_id = app.store_code(cw_pre_propose_base_proposal_single()); + let pre_propose_id = app.store_code(dao_pre_propose_approval_single_contract()); let submission_policy = if open_proposal_submission { PreProposeSubmissionPolicy::Anyone { denylist: None } @@ -128,7 +138,7 @@ fn setup_default_test( deposit_info: Option, open_proposal_submission: bool, ) -> DefaultTestSetup { - let dao_proposal_single_id = app.store_code(cw_dao_proposal_single_contract()); + let dao_proposal_single_id = app.store_code(dao_proposal_single_contract()); let proposal_module_instantiate = get_default_proposal_module_instantiate(app, deposit_info, open_proposal_submission); @@ -1580,10 +1590,10 @@ fn test_specific_allowlist_denylist() { fn test_instantiate_with_zero_native_deposit() { let mut app = App::default(); - let dao_proposal_single_id = app.store_code(cw_dao_proposal_single_contract()); + let dao_proposal_single_id = app.store_code(dao_proposal_single_contract()); let proposal_module_instantiate = { - let pre_propose_id = app.store_code(cw_pre_propose_base_proposal_single()); + let pre_propose_id = app.store_code(dao_pre_propose_approval_single_contract()); dao_proposal_single::msg::InstantiateMsg { threshold: Threshold::AbsolutePercentage { @@ -1649,10 +1659,10 @@ fn test_instantiate_with_zero_cw20_deposit() { let cw20_addr = instantiate_cw20_base_default(&mut app); - let dao_proposal_single_id = app.store_code(cw_dao_proposal_single_contract()); + let dao_proposal_single_id = app.store_code(dao_proposal_single_contract()); let proposal_module_instantiate = { - let pre_propose_id = app.store_code(cw_pre_propose_base_proposal_single()); + let pre_propose_id = app.store_code(dao_pre_propose_approval_single_contract()); dao_proposal_single::msg::InstantiateMsg { threshold: Threshold::AbsolutePercentage { @@ -2477,3 +2487,752 @@ fn test_withdraw() { let balance = get_balance_native(&app, core_addr.as_str(), "ujuno"); assert_eq!(balance, Uint128::new(30)); } + +#[test] +fn test_migrate_from_v241() { + let app = &mut App::default(); + + let core_v241_contract = Box::new( + ContractWrapper::new( + core_v241::contract::execute, + core_v241::contract::instantiate, + core_v241::contract::query, + ) + .with_reply(core_v241::contract::reply), + ); + let dvcw4_v241_contract = Box::new( + ContractWrapper::new( + dvcw4_v241::contract::execute, + dvcw4_v241::contract::instantiate, + dvcw4_v241::contract::query, + ) + .with_reply(dvcw4_v241::contract::reply), + ); + let dpps_v241_contract = Box::new(ContractWrapper::new( + dppas_v241::contract::execute, + dppas_v241::contract::instantiate, + dppas_v241::contract::query, + )); + let dps_v241_contract = Box::new( + ContractWrapper::new( + dps_v241::contract::execute, + dps_v241::contract::instantiate, + dps_v241::contract::query, + ) + .with_reply(dps_v241::contract::reply), + ); + + let core_id = app.store_code(core_v241_contract); + let cw4_id = app.store_code(cw4_group_contract()); + let dvcw4_v241_id = app.store_code(dvcw4_v241_contract); + let dpps_v241_id = app.store_code(dpps_v241_contract); + let dps_v241_id = app.store_code(dps_v241_contract); + + let governance_instantiate = di_v241::msg::InstantiateMsg { + dao_uri: None, + admin: None, + name: "DAO DAO".to_string(), + description: "A DAO that builds DAOs".to_string(), + image_url: None, + automatically_add_cw20s: true, + automatically_add_cw721s: true, + voting_module_instantiate_info: di_v241::state::ModuleInstantiateInfo { + code_id: dvcw4_v241_id, + msg: to_json_binary(&dvcw4_v241::msg::InstantiateMsg { + group_contract: dvcw4_v241::msg::GroupContract::New { + cw4_group_code_id: cw4_id, + initial_members: vec![ + cw4::Member { + addr: "ekez".to_string(), + weight: 9, + }, + cw4::Member { + addr: "keze".to_string(), + weight: 8, + }, + ], + }, + }) + .unwrap(), + admin: Some(di_v241::state::Admin::CoreModule {}), + funds: vec![], + label: "DAO DAO voting module".to_string(), + }, + proposal_modules_instantiate_info: vec![di_v241::state::ModuleInstantiateInfo { + code_id: dps_v241_id, + msg: to_json_binary(&dps_v241::msg::InstantiateMsg { + threshold: dv_v241::threshold::Threshold::AbsolutePercentage { + percentage: dv_v241::threshold::PercentageThreshold::Majority {}, + }, + max_voting_period: cw_utils::Duration::Time(86400), + min_voting_period: None, + only_members_execute: false, + allow_revoting: false, + pre_propose_info: dv_v241::pre_propose::PreProposeInfo::ModuleMayPropose { + info: di_v241::state::ModuleInstantiateInfo { + code_id: dpps_v241_id, + msg: to_json_binary(&dppas_v241::msg::InstantiateMsg { + deposit_info: None, + open_proposal_submission: false, + extension: dppas_v241::msg::InstantiateExt { + approver: "approver".to_string(), + }, + }) + .unwrap(), + admin: Some(di_v241::state::Admin::CoreModule {}), + funds: vec![], + label: "baby's first pre-propose module".to_string(), + }, + }, + close_proposal_on_execution_failure: false, + veto: None, + }) + .unwrap(), + admin: Some(di_v241::state::Admin::CoreModule {}), + funds: vec![], + label: "DAO DAO governance module".to_string(), + }], + initial_items: None, + }; + + let core_addr = app + .instantiate_contract( + core_id, + Addr::unchecked("ekez"), + &governance_instantiate, + &[], + "DAO DAO", + None, + ) + .unwrap(); + + app.update_block(|block| block.height += 1); + + let proposal_modules: Vec = app + .wrap() + .query_wasm_smart( + core_addr.clone(), + &di_v241::msg::QueryMsg::ProposalModules { + start_after: None, + limit: None, + }, + ) + .unwrap(); + + assert_eq!(proposal_modules.len(), 1); + let proposal_single = proposal_modules.into_iter().next().unwrap().address; + let proposal_creation_policy = app + .wrap() + .query_wasm_smart( + proposal_single.clone(), + &dps_v241::msg::QueryMsg::ProposalCreationPolicy {}, + ) + .unwrap(); + + let pre_propose = match proposal_creation_policy { + dv_v241::pre_propose::ProposalCreationPolicy::Module { addr } => addr, + _ => panic!("expected a module for the proposal creation policy"), + }; + + // Make sure things were set up correctly. + assert_eq!( + proposal_single, + get_proposal_module(app, pre_propose.clone()) + ); + assert_eq!(core_addr, get_dao(app, pre_propose.clone())); + let info: ContractVersion = from_json( + app.wrap() + .query_wasm_raw(pre_propose.clone(), "contract_info".as_bytes()) + .unwrap() + .unwrap(), + ) + .unwrap(); + assert_eq!( + ContractVersion { + contract: "crates.io:dao-pre-propose-approval-single".to_string(), + version: "2.4.1".to_string() + }, + info, + ); + + app.execute_contract( + Addr::unchecked("ekez"), + pre_propose.clone(), + &dppas_v241::msg::ExecuteMsg::Propose { + msg: dppas_v241::msg::ProposeMessage::Propose { + title: "title1".to_string(), + description: "d".to_string(), + msgs: vec![], + vote: Some(dv_v241::voting::SingleChoiceAutoVote { + vote: dv_v241::voting::Vote::Yes, + rationale: None, + }), + }, + }, + &[], + ) + .unwrap(); + app.execute_contract( + Addr::unchecked("approver"), + pre_propose.clone(), + &dppas_v241::msg::ExecuteMsg::Extension { + msg: dppas_v241::msg::ExecuteExt::Approve { id: 1 }, + }, + &[], + ) + .unwrap(); + + let proposal: dps_v241::query::ProposalResponse = app + .wrap() + .query_wasm_smart( + proposal_single.clone(), + &dps_v241::msg::QueryMsg::Proposal { proposal_id: 1 }, + ) + .unwrap(); + + assert_eq!(proposal.proposal.status, dv_v241::status::Status::Passed); + assert_eq!(proposal.proposal.proposer, Addr::unchecked("ekez")); + assert_eq!(proposal.proposal.title, "title1".to_string()); + assert_eq!(proposal.proposal.description, "d".to_string()); + assert_eq!(proposal.proposal.msgs, vec![]); + + app.execute_contract( + Addr::unchecked("ekez"), + proposal_single.clone(), + &dps_v241::msg::ExecuteMsg::Execute { proposal_id: 1 }, + &[], + ) + .unwrap(); + + let proposal: dps_v241::query::ProposalResponse = app + .wrap() + .query_wasm_smart( + proposal_single.clone(), + &dps_v241::msg::QueryMsg::Proposal { proposal_id: 1 }, + ) + .unwrap(); + + assert_eq!(proposal.proposal.status, dv_v241::status::Status::Executed); + + // UPGRADE ONLY PRE-PROPOSE TO LATEST VIA DAO PROPOSAL + + let dppas_latest_id = app.store_code(dao_pre_propose_approval_single_contract()); + + app.execute_contract( + Addr::unchecked("ekez"), + pre_propose.clone(), + &dppas_v241::msg::ExecuteMsg::Propose { + msg: dppas_v241::msg::ProposeMessage::Propose { + title: "upgrade pre-propose-single from v2.4.1".to_string(), + description: "d".to_string(), + msgs: vec![CosmosMsg::Wasm(WasmMsg::Migrate { + contract_addr: pre_propose.to_string(), + new_code_id: dppas_latest_id, + msg: to_json_binary(&MigrateMsg::FromUnderV250 { policy: None }).unwrap(), + })], + vote: Some(dv_v241::voting::SingleChoiceAutoVote { + vote: dv_v241::voting::Vote::Yes, + rationale: None, + }), + }, + }, + &[], + ) + .unwrap(); + app.execute_contract( + Addr::unchecked("approver"), + pre_propose.clone(), + &dppas_v241::msg::ExecuteMsg::Extension { + msg: dppas_v241::msg::ExecuteExt::Approve { id: 2 }, + }, + &[], + ) + .unwrap(); + app.execute_contract( + Addr::unchecked("ekez"), + proposal_single.clone(), + &dps_v241::msg::ExecuteMsg::Execute { proposal_id: 2 }, + &[], + ) + .unwrap(); + let proposal: dps_v241::query::ProposalResponse = app + .wrap() + .query_wasm_smart( + proposal_single.clone(), + &dps_v241::msg::QueryMsg::Proposal { proposal_id: 2 }, + ) + .unwrap(); + assert_eq!(proposal.proposal.status, dv_v241::status::Status::Executed); + + // MAKE SURE PRE PROPOSE INFO CHANGED + + let info: ContractVersion = from_json( + app.wrap() + .query_wasm_raw(pre_propose.clone(), "contract_info".as_bytes()) + .unwrap() + .unwrap(), + ) + .unwrap(); + assert_eq!( + ContractVersion { + contract: CONTRACT_NAME.to_string(), + version: CONTRACT_VERSION.to_string() + }, + info, + ); + + // MAKE SURE PRE PROPOSE CONFIG WAS UPDATED + + let config: Config = app + .wrap() + .query_wasm_smart(pre_propose.clone(), &QueryMsg::Config {}) + .unwrap(); + assert_eq!( + Config { + deposit_info: None, + submission_policy: PreProposeSubmissionPolicy::Specific { + dao_members: true, + allowlist: None, + denylist: None + } + }, + config + ); + + // NOW MAKE SURE WE CAN MAKE AND VOTE ON NEW PROPOSALS + + app.execute_contract( + Addr::unchecked("ekez"), + pre_propose.clone(), + &ExecuteMsg::Propose { + msg: ProposeMessage::Propose { + title: "title2 on latest version".to_string(), + description: "d".to_string(), + msgs: vec![], + vote: Some(SingleChoiceAutoVote { + vote: Vote::Yes, + rationale: None, + }), + }, + }, + &[], + ) + .unwrap(); + app.execute_contract( + Addr::unchecked("approver"), + pre_propose.clone(), + &dppas_v241::msg::ExecuteMsg::Extension { + msg: dppas_v241::msg::ExecuteExt::Approve { id: 3 }, + }, + &[], + ) + .unwrap(); + app.execute_contract( + Addr::unchecked("ekez"), + proposal_single.clone(), + &dao_proposal_single::msg::ExecuteMsg::Execute { proposal_id: 3 }, + &[], + ) + .unwrap(); + let proposal: ProposalResponse = app + .wrap() + .query_wasm_smart( + proposal_single.clone(), + &dao_proposal_single::msg::QueryMsg::Proposal { proposal_id: 3 }, + ) + .unwrap(); + assert_eq!(proposal.proposal.status, Status::Executed); +} + +#[test] +fn test_migrate_from_v241_with_policy_update() { + let app = &mut App::default(); + + let core_v241_contract = Box::new( + ContractWrapper::new( + core_v241::contract::execute, + core_v241::contract::instantiate, + core_v241::contract::query, + ) + .with_reply(core_v241::contract::reply), + ); + let dvcw4_v241_contract = Box::new( + ContractWrapper::new( + dvcw4_v241::contract::execute, + dvcw4_v241::contract::instantiate, + dvcw4_v241::contract::query, + ) + .with_reply(dvcw4_v241::contract::reply), + ); + let dpps_v241_contract = Box::new(ContractWrapper::new( + dppas_v241::contract::execute, + dppas_v241::contract::instantiate, + dppas_v241::contract::query, + )); + let dps_v241_contract = Box::new( + ContractWrapper::new( + dps_v241::contract::execute, + dps_v241::contract::instantiate, + dps_v241::contract::query, + ) + .with_reply(dps_v241::contract::reply), + ); + + let core_id = app.store_code(core_v241_contract); + let cw4_id = app.store_code(cw4_group_contract()); + let dvcw4_v241_id = app.store_code(dvcw4_v241_contract); + let dpps_v241_id = app.store_code(dpps_v241_contract); + let dps_v241_id = app.store_code(dps_v241_contract); + + let governance_instantiate = di_v241::msg::InstantiateMsg { + dao_uri: None, + admin: None, + name: "DAO DAO".to_string(), + description: "A DAO that builds DAOs".to_string(), + image_url: None, + automatically_add_cw20s: true, + automatically_add_cw721s: true, + voting_module_instantiate_info: di_v241::state::ModuleInstantiateInfo { + code_id: dvcw4_v241_id, + msg: to_json_binary(&dvcw4_v241::msg::InstantiateMsg { + group_contract: dvcw4_v241::msg::GroupContract::New { + cw4_group_code_id: cw4_id, + initial_members: vec![ + cw4::Member { + addr: "ekez".to_string(), + weight: 9, + }, + cw4::Member { + addr: "keze".to_string(), + weight: 8, + }, + ], + }, + }) + .unwrap(), + admin: Some(di_v241::state::Admin::CoreModule {}), + funds: vec![], + label: "DAO DAO voting module".to_string(), + }, + proposal_modules_instantiate_info: vec![di_v241::state::ModuleInstantiateInfo { + code_id: dps_v241_id, + msg: to_json_binary(&dps_v241::msg::InstantiateMsg { + threshold: dv_v241::threshold::Threshold::AbsolutePercentage { + percentage: dv_v241::threshold::PercentageThreshold::Majority {}, + }, + max_voting_period: cw_utils::Duration::Time(86400), + min_voting_period: None, + only_members_execute: false, + allow_revoting: false, + pre_propose_info: dv_v241::pre_propose::PreProposeInfo::ModuleMayPropose { + info: di_v241::state::ModuleInstantiateInfo { + code_id: dpps_v241_id, + msg: to_json_binary(&dppas_v241::msg::InstantiateMsg { + deposit_info: None, + open_proposal_submission: false, + extension: dppas_v241::msg::InstantiateExt { + approver: "approver".to_string(), + }, + }) + .unwrap(), + admin: Some(di_v241::state::Admin::CoreModule {}), + funds: vec![], + label: "baby's first pre-propose module".to_string(), + }, + }, + close_proposal_on_execution_failure: false, + veto: None, + }) + .unwrap(), + admin: Some(di_v241::state::Admin::CoreModule {}), + funds: vec![], + label: "DAO DAO governance module".to_string(), + }], + initial_items: None, + }; + + let core_addr = app + .instantiate_contract( + core_id, + Addr::unchecked("ekez"), + &governance_instantiate, + &[], + "DAO DAO", + None, + ) + .unwrap(); + + app.update_block(|block| block.height += 1); + + let proposal_modules: Vec = app + .wrap() + .query_wasm_smart( + core_addr.clone(), + &di_v241::msg::QueryMsg::ProposalModules { + start_after: None, + limit: None, + }, + ) + .unwrap(); + + assert_eq!(proposal_modules.len(), 1); + let proposal_single = proposal_modules.into_iter().next().unwrap().address; + let proposal_creation_policy = app + .wrap() + .query_wasm_smart( + proposal_single.clone(), + &dps_v241::msg::QueryMsg::ProposalCreationPolicy {}, + ) + .unwrap(); + + let pre_propose = match proposal_creation_policy { + dv_v241::pre_propose::ProposalCreationPolicy::Module { addr } => addr, + _ => panic!("expected a module for the proposal creation policy"), + }; + + // Make sure things were set up correctly. + assert_eq!( + proposal_single, + get_proposal_module(app, pre_propose.clone()) + ); + assert_eq!(core_addr, get_dao(app, pre_propose.clone())); + let info: ContractVersion = from_json( + app.wrap() + .query_wasm_raw(pre_propose.clone(), "contract_info".as_bytes()) + .unwrap() + .unwrap(), + ) + .unwrap(); + assert_eq!( + ContractVersion { + contract: "crates.io:dao-pre-propose-approval-single".to_string(), + version: "2.4.1".to_string() + }, + info, + ); + + app.execute_contract( + Addr::unchecked("ekez"), + pre_propose.clone(), + &dppas_v241::msg::ExecuteMsg::Propose { + msg: dppas_v241::msg::ProposeMessage::Propose { + title: "title1".to_string(), + description: "d".to_string(), + msgs: vec![], + vote: Some(dv_v241::voting::SingleChoiceAutoVote { + vote: dv_v241::voting::Vote::Yes, + rationale: None, + }), + }, + }, + &[], + ) + .unwrap(); + app.execute_contract( + Addr::unchecked("approver"), + pre_propose.clone(), + &dppas_v241::msg::ExecuteMsg::Extension { + msg: dppas_v241::msg::ExecuteExt::Approve { id: 1 }, + }, + &[], + ) + .unwrap(); + + let proposal: dps_v241::query::ProposalResponse = app + .wrap() + .query_wasm_smart( + proposal_single.clone(), + &dps_v241::msg::QueryMsg::Proposal { proposal_id: 1 }, + ) + .unwrap(); + + assert_eq!(proposal.proposal.status, dv_v241::status::Status::Passed); + assert_eq!(proposal.proposal.proposer, Addr::unchecked("ekez")); + assert_eq!(proposal.proposal.title, "title1".to_string()); + assert_eq!(proposal.proposal.description, "d".to_string()); + assert_eq!(proposal.proposal.msgs, vec![]); + + app.execute_contract( + Addr::unchecked("ekez"), + proposal_single.clone(), + &dps_v241::msg::ExecuteMsg::Execute { proposal_id: 1 }, + &[], + ) + .unwrap(); + + let proposal: dps_v241::query::ProposalResponse = app + .wrap() + .query_wasm_smart( + proposal_single.clone(), + &dps_v241::msg::QueryMsg::Proposal { proposal_id: 1 }, + ) + .unwrap(); + + assert_eq!(proposal.proposal.status, dv_v241::status::Status::Executed); + + // UPGRADE ONLY PRE-PROPOSE TO LATEST VIA DAO PROPOSAL WITH POLICY UPDATE + + let dppas_latest_id = app.store_code(dao_pre_propose_approval_single_contract()); + + app.execute_contract( + Addr::unchecked("ekez"), + pre_propose.clone(), + &dppas_v241::msg::ExecuteMsg::Propose { + msg: dppas_v241::msg::ProposeMessage::Propose { + title: "upgrade pre-propose-single from v2.4.1".to_string(), + description: "d".to_string(), + msgs: vec![CosmosMsg::Wasm(WasmMsg::Migrate { + contract_addr: pre_propose.to_string(), + new_code_id: dppas_latest_id, + msg: to_json_binary(&MigrateMsg::FromUnderV250 { + policy: Some(PreProposeSubmissionPolicy::Specific { + dao_members: false, + allowlist: Some(vec!["noob".to_string()]), + denylist: None, + }), + }) + .unwrap(), + })], + vote: Some(dv_v241::voting::SingleChoiceAutoVote { + vote: dv_v241::voting::Vote::Yes, + rationale: None, + }), + }, + }, + &[], + ) + .unwrap(); + app.execute_contract( + Addr::unchecked("approver"), + pre_propose.clone(), + &dppas_v241::msg::ExecuteMsg::Extension { + msg: dppas_v241::msg::ExecuteExt::Approve { id: 2 }, + }, + &[], + ) + .unwrap(); + app.execute_contract( + Addr::unchecked("ekez"), + proposal_single.clone(), + &dps_v241::msg::ExecuteMsg::Execute { proposal_id: 2 }, + &[], + ) + .unwrap(); + let proposal: dps_v241::query::ProposalResponse = app + .wrap() + .query_wasm_smart( + proposal_single.clone(), + &dps_v241::msg::QueryMsg::Proposal { proposal_id: 2 }, + ) + .unwrap(); + assert_eq!(proposal.proposal.status, dv_v241::status::Status::Executed); + + // MAKE SURE PRE PROPOSE INFO CHANGED + + let info: ContractVersion = from_json( + app.wrap() + .query_wasm_raw(pre_propose.clone(), "contract_info".as_bytes()) + .unwrap() + .unwrap(), + ) + .unwrap(); + assert_eq!( + ContractVersion { + contract: CONTRACT_NAME.to_string(), + version: CONTRACT_VERSION.to_string() + }, + info, + ); + + // MAKE SURE PRE PROPOSE CONFIG WAS UPDATED + + let config: Config = app + .wrap() + .query_wasm_smart(pre_propose.clone(), &QueryMsg::Config {}) + .unwrap(); + assert_eq!( + Config { + deposit_info: None, + submission_policy: PreProposeSubmissionPolicy::Specific { + dao_members: false, + allowlist: Some(vec!["noob".to_string()]), + denylist: None + } + }, + config + ); + + // NOW MAKE SURE ONLY NOOB CAN MAKE PROPOSALS + + let err: PreProposeError = app + .execute_contract( + Addr::unchecked("ekez"), + pre_propose.clone(), + &ExecuteMsg::Propose { + msg: ProposeMessage::Propose { + title: "title2 on latest version".to_string(), + description: "d".to_string(), + msgs: vec![], + vote: None, + }, + }, + &[], + ) + .unwrap_err() + .downcast() + .unwrap(); + assert_eq!( + err, + PreProposeError::SubmissionPolicy(PreProposeSubmissionPolicyError::Unauthorized {}) + ); + + app.execute_contract( + Addr::unchecked("noob"), + pre_propose.clone(), + &ExecuteMsg::Propose { + msg: ProposeMessage::Propose { + title: "title2 on latest version".to_string(), + description: "d".to_string(), + msgs: vec![], + vote: None, + }, + }, + &[], + ) + .unwrap(); + app.execute_contract( + Addr::unchecked("approver"), + pre_propose.clone(), + &dppas_v241::msg::ExecuteMsg::Extension { + msg: dppas_v241::msg::ExecuteExt::Approve { id: 3 }, + }, + &[], + ) + .unwrap(); + app.execute_contract( + Addr::unchecked("ekez"), + proposal_single.clone(), + &dao_proposal_single::msg::ExecuteMsg::Vote { + proposal_id: 3, + vote: Vote::Yes, + rationale: None, + }, + &[], + ) + .unwrap(); + app.execute_contract( + Addr::unchecked("ekez"), + proposal_single.clone(), + &dao_proposal_single::msg::ExecuteMsg::Execute { proposal_id: 3 }, + &[], + ) + .unwrap(); + let proposal: ProposalResponse = app + .wrap() + .query_wasm_smart( + proposal_single.clone(), + &dao_proposal_single::msg::QueryMsg::Proposal { proposal_id: 3 }, + ) + .unwrap(); + assert_eq!(proposal.proposal.status, Status::Executed); +} diff --git a/contracts/pre-propose/dao-pre-propose-approver/src/contract.rs b/contracts/pre-propose/dao-pre-propose-approver/src/contract.rs index cd45c6703..8a4a6f774 100644 --- a/contracts/pre-propose/dao-pre-propose-approver/src/contract.rs +++ b/contracts/pre-propose/dao-pre-propose-approver/src/contract.rs @@ -258,6 +258,8 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { } #[cfg_attr(not(feature = "library"), entry_point)] -pub fn migrate(deps: DepsMut, _env: Env, msg: MigrateMsg) -> Result { - PrePropose::default().migrate(deps, msg) +pub fn migrate(mut deps: DepsMut, _env: Env, msg: MigrateMsg) -> Result { + let res = PrePropose::default().migrate(deps.branch(), msg); + set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; + res } diff --git a/contracts/pre-propose/dao-pre-propose-multiple/Cargo.toml b/contracts/pre-propose/dao-pre-propose-multiple/Cargo.toml index 670c0f281..0aa8ba140 100644 --- a/contracts/pre-propose/dao-pre-propose-multiple/Cargo.toml +++ b/contracts/pre-propose/dao-pre-propose-multiple/Cargo.toml @@ -26,6 +26,7 @@ dao-voting = { workspace = true } [dev-dependencies] cw-multi-test = { workspace = true } cw-utils = { workspace = true } +cw4 = { workspace = true } cw4-group = { workspace = true } cw20 = { workspace = true } cw20-base = { workspace = true } @@ -38,3 +39,11 @@ cw-denom = { workspace = true } dao-interface = { workspace = true } dao-testing = { workspace = true } dao-hooks = { workspace = true } + +# v2.4.1 migration +dao-dao-core-v241 = { workspace = true } +dao-interface-v241 = { workspace = true } +dao-pre-propose-multiple-v241 = { workspace = true } +dao-proposal-multiple-v241 = { workspace = true } +dao-voting-cw4-v241 = { workspace = true } +dao-voting-v241 = { workspace = true } diff --git a/contracts/pre-propose/dao-pre-propose-multiple/src/contract.rs b/contracts/pre-propose/dao-pre-propose-multiple/src/contract.rs index a93957a22..082d129e0 100644 --- a/contracts/pre-propose/dao-pre-propose-multiple/src/contract.rs +++ b/contracts/pre-propose/dao-pre-propose-multiple/src/contract.rs @@ -133,6 +133,8 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { } #[cfg_attr(not(feature = "library"), entry_point)] -pub fn migrate(deps: DepsMut, _env: Env, msg: MigrateMsg) -> Result { - PrePropose::default().migrate(deps, msg) +pub fn migrate(mut deps: DepsMut, _env: Env, msg: MigrateMsg) -> Result { + let res = PrePropose::default().migrate(deps.branch(), msg); + set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; + res } diff --git a/contracts/pre-propose/dao-pre-propose-multiple/src/tests.rs b/contracts/pre-propose/dao-pre-propose-multiple/src/tests.rs index 1e7a41681..9fab05407 100644 --- a/contracts/pre-propose/dao-pre-propose-multiple/src/tests.rs +++ b/contracts/pre-propose/dao-pre-propose-multiple/src/tests.rs @@ -1,4 +1,6 @@ -use cosmwasm_std::{coins, from_json, to_json_binary, Addr, Coin, Decimal, Empty, Uint128}; +use cosmwasm_std::{ + coins, from_json, to_json_binary, Addr, Coin, CosmosMsg, Decimal, Empty, Uint128, WasmMsg, +}; use cpm::query::ProposalResponse; use cw2::ContractVersion; use cw20::Cw20Coin; @@ -10,7 +12,8 @@ use dao_interface::state::ProposalModule; use dao_interface::state::{Admin, ModuleInstantiateInfo}; use dao_pre_propose_base::{error::PreProposeError, msg::DepositInfoResponse, state::Config}; use dao_proposal_multiple as cpm; -use dao_testing::helpers::instantiate_with_cw4_groups_governance; +use dao_testing::{contracts::cw4_group_contract, helpers::instantiate_with_cw4_groups_governance}; +use dao_voting::multiple_choice::MultipleChoiceAutoVote; use dao_voting::pre_propose::{PreProposeSubmissionPolicy, PreProposeSubmissionPolicyError}; use dao_voting::{ deposit::{CheckedDepositInfo, DepositRefundPolicy, DepositToken, UncheckedDepositInfo}, @@ -23,9 +26,17 @@ use dao_voting::{ threshold::PercentageThreshold, }; +// test v2.4.1 migration +use dao_dao_core_v241 as core_v241; +use dao_interface_v241 as di_v241; +use dao_pre_propose_multiple_v241 as dppm_v241; +use dao_proposal_multiple_v241 as dpm_v241; +use dao_voting_cw4_v241 as dvcw4_v241; +use dao_voting_v241 as dv_v241; + use crate::contract::*; -fn cw_dao_proposal_multiple_contract() -> Box> { +fn dao_proposal_multiple_contract() -> Box> { let contract = ContractWrapper::new( cpm::contract::execute, cpm::contract::instantiate, @@ -35,8 +46,8 @@ fn cw_dao_proposal_multiple_contract() -> Box> { Box::new(contract) } -fn cw_pre_propose_base_proposal_single() -> Box> { - let contract = ContractWrapper::new(execute, instantiate, query); +fn dao_pre_propose_multiple_contract() -> Box> { + let contract = ContractWrapper::new(execute, instantiate, query).with_migrate(migrate); Box::new(contract) } @@ -54,7 +65,7 @@ fn get_default_proposal_module_instantiate( deposit_info: Option, open_proposal_submission: bool, ) -> cpm::msg::InstantiateMsg { - let pre_propose_id = app.store_code(cw_pre_propose_base_proposal_single()); + let pre_propose_id = app.store_code(dao_pre_propose_multiple_contract()); let submission_policy = if open_proposal_submission { PreProposeSubmissionPolicy::Anyone { denylist: None } @@ -127,7 +138,7 @@ fn setup_default_test( deposit_info: Option, open_proposal_submission: bool, ) -> DefaultTestSetup { - let cpm_id = app.store_code(cw_dao_proposal_multiple_contract()); + let cpm_id = app.store_code(dao_proposal_multiple_contract()); let proposal_module_instantiate = get_default_proposal_module_instantiate(app, deposit_info, open_proposal_submission); @@ -1333,10 +1344,10 @@ fn test_execute_extension_does_nothing() { fn test_instantiate_with_zero_native_deposit() { let mut app = App::default(); - let cpm_id = app.store_code(cw_dao_proposal_multiple_contract()); + let cpm_id = app.store_code(dao_proposal_multiple_contract()); let proposal_module_instantiate = { - let pre_propose_id = app.store_code(cw_pre_propose_base_proposal_single()); + let pre_propose_id = app.store_code(dao_pre_propose_multiple_contract()); cpm::msg::InstantiateMsg { voting_strategy: VotingStrategy::SingleChoice { @@ -1400,10 +1411,10 @@ fn test_instantiate_with_zero_cw20_deposit() { let cw20_addr = instantiate_cw20_base_default(&mut app); - let cpm_id = app.store_code(cw_dao_proposal_multiple_contract()); + let cpm_id = app.store_code(dao_proposal_multiple_contract()); let proposal_module_instantiate = { - let pre_propose_id = app.store_code(cw_pre_propose_base_proposal_single()); + let pre_propose_id = app.store_code(dao_pre_propose_multiple_contract()); cpm::msg::InstantiateMsg { voting_strategy: VotingStrategy::SingleChoice { @@ -2231,3 +2242,825 @@ fn test_withdraw() { let balance = get_balance_native(&app, core_addr.as_str(), "ujuno"); assert_eq!(balance, Uint128::new(30)); } + +#[test] +fn test_migrate_from_v241() { + let app = &mut App::default(); + + let core_v241_contract = Box::new( + ContractWrapper::new( + core_v241::contract::execute, + core_v241::contract::instantiate, + core_v241::contract::query, + ) + .with_reply(core_v241::contract::reply), + ); + let dvcw4_v241_contract = Box::new( + ContractWrapper::new( + dvcw4_v241::contract::execute, + dvcw4_v241::contract::instantiate, + dvcw4_v241::contract::query, + ) + .with_reply(dvcw4_v241::contract::reply), + ); + let dppm_v241_contract = Box::new(ContractWrapper::new( + dppm_v241::contract::execute, + dppm_v241::contract::instantiate, + dppm_v241::contract::query, + )); + let dpm_v241_contract = Box::new( + ContractWrapper::new( + dpm_v241::contract::execute, + dpm_v241::contract::instantiate, + dpm_v241::contract::query, + ) + .with_reply(dpm_v241::contract::reply), + ); + + let core_id = app.store_code(core_v241_contract); + let cw4_id = app.store_code(cw4_group_contract()); + let dvcw4_v241_id = app.store_code(dvcw4_v241_contract); + let dppm_v241_id = app.store_code(dppm_v241_contract); + let dpm_v241_id = app.store_code(dpm_v241_contract); + + let governance_instantiate = di_v241::msg::InstantiateMsg { + dao_uri: None, + admin: None, + name: "DAO DAO".to_string(), + description: "A DAO that builds DAOs".to_string(), + image_url: None, + automatically_add_cw20s: true, + automatically_add_cw721s: true, + voting_module_instantiate_info: di_v241::state::ModuleInstantiateInfo { + code_id: dvcw4_v241_id, + msg: to_json_binary(&dvcw4_v241::msg::InstantiateMsg { + group_contract: dvcw4_v241::msg::GroupContract::New { + cw4_group_code_id: cw4_id, + initial_members: vec![ + cw4::Member { + addr: "ekez".to_string(), + weight: 9, + }, + cw4::Member { + addr: "keze".to_string(), + weight: 8, + }, + ], + }, + }) + .unwrap(), + admin: Some(di_v241::state::Admin::CoreModule {}), + funds: vec![], + label: "DAO DAO voting module".to_string(), + }, + proposal_modules_instantiate_info: vec![di_v241::state::ModuleInstantiateInfo { + code_id: dpm_v241_id, + msg: to_json_binary(&dpm_v241::msg::InstantiateMsg { + voting_strategy: dv_v241::multiple_choice::VotingStrategy::SingleChoice { + quorum: dv_v241::threshold::PercentageThreshold::Majority {}, + }, + max_voting_period: cw_utils::Duration::Time(86400), + min_voting_period: None, + only_members_execute: false, + allow_revoting: false, + pre_propose_info: dv_v241::pre_propose::PreProposeInfo::ModuleMayPropose { + info: di_v241::state::ModuleInstantiateInfo { + code_id: dppm_v241_id, + msg: to_json_binary(&dppm_v241::InstantiateMsg { + deposit_info: None, + open_proposal_submission: true, + extension: Empty::default(), + }) + .unwrap(), + admin: Some(di_v241::state::Admin::CoreModule {}), + funds: vec![], + label: "baby's first pre-propose module".to_string(), + }, + }, + close_proposal_on_execution_failure: false, + veto: None, + }) + .unwrap(), + admin: Some(di_v241::state::Admin::CoreModule {}), + funds: vec![], + label: "DAO DAO governance module".to_string(), + }], + initial_items: None, + }; + + let core_addr = app + .instantiate_contract( + core_id, + Addr::unchecked("ekez"), + &governance_instantiate, + &[], + "DAO DAO", + None, + ) + .unwrap(); + + app.update_block(|block| block.height += 1); + + let proposal_modules: Vec = app + .wrap() + .query_wasm_smart( + core_addr.clone(), + &di_v241::msg::QueryMsg::ProposalModules { + start_after: None, + limit: None, + }, + ) + .unwrap(); + + assert_eq!(proposal_modules.len(), 1); + let proposal_single = proposal_modules.into_iter().next().unwrap().address; + let proposal_creation_policy = app + .wrap() + .query_wasm_smart( + proposal_single.clone(), + &dpm_v241::msg::QueryMsg::ProposalCreationPolicy {}, + ) + .unwrap(); + + let pre_propose = match proposal_creation_policy { + dv_v241::pre_propose::ProposalCreationPolicy::Module { addr } => addr, + _ => panic!("expected a module for the proposal creation policy"), + }; + + // Make sure things were set up correctly. + assert_eq!( + proposal_single, + get_proposal_module(app, pre_propose.clone()) + ); + assert_eq!(core_addr, get_dao(app, pre_propose.clone())); + let info: ContractVersion = from_json( + app.wrap() + .query_wasm_raw(pre_propose.clone(), "contract_info".as_bytes()) + .unwrap() + .unwrap(), + ) + .unwrap(); + assert_eq!( + ContractVersion { + contract: "crates.io:dao-pre-propose-multiple".to_string(), + version: "2.4.1".to_string() + }, + info, + ); + + app.execute_contract( + Addr::unchecked("ekez"), + pre_propose.clone(), + &dppm_v241::ExecuteMsg::Propose { + msg: dppm_v241::ProposeMessage::Propose { + title: "title1".to_string(), + description: "d".to_string(), + choices: dv_v241::multiple_choice::MultipleChoiceOptions { + options: vec![ + dv_v241::multiple_choice::MultipleChoiceOption { + title: "first".to_string(), + description: "d".to_string(), + msgs: vec![], + }, + dv_v241::multiple_choice::MultipleChoiceOption { + title: "second".to_string(), + description: "d".to_string(), + msgs: vec![], + }, + dv_v241::multiple_choice::MultipleChoiceOption { + title: "third".to_string(), + description: "d".to_string(), + msgs: vec![], + }, + ], + }, + vote: Some(dv_v241::multiple_choice::MultipleChoiceAutoVote { + vote: dv_v241::multiple_choice::MultipleChoiceVote { option_id: 1 }, + rationale: None, + }), + }, + }, + &[], + ) + .unwrap(); + + let proposal: dpm_v241::query::ProposalResponse = app + .wrap() + .query_wasm_smart( + proposal_single.clone(), + &dpm_v241::msg::QueryMsg::Proposal { proposal_id: 1 }, + ) + .unwrap(); + + assert_eq!(proposal.proposal.status, dv_v241::status::Status::Passed); + assert_eq!(proposal.proposal.proposer, Addr::unchecked("ekez")); + assert_eq!(proposal.proposal.title, "title1".to_string()); + assert_eq!(proposal.proposal.description, "d".to_string()); + assert_eq!( + proposal.proposal.choices.len(), + // 3 + none of the above + 4 + ); + + app.execute_contract( + Addr::unchecked("ekez"), + proposal_single.clone(), + &dpm_v241::msg::ExecuteMsg::Execute { proposal_id: 1 }, + &[], + ) + .unwrap(); + + let proposal: dpm_v241::query::ProposalResponse = app + .wrap() + .query_wasm_smart( + proposal_single.clone(), + &dpm_v241::msg::QueryMsg::Proposal { proposal_id: 1 }, + ) + .unwrap(); + + assert_eq!(proposal.proposal.status, dv_v241::status::Status::Executed); + + // UPGRADE ONLY PRE-PROPOSE TO LATEST VIA DAO PROPOSAL + + let dppm_latest_id = app.store_code(dao_pre_propose_multiple_contract()); + + app.execute_contract( + Addr::unchecked("ekez"), + pre_propose.clone(), + &dppm_v241::ExecuteMsg::Propose { + msg: dppm_v241::ProposeMessage::Propose { + title: "upgrade pre-propose-multiple from v2.4.1".to_string(), + description: "d".to_string(), + choices: dv_v241::multiple_choice::MultipleChoiceOptions { + options: vec![ + dv_v241::multiple_choice::MultipleChoiceOption { + title: "first".to_string(), + description: "d".to_string(), + msgs: vec![], + }, + dv_v241::multiple_choice::MultipleChoiceOption { + title: "second".to_string(), + description: "d".to_string(), + msgs: vec![CosmosMsg::Wasm(WasmMsg::Migrate { + contract_addr: pre_propose.to_string(), + new_code_id: dppm_latest_id, + msg: to_json_binary(&MigrateMsg::FromUnderV250 { policy: None }) + .unwrap(), + })], + }, + dv_v241::multiple_choice::MultipleChoiceOption { + title: "third".to_string(), + description: "d".to_string(), + msgs: vec![], + }, + ], + }, + vote: Some(dv_v241::multiple_choice::MultipleChoiceAutoVote { + vote: dv_v241::multiple_choice::MultipleChoiceVote { option_id: 1 }, + rationale: None, + }), + }, + }, + &[], + ) + .unwrap(); + app.execute_contract( + Addr::unchecked("ekez"), + proposal_single.clone(), + &dpm_v241::msg::ExecuteMsg::Execute { proposal_id: 2 }, + &[], + ) + .unwrap(); + let proposal: dpm_v241::query::ProposalResponse = app + .wrap() + .query_wasm_smart( + proposal_single.clone(), + &dpm_v241::msg::QueryMsg::Proposal { proposal_id: 2 }, + ) + .unwrap(); + assert_eq!(proposal.proposal.status, dv_v241::status::Status::Executed); + + // MAKE SURE PRE PROPOSE INFO CHANGED + + let info: ContractVersion = from_json( + app.wrap() + .query_wasm_raw(pre_propose.clone(), "contract_info".as_bytes()) + .unwrap() + .unwrap(), + ) + .unwrap(); + assert_eq!( + ContractVersion { + contract: CONTRACT_NAME.to_string(), + version: CONTRACT_VERSION.to_string() + }, + info, + ); + + // MAKE SURE PRE PROPOSE CONFIG WAS UPDATED + + let config: Config = app + .wrap() + .query_wasm_smart(pre_propose.clone(), &QueryMsg::Config {}) + .unwrap(); + assert_eq!( + Config { + deposit_info: None, + submission_policy: PreProposeSubmissionPolicy::Anyone { denylist: None } + }, + config + ); + + // NOW MAKE SURE WE CAN MAKE AND VOTE ON NEW PROPOSALS + + app.execute_contract( + Addr::unchecked("ekez"), + pre_propose.clone(), + &ExecuteMsg::Propose { + msg: ProposeMessage::Propose { + title: "title2 on latest version".to_string(), + description: "d".to_string(), + choices: MultipleChoiceOptions { + options: vec![ + MultipleChoiceOption { + title: "first".to_string(), + description: "d".to_string(), + msgs: vec![], + }, + MultipleChoiceOption { + title: "second".to_string(), + description: "d".to_string(), + msgs: vec![], + }, + MultipleChoiceOption { + title: "third".to_string(), + description: "d".to_string(), + msgs: vec![], + }, + ], + }, + vote: Some(MultipleChoiceAutoVote { + vote: MultipleChoiceVote { option_id: 1 }, + rationale: None, + }), + }, + }, + &[], + ) + .unwrap(); + app.execute_contract( + Addr::unchecked("ekez"), + proposal_single.clone(), + &dao_proposal_multiple::msg::ExecuteMsg::Execute { proposal_id: 3 }, + &[], + ) + .unwrap(); + let proposal: ProposalResponse = app + .wrap() + .query_wasm_smart( + proposal_single.clone(), + &dao_proposal_multiple::msg::QueryMsg::Proposal { proposal_id: 3 }, + ) + .unwrap(); + assert_eq!(proposal.proposal.status, Status::Executed); +} + +#[test] +fn test_migrate_from_v241_with_policy_update() { + let app = &mut App::default(); + + let core_v241_contract = Box::new( + ContractWrapper::new( + core_v241::contract::execute, + core_v241::contract::instantiate, + core_v241::contract::query, + ) + .with_reply(core_v241::contract::reply), + ); + let dvcw4_v241_contract = Box::new( + ContractWrapper::new( + dvcw4_v241::contract::execute, + dvcw4_v241::contract::instantiate, + dvcw4_v241::contract::query, + ) + .with_reply(dvcw4_v241::contract::reply), + ); + let dppm_v241_contract = Box::new(ContractWrapper::new( + dppm_v241::contract::execute, + dppm_v241::contract::instantiate, + dppm_v241::contract::query, + )); + let dpm_v241_contract = Box::new( + ContractWrapper::new( + dpm_v241::contract::execute, + dpm_v241::contract::instantiate, + dpm_v241::contract::query, + ) + .with_reply(dpm_v241::contract::reply), + ); + + let core_id = app.store_code(core_v241_contract); + let cw4_id = app.store_code(cw4_group_contract()); + let dvcw4_v241_id = app.store_code(dvcw4_v241_contract); + let dppm_v241_id = app.store_code(dppm_v241_contract); + let dpm_v241_id = app.store_code(dpm_v241_contract); + + let governance_instantiate = di_v241::msg::InstantiateMsg { + dao_uri: None, + admin: None, + name: "DAO DAO".to_string(), + description: "A DAO that builds DAOs".to_string(), + image_url: None, + automatically_add_cw20s: true, + automatically_add_cw721s: true, + voting_module_instantiate_info: di_v241::state::ModuleInstantiateInfo { + code_id: dvcw4_v241_id, + msg: to_json_binary(&dvcw4_v241::msg::InstantiateMsg { + group_contract: dvcw4_v241::msg::GroupContract::New { + cw4_group_code_id: cw4_id, + initial_members: vec![ + cw4::Member { + addr: "ekez".to_string(), + weight: 9, + }, + cw4::Member { + addr: "keze".to_string(), + weight: 8, + }, + ], + }, + }) + .unwrap(), + admin: Some(di_v241::state::Admin::CoreModule {}), + funds: vec![], + label: "DAO DAO voting module".to_string(), + }, + proposal_modules_instantiate_info: vec![di_v241::state::ModuleInstantiateInfo { + code_id: dpm_v241_id, + msg: to_json_binary(&dpm_v241::msg::InstantiateMsg { + voting_strategy: dv_v241::multiple_choice::VotingStrategy::SingleChoice { + quorum: dv_v241::threshold::PercentageThreshold::Majority {}, + }, + max_voting_period: cw_utils::Duration::Time(86400), + min_voting_period: None, + only_members_execute: false, + allow_revoting: false, + pre_propose_info: dv_v241::pre_propose::PreProposeInfo::ModuleMayPropose { + info: di_v241::state::ModuleInstantiateInfo { + code_id: dppm_v241_id, + msg: to_json_binary(&dppm_v241::InstantiateMsg { + deposit_info: None, + open_proposal_submission: false, + extension: Empty::default(), + }) + .unwrap(), + admin: Some(di_v241::state::Admin::CoreModule {}), + funds: vec![], + label: "baby's first pre-propose module".to_string(), + }, + }, + close_proposal_on_execution_failure: false, + veto: None, + }) + .unwrap(), + admin: Some(di_v241::state::Admin::CoreModule {}), + funds: vec![], + label: "DAO DAO governance module".to_string(), + }], + initial_items: None, + }; + + let core_addr = app + .instantiate_contract( + core_id, + Addr::unchecked("ekez"), + &governance_instantiate, + &[], + "DAO DAO", + None, + ) + .unwrap(); + + app.update_block(|block| block.height += 1); + + let proposal_modules: Vec = app + .wrap() + .query_wasm_smart( + core_addr.clone(), + &di_v241::msg::QueryMsg::ProposalModules { + start_after: None, + limit: None, + }, + ) + .unwrap(); + + assert_eq!(proposal_modules.len(), 1); + let proposal_single = proposal_modules.into_iter().next().unwrap().address; + let proposal_creation_policy = app + .wrap() + .query_wasm_smart( + proposal_single.clone(), + &dpm_v241::msg::QueryMsg::ProposalCreationPolicy {}, + ) + .unwrap(); + + let pre_propose = match proposal_creation_policy { + dv_v241::pre_propose::ProposalCreationPolicy::Module { addr } => addr, + _ => panic!("expected a module for the proposal creation policy"), + }; + + // Make sure things were set up correctly. + assert_eq!( + proposal_single, + get_proposal_module(app, pre_propose.clone()) + ); + assert_eq!(core_addr, get_dao(app, pre_propose.clone())); + let info: ContractVersion = from_json( + app.wrap() + .query_wasm_raw(pre_propose.clone(), "contract_info".as_bytes()) + .unwrap() + .unwrap(), + ) + .unwrap(); + assert_eq!( + ContractVersion { + contract: "crates.io:dao-pre-propose-multiple".to_string(), + version: "2.4.1".to_string() + }, + info, + ); + + app.execute_contract( + Addr::unchecked("ekez"), + pre_propose.clone(), + &dppm_v241::ExecuteMsg::Propose { + msg: dppm_v241::ProposeMessage::Propose { + title: "title1".to_string(), + description: "d".to_string(), + choices: dv_v241::multiple_choice::MultipleChoiceOptions { + options: vec![ + dv_v241::multiple_choice::MultipleChoiceOption { + title: "first".to_string(), + description: "d".to_string(), + msgs: vec![], + }, + dv_v241::multiple_choice::MultipleChoiceOption { + title: "second".to_string(), + description: "d".to_string(), + msgs: vec![], + }, + dv_v241::multiple_choice::MultipleChoiceOption { + title: "third".to_string(), + description: "d".to_string(), + msgs: vec![], + }, + ], + }, + vote: Some(dv_v241::multiple_choice::MultipleChoiceAutoVote { + vote: dv_v241::multiple_choice::MultipleChoiceVote { option_id: 1 }, + rationale: None, + }), + }, + }, + &[], + ) + .unwrap(); + + let proposal: dpm_v241::query::ProposalResponse = app + .wrap() + .query_wasm_smart( + proposal_single.clone(), + &dpm_v241::msg::QueryMsg::Proposal { proposal_id: 1 }, + ) + .unwrap(); + + assert_eq!(proposal.proposal.status, dv_v241::status::Status::Passed); + assert_eq!(proposal.proposal.proposer, Addr::unchecked("ekez")); + assert_eq!(proposal.proposal.title, "title1".to_string()); + assert_eq!(proposal.proposal.description, "d".to_string()); + assert_eq!( + proposal.proposal.choices.len(), + // 3 + none of the above + 4 + ); + + app.execute_contract( + Addr::unchecked("ekez"), + proposal_single.clone(), + &dpm_v241::msg::ExecuteMsg::Execute { proposal_id: 1 }, + &[], + ) + .unwrap(); + + let proposal: dpm_v241::query::ProposalResponse = app + .wrap() + .query_wasm_smart( + proposal_single.clone(), + &dpm_v241::msg::QueryMsg::Proposal { proposal_id: 1 }, + ) + .unwrap(); + + assert_eq!(proposal.proposal.status, dv_v241::status::Status::Executed); + + // UPGRADE ONLY PRE-PROPOSE TO LATEST VIA DAO PROPOSAL WITH POLICY UPDATE + + let dppm_latest_id = app.store_code(dao_pre_propose_multiple_contract()); + + app.execute_contract( + Addr::unchecked("ekez"), + pre_propose.clone(), + &dppm_v241::ExecuteMsg::Propose { + msg: dppm_v241::ProposeMessage::Propose { + title: "upgrade pre-propose-multiple from v2.4.1".to_string(), + description: "d".to_string(), + choices: dv_v241::multiple_choice::MultipleChoiceOptions { + options: vec![ + dv_v241::multiple_choice::MultipleChoiceOption { + title: "first".to_string(), + description: "d".to_string(), + msgs: vec![], + }, + dv_v241::multiple_choice::MultipleChoiceOption { + title: "second".to_string(), + description: "d".to_string(), + msgs: vec![CosmosMsg::Wasm(WasmMsg::Migrate { + contract_addr: pre_propose.to_string(), + new_code_id: dppm_latest_id, + msg: to_json_binary(&MigrateMsg::FromUnderV250 { + policy: Some(PreProposeSubmissionPolicy::Specific { + dao_members: false, + allowlist: Some(vec!["noob".to_string()]), + denylist: None, + }), + }) + .unwrap(), + })], + }, + dv_v241::multiple_choice::MultipleChoiceOption { + title: "third".to_string(), + description: "d".to_string(), + msgs: vec![], + }, + ], + }, + vote: Some(dv_v241::multiple_choice::MultipleChoiceAutoVote { + vote: dv_v241::multiple_choice::MultipleChoiceVote { option_id: 1 }, + rationale: None, + }), + }, + }, + &[], + ) + .unwrap(); + app.execute_contract( + Addr::unchecked("ekez"), + proposal_single.clone(), + &dpm_v241::msg::ExecuteMsg::Execute { proposal_id: 2 }, + &[], + ) + .unwrap(); + let proposal: dpm_v241::query::ProposalResponse = app + .wrap() + .query_wasm_smart( + proposal_single.clone(), + &dpm_v241::msg::QueryMsg::Proposal { proposal_id: 2 }, + ) + .unwrap(); + assert_eq!(proposal.proposal.status, dv_v241::status::Status::Executed); + + // MAKE SURE PRE PROPOSE INFO CHANGED + + let info: ContractVersion = from_json( + app.wrap() + .query_wasm_raw(pre_propose.clone(), "contract_info".as_bytes()) + .unwrap() + .unwrap(), + ) + .unwrap(); + assert_eq!( + ContractVersion { + contract: CONTRACT_NAME.to_string(), + version: CONTRACT_VERSION.to_string() + }, + info, + ); + + // MAKE SURE PRE PROPOSE CONFIG WAS UPDATED + + let config: Config = app + .wrap() + .query_wasm_smart(pre_propose.clone(), &QueryMsg::Config {}) + .unwrap(); + assert_eq!( + Config { + deposit_info: None, + submission_policy: PreProposeSubmissionPolicy::Specific { + dao_members: false, + allowlist: Some(vec!["noob".to_string()]), + denylist: None + } + }, + config + ); + + // NOW MAKE SURE ONLY NOOB CAN MAKE PROPOSALS + + let err: PreProposeError = app + .execute_contract( + Addr::unchecked("ekez"), + pre_propose.clone(), + &ExecuteMsg::Propose { + msg: ProposeMessage::Propose { + title: "title2 on latest version".to_string(), + description: "d".to_string(), + choices: MultipleChoiceOptions { + options: vec![ + MultipleChoiceOption { + title: "first".to_string(), + description: "d".to_string(), + msgs: vec![], + }, + MultipleChoiceOption { + title: "second".to_string(), + description: "d".to_string(), + msgs: vec![], + }, + MultipleChoiceOption { + title: "third".to_string(), + description: "d".to_string(), + msgs: vec![], + }, + ], + }, + vote: None, + }, + }, + &[], + ) + .unwrap_err() + .downcast() + .unwrap(); + assert_eq!( + err, + PreProposeError::SubmissionPolicy(PreProposeSubmissionPolicyError::Unauthorized {}) + ); + + app.execute_contract( + Addr::unchecked("noob"), + pre_propose.clone(), + &ExecuteMsg::Propose { + msg: ProposeMessage::Propose { + title: "title2 on latest version".to_string(), + description: "d".to_string(), + choices: MultipleChoiceOptions { + options: vec![ + MultipleChoiceOption { + title: "first".to_string(), + description: "d".to_string(), + msgs: vec![], + }, + MultipleChoiceOption { + title: "second".to_string(), + description: "d".to_string(), + msgs: vec![], + }, + MultipleChoiceOption { + title: "third".to_string(), + description: "d".to_string(), + msgs: vec![], + }, + ], + }, + vote: None, + }, + }, + &[], + ) + .unwrap(); + app.execute_contract( + Addr::unchecked("ekez"), + proposal_single.clone(), + &dao_proposal_multiple::msg::ExecuteMsg::Vote { + proposal_id: 3, + vote: MultipleChoiceVote { option_id: 1 }, + rationale: None, + }, + &[], + ) + .unwrap(); + app.execute_contract( + Addr::unchecked("ekez"), + proposal_single.clone(), + &dao_proposal_multiple::msg::ExecuteMsg::Execute { proposal_id: 3 }, + &[], + ) + .unwrap(); + let proposal: ProposalResponse = app + .wrap() + .query_wasm_smart( + proposal_single.clone(), + &dao_proposal_multiple::msg::QueryMsg::Proposal { proposal_id: 3 }, + ) + .unwrap(); + assert_eq!(proposal.proposal.status, Status::Executed); +} diff --git a/contracts/pre-propose/dao-pre-propose-single/Cargo.toml b/contracts/pre-propose/dao-pre-propose-single/Cargo.toml index ac1fea9d4..ce70721c6 100644 --- a/contracts/pre-propose/dao-pre-propose-single/Cargo.toml +++ b/contracts/pre-propose/dao-pre-propose-single/Cargo.toml @@ -26,6 +26,7 @@ dao-voting = { workspace = true } [dev-dependencies] cw-multi-test = { workspace = true } cw-utils = { workspace = true } +cw4 = { workspace = true } cw4-group = { workspace = true } cw20 = { workspace = true } cw20-base = { workspace = true } @@ -39,3 +40,11 @@ dao-testing = { workspace = true } dao-hooks = { workspace = true } dao-proposal-single = { workspace = true } cw-hooks = { workspace = true } + +# v2.4.1 migration +dao-dao-core-v241 = { workspace = true } +dao-interface-v241 = { workspace = true } +dao-pre-propose-single-v241 = { workspace = true } +dao-proposal-single-v241 = { workspace = true } +dao-voting-cw4-v241 = { workspace = true } +dao-voting-v241 = { workspace = true } diff --git a/contracts/pre-propose/dao-pre-propose-single/src/contract.rs b/contracts/pre-propose/dao-pre-propose-single/src/contract.rs index d159d823f..4eb840793 100644 --- a/contracts/pre-propose/dao-pre-propose-single/src/contract.rs +++ b/contracts/pre-propose/dao-pre-propose-single/src/contract.rs @@ -137,6 +137,8 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { } #[cfg_attr(not(feature = "library"), entry_point)] -pub fn migrate(deps: DepsMut, _env: Env, msg: MigrateMsg) -> Result { - PrePropose::default().migrate(deps, msg) +pub fn migrate(mut deps: DepsMut, _env: Env, msg: MigrateMsg) -> Result { + let res = PrePropose::default().migrate(deps.branch(), msg); + set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; + res } diff --git a/contracts/pre-propose/dao-pre-propose-single/src/tests.rs b/contracts/pre-propose/dao-pre-propose-single/src/tests.rs index 96d575e65..e7efc6615 100644 --- a/contracts/pre-propose/dao-pre-propose-single/src/tests.rs +++ b/contracts/pre-propose/dao-pre-propose-single/src/tests.rs @@ -1,4 +1,6 @@ -use cosmwasm_std::{coins, from_json, to_json_binary, Addr, Coin, Empty, Uint128}; +use cosmwasm_std::{ + coins, from_json, to_json_binary, Addr, Coin, CosmosMsg, Empty, Uint128, WasmMsg, +}; use cw2::ContractVersion; use cw20::Cw20Coin; use cw_denom::UncheckedDenom; @@ -9,20 +11,28 @@ use dao_interface::state::ProposalModule; use dao_interface::state::{Admin, ModuleInstantiateInfo}; use dao_pre_propose_base::{error::PreProposeError, msg::DepositInfoResponse, state::Config}; use dao_proposal_single as dps; -use dao_testing::helpers::instantiate_with_cw4_groups_governance; +use dao_testing::{contracts::cw4_group_contract, helpers::instantiate_with_cw4_groups_governance}; use dao_voting::pre_propose::{PreProposeSubmissionPolicy, PreProposeSubmissionPolicyError}; use dao_voting::{ deposit::{CheckedDepositInfo, DepositRefundPolicy, DepositToken, UncheckedDepositInfo}, pre_propose::{PreProposeInfo, ProposalCreationPolicy}, status::Status, threshold::{PercentageThreshold, Threshold}, - voting::Vote, + voting::{SingleChoiceAutoVote, Vote}, }; use dps::query::ProposalResponse; +// test v2.4.1 migration +use dao_dao_core_v241 as core_v241; +use dao_interface_v241 as di_v241; +use dao_pre_propose_single_v241 as dpps_v241; +use dao_proposal_single_v241 as dps_v241; +use dao_voting_cw4_v241 as dvcw4_v241; +use dao_voting_v241 as dv_v241; + use crate::contract::*; -fn cw_dao_proposal_single_contract() -> Box> { +fn dao_proposal_single_contract() -> Box> { let contract = ContractWrapper::new( dps::contract::execute, dps::contract::instantiate, @@ -33,8 +43,8 @@ fn cw_dao_proposal_single_contract() -> Box> { Box::new(contract) } -fn cw_pre_propose_base_proposal_single() -> Box> { - let contract = ContractWrapper::new(execute, instantiate, query); +fn dao_pre_propose_single_contract() -> Box> { + let contract = ContractWrapper::new(execute, instantiate, query).with_migrate(migrate); Box::new(contract) } @@ -52,7 +62,7 @@ fn get_default_proposal_module_instantiate( deposit_info: Option, open_proposal_submission: bool, ) -> dps::msg::InstantiateMsg { - let pre_propose_id = app.store_code(cw_pre_propose_base_proposal_single()); + let pre_propose_id = app.store_code(dao_pre_propose_single_contract()); let submission_policy = if open_proposal_submission { PreProposeSubmissionPolicy::Anyone { denylist: None } @@ -125,7 +135,7 @@ fn setup_default_test( deposit_info: Option, open_proposal_submission: bool, ) -> DefaultTestSetup { - let dps_id = app.store_code(cw_dao_proposal_single_contract()); + let dps_id = app.store_code(dao_proposal_single_contract()); let proposal_module_instantiate = get_default_proposal_module_instantiate(app, deposit_info, open_proposal_submission); @@ -1245,10 +1255,10 @@ fn test_execute_extension_does_nothing() { fn test_instantiate_with_zero_native_deposit() { let mut app = App::default(); - let dps_id = app.store_code(cw_dao_proposal_single_contract()); + let dps_id = app.store_code(dao_proposal_single_contract()); let proposal_module_instantiate = { - let pre_propose_id = app.store_code(cw_pre_propose_base_proposal_single()); + let pre_propose_id = app.store_code(dao_pre_propose_single_contract()); dps::msg::InstantiateMsg { threshold: Threshold::AbsolutePercentage { @@ -1312,10 +1322,10 @@ fn test_instantiate_with_zero_cw20_deposit() { let cw20_addr = instantiate_cw20_base_default(&mut app); - let dps_id = app.store_code(cw_dao_proposal_single_contract()); + let dps_id = app.store_code(dao_proposal_single_contract()); let proposal_module_instantiate = { - let pre_propose_id = app.store_code(cw_pre_propose_base_proposal_single()); + let pre_propose_id = app.store_code(dao_pre_propose_single_contract()); dps::msg::InstantiateMsg { threshold: Threshold::AbsolutePercentage { @@ -2171,3 +2181,694 @@ fn test_hook_management() { let hooks = query_hooks(app, pre_propose).hooks; assert_eq!(hooks, vec!["two".to_string()]) } + +#[test] +fn test_migrate_from_v241() { + let app = &mut App::default(); + + let core_v241_contract = Box::new( + ContractWrapper::new( + core_v241::contract::execute, + core_v241::contract::instantiate, + core_v241::contract::query, + ) + .with_reply(core_v241::contract::reply), + ); + let dvcw4_v241_contract = Box::new( + ContractWrapper::new( + dvcw4_v241::contract::execute, + dvcw4_v241::contract::instantiate, + dvcw4_v241::contract::query, + ) + .with_reply(dvcw4_v241::contract::reply), + ); + let dpps_v241_contract = Box::new(ContractWrapper::new( + dpps_v241::contract::execute, + dpps_v241::contract::instantiate, + dpps_v241::contract::query, + )); + let dps_v241_contract = Box::new( + ContractWrapper::new( + dps_v241::contract::execute, + dps_v241::contract::instantiate, + dps_v241::contract::query, + ) + .with_reply(dps_v241::contract::reply), + ); + + let core_id = app.store_code(core_v241_contract); + let cw4_id = app.store_code(cw4_group_contract()); + let dvcw4_v241_id = app.store_code(dvcw4_v241_contract); + let dpps_v241_id = app.store_code(dpps_v241_contract); + let dps_v241_id = app.store_code(dps_v241_contract); + + let governance_instantiate = di_v241::msg::InstantiateMsg { + dao_uri: None, + admin: None, + name: "DAO DAO".to_string(), + description: "A DAO that builds DAOs".to_string(), + image_url: None, + automatically_add_cw20s: true, + automatically_add_cw721s: true, + voting_module_instantiate_info: di_v241::state::ModuleInstantiateInfo { + code_id: dvcw4_v241_id, + msg: to_json_binary(&dvcw4_v241::msg::InstantiateMsg { + group_contract: dvcw4_v241::msg::GroupContract::New { + cw4_group_code_id: cw4_id, + initial_members: vec![ + cw4::Member { + addr: "ekez".to_string(), + weight: 9, + }, + cw4::Member { + addr: "keze".to_string(), + weight: 8, + }, + ], + }, + }) + .unwrap(), + admin: Some(di_v241::state::Admin::CoreModule {}), + funds: vec![], + label: "DAO DAO voting module".to_string(), + }, + proposal_modules_instantiate_info: vec![di_v241::state::ModuleInstantiateInfo { + code_id: dps_v241_id, + msg: to_json_binary(&dps_v241::msg::InstantiateMsg { + threshold: dv_v241::threshold::Threshold::AbsolutePercentage { + percentage: dv_v241::threshold::PercentageThreshold::Majority {}, + }, + max_voting_period: cw_utils::Duration::Time(86400), + min_voting_period: None, + only_members_execute: false, + allow_revoting: false, + pre_propose_info: dv_v241::pre_propose::PreProposeInfo::ModuleMayPropose { + info: di_v241::state::ModuleInstantiateInfo { + code_id: dpps_v241_id, + msg: to_json_binary(&dpps_v241::InstantiateMsg { + deposit_info: None, + open_proposal_submission: false, + extension: Empty::default(), + }) + .unwrap(), + admin: Some(di_v241::state::Admin::CoreModule {}), + funds: vec![], + label: "baby's first pre-propose module".to_string(), + }, + }, + close_proposal_on_execution_failure: false, + veto: None, + }) + .unwrap(), + admin: Some(di_v241::state::Admin::CoreModule {}), + funds: vec![], + label: "DAO DAO governance module".to_string(), + }], + initial_items: None, + }; + + let core_addr = app + .instantiate_contract( + core_id, + Addr::unchecked("ekez"), + &governance_instantiate, + &[], + "DAO DAO", + None, + ) + .unwrap(); + + app.update_block(|block| block.height += 1); + + let proposal_modules: Vec = app + .wrap() + .query_wasm_smart( + core_addr.clone(), + &di_v241::msg::QueryMsg::ProposalModules { + start_after: None, + limit: None, + }, + ) + .unwrap(); + + assert_eq!(proposal_modules.len(), 1); + let proposal_single = proposal_modules.into_iter().next().unwrap().address; + let proposal_creation_policy = app + .wrap() + .query_wasm_smart( + proposal_single.clone(), + &dps_v241::msg::QueryMsg::ProposalCreationPolicy {}, + ) + .unwrap(); + + let pre_propose = match proposal_creation_policy { + dv_v241::pre_propose::ProposalCreationPolicy::Module { addr } => addr, + _ => panic!("expected a module for the proposal creation policy"), + }; + + // Make sure things were set up correctly. + assert_eq!( + proposal_single, + get_proposal_module(app, pre_propose.clone()) + ); + assert_eq!(core_addr, get_dao(app, pre_propose.clone())); + let info: ContractVersion = from_json( + app.wrap() + .query_wasm_raw(pre_propose.clone(), "contract_info".as_bytes()) + .unwrap() + .unwrap(), + ) + .unwrap(); + assert_eq!( + ContractVersion { + contract: "crates.io:dao-pre-propose-single".to_string(), + version: "2.4.1".to_string() + }, + info, + ); + + app.execute_contract( + Addr::unchecked("ekez"), + pre_propose.clone(), + &dpps_v241::ExecuteMsg::Propose { + msg: dpps_v241::ProposeMessage::Propose { + title: "title1".to_string(), + description: "d".to_string(), + msgs: vec![], + vote: Some(dv_v241::voting::SingleChoiceAutoVote { + vote: dv_v241::voting::Vote::Yes, + rationale: None, + }), + }, + }, + &[], + ) + .unwrap(); + + let proposal: dps_v241::query::ProposalResponse = app + .wrap() + .query_wasm_smart( + proposal_single.clone(), + &dps_v241::msg::QueryMsg::Proposal { proposal_id: 1 }, + ) + .unwrap(); + + assert_eq!(proposal.proposal.status, dv_v241::status::Status::Passed); + assert_eq!(proposal.proposal.proposer, Addr::unchecked("ekez")); + assert_eq!(proposal.proposal.title, "title1".to_string()); + assert_eq!(proposal.proposal.description, "d".to_string()); + assert_eq!(proposal.proposal.msgs, vec![]); + + app.execute_contract( + Addr::unchecked("ekez"), + proposal_single.clone(), + &dps_v241::msg::ExecuteMsg::Execute { proposal_id: 1 }, + &[], + ) + .unwrap(); + + let proposal: dps_v241::query::ProposalResponse = app + .wrap() + .query_wasm_smart( + proposal_single.clone(), + &dps_v241::msg::QueryMsg::Proposal { proposal_id: 1 }, + ) + .unwrap(); + + assert_eq!(proposal.proposal.status, dv_v241::status::Status::Executed); + + // UPGRADE ONLY PRE-PROPOSE TO LATEST VIA DAO PROPOSAL + + let dpps_latest_id = app.store_code(dao_pre_propose_single_contract()); + + app.execute_contract( + Addr::unchecked("ekez"), + pre_propose.clone(), + &dpps_v241::ExecuteMsg::Propose { + msg: dpps_v241::ProposeMessage::Propose { + title: "upgrade pre-propose-single from v2.4.1".to_string(), + description: "d".to_string(), + msgs: vec![CosmosMsg::Wasm(WasmMsg::Migrate { + contract_addr: pre_propose.to_string(), + new_code_id: dpps_latest_id, + msg: to_json_binary(&MigrateMsg::FromUnderV250 { policy: None }).unwrap(), + })], + vote: Some(dv_v241::voting::SingleChoiceAutoVote { + vote: dv_v241::voting::Vote::Yes, + rationale: None, + }), + }, + }, + &[], + ) + .unwrap(); + app.execute_contract( + Addr::unchecked("ekez"), + proposal_single.clone(), + &dps_v241::msg::ExecuteMsg::Execute { proposal_id: 2 }, + &[], + ) + .unwrap(); + let proposal: dps_v241::query::ProposalResponse = app + .wrap() + .query_wasm_smart( + proposal_single.clone(), + &dps_v241::msg::QueryMsg::Proposal { proposal_id: 2 }, + ) + .unwrap(); + assert_eq!(proposal.proposal.status, dv_v241::status::Status::Executed); + + // MAKE SURE PRE PROPOSE INFO CHANGED + + let info: ContractVersion = from_json( + app.wrap() + .query_wasm_raw(pre_propose.clone(), "contract_info".as_bytes()) + .unwrap() + .unwrap(), + ) + .unwrap(); + assert_eq!( + ContractVersion { + contract: CONTRACT_NAME.to_string(), + version: CONTRACT_VERSION.to_string() + }, + info, + ); + + // MAKE SURE PRE PROPOSE CONFIG WAS UPDATED + + let config: Config = app + .wrap() + .query_wasm_smart(pre_propose.clone(), &QueryMsg::Config {}) + .unwrap(); + assert_eq!( + Config { + deposit_info: None, + submission_policy: PreProposeSubmissionPolicy::Specific { + dao_members: true, + allowlist: None, + denylist: None + } + }, + config + ); + + // NOW MAKE SURE WE CAN MAKE AND VOTE ON NEW PROPOSALS + + app.execute_contract( + Addr::unchecked("ekez"), + pre_propose.clone(), + &ExecuteMsg::Propose { + msg: ProposeMessage::Propose { + title: "title2 on latest version".to_string(), + description: "d".to_string(), + msgs: vec![], + vote: Some(SingleChoiceAutoVote { + vote: Vote::Yes, + rationale: None, + }), + }, + }, + &[], + ) + .unwrap(); + app.execute_contract( + Addr::unchecked("ekez"), + proposal_single.clone(), + &dao_proposal_single::msg::ExecuteMsg::Execute { proposal_id: 3 }, + &[], + ) + .unwrap(); + let proposal: ProposalResponse = app + .wrap() + .query_wasm_smart( + proposal_single.clone(), + &dao_proposal_single::msg::QueryMsg::Proposal { proposal_id: 3 }, + ) + .unwrap(); + assert_eq!(proposal.proposal.status, Status::Executed); +} + +#[test] +fn test_migrate_from_v241_with_policy_update() { + let app = &mut App::default(); + + let core_v241_contract = Box::new( + ContractWrapper::new( + core_v241::contract::execute, + core_v241::contract::instantiate, + core_v241::contract::query, + ) + .with_reply(core_v241::contract::reply), + ); + let dvcw4_v241_contract = Box::new( + ContractWrapper::new( + dvcw4_v241::contract::execute, + dvcw4_v241::contract::instantiate, + dvcw4_v241::contract::query, + ) + .with_reply(dvcw4_v241::contract::reply), + ); + let dpps_v241_contract = Box::new(ContractWrapper::new( + dpps_v241::contract::execute, + dpps_v241::contract::instantiate, + dpps_v241::contract::query, + )); + let dps_v241_contract = Box::new( + ContractWrapper::new( + dps_v241::contract::execute, + dps_v241::contract::instantiate, + dps_v241::contract::query, + ) + .with_reply(dps_v241::contract::reply), + ); + + let core_id = app.store_code(core_v241_contract); + let cw4_id = app.store_code(cw4_group_contract()); + let dvcw4_v241_id = app.store_code(dvcw4_v241_contract); + let dpps_v241_id = app.store_code(dpps_v241_contract); + let dps_v241_id = app.store_code(dps_v241_contract); + + let governance_instantiate = di_v241::msg::InstantiateMsg { + dao_uri: None, + admin: None, + name: "DAO DAO".to_string(), + description: "A DAO that builds DAOs".to_string(), + image_url: None, + automatically_add_cw20s: true, + automatically_add_cw721s: true, + voting_module_instantiate_info: di_v241::state::ModuleInstantiateInfo { + code_id: dvcw4_v241_id, + msg: to_json_binary(&dvcw4_v241::msg::InstantiateMsg { + group_contract: dvcw4_v241::msg::GroupContract::New { + cw4_group_code_id: cw4_id, + initial_members: vec![ + cw4::Member { + addr: "ekez".to_string(), + weight: 9, + }, + cw4::Member { + addr: "keze".to_string(), + weight: 8, + }, + ], + }, + }) + .unwrap(), + admin: Some(di_v241::state::Admin::CoreModule {}), + funds: vec![], + label: "DAO DAO voting module".to_string(), + }, + proposal_modules_instantiate_info: vec![di_v241::state::ModuleInstantiateInfo { + code_id: dps_v241_id, + msg: to_json_binary(&dps_v241::msg::InstantiateMsg { + threshold: dv_v241::threshold::Threshold::AbsolutePercentage { + percentage: dv_v241::threshold::PercentageThreshold::Majority {}, + }, + max_voting_period: cw_utils::Duration::Time(86400), + min_voting_period: None, + only_members_execute: false, + allow_revoting: false, + pre_propose_info: dv_v241::pre_propose::PreProposeInfo::ModuleMayPropose { + info: di_v241::state::ModuleInstantiateInfo { + code_id: dpps_v241_id, + msg: to_json_binary(&dpps_v241::InstantiateMsg { + deposit_info: None, + open_proposal_submission: false, + extension: Empty::default(), + }) + .unwrap(), + admin: Some(di_v241::state::Admin::CoreModule {}), + funds: vec![], + label: "baby's first pre-propose module".to_string(), + }, + }, + close_proposal_on_execution_failure: false, + veto: None, + }) + .unwrap(), + admin: Some(di_v241::state::Admin::CoreModule {}), + funds: vec![], + label: "DAO DAO governance module".to_string(), + }], + initial_items: None, + }; + + let core_addr = app + .instantiate_contract( + core_id, + Addr::unchecked("ekez"), + &governance_instantiate, + &[], + "DAO DAO", + None, + ) + .unwrap(); + + app.update_block(|block| block.height += 1); + + let proposal_modules: Vec = app + .wrap() + .query_wasm_smart( + core_addr.clone(), + &di_v241::msg::QueryMsg::ProposalModules { + start_after: None, + limit: None, + }, + ) + .unwrap(); + + assert_eq!(proposal_modules.len(), 1); + let proposal_single = proposal_modules.into_iter().next().unwrap().address; + let proposal_creation_policy = app + .wrap() + .query_wasm_smart( + proposal_single.clone(), + &dps_v241::msg::QueryMsg::ProposalCreationPolicy {}, + ) + .unwrap(); + + let pre_propose = match proposal_creation_policy { + dv_v241::pre_propose::ProposalCreationPolicy::Module { addr } => addr, + _ => panic!("expected a module for the proposal creation policy"), + }; + + // Make sure things were set up correctly. + assert_eq!( + proposal_single, + get_proposal_module(app, pre_propose.clone()) + ); + assert_eq!(core_addr, get_dao(app, pre_propose.clone())); + let info: ContractVersion = from_json( + app.wrap() + .query_wasm_raw(pre_propose.clone(), "contract_info".as_bytes()) + .unwrap() + .unwrap(), + ) + .unwrap(); + assert_eq!( + ContractVersion { + contract: "crates.io:dao-pre-propose-single".to_string(), + version: "2.4.1".to_string() + }, + info, + ); + + app.execute_contract( + Addr::unchecked("ekez"), + pre_propose.clone(), + &dpps_v241::ExecuteMsg::Propose { + msg: dpps_v241::ProposeMessage::Propose { + title: "title1".to_string(), + description: "d".to_string(), + msgs: vec![], + vote: Some(dv_v241::voting::SingleChoiceAutoVote { + vote: dv_v241::voting::Vote::Yes, + rationale: None, + }), + }, + }, + &[], + ) + .unwrap(); + + let proposal: dps_v241::query::ProposalResponse = app + .wrap() + .query_wasm_smart( + proposal_single.clone(), + &dps_v241::msg::QueryMsg::Proposal { proposal_id: 1 }, + ) + .unwrap(); + + assert_eq!(proposal.proposal.status, dv_v241::status::Status::Passed); + assert_eq!(proposal.proposal.proposer, Addr::unchecked("ekez")); + assert_eq!(proposal.proposal.title, "title1".to_string()); + assert_eq!(proposal.proposal.description, "d".to_string()); + assert_eq!(proposal.proposal.msgs, vec![]); + + app.execute_contract( + Addr::unchecked("ekez"), + proposal_single.clone(), + &dps_v241::msg::ExecuteMsg::Execute { proposal_id: 1 }, + &[], + ) + .unwrap(); + + let proposal: dps_v241::query::ProposalResponse = app + .wrap() + .query_wasm_smart( + proposal_single.clone(), + &dps_v241::msg::QueryMsg::Proposal { proposal_id: 1 }, + ) + .unwrap(); + + assert_eq!(proposal.proposal.status, dv_v241::status::Status::Executed); + + // UPGRADE ONLY PRE-PROPOSE TO LATEST VIA DAO PROPOSAL WITH POLICY UPDATE + + let dpps_latest_id = app.store_code(dao_pre_propose_single_contract()); + + app.execute_contract( + Addr::unchecked("ekez"), + pre_propose.clone(), + &dpps_v241::ExecuteMsg::Propose { + msg: dpps_v241::ProposeMessage::Propose { + title: "upgrade pre-propose-single from v2.4.1".to_string(), + description: "d".to_string(), + msgs: vec![CosmosMsg::Wasm(WasmMsg::Migrate { + contract_addr: pre_propose.to_string(), + new_code_id: dpps_latest_id, + msg: to_json_binary(&MigrateMsg::FromUnderV250 { + policy: Some(PreProposeSubmissionPolicy::Specific { + dao_members: false, + allowlist: Some(vec!["noob".to_string()]), + denylist: None, + }), + }) + .unwrap(), + })], + vote: Some(dv_v241::voting::SingleChoiceAutoVote { + vote: dv_v241::voting::Vote::Yes, + rationale: None, + }), + }, + }, + &[], + ) + .unwrap(); + app.execute_contract( + Addr::unchecked("ekez"), + proposal_single.clone(), + &dps_v241::msg::ExecuteMsg::Execute { proposal_id: 2 }, + &[], + ) + .unwrap(); + let proposal: dps_v241::query::ProposalResponse = app + .wrap() + .query_wasm_smart( + proposal_single.clone(), + &dps_v241::msg::QueryMsg::Proposal { proposal_id: 2 }, + ) + .unwrap(); + assert_eq!(proposal.proposal.status, dv_v241::status::Status::Executed); + + // MAKE SURE PRE PROPOSE INFO CHANGED + + let info: ContractVersion = from_json( + app.wrap() + .query_wasm_raw(pre_propose.clone(), "contract_info".as_bytes()) + .unwrap() + .unwrap(), + ) + .unwrap(); + assert_eq!( + ContractVersion { + contract: CONTRACT_NAME.to_string(), + version: CONTRACT_VERSION.to_string() + }, + info, + ); + + // MAKE SURE PRE PROPOSE CONFIG WAS UPDATED + + let config: Config = app + .wrap() + .query_wasm_smart(pre_propose.clone(), &QueryMsg::Config {}) + .unwrap(); + assert_eq!( + Config { + deposit_info: None, + submission_policy: PreProposeSubmissionPolicy::Specific { + dao_members: false, + allowlist: Some(vec!["noob".to_string()]), + denylist: None + } + }, + config + ); + + // NOW MAKE SURE ONLY NOOB CAN MAKE PROPOSALS + + let err: PreProposeError = app + .execute_contract( + Addr::unchecked("ekez"), + pre_propose.clone(), + &ExecuteMsg::Propose { + msg: ProposeMessage::Propose { + title: "title2 on latest version".to_string(), + description: "d".to_string(), + msgs: vec![], + vote: None, + }, + }, + &[], + ) + .unwrap_err() + .downcast() + .unwrap(); + assert_eq!( + err, + PreProposeError::SubmissionPolicy(PreProposeSubmissionPolicyError::Unauthorized {}) + ); + + app.execute_contract( + Addr::unchecked("noob"), + pre_propose.clone(), + &ExecuteMsg::Propose { + msg: ProposeMessage::Propose { + title: "title2 on latest version".to_string(), + description: "d".to_string(), + msgs: vec![], + vote: None, + }, + }, + &[], + ) + .unwrap(); + app.execute_contract( + Addr::unchecked("ekez"), + proposal_single.clone(), + &dao_proposal_single::msg::ExecuteMsg::Vote { + proposal_id: 3, + vote: Vote::Yes, + rationale: None, + }, + &[], + ) + .unwrap(); + app.execute_contract( + Addr::unchecked("ekez"), + proposal_single.clone(), + &dao_proposal_single::msg::ExecuteMsg::Execute { proposal_id: 3 }, + &[], + ) + .unwrap(); + let proposal: ProposalResponse = app + .wrap() + .query_wasm_smart( + proposal_single.clone(), + &dao_proposal_single::msg::QueryMsg::Proposal { proposal_id: 3 }, + ) + .unwrap(); + assert_eq!(proposal.proposal.status, Status::Executed); +} diff --git a/packages/dao-pre-propose-base/Cargo.toml b/packages/dao-pre-propose-base/Cargo.toml index 012273f2a..53beba053 100644 --- a/packages/dao-pre-propose-base/Cargo.toml +++ b/packages/dao-pre-propose-base/Cargo.toml @@ -30,7 +30,7 @@ serde = { workspace = true } thiserror = { workspace = true } semver = { workspace = true } -# v2.4.0 packages for state migration +# v2.4.1 packages for state migration cw-denom-v241 = { workspace = true } dao-pre-propose-base-v241 = { workspace = true } dao-voting-v241 = { workspace = true }