From dc085a3192c8c06a0fe5c0b5794778c7944aa9b0 Mon Sep 17 00:00:00 2001 From: Nickf12 Date: Wed, 29 Nov 2023 22:27:59 +0100 Subject: [PATCH] Auto Pass Props if User Controls 100% of Voting power #759 --- .../dao-proposal-single/src/contract.rs | 51 ++++--- .../src/testing/adversarial_tests.rs | 13 +- .../src/testing/instantiate.rs | 59 +++++--- .../dao-proposal-single/src/testing/mod.rs | 1 + .../dao-proposal-single/src/testing/tests.rs | 133 +++++++++++++--- .../dao-proposal-hook-counter/src/tests.rs | 44 ++++-- packages/dao-testing/src/tests.rs | 143 ++++++++++++------ 7 files changed, 318 insertions(+), 126 deletions(-) diff --git a/contracts/proposal/dao-proposal-single/src/contract.rs b/contracts/proposal/dao-proposal-single/src/contract.rs index 93e73eb76..d06980c44 100644 --- a/contracts/proposal/dao-proposal-single/src/contract.rs +++ b/contracts/proposal/dao-proposal-single/src/contract.rs @@ -1,7 +1,7 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - to_json_binary, Addr, Binary, CosmosMsg, Deps, DepsMut, Empty, Env, MessageInfo, Order, Reply, + to_binary, Addr, Binary, CosmosMsg, Deps, DepsMut, Empty, Env, MessageInfo, Order, Reply, Response, StdResult, Storage, SubMsg, WasmMsg, }; use cw2::{get_contract_version, set_contract_version, ContractVersion}; @@ -183,6 +183,14 @@ pub fn execute_propose( &dao_interface::msg::QueryMsg::VotingModule {}, )?; + // Get Proposer voting power + let power = get_voting_power( + deps.as_ref(), + proposer.clone(), + &config.dao, + Some(env.block.height), + )?; + // Voting modules are not required to implement this // query. Lacking an implementation they are active by default. let active_resp: IsActiveResponse = deps @@ -200,6 +208,7 @@ pub fn execute_propose( let proposal = { // Limit mutability to this block. + // If proposer has voting power = total_power. AutoPass proposal let mut proposal = SingleChoiceProposal { title, description, @@ -210,7 +219,11 @@ pub fn execute_propose( threshold: config.threshold, total_power, msgs, - status: Status::Open, + status: if power == total_power { + Status::Passed + } else { + Status::Open + }, votes: Votes::zero(), allow_revoting: config.allow_revoting, }; @@ -235,7 +248,7 @@ pub fn execute_propose( // // `to_vec` is the method used by cosmwasm to convert a struct // into it's byte representation in storage. - let proposal_size = cosmwasm_std::to_json_vec(&proposal)?.len() as u64; + let proposal_size = cosmwasm_std::to_vec(&proposal)?.len() as u64; if proposal_size > MAX_PROPOSAL_SIZE { return Err(ContractError::ProposalTooLarge { size: proposal_size, @@ -295,7 +308,7 @@ pub fn execute_execute( if !prop.msgs.is_empty() { let execute_message = WasmMsg::Execute { contract_addr: config.dao.to_string(), - msg: to_json_binary(&dao_interface::msg::ExecuteMsg::ExecuteProposalHook { + msg: to_binary(&dao_interface::msg::ExecuteMsg::ExecuteProposalHook { msgs: prop.msgs, })?, funds: vec![], @@ -326,7 +339,7 @@ pub fn execute_execute( let hooks = match proposal_creation_policy { ProposalCreationPolicy::Anyone {} => hooks, ProposalCreationPolicy::Module { addr } => { - let msg = to_json_binary(&PreProposeHookMsg::ProposalCompletedHook { + let msg = to_binary(&PreProposeHookMsg::ProposalCompletedHook { proposal_id, new_status: prop.status, })?; @@ -515,7 +528,7 @@ pub fn execute_close( let hooks = match proposal_creation_policy { ProposalCreationPolicy::Anyone {} => hooks, ProposalCreationPolicy::Module { addr } => { - let msg = to_json_binary(&PreProposeHookMsg::ProposalCompletedHook { + let msg = to_binary(&PreProposeHookMsg::ProposalCompletedHook { proposal_id, new_status: prop.status, })?; @@ -730,29 +743,29 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { limit, } => query_reverse_proposals(deps, env, start_before, limit), QueryMsg::ProposalCreationPolicy {} => query_creation_policy(deps), - QueryMsg::ProposalHooks {} => to_json_binary(&PROPOSAL_HOOKS.query_hooks(deps)?), - QueryMsg::VoteHooks {} => to_json_binary(&VOTE_HOOKS.query_hooks(deps)?), + QueryMsg::ProposalHooks {} => to_binary(&PROPOSAL_HOOKS.query_hooks(deps)?), + QueryMsg::VoteHooks {} => to_binary(&VOTE_HOOKS.query_hooks(deps)?), } } pub fn query_config(deps: Deps) -> StdResult { let config = CONFIG.load(deps.storage)?; - to_json_binary(&config) + to_binary(&config) } pub fn query_dao(deps: Deps) -> StdResult { let config = CONFIG.load(deps.storage)?; - to_json_binary(&config.dao) + to_binary(&config.dao) } pub fn query_proposal(deps: Deps, env: Env, id: u64) -> StdResult { let proposal = PROPOSALS.load(deps.storage, id)?; - to_json_binary(&proposal.into_response(&env.block, id)) + to_binary(&proposal.into_response(&env.block, id)) } pub fn query_creation_policy(deps: Deps) -> StdResult { let policy = CREATION_POLICY.load(deps.storage)?; - to_json_binary(&policy) + to_binary(&policy) } pub fn query_list_proposals( @@ -771,7 +784,7 @@ pub fn query_list_proposals( .map(|(id, proposal)| proposal.into_response(&env.block, id)) .collect(); - to_json_binary(&ProposalListResponse { proposals: props }) + to_binary(&ProposalListResponse { proposals: props }) } pub fn query_reverse_proposals( @@ -790,16 +803,16 @@ pub fn query_reverse_proposals( .map(|(id, proposal)| proposal.into_response(&env.block, id)) .collect(); - to_json_binary(&ProposalListResponse { proposals: props }) + to_binary(&ProposalListResponse { proposals: props }) } pub fn query_proposal_count(deps: Deps) -> StdResult { let proposal_count = PROPOSAL_COUNT.load(deps.storage)?; - to_json_binary(&proposal_count) + to_binary(&proposal_count) } pub fn query_next_proposal_id(deps: Deps) -> StdResult { - to_json_binary(&next_proposal_id(deps.storage)?) + to_binary(&next_proposal_id(deps.storage)?) } pub fn query_vote(deps: Deps, proposal_id: u64, voter: String) -> StdResult { @@ -811,7 +824,7 @@ pub fn query_vote(deps: Deps, proposal_id: u64, voter: String) -> StdResult>>()?; - to_json_binary(&VoteListResponse { votes }) + to_binary(&VoteListResponse { votes }) } pub fn query_info(deps: Deps) -> StdResult { let info = cw2::get_contract_version(deps.storage)?; - to_json_binary(&dao_interface::voting::InfoResponse { info }) + to_binary(&dao_interface::voting::InfoResponse { info }) } #[cfg_attr(not(feature = "library"), entry_point)] diff --git a/contracts/proposal/dao-proposal-single/src/testing/adversarial_tests.rs b/contracts/proposal/dao-proposal-single/src/testing/adversarial_tests.rs index 19d3900e9..e6c853a24 100644 --- a/contracts/proposal/dao-proposal-single/src/testing/adversarial_tests.rs +++ b/contracts/proposal/dao-proposal-single/src/testing/adversarial_tests.rs @@ -11,7 +11,7 @@ use crate::testing::{ }, queries::{query_balance_cw20, query_dao_token, query_proposal, query_single_proposal_module}, }; -use cosmwasm_std::{to_json_binary, Addr, CosmosMsg, Decimal, Uint128, WasmMsg}; +use cosmwasm_std::{to_binary, Addr, CosmosMsg, Decimal, Uint128, WasmMsg}; use cw20::Cw20Coin; use cw_multi_test::{next_block, App}; use cw_utils::Duration; @@ -22,7 +22,7 @@ use dao_voting::{ voting::Vote, }; -use super::CREATOR_ADDR; +use super::{CREATOR_ADDR, MEMBER_ADDR}; use crate::{query::ProposalResponse, ContractError}; struct CommonTest { @@ -134,6 +134,13 @@ fn test_execute_proposal_more_than_once() { proposal_id, Vote::Yes, ); + vote_on_proposal( + &mut app, + &proposal_module, + MEMBER_ADDR, + proposal_id, + Vote::Yes, + ); app.update_block(next_block); @@ -301,7 +308,7 @@ pub fn test_passed_prop_state_remains_after_vote_swing() { recipient: "threshold".to_string(), amount: Uint128::new(100_000_000), }; - let binary_msg = to_json_binary(&msg).unwrap(); + let binary_msg = to_binary(&msg).unwrap(); mint_cw20s(&mut app, &gov_token, &core_addr, CREATOR_ADDR, 10_000_000); let proposal_id = make_proposal( diff --git a/contracts/proposal/dao-proposal-single/src/testing/instantiate.rs b/contracts/proposal/dao-proposal-single/src/testing/instantiate.rs index 3fd485f62..fc3e74237 100644 --- a/contracts/proposal/dao-proposal-single/src/testing/instantiate.rs +++ b/contracts/proposal/dao-proposal-single/src/testing/instantiate.rs @@ -1,4 +1,4 @@ -use cosmwasm_std::{to_json_binary, Addr, Coin, Decimal, Empty, Uint128}; +use cosmwasm_std::{to_binary, Addr, Coin, Decimal, Empty, Uint128}; use cw20::Cw20Coin; use cw_multi_test::{next_block, App, BankSudo, Executor, SudoMsg}; @@ -21,7 +21,7 @@ use super::{ cw4_group_contract, cw4_voting_contract, cw721_base_contract, cw721_stake_contract, cw_core_contract, native_staked_balances_voting_contract, proposal_single_contract, }, - CREATOR_ADDR, + CREATOR_ADDR, MEMBER_ADDR, }; pub(crate) fn get_pre_propose_info( @@ -34,7 +34,7 @@ pub(crate) fn get_pre_propose_info( PreProposeInfo::ModuleMayPropose { info: ModuleInstantiateInfo { code_id: pre_propose_contract, - msg: to_json_binary(&cppbps::InstantiateMsg { + msg: to_binary(&cppbps::InstantiateMsg { deposit_info, open_proposal_submission, extension: Empty::default(), @@ -146,7 +146,7 @@ pub(crate) fn instantiate_with_staked_cw721_governance( automatically_add_cw721s: false, voting_module_instantiate_info: ModuleInstantiateInfo { code_id: cw721_stake_id, - msg: to_json_binary(&dao_voting_cw721_staked::msg::InstantiateMsg { + msg: to_binary(&dao_voting_cw721_staked::msg::InstantiateMsg { unstaking_duration: None, nft_contract: dao_voting_cw721_staked::msg::NftContract::Existing { address: nft_address.to_string(), @@ -160,7 +160,7 @@ pub(crate) fn instantiate_with_staked_cw721_governance( }, proposal_modules_instantiate_info: vec![ModuleInstantiateInfo { code_id: proposal_module_code_id, - msg: to_json_binary(&proposal_module_instantiate).unwrap(), + msg: to_binary(&proposal_module_instantiate).unwrap(), admin: Some(Admin::CoreModule {}), funds: vec![], label: "DAO DAO governance module.".to_string(), @@ -208,7 +208,7 @@ pub(crate) fn instantiate_with_staked_cw721_governance( &cw721_base::msg::ExecuteMsg::SendNft::, Empty> { contract: staking_addr.to_string(), token_id: format!("{address}_{i}"), - msg: to_json_binary("").unwrap(), + msg: to_binary("").unwrap(), }, &[], ) @@ -265,7 +265,7 @@ pub(crate) fn instantiate_with_native_staked_balances_governance( automatically_add_cw721s: false, voting_module_instantiate_info: ModuleInstantiateInfo { code_id: native_stake_id, - msg: to_json_binary(&dao_voting_token_staked::msg::InstantiateMsg { + msg: to_binary(&dao_voting_token_staked::msg::InstantiateMsg { token_info: dao_voting_token_staked::msg::TokenInfo::Existing { denom: "ujuno".to_string(), }, @@ -279,7 +279,7 @@ pub(crate) fn instantiate_with_native_staked_balances_governance( }, proposal_modules_instantiate_info: vec![ModuleInstantiateInfo { code_id: proposal_module_code_id, - msg: to_json_binary(&proposal_module_instantiate).unwrap(), + msg: to_binary(&proposal_module_instantiate).unwrap(), admin: Some(Admin::CoreModule {}), funds: vec![], label: "DAO DAO governance module.".to_string(), @@ -341,10 +341,16 @@ pub(crate) fn instantiate_with_staked_balances_governance( let proposal_module_code_id = app.store_code(proposal_single_contract()); let initial_balances = initial_balances.unwrap_or_else(|| { - vec![Cw20Coin { - address: CREATOR_ADDR.to_string(), - amount: Uint128::new(100_000_000), - }] + vec![ + Cw20Coin { + address: CREATOR_ADDR.to_string(), + amount: Uint128::new(100_000_000), + }, + Cw20Coin { + address: MEMBER_ADDR.to_string(), + amount: Uint128::new(100_000_000), + }, + ] }); // Collapse balances so that we can test double votes. @@ -378,7 +384,7 @@ pub(crate) fn instantiate_with_staked_balances_governance( automatically_add_cw721s: false, voting_module_instantiate_info: ModuleInstantiateInfo { code_id: staked_balances_voting_id, - msg: to_json_binary(&dao_voting_cw20_staked::msg::InstantiateMsg { + msg: to_binary(&dao_voting_cw20_staked::msg::InstantiateMsg { active_threshold: None, token_info: dao_voting_cw20_staked::msg::TokenInfo::New { code_id: cw20_id, @@ -400,7 +406,7 @@ pub(crate) fn instantiate_with_staked_balances_governance( }, proposal_modules_instantiate_info: vec![ModuleInstantiateInfo { code_id: proposal_module_code_id, - msg: to_json_binary(&proposal_module_instantiate).unwrap(), + msg: to_binary(&proposal_module_instantiate).unwrap(), admin: Some(Admin::CoreModule {}), funds: vec![], label: "DAO DAO governance module.".to_string(), @@ -451,7 +457,7 @@ pub(crate) fn instantiate_with_staked_balances_governance( &cw20::Cw20ExecuteMsg::Send { contract: staking_contract.to_string(), amount, - msg: to_json_binary(&cw20_stake::msg::ReceiveMsg::Stake {}).unwrap(), + msg: to_binary(&cw20_stake::msg::ReceiveMsg::Stake {}).unwrap(), }, &[], ) @@ -493,7 +499,7 @@ pub(crate) fn instantiate_with_staking_active_threshold( automatically_add_cw721s: true, voting_module_instantiate_info: ModuleInstantiateInfo { code_id: votemod_id, - msg: to_json_binary(&dao_voting_cw20_staked::msg::InstantiateMsg { + msg: to_binary(&dao_voting_cw20_staked::msg::InstantiateMsg { token_info: dao_voting_cw20_staked::msg::TokenInfo::New { code_id: cw20_id, label: "DAO DAO governance token".to_string(), @@ -515,7 +521,7 @@ pub(crate) fn instantiate_with_staking_active_threshold( }, proposal_modules_instantiate_info: vec![ModuleInstantiateInfo { code_id: proposal_module_code_id, - msg: to_json_binary(&proposal_module_instantiate).unwrap(), + msg: to_binary(&proposal_module_instantiate).unwrap(), admin: Some(Admin::CoreModule {}), funds: vec![], label: "DAO DAO governance module".to_string(), @@ -543,12 +549,17 @@ pub(crate) fn instantiate_with_cw4_groups_governance( let cw4_id = app.store_code(cw4_group_contract()); let core_id = app.store_code(cw_core_contract()); let votemod_id = app.store_code(cw4_voting_contract()); - let initial_weights = initial_weights.unwrap_or_else(|| { - vec![Cw20Coin { - address: CREATOR_ADDR.to_string(), - amount: Uint128::new(1), - }] + vec![ + Cw20Coin { + address: CREATOR_ADDR.to_string(), + amount: Uint128::new(1), + }, + Cw20Coin { + address: MEMBER_ADDR.to_string(), + amount: Uint128::new(1), + }, + ] }); // Remove duplicates so that we can test duplicate voting. @@ -581,7 +592,7 @@ pub(crate) fn instantiate_with_cw4_groups_governance( automatically_add_cw721s: true, voting_module_instantiate_info: ModuleInstantiateInfo { code_id: votemod_id, - msg: to_json_binary(&dao_voting_cw4::msg::InstantiateMsg { + msg: to_binary(&dao_voting_cw4::msg::InstantiateMsg { group_contract: GroupContract::New { cw4_group_code_id: cw4_id, initial_members: initial_weights, @@ -594,7 +605,7 @@ pub(crate) fn instantiate_with_cw4_groups_governance( }, proposal_modules_instantiate_info: vec![ModuleInstantiateInfo { code_id: proposal_module_code_id, - msg: to_json_binary(&proposal_module_instantiate).unwrap(), + msg: to_binary(&proposal_module_instantiate).unwrap(), admin: Some(Admin::CoreModule {}), funds: vec![], label: "DAO DAO governance module".to_string(), diff --git a/contracts/proposal/dao-proposal-single/src/testing/mod.rs b/contracts/proposal/dao-proposal-single/src/testing/mod.rs index 12c9b9af0..6b61b91ae 100644 --- a/contracts/proposal/dao-proposal-single/src/testing/mod.rs +++ b/contracts/proposal/dao-proposal-single/src/testing/mod.rs @@ -8,3 +8,4 @@ mod queries; mod tests; pub(crate) const CREATOR_ADDR: &str = "creator"; +pub(crate) const MEMBER_ADDR: &str = "dao_member"; diff --git a/contracts/proposal/dao-proposal-single/src/testing/tests.rs b/contracts/proposal/dao-proposal-single/src/testing/tests.rs index a5cc7aa01..b0679e6da 100644 --- a/contracts/proposal/dao-proposal-single/src/testing/tests.rs +++ b/contracts/proposal/dao-proposal-single/src/testing/tests.rs @@ -1,8 +1,8 @@ use cosmwasm_std::{ coins, testing::{mock_dependencies, mock_env}, - to_json_binary, Addr, Attribute, BankMsg, Binary, ContractInfoResponse, CosmosMsg, Decimal, - Empty, Reply, StdError, SubMsgResult, Uint128, WasmMsg, WasmQuery, + to_binary, Addr, Attribute, BankMsg, Binary, ContractInfoResponse, CosmosMsg, Decimal, Empty, + Reply, StdError, SubMsgResult, Uint128, WasmMsg, WasmQuery, }; use cw2::ContractVersion; use cw20::Cw20Coin; @@ -70,7 +70,7 @@ use super::{ do_votes::do_votes_staked_balances, execute::vote_on_proposal_with_rationale, queries::{query_next_proposal_id, query_vote}, - CREATOR_ADDR, + CREATOR_ADDR, MEMBER_ADDR, }; struct CommonTest { @@ -127,7 +127,7 @@ fn test_simple_propose_staked_balances() { threshold: PercentageThreshold::Majority {}, }, allow_revoting: false, - total_power: Uint128::new(100_000_000), + total_power: Uint128::new(200_000_000), msgs: vec![], status: Status::Open, votes: Votes::zero(), @@ -176,7 +176,7 @@ fn test_simple_proposal_cw4_voting() { quorum: PercentageThreshold::Majority {}, }, allow_revoting: false, - total_power: Uint128::new(1), + total_power: Uint128::new(2), msgs: vec![], status: Status::Open, votes: Votes::zero(), @@ -280,7 +280,7 @@ fn test_instantiate_with_non_voting_module_cw20_deposit() { quorum: PercentageThreshold::Majority {}, }, allow_revoting: false, - total_power: Uint128::new(1), + total_power: Uint128::new(2), msgs: vec![], status: Status::Open, votes: Votes::zero(), @@ -321,7 +321,7 @@ fn test_proposal_message_execution() { vec![ WasmMsg::Execute { contract_addr: gov_token.to_string(), - msg: to_json_binary(&cw20::Cw20ExecuteMsg::Mint { + msg: to_binary(&cw20::Cw20ExecuteMsg::Mint { recipient: CREATOR_ADDR.to_string(), amount: Uint128::new(10_000_000), }) @@ -348,6 +348,13 @@ fn test_proposal_message_execution() { proposal_id, Vote::Yes, ); + vote_on_proposal( + &mut app, + &proposal_module, + MEMBER_ADDR, + proposal_id, + Vote::Yes, + ); let proposal = query_proposal(&app, &proposal_module, proposal_id); assert_eq!(proposal.proposal.status, Status::Passed); @@ -494,6 +501,13 @@ fn test_execute_no_non_passed_execution() { proposal_id, Vote::Yes, ); + vote_on_proposal( + &mut app, + &proposal_module, + MEMBER_ADDR, + proposal_id, + Vote::Yes, + ); execute_proposal(&mut app, &proposal_module, CREATOR_ADDR, proposal_id); // Can't execute more than once. let err = execute_proposal_should_fail(&mut app, &proposal_module, CREATOR_ADDR, proposal_id); @@ -540,7 +554,7 @@ fn test_cant_execute_not_member_when_proposal_created() { &cw20::Cw20ExecuteMsg::Send { contract: staking_contract.to_string(), amount: Uint128::new(10_000_000), - msg: to_json_binary(&cw20_stake::msg::ReceiveMsg::Stake {}).unwrap(), + msg: to_binary(&cw20_stake::msg::ReceiveMsg::Stake {}).unwrap(), }, &[], ) @@ -570,6 +584,13 @@ fn test_update_config() { proposal_id, Vote::Yes, ); + vote_on_proposal( + &mut app, + &proposal_module, + MEMBER_ADDR, + proposal_id, + Vote::Yes, + ); execute_proposal(&mut app, &proposal_module, CREATOR_ADDR, proposal_id); // Make a proposal to update the config. let proposal_id = make_proposal( @@ -578,7 +599,7 @@ fn test_update_config() { CREATOR_ADDR, vec![WasmMsg::Execute { contract_addr: proposal_module.to_string(), - msg: to_json_binary(&ExecuteMsg::UpdateConfig { + msg: to_binary(&ExecuteMsg::UpdateConfig { threshold: Threshold::AbsoluteCount { threshold: Uint128::new(10_000), }, @@ -601,6 +622,13 @@ fn test_update_config() { proposal_id, Vote::Yes, ); + vote_on_proposal( + &mut app, + &proposal_module, + MEMBER_ADDR, + proposal_id, + Vote::Yes, + ); execute_proposal(&mut app, &proposal_module, CREATOR_ADDR, proposal_id); let config = query_proposal_config(&app, &proposal_module); @@ -661,6 +689,13 @@ fn test_anyone_may_propose_and_proposal_listing() { proposal_id, Vote::Yes, ); + vote_on_proposal( + &mut app, + &proposal_module, + MEMBER_ADDR, + proposal_id, + Vote::Yes, + ); // Only members can execute still. let err = execute_proposal_should_fail(&mut app, &proposal_module, &addr, proposal_id); assert!(matches!(err, ContractError::Unauthorized {})); @@ -711,11 +746,11 @@ fn test_anyone_may_propose_and_proposal_listing() { threshold: PercentageThreshold::Majority {}, }, allow_revoting: false, - total_power: Uint128::new(100_000_000), + total_power: Uint128::new(200_000_000), msgs: vec![], status: Status::Executed, votes: Votes { - yes: Uint128::new(100_000_000), + yes: Uint128::new(200_000_000), no: Uint128::zero(), abstain: Uint128::zero() }, @@ -890,7 +925,7 @@ fn test_active_threshold_absolute() { let msg = cw20::Cw20ExecuteMsg::Send { contract: staking_contract.to_string(), amount: Uint128::new(100), - msg: to_json_binary(&cw20_stake::msg::ReceiveMsg::Stake {}).unwrap(), + msg: to_binary(&cw20_stake::msg::ReceiveMsg::Stake {}).unwrap(), }; app.execute_contract(Addr::unchecked(CREATOR_ADDR), gov_token, &msg, &[]) .unwrap(); @@ -971,7 +1006,7 @@ fn test_active_threshold_percent() { let msg = cw20::Cw20ExecuteMsg::Send { contract: staking_contract.to_string(), amount: Uint128::new(20_000_000), - msg: to_json_binary(&cw20_stake::msg::ReceiveMsg::Stake {}).unwrap(), + msg: to_binary(&cw20_stake::msg::ReceiveMsg::Stake {}).unwrap(), }; app.execute_contract(Addr::unchecked(CREATOR_ADDR), gov_token, &msg, &[]) .unwrap(); @@ -1047,6 +1082,13 @@ fn test_min_voting_period_no_early_pass() { proposal_id, Vote::Yes, ); + vote_on_proposal( + &mut app, + &proposal_module, + MEMBER_ADDR, + proposal_id, + Vote::Yes, + ); let proposal_response = query_proposal(&app, &proposal_module, proposal_id); assert_eq!(proposal_response.proposal.status, Status::Open); @@ -1218,6 +1260,13 @@ fn test_allow_revoting_config_changes() { no_revoting_proposal, Vote::Yes, ); + vote_on_proposal( + &mut app, + &proposal_module, + MEMBER_ADDR, + no_revoting_proposal, + Vote::Yes, + ); // Proposal without revoting should have passed. let proposal_resp = query_proposal(&app, &proposal_module, no_revoting_proposal); @@ -1545,7 +1594,7 @@ fn test_migrate_from_compatible() { CosmosMsg::Wasm(WasmMsg::Migrate { contract_addr: proposal_module.to_string(), new_code_id, - msg: to_json_binary(&MigrateMsg::FromCompatible {}).unwrap(), + msg: to_binary(&MigrateMsg::FromCompatible {}).unwrap(), }), ) .unwrap(); @@ -1609,7 +1658,7 @@ fn test_migrate_from_v1() { automatically_add_cw721s: false, voting_module_instantiate_info: ModuleInstantiateInfo { code_id: staked_balances_voting_id, - msg: to_json_binary(&dao_voting_cw20_staked::msg::InstantiateMsg { + msg: to_binary(&dao_voting_cw20_staked::msg::InstantiateMsg { active_threshold: None, token_info: dao_voting_cw20_staked::msg::TokenInfo::New { code_id: cw20_id, @@ -1631,7 +1680,7 @@ fn test_migrate_from_v1() { }, proposal_modules_instantiate_info: vec![ModuleInstantiateInfo { code_id: v1_proposal_single_code, - msg: to_json_binary(&instantiate).unwrap(), + msg: to_binary(&instantiate).unwrap(), admin: Some(Admin::CoreModule {}), funds: vec![], label: "DAO DAO governance module.".to_string(), @@ -1682,7 +1731,7 @@ fn test_migrate_from_v1() { &cw20::Cw20ExecuteMsg::Send { contract: staking_contract.to_string(), amount, - msg: to_json_binary(&cw20_stake::msg::ReceiveMsg::Stake {}).unwrap(), + msg: to_binary(&cw20_stake::msg::ReceiveMsg::Stake {}).unwrap(), }, &[], ) @@ -1730,7 +1779,7 @@ fn test_migrate_from_v1() { pre_propose_info: PreProposeInfo::ModuleMayPropose { info: ModuleInstantiateInfo { code_id: pre_propose_single, - msg: to_json_binary(&dao_pre_propose_single::InstantiateMsg { + msg: to_binary(&dao_pre_propose_single::InstantiateMsg { deposit_info: Some(UncheckedDepositInfo { denom: dao_voting::deposit::DepositToken::VotingModuleToken {}, amount: Uint128::new(1), @@ -1752,7 +1801,7 @@ fn test_migrate_from_v1() { CosmosMsg::Wasm(WasmMsg::Migrate { contract_addr: proposal_module.to_string(), new_code_id: v2_proposal_single, - msg: to_json_binary(&migrate_msg).unwrap(), + msg: to_binary(&migrate_msg).unwrap(), }), ) .unwrap_err() @@ -1770,7 +1819,7 @@ fn test_migrate_from_v1() { CosmosMsg::Wasm(WasmMsg::Migrate { contract_addr: proposal_module.to_string(), new_code_id: v2_proposal_single, - msg: to_json_binary(&migrate_msg).unwrap(), + msg: to_binary(&migrate_msg).unwrap(), }), ) .unwrap(); @@ -1798,7 +1847,7 @@ fn test_migrate_from_v1() { CosmosMsg::Wasm(WasmMsg::Migrate { contract_addr: proposal_module.to_string(), new_code_id: v2_proposal_single, - msg: to_json_binary(&migrate_msg).unwrap(), + msg: to_binary(&migrate_msg).unwrap(), }), ) .unwrap_err() @@ -1882,6 +1931,13 @@ fn test_execution_failed() { proposal_id, Vote::Yes, ); + vote_on_proposal( + &mut app, + &proposal_module, + MEMBER_ADDR, + proposal_id, + Vote::Yes, + ); execute_proposal(&mut app, &proposal_module, CREATOR_ADDR, proposal_id); let proposal = query_proposal(&app, &proposal_module, proposal_id); @@ -1933,6 +1989,13 @@ fn test_execution_failed() { proposal_id, Vote::Yes, ); + vote_on_proposal( + &mut app, + &proposal_module, + MEMBER_ADDR, + proposal_id, + Vote::Yes, + ); let err: StdError = app .execute_contract( Addr::unchecked(CREATOR_ADDR), @@ -2397,11 +2460,11 @@ fn test_update_pre_propose_module() { CREATOR_ADDR, vec![WasmMsg::Execute { contract_addr: proposal_module.to_string(), - msg: to_json_binary(&ExecuteMsg::UpdatePreProposeInfo { + msg: to_binary(&ExecuteMsg::UpdatePreProposeInfo { info: PreProposeInfo::ModuleMayPropose { info: ModuleInstantiateInfo { code_id: pre_propose_id, - msg: to_json_binary(&dao_pre_propose_single::InstantiateMsg { + msg: to_binary(&dao_pre_propose_single::InstantiateMsg { deposit_info: Some(UncheckedDepositInfo { denom: dao_voting::deposit::DepositToken::VotingModuleToken {}, amount: Uint128::new(1), @@ -2430,6 +2493,13 @@ fn test_update_pre_propose_module() { proposal_id, Vote::Yes, ); + vote_on_proposal( + &mut app, + &proposal_module, + MEMBER_ADDR, + proposal_id, + Vote::Yes, + ); execute_proposal(&mut app, &proposal_module, CREATOR_ADDR, proposal_id); // Check that a new creation policy has been birthed. @@ -2477,6 +2547,13 @@ fn test_update_pre_propose_module() { pre_update_proposal_id, Vote::Yes, ); + vote_on_proposal( + &mut app, + &proposal_module, + MEMBER_ADDR, + pre_update_proposal_id, + Vote::Abstain, + ); execute_proposal( &mut app, &proposal_module, @@ -2495,8 +2572,7 @@ fn test_update_pre_propose_module() { CREATOR_ADDR, vec![WasmMsg::Execute { contract_addr: pre_propose_start.into_string(), - msg: to_json_binary(&dao_pre_propose_single::ExecuteMsg::Withdraw { denom: None }) - .unwrap(), + msg: to_binary(&dao_pre_propose_single::ExecuteMsg::Withdraw { denom: None }).unwrap(), funds: vec![], } .into()], @@ -2508,6 +2584,13 @@ fn test_update_pre_propose_module() { proposal_id, Vote::Yes, ); + vote_on_proposal( + &mut app, + &proposal_module, + MEMBER_ADDR, + proposal_id, + Vote::Yes, + ); execute_proposal(&mut app, &proposal_module, CREATOR_ADDR, proposal_id); // Make sure the left over deposit was returned to the DAO. diff --git a/contracts/test/dao-proposal-hook-counter/src/tests.rs b/contracts/test/dao-proposal-hook-counter/src/tests.rs index fddc0f104..9608150aa 100644 --- a/contracts/test/dao-proposal-hook-counter/src/tests.rs +++ b/contracts/test/dao-proposal-hook-counter/src/tests.rs @@ -1,4 +1,4 @@ -use cosmwasm_std::{to_json_binary, Addr, Empty, Uint128}; +use cosmwasm_std::{to_binary, Addr, Empty, Uint128}; use cw20::Cw20Coin; use cw_hooks::HooksResponse; use cw_multi_test::{App, Contract, ContractWrapper, Executor}; @@ -16,6 +16,7 @@ use dao_proposal_single::state::Config; use dao_voting::proposal::SingleChoiceProposeMsg as ProposeMsg; const CREATOR_ADDR: &str = "creator"; +const MEMBER_ADDR: &str = "dao_member"; fn cw20_contract() -> Box> { let contract = ContractWrapper::new( @@ -92,10 +93,16 @@ fn instantiate_with_default_governance( let votemod_id = app.store_code(cw20_balances_voting()); let initial_balances = initial_balances.unwrap_or_else(|| { - vec![Cw20Coin { - address: CREATOR_ADDR.to_string(), - amount: Uint128::new(100_000_000), - }] + vec![ + Cw20Coin { + address: CREATOR_ADDR.to_string(), + amount: Uint128::new(100_000_000), + }, + Cw20Coin { + address: MEMBER_ADDR.to_string(), + amount: Uint128::new(100_000_000), + }, + ] }); let governance_instantiate = dao_interface::msg::InstantiateMsg { @@ -108,7 +115,7 @@ fn instantiate_with_default_governance( automatically_add_cw721s: true, voting_module_instantiate_info: ModuleInstantiateInfo { code_id: votemod_id, - msg: to_json_binary(&dao_voting_cw20_balance::msg::InstantiateMsg { + msg: to_binary(&dao_voting_cw20_balance::msg::InstantiateMsg { token_info: dao_voting_cw20_balance::msg::TokenInfo::New { code_id: cw20_id, label: "DAO DAO governance token".to_string(), @@ -126,7 +133,7 @@ fn instantiate_with_default_governance( }, proposal_modules_instantiate_info: vec![ModuleInstantiateInfo { code_id, - msg: to_json_binary(&msg).unwrap(), + msg: to_binary(&msg).unwrap(), admin: Some(Admin::CoreModule {}), funds: vec![], label: "DAO DAO governance module".to_string(), @@ -250,6 +257,8 @@ fn test_counters() { .unwrap(); assert_eq!(resp.count, 0); + // Add a member to the DAO + // Create a new proposal. app.execute_contract( Addr::unchecked(CREATOR_ADDR), @@ -296,6 +305,17 @@ fn test_counters() { }, &[], ) + .unwrap(); // Vote + app.execute_contract( + Addr::unchecked(MEMBER_ADDR.to_string()), + govmod_single.clone(), + &dao_proposal_single::msg::ExecuteMsg::Vote { + proposal_id: 1, + vote: Vote::Yes, + rationale: None, + }, + &[], + ) .unwrap(); // Query vote counter, expect 1 @@ -303,7 +323,7 @@ fn test_counters() { .wrap() .query_wasm_smart(counters.clone(), &QueryMsg::VoteCounter {}) .unwrap(); - assert_eq!(resp.count, 1); + assert_eq!(resp.count, 2); // Query status changed counter, expect 1 let resp: CountResponse = app @@ -419,18 +439,18 @@ fn test_counters() { .unwrap(); // The success counters should still work - // Query vote counter, expect 2 + // Query vote counter, expect 3 let resp: CountResponse = app .wrap() .query_wasm_smart(counters.clone(), &QueryMsg::VoteCounter {}) .unwrap(); - assert_eq!(resp.count, 2); - // Query status changed counter, expect 2 + assert_eq!(resp.count, 3); + // Query status changed counter, expect 1 let resp: CountResponse = app .wrap() .query_wasm_smart(counters, &QueryMsg::StatusChangedCounter {}) .unwrap(); - assert_eq!(resp.count, 2); + assert_eq!(resp.count, 1); // The contract should of removed the failing counters let hooks: HooksResponse = app diff --git a/packages/dao-testing/src/tests.rs b/packages/dao-testing/src/tests.rs index d57377333..26ae095d5 100644 --- a/packages/dao-testing/src/tests.rs +++ b/packages/dao-testing/src/tests.rs @@ -45,14 +45,22 @@ where ); do_votes( - vec![TestSingleChoiceVote { - voter: "ekez".to_string(), - position: Vote::No, - weight: Uint128::new(10), - should_execute: ShouldExecute::Yes, - }], + vec![ + TestSingleChoiceVote { + voter: "zeke".to_string(), + position: Vote::Yes, + weight: Uint128::new(1), + should_execute: ShouldExecute::Yes, + }, + TestSingleChoiceVote { + voter: "123".to_string(), + position: Vote::No, + weight: Uint128::new(10), + should_execute: ShouldExecute::Yes, + }, + ], Threshold::AbsolutePercentage { - percentage: PercentageThreshold::Percent(Decimal::percent(100)), + percentage: PercentageThreshold::Percent(Decimal::percent(90)), }, Status::Rejected, None, @@ -124,12 +132,20 @@ where F: Fn(Vec, Threshold, Status, Option), { do_votes( - vec![TestSingleChoiceVote { - voter: "zeke".to_string(), - position: Vote::No, - weight: Uint128::new(1), - should_execute: ShouldExecute::Yes, - }], + vec![ + TestSingleChoiceVote { + voter: "zeke".to_string(), + position: Vote::No, + weight: Uint128::new(1), + should_execute: ShouldExecute::Yes, + }, + TestSingleChoiceVote { + voter: "keze".to_string(), + position: Vote::No, + weight: Uint128::new(1), + should_execute: ShouldExecute::Yes, + }, + ], Threshold::AbsolutePercentage { percentage: PercentageThreshold::Percent(Decimal::percent(100)), }, @@ -157,12 +173,20 @@ where F: Fn(Vec, Threshold, Status, Option), { do_votes( - vec![TestSingleChoiceVote { - voter: "ekez".to_string(), - position: Vote::Abstain, - weight: Uint128::new(u64::max_value().into()), - should_execute: ShouldExecute::Yes, - }], + vec![ + TestSingleChoiceVote { + voter: "ekez".to_string(), + position: Vote::Abstain, + weight: Uint128::new(10), + should_execute: ShouldExecute::Yes, + }, + TestSingleChoiceVote { + voter: "n1n0".to_string(), + position: Vote::Abstain, + weight: Uint128::new(10), + should_execute: ShouldExecute::Yes, + }, + ], Threshold::AbsolutePercentage { percentage: PercentageThreshold::Percent(Decimal::percent(100)), }, @@ -174,12 +198,20 @@ where // rejected. for i in 0..101 { do_votes( - vec![TestSingleChoiceVote { - voter: "ekez".to_string(), - position: Vote::Abstain, - weight: Uint128::new(u64::max_value().into()), - should_execute: ShouldExecute::Yes, - }], + vec![ + TestSingleChoiceVote { + voter: "ekez".to_string(), + position: Vote::Abstain, + weight: Uint128::new(10), + should_execute: ShouldExecute::Yes, + }, + TestSingleChoiceVote { + voter: "tere".to_string(), + position: Vote::Abstain, + weight: Uint128::new(10), + should_execute: ShouldExecute::Yes, + }, + ], Threshold::ThresholdQuorum { threshold: PercentageThreshold::Percent(Decimal::percent(100)), quorum: PercentageThreshold::Percent(Decimal::percent(i)), @@ -199,12 +231,20 @@ where // and 1 total vote. This should round up and only pass if there // are more than 1 yes votes. do_votes( - vec![TestSingleChoiceVote { - voter: "ekez".to_string(), - position: Vote::Yes, - weight: Uint128::new(1), - should_execute: ShouldExecute::Yes, - }], + vec![ + TestSingleChoiceVote { + voter: "ekez".to_string(), + position: Vote::Yes, + weight: Uint128::new(1), + should_execute: ShouldExecute::Yes, + }, + TestSingleChoiceVote { + voter: "prop1".to_string(), + position: Vote::Yes, + weight: Uint128::new(1), + should_execute: ShouldExecute::Yes, + }, + ], Threshold::AbsolutePercentage { percentage: PercentageThreshold::Percent(Decimal::percent(1)), }, @@ -213,12 +253,20 @@ where ); do_votes( - vec![TestSingleChoiceVote { - voter: "ekez".to_string(), - position: Vote::Yes, - weight: Uint128::new(10), - should_execute: ShouldExecute::Yes, - }], + vec![ + TestSingleChoiceVote { + voter: "ekez".to_string(), + position: Vote::Yes, + weight: Uint128::new(10), + should_execute: ShouldExecute::Yes, + }, + TestSingleChoiceVote { + voter: "prop2".to_string(), + position: Vote::Yes, + weight: Uint128::new(10), + should_execute: ShouldExecute::Yes, + }, + ], Threshold::AbsolutePercentage { percentage: PercentageThreshold::Percent(Decimal::percent(1)), }, @@ -227,6 +275,7 @@ where ); // HIGH PERCISION + // This is directly passing as there is only 1 member in the DAO do_votes( vec![TestSingleChoiceVote { voter: "ekez".to_string(), @@ -242,12 +291,20 @@ where ); do_votes( - vec![TestSingleChoiceVote { - voter: "ekez".to_string(), - position: Vote::Abstain, - weight: Uint128::new(1), - should_execute: ShouldExecute::Yes, - }], + vec![ + TestSingleChoiceVote { + voter: "ekez".to_string(), + position: Vote::Abstain, + weight: Uint128::new(1), + should_execute: ShouldExecute::Yes, + }, + TestSingleChoiceVote { + voter: "teze".to_string(), + position: Vote::Abstain, + weight: Uint128::new(1), + should_execute: ShouldExecute::Yes, + }, + ], Threshold::AbsolutePercentage { percentage: PercentageThreshold::Percent(Decimal::percent(1)), },