From 86c85f54c8b3c9d831ae77bb892ef68137770e9d Mon Sep 17 00:00:00 2001 From: Gabriel Lopez Date: Thu, 8 Feb 2024 12:54:22 -0600 Subject: [PATCH] Work on voting tests I think there's a bug in the removal of multiple hooks at the same time --- Cargo.lock | 3 + Cargo.toml | 1 + .../dao-proposal-incentives/README.md | 8 +- .../dao-proposal-incentives/src/tests.rs | 59 +-- .../external/dao-voting-incentives/Cargo.toml | 3 + .../dao-voting-incentives/src/contract.rs | 7 + .../external/dao-voting-incentives/src/lib.rs | 3 + .../external/dao-voting-incentives/src/msg.rs | 3 + .../dao-voting-incentives/src/query.rs | 12 +- .../dao-voting-incentives/src/tests.rs | 470 ++++++++++++++++++ packages/dao-testing/Cargo.toml | 3 +- packages/dao-testing/src/contracts.rs | 14 +- 12 files changed, 541 insertions(+), 45 deletions(-) create mode 100644 contracts/external/dao-voting-incentives/src/tests.rs diff --git a/Cargo.lock b/Cargo.lock index efef129ec..2e6dfc42d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2012,6 +2012,7 @@ dependencies = [ "dao-voting-cw4", "dao-voting-cw721-roles", "dao-voting-cw721-staked", + "dao-voting-incentives", "dao-voting-token-staked", "osmosis-std", "osmosis-test-tube", @@ -2173,6 +2174,8 @@ dependencies = [ "dao-dao-core", "dao-hooks", "dao-interface", + "dao-proposal-single", + "dao-testing", "dao-voting 2.4.0", "thiserror", ] diff --git a/Cargo.toml b/Cargo.toml index 634c38d94..bd8e44284 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -117,6 +117,7 @@ dao-voting-cw20-staked = { path = "./contracts/voting/dao-voting-cw20-staked", v dao-voting-cw4 = { path = "./contracts/voting/dao-voting-cw4", version = "2.4.0" } dao-voting-cw721-roles = { path = "./contracts/voting/dao-voting-cw721-roles", version = "2.4.0" } dao-voting-cw721-staked = { path = "./contracts/voting/dao-voting-cw721-staked", version = "2.4.0" } +dao-voting-incentives = { path = "./contracts/external/dao-voting-incentives", version = "2.4.0" } dao-voting-token-staked = { path = "./contracts/voting/dao-voting-token-staked", version = "2.4.0" } # v1 dependencies. used for state migrations. diff --git a/contracts/external/dao-proposal-incentives/README.md b/contracts/external/dao-proposal-incentives/README.md index f63d7b00a..45bd9e0db 100644 --- a/contracts/external/dao-proposal-incentives/README.md +++ b/contracts/external/dao-proposal-incentives/README.md @@ -12,11 +12,13 @@ To instantiate the contract, provide the following parameters: - `owner`: The DAO sending this contract proposal hooks. - `proposal_incentives`: Configuration for the incentives to be awarded for successful proposals. This should be specified using the `ProposalIncentivesUnchecked` structure. -## Setup +## Configuration - This contract should be added as a `ProposalHook` to either the `dao-voting-single` or `dao-voting-multiple` proposal modules. - The DAO must be set as the `owner` of this contract to manage incentives and ownership. +The incentives can be adjusted at any time by the owner of the contract. The rewards are determined based on the configuration at the proposal's `start_time`. This allows for dynamic adjustment of incentives to reflect the DAO's evolving priorities and resources. + ## Execute - **ProposalHook(ProposalHookMsg)**: Triggered when a proposal's status changes. This is used to evaluate and potentially reward successful proposals. @@ -27,7 +29,3 @@ To instantiate the contract, provide the following parameters: ## Query - **ProposalIncentives { height: Option }**: Returns the current configuration of the proposal incentives. The `height` parameter is optional and can be used to query the incentives at a specific blockchain height, providing a snapshot of the incentives at that point in time. - -## Configuration - -The incentives can be adjusted at any time by the owner of the contract. The rewards are determined based on the configuration at the proposal's `start_time`. This allows for dynamic adjustment of incentives to reflect the DAO's evolving priorities and resources. \ No newline at end of file diff --git a/contracts/external/dao-proposal-incentives/src/tests.rs b/contracts/external/dao-proposal-incentives/src/tests.rs index 6afc2dedf..2adca6808 100644 --- a/contracts/external/dao-proposal-incentives/src/tests.rs +++ b/contracts/external/dao-proposal-incentives/src/tests.rs @@ -2,17 +2,15 @@ use std::vec; use cosmwasm_std::{ testing::{mock_dependencies, mock_env}, - to_json_binary, Addr, Binary, Coin, CosmosMsg, Empty, Uint128, WasmMsg, + to_json_binary, Addr, Binary, Coin, CosmosMsg, Uint128, WasmMsg, }; use cw20::Cw20Coin; use cw_denom::{CheckedDenom, UncheckedDenom}; -use cw_multi_test::{ - error::AnyResult, App, AppBuilder, AppResponse, Contract, ContractWrapper, Executor, -}; +use cw_multi_test::{error::AnyResult, App, AppBuilder, AppResponse, Executor}; use cw_ownable::Ownership; use dao_testing::{ - contracts::{dao_proposal_incentives_contract, proposal_single_contract}, + contracts::{cw20_base_contract, dao_proposal_incentives_contract, proposal_single_contract}, helpers::instantiate_with_cw4_groups_governance, }; use dao_voting::{proposal::SingleChoiceProposeMsg, threshold::Threshold}; @@ -35,15 +33,6 @@ struct Context { dao_proposal_incentives_code_id: u64, } -fn cw20_base_contract() -> Box> { - let contract = ContractWrapper::new( - cw20_base::contract::execute, - cw20_base::contract::instantiate, - cw20_base::contract::query, - ); - Box::new(contract) -} - fn get_context() -> Context { // Set up app with native balances let mut app = AppBuilder::default().build(|router, _, storage| { @@ -352,28 +341,26 @@ pub fn test_hook() { assert!(result.is_ok()); // Propose adding a hook - context - .app - .execute_contract( - Addr::unchecked(ADDR1), - context.proposal_single_addr.clone(), - &dao_proposal_single::msg::ExecuteMsg::Propose(SingleChoiceProposeMsg { - title: "Add proposal hook".to_string(), - description: "Adding a proposal hook to test the dao_proposal_incentives contract" - .to_string(), - msgs: vec![CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: context.proposal_single_addr.to_string(), - msg: to_json_binary(&dao_proposal_single::msg::ExecuteMsg::AddProposalHook { - address: dao_proposal_incentives_addr.to_string(), - }) - .unwrap(), - funds: vec![], - })], - proposer: None, - }), - &[], - ) - .unwrap(); + let result = context.app.execute_contract( + Addr::unchecked(ADDR1), + context.proposal_single_addr.clone(), + &dao_proposal_single::msg::ExecuteMsg::Propose(SingleChoiceProposeMsg { + title: "Add proposal hook".to_string(), + description: "Adding a proposal hook to test the dao_proposal_incentives contract" + .to_string(), + msgs: vec![CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: context.proposal_single_addr.to_string(), + msg: to_json_binary(&dao_proposal_single::msg::ExecuteMsg::AddProposalHook { + address: dao_proposal_incentives_addr.to_string(), + }) + .unwrap(), + funds: vec![], + })], + proposer: None, + }), + &[], + ); + assert!(result.is_ok()); // Vote and execute the proposal to add the proposal hook vote_yes_on_proposal(&mut context, 1u64).unwrap(); diff --git a/contracts/external/dao-voting-incentives/Cargo.toml b/contracts/external/dao-voting-incentives/Cargo.toml index fbeccc8c0..be6d63c19 100644 --- a/contracts/external/dao-voting-incentives/Cargo.toml +++ b/contracts/external/dao-voting-incentives/Cargo.toml @@ -36,3 +36,6 @@ cw-multi-test = { workspace = true } dao-dao-core = { workspace = true, features = ["library"] } cw20-base = { workspace = true, features = ["library"] } cw-hooks = { workspace = true } +dao-testing = { workspace = true } +dao-hooks = { workspace = true } +dao-proposal-single = { workspace = true, features = ["library"] } \ No newline at end of file diff --git a/contracts/external/dao-voting-incentives/src/contract.rs b/contracts/external/dao-voting-incentives/src/contract.rs index 32ca6e86b..75d5b6050 100644 --- a/contracts/external/dao-voting-incentives/src/contract.rs +++ b/contracts/external/dao-voting-incentives/src/contract.rs @@ -5,6 +5,7 @@ use cosmwasm_std::{ }; use cw2::set_contract_version; use cw_ownable::get_ownership; +use cw_utils::Expiration; use crate::error::ContractError; use crate::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg}; @@ -33,6 +34,11 @@ pub fn instantiate( if msg.expiration.is_expired(&env.block) { return Err(ContractError::AlreadyExpired {}); } + if let Expiration::Never {} = msg.expiration { + return Err(ContractError::NotExpired { + expiration: Expiration::Never {}, + }); + } // Save voting incentives config CONFIG.save( @@ -81,6 +87,7 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { } QueryMsg::Config {} => to_json_binary(&query::config(deps)?), QueryMsg::Ownership {} => to_json_binary(&get_ownership(deps.storage)?), + QueryMsg::Votes { address } => to_json_binary(&query::votes(deps, address)?), } } diff --git a/contracts/external/dao-voting-incentives/src/lib.rs b/contracts/external/dao-voting-incentives/src/lib.rs index d2f25785c..bed930ba1 100644 --- a/contracts/external/dao-voting-incentives/src/lib.rs +++ b/contracts/external/dao-voting-incentives/src/lib.rs @@ -7,4 +7,7 @@ pub mod msg; pub mod query; pub mod state; +#[cfg(test)] +mod tests; + pub use crate::error::ContractError; diff --git a/contracts/external/dao-voting-incentives/src/msg.rs b/contracts/external/dao-voting-incentives/src/msg.rs index 1937ab74b..4eb0b7af4 100644 --- a/contracts/external/dao-voting-incentives/src/msg.rs +++ b/contracts/external/dao-voting-incentives/src/msg.rs @@ -42,6 +42,9 @@ pub enum QueryMsg { /// Returns the expected rewards for the given address #[returns(RewardResponse)] ExpectedRewards { address: String }, + /// Returns the votes count for the given address + #[returns(Uint128)] + Votes { address: String }, } #[cw_serde] diff --git a/contracts/external/dao-voting-incentives/src/query.rs b/contracts/external/dao-voting-incentives/src/query.rs index 96a6877ea..dcee2772f 100644 --- a/contracts/external/dao-voting-incentives/src/query.rs +++ b/contracts/external/dao-voting-incentives/src/query.rs @@ -1,8 +1,8 @@ -use cosmwasm_std::{Deps, Env, StdError, StdResult}; +use cosmwasm_std::{Deps, Env, StdError, StdResult, Uint128}; use crate::{ msg::RewardResponse, - state::{self, Config, CONFIG}, + state::{self, Config, CONFIG, USER_VOTE_COUNT}, }; pub fn rewards(deps: Deps, address: String) -> StdResult { @@ -21,3 +21,11 @@ pub fn expected_rewards(deps: Deps, env: Env, address: String) -> StdResult StdResult { CONFIG.load(deps.storage) } + +pub fn votes(deps: Deps, address: String) -> StdResult { + let address = deps.api.addr_validate(&address)?; + + Ok(USER_VOTE_COUNT + .may_load(deps.storage, &address)? + .unwrap_or_default()) +} diff --git a/contracts/external/dao-voting-incentives/src/tests.rs b/contracts/external/dao-voting-incentives/src/tests.rs new file mode 100644 index 000000000..de63a8419 --- /dev/null +++ b/contracts/external/dao-voting-incentives/src/tests.rs @@ -0,0 +1,470 @@ +use std::vec; + +use cosmwasm_std::{ + testing::{mock_dependencies, mock_env}, + to_json_binary, Addr, Binary, Coin, CosmosMsg, Uint128, WasmMsg, +}; + +use cw20::{Cw20Coin, Cw20ReceiveMsg}; +use cw_denom::UncheckedDenom; +use cw_multi_test::{error::AnyResult, App, AppBuilder, AppResponse, Executor}; +use cw_utils::Expiration; +use dao_testing::{ + contracts::{cw20_base_contract, dao_voting_incentives_contract, proposal_single_contract}, + helpers::instantiate_with_cw4_groups_governance, +}; +use dao_voting::{proposal::SingleChoiceProposeMsg, threshold::Threshold}; + +use crate::{ + contract::{migrate, CONTRACT_NAME, CONTRACT_VERSION}, + msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg}, + state::Config, +}; + +const ADMIN: &str = "admin"; +const ADDR1: &str = "addr1"; +const ADDR2: &str = "addr2"; +const DENOM: &str = "juno"; + +struct Context { + app: App, + cw20_addr: Addr, + proposal_single_addr: Addr, + dao_addr: Addr, + dao_voting_incentives_code_id: u64, +} + +fn get_context() -> Context { + // Set up app with native balances + let mut app = AppBuilder::default().build(|router, _, storage| { + router + .bank + .init_balance( + storage, + &Addr::unchecked(ADMIN), + vec![Coin { + denom: DENOM.to_string(), + amount: Uint128::new(1000), + }], + ) + .unwrap(); + }); + + // Set up cw20 with balances + let cw20_code_id = app.store_code(cw20_base_contract()); + let cw20_addr = app + .instantiate_contract( + cw20_code_id, + Addr::unchecked(ADMIN), + &cw20_base::msg::InstantiateMsg { + name: "cw20 token".to_string(), + symbol: "cwtoken".to_string(), + decimals: 6, + initial_balances: vec![Cw20Coin { + address: ADMIN.to_string(), + amount: Uint128::new(1000), + }], + mint: None, + marketing: None, + }, + &[], + "cw20-base", + None, + ) + .unwrap(); + + // Set up dao + let proposal_single_code_id = app.store_code(proposal_single_contract()); + let dao_addr = instantiate_with_cw4_groups_governance( + &mut app, + proposal_single_code_id, + to_json_binary(&dao_proposal_single::msg::InstantiateMsg { + threshold: Threshold::AbsolutePercentage { + percentage: dao_voting::threshold::PercentageThreshold::Majority {}, + }, + max_voting_period: cw_utils::Duration::Height(10u64), + min_voting_period: None, + only_members_execute: false, + allow_revoting: false, + pre_propose_info: dao_voting::pre_propose::PreProposeInfo::AnyoneMayPropose {}, + close_proposal_on_execution_failure: true, + veto: None, + }) + .unwrap(), + Some(vec![ + Cw20Coin { + address: ADDR1.to_string(), + amount: Uint128::one(), + }, + Cw20Coin { + address: ADDR2.to_string(), + amount: Uint128::one(), + }, + ]), + ); + + // Get proposal single addr + let proposal_modules: Vec = app + .wrap() + .query_wasm_smart( + dao_addr.clone(), + &dao_interface::msg::QueryMsg::ProposalModules { + start_after: None, + limit: Some(1u32), + }, + ) + .unwrap(); + assert!(!proposal_modules.is_empty()); + let proposal_single_addr = proposal_modules.first().unwrap().address.clone(); + + // Set up dao voting incentives code id + let dao_voting_incentives_code_id = app.store_code(dao_voting_incentives_contract()); + + Context { + app, + cw20_addr, + dao_addr, + dao_voting_incentives_code_id, + proposal_single_addr, + } +} + +fn vote_yes_on_proposal(context: &mut Context, proposal_id: u64) -> Vec> { + vec![ + context.app.execute_contract( + Addr::unchecked(ADDR1), + context.proposal_single_addr.clone(), + &dao_proposal_single::msg::ExecuteMsg::Vote { + proposal_id, + vote: dao_voting::voting::Vote::Yes, + rationale: None, + }, + &[], + ), + context.app.execute_contract( + Addr::unchecked(ADDR2), + context.proposal_single_addr.clone(), + &dao_proposal_single::msg::ExecuteMsg::Vote { + proposal_id, + vote: dao_voting::voting::Vote::Yes, + rationale: None, + }, + &[], + ), + ] +} + +fn execute_proposal(context: &mut Context, proposal_id: u64) { + context + .app + .execute_contract( + Addr::unchecked(ADDR1), + context.proposal_single_addr.clone(), + &dao_proposal_single::msg::ExecuteMsg::Execute { proposal_id }, + &[], + ) + .unwrap(); +} + +#[test] +pub fn test_instantiate_validation() { + let mut context = get_context(); + + // Check already expired is error + let result = context.app.instantiate_contract( + context.dao_voting_incentives_code_id, + Addr::unchecked(ADMIN), + &InstantiateMsg { + owner: context.dao_addr.to_string(), + denom: UncheckedDenom::Native(DENOM.to_string()), + expiration: Expiration::AtHeight(1u64), + }, + &[], + "dao_voting_incentives".to_string(), + None, + ); + assert!(result.is_err()); + + // Check expiration never is error + let result = context.app.instantiate_contract( + context.dao_voting_incentives_code_id, + Addr::unchecked(ADMIN), + &InstantiateMsg { + owner: context.dao_addr.to_string(), + denom: UncheckedDenom::Native(DENOM.to_string()), + expiration: Expiration::Never {}, + }, + &[], + "dao_voting_incentives".to_string(), + None, + ); + assert!(result.is_err()); +} + +#[test] +pub fn test_hooks() { + let mut context = get_context(); + + // Create the voting incentives contract for native + // The expiration is 10 blocks from start (12345 height) + let dao_voting_incentives_addr = context + .app + .instantiate_contract( + context.dao_voting_incentives_code_id, + Addr::unchecked(ADMIN), + &InstantiateMsg { + owner: context.dao_addr.to_string(), + denom: UncheckedDenom::Native(DENOM.to_string()), + expiration: Expiration::AtHeight(12355u64), + }, + &[], + "dao_voting_incentives".to_string(), + None, + ) + .unwrap(); + + // Also create a parallel voting incentives contract for cw20 + let dao_voting_incentives_cw20_addr = context + .app + .instantiate_contract( + context.dao_voting_incentives_code_id, + Addr::unchecked(ADMIN), + &InstantiateMsg { + owner: context.dao_addr.to_string(), + denom: UncheckedDenom::Cw20(context.cw20_addr.to_string()), + expiration: Expiration::AtHeight(12355u64), + }, + &[], + "dao_voting_incentives_cw20".to_string(), + None, + ) + .unwrap(); + + context.app.update_block(|x| x.height += 1); + + // Execute fails - unauthorized + let result = context.app.execute_contract( + Addr::unchecked(ADMIN), + dao_voting_incentives_addr.clone(), + &ExecuteMsg::VoteHook(dao_hooks::vote::VoteHookMsg::NewVote { + proposal_id: 1u64, + voter: ADMIN.to_string(), + vote: "a fake vote".to_string(), + }), + &[], + ); + assert!(result.is_err()); + + // Fund the incentives contracts for 1000 + context + .app + .send_tokens( + Addr::unchecked(ADMIN), + dao_voting_incentives_addr.clone(), + &[Coin { + denom: DENOM.to_string(), + amount: Uint128::new(1000), + }], + ) + .unwrap(); + let result = context.app.execute_contract( + Addr::unchecked(ADMIN), + context.cw20_addr.clone(), + &cw20::Cw20ExecuteMsg::Send { + contract: dao_voting_incentives_cw20_addr.to_string(), + amount: Uint128::new(1000), + msg: Binary::default(), + }, + &[], + ); + assert!(result.is_ok()); + + // Assert the cw20 voting incentives do not accept a random cw20 token + let result = context.app.execute_contract( + Addr::unchecked(ADMIN), + dao_voting_incentives_cw20_addr.clone(), + &ExecuteMsg::Receive(Cw20ReceiveMsg { + sender: ADMIN.to_string(), + amount: Uint128::new(1000), + msg: Binary::default(), + }), + &[], + ); + assert!(result.is_err()); + + // Propose adding both hooks + let result = context.app.execute_contract( + Addr::unchecked(ADDR1), + context.proposal_single_addr.clone(), + &dao_proposal_single::msg::ExecuteMsg::Propose(SingleChoiceProposeMsg { + title: "Add vote hooks".to_string(), + description: "Adding 2 voting hooks to test the dao_voting_incentives contract" + .to_string(), + msgs: vec![ + CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: context.proposal_single_addr.to_string(), + msg: to_json_binary(&dao_proposal_single::msg::ExecuteMsg::AddVoteHook { + address: dao_voting_incentives_addr.to_string(), + }) + .unwrap(), + funds: vec![], + }), + CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: context.proposal_single_addr.to_string(), + msg: to_json_binary(&dao_proposal_single::msg::ExecuteMsg::AddVoteHook { + address: dao_voting_incentives_cw20_addr.to_string(), + }) + .unwrap(), + funds: vec![], + }), + ], + proposer: None, + }), + &[], + ); + assert!(result.is_ok()); + + // Vote and execute the proposal to add the vote hooks + let results = vote_yes_on_proposal(&mut context, 1u64); + for result in results { + assert!(result.is_ok()); + } + execute_proposal(&mut context, 1u64); + + // Query for the newly-established hooks + let result: cw_hooks::HooksResponse = context + .app + .wrap() + .query_wasm_smart( + context.proposal_single_addr.clone(), + &dao_proposal_single::msg::QueryMsg::VoteHooks {}, + ) + .unwrap(); + assert!(result + .hooks + .contains(&dao_voting_incentives_addr.to_string())); + assert!(result + .hooks + .contains(&dao_voting_incentives_cw20_addr.to_string())); + + // Create a new proposal + context + .app + .execute_contract( + Addr::unchecked(ADDR1), + context.proposal_single_addr.clone(), + &dao_proposal_single::msg::ExecuteMsg::Propose(SingleChoiceProposeMsg { + title: "Test proposal".to_string(), + description: "Testing".to_string(), + msgs: vec![], + proposer: None, + }), + &[], + ) + .unwrap(); + + // Trigger a vote hook + let results = vote_yes_on_proposal(&mut context, 2u64); + for result in results { + assert!(result.is_ok()); + } + + // Assert that the vote hook has incremented vote counts + let votes: Uint128 = context + .app + .wrap() + .query_wasm_smart( + dao_voting_incentives_addr.clone(), + &QueryMsg::Votes { + address: ADDR1.to_string(), + }, + ) + .unwrap(); + assert_eq!(votes, Uint128::one()); + let votes: Uint128 = context + .app + .wrap() + .query_wasm_smart( + dao_voting_incentives_addr.clone(), + &QueryMsg::Votes { + address: ADDR2.to_string(), + }, + ) + .unwrap(); + assert_eq!(votes, Uint128::one()); + let votes: Uint128 = context + .app + .wrap() + .query_wasm_smart( + dao_voting_incentives_cw20_addr.clone(), + &QueryMsg::Votes { + address: ADDR1.to_string(), + }, + ) + .unwrap(); + assert_eq!(votes, Uint128::one()); + let votes: Uint128 = context + .app + .wrap() + .query_wasm_smart( + dao_voting_incentives_cw20_addr.clone(), + &QueryMsg::Votes { + address: ADDR2.to_string(), + }, + ) + .unwrap(); + assert_eq!(votes, Uint128::one()); + let config: Config = context + .app + .wrap() + .query_wasm_smart(dao_voting_incentives_addr.clone(), &QueryMsg::Config {}) + .unwrap(); + assert_eq!(config.total_votes, Uint128::new(2)); + + // Blocks have passed the voting incentives' expirations + context.app.update_block(|x| x.height += 100); + + // Creating another proposal and voting should still succeed but unregister these vote hooks + context + .app + .execute_contract( + Addr::unchecked(ADDR1), + context.proposal_single_addr.clone(), + &dao_proposal_single::msg::ExecuteMsg::Propose(SingleChoiceProposeMsg { + title: "Test proposal".to_string(), + description: "Testing".to_string(), + msgs: vec![], + proposer: None, + }), + &[], + ) + .unwrap(); + let results = vote_yes_on_proposal(&mut context, 3u64); + for (i, result) in results.iter().enumerate() { + assert!(result.is_ok()); + if i == 0 { + // Vote hooks should unregister on the first instance of expiration + let events = &result.as_ref().unwrap().events; + assert!(events.iter().any(|x| x + .attributes + .iter() + .any(|y| y.key == "removed_vote_hook" + && y.value == format!("{0}:0", dao_voting_incentives_addr.clone())))); + assert!(events.iter().any(|x| x + .attributes + .iter() + .any(|y| y.key == "removed_vote_hook" + && y.value == format!("{0}:1", dao_voting_incentives_cw20_addr.clone())))); + } + } +} + +#[test] +pub fn test_migrate_update_version() { + let mut deps = mock_dependencies(); + cw2::set_contract_version(&mut deps.storage, "my-contract", "old-version").unwrap(); + migrate(deps.as_mut(), mock_env(), MigrateMsg::FromCompatible {}).unwrap(); + let version = cw2::get_contract_version(&deps.storage).unwrap(); + assert_eq!(version.version, CONTRACT_VERSION); + assert_eq!(version.contract, CONTRACT_NAME); +} diff --git a/packages/dao-testing/Cargo.toml b/packages/dao-testing/Cargo.toml index 73df2521b..4609d2d9d 100644 --- a/packages/dao-testing/Cargo.toml +++ b/packages/dao-testing/Cargo.toml @@ -58,4 +58,5 @@ dao-voting-cw721-roles = { workspace = true } dao-voting-token-staked = { workspace = true } voting-v1 = { workspace = true } stake-cw20-v03 = { workspace = true } -dao-proposal-incentives = { workspace = true } \ No newline at end of file +dao-proposal-incentives = { workspace = true } +dao-voting-incentives = { workspace = true } \ No newline at end of file diff --git a/packages/dao-testing/src/contracts.rs b/packages/dao-testing/src/contracts.rs index 8ab5ab9ee..eae33ecc7 100644 --- a/packages/dao-testing/src/contracts.rs +++ b/packages/dao-testing/src/contracts.rs @@ -212,7 +212,19 @@ pub fn dao_proposal_incentives_contract() -> Box> { dao_proposal_incentives::contract::execute, dao_proposal_incentives::contract::instantiate, dao_proposal_incentives::contract::query, - ); + ) + .with_migrate(dao_proposal_incentives::contract::migrate); + + Box::new(contract) +} + +pub fn dao_voting_incentives_contract() -> Box> { + let contract = ContractWrapper::new( + dao_voting_incentives::contract::execute, + dao_voting_incentives::contract::instantiate, + dao_voting_incentives::contract::query, + ) + .with_migrate(dao_voting_incentives::contract::migrate); Box::new(contract) }