diff --git a/Cargo.lock b/Cargo.lock index 3819ab362..8b5375624 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2127,6 +2127,7 @@ dependencies = [ "dao-pre-propose-single", "dao-proposal-condorcet", "dao-proposal-single", + "dao-test-custom-factory", "dao-voting 0.1.0", "dao-voting 2.2.0", "dao-voting-cw20-balance", diff --git a/contracts/voting/dao-voting-token-staked/Cargo.toml b/contracts/voting/dao-voting-token-staked/Cargo.toml index 083129510..864b6eb23 100644 --- a/contracts/voting/dao-voting-token-staked/Cargo.toml +++ b/contracts/voting/dao-voting-token-staked/Cargo.toml @@ -19,7 +19,7 @@ library = [] # cargo test --features "test-tube" test-tube = [] # when writing tests you may wish to enable test-tube as a default feature -# default = ["test-tube"] +default = ["test-tube"] [dependencies] cosmwasm-std = { workspace = true, features = ["cosmwasm_1_1"] } diff --git a/contracts/voting/dao-voting-token-staked/src/tests/test_tube/integration_tests.rs b/contracts/voting/dao-voting-token-staked/src/tests/test_tube/integration_tests.rs index 2c1db58f3..0cb8059b6 100644 --- a/contracts/voting/dao-voting-token-staked/src/tests/test_tube/integration_tests.rs +++ b/contracts/voting/dao-voting-token-staked/src/tests/test_tube/integration_tests.rs @@ -1,6 +1,12 @@ -use cosmwasm_std::{Addr, Coin, Uint128}; +use cosmwasm_std::{to_binary, Addr, Coin, Decimal, Uint128, WasmMsg}; use cw_tokenfactory_issuer::msg::DenomUnit; -use dao_voting::threshold::{ActiveThreshold, ActiveThresholdError}; +use cw_utils::Duration; +use dao_interface::state::{Admin, ModuleInstantiateInfo}; +use dao_testing::test_tube::dao_dao_core::DaoCore; +use dao_voting::{ + pre_propose::PreProposeInfo, + threshold::{ActiveThreshold, ActiveThresholdError, PercentageThreshold, Threshold}, +}; use osmosis_std::types::cosmos::bank::v1beta1::QueryBalanceRequest; use osmosis_test_tube::{Account, OsmosisTestApp}; @@ -10,7 +16,7 @@ use crate::{ ContractError, }; -use super::test_env::{TestEnv, TestEnvBuilder}; +use super::test_env::{TestEnv, TestEnvBuilder, DENOM}; #[test] fn test_full_integration_correct_setup() { @@ -311,3 +317,95 @@ fn test_instantiate_no_initial_balances_fails() { TokenVotingContract::execute_submessage_error(ContractError::InitialBalancesError {}) ); } + +#[test] +fn test_factory() { + let app = OsmosisTestApp::new(); + let env = TestEnvBuilder::new(); + let TestEnv { + tf_issuer, + vp_contract, + proposal_single, + custom_factory, + accounts, + .. + } = env.full_dao_setup(&app); + + // TODO refactor so there is a function that just takes the message + // Instantiate a new voting contract using the factory pattern + let msg = dao_interface::msg::InstantiateMsg { + dao_uri: None, + admin: None, + name: "DAO DAO".to_string(), + description: "A DAO that makes DAO tooling".to_string(), + image_url: None, + automatically_add_cw20s: false, + automatically_add_cw721s: false, + voting_module_instantiate_info: ModuleInstantiateInfo { + code_id: vp_contract.code_id, + msg: to_binary(&InstantiateMsg { + token_info: TokenInfo::Factory( + to_binary(&WasmMsg::Execute { + contract_addr: custom_factory.unwrap().contract_addr.to_string(), + msg: to_binary( + &dao_test_custom_factory::msg::ExecuteMsg::TokenFactoryFactory( + dao_test_custom_factory::NewTokenInfo { + token_issuer_code_id: tf_issuer.code_id, + subdenom: DENOM.to_string(), + metadata: None, + initial_balances: vec![ + dao_test_custom_factory::InitialBalance { + address: accounts[0].address(), + amount: Uint128::new(100), + }, + ], + initial_dao_balance: None, + }, + ), + ) + .unwrap(), + funds: vec![], + }) + .unwrap(), + ), + unstaking_duration: Some(Duration::Time(2)), + active_threshold: Some(ActiveThreshold::AbsoluteCount { + count: Uint128::new(75), + }), + }) + .unwrap(), + admin: Some(Admin::CoreModule {}), + funds: vec![], + label: "DAO DAO Voting Module".to_string(), + }, + proposal_modules_instantiate_info: vec![ModuleInstantiateInfo { + code_id: proposal_single.unwrap().code_id, + msg: to_binary(&dao_proposal_single::msg::InstantiateMsg { + min_voting_period: None, + threshold: Threshold::ThresholdQuorum { + threshold: PercentageThreshold::Majority {}, + quorum: PercentageThreshold::Percent(Decimal::percent(35)), + }, + max_voting_period: Duration::Time(432000), + allow_revoting: false, + only_members_execute: true, + close_proposal_on_execution_failure: false, + pre_propose_info: PreProposeInfo::AnyoneMayPropose {}, + }) + .unwrap(), + admin: Some(Admin::CoreModule {}), + funds: vec![], + label: "DAO DAO Proposal Module".to_string(), + }], + initial_items: None, + }; + + // Instantiate DAO + let dao = DaoCore::new(&app, &msg, &accounts[0]).unwrap(); + + // TODO query voting module + + // TODO query denom + + // TODO query token contract +} diff --git a/contracts/voting/dao-voting-token-staked/src/tests/test_tube/test_env.rs b/contracts/voting/dao-voting-token-staked/src/tests/test_tube/test_env.rs index 5287cec34..10b1190c0 100644 --- a/contracts/voting/dao-voting-token-staked/src/tests/test_tube/test_env.rs +++ b/contracts/voting/dao-voting-token-staked/src/tests/test_tube/test_env.rs @@ -21,7 +21,7 @@ use dao_voting::{ use dao_testing::test_tube::{ cw_tokenfactory_issuer::TokenfactoryIssuer, dao_dao_core::DaoCore, - dao_proposal_single::DaoProposalSingle, + dao_proposal_single::DaoProposalSingle, dao_test_custom_factory::CustomFactoryContract, }; use dao_voting::threshold::ActiveThreshold; use osmosis_std::types::{ @@ -41,6 +41,7 @@ pub struct TestEnv<'a> { pub app: &'a OsmosisTestApp, pub dao: Option>, pub proposal_single: Option>, + pub custom_factory: Option>, pub vp_contract: TokenVotingContract<'a>, pub tf_issuer: TokenfactoryIssuer<'a>, pub accounts: Vec, @@ -168,6 +169,7 @@ impl TestEnvBuilder { accounts, dao: None, proposal_single: None, + custom_factory: None, tf_issuer, vp_contract, } @@ -227,6 +229,7 @@ impl TestEnvBuilder { }) .unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "DAO DAO Voting Module".to_string(), }, proposal_modules_instantiate_info: vec![ModuleInstantiateInfo { @@ -245,6 +248,7 @@ impl TestEnvBuilder { }) .unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "DAO DAO Proposal Module".to_string(), }], initial_items: None, @@ -277,11 +281,20 @@ impl TestEnvBuilder { TokenVotingContract::query(&vp_contract, &QueryMsg::TokenContract {}).unwrap(); let tf_issuer = TokenfactoryIssuer::new_with_values(app, issuer_id, issuer_addr).unwrap(); + // Instantiate Custom Factory + let custom_factory = CustomFactoryContract::new( + app, + &dao_test_custom_factory::msg::InstantiateMsg {}, + &accounts[0], + ) + .unwrap(); + TestEnv { app, dao: Some(dao), vp_contract, proposal_single: Some(proposal_single), + custom_factory: Some(custom_factory), tf_issuer, accounts, } diff --git a/packages/dao-testing/Cargo.toml b/packages/dao-testing/Cargo.toml index 0416bdc84..9698f9e24 100644 --- a/packages/dao-testing/Cargo.toml +++ b/packages/dao-testing/Cargo.toml @@ -49,6 +49,7 @@ dao-pre-propose-multiple = { workspace = true } dao-pre-propose-single = { workspace = true } dao-proposal-condorcet = { workspace = true } dao-proposal-single = { workspace = true } +dao-test-custom-factory = { workspace = true } dao-voting = { workspace = true } dao-voting-cw20-balance = { workspace = true } dao-voting-cw20-staked = { workspace = true } diff --git a/packages/dao-testing/src/test_tube/dao_test_custom_factory.rs b/packages/dao-testing/src/test_tube/dao_test_custom_factory.rs new file mode 100644 index 000000000..754374884 --- /dev/null +++ b/packages/dao-testing/src/test_tube/dao_test_custom_factory.rs @@ -0,0 +1,129 @@ +use cosmwasm_std::Coin; +use dao_test_custom_factory::{ + msg::{ExecuteMsg, InstantiateMsg, QueryMsg}, + ContractError, +}; +use osmosis_test_tube::{ + osmosis_std::types::cosmwasm::wasm::v1::MsgExecuteContractResponse, Account, Module, + OsmosisTestApp, RunnerError, RunnerExecuteResult, SigningAccount, Wasm, +}; +use serde::de::DeserializeOwned; +use std::fmt::Debug; +use std::path::PathBuf; + +#[derive(Debug)] +pub struct CustomFactoryContract<'a> { + pub app: &'a OsmosisTestApp, + pub code_id: u64, + pub contract_addr: String, +} + +impl<'a> CustomFactoryContract<'a> { + pub fn new( + app: &'a OsmosisTestApp, + instantiate_msg: &InstantiateMsg, + signer: &SigningAccount, + ) -> Result { + let wasm = Wasm::new(app); + let token_creation_fee = Coin::new(10000000, "uosmo"); + + let code_id = wasm + .store_code(&Self::get_wasm_byte_code(), None, signer)? + .data + .code_id; + + let contract_addr = wasm + .instantiate( + code_id, + &instantiate_msg, + Some(&signer.address()), + None, + &[token_creation_fee], + signer, + )? + .data + .address; + + Ok(Self { + app, + code_id, + contract_addr, + }) + } + + pub fn new_with_values( + app: &'a OsmosisTestApp, + code_id: u64, + contract_addr: String, + ) -> Result { + Ok(Self { + app, + code_id, + contract_addr, + }) + } + + /// uploads contract and returns a code ID + pub fn upload(app: &OsmosisTestApp, signer: &SigningAccount) -> Result { + let wasm = Wasm::new(app); + + let code_id = wasm + .store_code(&Self::get_wasm_byte_code(), None, signer)? + .data + .code_id; + + Ok(code_id) + } + + // executes + pub fn execute( + &self, + execute_msg: &ExecuteMsg, + funds: &[Coin], + signer: &SigningAccount, + ) -> RunnerExecuteResult { + let wasm = Wasm::new(self.app); + wasm.execute(&self.contract_addr, execute_msg, funds, signer) + } + + // queries + pub fn query(&self, query_msg: &QueryMsg) -> Result + where + T: DeserializeOwned, + { + let wasm = Wasm::new(self.app); + wasm.query(&self.contract_addr, query_msg) + } + + fn get_wasm_byte_code() -> Vec { + let manifest_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + let byte_code = std::fs::read( + manifest_path + .join("..") + .join("..") + .join("artifacts") + .join("dao_test_custom_factory.wasm"), + ); + match byte_code { + Ok(byte_code) => byte_code, + // On arm processors, the above path is not found, so we try the following path + Err(_) => std::fs::read( + manifest_path + .join("..") + .join("..") + .join("artifacts") + .join("dao_test_custom_factory-aarch64.wasm"), + ) + .unwrap(), + } + } + + pub fn execute_error(err: ContractError) -> RunnerError { + RunnerError::ExecuteError { + msg: format!( + "failed to execute message; message index: 0: {}: execute wasm contract failed", + err + ), + } + } +} diff --git a/packages/dao-testing/src/test_tube/mod.rs b/packages/dao-testing/src/test_tube/mod.rs index a5f997792..d24469e60 100644 --- a/packages/dao-testing/src/test_tube/mod.rs +++ b/packages/dao-testing/src/test_tube/mod.rs @@ -13,3 +13,6 @@ pub mod dao_dao_core; #[cfg(feature = "test-tube")] pub mod dao_proposal_single; + +#[cfg(feature = "test-tube")] +pub mod dao_test_custom_factory;