From 5a85380f7ace0d1f1f3bfa06d01430f4f44de865 Mon Sep 17 00:00:00 2001 From: Jake Hartnell Date: Tue, 12 Sep 2023 17:01:00 -0700 Subject: [PATCH 01/30] Start outlining factory pattern work --- Cargo.lock | 15 + Cargo.toml | 1 + ci/bootstrap-env/src/main.rs | 3 + ci/integration-tests/src/helpers/helper.rs | 3 + contracts/dao-dao-core/src/contract.rs | 1 + contracts/dao-dao-core/src/tests.rs | 36 ++ .../external/cw-admin-factory/src/tests.rs | 3 + .../dao-migrator/src/testing/setup.rs | 1 + .../src/tests.rs | 3 + .../dao-pre-propose-approver/src/tests.rs | 2 + .../dao-pre-propose-multiple/src/tests.rs | 3 + .../dao-pre-propose-single/src/tests.rs | 3 + .../src/testing/suite.rs | 2 + .../src/testing/instantiate.rs | 35 +- .../src/testing/tests.rs | 1 + .../src/testing/instantiate.rs | 23 +- .../dao-proposal-single/src/testing/tests.rs | 8 +- .../dao-voting-cw721-staked/src/contract.rs | 37 +- .../dao-voting-cw721-staked/src/error.rs | 7 + .../voting/dao-voting-cw721-staked/src/msg.rs | 35 +- .../dao-voting-token-staked/src/contract.rs | 41 +- .../dao-voting-token-staked/src/error.rs | 3 + .../voting/dao-voting-token-staked/src/msg.rs | 5 +- packages/dao-interface/src/state.rs | 9 +- packages/dao-testing/src/helpers.rs | 8 + packages/dao-voting/src/pre_propose.rs | 1 + .../dao-proposal-hook-counter/src/tests.rs | 2 + .../dao-test-custom-factory/.cargo/config | 4 + .../dao-test-custom-factory/.gitignore | 15 + .../dao-test-custom-factory/Cargo.toml | 30 ++ test-contracts/dao-test-custom-factory/README | 2 + .../examples/schema.rs | 10 + .../schema/admin_response.json | 6 + .../schema/dao_response.json | 6 + .../schema/execute_msg.json | 487 ++++++++++++++++++ .../schema/info_response.json | 32 ++ .../schema/instantiate_msg.json | 13 + .../schema/query_msg.json | 42 ++ .../dao-test-custom-factory/src/contract.rs | 91 ++++ .../dao-test-custom-factory/src/error.rs | 14 + .../dao-test-custom-factory/src/lib.rs | 5 + .../dao-test-custom-factory/src/msg.rs | 17 + 42 files changed, 1024 insertions(+), 41 deletions(-) create mode 100644 test-contracts/dao-test-custom-factory/.cargo/config create mode 100644 test-contracts/dao-test-custom-factory/.gitignore create mode 100644 test-contracts/dao-test-custom-factory/Cargo.toml create mode 100644 test-contracts/dao-test-custom-factory/README create mode 100644 test-contracts/dao-test-custom-factory/examples/schema.rs create mode 100644 test-contracts/dao-test-custom-factory/schema/admin_response.json create mode 100644 test-contracts/dao-test-custom-factory/schema/dao_response.json create mode 100644 test-contracts/dao-test-custom-factory/schema/execute_msg.json create mode 100644 test-contracts/dao-test-custom-factory/schema/info_response.json create mode 100644 test-contracts/dao-test-custom-factory/schema/instantiate_msg.json create mode 100644 test-contracts/dao-test-custom-factory/schema/query_msg.json create mode 100644 test-contracts/dao-test-custom-factory/src/contract.rs create mode 100644 test-contracts/dao-test-custom-factory/src/error.rs create mode 100644 test-contracts/dao-test-custom-factory/src/lib.rs create mode 100644 test-contracts/dao-test-custom-factory/src/msg.rs diff --git a/Cargo.lock b/Cargo.lock index b81b4604b..3cb7af2a4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1963,6 +1963,21 @@ dependencies = [ "thiserror", ] +[[package]] +name = "dao-test-custom-factory" +version = "2.2.0" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cosmwasm-storage", + "cw-multi-test", + "cw-storage-plus 1.1.0", + "cw2 1.1.0", + "dao-dao-macros", + "dao-interface", + "thiserror", +] + [[package]] name = "dao-testing" version = "2.2.0" diff --git a/Cargo.toml b/Cargo.toml index 0d55ba587..505d3739b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -105,6 +105,7 @@ dao-proposal-multiple = { path = "./contracts/proposal/dao-proposal-multiple", v dao-proposal-single = { path = "./contracts/proposal/dao-proposal-single", version = "2.2.0" } dao-proposal-sudo = { path = "./test-contracts/dao-proposal-sudo", version = "2.2.0" } dao-proposal-hook-counter = { path = "./test-contracts/dao-proposal-hook-counter", version = "2.2.0" } +dao-test-custom-factory = { path = "./test-contracts/dao-test-custom-factory", version = "*" } dao-testing = { path = "./packages/dao-testing", version = "2.2.0" } dao-voting = { path = "./packages/dao-voting", version = "2.2.0" } dao-voting-cw20-balance = { path = "./test-contracts/dao-voting-cw20-balance", version = "2.2.0" } diff --git a/ci/bootstrap-env/src/main.rs b/ci/bootstrap-env/src/main.rs index b8bdeb0a5..85fd119a5 100644 --- a/ci/bootstrap-env/src/main.rs +++ b/ci/bootstrap-env/src/main.rs @@ -73,6 +73,7 @@ fn main() -> Result<()> { }, active_threshold: None, })?, + funds: vec![], admin: Some(Admin::CoreModule {}), label: "DAO DAO Voting Module".to_string(), }, @@ -101,12 +102,14 @@ fn main() -> Result<()> { }) .unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "DAO DAO Pre-Propose Module".to_string(), }, }, close_proposal_on_execution_failure: false, })?, admin: Some(Admin::CoreModule {}), + funds: vec![], label: "DAO DAO Proposal Module".to_string(), }], initial_items: None, diff --git a/ci/integration-tests/src/helpers/helper.rs b/ci/integration-tests/src/helpers/helper.rs index 4d30c1670..bcfd0e26d 100644 --- a/ci/integration-tests/src/helpers/helper.rs +++ b/ci/integration-tests/src/helpers/helper.rs @@ -59,6 +59,7 @@ pub fn create_dao( })?, admin: Some(Admin::CoreModule {}), label: "DAO DAO Voting Module".to_string(), + funds: vec![], }, proposal_modules_instantiate_info: vec![ModuleInstantiateInfo { code_id: chain.orc.contract_map.code_id("dao_proposal_single")?, @@ -86,11 +87,13 @@ pub fn create_dao( }) .unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "DAO DAO Pre-Propose Module".to_string(), }, }, })?, admin: Some(Admin::CoreModule {}), + funds: vec![], label: "DAO DAO Proposal Module".to_string(), }], initial_items: None, diff --git a/contracts/dao-dao-core/src/contract.rs b/contracts/dao-dao-core/src/contract.rs index 97f3a7d3e..21e38784e 100644 --- a/contracts/dao-dao-core/src/contract.rs +++ b/contracts/dao-dao-core/src/contract.rs @@ -910,6 +910,7 @@ pub fn migrate(deps: DepsMut, env: Env, msg: MigrateMsg) -> Result>(); @@ -198,12 +201,14 @@ fn test_instantiate_with_submessage_failure() { code_id: cw20_id, msg: to_binary("bad").unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "I have a bad instantiate message".to_string(), }); governance_modules.push(ModuleInstantiateInfo { code_id: cw20_id, msg: to_binary(&cw20_instantiate).unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "Everybody knowing that goodness is good makes wickedness." @@ -222,6 +227,7 @@ makes wickedness." code_id: cw20_id, msg: to_binary(&cw20_instantiate).unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "voting module".to_string(), }, proposal_modules_instantiate_info: governance_modules, @@ -252,12 +258,14 @@ fn test_update_config() { code_id: govmod_id, msg: to_binary(&govmod_instantiate).unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "voting module".to_string(), }, proposal_modules_instantiate_info: vec![ModuleInstantiateInfo { code_id: govmod_id, msg: to_binary(&govmod_instantiate).unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "voting module".to_string(), }], initial_items: None, @@ -349,12 +357,14 @@ fn test_swap_governance(swaps: Vec<(u32, u32)>) { code_id: propmod_id, msg: to_binary(&govmod_instantiate).unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "voting module".to_string(), }, proposal_modules_instantiate_info: vec![ModuleInstantiateInfo { code_id: propmod_id, msg: to_binary(&govmod_instantiate).unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "governance module".to_string(), }], initial_items: None, @@ -418,6 +428,7 @@ fn test_swap_governance(swaps: Vec<(u32, u32)>) { code_id: propmod_id, msg: to_binary(&govmod_instantiate).unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: format!("governance module {n}"), }) .collect(); @@ -525,12 +536,14 @@ fn test_removed_modules_can_not_execute() { code_id: govmod_id, msg: to_binary(&govmod_instantiate).unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "voting module".to_string(), }, proposal_modules_instantiate_info: vec![ModuleInstantiateInfo { code_id: govmod_id, msg: to_binary(&govmod_instantiate).unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "governance module".to_string(), }], initial_items: None, @@ -566,6 +579,7 @@ fn test_removed_modules_can_not_execute() { code_id: govmod_id, msg: to_binary(&govmod_instantiate).unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "new governance module".to_string(), }]; @@ -597,6 +611,7 @@ fn test_removed_modules_can_not_execute() { code_id: govmod_id, msg: to_binary(&govmod_instantiate).unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "new governance module".to_string(), }]; let to_disable = vec![new_proposal_module.address.to_string()]; @@ -682,12 +697,14 @@ fn test_module_already_disabled() { code_id: govmod_id, msg: to_binary(&govmod_instantiate).unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "voting module".to_string(), }, proposal_modules_instantiate_info: vec![ModuleInstantiateInfo { code_id: govmod_id, msg: to_binary(&govmod_instantiate).unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "governance module".to_string(), }], initial_items: None, @@ -737,6 +754,7 @@ fn test_module_already_disabled() { code_id: govmod_id, msg: to_binary(&govmod_instantiate).unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "governance module".to_string(), }], to_disable, @@ -781,12 +799,14 @@ fn test_swap_voting_module() { code_id: govmod_id, msg: to_binary(&govmod_instantiate).unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "voting module".to_string(), }, proposal_modules_instantiate_info: vec![ModuleInstantiateInfo { code_id: govmod_id, msg: to_binary(&govmod_instantiate).unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "governance module".to_string(), }], initial_items: None, @@ -833,6 +853,7 @@ fn test_swap_voting_module() { code_id: govmod_id, msg: to_binary(&govmod_instantiate).unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "voting module".to_string(), }, }) @@ -882,12 +903,14 @@ fn test_permissions() { code_id: govmod_id, msg: to_binary(&govmod_instantiate).unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "voting module".to_string(), }, proposal_modules_instantiate_info: vec![ModuleInstantiateInfo { code_id: govmod_id, msg: to_binary(&govmod_instantiate).unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "governance module".to_string(), }], initial_items: None, @@ -914,6 +937,7 @@ fn test_permissions() { code_id: govmod_id, msg: to_binary(&govmod_instantiate).unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "voting module".to_string(), }, }, @@ -981,12 +1005,14 @@ fn do_standard_instantiate(auto_add: bool, admin: Option) -> (Addr, App) code_id: voting_id, msg: to_binary(&voting_instantiate).unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "voting module".to_string(), }, proposal_modules_instantiate_info: vec![ModuleInstantiateInfo { code_id: govmod_id, msg: to_binary(&govmod_instantiate).unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "governance module".to_string(), }], initial_items: None, @@ -1613,12 +1639,14 @@ fn test_list_items() { code_id: voting_id, msg: to_binary(&voting_instantiate).unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "voting module".to_string(), }, proposal_modules_instantiate_info: vec![ModuleInstantiateInfo { code_id: govmod_id, msg: to_binary(&govmod_instantiate).unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "governance module".to_string(), }], initial_items: None, @@ -1745,12 +1773,14 @@ fn test_instantiate_with_items() { code_id: voting_id, msg: to_binary(&voting_instantiate).unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "voting module".to_string(), }, proposal_modules_instantiate_info: vec![ModuleInstantiateInfo { code_id: govmod_id, msg: to_binary(&govmod_instantiate).unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "governance module".to_string(), }], initial_items: Some(initial_items.clone()), @@ -2576,12 +2606,14 @@ fn test_migrate_from_compatible() { code_id: voting_id, msg: to_binary(&voting_instantiate).unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "voting module".to_string(), }, proposal_modules_instantiate_info: vec![ModuleInstantiateInfo { code_id: govmod_id, msg: to_binary(&govmod_instantiate).unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "governance module".to_string(), }], initial_items: None, @@ -2859,6 +2891,7 @@ fn test_module_prefixes() { code_id: govmod_id, msg: to_binary(&govmod_instantiate).unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "voting module".to_string(), }, proposal_modules_instantiate_info: vec![ @@ -2866,18 +2899,21 @@ fn test_module_prefixes() { code_id: govmod_id, msg: to_binary(&govmod_instantiate).unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "proposal module 1".to_string(), }, ModuleInstantiateInfo { code_id: govmod_id, msg: to_binary(&govmod_instantiate).unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "proposal module 2".to_string(), }, ModuleInstantiateInfo { code_id: govmod_id, msg: to_binary(&govmod_instantiate).unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "proposal module 2".to_string(), }, ], diff --git a/contracts/external/cw-admin-factory/src/tests.rs b/contracts/external/cw-admin-factory/src/tests.rs index 226d1f1ef..27221cb31 100644 --- a/contracts/external/cw-admin-factory/src/tests.rs +++ b/contracts/external/cw-admin-factory/src/tests.rs @@ -84,6 +84,7 @@ pub fn test_set_admin() { code_id: cw20_code_id, msg: to_binary(&cw20_instantiate).unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "voting module".to_string(), }, proposal_modules_instantiate_info: vec![ @@ -91,12 +92,14 @@ pub fn test_set_admin() { code_id: cw20_code_id, msg: to_binary(&cw20_instantiate).unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "prop module".to_string(), }, ModuleInstantiateInfo { code_id: cw20_code_id, msg: to_binary(&cw20_instantiate).unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "prop module 2".to_string(), }, ], diff --git a/contracts/external/dao-migrator/src/testing/setup.rs b/contracts/external/dao-migrator/src/testing/setup.rs index b9d93950e..8b24e7a40 100644 --- a/contracts/external/dao-migrator/src/testing/setup.rs +++ b/contracts/external/dao-migrator/src/testing/setup.rs @@ -314,6 +314,7 @@ pub fn execute_migration( }) .unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "migrator".to_string(), }], to_disable: vec![], 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 2127cebf5..cf6eaf5ca 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 @@ -71,6 +71,7 @@ fn get_default_proposal_module_instantiate( }) .unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "baby's first pre-propose module".to_string(), }, }, @@ -1200,6 +1201,7 @@ fn test_instantiate_with_zero_native_deposit() { }) .unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "baby's first pre-propose module".to_string(), }, }, @@ -1263,6 +1265,7 @@ fn test_instantiate_with_zero_cw20_deposit() { }) .unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "baby's first pre-propose module".to_string(), }, }, diff --git a/contracts/pre-propose/dao-pre-propose-approver/src/tests.rs b/contracts/pre-propose/dao-pre-propose-approver/src/tests.rs index 5aea7b2bc..353fd0c41 100644 --- a/contracts/pre-propose/dao-pre-propose-approver/src/tests.rs +++ b/contracts/pre-propose/dao-pre-propose-approver/src/tests.rs @@ -95,6 +95,7 @@ fn get_proposal_module_approval_single_instantiate( }) .unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "baby's first pre-propose module, needs supervision".to_string(), }, }, @@ -126,6 +127,7 @@ fn get_proposal_module_approver_instantiate( }) .unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "approver module".to_string(), }, }, 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 3d7352515..2de6ee79c 100644 --- a/contracts/pre-propose/dao-pre-propose-multiple/src/tests.rs +++ b/contracts/pre-propose/dao-pre-propose-multiple/src/tests.rs @@ -72,6 +72,7 @@ fn get_default_proposal_module_instantiate( }) .unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "baby's first pre-propose module".to_string(), }, }, @@ -1059,6 +1060,7 @@ fn test_instantiate_with_zero_native_deposit() { }) .unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "baby's first pre-propose module".to_string(), }, }, @@ -1120,6 +1122,7 @@ fn test_instantiate_with_zero_cw20_deposit() { }) .unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "baby's first pre-propose module".to_string(), }, }, 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 078ea2d4b..e4ab908f9 100644 --- a/contracts/pre-propose/dao-pre-propose-single/src/tests.rs +++ b/contracts/pre-propose/dao-pre-propose-single/src/tests.rs @@ -70,6 +70,7 @@ fn get_default_proposal_module_instantiate( }) .unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "baby's first pre-propose module".to_string(), }, }, @@ -995,6 +996,7 @@ fn test_instantiate_with_zero_native_deposit() { }) .unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "baby's first pre-propose module".to_string(), }, }, @@ -1056,6 +1058,7 @@ fn test_instantiate_with_zero_cw20_deposit() { }) .unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "baby's first pre-propose module".to_string(), }, }, diff --git a/contracts/proposal/dao-proposal-condorcet/src/testing/suite.rs b/contracts/proposal/dao-proposal-condorcet/src/testing/suite.rs index a9e7ac30a..9f9f945be 100644 --- a/contracts/proposal/dao-proposal-condorcet/src/testing/suite.rs +++ b/contracts/proposal/dao-proposal-condorcet/src/testing/suite.rs @@ -96,12 +96,14 @@ impl SuiteBuilder { }) .unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "voting module".to_string(), }, proposal_modules_instantiate_info: vec![ModuleInstantiateInfo { code_id: condorcet_id, msg: to_binary(&self.instantiate).unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "condorcet module".to_string(), }], initial_items: None, diff --git a/contracts/proposal/dao-proposal-multiple/src/testing/instantiate.rs b/contracts/proposal/dao-proposal-multiple/src/testing/instantiate.rs index 2d767ae5d..f6188a6f0 100644 --- a/contracts/proposal/dao-proposal-multiple/src/testing/instantiate.rs +++ b/contracts/proposal/dao-proposal-multiple/src/testing/instantiate.rs @@ -39,6 +39,7 @@ fn get_pre_propose_info( }) .unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "pre_propose_contract".to_string(), }, } @@ -156,13 +157,15 @@ pub fn _instantiate_with_staked_cw721_governance( }) .unwrap(), admin: None, + funds: vec![], label: "DAO DAO voting module".to_string(), }, proposal_modules_instantiate_info: vec![ModuleInstantiateInfo { code_id: proposal_module_code_id, - label: "DAO DAO governance module.".to_string(), - admin: Some(Admin::CoreModule {}), msg: to_binary(&proposal_module_instantiate).unwrap(), + admin: Some(Admin::CoreModule {}), + funds: vec![], + label: "DAO DAO governance module.".to_string(), }], initial_items: None, dao_uri: None, @@ -273,13 +276,15 @@ pub fn _instantiate_with_native_staked_balances_governance( }) .unwrap(), admin: None, + funds: vec![], label: "DAO DAO voting module".to_string(), }, proposal_modules_instantiate_info: vec![ModuleInstantiateInfo { code_id: proposal_module_code_id, - label: "DAO DAO governance module.".to_string(), - admin: Some(Admin::CoreModule {}), msg: to_binary(&proposal_module_instantiate).unwrap(), + admin: Some(Admin::CoreModule {}), + funds: vec![], + label: "DAO DAO governance module.".to_string(), }], initial_items: None, dao_uri: None, @@ -387,13 +392,15 @@ pub fn instantiate_with_cw20_balances_governance( }) .unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "DAO DAO voting module".to_string(), }, proposal_modules_instantiate_info: vec![ModuleInstantiateInfo { code_id: proposal_module_code_id, - label: "DAO DAO governance module.".to_string(), - admin: Some(Admin::CoreModule {}), msg: to_binary(&proposal_module_instantiate).unwrap(), + admin: Some(Admin::CoreModule {}), + funds: vec![], + label: "DAO DAO governance module.".to_string(), }], initial_items: None, dao_uri: None, @@ -471,13 +478,15 @@ pub fn instantiate_with_staked_balances_governance( }) .unwrap(), admin: None, + funds: vec![], label: "DAO DAO voting module".to_string(), }, proposal_modules_instantiate_info: vec![ModuleInstantiateInfo { code_id: proposal_module_code_id, - label: "DAO DAO governance module.".to_string(), - admin: Some(Admin::CoreModule {}), msg: to_binary(&proposal_module_instantiate).unwrap(), + admin: Some(Admin::CoreModule {}), + funds: vec![], + label: "DAO DAO governance module.".to_string(), }], initial_items: None, dao_uri: None, @@ -608,13 +617,15 @@ pub fn instantiate_with_multiple_staked_balances_governance( }) .unwrap(), admin: None, + funds: vec![], label: "DAO DAO voting module".to_string(), }, proposal_modules_instantiate_info: vec![ModuleInstantiateInfo { code_id: proposal_module_code_id, - label: "DAO DAO governance module.".to_string(), - admin: Some(Admin::CoreModule {}), msg: to_binary(&proposal_module_instantiate).unwrap(), + admin: Some(Admin::CoreModule {}), + funds: vec![], + label: "DAO DAO governance module.".to_string(), }], initial_items: None, dao_uri: None, @@ -721,12 +732,14 @@ pub fn instantiate_with_staking_active_threshold( }) .unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "DAO DAO voting module".to_string(), }, proposal_modules_instantiate_info: vec![ModuleInstantiateInfo { code_id: proposal_module_code_id, msg: to_binary(&proposal_module_instantiate).unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "DAO DAO governance module".to_string(), }], initial_items: None, @@ -798,12 +811,14 @@ pub fn _instantiate_with_cw4_groups_governance( }) .unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "DAO DAO voting module".to_string(), }, proposal_modules_instantiate_info: vec![ModuleInstantiateInfo { code_id: proposal_module_code_id, msg: to_binary(&proposal_module_instantiate).unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "DAO DAO governance module".to_string(), }], initial_items: None, diff --git a/contracts/proposal/dao-proposal-multiple/src/testing/tests.rs b/contracts/proposal/dao-proposal-multiple/src/testing/tests.rs index eaf69fcc8..9f1749423 100644 --- a/contracts/proposal/dao-proposal-multiple/src/testing/tests.rs +++ b/contracts/proposal/dao-proposal-multiple/src/testing/tests.rs @@ -96,6 +96,7 @@ pub fn get_pre_propose_info( }) .unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "pre_propose_contract".to_string(), }, } diff --git a/contracts/proposal/dao-proposal-single/src/testing/instantiate.rs b/contracts/proposal/dao-proposal-single/src/testing/instantiate.rs index aafcb5abe..b9e9d74de 100644 --- a/contracts/proposal/dao-proposal-single/src/testing/instantiate.rs +++ b/contracts/proposal/dao-proposal-single/src/testing/instantiate.rs @@ -41,6 +41,7 @@ pub(crate) fn get_pre_propose_info( }) .unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "pre_propose_contract".to_string(), }, } @@ -154,13 +155,15 @@ pub(crate) fn instantiate_with_staked_cw721_governance( }) .unwrap(), admin: None, + funds: vec![], label: "DAO DAO voting module".to_string(), }, proposal_modules_instantiate_info: vec![ModuleInstantiateInfo { code_id: proposal_module_code_id, - label: "DAO DAO governance module.".to_string(), - admin: Some(Admin::CoreModule {}), msg: to_binary(&proposal_module_instantiate).unwrap(), + admin: Some(Admin::CoreModule {}), + funds: vec![], + label: "DAO DAO governance module.".to_string(), }], initial_items: None, }; @@ -271,13 +274,15 @@ pub(crate) fn instantiate_with_native_staked_balances_governance( }) .unwrap(), admin: None, + funds: vec![], label: "DAO DAO voting module".to_string(), }, proposal_modules_instantiate_info: vec![ModuleInstantiateInfo { code_id: proposal_module_code_id, - label: "DAO DAO governance module.".to_string(), - admin: Some(Admin::CoreModule {}), msg: to_binary(&proposal_module_instantiate).unwrap(), + admin: Some(Admin::CoreModule {}), + funds: vec![], + label: "DAO DAO governance module.".to_string(), }], initial_items: None, }; @@ -390,13 +395,15 @@ pub(crate) fn instantiate_with_staked_balances_governance( }) .unwrap(), admin: None, + funds: vec![], label: "DAO DAO voting module".to_string(), }, proposal_modules_instantiate_info: vec![ModuleInstantiateInfo { code_id: proposal_module_code_id, - label: "DAO DAO governance module.".to_string(), - admin: Some(Admin::CoreModule {}), msg: to_binary(&proposal_module_instantiate).unwrap(), + admin: Some(Admin::CoreModule {}), + funds: vec![], + label: "DAO DAO governance module.".to_string(), }], initial_items: None, }; @@ -503,12 +510,14 @@ pub(crate) fn instantiate_with_staking_active_threshold( }) .unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "DAO DAO voting module".to_string(), }, proposal_modules_instantiate_info: vec![ModuleInstantiateInfo { code_id: proposal_module_code_id, msg: to_binary(&proposal_module_instantiate).unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "DAO DAO governance module".to_string(), }], initial_items: None, @@ -580,12 +589,14 @@ pub(crate) fn instantiate_with_cw4_groups_governance( }) .unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "DAO DAO voting module".to_string(), }, proposal_modules_instantiate_info: vec![ModuleInstantiateInfo { code_id: proposal_module_code_id, msg: to_binary(&proposal_module_instantiate).unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "DAO DAO governance module".to_string(), }], initial_items: None, diff --git a/contracts/proposal/dao-proposal-single/src/testing/tests.rs b/contracts/proposal/dao-proposal-single/src/testing/tests.rs index a4e6f4b46..8634243dd 100644 --- a/contracts/proposal/dao-proposal-single/src/testing/tests.rs +++ b/contracts/proposal/dao-proposal-single/src/testing/tests.rs @@ -1626,13 +1626,15 @@ fn test_migrate_from_v1() { }) .unwrap(), admin: None, + funds: vec![], label: "DAO DAO voting module".to_string(), }, proposal_modules_instantiate_info: vec![ModuleInstantiateInfo { code_id: v1_proposal_single_code, - label: "DAO DAO governance module.".to_string(), - admin: Some(Admin::CoreModule {}), msg: to_binary(&instantiate).unwrap(), + admin: Some(Admin::CoreModule {}), + funds: vec![], + label: "DAO DAO governance module.".to_string(), }], initial_items: None, }; @@ -1739,6 +1741,7 @@ fn test_migrate_from_v1() { }) .unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "DAO DAO pre-propose".to_string(), }, }, @@ -2409,6 +2412,7 @@ fn test_update_pre_propose_module() { }) .unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "new pre-propose module".to_string(), }, }, diff --git a/contracts/voting/dao-voting-cw721-staked/src/contract.rs b/contracts/voting/dao-voting-cw721-staked/src/contract.rs index 82e6ac050..e8f97ab76 100644 --- a/contracts/voting/dao-voting-cw721-staked/src/contract.rs +++ b/contracts/voting/dao-voting-cw721-staked/src/contract.rs @@ -8,7 +8,7 @@ use cosmwasm_std::{ use cw2::{get_contract_version, set_contract_version, ContractVersion}; use cw721::{Cw721QueryMsg, Cw721ReceiveMsg, NumTokensResponse}; use cw_storage_plus::Bound; -use cw_utils::{parse_reply_instantiate_data, Duration}; +use cw_utils::{parse_reply_execute_data, parse_reply_instantiate_data, Duration}; use dao_hooks::nft_stake::{stake_nft_hook_msgs, unstake_nft_hook_msgs}; use dao_interface::voting::IsActiveResponse; use dao_voting::threshold::{ActiveThreshold, ActiveThresholdResponse}; @@ -25,6 +25,7 @@ pub(crate) const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); const INSTANTIATE_NFT_CONTRACT_REPLY_ID: u64 = 0; const VALIDATE_ABSOLUTE_COUNT_FOR_NEW_NFT_CONTRACTS: u64 = 1; +const FACTORY_EXECUTE_REPLY_ID: u64 = 2; // We multiply by this when calculating needed power for being active // when using active threshold with percent @@ -176,6 +177,25 @@ pub fn instantiate( .add_attribute("method", "instantiate") .add_submessage(instantiate_msg)) } + NftContract::Factory(binary) => match from_binary(&binary)? { + WasmMsg::Execute { + msg, + contract_addr, + funds, + } => Ok(Response::new() + .add_attribute("action", "intantiate") + .add_submessage(SubMsg::reply_on_success( + WasmMsg::Execute { + contract_addr, + msg, + // TODO what to do with funds for fair burn? + // Need to pass them along to the factory + funds, + }, + FACTORY_EXECUTE_REPLY_ID, + ))), + _ => return Err(ContractError::UnsupportedFactoryMsg {}), + }, } } @@ -713,6 +733,21 @@ pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result { + let res = parse_reply_execute_data(msg)?; + + // TODO validate active threshold is set. Some contracts such as a minter, + // contract may not have any supply until tokens are minted. + + match res.data { + Some(data) => { + // TODO parse data and save token contract address / denom + unimplemented!() + } + // TODO better error + None => return Err(ContractError::Unauthorized {}), + } + } _ => Err(ContractError::UnknownReplyId { id: msg.id }), } } diff --git a/contracts/voting/dao-voting-cw721-staked/src/error.rs b/contracts/voting/dao-voting-cw721-staked/src/error.rs index 15e2cecea..595336e97 100644 --- a/contracts/voting/dao-voting-cw721-staked/src/error.rs +++ b/contracts/voting/dao-voting-cw721-staked/src/error.rs @@ -1,4 +1,5 @@ use cosmwasm_std::{Addr, StdError}; +use cw_utils::ParseReplyError; use thiserror::Error; #[derive(Error, Debug, PartialEq)] @@ -6,6 +7,9 @@ pub enum ContractError { #[error(transparent)] Std(#[from] StdError), + #[error(transparent)] + ParseReplyError(#[from] ParseReplyError), + #[error("Can not stake that which has already been staked")] AlreadyStaked {}, @@ -45,6 +49,9 @@ pub enum ContractError { #[error("Got a submessage reply with unknown id: {id}")] UnknownReplyId { id: u64 }, + #[error("Factory message must serialize to WasmMsg::Execute")] + UnsupportedFactoryMsg {}, + #[error("Active threshold count must be greater than zero")] ZeroActiveCount {}, diff --git a/contracts/voting/dao-voting-cw721-staked/src/msg.rs b/contracts/voting/dao-voting-cw721-staked/src/msg.rs index f47d82533..7fb38d79d 100644 --- a/contracts/voting/dao-voting-cw721-staked/src/msg.rs +++ b/contracts/voting/dao-voting-cw721-staked/src/msg.rs @@ -8,10 +8,12 @@ use dao_voting::threshold::{ActiveThreshold, ActiveThresholdResponse}; #[cw_serde] #[allow(clippy::large_enum_variant)] pub enum NftContract { + /// Uses an existing cw721 or sg721 token contract. Existing { - /// Address of an already instantiated cw721 token contract. + /// Address of an already instantiated cw721 or sg721 token contract. address: String, }, + /// Creates a new NFT collection used for staking and governance. New { /// Code ID for cw721 token contract. code_id: u64, @@ -23,6 +25,9 @@ pub enum NftContract { /// valid mint message for the corresponding cw721 contract. initial_nfts: Vec, }, + /// Uses a factory pattern that must return the denom, optionally a Token Contract address, + /// and any setup messages. The binary must serialize to a `WasmMsg::Execute` message. + Factory(Binary), } #[cw_serde] @@ -46,22 +51,20 @@ pub enum ExecuteMsg { /// Unstakes the specified token_ids on behalf of the /// sender. token_ids must have unique values and have non-zero /// length. - Unstake { - token_ids: Vec, - }, + Unstake { token_ids: Vec }, + /// Claim NFTs that have been unstaked for the specified duration. ClaimNfts {}, - UpdateConfig { - duration: Option, - }, - AddHook { - addr: String, - }, - RemoveHook { - addr: String, - }, - /// Sets the active threshold to a new value. Only the - /// instantiator this contract (a DAO most likely) may call this - /// method. + /// Updates the contract configuration, namely unstaking duration. + /// Only callable by the DAO that initialized this voting contract. + UpdateConfig { duration: Option }, + /// Adds a hook which is called on staking / unstaking events. + /// Only callable by the DAO that initialized this voting contract. + AddHook { addr: String }, + /// Removes a hook which is called on staking / unstaking events. + /// Only callable by the DAO that initialized this voting contract. + RemoveHook { addr: String }, + /// Sets the active threshold to a new value. + /// Only callable by the DAO that initialized this voting contract. UpdateActiveThreshold { new_threshold: Option, }, diff --git a/contracts/voting/dao-voting-token-staked/src/contract.rs b/contracts/voting/dao-voting-token-staked/src/contract.rs index b9d692348..d47c164fe 100644 --- a/contracts/voting/dao-voting-token-staked/src/contract.rs +++ b/contracts/voting/dao-voting-token-staked/src/contract.rs @@ -2,8 +2,8 @@ use cosmwasm_std::entry_point; use cosmwasm_std::{ - coins, to_binary, BankMsg, BankQuery, Binary, Coin, CosmosMsg, Deps, DepsMut, Env, MessageInfo, - Order, Reply, Response, StdResult, SubMsg, Uint128, Uint256, WasmMsg, + coins, from_binary, to_binary, BankMsg, BankQuery, Binary, Coin, CosmosMsg, Deps, DepsMut, Env, + MessageInfo, Order, Reply, Response, StdResult, SubMsg, Uint128, Uint256, WasmMsg, }; use cw2::{get_contract_version, set_contract_version, ContractVersion}; use cw_controllers::ClaimsResponse; @@ -11,7 +11,9 @@ use cw_storage_plus::Bound; use cw_tokenfactory_issuer::msg::{ DenomUnit, ExecuteMsg as IssuerExecuteMsg, InstantiateMsg as IssuerInstantiateMsg, Metadata, }; -use cw_utils::{maybe_addr, must_pay, parse_reply_instantiate_data, Duration}; +use cw_utils::{ + maybe_addr, must_pay, parse_reply_execute_data, parse_reply_instantiate_data, Duration, +}; use dao_hooks::stake::{stake_hook_msgs, unstake_hook_msgs}; use dao_interface::state::ModuleInstantiateCallback; use dao_interface::voting::{ @@ -43,6 +45,7 @@ const MAX_LIMIT: u32 = 30; const DEFAULT_LIMIT: u32 = 10; const INSTANTIATE_TOKEN_FACTORY_ISSUER_REPLY_ID: u64 = 0; +const FACTORY_EXECUTE_REPLY_ID: u64 = 2; // We multiply by this when calculating needed power for being active // when using active threshold with percent @@ -122,6 +125,23 @@ pub fn instantiate( .add_attribute("token", "new_token") .add_submessage(issuer_instantiate_msg)) } + TokenInfo::Factory(binary) => match from_binary(&binary)? { + WasmMsg::Execute { + msg, contract_addr, .. + } => Ok(Response::new() + .add_attribute("action", "intantiate") + .add_submessage(SubMsg::reply_on_success( + WasmMsg::Execute { + contract_addr, + msg, + // TODO what to do with funds for fair burn? + // Need to pass them along to the factory + funds: vec![], + }, + FACTORY_EXECUTE_REPLY_ID, + ))), + _ => return Err(ContractError::UnsupportedFactoryMsg {}), + }, } } @@ -695,6 +715,21 @@ pub fn reply(deps: DepsMut, env: Env, msg: Reply) -> Result unreachable!(), } } + FACTORY_EXECUTE_REPLY_ID => { + let res = parse_reply_execute_data(msg)?; + + // TODO validate active threshold is set. Some contracts such as a minter, + // contract may not have any supply until tokens are minted. + + match res.data { + Some(data) => { + // TODO parse data and save token contract address / denom + unimplemented!() + } + // TODO better error + None => return Err(ContractError::Unauthorized {}), + } + } _ => Err(ContractError::UnknownReplyId { id: msg.id }), } } diff --git a/contracts/voting/dao-voting-token-staked/src/error.rs b/contracts/voting/dao-voting-token-staked/src/error.rs index 27bea213a..5239f3bd1 100644 --- a/contracts/voting/dao-voting-token-staked/src/error.rs +++ b/contracts/voting/dao-voting-token-staked/src/error.rs @@ -41,6 +41,9 @@ pub enum ContractError { #[error("Got a submessage reply with unknown id: {id}")] UnknownReplyId { id: u64 }, + #[error("Factory message must serialize to WasmMsg::Execute")] + UnsupportedFactoryMsg {}, + #[error("Amount being unstaked must be non-zero")] ZeroUnstake {}, } diff --git a/contracts/voting/dao-voting-token-staked/src/msg.rs b/contracts/voting/dao-voting-token-staked/src/msg.rs index e08123680..6d0581902 100644 --- a/contracts/voting/dao-voting-token-staked/src/msg.rs +++ b/contracts/voting/dao-voting-token-staked/src/msg.rs @@ -1,5 +1,5 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; -use cosmwasm_std::Uint128; +use cosmwasm_std::{Binary, Uint128}; use cw_tokenfactory_issuer::msg::DenomUnit; use cw_utils::Duration; use dao_dao_macros::{active_query, token_query, voting_module_query}; @@ -54,6 +54,9 @@ pub enum TokenInfo { /// Creates a new Token Factory token via the issue contract with the DAO automatically /// setup as admin and owner. New(NewTokenInfo), + /// Uses a factory pattern that must return the denom, optionally a Token Contract address. + /// The binary must serialize to a `WasmMsg::Execute` message. + Factory(Binary), } #[cw_serde] diff --git a/packages/dao-interface/src/state.rs b/packages/dao-interface/src/state.rs index 3a0841f9b..0bd445de3 100644 --- a/packages/dao-interface/src/state.rs +++ b/packages/dao-interface/src/state.rs @@ -1,5 +1,5 @@ use cosmwasm_schema::cw_serde; -use cosmwasm_std::{Addr, Binary, CosmosMsg, WasmMsg}; +use cosmwasm_std::{Addr, Binary, Coin, CosmosMsg, WasmMsg}; /// Top level config type for core module. #[cw_serde] @@ -60,6 +60,8 @@ pub struct ModuleInstantiateInfo { /// CosmWasm level admin of the instantiated contract. See: /// pub admin: Option, + /// Funds to be sent to the instantiated contract. + pub funds: Vec, /// Label for the instantiated contract. pub label: String, } @@ -73,7 +75,7 @@ impl ModuleInstantiateInfo { }), code_id: self.code_id, msg: self.msg, - funds: vec![], + funds: self.funds, label: self.label, } } @@ -98,6 +100,7 @@ mod tests { msg: to_binary("foo").unwrap(), admin: None, label: "bar".to_string(), + funds: vec![], }; assert_eq!( no_admin.into_wasm_msg(Addr::unchecked("ekez")), @@ -120,6 +123,7 @@ mod tests { addr: "core".to_string(), }), label: "bar".to_string(), + funds: vec![], }; assert_eq!( no_admin.into_wasm_msg(Addr::unchecked("ekez")), @@ -140,6 +144,7 @@ mod tests { msg: to_binary("foo").unwrap(), admin: Some(Admin::CoreModule {}), label: "bar".to_string(), + funds: vec![], }; assert_eq!( no_admin.into_wasm_msg(Addr::unchecked("ekez")), diff --git a/packages/dao-testing/src/helpers.rs b/packages/dao-testing/src/helpers.rs index ed95ea770..9bccd53c7 100644 --- a/packages/dao-testing/src/helpers.rs +++ b/packages/dao-testing/src/helpers.rs @@ -70,12 +70,14 @@ pub fn instantiate_with_cw20_balances_governance( }) .unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "DAO DAO voting module".to_string(), }, proposal_modules_instantiate_info: vec![ModuleInstantiateInfo { code_id: governance_code_id, msg: governance_instantiate, admin: Some(Admin::CoreModule {}), + funds: vec![], label: "DAO DAO governance module".to_string(), }], initial_items: None, @@ -153,6 +155,7 @@ pub fn instantiate_with_staked_balances_governance( }) .unwrap(), admin: None, + funds: vec![], label: "DAO DAO voting module".to_string(), }, proposal_modules_instantiate_info: vec![ModuleInstantiateInfo { @@ -160,6 +163,7 @@ pub fn instantiate_with_staked_balances_governance( label: "DAO DAO governance module.".to_string(), admin: Some(Admin::CoreModule {}), msg: governance_instantiate, + funds: vec![], }], initial_items: None, }; @@ -272,12 +276,14 @@ pub fn instantiate_with_staking_active_threshold( }) .unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "DAO DAO voting module".to_string(), }, proposal_modules_instantiate_info: vec![ModuleInstantiateInfo { code_id, msg: governance_instantiate, admin: Some(Admin::CoreModule {}), + funds: vec![], label: "DAO DAO governance module".to_string(), }], initial_items: None, @@ -344,12 +350,14 @@ pub fn instantiate_with_cw4_groups_governance( }) .unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "DAO DAO voting module".to_string(), }, proposal_modules_instantiate_info: vec![ModuleInstantiateInfo { code_id: core_code_id, msg: proposal_module_instantiate, admin: Some(Admin::CoreModule {}), + funds: vec![], label: "DAO DAO governance module".to_string(), }], initial_items: None, diff --git a/packages/dao-voting/src/pre_propose.rs b/packages/dao-voting/src/pre_propose.rs index 52bd3e7a5..7671425a3 100644 --- a/packages/dao-voting/src/pre_propose.rs +++ b/packages/dao-voting/src/pre_propose.rs @@ -116,6 +116,7 @@ mod tests { code_id: 42, msg: to_binary("foo").unwrap(), admin: None, + funds: vec![], label: "pre-propose-9000".to_string(), }, }; diff --git a/test-contracts/dao-proposal-hook-counter/src/tests.rs b/test-contracts/dao-proposal-hook-counter/src/tests.rs index cb3483a81..728baba65 100644 --- a/test-contracts/dao-proposal-hook-counter/src/tests.rs +++ b/test-contracts/dao-proposal-hook-counter/src/tests.rs @@ -121,12 +121,14 @@ fn instantiate_with_default_governance( }) .unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "DAO DAO voting module".to_string(), }, proposal_modules_instantiate_info: vec![ModuleInstantiateInfo { code_id, msg: to_binary(&msg).unwrap(), admin: Some(Admin::CoreModule {}), + funds: vec![], label: "DAO DAO governance module".to_string(), }], initial_items: None, diff --git a/test-contracts/dao-test-custom-factory/.cargo/config b/test-contracts/dao-test-custom-factory/.cargo/config new file mode 100644 index 000000000..336b618a1 --- /dev/null +++ b/test-contracts/dao-test-custom-factory/.cargo/config @@ -0,0 +1,4 @@ +[alias] +wasm = "build --release --target wasm32-unknown-unknown" +unit-test = "test --lib" +schema = "run --example schema" diff --git a/test-contracts/dao-test-custom-factory/.gitignore b/test-contracts/dao-test-custom-factory/.gitignore new file mode 100644 index 000000000..dfdaaa6bc --- /dev/null +++ b/test-contracts/dao-test-custom-factory/.gitignore @@ -0,0 +1,15 @@ +# Build results +/target + +# Cargo+Git helper file (https://github.com/rust-lang/cargo/blob/0.44.1/src/cargo/sources/git/utils.rs#L320-L327) +.cargo-ok + +# Text file backups +**/*.rs.bk + +# macOS +.DS_Store + +# IDEs +*.iml +.idea diff --git a/test-contracts/dao-test-custom-factory/Cargo.toml b/test-contracts/dao-test-custom-factory/Cargo.toml new file mode 100644 index 000000000..ebd21e686 --- /dev/null +++ b/test-contracts/dao-test-custom-factory/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "dao-test-custom-factory" +authors = ["ekez "] +description = "A proposal module that allows direct execution without voting." +edition = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +version = { workspace = true } + +[lib] +crate-type = ["cdylib", "rlib"] + +[features] +# for more explicit tests, cargo test --features=backtraces +backtraces = ["cosmwasm-std/backtraces"] +# use library feature to disable all instantiate/execute/query exports +library = [] + +[dependencies] +cosmwasm-std = { workspace = true } +cosmwasm-schema = { workspace = true } +cosmwasm-storage = { workspace = true } +cw-storage-plus = { workspace = true } +cw2 = { workspace = true } +thiserror = { workspace = true } +dao-dao-macros = { workspace = true } +dao-interface = { workspace = true } + +[dev-dependencies] +cw-multi-test = { workspace = true } diff --git a/test-contracts/dao-test-custom-factory/README b/test-contracts/dao-test-custom-factory/README new file mode 100644 index 000000000..1be9ff991 --- /dev/null +++ b/test-contracts/dao-test-custom-factory/README @@ -0,0 +1,2 @@ +# Test Custom Factory contract +Used for testing custom factories with `dao-voting-token-staked` and `dao-voting-cw721-staked`. diff --git a/test-contracts/dao-test-custom-factory/examples/schema.rs b/test-contracts/dao-test-custom-factory/examples/schema.rs new file mode 100644 index 000000000..f0a2ba4f4 --- /dev/null +++ b/test-contracts/dao-test-custom-factory/examples/schema.rs @@ -0,0 +1,10 @@ +use cosmwasm_schema::write_api; +use dao_test_custom_factory::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; + +fn main() { + write_api! { + instantiate: InstantiateMsg, + query: QueryMsg, + execute: ExecuteMsg, + } +} diff --git a/test-contracts/dao-test-custom-factory/schema/admin_response.json b/test-contracts/dao-test-custom-factory/schema/admin_response.json new file mode 100644 index 000000000..920968701 --- /dev/null +++ b/test-contracts/dao-test-custom-factory/schema/admin_response.json @@ -0,0 +1,6 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "AdminResponse", + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" +} diff --git a/test-contracts/dao-test-custom-factory/schema/dao_response.json b/test-contracts/dao-test-custom-factory/schema/dao_response.json new file mode 100644 index 000000000..9518ba3b5 --- /dev/null +++ b/test-contracts/dao-test-custom-factory/schema/dao_response.json @@ -0,0 +1,6 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "DaoResponse", + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" +} diff --git a/test-contracts/dao-test-custom-factory/schema/execute_msg.json b/test-contracts/dao-test-custom-factory/schema/execute_msg.json new file mode 100644 index 000000000..302a22adc --- /dev/null +++ b/test-contracts/dao-test-custom-factory/schema/execute_msg.json @@ -0,0 +1,487 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ExecuteMsg", + "oneOf": [ + { + "type": "object", + "required": [ + "execute" + ], + "properties": { + "execute": { + "type": "object", + "required": [ + "msgs" + ], + "properties": { + "msgs": { + "type": "array", + "items": { + "$ref": "#/definitions/CosmosMsg_for_Empty" + } + } + } + } + }, + "additionalProperties": false + } + ], + "definitions": { + "BankMsg": { + "description": "The message types of the bank module.\n\nSee https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/bank/v1beta1/tx.proto", + "oneOf": [ + { + "description": "Sends native tokens from the contract to the given address.\n\nThis is translated to a [MsgSend](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/bank/v1beta1/tx.proto#L19-L28). `from_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "send" + ], + "properties": { + "send": { + "type": "object", + "required": [ + "amount", + "to_address" + ], + "properties": { + "amount": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, + "to_address": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This will burn the given coins from the contract's account. There is no Cosmos SDK message that performs this, but it can be done by calling the bank keeper. Important if a contract controls significant token supply that must be retired.", + "type": "object", + "required": [ + "burn" + ], + "properties": { + "burn": { + "type": "object", + "required": [ + "amount" + ], + "properties": { + "amount": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + } + } + } + }, + "additionalProperties": false + } + ] + }, + "Binary": { + "description": "Binary is a wrapper around Vec to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "CosmosMsg_for_Empty": { + "oneOf": [ + { + "type": "object", + "required": [ + "bank" + ], + "properties": { + "bank": { + "$ref": "#/definitions/BankMsg" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "custom" + ], + "properties": { + "custom": { + "$ref": "#/definitions/Empty" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "staking" + ], + "properties": { + "staking": { + "$ref": "#/definitions/StakingMsg" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "distribution" + ], + "properties": { + "distribution": { + "$ref": "#/definitions/DistributionMsg" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "wasm" + ], + "properties": { + "wasm": { + "$ref": "#/definitions/WasmMsg" + } + }, + "additionalProperties": false + } + ] + }, + "DistributionMsg": { + "description": "The message types of the distribution module.\n\nSee https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto", + "oneOf": [ + { + "description": "This is translated to a [MsgSetWithdrawAddress](https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto#L29-L37). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "set_withdraw_address" + ], + "properties": { + "set_withdraw_address": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "description": "The `withdraw_address`", + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This is translated to a [[MsgWithdrawDelegatorReward](https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto#L42-L50). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "withdraw_delegator_reward" + ], + "properties": { + "withdraw_delegator_reward": { + "type": "object", + "required": [ + "validator" + ], + "properties": { + "validator": { + "description": "The `validator_address`", + "type": "string" + } + } + } + }, + "additionalProperties": false + } + ] + }, + "Empty": { + "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", + "type": "object" + }, + "StakingMsg": { + "description": "The message types of the staking module.\n\nSee https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto", + "oneOf": [ + { + "description": "This is translated to a [MsgDelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L81-L90). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "delegate" + ], + "properties": { + "delegate": { + "type": "object", + "required": [ + "amount", + "validator" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + }, + "validator": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This is translated to a [MsgUndelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L112-L121). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "undelegate" + ], + "properties": { + "undelegate": { + "type": "object", + "required": [ + "amount", + "validator" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + }, + "validator": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This is translated to a [MsgBeginRedelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L95-L105). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "redelegate" + ], + "properties": { + "redelegate": { + "type": "object", + "required": [ + "amount", + "dst_validator", + "src_validator" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + }, + "dst_validator": { + "type": "string" + }, + "src_validator": { + "type": "string" + } + } + } + }, + "additionalProperties": false + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + }, + "WasmMsg": { + "description": "The message types of the wasm module.\n\nSee https://github.com/CosmWasm/wasmd/blob/v0.14.0/x/wasm/internal/types/tx.proto", + "oneOf": [ + { + "description": "Dispatches a call to another contract at a known address (with known ABI).\n\nThis is translated to a [MsgExecuteContract](https://github.com/CosmWasm/wasmd/blob/v0.14.0/x/wasm/internal/types/tx.proto#L68-L78). `sender` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "execute" + ], + "properties": { + "execute": { + "type": "object", + "required": [ + "contract_addr", + "funds", + "msg" + ], + "properties": { + "contract_addr": { + "type": "string" + }, + "funds": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, + "msg": { + "description": "msg is the json-encoded ExecuteMsg struct (as raw Binary)", + "allOf": [ + { + "$ref": "#/definitions/Binary" + } + ] + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Instantiates a new contracts from previously uploaded Wasm code.\n\nThis is translated to a [MsgInstantiateContract](https://github.com/CosmWasm/wasmd/blob/v0.16.0-alpha1/x/wasm/internal/types/tx.proto#L47-L61). `sender` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "instantiate" + ], + "properties": { + "instantiate": { + "type": "object", + "required": [ + "code_id", + "funds", + "label", + "msg" + ], + "properties": { + "admin": { + "type": [ + "string", + "null" + ] + }, + "code_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "funds": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, + "label": { + "description": "A human-readbale label for the contract", + "type": "string" + }, + "msg": { + "description": "msg is the JSON-encoded InstantiateMsg struct (as raw Binary)", + "allOf": [ + { + "$ref": "#/definitions/Binary" + } + ] + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Migrates a given contracts to use new wasm code. Passes a MigrateMsg to allow us to customize behavior.\n\nOnly the contract admin (as defined in wasmd), if any, is able to make this call.\n\nThis is translated to a [MsgMigrateContract](https://github.com/CosmWasm/wasmd/blob/v0.14.0/x/wasm/internal/types/tx.proto#L86-L96). `sender` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "migrate" + ], + "properties": { + "migrate": { + "type": "object", + "required": [ + "contract_addr", + "msg", + "new_code_id" + ], + "properties": { + "contract_addr": { + "type": "string" + }, + "msg": { + "description": "msg is the json-encoded MigrateMsg struct that will be passed to the new code", + "allOf": [ + { + "$ref": "#/definitions/Binary" + } + ] + }, + "new_code_id": { + "description": "the code_id of the new logic to place in the given contract", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Sets a new admin (for migrate) on the given contract. Fails if this contract is not currently admin of the target contract.", + "type": "object", + "required": [ + "update_admin" + ], + "properties": { + "update_admin": { + "type": "object", + "required": [ + "admin", + "contract_addr" + ], + "properties": { + "admin": { + "type": "string" + }, + "contract_addr": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Clears the admin on the given contract, so no more migration possible. Fails if this contract is not currently admin of the target contract.", + "type": "object", + "required": [ + "clear_admin" + ], + "properties": { + "clear_admin": { + "type": "object", + "required": [ + "contract_addr" + ], + "properties": { + "contract_addr": { + "type": "string" + } + } + } + }, + "additionalProperties": false + } + ] + } + } +} diff --git a/test-contracts/dao-test-custom-factory/schema/info_response.json b/test-contracts/dao-test-custom-factory/schema/info_response.json new file mode 100644 index 000000000..a0516764e --- /dev/null +++ b/test-contracts/dao-test-custom-factory/schema/info_response.json @@ -0,0 +1,32 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "InfoResponse", + "type": "object", + "required": [ + "info" + ], + "properties": { + "info": { + "$ref": "#/definitions/ContractVersion" + } + }, + "definitions": { + "ContractVersion": { + "type": "object", + "required": [ + "contract", + "version" + ], + "properties": { + "contract": { + "description": "contract is the crate name of the implementing contract, eg. `crate:cw20-base` we will use other prefixes for other languages, and their standard global namespacing", + "type": "string" + }, + "version": { + "description": "version is any string that this implementation knows. It may be simple counter \"1\", \"2\". or semantic version on release tags \"v0.7.0\", or some custom feature flag list. the only code that needs to understand the version parsing is code that knows how to migrate from the given contract (and is tied to it's implementation somehow)", + "type": "string" + } + } + } + } +} diff --git a/test-contracts/dao-test-custom-factory/schema/instantiate_msg.json b/test-contracts/dao-test-custom-factory/schema/instantiate_msg.json new file mode 100644 index 000000000..8e3416dc4 --- /dev/null +++ b/test-contracts/dao-test-custom-factory/schema/instantiate_msg.json @@ -0,0 +1,13 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "InstantiateMsg", + "type": "object", + "required": [ + "root" + ], + "properties": { + "root": { + "type": "string" + } + } +} diff --git a/test-contracts/dao-test-custom-factory/schema/query_msg.json b/test-contracts/dao-test-custom-factory/schema/query_msg.json new file mode 100644 index 000000000..5262b4290 --- /dev/null +++ b/test-contracts/dao-test-custom-factory/schema/query_msg.json @@ -0,0 +1,42 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "QueryMsg", + "oneOf": [ + { + "type": "object", + "required": [ + "admin" + ], + "properties": { + "admin": { + "type": "object" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "dao" + ], + "properties": { + "dao": { + "type": "object" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "info" + ], + "properties": { + "info": { + "type": "object" + } + }, + "additionalProperties": false + } + ] +} diff --git a/test-contracts/dao-test-custom-factory/src/contract.rs b/test-contracts/dao-test-custom-factory/src/contract.rs new file mode 100644 index 000000000..56a1a275e --- /dev/null +++ b/test-contracts/dao-test-custom-factory/src/contract.rs @@ -0,0 +1,91 @@ +#[cfg(not(feature = "library"))] +use cosmwasm_std::entry_point; +use cosmwasm_std::{ + to_binary, Binary, CosmosMsg, Deps, DepsMut, Env, MessageInfo, Reply, Response, StdResult, + WasmMsg, +}; +use cw2::set_contract_version; + +use crate::{ + error::ContractError, + msg::{ExecuteMsg, InstantiateMsg, QueryMsg}, +}; + +const CONTRACT_NAME: &str = "CARGO_PKG_NAME"; +const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); + +const INSTANTIATE_BASE_MINTER_REPLY_ID: u64 = 1; + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn instantiate( + deps: DepsMut, + _env: Env, + info: MessageInfo, + msg: InstantiateMsg, +) -> Result { + set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; + + Ok(Response::new().add_attribute("method", "instantiate")) +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn execute( + deps: DepsMut, + env: Env, + info: MessageInfo, + msg: ExecuteMsg, +) -> Result { + match msg { + ExecuteMsg::StargazeBaseMinterFactory(msg) => { + execute_stargaze_base_minter_factory(deps, env, info, msg) + } + } +} + +pub fn execute_token_factory_factory( + deps: DepsMut, + env: Env, + info: MessageInfo, + msg: WasmMsg, +) -> Result { + unimplemented!() +} + +pub fn execute_stargaze_base_minter_factory( + deps: DepsMut, + env: Env, + info: MessageInfo, + msg: WasmMsg, +) -> Result { + // TODO query voting contract (the sender) for the DAO address + // TODO replace the Stargaze info to set the DAO address + + // TODO call base-factory to create minter + + // TODO this create a base-minter contract and a sg721 contract + + // in submsg reply, parse the response and save the contract address + unimplemented!() +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { + match msg { + QueryMsg::Info {} => query_info(deps), + } +} + +pub fn query_info(deps: Deps) -> StdResult { + let info = cw2::get_contract_version(deps.storage)?; + to_binary(&dao_interface::voting::InfoResponse { info }) +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn reply(deps: DepsMut, env: Env, msg: Reply) -> Result { + match msg.id { + INSTANTIATE_BASE_MINTER_REPLY_ID => { + unimplemented!() + } + _ => Err(ContractError::UnknownReplyId { id: msg.id }), + } +} diff --git a/test-contracts/dao-test-custom-factory/src/error.rs b/test-contracts/dao-test-custom-factory/src/error.rs new file mode 100644 index 000000000..ee02b079a --- /dev/null +++ b/test-contracts/dao-test-custom-factory/src/error.rs @@ -0,0 +1,14 @@ +use cosmwasm_std::StdError; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum ContractError { + #[error("{0}")] + Std(#[from] StdError), + + #[error("Unauthorized")] + Unauthorized {}, + + #[error("Got a submessage reply with unknown id: {id}")] + UnknownReplyId { id: u64 }, +} diff --git a/test-contracts/dao-test-custom-factory/src/lib.rs b/test-contracts/dao-test-custom-factory/src/lib.rs new file mode 100644 index 000000000..3915b791e --- /dev/null +++ b/test-contracts/dao-test-custom-factory/src/lib.rs @@ -0,0 +1,5 @@ +pub mod contract; +mod error; +pub mod msg; + +pub use crate::error::ContractError; diff --git a/test-contracts/dao-test-custom-factory/src/msg.rs b/test-contracts/dao-test-custom-factory/src/msg.rs new file mode 100644 index 000000000..1af086b5b --- /dev/null +++ b/test-contracts/dao-test-custom-factory/src/msg.rs @@ -0,0 +1,17 @@ +use cosmwasm_schema::{cw_serde, QueryResponses}; +use cosmwasm_std::WasmMsg; + +#[cw_serde] +pub struct InstantiateMsg {} + +#[cw_serde] +pub enum ExecuteMsg { + StargazeBaseMinterFactory(WasmMsg), +} + +#[cw_serde] +#[derive(QueryResponses)] +pub enum QueryMsg { + #[returns(dao_interface::voting::InfoResponse)] + Info {}, +} From d6ecac695987d7143d093e804b390982d35fd4e9 Mon Sep 17 00:00:00 2001 From: Jake Hartnell Date: Tue, 12 Sep 2023 20:38:19 -0700 Subject: [PATCH 02/30] Add FactoryCallbacks, build out test-contract --- Cargo.lock | 432 +++++++++++++++++- Cargo.toml | 2 + .../voting/dao-voting-token-staked/Cargo.toml | 3 +- .../dao-voting-token-staked/src/contract.rs | 27 +- .../voting/dao-voting-token-staked/src/msg.rs | 6 + .../src/tests/test_tube/integration_tests.rs | 104 ++++- .../src/tests/test_tube/test_env.rs | 15 +- packages/dao-testing/Cargo.toml | 1 + .../src/test_tube/dao_test_custom_factory.rs | 129 ++++++ packages/dao-testing/src/test_tube/mod.rs | 3 + .../dao-test-custom-factory/Cargo.toml | 10 +- .../dao-test-custom-factory/src/contract.rs | 148 +++++- .../dao-test-custom-factory/src/error.rs | 4 + .../dao-test-custom-factory/src/lib.rs | 1 + .../dao-test-custom-factory/src/msg.rs | 2 + 15 files changed, 857 insertions(+), 30 deletions(-) create mode 100644 packages/dao-testing/src/test_tube/dao_test_custom_factory.rs diff --git a/Cargo.lock b/Cargo.lock index 3cb7af2a4..8b5375624 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -43,6 +43,12 @@ version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + [[package]] name = "assert_matches" version = "1.5.0" @@ -159,6 +165,25 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base-factory" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de00b1bd4a381194ccaa4a790390d2599a56eaf50f5b5f76f4a975958188390" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 0.16.0", + "cw-utils 0.16.0", + "cw2 0.16.0", + "schemars", + "serde", + "sg-std 2.4.0", + "sg1", + "sg2", + "thiserror", +] + [[package]] name = "base16ct" version = "0.1.1" @@ -242,6 +267,28 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +[[package]] +name = "bitvec" +version = "0.17.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41262f11d771fd4a61aa3ce019fca363b4b6c282fca9da2a31186d3965a47a5c" +dependencies = [ + "either", + "radium 0.3.0", +] + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium 0.7.0", + "tap", + "wyz", +] + [[package]] name = "block-buffer" version = "0.9.0" @@ -289,6 +336,51 @@ dependencies = [ "serde_yaml", ] +[[package]] +name = "borsh" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4114279215a005bc675e386011e594e1d9b800918cea18fcadadcce864a2046b" +dependencies = [ + "borsh-derive", + "hashbrown 0.12.3", +] + +[[package]] +name = "borsh-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0754613691538d51f329cce9af41d7b7ca150bc973056f1156611489475f54f7" +dependencies = [ + "borsh-derive-internal", + "borsh-schema-derive-internal", + "proc-macro-crate", + "proc-macro2", + "syn 1.0.109", +] + +[[package]] +name = "borsh-derive-internal" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afb438156919598d2c7bad7e1c0adf3d26ed3840dbc010db1a882a65583ca2fb" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "borsh-schema-derive-internal" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634205cc43f74a1b9046ef87c4540ebda95696ec0f315024860cad7c5b0f5ccd" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "bs58" version = "0.4.0" @@ -304,6 +396,28 @@ version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" +[[package]] +name = "bytecheck" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6372023ac861f6e6dc89c8344a8f398fb42aaba2b5dbc649ca0c0e9dbcb627" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7ec4c6f261935ad534c0c22dbef2201b45918860eb1c574b972bd213a76af61" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "byteorder" version = "1.4.3" @@ -1971,11 +2085,19 @@ dependencies = [ "cosmwasm-std", "cosmwasm-storage", "cw-multi-test", + "cw-ownable", "cw-storage-plus 1.1.0", + "cw-tokenfactory-issuer", + "cw-utils 1.0.1", "cw2 1.1.0", "dao-dao-macros", "dao-interface", + "dao-voting-cw721-staked", + "dao-voting-token-staked", + "sg721-base", "thiserror", + "vending-factory", + "vending-minter", ] [[package]] @@ -2005,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", @@ -2152,8 +2275,8 @@ dependencies = [ "dao-testing", "dao-voting 2.2.0", "sg-multi-test", - "sg-std", - "sg721", + "sg-std 3.2.0", + "sg721 3.1.0", "sg721-base", "thiserror", ] @@ -2180,6 +2303,7 @@ dependencies = [ "dao-interface", "dao-proposal-hook-counter", "dao-proposal-single", + "dao-test-custom-factory", "dao-testing", "dao-voting 2.2.0", "osmosis-std", @@ -2482,6 +2606,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8cbd1169bd7b4a0a20d92b9af7a7e0422888bd38a6f5ec29c1fd8c1558a272e" +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + [[package]] name = "futures" version = "0.3.28" @@ -3395,6 +3525,15 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "proc-macro-crate" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" +dependencies = [ + "toml", +] + [[package]] name = "proc-macro2" version = "1.0.66" @@ -3471,6 +3610,26 @@ dependencies = [ "prost 0.11.9", ] +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "quote" version = "1.0.33" @@ -3480,6 +3639,18 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "radium" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "def50a86306165861203e7f84ecffbbdfdea79f0e51039b33de1e952358c47ac" + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + [[package]] name = "rand" version = "0.8.5" @@ -3516,6 +3687,15 @@ dependencies = [ "getrandom", ] +[[package]] +name = "rand_xoshiro" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" +dependencies = [ + "rand_core 0.6.4", +] + [[package]] name = "regex" version = "1.9.5" @@ -3545,6 +3725,15 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" +[[package]] +name = "rend" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581008d2099240d37fb08d77ad713bcaec2c4d89d50b5b21a8bb1996bbab68ab" +dependencies = [ + "bytecheck", +] + [[package]] name = "rfc6979" version = "0.3.1" @@ -3601,6 +3790,34 @@ dependencies = [ "opaque-debug", ] +[[package]] +name = "rkyv" +version = "0.7.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0200c8230b013893c0b2d6213d6ec64ed2b9be2e0e016682b7224ff82cff5c58" +dependencies = [ + "bitvec 1.0.1", + "bytecheck", + "hashbrown 0.12.3", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", + "tinyvec", + "uuid 1.4.1", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2e06b915b5c230a17d7a736d1e2e63ee753c256a8614ef3f5147b13a4f5541d" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "ron" version = "0.7.1" @@ -3622,6 +3839,22 @@ dependencies = [ "ordered-multimap", ] +[[package]] +name = "rust_decimal" +version = "1.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c4216490d5a413bc6d10fa4742bd7d4955941d062c0ef873141d6b0e7b30fd" +dependencies = [ + "arrayvec", + "borsh", + "bytes", + "num-traits", + "rand", + "rkyv", + "serde", + "serde_json", +] + [[package]] name = "rustc-demangle" version = "0.1.23" @@ -3736,6 +3969,12 @@ dependencies = [ "untrusted", ] +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + [[package]] name = "sec1" version = "0.3.0" @@ -3897,7 +4136,22 @@ dependencies = [ "cw-multi-test", "schemars", "serde", - "sg-std", + "sg-std 3.2.0", +] + +[[package]] +name = "sg-std" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc9b4ab6090f777940d3277632b1a3fff89f5a52191443a19da7cd99e3845041" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-utils 0.16.0", + "cw721 0.16.0", + "schemars", + "serde", + "thiserror", ] [[package]] @@ -3915,6 +4169,81 @@ dependencies = [ "thiserror", ] +[[package]] +name = "sg-whitelist" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a88e24759094f8927913efa92d3679011a40fa30c4e29ea7c3bc6cf8a3c514bb" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 0.16.0", + "cw-utils 0.16.0", + "cw2 0.16.0", + "rust_decimal", + "schemars", + "serde", + "sg-std 2.4.0", + "sg1", + "thiserror", +] + +[[package]] +name = "sg1" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c691fbf35f3deb9e2944b0a82db7280798a1b4b17c7e9fe22b78d89c5276c8e" +dependencies = [ + "cosmwasm-std", + "cw-utils 0.16.0", + "serde", + "sg-std 2.4.0", + "thiserror", +] + +[[package]] +name = "sg2" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0accfb1855c12e486b5c1afbd858652fabdf00bdacd221a9f96121c1d1955a62" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 0.16.0", + "cw-utils 0.16.0", + "schemars", + "serde", + "sg721 2.4.0", + "thiserror", +] + +[[package]] +name = "sg4" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18da9e17654454380aa81f92cbc650098f28d657932770fda1c911747eb95ce4" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "schemars", + "serde", + "thiserror", +] + +[[package]] +name = "sg721" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b37ea61753635e1de01a16947784ddd5ba8e256fa403be6246b2d88578914fb2" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-utils 0.16.0", + "cw721-base 0.16.0", + "serde", + "thiserror", +] + [[package]] name = "sg721" version = "3.1.0" @@ -3945,8 +4274,8 @@ dependencies = [ "cw721 0.18.0", "cw721-base 0.18.0", "serde", - "sg-std", - "sg721", + "sg-std 3.2.0", + "sg721 3.1.0", "thiserror", "url", ] @@ -4002,6 +4331,16 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" +[[package]] +name = "shuffle" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fecbe806be0726e22a6192ce0ec2f90e0798946ae0cbc2c10c7a3c68cae4eb2" +dependencies = [ + "bitvec 0.17.4", + "rand", +] + [[package]] name = "signature" version = "1.6.4" @@ -4022,6 +4361,12 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "simdutf8" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" + [[package]] name = "slab" version = "0.4.9" @@ -4184,6 +4529,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "tendermint" version = "0.23.9" @@ -4357,7 +4708,7 @@ dependencies = [ "tokio", "tracing", "url", - "uuid", + "uuid 0.8.2", "walkdir", ] @@ -4391,7 +4742,7 @@ dependencies = [ "tokio", "tracing", "url", - "uuid", + "uuid 0.8.2", "walkdir", ] @@ -4796,6 +5147,64 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" +[[package]] +name = "uuid" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" + +[[package]] +name = "vending-factory" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cf5cc560b6d037161d5e4cd3bbe794373c685afb2f30f739984bf135f4abd41" +dependencies = [ + "base-factory", + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 0.16.0", + "cw-utils 0.16.0", + "cw2 0.16.0", + "schemars", + "serde", + "sg-std 2.4.0", + "sg1", + "sg2", + "sg721 2.4.0", + "thiserror", +] + +[[package]] +name = "vending-minter" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "999a8d302970bed638c09c9c116e7ab41031089a4e707796e7ca9c3ef7b66ed5" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 0.16.0", + "cw-utils 0.16.0", + "cw2 0.16.0", + "cw721 0.16.0", + "cw721-base 0.16.0", + "rand_core 0.6.4", + "rand_xoshiro", + "schemars", + "semver", + "serde", + "sg-std 2.4.0", + "sg-whitelist", + "sg1", + "sg2", + "sg4", + "sg721 2.4.0", + "sha2 0.10.7", + "shuffle", + "thiserror", + "url", + "vending-factory", +] + [[package]] name = "version_check" version = "0.9.4" @@ -5043,6 +5452,15 @@ dependencies = [ "thiserror", ] +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + [[package]] name = "yaml-rust" version = "0.4.5" diff --git a/Cargo.toml b/Cargo.toml index 505d3739b..2bca65d85 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,6 +72,8 @@ syn = { version = "1.0", features = ["derive"] } test-context = "0.1" thiserror = { version = "1.0" } token-bindings = "0.11.0" +vending-factory = "2.4.0" +vending-minter = "2.4.0" wynd-utils = "0.4" # One commit ahead of version 0.3.0. Allows initialization with an diff --git a/contracts/voting/dao-voting-token-staked/Cargo.toml b/contracts/voting/dao-voting-token-staked/Cargo.toml index 60d2b5c90..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"] } @@ -46,6 +46,7 @@ cw-multi-test = { git = "https://github.com/CosmWasm/cw-multi-test.git", rev = " cw-tokenfactory-issuer = { workspace = true } dao-proposal-single = { workspace = true } dao-proposal-hook-counter = { workspace = true } +dao-test-custom-factory = { workspace = true } dao-testing = { workspace = true, features = ["test-tube"] } osmosis-std = { workpsace = true } osmosis-test-tube = { workspace = true } diff --git a/contracts/voting/dao-voting-token-staked/src/contract.rs b/contracts/voting/dao-voting-token-staked/src/contract.rs index d47c164fe..0bba24c7d 100644 --- a/contracts/voting/dao-voting-token-staked/src/contract.rs +++ b/contracts/voting/dao-voting-token-staked/src/contract.rs @@ -29,7 +29,7 @@ use dao_voting::{ use crate::error::ContractError; use crate::msg::{ - DenomResponse, ExecuteMsg, GetHooksResponse, InitialBalance, InstantiateMsg, + DenomResponse, ExecuteMsg, FactoryCallback, GetHooksResponse, InitialBalance, InstantiateMsg, ListStakersResponse, MigrateMsg, NewTokenInfo, QueryMsg, StakerBalanceResponse, TokenInfo, }; use crate::state::{ @@ -716,15 +716,28 @@ pub fn reply(deps: DepsMut, env: Env, msg: Reply) -> Result { + // Parse reply let res = parse_reply_execute_data(msg)?; - - // TODO validate active threshold is set. Some contracts such as a minter, - // contract may not have any supply until tokens are minted. - match res.data { Some(data) => { - // TODO parse data and save token contract address / denom - unimplemented!() + // Parse info from the callback, this will fail + // if incorrectly formatted. + let info: FactoryCallback = from_binary(&data)?; + + // Save Denom + DENOM.save(deps.storage, &info.denom)?; + + // Save token issuer contract if one is returned + if let Some(token_contract) = info.token_contract { + TOKEN_ISSUER_CONTRACT + .save(deps.storage, &deps.api.addr_validate(&token_contract)?)?; + } + + // TODO validate active threshold is set? Some contracts such as a minter, + // contract may not have any supply until tokens are minted. + + // TODO also include token contract + Ok(Response::new().add_attribute("denom", info.denom)) } // TODO better error None => return Err(ContractError::Unauthorized {}), diff --git a/contracts/voting/dao-voting-token-staked/src/msg.rs b/contracts/voting/dao-voting-token-staked/src/msg.rs index 6d0581902..500af96c6 100644 --- a/contracts/voting/dao-voting-token-staked/src/msg.rs +++ b/contracts/voting/dao-voting-token-staked/src/msg.rs @@ -138,3 +138,9 @@ pub struct DenomResponse { pub struct GetHooksResponse { pub hooks: Vec, } + +#[cw_serde] +pub struct FactoryCallback { + pub denom: String, + pub token_contract: Option, +} 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; diff --git a/test-contracts/dao-test-custom-factory/Cargo.toml b/test-contracts/dao-test-custom-factory/Cargo.toml index ebd21e686..02a16a9c7 100644 --- a/test-contracts/dao-test-custom-factory/Cargo.toml +++ b/test-contracts/dao-test-custom-factory/Cargo.toml @@ -20,11 +20,19 @@ library = [] cosmwasm-std = { workspace = true } cosmwasm-schema = { workspace = true } cosmwasm-storage = { workspace = true } -cw-storage-plus = { workspace = true } cw2 = { workspace = true } +cw-ownable = { workspace = true } +cw-storage-plus = { workspace = true } +cw-utils = { workspace = true } thiserror = { workspace = true } dao-dao-macros = { workspace = true } dao-interface = { workspace = true } +dao-voting-cw721-staked = { workspace = true, features = ["library"] } +dao-voting-token-staked = { workspace = true, features = ["library"] } +vending-factory = { workspace = true, features = ["library"] } +vending-minter = { workspace = true, features = ["library"] } +cw-tokenfactory-issuer = { workspace = true, features = ["library"] } +sg721-base = { workspace = true, features = ["library"] } [dev-dependencies] cw-multi-test = { workspace = true } diff --git a/test-contracts/dao-test-custom-factory/src/contract.rs b/test-contracts/dao-test-custom-factory/src/contract.rs index 56a1a275e..69e2a67bf 100644 --- a/test-contracts/dao-test-custom-factory/src/contract.rs +++ b/test-contracts/dao-test-custom-factory/src/contract.rs @@ -1,20 +1,32 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - to_binary, Binary, CosmosMsg, Deps, DepsMut, Env, MessageInfo, Reply, Response, StdResult, - WasmMsg, + to_binary, Addr, Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response, StdResult, SubMsg, + Uint128, WasmMsg, }; use cw2::set_contract_version; +use cw_storage_plus::Item; +use cw_tokenfactory_issuer::msg::{ + ExecuteMsg as IssuerExecuteMsg, InstantiateMsg as IssuerInstantiateMsg, +}; +use cw_utils::parse_reply_instantiate_data; +use dao_voting_token_staked::msg::{ + FactoryCallback, InitialBalance, NewTokenInfo, QueryMsg as TokenVotingQueryMsg, +}; use crate::{ error::ContractError, msg::{ExecuteMsg, InstantiateMsg, QueryMsg}, }; -const CONTRACT_NAME: &str = "CARGO_PKG_NAME"; +const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); const INSTANTIATE_BASE_MINTER_REPLY_ID: u64 = 1; +const INSTANTIATE_ISSUER_REPLY_ID: u64 = 2; + +const DAO: Item = Item::new("dao"); +const TOKEN_INFO: Item = Item::new("token_info"); #[cfg_attr(not(feature = "library"), entry_point)] pub fn instantiate( @@ -39,23 +51,54 @@ pub fn execute( ExecuteMsg::StargazeBaseMinterFactory(msg) => { execute_stargaze_base_minter_factory(deps, env, info, msg) } + ExecuteMsg::TokenFactoryFactory(token) => { + execute_token_factory_factory(deps, env, info, token) + } } } +/// An example factory that instantiates a cw_tokenfactory_issuer contract +/// A more realistic example would be something like a DeFi Pool or Augmented +/// bonding curve. pub fn execute_token_factory_factory( deps: DepsMut, - env: Env, + _env: Env, info: MessageInfo, - msg: WasmMsg, + token: NewTokenInfo, ) -> Result { - unimplemented!() + // Query for DAO + let dao: Addr = deps + .querier + .query_wasm_smart(info.sender, &TokenVotingQueryMsg::Dao {})?; + + // Save DAO and TOKEN_INFO for use in replies + DAO.save(deps.storage, &dao)?; + TOKEN_INFO.save(deps.storage, &token)?; + + // Instantiate new contract, further setup is handled in the + // SubMsg reply. + let msg = SubMsg::reply_on_success( + WasmMsg::Instantiate { + admin: Some(dao.to_string()), + code_id: token.token_issuer_code_id, + msg: to_binary(&IssuerInstantiateMsg::NewToken { + subdenom: token.subdenom, + })?, + funds: vec![], + label: "cw_tokenfactory_issuer".to_string(), + }, + INSTANTIATE_ISSUER_REPLY_ID, + ); + + Ok(Response::new().add_submessage(msg)) } +/// Example Stargaze factory. pub fn execute_stargaze_base_minter_factory( - deps: DepsMut, - env: Env, - info: MessageInfo, - msg: WasmMsg, + _deps: DepsMut, + _env: Env, + _info: MessageInfo, + _msg: WasmMsg, ) -> Result { // TODO query voting contract (the sender) for the DAO address // TODO replace the Stargaze info to set the DAO address @@ -86,6 +129,91 @@ pub fn reply(deps: DepsMut, env: Env, msg: Reply) -> Result { unimplemented!() } + INSTANTIATE_ISSUER_REPLY_ID => { + // Load DAO address and TOKEN_INFO + let dao = DAO.load(deps.storage)?; + let token = TOKEN_INFO.load(deps.storage)?; + + // Parse issuer address from instantiate reply + let issuer_addr = parse_reply_instantiate_data(msg)?.contract_address; + + // Format the denom + let denom = format!("factory/{}/{}", &issuer_addr, token.subdenom); + + let initial_supply = token + .initial_balances + .iter() + .fold(Uint128::zero(), |previous, new_balance| { + previous + new_balance.amount + }); + let total_supply = initial_supply + token.initial_dao_balance.unwrap_or_default(); + + // TODO query active threshold and validate the count + + // Msgs to be executed to finalize setup + let mut msgs: Vec = vec![]; + + // Grant an allowance to mint the initial supply + msgs.push(WasmMsg::Execute { + contract_addr: issuer_addr.clone(), + msg: to_binary(&IssuerExecuteMsg::SetMinterAllowance { + address: env.contract.address.to_string(), + allowance: total_supply, + })?, + funds: vec![], + }); + + // Call issuer contract to mint tokens for initial balances + token + .initial_balances + .iter() + .for_each(|b: &InitialBalance| { + msgs.push(WasmMsg::Execute { + contract_addr: issuer_addr.clone(), + msg: to_binary(&IssuerExecuteMsg::Mint { + to_address: b.address.clone(), + amount: b.amount, + }) + .unwrap_or_default(), + funds: vec![], + }); + }); + + // Add initial DAO balance to initial_balances if nonzero. + if let Some(initial_dao_balance) = token.initial_dao_balance { + if !initial_dao_balance.is_zero() { + msgs.push(WasmMsg::Execute { + contract_addr: issuer_addr.clone(), + msg: to_binary(&IssuerExecuteMsg::Mint { + to_address: dao.to_string(), + amount: initial_dao_balance, + })?, + funds: vec![], + }); + } + } + + // Begin update issuer contract owner to be the DAO, this is a + // two-step ownership transfer. + // DAO must pass a prop to Accept Ownership + msgs.push(WasmMsg::Execute { + contract_addr: issuer_addr.clone(), + msg: to_binary(&IssuerExecuteMsg::UpdateOwnership( + cw_ownable::Action::TransferOwnership { + new_owner: dao.to_string(), + expiry: None, + }, + ))?, + funds: vec![], + }); + + Ok(Response::new() + .add_messages(msgs) + .set_data(to_binary(&FactoryCallback { + denom, + token_contract: Some(issuer_addr.to_string()), + })?)) + } _ => Err(ContractError::UnknownReplyId { id: msg.id }), } } diff --git a/test-contracts/dao-test-custom-factory/src/error.rs b/test-contracts/dao-test-custom-factory/src/error.rs index ee02b079a..dbac5032a 100644 --- a/test-contracts/dao-test-custom-factory/src/error.rs +++ b/test-contracts/dao-test-custom-factory/src/error.rs @@ -1,4 +1,5 @@ use cosmwasm_std::StdError; +use cw_utils::ParseReplyError; use thiserror::Error; #[derive(Error, Debug)] @@ -6,6 +7,9 @@ pub enum ContractError { #[error("{0}")] Std(#[from] StdError), + #[error(transparent)] + ParseReplyError(#[from] ParseReplyError), + #[error("Unauthorized")] Unauthorized {}, diff --git a/test-contracts/dao-test-custom-factory/src/lib.rs b/test-contracts/dao-test-custom-factory/src/lib.rs index 3915b791e..07a196052 100644 --- a/test-contracts/dao-test-custom-factory/src/lib.rs +++ b/test-contracts/dao-test-custom-factory/src/lib.rs @@ -3,3 +3,4 @@ mod error; pub mod msg; pub use crate::error::ContractError; +pub use dao_voting_token_staked::msg::{InitialBalance, NewTokenInfo}; diff --git a/test-contracts/dao-test-custom-factory/src/msg.rs b/test-contracts/dao-test-custom-factory/src/msg.rs index 1af086b5b..5586455bd 100644 --- a/test-contracts/dao-test-custom-factory/src/msg.rs +++ b/test-contracts/dao-test-custom-factory/src/msg.rs @@ -1,5 +1,6 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; use cosmwasm_std::WasmMsg; +use dao_voting_token_staked::msg::NewTokenInfo; #[cw_serde] pub struct InstantiateMsg {} @@ -7,6 +8,7 @@ pub struct InstantiateMsg {} #[cw_serde] pub enum ExecuteMsg { StargazeBaseMinterFactory(WasmMsg), + TokenFactoryFactory(NewTokenInfo), } #[cw_serde] From ebd07573271668908ce8844b7c238602889bc06a Mon Sep 17 00:00:00 2001 From: Jake Hartnell Date: Tue, 12 Sep 2023 21:40:38 -0700 Subject: [PATCH 03/30] Factory callback for NFT contracts --- .../dao-voting-cw721-staked/src/contract.rs | 23 ++++++++++++++----- .../voting/dao-voting-cw721-staked/src/msg.rs | 5 ++++ .../dao-voting-token-staked/src/contract.rs | 10 ++++---- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/contracts/voting/dao-voting-cw721-staked/src/contract.rs b/contracts/voting/dao-voting-cw721-staked/src/contract.rs index e8f97ab76..4cfd0d850 100644 --- a/contracts/voting/dao-voting-cw721-staked/src/contract.rs +++ b/contracts/voting/dao-voting-cw721-staked/src/contract.rs @@ -13,7 +13,7 @@ use dao_hooks::nft_stake::{stake_nft_hook_msgs, unstake_nft_hook_msgs}; use dao_interface::voting::IsActiveResponse; use dao_voting::threshold::{ActiveThreshold, ActiveThresholdResponse}; -use crate::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, NftContract, QueryMsg}; +use crate::msg::{ExecuteMsg, FactoryCallback, InstantiateMsg, MigrateMsg, NftContract, QueryMsg}; use crate::state::{ register_staked_nft, register_unstaked_nfts, Config, ACTIVE_THRESHOLD, CONFIG, DAO, HOOKS, INITIAL_NFTS, MAX_CLAIMS, NFT_BALANCES, NFT_CLAIMS, STAKED_NFTS_PER_OWNER, TOTAL_STAKED_NFTS, @@ -736,13 +736,24 @@ pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result { let res = parse_reply_execute_data(msg)?; - // TODO validate active threshold is set. Some contracts such as a minter, - // contract may not have any supply until tokens are minted. - match res.data { Some(data) => { - // TODO parse data and save token contract address / denom - unimplemented!() + // TODO validate active threshold is set? Some contracts such as a minter, + // contract may not have any supply until tokens are minted. + + let mut config = CONFIG.load(deps.storage)?; + + // Parse info from the callback, this will fail + // if incorrectly formatted. + let info: FactoryCallback = from_binary(&data)?; + + // TODO validate that this is an NFT with a query + + // Update NFT contract + config.nft_address = deps.api.addr_validate(&info.nft_contract)?; + CONFIG.save(deps.storage, &config)?; + + Ok(Response::new().add_attribute("nft_contract", info.nft_contract)) } // TODO better error None => return Err(ContractError::Unauthorized {}), diff --git a/contracts/voting/dao-voting-cw721-staked/src/msg.rs b/contracts/voting/dao-voting-cw721-staked/src/msg.rs index 7fb38d79d..dc1eaa22b 100644 --- a/contracts/voting/dao-voting-cw721-staked/src/msg.rs +++ b/contracts/voting/dao-voting-cw721-staked/src/msg.rs @@ -94,3 +94,8 @@ pub enum QueryMsg { #[cw_serde] pub struct MigrateMsg {} + +#[cw_serde] +pub struct FactoryCallback { + pub nft_contract: String, +} diff --git a/contracts/voting/dao-voting-token-staked/src/contract.rs b/contracts/voting/dao-voting-token-staked/src/contract.rs index 0bba24c7d..662c10053 100644 --- a/contracts/voting/dao-voting-token-staked/src/contract.rs +++ b/contracts/voting/dao-voting-token-staked/src/contract.rs @@ -130,6 +130,7 @@ pub fn instantiate( msg, contract_addr, .. } => Ok(Response::new() .add_attribute("action", "intantiate") + .add_attribute("token", "custom_factory") .add_submessage(SubMsg::reply_on_success( WasmMsg::Execute { contract_addr, @@ -707,8 +708,8 @@ pub fn reply(deps: DepsMut, env: Env, msg: Reply) -> Result Result Result return Err(ContractError::Unauthorized {}), From 29f5781d6ada89986c241efc236e6e132aecdc56 Mon Sep 17 00:00:00 2001 From: Jake Hartnell Date: Thu, 14 Sep 2023 14:51:13 -0700 Subject: [PATCH 04/30] Remove unneeded files, generate Schema --- .../dao-dao-core/schema/dao-dao-core.json | 62 +++ .../dao-migrator/schema/dao-migrator.json | 58 +++ .../schema/dao-proposal-multiple.json | 62 +++ .../schema/dao-proposal-single.json | 58 +++ .../schema/dao-voting-cw721-staked.json | 23 +- .../schema/dao-voting-token-staked.json | 17 + .../dao-test-custom-factory/.gitignore | 15 - .../schema/admin_response.json | 6 - .../schema/dao_response.json | 6 - .../schema/execute_msg.json | 487 ------------------ .../schema/info_response.json | 32 -- .../schema/instantiate_msg.json | 13 - .../schema/query_msg.json | 42 -- 13 files changed, 278 insertions(+), 603 deletions(-) delete mode 100644 test-contracts/dao-test-custom-factory/.gitignore delete mode 100644 test-contracts/dao-test-custom-factory/schema/admin_response.json delete mode 100644 test-contracts/dao-test-custom-factory/schema/dao_response.json delete mode 100644 test-contracts/dao-test-custom-factory/schema/execute_msg.json delete mode 100644 test-contracts/dao-test-custom-factory/schema/info_response.json delete mode 100644 test-contracts/dao-test-custom-factory/schema/instantiate_msg.json delete mode 100644 test-contracts/dao-test-custom-factory/schema/query_msg.json diff --git a/contracts/dao-dao-core/schema/dao-dao-core.json b/contracts/dao-dao-core/schema/dao-dao-core.json index c2e9cf422..56e65cdab 100644 --- a/contracts/dao-dao-core/schema/dao-dao-core.json +++ b/contracts/dao-dao-core/schema/dao-dao-core.json @@ -125,6 +125,21 @@ "description": "Binary is a wrapper around Vec to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec. See also .", "type": "string" }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, "InitialItem": { "description": "Information about an item to be stored in the items list.", "type": "object", @@ -149,6 +164,7 @@ "type": "object", "required": [ "code_id", + "funds", "label", "msg" ], @@ -170,6 +186,13 @@ "format": "uint64", "minimum": 0.0 }, + "funds": { + "description": "Funds to be sent to the instantiated contract.", + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, "label": { "description": "Label for the instantiated contract.", "type": "string" @@ -184,6 +207,10 @@ } }, "additionalProperties": false + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" } } }, @@ -1159,6 +1186,7 @@ "type": "object", "required": [ "code_id", + "funds", "label", "msg" ], @@ -1180,6 +1208,13 @@ "format": "uint64", "minimum": 0.0 }, + "funds": { + "description": "Funds to be sent to the instantiated contract.", + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, "label": { "description": "Label for the instantiated contract.", "type": "string" @@ -2014,6 +2049,21 @@ "description": "Binary is a wrapper around Vec to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec. See also .", "type": "string" }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, "MigrateParams": { "type": "object", "required": [ @@ -2096,6 +2146,7 @@ "type": "object", "required": [ "code_id", + "funds", "label", "msg" ], @@ -2117,6 +2168,13 @@ "format": "uint64", "minimum": 0.0 }, + "funds": { + "description": "Funds to be sent to the instantiated contract.", + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, "label": { "description": "Label for the instantiated contract.", "type": "string" @@ -2209,6 +2267,10 @@ }, "additionalProperties": false }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + }, "V1CodeIds": { "type": "object", "required": [ diff --git a/contracts/external/dao-migrator/schema/dao-migrator.json b/contracts/external/dao-migrator/schema/dao-migrator.json index db060770c..ad401a0a8 100644 --- a/contracts/external/dao-migrator/schema/dao-migrator.json +++ b/contracts/external/dao-migrator/schema/dao-migrator.json @@ -110,6 +110,21 @@ "description": "Binary is a wrapper around Vec to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec. See also .", "type": "string" }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", "type": "string" @@ -153,6 +168,7 @@ "type": "object", "required": [ "code_id", + "funds", "label", "msg" ], @@ -174,6 +190,13 @@ "format": "uint64", "minimum": 0.0 }, + "funds": { + "description": "Funds to be sent to the instantiated contract.", + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, "label": { "description": "Label for the instantiated contract.", "type": "string" @@ -1175,6 +1198,7 @@ "type": "object", "required": [ "code_id", + "funds", "label", "msg" ], @@ -1196,6 +1220,13 @@ "format": "uint64", "minimum": 0.0 }, + "funds": { + "description": "Funds to be sent to the instantiated contract.", + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, "label": { "description": "Label for the instantiated contract.", "type": "string" @@ -2077,11 +2108,27 @@ "description": "Binary is a wrapper around Vec to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec. See also .", "type": "string" }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, "ModuleInstantiateInfo": { "description": "Information needed to instantiate a module.", "type": "object", "required": [ "code_id", + "funds", "label", "msg" ], @@ -2103,6 +2150,13 @@ "format": "uint64", "minimum": 0.0 }, + "funds": { + "description": "Funds to be sent to the instantiated contract.", + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, "label": { "description": "Label for the instantiated contract.", "type": "string" @@ -2157,6 +2211,10 @@ "additionalProperties": false } ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" } } }, diff --git a/contracts/proposal/dao-proposal-multiple/schema/dao-proposal-multiple.json b/contracts/proposal/dao-proposal-multiple/schema/dao-proposal-multiple.json index 8d67f6e3c..11c7ed910 100644 --- a/contracts/proposal/dao-proposal-multiple/schema/dao-proposal-multiple.json +++ b/contracts/proposal/dao-proposal-multiple/schema/dao-proposal-multiple.json @@ -110,6 +110,21 @@ "description": "Binary is a wrapper around Vec to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec. See also .", "type": "string" }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", "type": "string" @@ -153,6 +168,7 @@ "type": "object", "required": [ "code_id", + "funds", "label", "msg" ], @@ -174,6 +190,13 @@ "format": "uint64", "minimum": 0.0 }, + "funds": { + "description": "Funds to be sent to the instantiated contract.", + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, "label": { "description": "Label for the instantiated contract.", "type": "string" @@ -261,6 +284,10 @@ } ] }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + }, "VotingStrategy": { "description": "Determines how many choices may be selected.", "oneOf": [ @@ -1149,6 +1176,7 @@ "type": "object", "required": [ "code_id", + "funds", "label", "msg" ], @@ -1170,6 +1198,13 @@ "format": "uint64", "minimum": 0.0 }, + "funds": { + "description": "Funds to be sent to the instantiated contract.", + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, "label": { "description": "Label for the instantiated contract.", "type": "string" @@ -1991,11 +2026,27 @@ "description": "Binary is a wrapper around Vec to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec. See also .", "type": "string" }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, "ModuleInstantiateInfo": { "description": "Information needed to instantiate a module.", "type": "object", "required": [ "code_id", + "funds", "label", "msg" ], @@ -2017,6 +2068,13 @@ "format": "uint64", "minimum": 0.0 }, + "funds": { + "description": "Funds to be sent to the instantiated contract.", + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, "label": { "description": "Label for the instantiated contract.", "type": "string" @@ -2071,6 +2129,10 @@ "additionalProperties": false } ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" } } }, diff --git a/contracts/proposal/dao-proposal-single/schema/dao-proposal-single.json b/contracts/proposal/dao-proposal-single/schema/dao-proposal-single.json index 44da656fc..b98955db8 100644 --- a/contracts/proposal/dao-proposal-single/schema/dao-proposal-single.json +++ b/contracts/proposal/dao-proposal-single/schema/dao-proposal-single.json @@ -110,6 +110,21 @@ "description": "Binary is a wrapper around Vec to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec. See also .", "type": "string" }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", "type": "string" @@ -153,6 +168,7 @@ "type": "object", "required": [ "code_id", + "funds", "label", "msg" ], @@ -174,6 +190,13 @@ "format": "uint64", "minimum": 0.0 }, + "funds": { + "description": "Funds to be sent to the instantiated contract.", + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, "label": { "description": "Label for the instantiated contract.", "type": "string" @@ -1175,6 +1198,7 @@ "type": "object", "required": [ "code_id", + "funds", "label", "msg" ], @@ -1196,6 +1220,13 @@ "format": "uint64", "minimum": 0.0 }, + "funds": { + "description": "Funds to be sent to the instantiated contract.", + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, "label": { "description": "Label for the instantiated contract.", "type": "string" @@ -2077,11 +2108,27 @@ "description": "Binary is a wrapper around Vec to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec. See also .", "type": "string" }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, "ModuleInstantiateInfo": { "description": "Information needed to instantiate a module.", "type": "object", "required": [ "code_id", + "funds", "label", "msg" ], @@ -2103,6 +2150,13 @@ "format": "uint64", "minimum": 0.0 }, + "funds": { + "description": "Funds to be sent to the instantiated contract.", + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, "label": { "description": "Label for the instantiated contract.", "type": "string" @@ -2157,6 +2211,10 @@ "additionalProperties": false } ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" } } }, diff --git a/contracts/voting/dao-voting-cw721-staked/schema/dao-voting-cw721-staked.json b/contracts/voting/dao-voting-cw721-staked/schema/dao-voting-cw721-staked.json index aabc38089..84ff504cb 100644 --- a/contracts/voting/dao-voting-cw721-staked/schema/dao-voting-cw721-staked.json +++ b/contracts/voting/dao-voting-cw721-staked/schema/dao-voting-cw721-staked.json @@ -137,6 +137,7 @@ "NftContract": { "oneOf": [ { + "description": "Uses an existing cw721 or sg721 token contract.", "type": "object", "required": [ "existing" @@ -149,7 +150,7 @@ ], "properties": { "address": { - "description": "Address of an already instantiated cw721 token contract.", + "description": "Address of an already instantiated cw721 or sg721 token contract.", "type": "string" } }, @@ -159,6 +160,7 @@ "additionalProperties": false }, { + "description": "Creates a new NFT collection used for staking and governance.", "type": "object", "required": [ "new" @@ -198,6 +200,19 @@ } }, "additionalProperties": false + }, + { + "description": "Uses a factory pattern that must return the denom, optionally a Token Contract address, and any setup messages. The binary must serialize to a `WasmMsg::Execute` message.", + "type": "object", + "required": [ + "factory" + ], + "properties": { + "factory": { + "$ref": "#/definitions/Binary" + } + }, + "additionalProperties": false } ] }, @@ -250,6 +265,7 @@ "additionalProperties": false }, { + "description": "Claim NFTs that have been unstaked for the specified duration.", "type": "object", "required": [ "claim_nfts" @@ -263,6 +279,7 @@ "additionalProperties": false }, { + "description": "Updates the contract configuration, namely unstaking duration. Only callable by the DAO that initialized this voting contract.", "type": "object", "required": [ "update_config" @@ -288,6 +305,7 @@ "additionalProperties": false }, { + "description": "Adds a hook which is called on staking / unstaking events. Only callable by the DAO that initialized this voting contract.", "type": "object", "required": [ "add_hook" @@ -309,6 +327,7 @@ "additionalProperties": false }, { + "description": "Removes a hook which is called on staking / unstaking events. Only callable by the DAO that initialized this voting contract.", "type": "object", "required": [ "remove_hook" @@ -330,7 +349,7 @@ "additionalProperties": false }, { - "description": "Sets the active threshold to a new value. Only the instantiator this contract (a DAO most likely) may call this method.", + "description": "Sets the active threshold to a new value. Only callable by the DAO that initialized this voting contract.", "type": "object", "required": [ "update_active_threshold" diff --git a/contracts/voting/dao-voting-token-staked/schema/dao-voting-token-staked.json b/contracts/voting/dao-voting-token-staked/schema/dao-voting-token-staked.json index b102a0e25..b3e7c2bb1 100644 --- a/contracts/voting/dao-voting-token-staked/schema/dao-voting-token-staked.json +++ b/contracts/voting/dao-voting-token-staked/schema/dao-voting-token-staked.json @@ -92,6 +92,10 @@ } ] }, + "Binary": { + "description": "Binary is a wrapper around Vec to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec. See also .", + "type": "string" + }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", "type": "string" @@ -299,6 +303,19 @@ } }, "additionalProperties": false + }, + { + "description": "Uses a factory pattern that must return the denom, optionally a Token Contract address. The binary must serialize to a `WasmMsg::Execute` message.", + "type": "object", + "required": [ + "factory" + ], + "properties": { + "factory": { + "$ref": "#/definitions/Binary" + } + }, + "additionalProperties": false } ] }, diff --git a/test-contracts/dao-test-custom-factory/.gitignore b/test-contracts/dao-test-custom-factory/.gitignore deleted file mode 100644 index dfdaaa6bc..000000000 --- a/test-contracts/dao-test-custom-factory/.gitignore +++ /dev/null @@ -1,15 +0,0 @@ -# Build results -/target - -# Cargo+Git helper file (https://github.com/rust-lang/cargo/blob/0.44.1/src/cargo/sources/git/utils.rs#L320-L327) -.cargo-ok - -# Text file backups -**/*.rs.bk - -# macOS -.DS_Store - -# IDEs -*.iml -.idea diff --git a/test-contracts/dao-test-custom-factory/schema/admin_response.json b/test-contracts/dao-test-custom-factory/schema/admin_response.json deleted file mode 100644 index 920968701..000000000 --- a/test-contracts/dao-test-custom-factory/schema/admin_response.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "AdminResponse", - "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", - "type": "string" -} diff --git a/test-contracts/dao-test-custom-factory/schema/dao_response.json b/test-contracts/dao-test-custom-factory/schema/dao_response.json deleted file mode 100644 index 9518ba3b5..000000000 --- a/test-contracts/dao-test-custom-factory/schema/dao_response.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "DaoResponse", - "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", - "type": "string" -} diff --git a/test-contracts/dao-test-custom-factory/schema/execute_msg.json b/test-contracts/dao-test-custom-factory/schema/execute_msg.json deleted file mode 100644 index 302a22adc..000000000 --- a/test-contracts/dao-test-custom-factory/schema/execute_msg.json +++ /dev/null @@ -1,487 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "ExecuteMsg", - "oneOf": [ - { - "type": "object", - "required": [ - "execute" - ], - "properties": { - "execute": { - "type": "object", - "required": [ - "msgs" - ], - "properties": { - "msgs": { - "type": "array", - "items": { - "$ref": "#/definitions/CosmosMsg_for_Empty" - } - } - } - } - }, - "additionalProperties": false - } - ], - "definitions": { - "BankMsg": { - "description": "The message types of the bank module.\n\nSee https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/bank/v1beta1/tx.proto", - "oneOf": [ - { - "description": "Sends native tokens from the contract to the given address.\n\nThis is translated to a [MsgSend](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/bank/v1beta1/tx.proto#L19-L28). `from_address` is automatically filled with the current contract's address.", - "type": "object", - "required": [ - "send" - ], - "properties": { - "send": { - "type": "object", - "required": [ - "amount", - "to_address" - ], - "properties": { - "amount": { - "type": "array", - "items": { - "$ref": "#/definitions/Coin" - } - }, - "to_address": { - "type": "string" - } - } - } - }, - "additionalProperties": false - }, - { - "description": "This will burn the given coins from the contract's account. There is no Cosmos SDK message that performs this, but it can be done by calling the bank keeper. Important if a contract controls significant token supply that must be retired.", - "type": "object", - "required": [ - "burn" - ], - "properties": { - "burn": { - "type": "object", - "required": [ - "amount" - ], - "properties": { - "amount": { - "type": "array", - "items": { - "$ref": "#/definitions/Coin" - } - } - } - } - }, - "additionalProperties": false - } - ] - }, - "Binary": { - "description": "Binary is a wrapper around Vec to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec", - "type": "string" - }, - "Coin": { - "type": "object", - "required": [ - "amount", - "denom" - ], - "properties": { - "amount": { - "$ref": "#/definitions/Uint128" - }, - "denom": { - "type": "string" - } - } - }, - "CosmosMsg_for_Empty": { - "oneOf": [ - { - "type": "object", - "required": [ - "bank" - ], - "properties": { - "bank": { - "$ref": "#/definitions/BankMsg" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "custom" - ], - "properties": { - "custom": { - "$ref": "#/definitions/Empty" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "staking" - ], - "properties": { - "staking": { - "$ref": "#/definitions/StakingMsg" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "distribution" - ], - "properties": { - "distribution": { - "$ref": "#/definitions/DistributionMsg" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "wasm" - ], - "properties": { - "wasm": { - "$ref": "#/definitions/WasmMsg" - } - }, - "additionalProperties": false - } - ] - }, - "DistributionMsg": { - "description": "The message types of the distribution module.\n\nSee https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto", - "oneOf": [ - { - "description": "This is translated to a [MsgSetWithdrawAddress](https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto#L29-L37). `delegator_address` is automatically filled with the current contract's address.", - "type": "object", - "required": [ - "set_withdraw_address" - ], - "properties": { - "set_withdraw_address": { - "type": "object", - "required": [ - "address" - ], - "properties": { - "address": { - "description": "The `withdraw_address`", - "type": "string" - } - } - } - }, - "additionalProperties": false - }, - { - "description": "This is translated to a [[MsgWithdrawDelegatorReward](https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto#L42-L50). `delegator_address` is automatically filled with the current contract's address.", - "type": "object", - "required": [ - "withdraw_delegator_reward" - ], - "properties": { - "withdraw_delegator_reward": { - "type": "object", - "required": [ - "validator" - ], - "properties": { - "validator": { - "description": "The `validator_address`", - "type": "string" - } - } - } - }, - "additionalProperties": false - } - ] - }, - "Empty": { - "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", - "type": "object" - }, - "StakingMsg": { - "description": "The message types of the staking module.\n\nSee https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto", - "oneOf": [ - { - "description": "This is translated to a [MsgDelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L81-L90). `delegator_address` is automatically filled with the current contract's address.", - "type": "object", - "required": [ - "delegate" - ], - "properties": { - "delegate": { - "type": "object", - "required": [ - "amount", - "validator" - ], - "properties": { - "amount": { - "$ref": "#/definitions/Coin" - }, - "validator": { - "type": "string" - } - } - } - }, - "additionalProperties": false - }, - { - "description": "This is translated to a [MsgUndelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L112-L121). `delegator_address` is automatically filled with the current contract's address.", - "type": "object", - "required": [ - "undelegate" - ], - "properties": { - "undelegate": { - "type": "object", - "required": [ - "amount", - "validator" - ], - "properties": { - "amount": { - "$ref": "#/definitions/Coin" - }, - "validator": { - "type": "string" - } - } - } - }, - "additionalProperties": false - }, - { - "description": "This is translated to a [MsgBeginRedelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L95-L105). `delegator_address` is automatically filled with the current contract's address.", - "type": "object", - "required": [ - "redelegate" - ], - "properties": { - "redelegate": { - "type": "object", - "required": [ - "amount", - "dst_validator", - "src_validator" - ], - "properties": { - "amount": { - "$ref": "#/definitions/Coin" - }, - "dst_validator": { - "type": "string" - }, - "src_validator": { - "type": "string" - } - } - } - }, - "additionalProperties": false - } - ] - }, - "Uint128": { - "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", - "type": "string" - }, - "WasmMsg": { - "description": "The message types of the wasm module.\n\nSee https://github.com/CosmWasm/wasmd/blob/v0.14.0/x/wasm/internal/types/tx.proto", - "oneOf": [ - { - "description": "Dispatches a call to another contract at a known address (with known ABI).\n\nThis is translated to a [MsgExecuteContract](https://github.com/CosmWasm/wasmd/blob/v0.14.0/x/wasm/internal/types/tx.proto#L68-L78). `sender` is automatically filled with the current contract's address.", - "type": "object", - "required": [ - "execute" - ], - "properties": { - "execute": { - "type": "object", - "required": [ - "contract_addr", - "funds", - "msg" - ], - "properties": { - "contract_addr": { - "type": "string" - }, - "funds": { - "type": "array", - "items": { - "$ref": "#/definitions/Coin" - } - }, - "msg": { - "description": "msg is the json-encoded ExecuteMsg struct (as raw Binary)", - "allOf": [ - { - "$ref": "#/definitions/Binary" - } - ] - } - } - } - }, - "additionalProperties": false - }, - { - "description": "Instantiates a new contracts from previously uploaded Wasm code.\n\nThis is translated to a [MsgInstantiateContract](https://github.com/CosmWasm/wasmd/blob/v0.16.0-alpha1/x/wasm/internal/types/tx.proto#L47-L61). `sender` is automatically filled with the current contract's address.", - "type": "object", - "required": [ - "instantiate" - ], - "properties": { - "instantiate": { - "type": "object", - "required": [ - "code_id", - "funds", - "label", - "msg" - ], - "properties": { - "admin": { - "type": [ - "string", - "null" - ] - }, - "code_id": { - "type": "integer", - "format": "uint64", - "minimum": 0.0 - }, - "funds": { - "type": "array", - "items": { - "$ref": "#/definitions/Coin" - } - }, - "label": { - "description": "A human-readbale label for the contract", - "type": "string" - }, - "msg": { - "description": "msg is the JSON-encoded InstantiateMsg struct (as raw Binary)", - "allOf": [ - { - "$ref": "#/definitions/Binary" - } - ] - } - } - } - }, - "additionalProperties": false - }, - { - "description": "Migrates a given contracts to use new wasm code. Passes a MigrateMsg to allow us to customize behavior.\n\nOnly the contract admin (as defined in wasmd), if any, is able to make this call.\n\nThis is translated to a [MsgMigrateContract](https://github.com/CosmWasm/wasmd/blob/v0.14.0/x/wasm/internal/types/tx.proto#L86-L96). `sender` is automatically filled with the current contract's address.", - "type": "object", - "required": [ - "migrate" - ], - "properties": { - "migrate": { - "type": "object", - "required": [ - "contract_addr", - "msg", - "new_code_id" - ], - "properties": { - "contract_addr": { - "type": "string" - }, - "msg": { - "description": "msg is the json-encoded MigrateMsg struct that will be passed to the new code", - "allOf": [ - { - "$ref": "#/definitions/Binary" - } - ] - }, - "new_code_id": { - "description": "the code_id of the new logic to place in the given contract", - "type": "integer", - "format": "uint64", - "minimum": 0.0 - } - } - } - }, - "additionalProperties": false - }, - { - "description": "Sets a new admin (for migrate) on the given contract. Fails if this contract is not currently admin of the target contract.", - "type": "object", - "required": [ - "update_admin" - ], - "properties": { - "update_admin": { - "type": "object", - "required": [ - "admin", - "contract_addr" - ], - "properties": { - "admin": { - "type": "string" - }, - "contract_addr": { - "type": "string" - } - } - } - }, - "additionalProperties": false - }, - { - "description": "Clears the admin on the given contract, so no more migration possible. Fails if this contract is not currently admin of the target contract.", - "type": "object", - "required": [ - "clear_admin" - ], - "properties": { - "clear_admin": { - "type": "object", - "required": [ - "contract_addr" - ], - "properties": { - "contract_addr": { - "type": "string" - } - } - } - }, - "additionalProperties": false - } - ] - } - } -} diff --git a/test-contracts/dao-test-custom-factory/schema/info_response.json b/test-contracts/dao-test-custom-factory/schema/info_response.json deleted file mode 100644 index a0516764e..000000000 --- a/test-contracts/dao-test-custom-factory/schema/info_response.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "InfoResponse", - "type": "object", - "required": [ - "info" - ], - "properties": { - "info": { - "$ref": "#/definitions/ContractVersion" - } - }, - "definitions": { - "ContractVersion": { - "type": "object", - "required": [ - "contract", - "version" - ], - "properties": { - "contract": { - "description": "contract is the crate name of the implementing contract, eg. `crate:cw20-base` we will use other prefixes for other languages, and their standard global namespacing", - "type": "string" - }, - "version": { - "description": "version is any string that this implementation knows. It may be simple counter \"1\", \"2\". or semantic version on release tags \"v0.7.0\", or some custom feature flag list. the only code that needs to understand the version parsing is code that knows how to migrate from the given contract (and is tied to it's implementation somehow)", - "type": "string" - } - } - } - } -} diff --git a/test-contracts/dao-test-custom-factory/schema/instantiate_msg.json b/test-contracts/dao-test-custom-factory/schema/instantiate_msg.json deleted file mode 100644 index 8e3416dc4..000000000 --- a/test-contracts/dao-test-custom-factory/schema/instantiate_msg.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "InstantiateMsg", - "type": "object", - "required": [ - "root" - ], - "properties": { - "root": { - "type": "string" - } - } -} diff --git a/test-contracts/dao-test-custom-factory/schema/query_msg.json b/test-contracts/dao-test-custom-factory/schema/query_msg.json deleted file mode 100644 index 5262b4290..000000000 --- a/test-contracts/dao-test-custom-factory/schema/query_msg.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "QueryMsg", - "oneOf": [ - { - "type": "object", - "required": [ - "admin" - ], - "properties": { - "admin": { - "type": "object" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "dao" - ], - "properties": { - "dao": { - "type": "object" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "info" - ], - "properties": { - "info": { - "type": "object" - } - }, - "additionalProperties": false - } - ] -} From 5e64f30d6cf1f6defbce211cc8869ce80072982e Mon Sep 17 00:00:00 2001 From: Jake Hartnell Date: Thu, 14 Sep 2023 17:53:57 -0700 Subject: [PATCH 05/30] Fix WASM build --- Cargo.lock | 428 +----------------- .../dao-voting-token-staked/src/contract.rs | 11 +- .../voting/dao-voting-token-staked/src/msg.rs | 45 +- .../src/tests/test_tube/integration_tests.rs | 19 +- .../src/tests/test_tube/test_env.rs | 7 +- justfile | 6 +- packages/dao-interface/Cargo.toml | 1 + packages/dao-interface/src/lib.rs | 1 + packages/dao-interface/src/token.rs | 49 ++ .../dao-proposal-hook-counter/src/contract.rs | 10 +- .../dao-proposal-hook-counter/src/error.rs | 5 +- .../dao-test-custom-factory/Cargo.toml | 8 +- .../dao-test-custom-factory/src/contract.rs | 11 +- .../dao-test-custom-factory/src/lib.rs | 1 - .../dao-test-custom-factory/src/msg.rs | 2 +- 15 files changed, 102 insertions(+), 502 deletions(-) create mode 100644 packages/dao-interface/src/token.rs diff --git a/Cargo.lock b/Cargo.lock index 8b5375624..857525a76 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -43,12 +43,6 @@ version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" -[[package]] -name = "arrayvec" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" - [[package]] name = "assert_matches" version = "1.5.0" @@ -165,25 +159,6 @@ dependencies = [ "rustc-demangle", ] -[[package]] -name = "base-factory" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de00b1bd4a381194ccaa4a790390d2599a56eaf50f5b5f76f4a975958188390" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus 0.16.0", - "cw-utils 0.16.0", - "cw2 0.16.0", - "schemars", - "serde", - "sg-std 2.4.0", - "sg1", - "sg2", - "thiserror", -] - [[package]] name = "base16ct" version = "0.1.1" @@ -267,28 +242,6 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" -[[package]] -name = "bitvec" -version = "0.17.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41262f11d771fd4a61aa3ce019fca363b4b6c282fca9da2a31186d3965a47a5c" -dependencies = [ - "either", - "radium 0.3.0", -] - -[[package]] -name = "bitvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" -dependencies = [ - "funty", - "radium 0.7.0", - "tap", - "wyz", -] - [[package]] name = "block-buffer" version = "0.9.0" @@ -336,51 +289,6 @@ dependencies = [ "serde_yaml", ] -[[package]] -name = "borsh" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4114279215a005bc675e386011e594e1d9b800918cea18fcadadcce864a2046b" -dependencies = [ - "borsh-derive", - "hashbrown 0.12.3", -] - -[[package]] -name = "borsh-derive" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0754613691538d51f329cce9af41d7b7ca150bc973056f1156611489475f54f7" -dependencies = [ - "borsh-derive-internal", - "borsh-schema-derive-internal", - "proc-macro-crate", - "proc-macro2", - "syn 1.0.109", -] - -[[package]] -name = "borsh-derive-internal" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afb438156919598d2c7bad7e1c0adf3d26ed3840dbc010db1a882a65583ca2fb" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "borsh-schema-derive-internal" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634205cc43f74a1b9046ef87c4540ebda95696ec0f315024860cad7c5b0f5ccd" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "bs58" version = "0.4.0" @@ -396,28 +304,6 @@ version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" -[[package]] -name = "bytecheck" -version = "0.6.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6372023ac861f6e6dc89c8344a8f398fb42aaba2b5dbc649ca0c0e9dbcb627" -dependencies = [ - "bytecheck_derive", - "ptr_meta", - "simdutf8", -] - -[[package]] -name = "bytecheck_derive" -version = "0.6.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7ec4c6f261935ad534c0c22dbef2201b45918860eb1c574b972bd213a76af61" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "byteorder" version = "1.4.3" @@ -1784,6 +1670,7 @@ dependencies = [ "cw2 1.1.0", "cw20 1.1.0", "cw721 0.18.0", + "osmosis-std", ] [[package]] @@ -2092,12 +1979,7 @@ dependencies = [ "cw2 1.1.0", "dao-dao-macros", "dao-interface", - "dao-voting-cw721-staked", - "dao-voting-token-staked", - "sg721-base", "thiserror", - "vending-factory", - "vending-minter", ] [[package]] @@ -2275,8 +2157,8 @@ dependencies = [ "dao-testing", "dao-voting 2.2.0", "sg-multi-test", - "sg-std 3.2.0", - "sg721 3.1.0", + "sg-std", + "sg721", "sg721-base", "thiserror", ] @@ -2606,12 +2488,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8cbd1169bd7b4a0a20d92b9af7a7e0422888bd38a6f5ec29c1fd8c1558a272e" -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - [[package]] name = "futures" version = "0.3.28" @@ -3525,15 +3401,6 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" -[[package]] -name = "proc-macro-crate" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" -dependencies = [ - "toml", -] - [[package]] name = "proc-macro2" version = "1.0.66" @@ -3610,26 +3477,6 @@ dependencies = [ "prost 0.11.9", ] -[[package]] -name = "ptr_meta" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" -dependencies = [ - "ptr_meta_derive", -] - -[[package]] -name = "ptr_meta_derive" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "quote" version = "1.0.33" @@ -3639,18 +3486,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "radium" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "def50a86306165861203e7f84ecffbbdfdea79f0e51039b33de1e952358c47ac" - -[[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - [[package]] name = "rand" version = "0.8.5" @@ -3687,15 +3522,6 @@ dependencies = [ "getrandom", ] -[[package]] -name = "rand_xoshiro" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" -dependencies = [ - "rand_core 0.6.4", -] - [[package]] name = "regex" version = "1.9.5" @@ -3725,15 +3551,6 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" -[[package]] -name = "rend" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581008d2099240d37fb08d77ad713bcaec2c4d89d50b5b21a8bb1996bbab68ab" -dependencies = [ - "bytecheck", -] - [[package]] name = "rfc6979" version = "0.3.1" @@ -3790,34 +3607,6 @@ dependencies = [ "opaque-debug", ] -[[package]] -name = "rkyv" -version = "0.7.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0200c8230b013893c0b2d6213d6ec64ed2b9be2e0e016682b7224ff82cff5c58" -dependencies = [ - "bitvec 1.0.1", - "bytecheck", - "hashbrown 0.12.3", - "ptr_meta", - "rend", - "rkyv_derive", - "seahash", - "tinyvec", - "uuid 1.4.1", -] - -[[package]] -name = "rkyv_derive" -version = "0.7.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2e06b915b5c230a17d7a736d1e2e63ee753c256a8614ef3f5147b13a4f5541d" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "ron" version = "0.7.1" @@ -3839,22 +3628,6 @@ dependencies = [ "ordered-multimap", ] -[[package]] -name = "rust_decimal" -version = "1.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c4216490d5a413bc6d10fa4742bd7d4955941d062c0ef873141d6b0e7b30fd" -dependencies = [ - "arrayvec", - "borsh", - "bytes", - "num-traits", - "rand", - "rkyv", - "serde", - "serde_json", -] - [[package]] name = "rustc-demangle" version = "0.1.23" @@ -3969,12 +3742,6 @@ dependencies = [ "untrusted", ] -[[package]] -name = "seahash" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" - [[package]] name = "sec1" version = "0.3.0" @@ -4136,22 +3903,7 @@ dependencies = [ "cw-multi-test", "schemars", "serde", - "sg-std 3.2.0", -] - -[[package]] -name = "sg-std" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc9b4ab6090f777940d3277632b1a3fff89f5a52191443a19da7cd99e3845041" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-utils 0.16.0", - "cw721 0.16.0", - "schemars", - "serde", - "thiserror", + "sg-std", ] [[package]] @@ -4169,81 +3921,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "sg-whitelist" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a88e24759094f8927913efa92d3679011a40fa30c4e29ea7c3bc6cf8a3c514bb" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus 0.16.0", - "cw-utils 0.16.0", - "cw2 0.16.0", - "rust_decimal", - "schemars", - "serde", - "sg-std 2.4.0", - "sg1", - "thiserror", -] - -[[package]] -name = "sg1" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c691fbf35f3deb9e2944b0a82db7280798a1b4b17c7e9fe22b78d89c5276c8e" -dependencies = [ - "cosmwasm-std", - "cw-utils 0.16.0", - "serde", - "sg-std 2.4.0", - "thiserror", -] - -[[package]] -name = "sg2" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0accfb1855c12e486b5c1afbd858652fabdf00bdacd221a9f96121c1d1955a62" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus 0.16.0", - "cw-utils 0.16.0", - "schemars", - "serde", - "sg721 2.4.0", - "thiserror", -] - -[[package]] -name = "sg4" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18da9e17654454380aa81f92cbc650098f28d657932770fda1c911747eb95ce4" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "schemars", - "serde", - "thiserror", -] - -[[package]] -name = "sg721" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b37ea61753635e1de01a16947784ddd5ba8e256fa403be6246b2d88578914fb2" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-utils 0.16.0", - "cw721-base 0.16.0", - "serde", - "thiserror", -] - [[package]] name = "sg721" version = "3.1.0" @@ -4274,8 +3951,8 @@ dependencies = [ "cw721 0.18.0", "cw721-base 0.18.0", "serde", - "sg-std 3.2.0", - "sg721 3.1.0", + "sg-std", + "sg721", "thiserror", "url", ] @@ -4331,16 +4008,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" -[[package]] -name = "shuffle" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fecbe806be0726e22a6192ce0ec2f90e0798946ae0cbc2c10c7a3c68cae4eb2" -dependencies = [ - "bitvec 0.17.4", - "rand", -] - [[package]] name = "signature" version = "1.6.4" @@ -4361,12 +4028,6 @@ dependencies = [ "rand_core 0.6.4", ] -[[package]] -name = "simdutf8" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" - [[package]] name = "slab" version = "0.4.9" @@ -4529,12 +4190,6 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - [[package]] name = "tendermint" version = "0.23.9" @@ -4708,7 +4363,7 @@ dependencies = [ "tokio", "tracing", "url", - "uuid 0.8.2", + "uuid", "walkdir", ] @@ -4742,7 +4397,7 @@ dependencies = [ "tokio", "tracing", "url", - "uuid 0.8.2", + "uuid", "walkdir", ] @@ -5147,64 +4802,6 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" -[[package]] -name = "uuid" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" - -[[package]] -name = "vending-factory" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cf5cc560b6d037161d5e4cd3bbe794373c685afb2f30f739984bf135f4abd41" -dependencies = [ - "base-factory", - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus 0.16.0", - "cw-utils 0.16.0", - "cw2 0.16.0", - "schemars", - "serde", - "sg-std 2.4.0", - "sg1", - "sg2", - "sg721 2.4.0", - "thiserror", -] - -[[package]] -name = "vending-minter" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "999a8d302970bed638c09c9c116e7ab41031089a4e707796e7ca9c3ef7b66ed5" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus 0.16.0", - "cw-utils 0.16.0", - "cw2 0.16.0", - "cw721 0.16.0", - "cw721-base 0.16.0", - "rand_core 0.6.4", - "rand_xoshiro", - "schemars", - "semver", - "serde", - "sg-std 2.4.0", - "sg-whitelist", - "sg1", - "sg2", - "sg4", - "sg721 2.4.0", - "sha2 0.10.7", - "shuffle", - "thiserror", - "url", - "vending-factory", -] - [[package]] name = "version_check" version = "0.9.4" @@ -5452,15 +5049,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "wyz" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" -dependencies = [ - "tap", -] - [[package]] name = "yaml-rust" version = "0.4.5" diff --git a/contracts/voting/dao-voting-token-staked/src/contract.rs b/contracts/voting/dao-voting-token-staked/src/contract.rs index 662c10053..50a0aff7d 100644 --- a/contracts/voting/dao-voting-token-staked/src/contract.rs +++ b/contracts/voting/dao-voting-token-staked/src/contract.rs @@ -15,9 +15,10 @@ use cw_utils::{ maybe_addr, must_pay, parse_reply_execute_data, parse_reply_instantiate_data, Duration, }; use dao_hooks::stake::{stake_hook_msgs, unstake_hook_msgs}; -use dao_interface::state::ModuleInstantiateCallback; -use dao_interface::voting::{ - IsActiveResponse, TotalPowerAtHeightResponse, VotingPowerAtHeightResponse, +use dao_interface::{ + state::ModuleInstantiateCallback, + token::{FactoryCallback, InitialBalance, NewTokenInfo}, + voting::{IsActiveResponse, TotalPowerAtHeightResponse, VotingPowerAtHeightResponse}, }; use dao_voting::{ duration::validate_duration, @@ -29,8 +30,8 @@ use dao_voting::{ use crate::error::ContractError; use crate::msg::{ - DenomResponse, ExecuteMsg, FactoryCallback, GetHooksResponse, InitialBalance, InstantiateMsg, - ListStakersResponse, MigrateMsg, NewTokenInfo, QueryMsg, StakerBalanceResponse, TokenInfo, + DenomResponse, ExecuteMsg, GetHooksResponse, InstantiateMsg, ListStakersResponse, MigrateMsg, + QueryMsg, StakerBalanceResponse, TokenInfo, }; use crate::state::{ Config, ACTIVE_THRESHOLD, CLAIMS, CONFIG, DAO, DENOM, HOOKS, MAX_CLAIMS, STAKED_BALANCES, diff --git a/contracts/voting/dao-voting-token-staked/src/msg.rs b/contracts/voting/dao-voting-token-staked/src/msg.rs index 500af96c6..a5210ba9c 100644 --- a/contracts/voting/dao-voting-token-staked/src/msg.rs +++ b/contracts/voting/dao-voting-token-staked/src/msg.rs @@ -1,47 +1,10 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; use cosmwasm_std::{Binary, Uint128}; -use cw_tokenfactory_issuer::msg::DenomUnit; use cw_utils::Duration; use dao_dao_macros::{active_query, token_query, voting_module_query}; +use dao_interface::token::NewTokenInfo; use dao_voting::threshold::{ActiveThreshold, ActiveThresholdResponse}; -#[cw_serde] -pub struct InitialBalance { - pub amount: Uint128, - pub address: String, -} - -#[cw_serde] -pub struct NewDenomMetadata { - /// The name of the token (e.g. "Cat Coin") - pub name: String, - /// The description of the token - pub description: String, - /// The ticker symbol of the token (e.g. "CAT") - pub symbol: String, - /// The unit commonly used in communication (e.g. "cat") - pub display: String, - /// Used define additional units of the token (e.g. "tiger") - /// These must have an exponent larger than 0. - pub additional_denom_units: Option>, -} - -#[cw_serde] -pub struct NewTokenInfo { - /// The code id of the cw-tokenfactory-issuer contract - pub token_issuer_code_id: u64, - /// The subdenom of the token to create, will also be used as an alias - /// for the denom. The Token Factory denom will have the format of - /// factory/{contract_address}/{subdenom} - pub subdenom: String, - /// Optional metadata for the token, this can additionally be set later. - pub metadata: Option, - /// The initial balances to set for the token, cannot be empty. - pub initial_balances: Vec, - /// Optional balance to mint for the DAO. - pub initial_dao_balance: Option, -} - #[cw_serde] pub enum TokenInfo { /// Uses an existing Token Factory token and creates a new issuer contract. @@ -138,9 +101,3 @@ pub struct DenomResponse { pub struct GetHooksResponse { pub hooks: Vec, } - -#[cw_serde] -pub struct FactoryCallback { - pub denom: String, - pub token_contract: Option, -} 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 0cb8059b6..bf55cacc7 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,7 +1,10 @@ use cosmwasm_std::{to_binary, Addr, Coin, Decimal, Uint128, WasmMsg}; use cw_tokenfactory_issuer::msg::DenomUnit; use cw_utils::Duration; -use dao_interface::state::{Admin, ModuleInstantiateInfo}; +use dao_interface::{ + state::{Admin, ModuleInstantiateInfo}, + token::{InitialBalance, NewDenomMetadata, NewTokenInfo}, +}; use dao_testing::test_tube::dao_dao_core::DaoCore; use dao_voting::{ pre_propose::PreProposeInfo, @@ -11,7 +14,7 @@ use osmosis_std::types::cosmos::bank::v1beta1::QueryBalanceRequest; use osmosis_test_tube::{Account, OsmosisTestApp}; use crate::{ - msg::{ExecuteMsg, InitialBalance, InstantiateMsg, NewDenomMetadata, NewTokenInfo, TokenInfo}, + msg::{ExecuteMsg, InstantiateMsg, TokenInfo}, tests::test_tube::test_env::TokenVotingContract, ContractError, }; @@ -349,16 +352,14 @@ fn test_factory() { contract_addr: custom_factory.unwrap().contract_addr.to_string(), msg: to_binary( &dao_test_custom_factory::msg::ExecuteMsg::TokenFactoryFactory( - dao_test_custom_factory::NewTokenInfo { + 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_balances: vec![InitialBalance { + address: accounts[0].address(), + amount: Uint128::new(100), + }], initial_dao_balance: None, }, ), 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 10b1190c0..1d8d8f1d2 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 @@ -3,7 +3,7 @@ #![allow(dead_code)] use crate::{ - msg::{ExecuteMsg, InitialBalance, InstantiateMsg, NewTokenInfo, QueryMsg, TokenInfo}, + msg::{ExecuteMsg, InstantiateMsg, QueryMsg, TokenInfo}, ContractError, }; @@ -13,6 +13,7 @@ use cw_utils::Duration; use dao_interface::{ msg::QueryMsg as DaoQueryMsg, state::{Admin, ModuleInstantiateInfo, ProposalModule}, + token::{InitialBalance, NewDenomMetadata, NewTokenInfo}, voting::{IsActiveResponse, VotingPowerAtHeightResponse}, }; use dao_voting::{ @@ -136,7 +137,7 @@ impl TestEnvBuilder { token_info: TokenInfo::New(NewTokenInfo { token_issuer_code_id: issuer_id, subdenom: DENOM.to_string(), - metadata: Some(crate::msg::NewDenomMetadata { + metadata: Some(NewDenomMetadata { description: "Awesome token, get it meow!".to_string(), additional_denom_units: Some(vec![DenomUnit { denom: "cat".to_string(), @@ -208,7 +209,7 @@ impl TestEnvBuilder { token_info: TokenInfo::New(NewTokenInfo { token_issuer_code_id: issuer_id, subdenom: DENOM.to_string(), - metadata: Some(crate::msg::NewDenomMetadata { + metadata: Some(NewDenomMetadata { description: "Awesome token, get it meow!".to_string(), additional_denom_units: Some(vec![DenomUnit { denom: "cat".to_string(), diff --git a/justfile b/justfile index 1aff4f8ad..5ceec3503 100644 --- a/justfile +++ b/justfile @@ -59,14 +59,14 @@ workspace-optimize: --mount type=volume,source="$(basename "$(pwd)")_cache",target=/target \ --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ --platform linux/arm64 \ - cosmwasm/workspace-optimizer-arm64:0.13.0; \ + cosmwasm/workspace-optimizer-arm64:0.14.0; \ elif [[ $(uname -m) == 'aarch64' ]]; then docker run --rm -v "$(pwd)":/code \ --mount type=volume,source="$(basename "$(pwd)")_cache",target=/target \ --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ --platform linux/arm64 \ - cosmwasm/workspace-optimizer-arm64:0.13.0; \ + cosmwasm/workspace-optimizer-arm64:0.14.0; \ elif [[ $(uname -m) == 'x86_64' ]]; then docker run --rm -v "$(pwd)":/code \ --mount type=volume,source="$(basename "$(pwd)")_cache",target=/target \ --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ --platform linux/amd64 \ - cosmwasm/workspace-optimizer:0.13.0; fi + cosmwasm/workspace-optimizer:0.14.0; fi diff --git a/packages/dao-interface/Cargo.toml b/packages/dao-interface/Cargo.toml index c82abc5e4..f05475090 100644 --- a/packages/dao-interface/Cargo.toml +++ b/packages/dao-interface/Cargo.toml @@ -15,6 +15,7 @@ cw20 = { workspace = true } cw721 = { workspace = true } cw-hooks = { workspace = true } cw-utils = { workspace = true } +osmosis-std = { workspace = true } [dev-dependencies] cosmwasm-schema = { workspace = true } diff --git a/packages/dao-interface/src/lib.rs b/packages/dao-interface/src/lib.rs index f46f381c2..31f85bb7b 100644 --- a/packages/dao-interface/src/lib.rs +++ b/packages/dao-interface/src/lib.rs @@ -5,4 +5,5 @@ pub mod msg; pub mod proposal; pub mod query; pub mod state; +pub mod token; pub mod voting; diff --git a/packages/dao-interface/src/token.rs b/packages/dao-interface/src/token.rs new file mode 100644 index 000000000..fa60aae1b --- /dev/null +++ b/packages/dao-interface/src/token.rs @@ -0,0 +1,49 @@ +use cosmwasm_schema::cw_serde; +use cosmwasm_std::Uint128; + +// These are Cosmos Proto types used for Denom Metadata. +// We re-export them here for convenience. +pub use osmosis_std::types::cosmos::bank::v1beta1::{DenomUnit, Metadata}; + +#[cw_serde] +pub struct InitialBalance { + pub amount: Uint128, + pub address: String, +} + +#[cw_serde] +pub struct NewDenomMetadata { + /// The name of the token (e.g. "Cat Coin") + pub name: String, + /// The description of the token + pub description: String, + /// The ticker symbol of the token (e.g. "CAT") + pub symbol: String, + /// The unit commonly used in communication (e.g. "cat") + pub display: String, + /// Used define additional units of the token (e.g. "tiger") + /// These must have an exponent larger than 0. + pub additional_denom_units: Option>, +} + +#[cw_serde] +pub struct NewTokenInfo { + /// The code id of the cw-tokenfactory-issuer contract + pub token_issuer_code_id: u64, + /// The subdenom of the token to create, will also be used as an alias + /// for the denom. The Token Factory denom will have the format of + /// factory/{contract_address}/{subdenom} + pub subdenom: String, + /// Optional metadata for the token, this can additionally be set later. + pub metadata: Option, + /// The initial balances to set for the token, cannot be empty. + pub initial_balances: Vec, + /// Optional balance to mint for the DAO. + pub initial_dao_balance: Option, +} + +#[cw_serde] +pub struct FactoryCallback { + pub denom: String, + pub token_contract: Option, +} diff --git a/test-contracts/dao-proposal-hook-counter/src/contract.rs b/test-contracts/dao-proposal-hook-counter/src/contract.rs index be3cddc8d..0f2a36fd3 100644 --- a/test-contracts/dao-proposal-hook-counter/src/contract.rs +++ b/test-contracts/dao-proposal-hook-counter/src/contract.rs @@ -66,12 +66,12 @@ pub fn execute_proposal_hook( match proposal_hook { ProposalHookMsg::NewProposal { .. } => { let mut count = PROPOSAL_COUNTER.load(deps.storage)?; - count += 1; + count = count.checked_add(1).unwrap_or_default(); PROPOSAL_COUNTER.save(deps.storage, &count)?; } ProposalHookMsg::ProposalStatusChanged { .. } => { let mut count = STATUS_CHANGED_COUNTER.load(deps.storage)?; - count += 1; + count = count.checked_add(1).unwrap_or_default(); STATUS_CHANGED_COUNTER.save(deps.storage, &count)?; } } @@ -88,12 +88,12 @@ pub fn execute_stake_hook( match stake_hook { StakeChangedHookMsg::Stake { .. } => { let mut count = STAKE_COUNTER.load(deps.storage)?; - count += Uint128::new(1); + count = count.checked_add(Uint128::new(1))?; STAKE_COUNTER.save(deps.storage, &count)?; } StakeChangedHookMsg::Unstake { .. } => { let mut count = STAKE_COUNTER.load(deps.storage)?; - count += Uint128::new(1); + count = count.checked_add(Uint128::new(1))?; STAKE_COUNTER.save(deps.storage, &count)?; } } @@ -110,7 +110,7 @@ pub fn execute_vote_hook( match vote_hook { VoteHookMsg::NewVote { .. } => { let mut count = VOTE_COUNTER.load(deps.storage)?; - count += 1; + count = count.checked_add(1).unwrap_or_default(); VOTE_COUNTER.save(deps.storage, &count)?; } } diff --git a/test-contracts/dao-proposal-hook-counter/src/error.rs b/test-contracts/dao-proposal-hook-counter/src/error.rs index dc19f1033..0e7ceb05e 100644 --- a/test-contracts/dao-proposal-hook-counter/src/error.rs +++ b/test-contracts/dao-proposal-hook-counter/src/error.rs @@ -1,4 +1,4 @@ -use cosmwasm_std::StdError; +use cosmwasm_std::{OverflowError, StdError}; use thiserror::Error; #[derive(Error, Debug)] @@ -6,6 +6,9 @@ pub enum ContractError { #[error("{0}")] Std(#[from] StdError), + #[error(transparent)] + OverflowError(#[from] OverflowError), + #[error("Unauthorized")] Unauthorized {}, } diff --git a/test-contracts/dao-test-custom-factory/Cargo.toml b/test-contracts/dao-test-custom-factory/Cargo.toml index 02a16a9c7..f96c40a4d 100644 --- a/test-contracts/dao-test-custom-factory/Cargo.toml +++ b/test-contracts/dao-test-custom-factory/Cargo.toml @@ -27,12 +27,10 @@ cw-utils = { workspace = true } thiserror = { workspace = true } dao-dao-macros = { workspace = true } dao-interface = { workspace = true } -dao-voting-cw721-staked = { workspace = true, features = ["library"] } -dao-voting-token-staked = { workspace = true, features = ["library"] } -vending-factory = { workspace = true, features = ["library"] } -vending-minter = { workspace = true, features = ["library"] } +# vending-factory = { workspace = true, features = ["library"] } +# vending-minter = { workspace = true, features = ["library"] } cw-tokenfactory-issuer = { workspace = true, features = ["library"] } -sg721-base = { workspace = true, features = ["library"] } +# sg721-base = { workspace = true, features = ["library"] } [dev-dependencies] cw-multi-test = { workspace = true } diff --git a/test-contracts/dao-test-custom-factory/src/contract.rs b/test-contracts/dao-test-custom-factory/src/contract.rs index 69e2a67bf..8250dd8df 100644 --- a/test-contracts/dao-test-custom-factory/src/contract.rs +++ b/test-contracts/dao-test-custom-factory/src/contract.rs @@ -10,8 +10,9 @@ use cw_tokenfactory_issuer::msg::{ ExecuteMsg as IssuerExecuteMsg, InstantiateMsg as IssuerInstantiateMsg, }; use cw_utils::parse_reply_instantiate_data; -use dao_voting_token_staked::msg::{ - FactoryCallback, InitialBalance, NewTokenInfo, QueryMsg as TokenVotingQueryMsg, +use dao_interface::{ + token::{FactoryCallback, InitialBalance, NewTokenInfo}, + voting::Query as VotingModuleQueryMsg, }; use crate::{ @@ -32,8 +33,8 @@ const TOKEN_INFO: Item = Item::new("token_info"); pub fn instantiate( deps: DepsMut, _env: Env, - info: MessageInfo, - msg: InstantiateMsg, + _info: MessageInfo, + _msg: InstantiateMsg, ) -> Result { set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; @@ -69,7 +70,7 @@ pub fn execute_token_factory_factory( // Query for DAO let dao: Addr = deps .querier - .query_wasm_smart(info.sender, &TokenVotingQueryMsg::Dao {})?; + .query_wasm_smart(info.sender, &VotingModuleQueryMsg::Dao {})?; // Save DAO and TOKEN_INFO for use in replies DAO.save(deps.storage, &dao)?; diff --git a/test-contracts/dao-test-custom-factory/src/lib.rs b/test-contracts/dao-test-custom-factory/src/lib.rs index 07a196052..3915b791e 100644 --- a/test-contracts/dao-test-custom-factory/src/lib.rs +++ b/test-contracts/dao-test-custom-factory/src/lib.rs @@ -3,4 +3,3 @@ mod error; pub mod msg; pub use crate::error::ContractError; -pub use dao_voting_token_staked::msg::{InitialBalance, NewTokenInfo}; diff --git a/test-contracts/dao-test-custom-factory/src/msg.rs b/test-contracts/dao-test-custom-factory/src/msg.rs index 5586455bd..bbeb6c66f 100644 --- a/test-contracts/dao-test-custom-factory/src/msg.rs +++ b/test-contracts/dao-test-custom-factory/src/msg.rs @@ -1,6 +1,6 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; use cosmwasm_std::WasmMsg; -use dao_voting_token_staked::msg::NewTokenInfo; +use dao_interface::token::NewTokenInfo; #[cw_serde] pub struct InstantiateMsg {} From d93621019b87621d8fc85e5d3a39d1d77345fbbf Mon Sep 17 00:00:00 2001 From: Jake Hartnell Date: Thu, 14 Sep 2023 20:14:32 -0700 Subject: [PATCH 06/30] Move test-contracts to contracts/test Needed for workspace optimizer as we are now using test contract binaries with tests --- Cargo.lock | 74 +++++++++---------- Cargo.toml | 10 +-- .../dao-proposal-hook-counter/.cargo/config | 0 .../dao-proposal-hook-counter/.gitignore | 0 .../dao-proposal-hook-counter/Cargo.toml | 0 .../test}/dao-proposal-hook-counter/README.md | 0 .../examples/schema.rs | 0 .../schema/execute_msg.json | 0 .../schema/instantiate_msg.json | 0 .../schema/query_msg.json | 0 .../dao-proposal-hook-counter/src/contract.rs | 0 .../dao-proposal-hook-counter/src/error.rs | 0 .../dao-proposal-hook-counter/src/lib.rs | 0 .../dao-proposal-hook-counter/src/msg.rs | 0 .../dao-proposal-hook-counter/src/state.rs | 0 .../dao-proposal-hook-counter/src/tests.rs | 0 .../test}/dao-proposal-sudo/.cargo/config | 0 .../test}/dao-proposal-sudo/.gitignore | 0 .../test}/dao-proposal-sudo/Cargo.toml | 0 .../test}/dao-proposal-sudo/README | 0 .../dao-proposal-sudo/examples/schema.rs | 0 .../schema/admin_response.json | 0 .../schema/dao_response.json | 0 .../dao-proposal-sudo/schema/execute_msg.json | 0 .../schema/info_response.json | 0 .../schema/instantiate_msg.json | 0 .../dao-proposal-sudo/schema/query_msg.json | 0 .../test}/dao-proposal-sudo/src/contract.rs | 0 .../test}/dao-proposal-sudo/src/error.rs | 0 .../test}/dao-proposal-sudo/src/lib.rs | 0 .../test}/dao-proposal-sudo/src/msg.rs | 0 .../test}/dao-proposal-sudo/src/state.rs | 0 .../dao-test-custom-factory/.cargo/config | 0 .../test}/dao-test-custom-factory/Cargo.toml | 0 .../test}/dao-test-custom-factory/README | 0 .../examples/schema.rs | 0 .../dao-test-custom-factory/src/contract.rs | 0 .../dao-test-custom-factory/src/error.rs | 0 .../test}/dao-test-custom-factory/src/lib.rs | 0 .../test}/dao-test-custom-factory/src/msg.rs | 0 .../dao-voting-cw20-balance/.cargo/config | 0 .../test}/dao-voting-cw20-balance/.gitignore | 0 .../test}/dao-voting-cw20-balance/Cargo.toml | 0 .../test}/dao-voting-cw20-balance/README.md | 0 .../examples/schema.rs | 0 .../schema/execute_msg.json | 0 .../schema/info_response.json | 0 .../schema/instantiate_msg.json | 0 .../schema/query_msg.json | 0 .../total_power_at_height_response.json | 0 .../voting_power_at_height_response.json | 0 .../dao-voting-cw20-balance/src/contract.rs | 0 .../dao-voting-cw20-balance/src/error.rs | 0 .../test}/dao-voting-cw20-balance/src/lib.rs | 0 .../test}/dao-voting-cw20-balance/src/msg.rs | 0 .../dao-voting-cw20-balance/src/state.rs | 0 .../dao-voting-cw20-balance/src/tests.rs | 0 57 files changed, 42 insertions(+), 42 deletions(-) rename {test-contracts => contracts/test}/dao-proposal-hook-counter/.cargo/config (100%) rename {test-contracts => contracts/test}/dao-proposal-hook-counter/.gitignore (100%) rename {test-contracts => contracts/test}/dao-proposal-hook-counter/Cargo.toml (100%) rename {test-contracts => contracts/test}/dao-proposal-hook-counter/README.md (100%) rename {test-contracts => contracts/test}/dao-proposal-hook-counter/examples/schema.rs (100%) rename {test-contracts => contracts/test}/dao-proposal-hook-counter/schema/execute_msg.json (100%) rename {test-contracts => contracts/test}/dao-proposal-hook-counter/schema/instantiate_msg.json (100%) rename {test-contracts => contracts/test}/dao-proposal-hook-counter/schema/query_msg.json (100%) rename {test-contracts => contracts/test}/dao-proposal-hook-counter/src/contract.rs (100%) rename {test-contracts => contracts/test}/dao-proposal-hook-counter/src/error.rs (100%) rename {test-contracts => contracts/test}/dao-proposal-hook-counter/src/lib.rs (100%) rename {test-contracts => contracts/test}/dao-proposal-hook-counter/src/msg.rs (100%) rename {test-contracts => contracts/test}/dao-proposal-hook-counter/src/state.rs (100%) rename {test-contracts => contracts/test}/dao-proposal-hook-counter/src/tests.rs (100%) rename {test-contracts => contracts/test}/dao-proposal-sudo/.cargo/config (100%) rename {test-contracts => contracts/test}/dao-proposal-sudo/.gitignore (100%) rename {test-contracts => contracts/test}/dao-proposal-sudo/Cargo.toml (100%) rename {test-contracts => contracts/test}/dao-proposal-sudo/README (100%) rename {test-contracts => contracts/test}/dao-proposal-sudo/examples/schema.rs (100%) rename {test-contracts => contracts/test}/dao-proposal-sudo/schema/admin_response.json (100%) rename {test-contracts => contracts/test}/dao-proposal-sudo/schema/dao_response.json (100%) rename {test-contracts => contracts/test}/dao-proposal-sudo/schema/execute_msg.json (100%) rename {test-contracts => contracts/test}/dao-proposal-sudo/schema/info_response.json (100%) rename {test-contracts => contracts/test}/dao-proposal-sudo/schema/instantiate_msg.json (100%) rename {test-contracts => contracts/test}/dao-proposal-sudo/schema/query_msg.json (100%) rename {test-contracts => contracts/test}/dao-proposal-sudo/src/contract.rs (100%) rename {test-contracts => contracts/test}/dao-proposal-sudo/src/error.rs (100%) rename {test-contracts => contracts/test}/dao-proposal-sudo/src/lib.rs (100%) rename {test-contracts => contracts/test}/dao-proposal-sudo/src/msg.rs (100%) rename {test-contracts => contracts/test}/dao-proposal-sudo/src/state.rs (100%) rename {test-contracts => contracts/test}/dao-test-custom-factory/.cargo/config (100%) rename {test-contracts => contracts/test}/dao-test-custom-factory/Cargo.toml (100%) rename {test-contracts => contracts/test}/dao-test-custom-factory/README (100%) rename {test-contracts => contracts/test}/dao-test-custom-factory/examples/schema.rs (100%) rename {test-contracts => contracts/test}/dao-test-custom-factory/src/contract.rs (100%) rename {test-contracts => contracts/test}/dao-test-custom-factory/src/error.rs (100%) rename {test-contracts => contracts/test}/dao-test-custom-factory/src/lib.rs (100%) rename {test-contracts => contracts/test}/dao-test-custom-factory/src/msg.rs (100%) rename {test-contracts => contracts/test}/dao-voting-cw20-balance/.cargo/config (100%) rename {test-contracts => contracts/test}/dao-voting-cw20-balance/.gitignore (100%) rename {test-contracts => contracts/test}/dao-voting-cw20-balance/Cargo.toml (100%) rename {test-contracts => contracts/test}/dao-voting-cw20-balance/README.md (100%) rename {test-contracts => contracts/test}/dao-voting-cw20-balance/examples/schema.rs (100%) rename {test-contracts => contracts/test}/dao-voting-cw20-balance/schema/execute_msg.json (100%) rename {test-contracts => contracts/test}/dao-voting-cw20-balance/schema/info_response.json (100%) rename {test-contracts => contracts/test}/dao-voting-cw20-balance/schema/instantiate_msg.json (100%) rename {test-contracts => contracts/test}/dao-voting-cw20-balance/schema/query_msg.json (100%) rename {test-contracts => contracts/test}/dao-voting-cw20-balance/schema/total_power_at_height_response.json (100%) rename {test-contracts => contracts/test}/dao-voting-cw20-balance/schema/voting_power_at_height_response.json (100%) rename {test-contracts => contracts/test}/dao-voting-cw20-balance/src/contract.rs (100%) rename {test-contracts => contracts/test}/dao-voting-cw20-balance/src/error.rs (100%) rename {test-contracts => contracts/test}/dao-voting-cw20-balance/src/lib.rs (100%) rename {test-contracts => contracts/test}/dao-voting-cw20-balance/src/msg.rs (100%) rename {test-contracts => contracts/test}/dao-voting-cw20-balance/src/state.rs (100%) rename {test-contracts => contracts/test}/dao-voting-cw20-balance/src/tests.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index 857525a76..8def01025 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -68,7 +68,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.31", + "syn 2.0.33", ] [[package]] @@ -79,7 +79,7 @@ checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.31", + "syn 2.0.33", ] [[package]] @@ -179,9 +179,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.3" +version = "0.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "414dcefbc63d77c526a76b3afcf6fbb9b5e2791c19c3aa2297733208750c6e53" +checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" [[package]] name = "base64ct" @@ -300,9 +300,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.13.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "byteorder" @@ -589,7 +589,7 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a44d3f9c25b2f864737c6605a98f2e4675d53fd8bbc7cf4d7c02475661a793d" dependencies = [ - "base64 0.21.3", + "base64 0.21.4", "bnum", "cosmwasm-crypto", "cosmwasm-derive", @@ -2544,7 +2544,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.31", + "syn 2.0.33", ] [[package]] @@ -2675,7 +2675,7 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06683b93020a07e3dbcf5f8c0f6d40080d725bea7936fc01ad345c01b97dc270" dependencies = [ - "base64 0.21.3", + "base64 0.21.4", "bytes", "headers-core", "http", @@ -3023,9 +3023,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.147" +version = "0.2.148" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" [[package]] name = "libloading" @@ -3045,9 +3045,9 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -version = "0.4.5" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" +checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128" [[package]] name = "log" @@ -3329,7 +3329,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.31", + "syn 2.0.33", ] [[package]] @@ -3360,7 +3360,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 2.0.31", + "syn 2.0.33", ] [[package]] @@ -3403,9 +3403,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.66" +version = "1.0.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" dependencies = [ "unicode-ident", ] @@ -3642,9 +3642,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustix" -version = "0.38.11" +version = "0.38.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0c3dde1fc030af041adc40e79c0e7fbcf431dd24870053d187d7c66e4b87453" +checksum = "d7db8590df6dfcd144d22afd1b83b36c21a18d7cbc1dc4bb5295a8712e9eb662" dependencies = [ "bitflags 2.4.0", "errno", @@ -3843,7 +3843,7 @@ checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.31", + "syn 2.0.33", ] [[package]] @@ -3859,9 +3859,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.105" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" +checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" dependencies = [ "itoa", "ryu", @@ -3876,7 +3876,7 @@ checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.31", + "syn 2.0.33", ] [[package]] @@ -4049,9 +4049,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877" +checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e" dependencies = [ "libc", "windows-sys", @@ -4175,9 +4175,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.31" +version = "2.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "718fa2415bcb8d8bd775917a1bf12a7931b6dfa890753378538118181e0cb398" +checksum = "9caece70c63bfba29ec2fed841a09851b14a235c60010fa4de58089b6c025668" dependencies = [ "proc-macro2", "quote", @@ -4470,7 +4470,7 @@ checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35" dependencies = [ "proc-macro2", "quote", - "syn 2.0.31", + "syn 2.0.33", ] [[package]] @@ -4529,7 +4529,7 @@ dependencies = [ "mio", "num_cpus", "pin-project-lite", - "socket2 0.5.3", + "socket2 0.5.4", "tokio-macros", "windows-sys", ] @@ -4552,7 +4552,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.31", + "syn 2.0.33", ] [[package]] @@ -4640,7 +4640,7 @@ checksum = "3082666a3a6433f7f511c7192923fa1fe07c69332d3c6a2e6bb040b569199d5a" dependencies = [ "async-trait", "axum", - "base64 0.21.3", + "base64 0.21.4", "bytes", "futures-core", "futures-util", @@ -4712,7 +4712,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.31", + "syn 2.0.33", ] [[package]] @@ -4760,9 +4760,9 @@ checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" @@ -4866,7 +4866,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.31", + "syn 2.0.33", "wasm-bindgen-shared", ] @@ -4888,7 +4888,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.31", + "syn 2.0.33", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -5075,5 +5075,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.31", + "syn 2.0.33", ] diff --git a/Cargo.toml b/Cargo.toml index 2bca65d85..e126657e6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,9 +5,9 @@ members = [ "contracts/proposal/*", "contracts/pre-propose/*", "contracts/staking/*", + "contracts/test/*", "contracts/voting/*", "packages/*", - "test-contracts/*", "ci/*", ] exclude = ["ci/configs/", "wasmvm/libwasmvm"] @@ -105,12 +105,12 @@ dao-pre-propose-single = { path = "./contracts/pre-propose/dao-pre-propose-singl dao-proposal-condorcet = { path = "./contracts/proposal/dao-proposal-condorcet", version = "2.2.0" } dao-proposal-multiple = { path = "./contracts/proposal/dao-proposal-multiple", version = "2.2.0" } dao-proposal-single = { path = "./contracts/proposal/dao-proposal-single", version = "2.2.0" } -dao-proposal-sudo = { path = "./test-contracts/dao-proposal-sudo", version = "2.2.0" } -dao-proposal-hook-counter = { path = "./test-contracts/dao-proposal-hook-counter", version = "2.2.0" } -dao-test-custom-factory = { path = "./test-contracts/dao-test-custom-factory", version = "*" } +dao-proposal-sudo = { path = "./contracts/test/dao-proposal-sudo", version = "2.2.0" } +dao-proposal-hook-counter = { path = "./contracts/test/dao-proposal-hook-counter", version = "2.2.0" } +dao-test-custom-factory = { path = "./contracts/test/dao-test-custom-factory", version = "*" } dao-testing = { path = "./packages/dao-testing", version = "2.2.0" } dao-voting = { path = "./packages/dao-voting", version = "2.2.0" } -dao-voting-cw20-balance = { path = "./test-contracts/dao-voting-cw20-balance", version = "2.2.0" } +dao-voting-cw20-balance = { path = "./contracts/test/dao-voting-cw20-balance", version = "2.2.0" } dao-voting-cw20-staked = { path = "./contracts/voting/dao-voting-cw20-staked", version = "2.2.0" } dao-voting-cw4 = { path = "./contracts/voting/dao-voting-cw4", version = "2.2.0" } dao-voting-cw721-roles = { path = "./contracts/voting/dao-voting-cw721-roles", version = "*" } diff --git a/test-contracts/dao-proposal-hook-counter/.cargo/config b/contracts/test/dao-proposal-hook-counter/.cargo/config similarity index 100% rename from test-contracts/dao-proposal-hook-counter/.cargo/config rename to contracts/test/dao-proposal-hook-counter/.cargo/config diff --git a/test-contracts/dao-proposal-hook-counter/.gitignore b/contracts/test/dao-proposal-hook-counter/.gitignore similarity index 100% rename from test-contracts/dao-proposal-hook-counter/.gitignore rename to contracts/test/dao-proposal-hook-counter/.gitignore diff --git a/test-contracts/dao-proposal-hook-counter/Cargo.toml b/contracts/test/dao-proposal-hook-counter/Cargo.toml similarity index 100% rename from test-contracts/dao-proposal-hook-counter/Cargo.toml rename to contracts/test/dao-proposal-hook-counter/Cargo.toml diff --git a/test-contracts/dao-proposal-hook-counter/README.md b/contracts/test/dao-proposal-hook-counter/README.md similarity index 100% rename from test-contracts/dao-proposal-hook-counter/README.md rename to contracts/test/dao-proposal-hook-counter/README.md diff --git a/test-contracts/dao-proposal-hook-counter/examples/schema.rs b/contracts/test/dao-proposal-hook-counter/examples/schema.rs similarity index 100% rename from test-contracts/dao-proposal-hook-counter/examples/schema.rs rename to contracts/test/dao-proposal-hook-counter/examples/schema.rs diff --git a/test-contracts/dao-proposal-hook-counter/schema/execute_msg.json b/contracts/test/dao-proposal-hook-counter/schema/execute_msg.json similarity index 100% rename from test-contracts/dao-proposal-hook-counter/schema/execute_msg.json rename to contracts/test/dao-proposal-hook-counter/schema/execute_msg.json diff --git a/test-contracts/dao-proposal-hook-counter/schema/instantiate_msg.json b/contracts/test/dao-proposal-hook-counter/schema/instantiate_msg.json similarity index 100% rename from test-contracts/dao-proposal-hook-counter/schema/instantiate_msg.json rename to contracts/test/dao-proposal-hook-counter/schema/instantiate_msg.json diff --git a/test-contracts/dao-proposal-hook-counter/schema/query_msg.json b/contracts/test/dao-proposal-hook-counter/schema/query_msg.json similarity index 100% rename from test-contracts/dao-proposal-hook-counter/schema/query_msg.json rename to contracts/test/dao-proposal-hook-counter/schema/query_msg.json diff --git a/test-contracts/dao-proposal-hook-counter/src/contract.rs b/contracts/test/dao-proposal-hook-counter/src/contract.rs similarity index 100% rename from test-contracts/dao-proposal-hook-counter/src/contract.rs rename to contracts/test/dao-proposal-hook-counter/src/contract.rs diff --git a/test-contracts/dao-proposal-hook-counter/src/error.rs b/contracts/test/dao-proposal-hook-counter/src/error.rs similarity index 100% rename from test-contracts/dao-proposal-hook-counter/src/error.rs rename to contracts/test/dao-proposal-hook-counter/src/error.rs diff --git a/test-contracts/dao-proposal-hook-counter/src/lib.rs b/contracts/test/dao-proposal-hook-counter/src/lib.rs similarity index 100% rename from test-contracts/dao-proposal-hook-counter/src/lib.rs rename to contracts/test/dao-proposal-hook-counter/src/lib.rs diff --git a/test-contracts/dao-proposal-hook-counter/src/msg.rs b/contracts/test/dao-proposal-hook-counter/src/msg.rs similarity index 100% rename from test-contracts/dao-proposal-hook-counter/src/msg.rs rename to contracts/test/dao-proposal-hook-counter/src/msg.rs diff --git a/test-contracts/dao-proposal-hook-counter/src/state.rs b/contracts/test/dao-proposal-hook-counter/src/state.rs similarity index 100% rename from test-contracts/dao-proposal-hook-counter/src/state.rs rename to contracts/test/dao-proposal-hook-counter/src/state.rs diff --git a/test-contracts/dao-proposal-hook-counter/src/tests.rs b/contracts/test/dao-proposal-hook-counter/src/tests.rs similarity index 100% rename from test-contracts/dao-proposal-hook-counter/src/tests.rs rename to contracts/test/dao-proposal-hook-counter/src/tests.rs diff --git a/test-contracts/dao-proposal-sudo/.cargo/config b/contracts/test/dao-proposal-sudo/.cargo/config similarity index 100% rename from test-contracts/dao-proposal-sudo/.cargo/config rename to contracts/test/dao-proposal-sudo/.cargo/config diff --git a/test-contracts/dao-proposal-sudo/.gitignore b/contracts/test/dao-proposal-sudo/.gitignore similarity index 100% rename from test-contracts/dao-proposal-sudo/.gitignore rename to contracts/test/dao-proposal-sudo/.gitignore diff --git a/test-contracts/dao-proposal-sudo/Cargo.toml b/contracts/test/dao-proposal-sudo/Cargo.toml similarity index 100% rename from test-contracts/dao-proposal-sudo/Cargo.toml rename to contracts/test/dao-proposal-sudo/Cargo.toml diff --git a/test-contracts/dao-proposal-sudo/README b/contracts/test/dao-proposal-sudo/README similarity index 100% rename from test-contracts/dao-proposal-sudo/README rename to contracts/test/dao-proposal-sudo/README diff --git a/test-contracts/dao-proposal-sudo/examples/schema.rs b/contracts/test/dao-proposal-sudo/examples/schema.rs similarity index 100% rename from test-contracts/dao-proposal-sudo/examples/schema.rs rename to contracts/test/dao-proposal-sudo/examples/schema.rs diff --git a/test-contracts/dao-proposal-sudo/schema/admin_response.json b/contracts/test/dao-proposal-sudo/schema/admin_response.json similarity index 100% rename from test-contracts/dao-proposal-sudo/schema/admin_response.json rename to contracts/test/dao-proposal-sudo/schema/admin_response.json diff --git a/test-contracts/dao-proposal-sudo/schema/dao_response.json b/contracts/test/dao-proposal-sudo/schema/dao_response.json similarity index 100% rename from test-contracts/dao-proposal-sudo/schema/dao_response.json rename to contracts/test/dao-proposal-sudo/schema/dao_response.json diff --git a/test-contracts/dao-proposal-sudo/schema/execute_msg.json b/contracts/test/dao-proposal-sudo/schema/execute_msg.json similarity index 100% rename from test-contracts/dao-proposal-sudo/schema/execute_msg.json rename to contracts/test/dao-proposal-sudo/schema/execute_msg.json diff --git a/test-contracts/dao-proposal-sudo/schema/info_response.json b/contracts/test/dao-proposal-sudo/schema/info_response.json similarity index 100% rename from test-contracts/dao-proposal-sudo/schema/info_response.json rename to contracts/test/dao-proposal-sudo/schema/info_response.json diff --git a/test-contracts/dao-proposal-sudo/schema/instantiate_msg.json b/contracts/test/dao-proposal-sudo/schema/instantiate_msg.json similarity index 100% rename from test-contracts/dao-proposal-sudo/schema/instantiate_msg.json rename to contracts/test/dao-proposal-sudo/schema/instantiate_msg.json diff --git a/test-contracts/dao-proposal-sudo/schema/query_msg.json b/contracts/test/dao-proposal-sudo/schema/query_msg.json similarity index 100% rename from test-contracts/dao-proposal-sudo/schema/query_msg.json rename to contracts/test/dao-proposal-sudo/schema/query_msg.json diff --git a/test-contracts/dao-proposal-sudo/src/contract.rs b/contracts/test/dao-proposal-sudo/src/contract.rs similarity index 100% rename from test-contracts/dao-proposal-sudo/src/contract.rs rename to contracts/test/dao-proposal-sudo/src/contract.rs diff --git a/test-contracts/dao-proposal-sudo/src/error.rs b/contracts/test/dao-proposal-sudo/src/error.rs similarity index 100% rename from test-contracts/dao-proposal-sudo/src/error.rs rename to contracts/test/dao-proposal-sudo/src/error.rs diff --git a/test-contracts/dao-proposal-sudo/src/lib.rs b/contracts/test/dao-proposal-sudo/src/lib.rs similarity index 100% rename from test-contracts/dao-proposal-sudo/src/lib.rs rename to contracts/test/dao-proposal-sudo/src/lib.rs diff --git a/test-contracts/dao-proposal-sudo/src/msg.rs b/contracts/test/dao-proposal-sudo/src/msg.rs similarity index 100% rename from test-contracts/dao-proposal-sudo/src/msg.rs rename to contracts/test/dao-proposal-sudo/src/msg.rs diff --git a/test-contracts/dao-proposal-sudo/src/state.rs b/contracts/test/dao-proposal-sudo/src/state.rs similarity index 100% rename from test-contracts/dao-proposal-sudo/src/state.rs rename to contracts/test/dao-proposal-sudo/src/state.rs diff --git a/test-contracts/dao-test-custom-factory/.cargo/config b/contracts/test/dao-test-custom-factory/.cargo/config similarity index 100% rename from test-contracts/dao-test-custom-factory/.cargo/config rename to contracts/test/dao-test-custom-factory/.cargo/config diff --git a/test-contracts/dao-test-custom-factory/Cargo.toml b/contracts/test/dao-test-custom-factory/Cargo.toml similarity index 100% rename from test-contracts/dao-test-custom-factory/Cargo.toml rename to contracts/test/dao-test-custom-factory/Cargo.toml diff --git a/test-contracts/dao-test-custom-factory/README b/contracts/test/dao-test-custom-factory/README similarity index 100% rename from test-contracts/dao-test-custom-factory/README rename to contracts/test/dao-test-custom-factory/README diff --git a/test-contracts/dao-test-custom-factory/examples/schema.rs b/contracts/test/dao-test-custom-factory/examples/schema.rs similarity index 100% rename from test-contracts/dao-test-custom-factory/examples/schema.rs rename to contracts/test/dao-test-custom-factory/examples/schema.rs diff --git a/test-contracts/dao-test-custom-factory/src/contract.rs b/contracts/test/dao-test-custom-factory/src/contract.rs similarity index 100% rename from test-contracts/dao-test-custom-factory/src/contract.rs rename to contracts/test/dao-test-custom-factory/src/contract.rs diff --git a/test-contracts/dao-test-custom-factory/src/error.rs b/contracts/test/dao-test-custom-factory/src/error.rs similarity index 100% rename from test-contracts/dao-test-custom-factory/src/error.rs rename to contracts/test/dao-test-custom-factory/src/error.rs diff --git a/test-contracts/dao-test-custom-factory/src/lib.rs b/contracts/test/dao-test-custom-factory/src/lib.rs similarity index 100% rename from test-contracts/dao-test-custom-factory/src/lib.rs rename to contracts/test/dao-test-custom-factory/src/lib.rs diff --git a/test-contracts/dao-test-custom-factory/src/msg.rs b/contracts/test/dao-test-custom-factory/src/msg.rs similarity index 100% rename from test-contracts/dao-test-custom-factory/src/msg.rs rename to contracts/test/dao-test-custom-factory/src/msg.rs diff --git a/test-contracts/dao-voting-cw20-balance/.cargo/config b/contracts/test/dao-voting-cw20-balance/.cargo/config similarity index 100% rename from test-contracts/dao-voting-cw20-balance/.cargo/config rename to contracts/test/dao-voting-cw20-balance/.cargo/config diff --git a/test-contracts/dao-voting-cw20-balance/.gitignore b/contracts/test/dao-voting-cw20-balance/.gitignore similarity index 100% rename from test-contracts/dao-voting-cw20-balance/.gitignore rename to contracts/test/dao-voting-cw20-balance/.gitignore diff --git a/test-contracts/dao-voting-cw20-balance/Cargo.toml b/contracts/test/dao-voting-cw20-balance/Cargo.toml similarity index 100% rename from test-contracts/dao-voting-cw20-balance/Cargo.toml rename to contracts/test/dao-voting-cw20-balance/Cargo.toml diff --git a/test-contracts/dao-voting-cw20-balance/README.md b/contracts/test/dao-voting-cw20-balance/README.md similarity index 100% rename from test-contracts/dao-voting-cw20-balance/README.md rename to contracts/test/dao-voting-cw20-balance/README.md diff --git a/test-contracts/dao-voting-cw20-balance/examples/schema.rs b/contracts/test/dao-voting-cw20-balance/examples/schema.rs similarity index 100% rename from test-contracts/dao-voting-cw20-balance/examples/schema.rs rename to contracts/test/dao-voting-cw20-balance/examples/schema.rs diff --git a/test-contracts/dao-voting-cw20-balance/schema/execute_msg.json b/contracts/test/dao-voting-cw20-balance/schema/execute_msg.json similarity index 100% rename from test-contracts/dao-voting-cw20-balance/schema/execute_msg.json rename to contracts/test/dao-voting-cw20-balance/schema/execute_msg.json diff --git a/test-contracts/dao-voting-cw20-balance/schema/info_response.json b/contracts/test/dao-voting-cw20-balance/schema/info_response.json similarity index 100% rename from test-contracts/dao-voting-cw20-balance/schema/info_response.json rename to contracts/test/dao-voting-cw20-balance/schema/info_response.json diff --git a/test-contracts/dao-voting-cw20-balance/schema/instantiate_msg.json b/contracts/test/dao-voting-cw20-balance/schema/instantiate_msg.json similarity index 100% rename from test-contracts/dao-voting-cw20-balance/schema/instantiate_msg.json rename to contracts/test/dao-voting-cw20-balance/schema/instantiate_msg.json diff --git a/test-contracts/dao-voting-cw20-balance/schema/query_msg.json b/contracts/test/dao-voting-cw20-balance/schema/query_msg.json similarity index 100% rename from test-contracts/dao-voting-cw20-balance/schema/query_msg.json rename to contracts/test/dao-voting-cw20-balance/schema/query_msg.json diff --git a/test-contracts/dao-voting-cw20-balance/schema/total_power_at_height_response.json b/contracts/test/dao-voting-cw20-balance/schema/total_power_at_height_response.json similarity index 100% rename from test-contracts/dao-voting-cw20-balance/schema/total_power_at_height_response.json rename to contracts/test/dao-voting-cw20-balance/schema/total_power_at_height_response.json diff --git a/test-contracts/dao-voting-cw20-balance/schema/voting_power_at_height_response.json b/contracts/test/dao-voting-cw20-balance/schema/voting_power_at_height_response.json similarity index 100% rename from test-contracts/dao-voting-cw20-balance/schema/voting_power_at_height_response.json rename to contracts/test/dao-voting-cw20-balance/schema/voting_power_at_height_response.json diff --git a/test-contracts/dao-voting-cw20-balance/src/contract.rs b/contracts/test/dao-voting-cw20-balance/src/contract.rs similarity index 100% rename from test-contracts/dao-voting-cw20-balance/src/contract.rs rename to contracts/test/dao-voting-cw20-balance/src/contract.rs diff --git a/test-contracts/dao-voting-cw20-balance/src/error.rs b/contracts/test/dao-voting-cw20-balance/src/error.rs similarity index 100% rename from test-contracts/dao-voting-cw20-balance/src/error.rs rename to contracts/test/dao-voting-cw20-balance/src/error.rs diff --git a/test-contracts/dao-voting-cw20-balance/src/lib.rs b/contracts/test/dao-voting-cw20-balance/src/lib.rs similarity index 100% rename from test-contracts/dao-voting-cw20-balance/src/lib.rs rename to contracts/test/dao-voting-cw20-balance/src/lib.rs diff --git a/test-contracts/dao-voting-cw20-balance/src/msg.rs b/contracts/test/dao-voting-cw20-balance/src/msg.rs similarity index 100% rename from test-contracts/dao-voting-cw20-balance/src/msg.rs rename to contracts/test/dao-voting-cw20-balance/src/msg.rs diff --git a/test-contracts/dao-voting-cw20-balance/src/state.rs b/contracts/test/dao-voting-cw20-balance/src/state.rs similarity index 100% rename from test-contracts/dao-voting-cw20-balance/src/state.rs rename to contracts/test/dao-voting-cw20-balance/src/state.rs diff --git a/test-contracts/dao-voting-cw20-balance/src/tests.rs b/contracts/test/dao-voting-cw20-balance/src/tests.rs similarity index 100% rename from test-contracts/dao-voting-cw20-balance/src/tests.rs rename to contracts/test/dao-voting-cw20-balance/src/tests.rs From d64fbebd5e27af0426173fc7f21adabe80b9db64 Mon Sep 17 00:00:00 2001 From: Jake Hartnell Date: Thu, 14 Sep 2023 20:21:26 -0700 Subject: [PATCH 07/30] Clippy. : ) --- contracts/voting/dao-voting-cw721-staked/src/contract.rs | 4 ++-- contracts/voting/dao-voting-token-staked/src/contract.rs | 6 +++--- .../src/tests/test_tube/integration_tests.rs | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/contracts/voting/dao-voting-cw721-staked/src/contract.rs b/contracts/voting/dao-voting-cw721-staked/src/contract.rs index 4cfd0d850..de0ac036f 100644 --- a/contracts/voting/dao-voting-cw721-staked/src/contract.rs +++ b/contracts/voting/dao-voting-cw721-staked/src/contract.rs @@ -194,7 +194,7 @@ pub fn instantiate( }, FACTORY_EXECUTE_REPLY_ID, ))), - _ => return Err(ContractError::UnsupportedFactoryMsg {}), + _ => Err(ContractError::UnsupportedFactoryMsg {}), }, } } @@ -756,7 +756,7 @@ pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result return Err(ContractError::Unauthorized {}), + None => Err(ContractError::Unauthorized {}), } } _ => Err(ContractError::UnknownReplyId { id: msg.id }), diff --git a/contracts/voting/dao-voting-token-staked/src/contract.rs b/contracts/voting/dao-voting-token-staked/src/contract.rs index 50a0aff7d..adb6dcbe1 100644 --- a/contracts/voting/dao-voting-token-staked/src/contract.rs +++ b/contracts/voting/dao-voting-token-staked/src/contract.rs @@ -142,7 +142,7 @@ pub fn instantiate( }, FACTORY_EXECUTE_REPLY_ID, ))), - _ => return Err(ContractError::UnsupportedFactoryMsg {}), + _ => Err(ContractError::UnsupportedFactoryMsg {}), }, } } @@ -732,7 +732,7 @@ pub fn reply(deps: DepsMut, env: Env, msg: Reply) -> Result Result return Err(ContractError::Unauthorized {}), + None => Err(ContractError::Unauthorized {}), } } _ => Err(ContractError::UnknownReplyId { id: msg.id }), 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 bf55cacc7..6083da364 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 @@ -402,7 +402,7 @@ fn test_factory() { }; // Instantiate DAO - let dao = DaoCore::new(&app, &msg, &accounts[0]).unwrap(); + let _dao = DaoCore::new(&app, &msg, &accounts[0]).unwrap(); // TODO query voting module From 5cb8a6a41558439abc150d54a7d4e8146428146e Mon Sep 17 00:00:00 2001 From: Jake Hartnell Date: Fri, 15 Sep 2023 13:53:27 -0700 Subject: [PATCH 08/30] Breakout sg-minter-factory contract --- Cargo.lock | 435 +++++++++++++++++- Cargo.toml | 12 +- .../external/sg-minter-factory/.cargo/config | 4 + .../external/sg-minter-factory/Cargo.toml | 43 ++ contracts/external/sg-minter-factory/README | 2 + .../sg-minter-factory/examples/schema.rs | 10 + .../sg-minter-factory/src/contract.rs | 135 ++++++ .../external/sg-minter-factory/src/error.rs | 21 + .../external/sg-minter-factory/src/lib.rs | 8 + .../external/sg-minter-factory/src/msg.rs | 17 + .../external/sg-minter-factory/src/tests.rs | 96 ++++ .../test/dao-test-custom-factory/Cargo.toml | 7 +- contracts/test/dao-test-custom-factory/README | 2 +- .../dao-test-custom-factory/src/contract.rs | 37 +- .../test/dao-test-custom-factory/src/error.rs | 3 + .../test/dao-test-custom-factory/src/msg.rs | 2 - .../voting/dao-voting-cw721-staked/Cargo.toml | 2 +- 17 files changed, 792 insertions(+), 44 deletions(-) create mode 100644 contracts/external/sg-minter-factory/.cargo/config create mode 100644 contracts/external/sg-minter-factory/Cargo.toml create mode 100644 contracts/external/sg-minter-factory/README create mode 100644 contracts/external/sg-minter-factory/examples/schema.rs create mode 100644 contracts/external/sg-minter-factory/src/contract.rs create mode 100644 contracts/external/sg-minter-factory/src/error.rs create mode 100644 contracts/external/sg-minter-factory/src/lib.rs create mode 100644 contracts/external/sg-minter-factory/src/msg.rs create mode 100644 contracts/external/sg-minter-factory/src/tests.rs diff --git a/Cargo.lock b/Cargo.lock index 8def01025..2e507602f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -43,6 +43,12 @@ version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + [[package]] name = "assert_matches" version = "1.5.0" @@ -159,6 +165,51 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base-factory" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ccff69b8782aaa2104b32c76d22bd58c3ab71487e9026ae40fb3e705549334" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 1.1.0", + "cw-utils 1.0.1", + "cw2 1.1.0", + "schemars", + "serde", + "sg-std", + "sg1", + "sg2", + "thiserror", +] + +[[package]] +name = "base-minter" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddc0fabe73ec51bb940bcaf8693a60967386a224d172db90d0609978006052fd" +dependencies = [ + "base-factory", + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 1.1.0", + "cw-utils 1.0.1", + "cw2 1.1.0", + "cw721 0.18.0", + "cw721-base 0.18.0", + "schemars", + "serde", + "sg-std", + "sg1", + "sg2", + "sg4", + "sg721", + "sg721-base", + "thiserror", + "url", +] + [[package]] name = "base16ct" version = "0.1.1" @@ -242,6 +293,28 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +[[package]] +name = "bitvec" +version = "0.17.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41262f11d771fd4a61aa3ce019fca363b4b6c282fca9da2a31186d3965a47a5c" +dependencies = [ + "either", + "radium 0.3.0", +] + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium 0.7.0", + "tap", + "wyz", +] + [[package]] name = "block-buffer" version = "0.9.0" @@ -289,6 +362,51 @@ dependencies = [ "serde_yaml", ] +[[package]] +name = "borsh" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4114279215a005bc675e386011e594e1d9b800918cea18fcadadcce864a2046b" +dependencies = [ + "borsh-derive", + "hashbrown 0.12.3", +] + +[[package]] +name = "borsh-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0754613691538d51f329cce9af41d7b7ca150bc973056f1156611489475f54f7" +dependencies = [ + "borsh-derive-internal", + "borsh-schema-derive-internal", + "proc-macro-crate", + "proc-macro2", + "syn 1.0.109", +] + +[[package]] +name = "borsh-derive-internal" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afb438156919598d2c7bad7e1c0adf3d26ed3840dbc010db1a882a65583ca2fb" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "borsh-schema-derive-internal" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634205cc43f74a1b9046ef87c4540ebda95696ec0f315024860cad7c5b0f5ccd" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "bs58" version = "0.4.0" @@ -304,6 +422,28 @@ version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +[[package]] +name = "bytecheck" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6372023ac861f6e6dc89c8344a8f398fb42aaba2b5dbc649ca0c0e9dbcb627" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7ec4c6f261935ad534c0c22dbef2201b45918860eb1c574b972bd213a76af61" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "byteorder" version = "1.4.3" @@ -2488,6 +2628,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8cbd1169bd7b4a0a20d92b9af7a7e0422888bd38a6f5ec29c1fd8c1558a272e" +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + [[package]] name = "futures" version = "0.3.28" @@ -3401,6 +3547,15 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "proc-macro-crate" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" +dependencies = [ + "toml", +] + [[package]] name = "proc-macro2" version = "1.0.67" @@ -3477,6 +3632,26 @@ dependencies = [ "prost 0.11.9", ] +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "quote" version = "1.0.33" @@ -3486,6 +3661,18 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "radium" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "def50a86306165861203e7f84ecffbbdfdea79f0e51039b33de1e952358c47ac" + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + [[package]] name = "rand" version = "0.8.5" @@ -3522,6 +3709,15 @@ dependencies = [ "getrandom", ] +[[package]] +name = "rand_xoshiro" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" +dependencies = [ + "rand_core 0.6.4", +] + [[package]] name = "regex" version = "1.9.5" @@ -3551,6 +3747,15 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" +[[package]] +name = "rend" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581008d2099240d37fb08d77ad713bcaec2c4d89d50b5b21a8bb1996bbab68ab" +dependencies = [ + "bytecheck", +] + [[package]] name = "rfc6979" version = "0.3.1" @@ -3607,6 +3812,34 @@ dependencies = [ "opaque-debug", ] +[[package]] +name = "rkyv" +version = "0.7.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0200c8230b013893c0b2d6213d6ec64ed2b9be2e0e016682b7224ff82cff5c58" +dependencies = [ + "bitvec 1.0.1", + "bytecheck", + "hashbrown 0.12.3", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", + "tinyvec", + "uuid 1.4.1", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2e06b915b5c230a17d7a736d1e2e63ee753c256a8614ef3f5147b13a4f5541d" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "ron" version = "0.7.1" @@ -3628,6 +3861,22 @@ dependencies = [ "ordered-multimap", ] +[[package]] +name = "rust_decimal" +version = "1.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c4216490d5a413bc6d10fa4742bd7d4955941d062c0ef873141d6b0e7b30fd" +dependencies = [ + "arrayvec", + "borsh", + "bytes", + "num-traits", + "rand", + "rkyv", + "serde", + "serde_json", +] + [[package]] name = "rustc-demangle" version = "0.1.23" @@ -3742,6 +3991,12 @@ dependencies = [ "untrusted", ] +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + [[package]] name = "sec1" version = "0.3.0" @@ -3892,6 +4147,32 @@ dependencies = [ "unsafe-libyaml", ] +[[package]] +name = "sg-minter-factory" +version = "2.2.0" +dependencies = [ + "base-factory", + "base-minter", + "cosmwasm-schema", + "cosmwasm-std", + "cosmwasm-storage", + "cw-multi-test", + "cw-ownable", + "cw-storage-plus 1.1.0", + "cw-utils 1.0.1", + "cw2 1.1.0", + "dao-dao-macros", + "dao-interface", + "dao-voting-cw721-staked", + "sg-multi-test", + "sg-std", + "sg2", + "sg721-base", + "thiserror", + "vending-factory", + "vending-minter", +] + [[package]] name = "sg-multi-test" version = "3.1.0" @@ -3921,6 +4202,67 @@ dependencies = [ "thiserror", ] +[[package]] +name = "sg-whitelist" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "743da3e55d0f52e28dd8c9ba060bbf4258102eb2d284b9a9ab15a547e2f8b48b" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 1.1.0", + "cw-utils 1.0.1", + "cw2 1.1.0", + "rust_decimal", + "schemars", + "serde", + "sg-std", + "sg1", + "thiserror", +] + +[[package]] +name = "sg1" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b57d46d7140a36563b8ca2d24d76e2caba09fd5327f6e89cbc83f788412bfab" +dependencies = [ + "cosmwasm-std", + "cw-utils 1.0.1", + "serde", + "sg-std", + "thiserror", +] + +[[package]] +name = "sg2" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9731226da77a03119a7e977e8d15b7b5f6f27396bb4003cc6449fafabaec213" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 1.1.0", + "cw-utils 1.0.1", + "schemars", + "serde", + "sg721", + "thiserror", +] + +[[package]] +name = "sg4" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "805cc3e44916242a540788e0cc5ed2d4665f95e3dcd91d4b37385a7bd82805a1" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "schemars", + "serde", + "thiserror", +] + [[package]] name = "sg721" version = "3.1.0" @@ -4008,6 +4350,16 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" +[[package]] +name = "shuffle" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fecbe806be0726e22a6192ce0ec2f90e0798946ae0cbc2c10c7a3c68cae4eb2" +dependencies = [ + "bitvec 0.17.4", + "rand", +] + [[package]] name = "signature" version = "1.6.4" @@ -4028,6 +4380,12 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "simdutf8" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" + [[package]] name = "slab" version = "0.4.9" @@ -4190,6 +4548,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "tendermint" version = "0.23.9" @@ -4363,7 +4727,7 @@ dependencies = [ "tokio", "tracing", "url", - "uuid", + "uuid 0.8.2", "walkdir", ] @@ -4397,7 +4761,7 @@ dependencies = [ "tokio", "tracing", "url", - "uuid", + "uuid 0.8.2", "walkdir", ] @@ -4802,6 +5166,64 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" +[[package]] +name = "uuid" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" + +[[package]] +name = "vending-factory" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "610ec4018e2b033ac836c9e04534a4bd8164c386ae3963bd2aa142c4d77c6749" +dependencies = [ + "base-factory", + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 1.1.0", + "cw-utils 1.0.1", + "cw2 1.1.0", + "schemars", + "serde", + "sg-std", + "sg1", + "sg2", + "sg721", + "thiserror", +] + +[[package]] +name = "vending-minter" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3035a883d15d475051fe15ae34c6b5c6ac99f572ae7e5fda28d28f72e0217a1" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 1.1.0", + "cw-utils 1.0.1", + "cw2 1.1.0", + "cw721 0.18.0", + "cw721-base 0.18.0", + "rand_core 0.6.4", + "rand_xoshiro", + "schemars", + "semver", + "serde", + "sg-std", + "sg-whitelist", + "sg1", + "sg2", + "sg4", + "sg721", + "sha2 0.10.7", + "shuffle", + "thiserror", + "url", + "vending-factory", +] + [[package]] name = "version_check" version = "0.9.4" @@ -5049,6 +5471,15 @@ dependencies = [ "thiserror", ] +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + [[package]] name = "yaml-rust" version = "0.4.5" diff --git a/Cargo.toml b/Cargo.toml index e126657e6..dd9636283 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,8 @@ overflow-checks = true [workspace.dependencies] anyhow = { version = "1.0" } assert_matches = "1.5" +base-factory = "3.1.0" +base-minter = "3.1.0" cosm-orc = { version = "4.0" } cosm-tome = "0.2" cosmos-sdk-proto = "0.19" @@ -56,6 +58,9 @@ env_logger = "0.10" once_cell = "1.18" osmosis-std = "0.19.0" osmosis-test-tube = "19.0.0" +# https://github.com/public-awesome/launchpad/issues/616 +# open-edition-factory = "3.1.0" +# open-edition-minter = "3.1.0" proc-macro2 = "1.0" prost = "0.11" quote = "1.0" @@ -64,6 +69,8 @@ schemars = "0.8" serde = { version = "1.0", default-features = false, features = ["derive"] } serde_json = "1.0" serde_yaml = "0.9" +sg1 = "3.1.0" +sg2 = "3.1.0" sg-std = "3.1.0" sg721 = "3.1.0" sg721-base = "3.1.0" @@ -72,8 +79,8 @@ syn = { version = "1.0", features = ["derive"] } test-context = "0.1" thiserror = { version = "1.0" } token-bindings = "0.11.0" -vending-factory = "2.4.0" -vending-minter = "2.4.0" +vending-factory = "3.1.0" +vending-minter = "3.1.0" wynd-utils = "0.4" # One commit ahead of version 0.3.0. Allows initialization with an @@ -116,6 +123,7 @@ dao-voting-cw4 = { path = "./contracts/voting/dao-voting-cw4", version = "2.2.0" dao-voting-cw721-roles = { path = "./contracts/voting/dao-voting-cw721-roles", version = "*" } dao-voting-cw721-staked = { path = "./contracts/voting/dao-voting-cw721-staked", version = "2.2.0" } dao-voting-token-staked = { path = "./contracts/voting/dao-voting-token-staked", version = "2.2.0" } +sg-minter-factory = { path = "./contracts/external/sg-minter-factory", version = "*" } # v1 dependencies. used for state migrations. cw-core-v1 = { package = "cw-core", version = "0.1.0" } diff --git a/contracts/external/sg-minter-factory/.cargo/config b/contracts/external/sg-minter-factory/.cargo/config new file mode 100644 index 000000000..336b618a1 --- /dev/null +++ b/contracts/external/sg-minter-factory/.cargo/config @@ -0,0 +1,4 @@ +[alias] +wasm = "build --release --target wasm32-unknown-unknown" +unit-test = "test --lib" +schema = "run --example schema" diff --git a/contracts/external/sg-minter-factory/Cargo.toml b/contracts/external/sg-minter-factory/Cargo.toml new file mode 100644 index 000000000..82c4419bf --- /dev/null +++ b/contracts/external/sg-minter-factory/Cargo.toml @@ -0,0 +1,43 @@ +[package] +name = "sg-minter-factory" +authors = ["Jake Hartnell"] +description = "A Stargaze minter factory for dao-voting-cw721-staked" +edition = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +version = { workspace = true } + +[lib] +crate-type = ["cdylib", "rlib"] + +[features] +# for more explicit tests, cargo test --features=backtraces +backtraces = ["cosmwasm-std/backtraces"] +# use library feature to disable all instantiate/execute/query exports +library = [] + +[dependencies] +base-factory = { workspace = true, features = ["library"] } +base-minter = { workspace = true, features = ["library"] } +cosmwasm-std = { workspace = true } +cosmwasm-schema = { workspace = true } +cosmwasm-storage = { workspace = true } +cw2 = { workspace = true } +cw-ownable = { workspace = true } +cw-storage-plus = { workspace = true } +cw-utils = { workspace = true } +dao-dao-macros = { workspace = true } +dao-interface = { workspace = true } +# open-edition-factory = { workspace = true, features = ["library"] } +# open-edition-minter = { workspace = true, features = ["library"] } +sg-std = { workspace = true } +sg2 = { workspace = true } +sg721-base = { workspace = true, features = ["library"] } +thiserror = { workspace = true } +vending-factory = { workspace = true, features = ["library"] } +vending-minter = { workspace = true, features = ["library"] } + +[dev-dependencies] +cw-multi-test = { workspace = true } +dao-voting-cw721-staked = { workspace = true } +sg-multi-test = { workspace = true } diff --git a/contracts/external/sg-minter-factory/README b/contracts/external/sg-minter-factory/README new file mode 100644 index 000000000..1be9ff991 --- /dev/null +++ b/contracts/external/sg-minter-factory/README @@ -0,0 +1,2 @@ +# Test Custom Factory contract +Used for testing custom factories with `dao-voting-token-staked` and `dao-voting-cw721-staked`. diff --git a/contracts/external/sg-minter-factory/examples/schema.rs b/contracts/external/sg-minter-factory/examples/schema.rs new file mode 100644 index 000000000..83dd6e8a1 --- /dev/null +++ b/contracts/external/sg-minter-factory/examples/schema.rs @@ -0,0 +1,10 @@ +use cosmwasm_schema::write_api; +use sg_minter_factory::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; + +fn main() { + write_api! { + instantiate: InstantiateMsg, + query: QueryMsg, + execute: ExecuteMsg, + } +} diff --git a/contracts/external/sg-minter-factory/src/contract.rs b/contracts/external/sg-minter-factory/src/contract.rs new file mode 100644 index 000000000..67df87ce3 --- /dev/null +++ b/contracts/external/sg-minter-factory/src/contract.rs @@ -0,0 +1,135 @@ +#[cfg(not(feature = "library"))] +use cosmwasm_std::entry_point; +use cosmwasm_std::{ + from_binary, to_binary, Addr, Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response, + StdResult, SubMsg, Uint128, WasmMsg, +}; +use cw2::set_contract_version; +use cw_storage_plus::Item; +use cw_utils::parse_reply_execute_data; +use dao_interface::voting::Query as VotingModuleQueryMsg; +use vending_factory::msg::VendingMinterCreateMsg; + +use crate::{ + error::ContractError, + msg::{ExecuteMsg, InstantiateMsg, QueryMsg}, +}; + +const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); +const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); + +const INSTANTIATE_STARGAZE_MINTER_REPLY_ID: u64 = 1; + +const DAO: Item = Item::new("dao"); + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn instantiate( + deps: DepsMut, + _env: Env, + info: MessageInfo, + msg: InstantiateMsg, +) -> Result { + set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; + + Ok(Response::new().add_attribute("method", "instantiate")) +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn execute( + deps: DepsMut, + env: Env, + info: MessageInfo, + msg: ExecuteMsg, +) -> Result { + match msg { + ExecuteMsg::StargazeBaseMinterFactory(msg) => { + execute_stargaze_base_minter_factory(deps, env, info, msg) + } + } +} + +/// Example Stargaze factory. +pub fn execute_stargaze_base_minter_factory( + deps: DepsMut, + _env: Env, + info: MessageInfo, + msg: WasmMsg, +) -> Result { + // Query for DAO + let dao: Addr = deps + .querier + .query_wasm_smart(info.sender, &VotingModuleQueryMsg::Dao {})?; + + DAO.save(deps.storage, &dao)?; + + // Parse msg, only an execute message is valid + match msg { + WasmMsg::Execute { + contract_addr, + msg: create_msg, + funds, + } => { + // TODO no match? Doens't really make sense here + // Match Stargaze msg + match from_binary::(&create_msg)? { + VendingMinterCreateMsg { + init_msg, + collection_params, + } => { + // Replace the Stargaze info to set the DAO address + let mut params = collection_params; + params.info.creator = dao.to_string(); + + // TODO replace royalties with DAO address + + // This creates a vending-minter contract and a sg721 contract + // in submsg reply, parse the response and save the contract address + Ok(Response::new().add_submessage(SubMsg::reply_on_success( + WasmMsg::Execute { + contract_addr, + msg: to_binary(&VendingMinterCreateMsg { + init_msg, + collection_params: params, + })?, + funds, + }, + INSTANTIATE_STARGAZE_MINTER_REPLY_ID, + ))) + } + // TODO better error + _ => Err(ContractError::UnsupportedFactoryMsg {}), + } + } + _ => Err(ContractError::UnsupportedFactoryMsg {}), + } +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { + match msg { + QueryMsg::Info {} => query_info(deps), + } +} + +pub fn query_info(deps: Deps) -> StdResult { + let info = cw2::get_contract_version(deps.storage)?; + to_binary(&dao_interface::voting::InfoResponse { info }) +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn reply(deps: DepsMut, env: Env, msg: Reply) -> Result { + match msg.id { + INSTANTIATE_STARGAZE_MINTER_REPLY_ID => { + // TODO get events + let res = parse_reply_execute_data(msg)?; + println!("{:?}", res); + + // TODO filter through events and find sg721_address + // set-data in response so that the voting module can be aware of this address + // and verify it's an NFT contract. + + unimplemented!() + } + _ => Err(ContractError::UnknownReplyId { id: msg.id }), + } +} diff --git a/contracts/external/sg-minter-factory/src/error.rs b/contracts/external/sg-minter-factory/src/error.rs new file mode 100644 index 000000000..ef813fa6d --- /dev/null +++ b/contracts/external/sg-minter-factory/src/error.rs @@ -0,0 +1,21 @@ +use cosmwasm_std::StdError; +use cw_utils::ParseReplyError; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum ContractError { + #[error("{0}")] + Std(#[from] StdError), + + #[error(transparent)] + ParseReplyError(#[from] ParseReplyError), + + #[error("Unauthorized")] + Unauthorized {}, + + #[error("Got a submessage reply with unknown id: {id}")] + UnknownReplyId { id: u64 }, + + #[error("Factory message must serialize to WasmMsg::Execute")] + UnsupportedFactoryMsg {}, +} diff --git a/contracts/external/sg-minter-factory/src/lib.rs b/contracts/external/sg-minter-factory/src/lib.rs new file mode 100644 index 000000000..2286f8eba --- /dev/null +++ b/contracts/external/sg-minter-factory/src/lib.rs @@ -0,0 +1,8 @@ +pub mod contract; +mod error; +pub mod msg; + +#[cfg(test)] +mod tests; + +pub use crate::error::ContractError; diff --git a/contracts/external/sg-minter-factory/src/msg.rs b/contracts/external/sg-minter-factory/src/msg.rs new file mode 100644 index 000000000..1af086b5b --- /dev/null +++ b/contracts/external/sg-minter-factory/src/msg.rs @@ -0,0 +1,17 @@ +use cosmwasm_schema::{cw_serde, QueryResponses}; +use cosmwasm_std::WasmMsg; + +#[cw_serde] +pub struct InstantiateMsg {} + +#[cw_serde] +pub enum ExecuteMsg { + StargazeBaseMinterFactory(WasmMsg), +} + +#[cw_serde] +#[derive(QueryResponses)] +pub enum QueryMsg { + #[returns(dao_interface::voting::InfoResponse)] + Info {}, +} diff --git a/contracts/external/sg-minter-factory/src/tests.rs b/contracts/external/sg-minter-factory/src/tests.rs new file mode 100644 index 000000000..c0dc31c02 --- /dev/null +++ b/contracts/external/sg-minter-factory/src/tests.rs @@ -0,0 +1,96 @@ +use cosmwasm_std::Addr; +use cw_multi_test::{Contract, ContractWrapper, Executor}; +use sg_multi_test::StargazeApp as App; +use sg_std::StargazeMsgWrapper; + +fn sg721_base_contract() -> Box> { + let contract = ContractWrapper::new( + sg721_base::entry::execute, + sg721_base::entry::instantiate, + sg721_base::entry::query, + ); + Box::new(contract) +} + +fn base_minter_contract() -> Box> { + let contract = ContractWrapper::new( + base_minter::contract::execute, + base_minter::contract::instantiate, + base_minter::contract::query, + ) + .with_reply(base_minter::contract::reply); + Box::new(contract) +} + +fn base_factory_contract() -> Box> { + let contract = ContractWrapper::new( + base_factory::contract::execute, + base_factory::contract::instantiate, + base_factory::contract::query, + ); + Box::new(contract) +} + +fn vending_minter_contract() -> Box> { + let contract = ContractWrapper::new( + vending_minter::contract::execute, + vending_minter::contract::instantiate, + vending_minter::contract::query, + ) + .with_reply(vending_minter::contract::reply); + Box::new(contract) +} + +fn vending_factory_contract() -> Box> { + let contract = ContractWrapper::new( + vending_factory::contract::execute, + vending_factory::contract::instantiate, + vending_factory::contract::query, + ); + Box::new(contract) +} + +fn voting_sg721_staked_contract() -> Box> { + let contract = ContractWrapper::new_with_empty( + dao_voting_cw721_staked::contract::execute, + dao_voting_cw721_staked::contract::instantiate, + dao_voting_cw721_staked::contract::query, + ) + .with_reply_empty(dao_voting_cw721_staked::contract::reply); + Box::new(contract) +} + +pub struct TestEnv { + pub app: App, + // pub base_factory: Addr, + pub base_minter_id: u64, + pub sg721_base_id: u64, + // pub vending_factory: Addr, + pub vending_minter_id: u64, + pub voting_: u64, +} + +fn setup() -> TestEnv { + let mut app = App::default(); + let base_factory_id = app.store_code(base_factory_contract()); + let base_minter_id = app.store_code(base_minter_contract()); + let sg721_base_id = app.store_code(sg721_base_contract()); + let vending_factory_id = app.store_code(vending_factory_contract()); + let vending_minter_id = app.store_code(vending_minter_contract()); + let voting_id = app.store_code(voting_sg721_staked_contract()); + + TestEnv { + app, + // base_factory: base_factory_id, + base_minter_id, + sg721_base_id, + // vending_factory: vending_factory_id, + vending_minter_id, + voting_: voting_id, + } +} + +#[test] +fn test_factory_happy_path() { + setup(); +} diff --git a/contracts/test/dao-test-custom-factory/Cargo.toml b/contracts/test/dao-test-custom-factory/Cargo.toml index f96c40a4d..c00f9947d 100644 --- a/contracts/test/dao-test-custom-factory/Cargo.toml +++ b/contracts/test/dao-test-custom-factory/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "dao-test-custom-factory" -authors = ["ekez "] -description = "A proposal module that allows direct execution without voting." +authors = ["Jake Hartnell"] +description = "A test contract for testing factory patterns in dao-voting-token-staked." edition = { workspace = true } license = { workspace = true } repository = { workspace = true } @@ -27,10 +27,7 @@ cw-utils = { workspace = true } thiserror = { workspace = true } dao-dao-macros = { workspace = true } dao-interface = { workspace = true } -# vending-factory = { workspace = true, features = ["library"] } -# vending-minter = { workspace = true, features = ["library"] } cw-tokenfactory-issuer = { workspace = true, features = ["library"] } -# sg721-base = { workspace = true, features = ["library"] } [dev-dependencies] cw-multi-test = { workspace = true } diff --git a/contracts/test/dao-test-custom-factory/README b/contracts/test/dao-test-custom-factory/README index 1be9ff991..9dc8b8b94 100644 --- a/contracts/test/dao-test-custom-factory/README +++ b/contracts/test/dao-test-custom-factory/README @@ -1,2 +1,2 @@ # Test Custom Factory contract -Used for testing custom factories with `dao-voting-token-staked` and `dao-voting-cw721-staked`. +Used for testing custom factories with `dao-voting-token-staked`. diff --git a/contracts/test/dao-test-custom-factory/src/contract.rs b/contracts/test/dao-test-custom-factory/src/contract.rs index 8250dd8df..21778fb05 100644 --- a/contracts/test/dao-test-custom-factory/src/contract.rs +++ b/contracts/test/dao-test-custom-factory/src/contract.rs @@ -1,8 +1,8 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - to_binary, Addr, Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response, StdResult, SubMsg, - Uint128, WasmMsg, + from_binary, to_binary, Addr, Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response, + StdResult, SubMsg, Uint128, WasmMsg, }; use cw2::set_contract_version; use cw_storage_plus::Item; @@ -23,8 +23,7 @@ use crate::{ const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); -const INSTANTIATE_BASE_MINTER_REPLY_ID: u64 = 1; -const INSTANTIATE_ISSUER_REPLY_ID: u64 = 2; +const INSTANTIATE_ISSUER_REPLY_ID: u64 = 1; const DAO: Item = Item::new("dao"); const TOKEN_INFO: Item = Item::new("token_info"); @@ -33,8 +32,8 @@ const TOKEN_INFO: Item = Item::new("token_info"); pub fn instantiate( deps: DepsMut, _env: Env, - _info: MessageInfo, - _msg: InstantiateMsg, + info: MessageInfo, + msg: InstantiateMsg, ) -> Result { set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; @@ -49,9 +48,6 @@ pub fn execute( msg: ExecuteMsg, ) -> Result { match msg { - ExecuteMsg::StargazeBaseMinterFactory(msg) => { - execute_stargaze_base_minter_factory(deps, env, info, msg) - } ExecuteMsg::TokenFactoryFactory(token) => { execute_token_factory_factory(deps, env, info, token) } @@ -94,24 +90,6 @@ pub fn execute_token_factory_factory( Ok(Response::new().add_submessage(msg)) } -/// Example Stargaze factory. -pub fn execute_stargaze_base_minter_factory( - _deps: DepsMut, - _env: Env, - _info: MessageInfo, - _msg: WasmMsg, -) -> Result { - // TODO query voting contract (the sender) for the DAO address - // TODO replace the Stargaze info to set the DAO address - - // TODO call base-factory to create minter - - // TODO this create a base-minter contract and a sg721 contract - - // in submsg reply, parse the response and save the contract address - unimplemented!() -} - #[cfg_attr(not(feature = "library"), entry_point)] pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { match msg { @@ -127,9 +105,6 @@ pub fn query_info(deps: Deps) -> StdResult { #[cfg_attr(not(feature = "library"), entry_point)] pub fn reply(deps: DepsMut, env: Env, msg: Reply) -> Result { match msg.id { - INSTANTIATE_BASE_MINTER_REPLY_ID => { - unimplemented!() - } INSTANTIATE_ISSUER_REPLY_ID => { // Load DAO address and TOKEN_INFO let dao = DAO.load(deps.storage)?; @@ -149,7 +124,7 @@ pub fn reply(deps: DepsMut, env: Env, msg: Reply) -> Result = vec![]; diff --git a/contracts/test/dao-test-custom-factory/src/error.rs b/contracts/test/dao-test-custom-factory/src/error.rs index dbac5032a..ef813fa6d 100644 --- a/contracts/test/dao-test-custom-factory/src/error.rs +++ b/contracts/test/dao-test-custom-factory/src/error.rs @@ -15,4 +15,7 @@ pub enum ContractError { #[error("Got a submessage reply with unknown id: {id}")] UnknownReplyId { id: u64 }, + + #[error("Factory message must serialize to WasmMsg::Execute")] + UnsupportedFactoryMsg {}, } diff --git a/contracts/test/dao-test-custom-factory/src/msg.rs b/contracts/test/dao-test-custom-factory/src/msg.rs index bbeb6c66f..ec285be57 100644 --- a/contracts/test/dao-test-custom-factory/src/msg.rs +++ b/contracts/test/dao-test-custom-factory/src/msg.rs @@ -1,5 +1,4 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; -use cosmwasm_std::WasmMsg; use dao_interface::token::NewTokenInfo; #[cw_serde] @@ -7,7 +6,6 @@ pub struct InstantiateMsg {} #[cw_serde] pub enum ExecuteMsg { - StargazeBaseMinterFactory(WasmMsg), TokenFactoryFactory(NewTokenInfo), } diff --git a/contracts/voting/dao-voting-cw721-staked/Cargo.toml b/contracts/voting/dao-voting-cw721-staked/Cargo.toml index a52107cca..1ac425af0 100644 --- a/contracts/voting/dao-voting-cw721-staked/Cargo.toml +++ b/contracts/voting/dao-voting-cw721-staked/Cargo.toml @@ -30,7 +30,6 @@ dao-dao-macros = { workspace = true } dao-hooks = { workspace = true } dao-interface = { workspace = true } dao-voting = { workspace = true } -sg-std = { workspace = true } sg721 = { workspace = true } thiserror = { workspace = true } @@ -39,4 +38,5 @@ anyhow = { workspace = true } cw-multi-test = { workspace = true } dao-testing = { workspace = true } sg721-base = { workspace = true, features = ["library"] } +sg-std = { workspace = true } sg-multi-test = { workspace = true } From 201b6e297ec3a7d77f5d8e0a8e459e1afa6ae955 Mon Sep 17 00:00:00 2001 From: Jake Hartnell Date: Fri, 15 Sep 2023 14:26:35 -0700 Subject: [PATCH 09/30] # This is a combination of 2 commits. # This is the 1st commit message: Simpler NFT factory test # The commit message #2 will be skipped: # f --- Cargo.toml | 8 ++++---- contracts/external/sg-minter-factory/src/lib.rs | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index dd9636283..a798a998a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,8 +34,8 @@ overflow-checks = true [workspace.dependencies] anyhow = { version = "1.0" } assert_matches = "1.5" -base-factory = "3.1.0" -base-minter = "3.1.0" +base-factory = "2.4.0" +base-minter = "2.4.0" cosm-orc = { version = "4.0" } cosm-tome = "0.2" cosmos-sdk-proto = "0.19" @@ -79,8 +79,8 @@ syn = { version = "1.0", features = ["derive"] } test-context = "0.1" thiserror = { version = "1.0" } token-bindings = "0.11.0" -vending-factory = "3.1.0" -vending-minter = "3.1.0" +vending-factory = "2.4.0" +vending-minter = "2.4.0" wynd-utils = "0.4" # One commit ahead of version 0.3.0. Allows initialization with an diff --git a/contracts/external/sg-minter-factory/src/lib.rs b/contracts/external/sg-minter-factory/src/lib.rs index 2286f8eba..25e31af30 100644 --- a/contracts/external/sg-minter-factory/src/lib.rs +++ b/contracts/external/sg-minter-factory/src/lib.rs @@ -2,7 +2,7 @@ pub mod contract; mod error; pub mod msg; -#[cfg(test)] -mod tests; +// #[cfg(test)] +// mod tests; pub use crate::error::ContractError; From 42442607349d23f2e6114c956a16c2b37270f69e Mon Sep 17 00:00:00 2001 From: Jake Hartnell Date: Fri, 15 Sep 2023 14:30:30 -0700 Subject: [PATCH 10/30] Simpler NFT factory test --- Cargo.lock | 244 ++++++++++++------ .../test/dao-test-custom-factory/Cargo.toml | 1 + .../dao-test-custom-factory/src/contract.rs | 51 +++- .../test/dao-test-custom-factory/src/msg.rs | 2 + .../dao-voting-cw721-staked/src/contract.rs | 18 +- .../voting/dao-voting-cw721-staked/src/msg.rs | 2 +- 6 files changed, 229 insertions(+), 89 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2e507602f..1f75a390f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,6 +28,17 @@ dependencies = [ "version_check", ] +[[package]] +name = "ahash" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", +] + [[package]] name = "aho-corasick" version = "1.0.5" @@ -74,7 +85,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.33", + "syn 2.0.34", ] [[package]] @@ -85,7 +96,7 @@ checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.33", + "syn 2.0.34", ] [[package]] @@ -167,45 +178,45 @@ dependencies = [ [[package]] name = "base-factory" -version = "3.1.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55ccff69b8782aaa2104b32c76d22bd58c3ab71487e9026ae40fb3e705549334" +checksum = "0de00b1bd4a381194ccaa4a790390d2599a56eaf50f5b5f76f4a975958188390" dependencies = [ "cosmwasm-schema", "cosmwasm-std", - "cw-storage-plus 1.1.0", - "cw-utils 1.0.1", - "cw2 1.1.0", + "cw-storage-plus 0.16.0", + "cw-utils 0.16.0", + "cw2 0.16.0", "schemars", "serde", - "sg-std", + "sg-std 2.4.0", "sg1", - "sg2", + "sg2 2.4.0", "thiserror", ] [[package]] name = "base-minter" -version = "3.1.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddc0fabe73ec51bb940bcaf8693a60967386a224d172db90d0609978006052fd" +checksum = "9912088c59aaa58f2f9db151f559615bc8af8179e6e3d7e4ee393692421d5198" dependencies = [ "base-factory", "cosmwasm-schema", "cosmwasm-std", - "cw-storage-plus 1.1.0", - "cw-utils 1.0.1", - "cw2 1.1.0", - "cw721 0.18.0", - "cw721-base 0.18.0", + "cw-storage-plus 0.16.0", + "cw-utils 0.16.0", + "cw2 0.16.0", + "cw721 0.16.0", + "cw721-base 0.16.0", "schemars", "serde", - "sg-std", + "sg-std 2.4.0", "sg1", - "sg2", + "sg2 2.4.0", "sg4", - "sg721", - "sg721-base", + "sg721 2.4.0", + "sg721-base 2.4.0", "thiserror", "url", ] @@ -369,7 +380,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4114279215a005bc675e386011e594e1d9b800918cea18fcadadcce864a2046b" dependencies = [ "borsh-derive", - "hashbrown 0.12.3", + "hashbrown 0.13.2", ] [[package]] @@ -485,9 +496,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.30" +version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "defd4e7873dbddba6c7c91e199c7fcb946abc4a6a4ac3195400bcfb01b5de877" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" dependencies = [ "num-traits", ] @@ -2117,6 +2128,7 @@ dependencies = [ "cw-tokenfactory-issuer", "cw-utils 1.0.1", "cw2 1.1.0", + "cw721-base 0.18.0", "dao-dao-macros", "dao-interface", "thiserror", @@ -2297,9 +2309,9 @@ dependencies = [ "dao-testing", "dao-voting 2.2.0", "sg-multi-test", - "sg-std", - "sg721", - "sg721-base", + "sg-std 3.2.0", + "sg721 3.1.0", + "sg721-base 3.1.0", "thiserror", ] @@ -2690,7 +2702,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.33", + "syn 2.0.34", ] [[package]] @@ -2806,7 +2818,16 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ - "ahash", + "ahash 0.7.6", +] + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash 0.8.3", ] [[package]] @@ -3475,7 +3496,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.33", + "syn 2.0.34", ] [[package]] @@ -3506,7 +3527,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 2.0.33", + "syn 2.0.34", ] [[package]] @@ -4098,7 +4119,7 @@ checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.33", + "syn 2.0.34", ] [[package]] @@ -4131,7 +4152,7 @@ checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.33", + "syn 2.0.34", ] [[package]] @@ -4165,9 +4186,9 @@ dependencies = [ "dao-interface", "dao-voting-cw721-staked", "sg-multi-test", - "sg-std", - "sg2", - "sg721-base", + "sg-std 3.2.0", + "sg2 3.1.0", + "sg721-base 3.1.0", "thiserror", "vending-factory", "vending-minter", @@ -4184,7 +4205,22 @@ dependencies = [ "cw-multi-test", "schemars", "serde", - "sg-std", + "sg-std 3.2.0", +] + +[[package]] +name = "sg-std" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc9b4ab6090f777940d3277632b1a3fff89f5a52191443a19da7cd99e3845041" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-utils 0.16.0", + "cw721 0.16.0", + "schemars", + "serde", + "thiserror", ] [[package]] @@ -4204,33 +4240,49 @@ dependencies = [ [[package]] name = "sg-whitelist" -version = "3.1.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "743da3e55d0f52e28dd8c9ba060bbf4258102eb2d284b9a9ab15a547e2f8b48b" +checksum = "a88e24759094f8927913efa92d3679011a40fa30c4e29ea7c3bc6cf8a3c514bb" dependencies = [ "cosmwasm-schema", "cosmwasm-std", - "cw-storage-plus 1.1.0", - "cw-utils 1.0.1", - "cw2 1.1.0", + "cw-storage-plus 0.16.0", + "cw-utils 0.16.0", + "cw2 0.16.0", "rust_decimal", "schemars", "serde", - "sg-std", + "sg-std 2.4.0", "sg1", "thiserror", ] [[package]] name = "sg1" -version = "3.1.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b57d46d7140a36563b8ca2d24d76e2caba09fd5327f6e89cbc83f788412bfab" +checksum = "1c691fbf35f3deb9e2944b0a82db7280798a1b4b17c7e9fe22b78d89c5276c8e" dependencies = [ "cosmwasm-std", - "cw-utils 1.0.1", + "cw-utils 0.16.0", "serde", - "sg-std", + "sg-std 2.4.0", + "thiserror", +] + +[[package]] +name = "sg2" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0accfb1855c12e486b5c1afbd858652fabdf00bdacd221a9f96121c1d1955a62" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 0.16.0", + "cw-utils 0.16.0", + "schemars", + "serde", + "sg721 2.4.0", "thiserror", ] @@ -4246,15 +4298,15 @@ dependencies = [ "cw-utils 1.0.1", "schemars", "serde", - "sg721", + "sg721 3.1.0", "thiserror", ] [[package]] name = "sg4" -version = "3.1.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "805cc3e44916242a540788e0cc5ed2d4665f95e3dcd91d4b37385a7bd82805a1" +checksum = "18da9e17654454380aa81f92cbc650098f28d657932770fda1c911747eb95ce4" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -4263,6 +4315,20 @@ dependencies = [ "thiserror", ] +[[package]] +name = "sg721" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b37ea61753635e1de01a16947784ddd5ba8e256fa403be6246b2d88578914fb2" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-utils 0.16.0", + "cw721-base 0.16.0", + "serde", + "thiserror", +] + [[package]] name = "sg721" version = "3.1.0" @@ -4278,6 +4344,26 @@ dependencies = [ "thiserror", ] +[[package]] +name = "sg721-base" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b81900474a5a2a6cf25e70720d62a565abc56caa1fe3613cfd15157c63dfb89" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 0.16.0", + "cw-utils 0.16.0", + "cw2 0.16.0", + "cw721 0.16.0", + "cw721-base 0.16.0", + "serde", + "sg-std 2.4.0", + "sg721 2.4.0", + "thiserror", + "url", +] + [[package]] name = "sg721-base" version = "3.1.0" @@ -4293,8 +4379,8 @@ dependencies = [ "cw721 0.18.0", "cw721-base 0.18.0", "serde", - "sg-std", - "sg721", + "sg-std 3.2.0", + "sg721 3.1.0", "thiserror", "url", ] @@ -4533,9 +4619,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.33" +version = "2.0.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9caece70c63bfba29ec2fed841a09851b14a235c60010fa4de58089b6c025668" +checksum = "88ec6cdb6a4c16306eccf52ccd8d492e4ab64705a15a5016acb205251001bf72" dependencies = [ "proc-macro2", "quote", @@ -4834,7 +4920,7 @@ checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35" dependencies = [ "proc-macro2", "quote", - "syn 2.0.33", + "syn 2.0.34", ] [[package]] @@ -4916,7 +5002,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.33", + "syn 2.0.34", ] [[package]] @@ -5076,7 +5162,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.33", + "syn 2.0.34", ] [[package]] @@ -5106,9 +5192,9 @@ checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "typenum" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "ucd-trie" @@ -5174,49 +5260,49 @@ checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" [[package]] name = "vending-factory" -version = "3.1.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "610ec4018e2b033ac836c9e04534a4bd8164c386ae3963bd2aa142c4d77c6749" +checksum = "1cf5cc560b6d037161d5e4cd3bbe794373c685afb2f30f739984bf135f4abd41" dependencies = [ "base-factory", "cosmwasm-schema", "cosmwasm-std", - "cw-storage-plus 1.1.0", - "cw-utils 1.0.1", - "cw2 1.1.0", + "cw-storage-plus 0.16.0", + "cw-utils 0.16.0", + "cw2 0.16.0", "schemars", "serde", - "sg-std", + "sg-std 2.4.0", "sg1", - "sg2", - "sg721", + "sg2 2.4.0", + "sg721 2.4.0", "thiserror", ] [[package]] name = "vending-minter" -version = "3.1.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3035a883d15d475051fe15ae34c6b5c6ac99f572ae7e5fda28d28f72e0217a1" +checksum = "999a8d302970bed638c09c9c116e7ab41031089a4e707796e7ca9c3ef7b66ed5" dependencies = [ "cosmwasm-schema", "cosmwasm-std", - "cw-storage-plus 1.1.0", - "cw-utils 1.0.1", - "cw2 1.1.0", - "cw721 0.18.0", - "cw721-base 0.18.0", + "cw-storage-plus 0.16.0", + "cw-utils 0.16.0", + "cw2 0.16.0", + "cw721 0.16.0", + "cw721-base 0.16.0", "rand_core 0.6.4", "rand_xoshiro", "schemars", "semver", "serde", - "sg-std", + "sg-std 2.4.0", "sg-whitelist", "sg1", - "sg2", + "sg2 2.4.0", "sg4", - "sg721", + "sg721 2.4.0", "sha2 0.10.7", "shuffle", "thiserror", @@ -5288,7 +5374,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.33", + "syn 2.0.34", "wasm-bindgen-shared", ] @@ -5310,7 +5396,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.33", + "syn 2.0.34", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -5506,5 +5592,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.33", + "syn 2.0.34", ] diff --git a/contracts/test/dao-test-custom-factory/Cargo.toml b/contracts/test/dao-test-custom-factory/Cargo.toml index c00f9947d..ffcca3a22 100644 --- a/contracts/test/dao-test-custom-factory/Cargo.toml +++ b/contracts/test/dao-test-custom-factory/Cargo.toml @@ -21,6 +21,7 @@ cosmwasm-std = { workspace = true } cosmwasm-schema = { workspace = true } cosmwasm-storage = { workspace = true } cw2 = { workspace = true } +cw721-base = { workspace = true, features = ["library"] } cw-ownable = { workspace = true } cw-storage-plus = { workspace = true } cw-utils = { workspace = true } diff --git a/contracts/test/dao-test-custom-factory/src/contract.rs b/contracts/test/dao-test-custom-factory/src/contract.rs index 21778fb05..a58fd0a57 100644 --- a/contracts/test/dao-test-custom-factory/src/contract.rs +++ b/contracts/test/dao-test-custom-factory/src/contract.rs @@ -5,6 +5,7 @@ use cosmwasm_std::{ StdResult, SubMsg, Uint128, WasmMsg, }; use cw2::set_contract_version; +use cw721_base::InstantiateMsg as Cw721InstantiateMsg; use cw_storage_plus::Item; use cw_tokenfactory_issuer::msg::{ ExecuteMsg as IssuerExecuteMsg, InstantiateMsg as IssuerInstantiateMsg, @@ -24,6 +25,7 @@ const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); const INSTANTIATE_ISSUER_REPLY_ID: u64 = 1; +const INSTANTIATE_NFT_REPLY_ID: u64 = 2; const DAO: Item = Item::new("dao"); const TOKEN_INFO: Item = Item::new("token_info"); @@ -32,8 +34,8 @@ const TOKEN_INFO: Item = Item::new("token_info"); pub fn instantiate( deps: DepsMut, _env: Env, - info: MessageInfo, - msg: InstantiateMsg, + _info: MessageInfo, + _msg: InstantiateMsg, ) -> Result { set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; @@ -51,9 +53,44 @@ pub fn execute( ExecuteMsg::TokenFactoryFactory(token) => { execute_token_factory_factory(deps, env, info, token) } + ExecuteMsg::NftFactory(cw721_msg) => execute_nft_factory(deps, env, info, cw721_msg), } } +/// An example factory that instantiates a new NFT contract +/// A more realistic example would be something like a minter contract that creates +/// an NFT along with a minter contract for sales like on Stargaze. +pub fn execute_nft_factory( + deps: DepsMut, + _env: Env, + info: MessageInfo, + cw721_msg: Cw721InstantiateMsg, +) -> Result { + // Query for DAO + let dao: Addr = deps + .querier + .query_wasm_smart(info.sender, &VotingModuleQueryMsg::Dao {})?; + + // Save DAO and TOKEN_INFO for use in replies + DAO.save(deps.storage, &dao)?; + + // Instantiate new contract, further setup is handled in the + // SubMsg reply. + let msg = SubMsg::reply_on_success( + WasmMsg::Instantiate { + admin: Some(dao.to_string()), + // TODO, need to pass this in + code_id: 0, + msg: to_binary(&cw721_msg)?, + funds: vec![], + label: "cw_tokenfactory_issuer".to_string(), + }, + INSTANTIATE_NFT_REPLY_ID, + ); + + Ok(Response::new().add_submessage(msg)) +} + /// An example factory that instantiates a cw_tokenfactory_issuer contract /// A more realistic example would be something like a DeFi Pool or Augmented /// bonding curve. @@ -190,6 +227,16 @@ pub fn reply(deps: DepsMut, env: Env, msg: Reply) -> Result { + // Parse issuer address from instantiate reply + let nft_address = parse_reply_instantiate_data(msg)?.contract_address; + + // Mint an NFT (in reality, the factory would likely be used in conjunction with a minter contract) + + // Set reply data + + unimplemented!() + } _ => Err(ContractError::UnknownReplyId { id: msg.id }), } } diff --git a/contracts/test/dao-test-custom-factory/src/msg.rs b/contracts/test/dao-test-custom-factory/src/msg.rs index ec285be57..aa06f7df2 100644 --- a/contracts/test/dao-test-custom-factory/src/msg.rs +++ b/contracts/test/dao-test-custom-factory/src/msg.rs @@ -1,4 +1,5 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; +use cw721_base::InstantiateMsg as Cw721InstantiateMsg; use dao_interface::token::NewTokenInfo; #[cw_serde] @@ -7,6 +8,7 @@ pub struct InstantiateMsg {} #[cw_serde] pub enum ExecuteMsg { TokenFactoryFactory(NewTokenInfo), + NftFactory(Cw721InstantiateMsg), } #[cw_serde] diff --git a/contracts/voting/dao-voting-cw721-staked/src/contract.rs b/contracts/voting/dao-voting-cw721-staked/src/contract.rs index de0ac036f..7856d0b5d 100644 --- a/contracts/voting/dao-voting-cw721-staked/src/contract.rs +++ b/contracts/voting/dao-voting-cw721-staked/src/contract.rs @@ -13,7 +13,9 @@ use dao_hooks::nft_stake::{stake_nft_hook_msgs, unstake_nft_hook_msgs}; use dao_interface::voting::IsActiveResponse; use dao_voting::threshold::{ActiveThreshold, ActiveThresholdResponse}; -use crate::msg::{ExecuteMsg, FactoryCallback, InstantiateMsg, MigrateMsg, NftContract, QueryMsg}; +use crate::msg::{ + ExecuteMsg, InstantiateMsg, MigrateMsg, NftContract, NftFactoryCallback, QueryMsg, +}; use crate::state::{ register_staked_nft, register_unstaked_nfts, Config, ACTIVE_THRESHOLD, CONFIG, DAO, HOOKS, INITIAL_NFTS, MAX_CLAIMS, NFT_BALANCES, NFT_CLAIMS, STAKED_NFTS_PER_OWNER, TOTAL_STAKED_NFTS, @@ -738,19 +740,21 @@ pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result { - // TODO validate active threshold is set? Some contracts such as a minter, - // contract may not have any supply until tokens are minted. - let mut config = CONFIG.load(deps.storage)?; // Parse info from the callback, this will fail // if incorrectly formatted. - let info: FactoryCallback = from_binary(&data)?; + let info: NftFactoryCallback = from_binary(&data)?; + + // Validate NFT contract address + let nft_address = deps.api.addr_validate(&info.nft_contract)?; - // TODO validate that this is an NFT with a query + // Validate that this is an NFT with a query + deps.querier + .query_wasm_smart(nft_address.clone(), &Cw721QueryMsg::NumTokens {})?; // Update NFT contract - config.nft_address = deps.api.addr_validate(&info.nft_contract)?; + config.nft_address = nft_address; CONFIG.save(deps.storage, &config)?; Ok(Response::new().add_attribute("nft_contract", info.nft_contract)) diff --git a/contracts/voting/dao-voting-cw721-staked/src/msg.rs b/contracts/voting/dao-voting-cw721-staked/src/msg.rs index dc1eaa22b..52c0acdb9 100644 --- a/contracts/voting/dao-voting-cw721-staked/src/msg.rs +++ b/contracts/voting/dao-voting-cw721-staked/src/msg.rs @@ -96,6 +96,6 @@ pub enum QueryMsg { pub struct MigrateMsg {} #[cw_serde] -pub struct FactoryCallback { +pub struct NftFactoryCallback { pub nft_contract: String, } From 1511a9cf3908b7b08252cff7270a8a7f22673eeb Mon Sep 17 00:00:00 2001 From: Jake Hartnell Date: Fri, 15 Sep 2023 14:41:25 -0700 Subject: [PATCH 11/30] Remove sg-minter-factory (for now) --- Cargo.lock | 534 +----------------- Cargo.toml | 10 - .../external/sg-minter-factory/.cargo/config | 4 - .../external/sg-minter-factory/Cargo.toml | 43 -- contracts/external/sg-minter-factory/README | 2 - .../sg-minter-factory/examples/schema.rs | 10 - .../sg-minter-factory/src/contract.rs | 135 ----- .../external/sg-minter-factory/src/error.rs | 21 - .../external/sg-minter-factory/src/lib.rs | 8 - .../external/sg-minter-factory/src/msg.rs | 17 - .../external/sg-minter-factory/src/tests.rs | 96 ---- 11 files changed, 9 insertions(+), 871 deletions(-) delete mode 100644 contracts/external/sg-minter-factory/.cargo/config delete mode 100644 contracts/external/sg-minter-factory/Cargo.toml delete mode 100644 contracts/external/sg-minter-factory/README delete mode 100644 contracts/external/sg-minter-factory/examples/schema.rs delete mode 100644 contracts/external/sg-minter-factory/src/contract.rs delete mode 100644 contracts/external/sg-minter-factory/src/error.rs delete mode 100644 contracts/external/sg-minter-factory/src/lib.rs delete mode 100644 contracts/external/sg-minter-factory/src/msg.rs delete mode 100644 contracts/external/sg-minter-factory/src/tests.rs diff --git a/Cargo.lock b/Cargo.lock index 1f75a390f..011edad90 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,17 +28,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "ahash" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", -] - [[package]] name = "aho-corasick" version = "1.0.5" @@ -54,12 +43,6 @@ version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" -[[package]] -name = "arrayvec" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" - [[package]] name = "assert_matches" version = "1.5.0" @@ -176,51 +159,6 @@ dependencies = [ "rustc-demangle", ] -[[package]] -name = "base-factory" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de00b1bd4a381194ccaa4a790390d2599a56eaf50f5b5f76f4a975958188390" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus 0.16.0", - "cw-utils 0.16.0", - "cw2 0.16.0", - "schemars", - "serde", - "sg-std 2.4.0", - "sg1", - "sg2 2.4.0", - "thiserror", -] - -[[package]] -name = "base-minter" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9912088c59aaa58f2f9db151f559615bc8af8179e6e3d7e4ee393692421d5198" -dependencies = [ - "base-factory", - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus 0.16.0", - "cw-utils 0.16.0", - "cw2 0.16.0", - "cw721 0.16.0", - "cw721-base 0.16.0", - "schemars", - "serde", - "sg-std 2.4.0", - "sg1", - "sg2 2.4.0", - "sg4", - "sg721 2.4.0", - "sg721-base 2.4.0", - "thiserror", - "url", -] - [[package]] name = "base16ct" version = "0.1.1" @@ -304,28 +242,6 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" -[[package]] -name = "bitvec" -version = "0.17.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41262f11d771fd4a61aa3ce019fca363b4b6c282fca9da2a31186d3965a47a5c" -dependencies = [ - "either", - "radium 0.3.0", -] - -[[package]] -name = "bitvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" -dependencies = [ - "funty", - "radium 0.7.0", - "tap", - "wyz", -] - [[package]] name = "block-buffer" version = "0.9.0" @@ -373,51 +289,6 @@ dependencies = [ "serde_yaml", ] -[[package]] -name = "borsh" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4114279215a005bc675e386011e594e1d9b800918cea18fcadadcce864a2046b" -dependencies = [ - "borsh-derive", - "hashbrown 0.13.2", -] - -[[package]] -name = "borsh-derive" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0754613691538d51f329cce9af41d7b7ca150bc973056f1156611489475f54f7" -dependencies = [ - "borsh-derive-internal", - "borsh-schema-derive-internal", - "proc-macro-crate", - "proc-macro2", - "syn 1.0.109", -] - -[[package]] -name = "borsh-derive-internal" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afb438156919598d2c7bad7e1c0adf3d26ed3840dbc010db1a882a65583ca2fb" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "borsh-schema-derive-internal" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634205cc43f74a1b9046ef87c4540ebda95696ec0f315024860cad7c5b0f5ccd" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "bs58" version = "0.4.0" @@ -433,28 +304,6 @@ version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" -[[package]] -name = "bytecheck" -version = "0.6.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6372023ac861f6e6dc89c8344a8f398fb42aaba2b5dbc649ca0c0e9dbcb627" -dependencies = [ - "bytecheck_derive", - "ptr_meta", - "simdutf8", -] - -[[package]] -name = "bytecheck_derive" -version = "0.6.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7ec4c6f261935ad534c0c22dbef2201b45918860eb1c574b972bd213a76af61" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "byteorder" version = "1.4.3" @@ -2309,9 +2158,9 @@ dependencies = [ "dao-testing", "dao-voting 2.2.0", "sg-multi-test", - "sg-std 3.2.0", - "sg721 3.1.0", - "sg721-base 3.1.0", + "sg-std", + "sg721", + "sg721-base", "thiserror", ] @@ -2640,12 +2489,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8cbd1169bd7b4a0a20d92b9af7a7e0422888bd38a6f5ec29c1fd8c1558a272e" -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - [[package]] name = "futures" version = "0.3.28" @@ -2818,16 +2661,7 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ - "ahash 0.7.6", -] - -[[package]] -name = "hashbrown" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" -dependencies = [ - "ahash 0.8.3", + "ahash", ] [[package]] @@ -3568,15 +3402,6 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" -[[package]] -name = "proc-macro-crate" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" -dependencies = [ - "toml", -] - [[package]] name = "proc-macro2" version = "1.0.67" @@ -3653,26 +3478,6 @@ dependencies = [ "prost 0.11.9", ] -[[package]] -name = "ptr_meta" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" -dependencies = [ - "ptr_meta_derive", -] - -[[package]] -name = "ptr_meta_derive" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "quote" version = "1.0.33" @@ -3682,18 +3487,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "radium" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "def50a86306165861203e7f84ecffbbdfdea79f0e51039b33de1e952358c47ac" - -[[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - [[package]] name = "rand" version = "0.8.5" @@ -3730,15 +3523,6 @@ dependencies = [ "getrandom", ] -[[package]] -name = "rand_xoshiro" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" -dependencies = [ - "rand_core 0.6.4", -] - [[package]] name = "regex" version = "1.9.5" @@ -3768,15 +3552,6 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" -[[package]] -name = "rend" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581008d2099240d37fb08d77ad713bcaec2c4d89d50b5b21a8bb1996bbab68ab" -dependencies = [ - "bytecheck", -] - [[package]] name = "rfc6979" version = "0.3.1" @@ -3833,34 +3608,6 @@ dependencies = [ "opaque-debug", ] -[[package]] -name = "rkyv" -version = "0.7.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0200c8230b013893c0b2d6213d6ec64ed2b9be2e0e016682b7224ff82cff5c58" -dependencies = [ - "bitvec 1.0.1", - "bytecheck", - "hashbrown 0.12.3", - "ptr_meta", - "rend", - "rkyv_derive", - "seahash", - "tinyvec", - "uuid 1.4.1", -] - -[[package]] -name = "rkyv_derive" -version = "0.7.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2e06b915b5c230a17d7a736d1e2e63ee753c256a8614ef3f5147b13a4f5541d" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "ron" version = "0.7.1" @@ -3882,22 +3629,6 @@ dependencies = [ "ordered-multimap", ] -[[package]] -name = "rust_decimal" -version = "1.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c4216490d5a413bc6d10fa4742bd7d4955941d062c0ef873141d6b0e7b30fd" -dependencies = [ - "arrayvec", - "borsh", - "bytes", - "num-traits", - "rand", - "rkyv", - "serde", - "serde_json", -] - [[package]] name = "rustc-demangle" version = "0.1.23" @@ -4012,12 +3743,6 @@ dependencies = [ "untrusted", ] -[[package]] -name = "seahash" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" - [[package]] name = "sec1" version = "0.3.0" @@ -4168,32 +3893,6 @@ dependencies = [ "unsafe-libyaml", ] -[[package]] -name = "sg-minter-factory" -version = "2.2.0" -dependencies = [ - "base-factory", - "base-minter", - "cosmwasm-schema", - "cosmwasm-std", - "cosmwasm-storage", - "cw-multi-test", - "cw-ownable", - "cw-storage-plus 1.1.0", - "cw-utils 1.0.1", - "cw2 1.1.0", - "dao-dao-macros", - "dao-interface", - "dao-voting-cw721-staked", - "sg-multi-test", - "sg-std 3.2.0", - "sg2 3.1.0", - "sg721-base 3.1.0", - "thiserror", - "vending-factory", - "vending-minter", -] - [[package]] name = "sg-multi-test" version = "3.1.0" @@ -4205,22 +3904,7 @@ dependencies = [ "cw-multi-test", "schemars", "serde", - "sg-std 3.2.0", -] - -[[package]] -name = "sg-std" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc9b4ab6090f777940d3277632b1a3fff89f5a52191443a19da7cd99e3845041" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-utils 0.16.0", - "cw721 0.16.0", - "schemars", - "serde", - "thiserror", + "sg-std", ] [[package]] @@ -4238,97 +3922,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "sg-whitelist" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a88e24759094f8927913efa92d3679011a40fa30c4e29ea7c3bc6cf8a3c514bb" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus 0.16.0", - "cw-utils 0.16.0", - "cw2 0.16.0", - "rust_decimal", - "schemars", - "serde", - "sg-std 2.4.0", - "sg1", - "thiserror", -] - -[[package]] -name = "sg1" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c691fbf35f3deb9e2944b0a82db7280798a1b4b17c7e9fe22b78d89c5276c8e" -dependencies = [ - "cosmwasm-std", - "cw-utils 0.16.0", - "serde", - "sg-std 2.4.0", - "thiserror", -] - -[[package]] -name = "sg2" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0accfb1855c12e486b5c1afbd858652fabdf00bdacd221a9f96121c1d1955a62" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus 0.16.0", - "cw-utils 0.16.0", - "schemars", - "serde", - "sg721 2.4.0", - "thiserror", -] - -[[package]] -name = "sg2" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9731226da77a03119a7e977e8d15b7b5f6f27396bb4003cc6449fafabaec213" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus 1.1.0", - "cw-utils 1.0.1", - "schemars", - "serde", - "sg721 3.1.0", - "thiserror", -] - -[[package]] -name = "sg4" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18da9e17654454380aa81f92cbc650098f28d657932770fda1c911747eb95ce4" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "schemars", - "serde", - "thiserror", -] - -[[package]] -name = "sg721" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b37ea61753635e1de01a16947784ddd5ba8e256fa403be6246b2d88578914fb2" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-utils 0.16.0", - "cw721-base 0.16.0", - "serde", - "thiserror", -] - [[package]] name = "sg721" version = "3.1.0" @@ -4344,26 +3937,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "sg721-base" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b81900474a5a2a6cf25e70720d62a565abc56caa1fe3613cfd15157c63dfb89" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus 0.16.0", - "cw-utils 0.16.0", - "cw2 0.16.0", - "cw721 0.16.0", - "cw721-base 0.16.0", - "serde", - "sg-std 2.4.0", - "sg721 2.4.0", - "thiserror", - "url", -] - [[package]] name = "sg721-base" version = "3.1.0" @@ -4379,8 +3952,8 @@ dependencies = [ "cw721 0.18.0", "cw721-base 0.18.0", "serde", - "sg-std 3.2.0", - "sg721 3.1.0", + "sg-std", + "sg721", "thiserror", "url", ] @@ -4436,16 +4009,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" -[[package]] -name = "shuffle" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fecbe806be0726e22a6192ce0ec2f90e0798946ae0cbc2c10c7a3c68cae4eb2" -dependencies = [ - "bitvec 0.17.4", - "rand", -] - [[package]] name = "signature" version = "1.6.4" @@ -4466,12 +4029,6 @@ dependencies = [ "rand_core 0.6.4", ] -[[package]] -name = "simdutf8" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" - [[package]] name = "slab" version = "0.4.9" @@ -4634,12 +4191,6 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - [[package]] name = "tendermint" version = "0.23.9" @@ -4813,7 +4364,7 @@ dependencies = [ "tokio", "tracing", "url", - "uuid 0.8.2", + "uuid", "walkdir", ] @@ -4847,7 +4398,7 @@ dependencies = [ "tokio", "tracing", "url", - "uuid 0.8.2", + "uuid", "walkdir", ] @@ -5252,64 +4803,6 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" -[[package]] -name = "uuid" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" - -[[package]] -name = "vending-factory" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cf5cc560b6d037161d5e4cd3bbe794373c685afb2f30f739984bf135f4abd41" -dependencies = [ - "base-factory", - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus 0.16.0", - "cw-utils 0.16.0", - "cw2 0.16.0", - "schemars", - "serde", - "sg-std 2.4.0", - "sg1", - "sg2 2.4.0", - "sg721 2.4.0", - "thiserror", -] - -[[package]] -name = "vending-minter" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "999a8d302970bed638c09c9c116e7ab41031089a4e707796e7ca9c3ef7b66ed5" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus 0.16.0", - "cw-utils 0.16.0", - "cw2 0.16.0", - "cw721 0.16.0", - "cw721-base 0.16.0", - "rand_core 0.6.4", - "rand_xoshiro", - "schemars", - "semver", - "serde", - "sg-std 2.4.0", - "sg-whitelist", - "sg1", - "sg2 2.4.0", - "sg4", - "sg721 2.4.0", - "sha2 0.10.7", - "shuffle", - "thiserror", - "url", - "vending-factory", -] - [[package]] name = "version_check" version = "0.9.4" @@ -5557,15 +5050,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "wyz" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" -dependencies = [ - "tap", -] - [[package]] name = "yaml-rust" version = "0.4.5" diff --git a/Cargo.toml b/Cargo.toml index a798a998a..c2f03267e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,8 +34,6 @@ overflow-checks = true [workspace.dependencies] anyhow = { version = "1.0" } assert_matches = "1.5" -base-factory = "2.4.0" -base-minter = "2.4.0" cosm-orc = { version = "4.0" } cosm-tome = "0.2" cosmos-sdk-proto = "0.19" @@ -58,9 +56,6 @@ env_logger = "0.10" once_cell = "1.18" osmosis-std = "0.19.0" osmosis-test-tube = "19.0.0" -# https://github.com/public-awesome/launchpad/issues/616 -# open-edition-factory = "3.1.0" -# open-edition-minter = "3.1.0" proc-macro2 = "1.0" prost = "0.11" quote = "1.0" @@ -69,8 +64,6 @@ schemars = "0.8" serde = { version = "1.0", default-features = false, features = ["derive"] } serde_json = "1.0" serde_yaml = "0.9" -sg1 = "3.1.0" -sg2 = "3.1.0" sg-std = "3.1.0" sg721 = "3.1.0" sg721-base = "3.1.0" @@ -79,8 +72,6 @@ syn = { version = "1.0", features = ["derive"] } test-context = "0.1" thiserror = { version = "1.0" } token-bindings = "0.11.0" -vending-factory = "2.4.0" -vending-minter = "2.4.0" wynd-utils = "0.4" # One commit ahead of version 0.3.0. Allows initialization with an @@ -123,7 +114,6 @@ dao-voting-cw4 = { path = "./contracts/voting/dao-voting-cw4", version = "2.2.0" dao-voting-cw721-roles = { path = "./contracts/voting/dao-voting-cw721-roles", version = "*" } dao-voting-cw721-staked = { path = "./contracts/voting/dao-voting-cw721-staked", version = "2.2.0" } dao-voting-token-staked = { path = "./contracts/voting/dao-voting-token-staked", version = "2.2.0" } -sg-minter-factory = { path = "./contracts/external/sg-minter-factory", version = "*" } # v1 dependencies. used for state migrations. cw-core-v1 = { package = "cw-core", version = "0.1.0" } diff --git a/contracts/external/sg-minter-factory/.cargo/config b/contracts/external/sg-minter-factory/.cargo/config deleted file mode 100644 index 336b618a1..000000000 --- a/contracts/external/sg-minter-factory/.cargo/config +++ /dev/null @@ -1,4 +0,0 @@ -[alias] -wasm = "build --release --target wasm32-unknown-unknown" -unit-test = "test --lib" -schema = "run --example schema" diff --git a/contracts/external/sg-minter-factory/Cargo.toml b/contracts/external/sg-minter-factory/Cargo.toml deleted file mode 100644 index 82c4419bf..000000000 --- a/contracts/external/sg-minter-factory/Cargo.toml +++ /dev/null @@ -1,43 +0,0 @@ -[package] -name = "sg-minter-factory" -authors = ["Jake Hartnell"] -description = "A Stargaze minter factory for dao-voting-cw721-staked" -edition = { workspace = true } -license = { workspace = true } -repository = { workspace = true } -version = { workspace = true } - -[lib] -crate-type = ["cdylib", "rlib"] - -[features] -# for more explicit tests, cargo test --features=backtraces -backtraces = ["cosmwasm-std/backtraces"] -# use library feature to disable all instantiate/execute/query exports -library = [] - -[dependencies] -base-factory = { workspace = true, features = ["library"] } -base-minter = { workspace = true, features = ["library"] } -cosmwasm-std = { workspace = true } -cosmwasm-schema = { workspace = true } -cosmwasm-storage = { workspace = true } -cw2 = { workspace = true } -cw-ownable = { workspace = true } -cw-storage-plus = { workspace = true } -cw-utils = { workspace = true } -dao-dao-macros = { workspace = true } -dao-interface = { workspace = true } -# open-edition-factory = { workspace = true, features = ["library"] } -# open-edition-minter = { workspace = true, features = ["library"] } -sg-std = { workspace = true } -sg2 = { workspace = true } -sg721-base = { workspace = true, features = ["library"] } -thiserror = { workspace = true } -vending-factory = { workspace = true, features = ["library"] } -vending-minter = { workspace = true, features = ["library"] } - -[dev-dependencies] -cw-multi-test = { workspace = true } -dao-voting-cw721-staked = { workspace = true } -sg-multi-test = { workspace = true } diff --git a/contracts/external/sg-minter-factory/README b/contracts/external/sg-minter-factory/README deleted file mode 100644 index 1be9ff991..000000000 --- a/contracts/external/sg-minter-factory/README +++ /dev/null @@ -1,2 +0,0 @@ -# Test Custom Factory contract -Used for testing custom factories with `dao-voting-token-staked` and `dao-voting-cw721-staked`. diff --git a/contracts/external/sg-minter-factory/examples/schema.rs b/contracts/external/sg-minter-factory/examples/schema.rs deleted file mode 100644 index 83dd6e8a1..000000000 --- a/contracts/external/sg-minter-factory/examples/schema.rs +++ /dev/null @@ -1,10 +0,0 @@ -use cosmwasm_schema::write_api; -use sg_minter_factory::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; - -fn main() { - write_api! { - instantiate: InstantiateMsg, - query: QueryMsg, - execute: ExecuteMsg, - } -} diff --git a/contracts/external/sg-minter-factory/src/contract.rs b/contracts/external/sg-minter-factory/src/contract.rs deleted file mode 100644 index 67df87ce3..000000000 --- a/contracts/external/sg-minter-factory/src/contract.rs +++ /dev/null @@ -1,135 +0,0 @@ -#[cfg(not(feature = "library"))] -use cosmwasm_std::entry_point; -use cosmwasm_std::{ - from_binary, to_binary, Addr, Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response, - StdResult, SubMsg, Uint128, WasmMsg, -}; -use cw2::set_contract_version; -use cw_storage_plus::Item; -use cw_utils::parse_reply_execute_data; -use dao_interface::voting::Query as VotingModuleQueryMsg; -use vending_factory::msg::VendingMinterCreateMsg; - -use crate::{ - error::ContractError, - msg::{ExecuteMsg, InstantiateMsg, QueryMsg}, -}; - -const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); -const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); - -const INSTANTIATE_STARGAZE_MINTER_REPLY_ID: u64 = 1; - -const DAO: Item = Item::new("dao"); - -#[cfg_attr(not(feature = "library"), entry_point)] -pub fn instantiate( - deps: DepsMut, - _env: Env, - info: MessageInfo, - msg: InstantiateMsg, -) -> Result { - set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; - - Ok(Response::new().add_attribute("method", "instantiate")) -} - -#[cfg_attr(not(feature = "library"), entry_point)] -pub fn execute( - deps: DepsMut, - env: Env, - info: MessageInfo, - msg: ExecuteMsg, -) -> Result { - match msg { - ExecuteMsg::StargazeBaseMinterFactory(msg) => { - execute_stargaze_base_minter_factory(deps, env, info, msg) - } - } -} - -/// Example Stargaze factory. -pub fn execute_stargaze_base_minter_factory( - deps: DepsMut, - _env: Env, - info: MessageInfo, - msg: WasmMsg, -) -> Result { - // Query for DAO - let dao: Addr = deps - .querier - .query_wasm_smart(info.sender, &VotingModuleQueryMsg::Dao {})?; - - DAO.save(deps.storage, &dao)?; - - // Parse msg, only an execute message is valid - match msg { - WasmMsg::Execute { - contract_addr, - msg: create_msg, - funds, - } => { - // TODO no match? Doens't really make sense here - // Match Stargaze msg - match from_binary::(&create_msg)? { - VendingMinterCreateMsg { - init_msg, - collection_params, - } => { - // Replace the Stargaze info to set the DAO address - let mut params = collection_params; - params.info.creator = dao.to_string(); - - // TODO replace royalties with DAO address - - // This creates a vending-minter contract and a sg721 contract - // in submsg reply, parse the response and save the contract address - Ok(Response::new().add_submessage(SubMsg::reply_on_success( - WasmMsg::Execute { - contract_addr, - msg: to_binary(&VendingMinterCreateMsg { - init_msg, - collection_params: params, - })?, - funds, - }, - INSTANTIATE_STARGAZE_MINTER_REPLY_ID, - ))) - } - // TODO better error - _ => Err(ContractError::UnsupportedFactoryMsg {}), - } - } - _ => Err(ContractError::UnsupportedFactoryMsg {}), - } -} - -#[cfg_attr(not(feature = "library"), entry_point)] -pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { - match msg { - QueryMsg::Info {} => query_info(deps), - } -} - -pub fn query_info(deps: Deps) -> StdResult { - let info = cw2::get_contract_version(deps.storage)?; - to_binary(&dao_interface::voting::InfoResponse { info }) -} - -#[cfg_attr(not(feature = "library"), entry_point)] -pub fn reply(deps: DepsMut, env: Env, msg: Reply) -> Result { - match msg.id { - INSTANTIATE_STARGAZE_MINTER_REPLY_ID => { - // TODO get events - let res = parse_reply_execute_data(msg)?; - println!("{:?}", res); - - // TODO filter through events and find sg721_address - // set-data in response so that the voting module can be aware of this address - // and verify it's an NFT contract. - - unimplemented!() - } - _ => Err(ContractError::UnknownReplyId { id: msg.id }), - } -} diff --git a/contracts/external/sg-minter-factory/src/error.rs b/contracts/external/sg-minter-factory/src/error.rs deleted file mode 100644 index ef813fa6d..000000000 --- a/contracts/external/sg-minter-factory/src/error.rs +++ /dev/null @@ -1,21 +0,0 @@ -use cosmwasm_std::StdError; -use cw_utils::ParseReplyError; -use thiserror::Error; - -#[derive(Error, Debug)] -pub enum ContractError { - #[error("{0}")] - Std(#[from] StdError), - - #[error(transparent)] - ParseReplyError(#[from] ParseReplyError), - - #[error("Unauthorized")] - Unauthorized {}, - - #[error("Got a submessage reply with unknown id: {id}")] - UnknownReplyId { id: u64 }, - - #[error("Factory message must serialize to WasmMsg::Execute")] - UnsupportedFactoryMsg {}, -} diff --git a/contracts/external/sg-minter-factory/src/lib.rs b/contracts/external/sg-minter-factory/src/lib.rs deleted file mode 100644 index 25e31af30..000000000 --- a/contracts/external/sg-minter-factory/src/lib.rs +++ /dev/null @@ -1,8 +0,0 @@ -pub mod contract; -mod error; -pub mod msg; - -// #[cfg(test)] -// mod tests; - -pub use crate::error::ContractError; diff --git a/contracts/external/sg-minter-factory/src/msg.rs b/contracts/external/sg-minter-factory/src/msg.rs deleted file mode 100644 index 1af086b5b..000000000 --- a/contracts/external/sg-minter-factory/src/msg.rs +++ /dev/null @@ -1,17 +0,0 @@ -use cosmwasm_schema::{cw_serde, QueryResponses}; -use cosmwasm_std::WasmMsg; - -#[cw_serde] -pub struct InstantiateMsg {} - -#[cw_serde] -pub enum ExecuteMsg { - StargazeBaseMinterFactory(WasmMsg), -} - -#[cw_serde] -#[derive(QueryResponses)] -pub enum QueryMsg { - #[returns(dao_interface::voting::InfoResponse)] - Info {}, -} diff --git a/contracts/external/sg-minter-factory/src/tests.rs b/contracts/external/sg-minter-factory/src/tests.rs deleted file mode 100644 index c0dc31c02..000000000 --- a/contracts/external/sg-minter-factory/src/tests.rs +++ /dev/null @@ -1,96 +0,0 @@ -use cosmwasm_std::Addr; -use cw_multi_test::{Contract, ContractWrapper, Executor}; -use sg_multi_test::StargazeApp as App; -use sg_std::StargazeMsgWrapper; - -fn sg721_base_contract() -> Box> { - let contract = ContractWrapper::new( - sg721_base::entry::execute, - sg721_base::entry::instantiate, - sg721_base::entry::query, - ); - Box::new(contract) -} - -fn base_minter_contract() -> Box> { - let contract = ContractWrapper::new( - base_minter::contract::execute, - base_minter::contract::instantiate, - base_minter::contract::query, - ) - .with_reply(base_minter::contract::reply); - Box::new(contract) -} - -fn base_factory_contract() -> Box> { - let contract = ContractWrapper::new( - base_factory::contract::execute, - base_factory::contract::instantiate, - base_factory::contract::query, - ); - Box::new(contract) -} - -fn vending_minter_contract() -> Box> { - let contract = ContractWrapper::new( - vending_minter::contract::execute, - vending_minter::contract::instantiate, - vending_minter::contract::query, - ) - .with_reply(vending_minter::contract::reply); - Box::new(contract) -} - -fn vending_factory_contract() -> Box> { - let contract = ContractWrapper::new( - vending_factory::contract::execute, - vending_factory::contract::instantiate, - vending_factory::contract::query, - ); - Box::new(contract) -} - -fn voting_sg721_staked_contract() -> Box> { - let contract = ContractWrapper::new_with_empty( - dao_voting_cw721_staked::contract::execute, - dao_voting_cw721_staked::contract::instantiate, - dao_voting_cw721_staked::contract::query, - ) - .with_reply_empty(dao_voting_cw721_staked::contract::reply); - Box::new(contract) -} - -pub struct TestEnv { - pub app: App, - // pub base_factory: Addr, - pub base_minter_id: u64, - pub sg721_base_id: u64, - // pub vending_factory: Addr, - pub vending_minter_id: u64, - pub voting_: u64, -} - -fn setup() -> TestEnv { - let mut app = App::default(); - let base_factory_id = app.store_code(base_factory_contract()); - let base_minter_id = app.store_code(base_minter_contract()); - let sg721_base_id = app.store_code(sg721_base_contract()); - let vending_factory_id = app.store_code(vending_factory_contract()); - let vending_minter_id = app.store_code(vending_minter_contract()); - let voting_id = app.store_code(voting_sg721_staked_contract()); - - TestEnv { - app, - // base_factory: base_factory_id, - base_minter_id, - sg721_base_id, - // vending_factory: vending_factory_id, - vending_minter_id, - voting_: voting_id, - } -} - -#[test] -fn test_factory_happy_path() { - setup(); -} From 05424d488830a5d937711a58405daaa6ee258c38 Mon Sep 17 00:00:00 2001 From: Jake Hartnell Date: Sat, 16 Sep 2023 13:25:16 -0700 Subject: [PATCH 12/30] Implement factory pattern for NFTs --- Cargo.lock | 1 + .../dao-test-custom-factory/src/contract.rs | 26 +-- .../test/dao-test-custom-factory/src/msg.rs | 5 +- .../voting/dao-voting-cw721-staked/Cargo.toml | 1 + .../dao-voting-cw721-staked/src/contract.rs | 9 +- .../dao-voting-cw721-staked/src/error.rs | 3 + .../src/testing/mod.rs | 1 + .../src/testing/stargaze.rs | 167 +++++++++++++++++ .../src/testing/tests.rs | 172 ++++-------------- packages/dao-interface/src/lib.rs | 1 + packages/dao-interface/src/nft.rs | 6 + packages/dao-testing/src/contracts.rs | 10 + 12 files changed, 245 insertions(+), 157 deletions(-) create mode 100644 contracts/voting/dao-voting-cw721-staked/src/testing/stargaze.rs create mode 100644 packages/dao-interface/src/nft.rs diff --git a/Cargo.lock b/Cargo.lock index 011edad90..f93691d64 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2155,6 +2155,7 @@ dependencies = [ "dao-dao-macros", "dao-hooks", "dao-interface", + "dao-test-custom-factory", "dao-testing", "dao-voting 2.2.0", "sg-multi-test", diff --git a/contracts/test/dao-test-custom-factory/src/contract.rs b/contracts/test/dao-test-custom-factory/src/contract.rs index a58fd0a57..11d210883 100644 --- a/contracts/test/dao-test-custom-factory/src/contract.rs +++ b/contracts/test/dao-test-custom-factory/src/contract.rs @@ -1,8 +1,8 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - from_binary, to_binary, Addr, Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response, - StdResult, SubMsg, Uint128, WasmMsg, + to_binary, Addr, Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response, StdResult, SubMsg, + Uint128, WasmMsg, }; use cw2::set_contract_version; use cw721_base::InstantiateMsg as Cw721InstantiateMsg; @@ -12,6 +12,7 @@ use cw_tokenfactory_issuer::msg::{ }; use cw_utils::parse_reply_instantiate_data; use dao_interface::{ + nft::NftFactoryCallback, token::{FactoryCallback, InitialBalance, NewTokenInfo}, voting::Query as VotingModuleQueryMsg, }; @@ -53,7 +54,10 @@ pub fn execute( ExecuteMsg::TokenFactoryFactory(token) => { execute_token_factory_factory(deps, env, info, token) } - ExecuteMsg::NftFactory(cw721_msg) => execute_nft_factory(deps, env, info, cw721_msg), + ExecuteMsg::NftFactory { + code_id, + cw721_instantiate_msg, + } => execute_nft_factory(deps, env, info, cw721_instantiate_msg, code_id), } } @@ -64,7 +68,8 @@ pub fn execute_nft_factory( deps: DepsMut, _env: Env, info: MessageInfo, - cw721_msg: Cw721InstantiateMsg, + cw721_instantiate_msg: Cw721InstantiateMsg, + code_id: u64, ) -> Result { // Query for DAO let dao: Addr = deps @@ -79,9 +84,8 @@ pub fn execute_nft_factory( let msg = SubMsg::reply_on_success( WasmMsg::Instantiate { admin: Some(dao.to_string()), - // TODO, need to pass this in - code_id: 0, - msg: to_binary(&cw721_msg)?, + code_id, + msg: to_binary(&cw721_instantiate_msg)?, funds: vec![], label: "cw_tokenfactory_issuer".to_string(), }, @@ -231,11 +235,9 @@ pub fn reply(deps: DepsMut, env: Env, msg: Reply) -> Result Err(ContractError::UnknownReplyId { id: msg.id }), } diff --git a/contracts/test/dao-test-custom-factory/src/msg.rs b/contracts/test/dao-test-custom-factory/src/msg.rs index aa06f7df2..edb403c0e 100644 --- a/contracts/test/dao-test-custom-factory/src/msg.rs +++ b/contracts/test/dao-test-custom-factory/src/msg.rs @@ -8,7 +8,10 @@ pub struct InstantiateMsg {} #[cw_serde] pub enum ExecuteMsg { TokenFactoryFactory(NewTokenInfo), - NftFactory(Cw721InstantiateMsg), + NftFactory { + code_id: u64, + cw721_instantiate_msg: Cw721InstantiateMsg, + }, } #[cw_serde] diff --git a/contracts/voting/dao-voting-cw721-staked/Cargo.toml b/contracts/voting/dao-voting-cw721-staked/Cargo.toml index 1ac425af0..02eb69169 100644 --- a/contracts/voting/dao-voting-cw721-staked/Cargo.toml +++ b/contracts/voting/dao-voting-cw721-staked/Cargo.toml @@ -40,3 +40,4 @@ dao-testing = { workspace = true } sg721-base = { workspace = true, features = ["library"] } sg-std = { workspace = true } sg-multi-test = { workspace = true } +dao-test-custom-factory = { workspace = true } diff --git a/contracts/voting/dao-voting-cw721-staked/src/contract.rs b/contracts/voting/dao-voting-cw721-staked/src/contract.rs index 7856d0b5d..5e4cc395f 100644 --- a/contracts/voting/dao-voting-cw721-staked/src/contract.rs +++ b/contracts/voting/dao-voting-cw721-staked/src/contract.rs @@ -10,12 +10,10 @@ use cw721::{Cw721QueryMsg, Cw721ReceiveMsg, NumTokensResponse}; use cw_storage_plus::Bound; use cw_utils::{parse_reply_execute_data, parse_reply_instantiate_data, Duration}; use dao_hooks::nft_stake::{stake_nft_hook_msgs, unstake_nft_hook_msgs}; -use dao_interface::voting::IsActiveResponse; +use dao_interface::{nft::NftFactoryCallback, voting::IsActiveResponse}; use dao_voting::threshold::{ActiveThreshold, ActiveThresholdResponse}; -use crate::msg::{ - ExecuteMsg, InstantiateMsg, MigrateMsg, NftContract, NftFactoryCallback, QueryMsg, -}; +use crate::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, NftContract, QueryMsg}; use crate::state::{ register_staked_nft, register_unstaked_nfts, Config, ACTIVE_THRESHOLD, CONFIG, DAO, HOOKS, INITIAL_NFTS, MAX_CLAIMS, NFT_BALANCES, NFT_CLAIMS, STAKED_NFTS_PER_OWNER, TOTAL_STAKED_NFTS, @@ -759,8 +757,7 @@ pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result Err(ContractError::Unauthorized {}), + None => Err(ContractError::NoFactoryCallback {}), } } _ => Err(ContractError::UnknownReplyId { id: msg.id }), diff --git a/contracts/voting/dao-voting-cw721-staked/src/error.rs b/contracts/voting/dao-voting-cw721-staked/src/error.rs index 595336e97..017041216 100644 --- a/contracts/voting/dao-voting-cw721-staked/src/error.rs +++ b/contracts/voting/dao-voting-cw721-staked/src/error.rs @@ -31,6 +31,9 @@ pub enum ContractError { #[error("New NFT contract must be instantiated with at least one NFT")] NoInitialNfts {}, + #[error("Factory contract did not implment the required NftFactoryCallback interface")] + NoFactoryCallback {}, + #[error("Nothing to claim")] NothingToClaim {}, diff --git a/contracts/voting/dao-voting-cw721-staked/src/testing/mod.rs b/contracts/voting/dao-voting-cw721-staked/src/testing/mod.rs index ea43bc797..10b22e8ee 100644 --- a/contracts/voting/dao-voting-cw721-staked/src/testing/mod.rs +++ b/contracts/voting/dao-voting-cw721-staked/src/testing/mod.rs @@ -3,6 +3,7 @@ mod execute; mod hooks; mod instantiate; mod queries; +mod stargaze; mod tests; use cosmwasm_std::Addr; diff --git a/contracts/voting/dao-voting-cw721-staked/src/testing/stargaze.rs b/contracts/voting/dao-voting-cw721-staked/src/testing/stargaze.rs new file mode 100644 index 000000000..0ce304712 --- /dev/null +++ b/contracts/voting/dao-voting-cw721-staked/src/testing/stargaze.rs @@ -0,0 +1,167 @@ +use cosmwasm_std::{to_binary, Addr, Empty, Uint128}; +use cw721::OwnerOfResponse; +use cw_multi_test::{Contract, ContractWrapper, Executor}; +use dao_voting::threshold::ActiveThreshold; +use sg721::{CollectionInfo, RoyaltyInfoResponse, UpdateCollectionInfoMsg}; +use sg721_base::msg::CollectionInfoResponse; +use sg_multi_test::StargazeApp; +use sg_std::StargazeMsgWrapper; + +use crate::{ + msg::{InstantiateMsg, NftContract, QueryMsg}, + state::Config, + testing::CREATOR_ADDR, +}; + +// Setup Stargaze contracts for multi-test +fn sg721_base_contract() -> Box> { + let contract = ContractWrapper::new( + sg721_base::entry::execute, + sg721_base::entry::instantiate, + sg721_base::entry::query, + ); + Box::new(contract) +} + +// Stargze contracts need a custom message wrapper +fn voting_sg721_staked_contract() -> Box> { + let contract = ContractWrapper::new_with_empty( + crate::contract::execute, + crate::contract::instantiate, + crate::contract::query, + ) + .with_reply_empty(crate::contract::reply); + Box::new(contract) +} + +// I can create new Stargaze NFT collection when creating a dao-voting-cw721-staked contract +#[test] +fn test_instantiate_with_new_sg721_collection() -> anyhow::Result<()> { + let mut app = StargazeApp::default(); + let module_id = app.store_code(voting_sg721_staked_contract()); + let sg721_id = app.store_code(sg721_base_contract()); + + let module_addr = app + .instantiate_contract( + module_id, + Addr::unchecked(CREATOR_ADDR), + &InstantiateMsg { + nft_contract: NftContract::New { + code_id: sg721_id, + label: "Test NFT".to_string(), + msg: to_binary(&sg721::InstantiateMsg { + name: "Test NFT".to_string(), + symbol: "TEST".to_string(), + minter: CREATOR_ADDR.to_string(), + collection_info: CollectionInfo { + creator: CREATOR_ADDR.to_string(), + description: "Test NFT".to_string(), + image: "https://example.com/image.jpg".to_string(), + external_link: None, + explicit_content: None, + start_trading_time: None, + royalty_info: None, + }, + })?, + initial_nfts: vec![to_binary(&sg721::ExecuteMsg::::Mint { + owner: CREATOR_ADDR.to_string(), + token_uri: Some("https://example.com".to_string()), + token_id: "1".to_string(), + extension: Empty {}, + })?], + }, + unstaking_duration: None, + active_threshold: None, + }, + &[], + "cw721_voting", + None, + ) + .unwrap(); + + let config: Config = app + .wrap() + .query_wasm_smart(module_addr, &QueryMsg::Config {})?; + let sg721_addr = config.nft_address; + + // Check that the NFT contract was created + let owner: OwnerOfResponse = app.wrap().query_wasm_smart( + sg721_addr.clone(), + &cw721::Cw721QueryMsg::OwnerOf { + token_id: "1".to_string(), + include_expired: None, + }, + )?; + assert_eq!(owner.owner, CREATOR_ADDR); + + // Check that collection info creator is set to the DAO (in this case CREATOR_ADDR) + // Normally the DAO would instantiate this contract + let creator: CollectionInfoResponse = app + .wrap() + .query_wasm_smart(sg721_addr, &sg721_base::msg::QueryMsg::CollectionInfo {})?; + assert_eq!(creator.creator, CREATOR_ADDR.to_string()); + + Ok(()) +} + +#[test] +#[should_panic(expected = "Active threshold count is greater than supply")] +fn test_instantiate_with_new_sg721_collection_abs_count_validation() { + let mut app = StargazeApp::default(); + let module_id = app.store_code(voting_sg721_staked_contract()); + let sg721_id = app.store_code(sg721_base_contract()); + + // Test edge case + app.instantiate_contract( + module_id, + Addr::unchecked("contract0"), + &InstantiateMsg { + nft_contract: NftContract::New { + code_id: sg721_id, + label: "Test NFT".to_string(), + msg: to_binary(&sg721::InstantiateMsg { + name: "Test NFT".to_string(), + symbol: "TEST".to_string(), + minter: "contract0".to_string(), + collection_info: CollectionInfo { + creator: "contract0".to_string(), + description: "Test NFT".to_string(), + image: "https://example.com/image.jpg".to_string(), + external_link: None, + explicit_content: None, + start_trading_time: None, + royalty_info: None, + }, + }) + .unwrap(), + initial_nfts: vec![ + to_binary(&sg721::ExecuteMsg::::Mint { + owner: "contract0".to_string(), + token_uri: Some("https://example.com".to_string()), + token_id: "1".to_string(), + extension: Empty {}, + }) + .unwrap(), + to_binary(&sg721::ExecuteMsg::::UpdateCollectionInfo { + collection_info: UpdateCollectionInfoMsg:: { + description: None, + image: None, + external_link: None, + explicit_content: None, + royalty_info: None, + }, + }) + .unwrap(), + ], + }, + unstaking_duration: None, + active_threshold: Some(ActiveThreshold::AbsoluteCount { + count: Uint128::new(2), + }), + }, + &[], + "cw721_voting", + None, + ) + .unwrap(); +} diff --git a/contracts/voting/dao-voting-cw721-staked/src/testing/tests.rs b/contracts/voting/dao-voting-cw721-staked/src/testing/tests.rs index 9f37af952..d0fcd621a 100644 --- a/contracts/voting/dao-voting-cw721-staked/src/testing/tests.rs +++ b/contracts/voting/dao-voting-cw721-staked/src/testing/tests.rs @@ -1,22 +1,19 @@ use cosmwasm_std::testing::{mock_dependencies, mock_env}; -use cosmwasm_std::{to_binary, Addr, Decimal, Empty, Uint128}; -use cw721::OwnerOfResponse; +use cosmwasm_std::{to_binary, Addr, Decimal, Empty, Uint128, WasmMsg}; use cw721_base::msg::{ExecuteMsg as Cw721ExecuteMsg, InstantiateMsg as Cw721InstantiateMsg}; use cw721_controllers::{NftClaim, NftClaimsResponse}; -use cw_multi_test::{next_block, App, Contract, ContractWrapper, Executor}; +use cw_multi_test::{next_block, App, Executor}; use cw_utils::Duration; use dao_interface::voting::IsActiveResponse; -use dao_testing::contracts::{cw721_base_contract, voting_cw721_staked_contract}; +use dao_testing::contracts::{ + cw721_base_contract, dao_test_custom_factory, voting_cw721_staked_contract, +}; use dao_voting::threshold::{ActiveThreshold, ActiveThresholdResponse}; -use sg721::{CollectionInfo, RoyaltyInfoResponse, UpdateCollectionInfoMsg}; -use sg721_base::msg::CollectionInfoResponse; -use sg_multi_test::StargazeApp; -use sg_std::StargazeMsgWrapper; use crate::{ contract::{migrate, CONTRACT_NAME, CONTRACT_VERSION}, msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, NftContract, QueryMsg}, - state::{Config, MAX_CLAIMS}, + state::MAX_CLAIMS, testing::{ execute::{ claim_nfts, mint_and_stake_nft, mint_nft, stake_nft, unstake_nfts, update_config, @@ -994,157 +991,56 @@ fn test_no_initial_nfts_fails() { ); } -// Setup Stargaze contracts for multi-test -fn sg721_base_contract() -> Box> { - let contract = ContractWrapper::new( - sg721_base::entry::execute, - sg721_base::entry::instantiate, - sg721_base::entry::query, - ); - Box::new(contract) -} - -// Stargze contracts need a custom message wrapper -fn voting_sg721_staked_contract() -> Box> { - let contract = ContractWrapper::new_with_empty( - crate::contract::execute, - crate::contract::instantiate, - crate::contract::query, - ) - .with_reply_empty(crate::contract::reply); - Box::new(contract) -} - -// I can create new Stargaze NFT collection when creating a dao-voting-cw721-staked contract #[test] -fn test_instantiate_with_new_sg721_collection() -> anyhow::Result<()> { - let mut app = StargazeApp::default(); - let module_id = app.store_code(voting_sg721_staked_contract()); - let sg721_id = app.store_code(sg721_base_contract()); +fn test_factory() { + let mut app = App::default(); + let module_id = app.store_code(voting_cw721_staked_contract()); + let cw721_id = app.store_code(cw721_base_contract()); + let factory_id = app.store_code(dao_test_custom_factory()); - let module_addr = app + // Instantiate factory + let factory_addr = app .instantiate_contract( - module_id, + factory_id, Addr::unchecked(CREATOR_ADDR), - &InstantiateMsg { - nft_contract: NftContract::New { - code_id: sg721_id, - label: "Test NFT".to_string(), - msg: to_binary(&sg721::InstantiateMsg { - name: "Test NFT".to_string(), - symbol: "TEST".to_string(), - minter: CREATOR_ADDR.to_string(), - collection_info: CollectionInfo { - creator: CREATOR_ADDR.to_string(), - description: "Test NFT".to_string(), - image: "https://example.com/image.jpg".to_string(), - external_link: None, - explicit_content: None, - start_trading_time: None, - royalty_info: None, - }, - })?, - initial_nfts: vec![to_binary(&sg721::ExecuteMsg::::Mint { - owner: CREATOR_ADDR.to_string(), - token_uri: Some("https://example.com".to_string()), - token_id: "1".to_string(), - extension: Empty {}, - })?], - }, - unstaking_duration: None, - active_threshold: None, - }, + &dao_test_custom_factory::msg::InstantiateMsg {}, &[], - "cw721_voting", + "test factory".to_string(), None, ) .unwrap(); - let config: Config = app - .wrap() - .query_wasm_smart(module_addr, &QueryMsg::Config {})?; - let sg721_addr = config.nft_address; - - // Check that the NFT contract was created - let owner: OwnerOfResponse = app.wrap().query_wasm_smart( - sg721_addr.clone(), - &cw721::Cw721QueryMsg::OwnerOf { - token_id: "1".to_string(), - include_expired: None, - }, - )?; - assert_eq!(owner.owner, CREATOR_ADDR); - - // Check that collection info creator is set to the DAO (in this case CREATOR_ADDR) - // Normally the DAO would instantiate this contract - let creator: CollectionInfoResponse = app - .wrap() - .query_wasm_smart(sg721_addr, &sg721_base::msg::QueryMsg::CollectionInfo {})?; - assert_eq!(creator.creator, CREATOR_ADDR.to_string()); - - Ok(()) -} - -#[test] -#[should_panic(expected = "Active threshold count is greater than supply")] -fn test_instantiate_with_new_sg721_collection_abs_count_validation() { - let mut app = StargazeApp::default(); - let module_id = app.store_code(voting_sg721_staked_contract()); - let sg721_id = app.store_code(sg721_base_contract()); - - // Test edge case + // Instantiate using factory succeeds app.instantiate_contract( module_id, - Addr::unchecked("contract0"), + Addr::unchecked(CREATOR_ADDR), &InstantiateMsg { - nft_contract: NftContract::New { - code_id: sg721_id, - label: "Test NFT".to_string(), - msg: to_binary(&sg721::InstantiateMsg { - name: "Test NFT".to_string(), - symbol: "TEST".to_string(), - minter: "contract0".to_string(), - collection_info: CollectionInfo { - creator: "contract0".to_string(), - description: "Test NFT".to_string(), - image: "https://example.com/image.jpg".to_string(), - external_link: None, - explicit_content: None, - start_trading_time: None, - royalty_info: None, - }, - }) - .unwrap(), - initial_nfts: vec![ - to_binary(&sg721::ExecuteMsg::::Mint { - owner: "contract0".to_string(), - token_uri: Some("https://example.com".to_string()), - token_id: "1".to_string(), - extension: Empty {}, - }) - .unwrap(), - to_binary(&sg721::ExecuteMsg::::UpdateCollectionInfo { - collection_info: UpdateCollectionInfoMsg:: { - description: None, - image: None, - external_link: None, - explicit_content: None, - royalty_info: None, + nft_contract: NftContract::Factory( + to_binary(&WasmMsg::Execute { + contract_addr: factory_addr.to_string(), + msg: to_binary(&dao_test_custom_factory::msg::ExecuteMsg::NftFactory { + code_id: cw721_id, + cw721_instantiate_msg: Cw721InstantiateMsg { + name: "Test NFT".to_string(), + symbol: "TEST".to_string(), + minter: CREATOR_ADDR.to_string(), }, }) .unwrap(), - ], - }, + funds: vec![], + }) + .unwrap(), + ), unstaking_duration: None, - active_threshold: Some(ActiveThreshold::AbsoluteCount { - count: Uint128::new(2), + active_threshold: Some(ActiveThreshold::Percentage { + percent: Decimal::percent(1), }), }, &[], "cw721_voting", None, ) - .unwrap(); + .unwrap_err(); } #[test] diff --git a/packages/dao-interface/src/lib.rs b/packages/dao-interface/src/lib.rs index 31f85bb7b..aae0678fd 100644 --- a/packages/dao-interface/src/lib.rs +++ b/packages/dao-interface/src/lib.rs @@ -2,6 +2,7 @@ pub mod migrate_msg; pub mod msg; +pub mod nft; pub mod proposal; pub mod query; pub mod state; diff --git a/packages/dao-interface/src/nft.rs b/packages/dao-interface/src/nft.rs new file mode 100644 index 000000000..4f7281734 --- /dev/null +++ b/packages/dao-interface/src/nft.rs @@ -0,0 +1,6 @@ +use cosmwasm_schema::cw_serde; + +#[cw_serde] +pub struct NftFactoryCallback { + pub nft_contract: String, +} diff --git a/packages/dao-testing/src/contracts.rs b/packages/dao-testing/src/contracts.rs index b2fb42b27..a0418a48f 100644 --- a/packages/dao-testing/src/contracts.rs +++ b/packages/dao-testing/src/contracts.rs @@ -196,3 +196,13 @@ pub fn stake_cw20_v03_contract() -> Box> { ); Box::new(contract) } + +pub fn dao_test_custom_factory() -> Box> { + let contract = ContractWrapper::new( + dao_test_custom_factory::contract::execute, + dao_test_custom_factory::contract::instantiate, + dao_test_custom_factory::contract::query, + ) + .with_reply(dao_test_custom_factory::contract::reply); + Box::new(contract) +} From 86109f14d0f8c1007e5b0a79a53e990c89f4f664 Mon Sep 17 00:00:00 2001 From: Jake Hartnell Date: Sat, 16 Sep 2023 14:01:16 -0700 Subject: [PATCH 13/30] Better tests and token factory cleanup --- Cargo.lock | 1 + .../test/dao-test-custom-factory/Cargo.toml | 1 + .../dao-test-custom-factory/src/contract.rs | 28 +++++++++++++++++-- .../test/dao-test-custom-factory/src/error.rs | 4 +++ .../dao-voting-cw721-staked/src/contract.rs | 2 -- .../dao-voting-token-staked/src/contract.rs | 8 +----- .../dao-voting-token-staked/src/error.rs | 3 ++ .../src/tests/test_tube/integration_tests.rs | 27 ++++++++++++++---- packages/dao-interface/src/voting.rs | 5 ++++ 9 files changed, 62 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f93691d64..99fa992c8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1980,6 +1980,7 @@ dependencies = [ "cw721-base 0.18.0", "dao-dao-macros", "dao-interface", + "dao-voting 2.2.0", "thiserror", ] diff --git a/contracts/test/dao-test-custom-factory/Cargo.toml b/contracts/test/dao-test-custom-factory/Cargo.toml index ffcca3a22..8c5dff781 100644 --- a/contracts/test/dao-test-custom-factory/Cargo.toml +++ b/contracts/test/dao-test-custom-factory/Cargo.toml @@ -28,6 +28,7 @@ cw-utils = { workspace = true } thiserror = { workspace = true } dao-dao-macros = { workspace = true } dao-interface = { workspace = true } +dao-voting = { workspace = true } cw-tokenfactory-issuer = { workspace = true, features = ["library"] } [dev-dependencies] diff --git a/contracts/test/dao-test-custom-factory/src/contract.rs b/contracts/test/dao-test-custom-factory/src/contract.rs index 11d210883..780fe94af 100644 --- a/contracts/test/dao-test-custom-factory/src/contract.rs +++ b/contracts/test/dao-test-custom-factory/src/contract.rs @@ -14,7 +14,11 @@ use cw_utils::parse_reply_instantiate_data; use dao_interface::{ nft::NftFactoryCallback, token::{FactoryCallback, InitialBalance, NewTokenInfo}, - voting::Query as VotingModuleQueryMsg, + voting::{ActiveThresholdQuery, Query as VotingModuleQueryMsg}, +}; +use dao_voting::threshold::{ + assert_valid_absolute_count_threshold, assert_valid_percentage_threshold, ActiveThreshold, + ActiveThresholdResponse, }; use crate::{ @@ -29,6 +33,7 @@ const INSTANTIATE_ISSUER_REPLY_ID: u64 = 1; const INSTANTIATE_NFT_REPLY_ID: u64 = 2; const DAO: Item = Item::new("dao"); +const VOTING_MODULE: Item = Item::new("voting_module"); const TOKEN_INFO: Item = Item::new("token_info"); #[cfg_attr(not(feature = "library"), entry_point)] @@ -104,6 +109,9 @@ pub fn execute_token_factory_factory( info: MessageInfo, token: NewTokenInfo, ) -> Result { + // Save voting module address + VOTING_MODULE.save(deps.storage, &info.sender)?; + // Query for DAO let dao: Addr = deps .querier @@ -150,6 +158,7 @@ pub fn reply(deps: DepsMut, env: Env, msg: Reply) -> Result Result { + assert_valid_percentage_threshold(percent)?; + } + ActiveThreshold::AbsoluteCount { count } => { + assert_valid_absolute_count_threshold(count, initial_supply)?; + } + } + } // Msgs to be executed to finalize setup let mut msgs: Vec = vec![]; diff --git a/contracts/test/dao-test-custom-factory/src/error.rs b/contracts/test/dao-test-custom-factory/src/error.rs index ef813fa6d..5c8551174 100644 --- a/contracts/test/dao-test-custom-factory/src/error.rs +++ b/contracts/test/dao-test-custom-factory/src/error.rs @@ -1,5 +1,6 @@ use cosmwasm_std::StdError; use cw_utils::ParseReplyError; +use dao_voting::threshold::ActiveThresholdError; use thiserror::Error; #[derive(Error, Debug)] @@ -7,6 +8,9 @@ pub enum ContractError { #[error("{0}")] Std(#[from] StdError), + #[error(transparent)] + ActiveThresholdError(#[from] ActiveThresholdError), + #[error(transparent)] ParseReplyError(#[from] ParseReplyError), diff --git a/contracts/voting/dao-voting-cw721-staked/src/contract.rs b/contracts/voting/dao-voting-cw721-staked/src/contract.rs index 5e4cc395f..da1158d22 100644 --- a/contracts/voting/dao-voting-cw721-staked/src/contract.rs +++ b/contracts/voting/dao-voting-cw721-staked/src/contract.rs @@ -188,8 +188,6 @@ pub fn instantiate( WasmMsg::Execute { contract_addr, msg, - // TODO what to do with funds for fair burn? - // Need to pass them along to the factory funds, }, FACTORY_EXECUTE_REPLY_ID, diff --git a/contracts/voting/dao-voting-token-staked/src/contract.rs b/contracts/voting/dao-voting-token-staked/src/contract.rs index adb6dcbe1..bf1e0c26c 100644 --- a/contracts/voting/dao-voting-token-staked/src/contract.rs +++ b/contracts/voting/dao-voting-token-staked/src/contract.rs @@ -136,8 +136,6 @@ pub fn instantiate( WasmMsg::Execute { contract_addr, msg, - // TODO what to do with funds for fair burn? - // Need to pass them along to the factory funds: vec![], }, FACTORY_EXECUTE_REPLY_ID, @@ -735,15 +733,11 @@ pub fn reply(deps: DepsMut, env: Env, msg: Reply) -> Result Err(ContractError::Unauthorized {}), + None => Err(ContractError::NoFactoryCallback {}), } } _ => Err(ContractError::UnknownReplyId { id: msg.id }), diff --git a/contracts/voting/dao-voting-token-staked/src/error.rs b/contracts/voting/dao-voting-token-staked/src/error.rs index 5239f3bd1..a5121ef14 100644 --- a/contracts/voting/dao-voting-token-staked/src/error.rs +++ b/contracts/voting/dao-voting-token-staked/src/error.rs @@ -29,6 +29,9 @@ pub enum ContractError { #[error("Can only unstake less than or equal to the amount you have staked")] InvalidUnstakeAmount {}, + #[error("Factory contract did not implment the required NftFactoryCallback interface")] + NoFactoryCallback {}, + #[error("Nothing to claim")] NothingToClaim {}, 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 6083da364..c70924958 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 @@ -2,6 +2,7 @@ use cosmwasm_std::{to_binary, Addr, Coin, Decimal, Uint128, WasmMsg}; use cw_tokenfactory_issuer::msg::DenomUnit; use cw_utils::Duration; use dao_interface::{ + msg::QueryMsg as DaoQueryMsg, state::{Admin, ModuleInstantiateInfo}, token::{InitialBalance, NewDenomMetadata, NewTokenInfo}, }; @@ -14,7 +15,7 @@ use osmosis_std::types::cosmos::bank::v1beta1::QueryBalanceRequest; use osmosis_test_tube::{Account, OsmosisTestApp}; use crate::{ - msg::{ExecuteMsg, InstantiateMsg, TokenInfo}, + msg::{ExecuteMsg, InstantiateMsg, QueryMsg, TokenInfo}, tests::test_tube::test_env::TokenVotingContract, ContractError, }; @@ -334,7 +335,6 @@ fn test_factory() { .. } = 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, @@ -402,11 +402,26 @@ fn test_factory() { }; // Instantiate DAO - let _dao = DaoCore::new(&app, &msg, &accounts[0]).unwrap(); + let dao = DaoCore::new(&app, &msg, &accounts[0]).unwrap(); - // TODO query voting module + // Query voting module + let voting_module: Addr = dao.query(&DaoQueryMsg::VotingModule {}).unwrap(); + let voting = + TokenVotingContract::new_with_values(&app, vp_contract.code_id, voting_module.to_string()) + .unwrap(); - // TODO query denom + // Query denom + let denom = voting.query_denom().unwrap().denom; - // TODO query token contract + // Query token contract + let token_contract: Addr = voting.query(&QueryMsg::TokenContract {}).unwrap(); + + assert_eq!( + denom, + format!( + "factory/{}/{}", + token_contract.to_string(), + DENOM.to_string() + ) + ); } diff --git a/packages/dao-interface/src/voting.rs b/packages/dao-interface/src/voting.rs index f2df11a44..cf6d43707 100644 --- a/packages/dao-interface/src/voting.rs +++ b/packages/dao-interface/src/voting.rs @@ -30,6 +30,11 @@ pub enum Query { IsActive {}, } +#[cw_serde] +pub enum ActiveThresholdQuery { + ActiveThreshold {}, +} + #[cw_serde] pub struct VotingPowerAtHeightResponse { pub power: Uint128, From 274635e4c72b91932eb98f8e8dbfde6819f31ded Mon Sep 17 00:00:00 2001 From: Jake Hartnell Date: Sat, 16 Sep 2023 14:24:40 -0700 Subject: [PATCH 14/30] Remove Stargaze support (supported via factory now) Context: after speaking with the team, they want all NFTs on the platform to utilize the fairburn mechanism. In addition, many artists creating new collections on Stargaze also want to create a minter for that collection to allow for sales. The factory pattern now allows for both. --- .../voting/dao-voting-cw721-staked/Cargo.toml | 6 +- .../voting/dao-voting-cw721-staked/README.md | 8 +- .../schema/dao-voting-cw721-staked.json | 2 +- .../dao-voting-cw721-staked/src/contract.rs | 26 +-- .../voting/dao-voting-cw721-staked/src/msg.rs | 11 +- .../src/testing/mod.rs | 1 - .../src/testing/stargaze.rs | 167 ------------------ 7 files changed, 16 insertions(+), 205 deletions(-) delete mode 100644 contracts/voting/dao-voting-cw721-staked/src/testing/stargaze.rs diff --git a/contracts/voting/dao-voting-cw721-staked/Cargo.toml b/contracts/voting/dao-voting-cw721-staked/Cargo.toml index 02eb69169..39d24543a 100644 --- a/contracts/voting/dao-voting-cw721-staked/Cargo.toml +++ b/contracts/voting/dao-voting-cw721-staked/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "dao-voting-cw721-staked" -authors = ["CypherApe cypherape@protonmail.com"] +authors = ["CypherApe cypherape@protonmail.com", "Jake Hartnell", "ekez"] description = "A DAO DAO voting module based on staked cw721 tokens." edition = { workspace = true } license = { workspace = true } @@ -30,14 +30,10 @@ dao-dao-macros = { workspace = true } dao-hooks = { workspace = true } dao-interface = { workspace = true } dao-voting = { workspace = true } -sg721 = { workspace = true } thiserror = { workspace = true } [dev-dependencies] anyhow = { workspace = true } cw-multi-test = { workspace = true } dao-testing = { workspace = true } -sg721-base = { workspace = true, features = ["library"] } -sg-std = { workspace = true } -sg-multi-test = { workspace = true } dao-test-custom-factory = { workspace = true } diff --git a/contracts/voting/dao-voting-cw721-staked/README.md b/contracts/voting/dao-voting-cw721-staked/README.md index ac91953ae..8ea1d05e7 100644 --- a/contracts/voting/dao-voting-cw721-staked/README.md +++ b/contracts/voting/dao-voting-cw721-staked/README.md @@ -7,4 +7,10 @@ This is a basic implementation of an NFT staking contract. Staked tokens can be unbonded with a configurable unbonding period. Staked balances can be queried at any arbitrary height by external contracts. This contract implements the interface needed to be a DAO DAO [voting module](https://github.com/DA0-DA0/dao-contracts/wiki/DAO-DAO-Contracts-Design#the-voting-module). -`dao-voting-cw721-staked` can be used with existing NFT collections or to create a new `cw721` or `sg721` collections upon instantiation (with the DAO as admin and `minter` / `creator`). +`dao-voting-cw721-staked` can be used with an `existing` NFT collection or to create a `new` `cw721` collection upon instantiation (with the DAO as admin and `minter`). + +To support Stargaze NFTs and other custom NFT contracts or setups with minters (such as the Stargaze Open Edition minter), this contract also supports a `factory` pattern which takes a single `WasmMsg::Execute` message that calls into a custom factory contract. + +**NOTE:** when using the factory pattern, it is important to only use a trusted factory contract, as all validation happens in the factory contract. + +The [dao-test-custom-factory contract](../test/dao-test-custom-factory) provides an example of how this can be done and is used for tests. It is NOT production ready, but meant to serve as an example for building factory contracts. diff --git a/contracts/voting/dao-voting-cw721-staked/schema/dao-voting-cw721-staked.json b/contracts/voting/dao-voting-cw721-staked/schema/dao-voting-cw721-staked.json index 84ff504cb..d7d0bd586 100644 --- a/contracts/voting/dao-voting-cw721-staked/schema/dao-voting-cw721-staked.json +++ b/contracts/voting/dao-voting-cw721-staked/schema/dao-voting-cw721-staked.json @@ -202,7 +202,7 @@ "additionalProperties": false }, { - "description": "Uses a factory pattern that must return the denom, optionally a Token Contract address, and any setup messages. The binary must serialize to a `WasmMsg::Execute` message.", + "description": "Uses a factory contract that must return the address of the NFT contract. The binary must serialize to a `WasmMsg::Execute` message. Validation happens in the factory contract itself, so be sure to use a trusted factory contract.", "type": "object", "required": [ "factory" diff --git a/contracts/voting/dao-voting-cw721-staked/src/contract.rs b/contracts/voting/dao-voting-cw721-staked/src/contract.rs index da1158d22..8e8e88a5f 100644 --- a/contracts/voting/dao-voting-cw721-staked/src/contract.rs +++ b/contracts/voting/dao-voting-cw721-staked/src/contract.rs @@ -34,30 +34,19 @@ const PRECISION_FACTOR: u128 = 10u128.pow(9); #[cw_serde] pub enum NftInstantiateMsg { Cw721(cw721_base::InstantiateMsg), - Sg721(sg721::InstantiateMsg), } impl NftInstantiateMsg { - fn modify_instantiate_msg(&mut self, minter: &str, dao: &str) { + fn modify_instantiate_msg(&mut self, minter: &str) { match self { // Update minter for cw721 NFTs NftInstantiateMsg::Cw721(msg) => msg.minter = minter.to_string(), - NftInstantiateMsg::Sg721(msg) => { - // Update minter and collection creator for sg721 NFTs - // The collection creator is the only one able to call certain methods - // in sg721 contracts - msg.minter = minter.to_string(); - // This should be the DAO, which will be able to control metadata about - // the collection as well as royalties - msg.collection_info.creator = dao.to_string(); - } } } fn to_binary(&self) -> Result { match self { NftInstantiateMsg::Cw721(msg) => to_binary(&msg), - NftInstantiateMsg::Sg721(msg) => to_binary(&msg), } } } @@ -69,10 +58,6 @@ pub fn try_deserialize_nft_instantiate_msg( return Ok(NftInstantiateMsg::Cw721(cw721_msg)); } - if let Ok(sg721_msg) = from_binary::(&instantiate_msg) { - return Ok(NftInstantiateMsg::Sg721(sg721_msg)); - } - Err(ContractError::NftInstantiateError {}) } @@ -134,17 +119,12 @@ pub fn instantiate( msg: instantiate_msg, initial_nfts, } => { - // Deserialize the binary msg into either cw721 or sg721 + // Deserialize the binary msg into cw721 let mut instantiate_msg = try_deserialize_nft_instantiate_msg(instantiate_msg)?; // Modify the InstantiateMsg such that the minter is now this contract. // We will update ownership of the NFT contract to be the DAO in the submessage reply. - // - // NOTE: sg721 also has a creator that is set in the `collection_info` field, - // we override this with the address of the DAO (the sender of this message). - // In sg721 the `creator` address controls metadata and royalties. - instantiate_msg - .modify_instantiate_msg(env.contract.address.as_str(), info.sender.as_str()); + instantiate_msg.modify_instantiate_msg(env.contract.address.as_str()); // Check there is at least one NFT to initialize if initial_nfts.is_empty() { diff --git a/contracts/voting/dao-voting-cw721-staked/src/msg.rs b/contracts/voting/dao-voting-cw721-staked/src/msg.rs index 52c0acdb9..837851ed3 100644 --- a/contracts/voting/dao-voting-cw721-staked/src/msg.rs +++ b/contracts/voting/dao-voting-cw721-staked/src/msg.rs @@ -25,8 +25,10 @@ pub enum NftContract { /// valid mint message for the corresponding cw721 contract. initial_nfts: Vec, }, - /// Uses a factory pattern that must return the denom, optionally a Token Contract address, - /// and any setup messages. The binary must serialize to a `WasmMsg::Execute` message. + /// Uses a factory contract that must return the address of the NFT contract. + /// The binary must serialize to a `WasmMsg::Execute` message. + /// Validation happens in the factory contract itself, so be sure to use a + /// trusted factory contract. Factory(Binary), } @@ -94,8 +96,3 @@ pub enum QueryMsg { #[cw_serde] pub struct MigrateMsg {} - -#[cw_serde] -pub struct NftFactoryCallback { - pub nft_contract: String, -} diff --git a/contracts/voting/dao-voting-cw721-staked/src/testing/mod.rs b/contracts/voting/dao-voting-cw721-staked/src/testing/mod.rs index 10b22e8ee..ea43bc797 100644 --- a/contracts/voting/dao-voting-cw721-staked/src/testing/mod.rs +++ b/contracts/voting/dao-voting-cw721-staked/src/testing/mod.rs @@ -3,7 +3,6 @@ mod execute; mod hooks; mod instantiate; mod queries; -mod stargaze; mod tests; use cosmwasm_std::Addr; diff --git a/contracts/voting/dao-voting-cw721-staked/src/testing/stargaze.rs b/contracts/voting/dao-voting-cw721-staked/src/testing/stargaze.rs deleted file mode 100644 index 0ce304712..000000000 --- a/contracts/voting/dao-voting-cw721-staked/src/testing/stargaze.rs +++ /dev/null @@ -1,167 +0,0 @@ -use cosmwasm_std::{to_binary, Addr, Empty, Uint128}; -use cw721::OwnerOfResponse; -use cw_multi_test::{Contract, ContractWrapper, Executor}; -use dao_voting::threshold::ActiveThreshold; -use sg721::{CollectionInfo, RoyaltyInfoResponse, UpdateCollectionInfoMsg}; -use sg721_base::msg::CollectionInfoResponse; -use sg_multi_test::StargazeApp; -use sg_std::StargazeMsgWrapper; - -use crate::{ - msg::{InstantiateMsg, NftContract, QueryMsg}, - state::Config, - testing::CREATOR_ADDR, -}; - -// Setup Stargaze contracts for multi-test -fn sg721_base_contract() -> Box> { - let contract = ContractWrapper::new( - sg721_base::entry::execute, - sg721_base::entry::instantiate, - sg721_base::entry::query, - ); - Box::new(contract) -} - -// Stargze contracts need a custom message wrapper -fn voting_sg721_staked_contract() -> Box> { - let contract = ContractWrapper::new_with_empty( - crate::contract::execute, - crate::contract::instantiate, - crate::contract::query, - ) - .with_reply_empty(crate::contract::reply); - Box::new(contract) -} - -// I can create new Stargaze NFT collection when creating a dao-voting-cw721-staked contract -#[test] -fn test_instantiate_with_new_sg721_collection() -> anyhow::Result<()> { - let mut app = StargazeApp::default(); - let module_id = app.store_code(voting_sg721_staked_contract()); - let sg721_id = app.store_code(sg721_base_contract()); - - let module_addr = app - .instantiate_contract( - module_id, - Addr::unchecked(CREATOR_ADDR), - &InstantiateMsg { - nft_contract: NftContract::New { - code_id: sg721_id, - label: "Test NFT".to_string(), - msg: to_binary(&sg721::InstantiateMsg { - name: "Test NFT".to_string(), - symbol: "TEST".to_string(), - minter: CREATOR_ADDR.to_string(), - collection_info: CollectionInfo { - creator: CREATOR_ADDR.to_string(), - description: "Test NFT".to_string(), - image: "https://example.com/image.jpg".to_string(), - external_link: None, - explicit_content: None, - start_trading_time: None, - royalty_info: None, - }, - })?, - initial_nfts: vec![to_binary(&sg721::ExecuteMsg::::Mint { - owner: CREATOR_ADDR.to_string(), - token_uri: Some("https://example.com".to_string()), - token_id: "1".to_string(), - extension: Empty {}, - })?], - }, - unstaking_duration: None, - active_threshold: None, - }, - &[], - "cw721_voting", - None, - ) - .unwrap(); - - let config: Config = app - .wrap() - .query_wasm_smart(module_addr, &QueryMsg::Config {})?; - let sg721_addr = config.nft_address; - - // Check that the NFT contract was created - let owner: OwnerOfResponse = app.wrap().query_wasm_smart( - sg721_addr.clone(), - &cw721::Cw721QueryMsg::OwnerOf { - token_id: "1".to_string(), - include_expired: None, - }, - )?; - assert_eq!(owner.owner, CREATOR_ADDR); - - // Check that collection info creator is set to the DAO (in this case CREATOR_ADDR) - // Normally the DAO would instantiate this contract - let creator: CollectionInfoResponse = app - .wrap() - .query_wasm_smart(sg721_addr, &sg721_base::msg::QueryMsg::CollectionInfo {})?; - assert_eq!(creator.creator, CREATOR_ADDR.to_string()); - - Ok(()) -} - -#[test] -#[should_panic(expected = "Active threshold count is greater than supply")] -fn test_instantiate_with_new_sg721_collection_abs_count_validation() { - let mut app = StargazeApp::default(); - let module_id = app.store_code(voting_sg721_staked_contract()); - let sg721_id = app.store_code(sg721_base_contract()); - - // Test edge case - app.instantiate_contract( - module_id, - Addr::unchecked("contract0"), - &InstantiateMsg { - nft_contract: NftContract::New { - code_id: sg721_id, - label: "Test NFT".to_string(), - msg: to_binary(&sg721::InstantiateMsg { - name: "Test NFT".to_string(), - symbol: "TEST".to_string(), - minter: "contract0".to_string(), - collection_info: CollectionInfo { - creator: "contract0".to_string(), - description: "Test NFT".to_string(), - image: "https://example.com/image.jpg".to_string(), - external_link: None, - explicit_content: None, - start_trading_time: None, - royalty_info: None, - }, - }) - .unwrap(), - initial_nfts: vec![ - to_binary(&sg721::ExecuteMsg::::Mint { - owner: "contract0".to_string(), - token_uri: Some("https://example.com".to_string()), - token_id: "1".to_string(), - extension: Empty {}, - }) - .unwrap(), - to_binary(&sg721::ExecuteMsg::::UpdateCollectionInfo { - collection_info: UpdateCollectionInfoMsg:: { - description: None, - image: None, - external_link: None, - explicit_content: None, - royalty_info: None, - }, - }) - .unwrap(), - ], - }, - unstaking_duration: None, - active_threshold: Some(ActiveThreshold::AbsoluteCount { - count: Uint128::new(2), - }), - }, - &[], - "cw721_voting", - None, - ) - .unwrap(); -} From 9b9df6a47d8310dc0e59144c6d94da666e145ebc Mon Sep 17 00:00:00 2001 From: Jake Hartnell Date: Sat, 16 Sep 2023 14:41:08 -0700 Subject: [PATCH 15/30] Docs and comments on factory pattern, clean up --- Cargo.lock | 69 ------------------- .../voting/dao-voting-token-staked/Cargo.toml | 2 +- .../voting/dao-voting-token-staked/README.md | 8 +++ .../schema/dao-voting-token-staked.json | 2 +- .../voting/dao-voting-token-staked/src/msg.rs | 4 +- .../src/tests/test_tube/integration_tests.rs | 9 +-- 6 files changed, 14 insertions(+), 80 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 99fa992c8..284445c3d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2159,10 +2159,6 @@ dependencies = [ "dao-test-custom-factory", "dao-testing", "dao-voting 2.2.0", - "sg-multi-test", - "sg-std", - "sg721", - "sg721-base", "thiserror", ] @@ -3895,71 +3891,6 @@ dependencies = [ "unsafe-libyaml", ] -[[package]] -name = "sg-multi-test" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20744734b8049c64747bfb083bbc06a3c7204d1d34881ed3d89698e182aa9f97" -dependencies = [ - "anyhow", - "cosmwasm-std", - "cw-multi-test", - "schemars", - "serde", - "sg-std", -] - -[[package]] -name = "sg-std" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4db53aebc2b4f981dc20a51213544adde8beaace6880345627f4babe2e1bc3ab" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-utils 1.0.1", - "cw721 0.18.0", - "schemars", - "serde", - "thiserror", -] - -[[package]] -name = "sg721" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e7d8f93b519c4c95973a68c7abee2de838497974d666dddb4dabd04d9c7cbf6" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-ownable", - "cw-utils 1.0.1", - "cw721-base 0.18.0", - "serde", - "thiserror", -] - -[[package]] -name = "sg721-base" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af08801d6f50cb13be05a3d2e815fbdb9dbba82086bbab877599ed4d422e9441" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-ownable", - "cw-storage-plus 1.1.0", - "cw-utils 1.0.1", - "cw2 1.1.0", - "cw721 0.18.0", - "cw721-base 0.18.0", - "serde", - "sg-std", - "sg721", - "thiserror", - "url", -] - [[package]] name = "sha1" version = "0.10.5" diff --git a/contracts/voting/dao-voting-token-staked/Cargo.toml b/contracts/voting/dao-voting-token-staked/Cargo.toml index 864b6eb23..083129510 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/README.md b/contracts/voting/dao-voting-token-staked/README.md index c9275c390..2f1b9d693 100644 --- a/contracts/voting/dao-voting-token-staked/README.md +++ b/contracts/voting/dao-voting-token-staked/README.md @@ -80,3 +80,11 @@ Example insantiation mesggage: NOTE: if using an existing Token Factory token, double check the Token Factory admin and consider changing the Token Factory to be the DAO after the DAO is created. +### Use a factory +Occassionally, more customization is needed. Maybe you want to have an Augmented Bonding Curve contract or LP pool that requires additional setup? It's possible with factory contracts! + +The `factory` pattern takes a single `WasmMsg::Execute` message that calls into a custom factory contract. + +**NOTE:** when using the factory pattern, it is important to only use a trusted factory contract, as all validation happens in the factory contract. + +The [dao-test-custom-factory contract](../test/dao-test-custom-factory) provides an example of how this can be done and is used for tests. It is NOT production ready, but meant to serve as an example for building factory contracts. diff --git a/contracts/voting/dao-voting-token-staked/schema/dao-voting-token-staked.json b/contracts/voting/dao-voting-token-staked/schema/dao-voting-token-staked.json index b3e7c2bb1..99b6ec837 100644 --- a/contracts/voting/dao-voting-token-staked/schema/dao-voting-token-staked.json +++ b/contracts/voting/dao-voting-token-staked/schema/dao-voting-token-staked.json @@ -305,7 +305,7 @@ "additionalProperties": false }, { - "description": "Uses a factory pattern that must return the denom, optionally a Token Contract address. The binary must serialize to a `WasmMsg::Execute` message.", + "description": "Uses a factory contract that must return the denom, optionally a Token Contract address. The binary must serialize to a `WasmMsg::Execute` message. Validation happens in the factory contract itself, so be sure to use a trusted factory contract.", "type": "object", "required": [ "factory" diff --git a/contracts/voting/dao-voting-token-staked/src/msg.rs b/contracts/voting/dao-voting-token-staked/src/msg.rs index a5210ba9c..90ac64f02 100644 --- a/contracts/voting/dao-voting-token-staked/src/msg.rs +++ b/contracts/voting/dao-voting-token-staked/src/msg.rs @@ -17,8 +17,10 @@ pub enum TokenInfo { /// Creates a new Token Factory token via the issue contract with the DAO automatically /// setup as admin and owner. New(NewTokenInfo), - /// Uses a factory pattern that must return the denom, optionally a Token Contract address. + /// Uses a factory contract that must return the denom, optionally a Token Contract address. /// The binary must serialize to a `WasmMsg::Execute` message. + /// Validation happens in the factory contract itself, so be sure to use a + /// trusted factory contract. Factory(Binary), } 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 c70924958..79126454c 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 @@ -416,12 +416,5 @@ fn test_factory() { // Query token contract let token_contract: Addr = voting.query(&QueryMsg::TokenContract {}).unwrap(); - assert_eq!( - denom, - format!( - "factory/{}/{}", - token_contract.to_string(), - DENOM.to_string() - ) - ); + assert_eq!(denom, format!("factory/{}/{}", token_contract, DENOM)); } From d0385e24a11ad955e991dd3ea422a63708b9c0d9 Mon Sep 17 00:00:00 2001 From: Jake Hartnell Date: Sat, 16 Sep 2023 15:50:29 -0700 Subject: [PATCH 16/30] FactoryCallback -> TokenFactoryCallback --- .../test/dao-test-custom-factory/src/contract.rs | 11 +++++++---- contracts/test/dao-test-custom-factory/src/error.rs | 5 ++++- .../voting/dao-voting-token-staked/src/contract.rs | 4 ++-- contracts/voting/dao-voting-token-staked/src/error.rs | 2 +- packages/dao-interface/src/token.rs | 2 +- 5 files changed, 15 insertions(+), 9 deletions(-) diff --git a/contracts/test/dao-test-custom-factory/src/contract.rs b/contracts/test/dao-test-custom-factory/src/contract.rs index 780fe94af..27904fd07 100644 --- a/contracts/test/dao-test-custom-factory/src/contract.rs +++ b/contracts/test/dao-test-custom-factory/src/contract.rs @@ -10,10 +10,10 @@ use cw_storage_plus::Item; use cw_tokenfactory_issuer::msg::{ ExecuteMsg as IssuerExecuteMsg, InstantiateMsg as IssuerInstantiateMsg, }; -use cw_utils::parse_reply_instantiate_data; +use cw_utils::{one_coin, parse_reply_instantiate_data}; use dao_interface::{ nft::NftFactoryCallback, - token::{FactoryCallback, InitialBalance, NewTokenInfo}, + token::{InitialBalance, NewTokenInfo, TokenFactoryCallback}, voting::{ActiveThresholdQuery, Query as VotingModuleQueryMsg}, }; use dao_voting::threshold::{ @@ -109,6 +109,9 @@ pub fn execute_token_factory_factory( info: MessageInfo, token: NewTokenInfo, ) -> Result { + // Validate one coin was sent + one_coin(&info)?; + // Save voting module address VOTING_MODULE.save(deps.storage, &info.sender)?; @@ -250,13 +253,13 @@ pub fn reply(deps: DepsMut, env: Env, msg: Reply) -> Result { - // Parse issuer address from instantiate reply + // Parse nft address from instantiate reply let nft_address = parse_reply_instantiate_data(msg)?.contract_address; Ok(Response::new().set_data(to_binary(&NftFactoryCallback { diff --git a/contracts/test/dao-test-custom-factory/src/error.rs b/contracts/test/dao-test-custom-factory/src/error.rs index 5c8551174..4a29782c7 100644 --- a/contracts/test/dao-test-custom-factory/src/error.rs +++ b/contracts/test/dao-test-custom-factory/src/error.rs @@ -1,5 +1,5 @@ use cosmwasm_std::StdError; -use cw_utils::ParseReplyError; +use cw_utils::{ParseReplyError, PaymentError}; use dao_voting::threshold::ActiveThresholdError; use thiserror::Error; @@ -14,6 +14,9 @@ pub enum ContractError { #[error(transparent)] ParseReplyError(#[from] ParseReplyError), + #[error(transparent)] + PaymentError(#[from] PaymentError), + #[error("Unauthorized")] Unauthorized {}, diff --git a/contracts/voting/dao-voting-token-staked/src/contract.rs b/contracts/voting/dao-voting-token-staked/src/contract.rs index bf1e0c26c..cc1eb16ac 100644 --- a/contracts/voting/dao-voting-token-staked/src/contract.rs +++ b/contracts/voting/dao-voting-token-staked/src/contract.rs @@ -17,7 +17,7 @@ use cw_utils::{ use dao_hooks::stake::{stake_hook_msgs, unstake_hook_msgs}; use dao_interface::{ state::ModuleInstantiateCallback, - token::{FactoryCallback, InitialBalance, NewTokenInfo}, + token::{InitialBalance, NewTokenInfo, TokenFactoryCallback}, voting::{IsActiveResponse, TotalPowerAtHeightResponse, VotingPowerAtHeightResponse}, }; use dao_voting::{ @@ -722,7 +722,7 @@ pub fn reply(deps: DepsMut, env: Env, msg: Reply) -> Result { // Parse info from the callback, this will fail // if incorrectly formatted. - let info: FactoryCallback = from_binary(&data)?; + let info: TokenFactoryCallback = from_binary(&data)?; // Save Denom DENOM.save(deps.storage, &info.denom)?; diff --git a/contracts/voting/dao-voting-token-staked/src/error.rs b/contracts/voting/dao-voting-token-staked/src/error.rs index a5121ef14..7086f9bb6 100644 --- a/contracts/voting/dao-voting-token-staked/src/error.rs +++ b/contracts/voting/dao-voting-token-staked/src/error.rs @@ -29,7 +29,7 @@ pub enum ContractError { #[error("Can only unstake less than or equal to the amount you have staked")] InvalidUnstakeAmount {}, - #[error("Factory contract did not implment the required NftFactoryCallback interface")] + #[error("Factory contract did not implment the required TokenFactoryCallback interface")] NoFactoryCallback {}, #[error("Nothing to claim")] diff --git a/packages/dao-interface/src/token.rs b/packages/dao-interface/src/token.rs index fa60aae1b..3bd8e6875 100644 --- a/packages/dao-interface/src/token.rs +++ b/packages/dao-interface/src/token.rs @@ -43,7 +43,7 @@ pub struct NewTokenInfo { } #[cw_serde] -pub struct FactoryCallback { +pub struct TokenFactoryCallback { pub denom: String, pub token_contract: Option, } From 01aed475afda7aeb2ef199cc3043c7596a003e7c Mon Sep 17 00:00:00 2001 From: Jake Hartnell Date: Sat, 16 Sep 2023 16:23:56 -0700 Subject: [PATCH 17/30] Fix NFT factory and tests --- .../dao-voting-cw721-staked/src/contract.rs | 39 ++++++++++++------- .../src/testing/tests.rs | 2 +- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/contracts/voting/dao-voting-cw721-staked/src/contract.rs b/contracts/voting/dao-voting-cw721-staked/src/contract.rs index 8e8e88a5f..fd7203c41 100644 --- a/contracts/voting/dao-voting-cw721-staked/src/contract.rs +++ b/contracts/voting/dao-voting-cw721-staked/src/contract.rs @@ -159,19 +159,28 @@ pub fn instantiate( } NftContract::Factory(binary) => match from_binary(&binary)? { WasmMsg::Execute { - msg, + msg: wasm_msg, contract_addr, funds, - } => Ok(Response::new() - .add_attribute("action", "intantiate") - .add_submessage(SubMsg::reply_on_success( - WasmMsg::Execute { - contract_addr, - msg, - funds, - }, - FACTORY_EXECUTE_REPLY_ID, - ))), + } => { + // Save config with empty nft_address + let config = Config { + nft_address: Addr::unchecked(""), + unstaking_duration: msg.unstaking_duration, + }; + CONFIG.save(deps.storage, &config)?; + + Ok(Response::new() + .add_attribute("action", "intantiate") + .add_submessage(SubMsg::reply_on_success( + WasmMsg::Execute { + contract_addr, + msg: wasm_msg, + funds, + }, + FACTORY_EXECUTE_REPLY_ID, + ))) + } _ => Err(ContractError::UnsupportedFactoryMsg {}), }, } @@ -712,8 +721,8 @@ pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result { + // Parse reply data let res = parse_reply_execute_data(msg)?; - match res.data { Some(data) => { let mut config = CONFIG.load(deps.storage)?; @@ -726,8 +735,10 @@ pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result( + nft_address.clone(), + &Cw721QueryMsg::NumTokens {}, + )?; // Update NFT contract config.nft_address = nft_address; diff --git a/contracts/voting/dao-voting-cw721-staked/src/testing/tests.rs b/contracts/voting/dao-voting-cw721-staked/src/testing/tests.rs index d0fcd621a..0e4630244 100644 --- a/contracts/voting/dao-voting-cw721-staked/src/testing/tests.rs +++ b/contracts/voting/dao-voting-cw721-staked/src/testing/tests.rs @@ -1040,7 +1040,7 @@ fn test_factory() { "cw721_voting", None, ) - .unwrap_err(); + .unwrap(); } #[test] From 39ec340378b0a7d3be2fe1ee000ab65e9adb36a2 Mon Sep 17 00:00:00 2001 From: Jake Hartnell Date: Sat, 16 Sep 2023 16:48:03 -0700 Subject: [PATCH 18/30] Fixup and tests for passthrough of funds --- .../dao-voting-token-staked/src/contract.rs | 6 +- .../src/tests/test_tube/integration_tests.rs | 65 +++++++++++++++++-- .../src/tests/test_tube/test_env.rs | 2 +- .../dao-testing/src/test_tube/dao_dao_core.rs | 5 +- 4 files changed, 67 insertions(+), 11 deletions(-) diff --git a/contracts/voting/dao-voting-token-staked/src/contract.rs b/contracts/voting/dao-voting-token-staked/src/contract.rs index cc1eb16ac..5d9eadd1f 100644 --- a/contracts/voting/dao-voting-token-staked/src/contract.rs +++ b/contracts/voting/dao-voting-token-staked/src/contract.rs @@ -128,7 +128,9 @@ pub fn instantiate( } TokenInfo::Factory(binary) => match from_binary(&binary)? { WasmMsg::Execute { - msg, contract_addr, .. + msg, + contract_addr, + funds, } => Ok(Response::new() .add_attribute("action", "intantiate") .add_attribute("token", "custom_factory") @@ -136,7 +138,7 @@ pub fn instantiate( WasmMsg::Execute { contract_addr, msg, - funds: vec![], + funds, }, FACTORY_EXECUTE_REPLY_ID, ))), 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 79126454c..df2629e5a 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 @@ -12,7 +12,7 @@ use dao_voting::{ threshold::{ActiveThreshold, ActiveThresholdError, PercentageThreshold, Threshold}, }; use osmosis_std::types::cosmos::bank::v1beta1::QueryBalanceRequest; -use osmosis_test_tube::{Account, OsmosisTestApp}; +use osmosis_test_tube::{Account, OsmosisTestApp, RunnerError}; use crate::{ msg::{ExecuteMsg, InstantiateMsg, QueryMsg, TokenInfo}, @@ -335,8 +335,10 @@ fn test_factory() { .. } = env.full_dao_setup(&app); + let factory_addr = custom_factory.unwrap().contract_addr.to_string(); + // Instantiate a new voting contract using the factory pattern - let msg = dao_interface::msg::InstantiateMsg { + let mut msg = dao_interface::msg::InstantiateMsg { dao_uri: None, admin: None, name: "DAO DAO".to_string(), @@ -349,7 +351,7 @@ fn test_factory() { msg: to_binary(&InstantiateMsg { token_info: TokenInfo::Factory( to_binary(&WasmMsg::Execute { - contract_addr: custom_factory.unwrap().contract_addr.to_string(), + contract_addr: factory_addr.clone(), msg: to_binary( &dao_test_custom_factory::msg::ExecuteMsg::TokenFactoryFactory( NewTokenInfo { @@ -401,8 +403,60 @@ fn test_factory() { initial_items: None, }; - // Instantiate DAO - let dao = DaoCore::new(&app, &msg, &accounts[0]).unwrap(); + // Instantiate DAO fails because no funds to create the token were sent + let err = DaoCore::new(&app, &msg, &accounts[0], &vec![]).unwrap_err(); + + // Check error is no funds sent + assert_eq!( + err, + RunnerError::ExecuteError { + msg: "failed to execute message; message index: 0: dispatch: submessages: dispatch: submessages: No funds sent: execute wasm contract failed".to_string(), + } + ); + + // Include funds in ModuleInstantiateInfo + let funds = vec![Coin { + denom: "uosmo".to_string(), + amount: Uint128::new(100), + }]; + msg.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: factory_addr, + msg: to_binary( + &dao_test_custom_factory::msg::ExecuteMsg::TokenFactoryFactory( + NewTokenInfo { + token_issuer_code_id: tf_issuer.code_id, + subdenom: DENOM.to_string(), + metadata: None, + initial_balances: vec![InitialBalance { + address: accounts[0].address(), + amount: Uint128::new(100), + }], + initial_dao_balance: None, + }, + ), + ) + .unwrap(), + funds: funds.clone(), + }) + .unwrap(), + ), + unstaking_duration: Some(Duration::Time(2)), + active_threshold: Some(ActiveThreshold::AbsoluteCount { + count: Uint128::new(75), + }), + }) + .unwrap(), + admin: Some(Admin::CoreModule {}), + funds: funds.clone(), + label: "DAO DAO Voting Module".to_string(), + }; + + // Creating the DAO now succeeds + let dao = DaoCore::new(&app, &msg, &accounts[0], &funds).unwrap(); // Query voting module let voting_module: Addr = dao.query(&DaoQueryMsg::VotingModule {}).unwrap(); @@ -416,5 +470,6 @@ fn test_factory() { // Query token contract let token_contract: Addr = voting.query(&QueryMsg::TokenContract {}).unwrap(); + // Check the TF denom is as expected assert_eq!(denom, format!("factory/{}/{}", token_contract, DENOM)); } 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 1d8d8f1d2..a9916c48d 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 @@ -256,7 +256,7 @@ impl TestEnvBuilder { }; // Instantiate DAO - let dao = DaoCore::new(app, &msg, &accounts[0]).unwrap(); + let dao = DaoCore::new(app, &msg, &accounts[0], &vec![]).unwrap(); // Get voting module address, setup vp_contract helper let vp_addr: Addr = dao.query(&DaoQueryMsg::VotingModule {}).unwrap(); diff --git a/packages/dao-testing/src/test_tube/dao_dao_core.rs b/packages/dao-testing/src/test_tube/dao_dao_core.rs index 0b9f6752f..1e24998b3 100644 --- a/packages/dao-testing/src/test_tube/dao_dao_core.rs +++ b/packages/dao-testing/src/test_tube/dao_dao_core.rs @@ -21,10 +21,9 @@ impl<'a> DaoCore<'a> { app: &'a OsmosisTestApp, instantiate_msg: &InstantiateMsg, signer: &SigningAccount, + funds: &Vec, ) -> 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 @@ -36,7 +35,7 @@ impl<'a> DaoCore<'a> { &instantiate_msg, Some(&signer.address()), None, - &[token_creation_fee], + funds, signer, )? .data From 5f509ed43f57af42d706d6dc2c801fd98dc98df3 Mon Sep 17 00:00:00 2001 From: Jake Hartnell Date: Sat, 16 Sep 2023 18:12:13 -0700 Subject: [PATCH 19/30] Better test coverage for factory pattern --- .../dao-test-custom-factory/src/contract.rs | 94 +++++- .../test/dao-test-custom-factory/src/msg.rs | 9 + .../dao-voting-cw721-staked/src/contract.rs | 3 +- .../src/testing/tests.rs | 246 +++++++++++++++- .../src/tests/test_tube/integration_tests.rs | 277 +++++++++++++++++- .../dao-testing/src/test_tube/dao_dao_core.rs | 2 +- 6 files changed, 606 insertions(+), 25 deletions(-) diff --git a/contracts/test/dao-test-custom-factory/src/contract.rs b/contracts/test/dao-test-custom-factory/src/contract.rs index 27904fd07..96127718f 100644 --- a/contracts/test/dao-test-custom-factory/src/contract.rs +++ b/contracts/test/dao-test-custom-factory/src/contract.rs @@ -56,13 +56,30 @@ pub fn execute( msg: ExecuteMsg, ) -> Result { match msg { - ExecuteMsg::TokenFactoryFactory(token) => { - execute_token_factory_factory(deps, env, info, token) - } ExecuteMsg::NftFactory { code_id, cw721_instantiate_msg, } => execute_nft_factory(deps, env, info, cw721_instantiate_msg, code_id), + ExecuteMsg::NftFactoryWithFunds { + code_id, + cw721_instantiate_msg, + } => execute_nft_factory_with_funds(deps, env, info, cw721_instantiate_msg, code_id), + ExecuteMsg::NftFactoryNoCallback {} => execute_nft_factory_no_callback(deps, env, info), + ExecuteMsg::NftFactoryWrongCallback {} => { + execute_nft_factory_wrong_callback(deps, env, info) + } + ExecuteMsg::TokenFactoryFactory(token) => { + execute_token_factory_factory(deps, env, info, token) + } + ExecuteMsg::TokenFactoryFactoryWithFunds(token) => { + execute_token_factory_factory_with_funds(deps, env, info, token) + } + ExecuteMsg::TokenFactoryFactoryNoCallback {} => { + execute_token_factory_factory_no_callback(deps, env, info) + } + ExecuteMsg::TokenFactoryFactoryWrongCallback {} => { + execute_token_factory_factory_wrong_callback(deps, env, info) + } } } @@ -100,6 +117,41 @@ pub fn execute_nft_factory( Ok(Response::new().add_submessage(msg)) } +/// Requires one coin sent to test funds pass through for factory contracts +pub fn execute_nft_factory_with_funds( + deps: DepsMut, + env: Env, + info: MessageInfo, + cw721_instantiate_msg: Cw721InstantiateMsg, + code_id: u64, +) -> Result { + // Validate one coin was sent + one_coin(&info)?; + + execute_nft_factory(deps, env, info, cw721_instantiate_msg, code_id) +} + +/// No callback for testing +pub fn execute_nft_factory_no_callback( + _deps: DepsMut, + _env: Env, + _info: MessageInfo, +) -> Result { + Ok(Response::new()) +} + +/// Wrong callback for testing +pub fn execute_nft_factory_wrong_callback( + _deps: DepsMut, + _env: Env, + _info: MessageInfo, +) -> Result { + Ok(Response::new().set_data(to_binary(&TokenFactoryCallback { + denom: "wrong".to_string(), + token_contract: None, + })?)) +} + /// An example factory that instantiates a cw_tokenfactory_issuer contract /// A more realistic example would be something like a DeFi Pool or Augmented /// bonding curve. @@ -109,9 +161,6 @@ pub fn execute_token_factory_factory( info: MessageInfo, token: NewTokenInfo, ) -> Result { - // Validate one coin was sent - one_coin(&info)?; - // Save voting module address VOTING_MODULE.save(deps.storage, &info.sender)?; @@ -142,6 +191,39 @@ pub fn execute_token_factory_factory( Ok(Response::new().add_submessage(msg)) } +/// Requires one coin sent to test funds pass through for factory contracts +pub fn execute_token_factory_factory_with_funds( + deps: DepsMut, + env: Env, + info: MessageInfo, + token: NewTokenInfo, +) -> Result { + // Validate one coin was sent + one_coin(&info)?; + + execute_token_factory_factory(deps, env, info, token) +} + +/// No callback for testing +pub fn execute_token_factory_factory_no_callback( + _deps: DepsMut, + _env: Env, + _info: MessageInfo, +) -> Result { + Ok(Response::new()) +} + +/// Wrong callback for testing +pub fn execute_token_factory_factory_wrong_callback( + _deps: DepsMut, + _env: Env, + _info: MessageInfo, +) -> Result { + Ok(Response::new().set_data(to_binary(&NftFactoryCallback { + nft_contract: "nope".to_string(), + })?)) +} + #[cfg_attr(not(feature = "library"), entry_point)] pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { match msg { diff --git a/contracts/test/dao-test-custom-factory/src/msg.rs b/contracts/test/dao-test-custom-factory/src/msg.rs index edb403c0e..5d9e9f8e0 100644 --- a/contracts/test/dao-test-custom-factory/src/msg.rs +++ b/contracts/test/dao-test-custom-factory/src/msg.rs @@ -8,10 +8,19 @@ pub struct InstantiateMsg {} #[cw_serde] pub enum ExecuteMsg { TokenFactoryFactory(NewTokenInfo), + TokenFactoryFactoryWithFunds(NewTokenInfo), + TokenFactoryFactoryNoCallback {}, + TokenFactoryFactoryWrongCallback {}, NftFactory { code_id: u64, cw721_instantiate_msg: Cw721InstantiateMsg, }, + NftFactoryWithFunds { + code_id: u64, + cw721_instantiate_msg: Cw721InstantiateMsg, + }, + NftFactoryNoCallback {}, + NftFactoryWrongCallback {}, } #[cw_serde] diff --git a/contracts/voting/dao-voting-cw721-staked/src/contract.rs b/contracts/voting/dao-voting-cw721-staked/src/contract.rs index fd7203c41..c3834f4a9 100644 --- a/contracts/voting/dao-voting-cw721-staked/src/contract.rs +++ b/contracts/voting/dao-voting-cw721-staked/src/contract.rs @@ -1,4 +1,3 @@ -use cosmwasm_schema::cw_serde; #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ @@ -31,7 +30,7 @@ const FACTORY_EXECUTE_REPLY_ID: u64 = 2; // when using active threshold with percent const PRECISION_FACTOR: u128 = 10u128.pow(9); -#[cw_serde] +// Supported NFT instantiation messages pub enum NftInstantiateMsg { Cw721(cw721_base::InstantiateMsg), } diff --git a/contracts/voting/dao-voting-cw721-staked/src/testing/tests.rs b/contracts/voting/dao-voting-cw721-staked/src/testing/tests.rs index 0e4630244..df3aeb22f 100644 --- a/contracts/voting/dao-voting-cw721-staked/src/testing/tests.rs +++ b/contracts/voting/dao-voting-cw721-staked/src/testing/tests.rs @@ -1,8 +1,8 @@ use cosmwasm_std::testing::{mock_dependencies, mock_env}; -use cosmwasm_std::{to_binary, Addr, Decimal, Empty, Uint128, WasmMsg}; +use cosmwasm_std::{to_binary, Addr, Coin, Decimal, Empty, Uint128, WasmMsg}; use cw721_base::msg::{ExecuteMsg as Cw721ExecuteMsg, InstantiateMsg as Cw721InstantiateMsg}; use cw721_controllers::{NftClaim, NftClaimsResponse}; -use cw_multi_test::{next_block, App, Executor}; +use cw_multi_test::{next_block, App, BankSudo, Executor, SudoMsg}; use cw_utils::Duration; use dao_interface::voting::IsActiveResponse; use dao_testing::contracts::{ @@ -1043,6 +1043,248 @@ fn test_factory() { .unwrap(); } +#[test] +fn test_factory_with_funds_pass_through() { + let mut app = App::default(); + let module_id = app.store_code(voting_cw721_staked_contract()); + let cw721_id = app.store_code(cw721_base_contract()); + let factory_id = app.store_code(dao_test_custom_factory()); + + // Mint some tokens to creator + app.sudo(SudoMsg::Bank(BankSudo::Mint { + to_address: CREATOR_ADDR.to_string(), + amount: vec![Coin { + denom: "ujuno".to_string(), + amount: Uint128::new(10000), + }], + })) + .unwrap(); + + // Instantiate factory + let factory_addr = app + .instantiate_contract( + factory_id, + Addr::unchecked(CREATOR_ADDR), + &dao_test_custom_factory::msg::InstantiateMsg {}, + &[], + "test factory".to_string(), + None, + ) + .unwrap(); + + // Instantiate without funds fails + app.instantiate_contract( + module_id, + Addr::unchecked(CREATOR_ADDR), + &InstantiateMsg { + nft_contract: NftContract::Factory( + to_binary(&WasmMsg::Execute { + contract_addr: factory_addr.to_string(), + msg: to_binary( + &dao_test_custom_factory::msg::ExecuteMsg::NftFactoryWithFunds { + code_id: cw721_id, + cw721_instantiate_msg: Cw721InstantiateMsg { + name: "Test NFT".to_string(), + symbol: "TEST".to_string(), + minter: CREATOR_ADDR.to_string(), + }, + }, + ) + .unwrap(), + funds: vec![], + }) + .unwrap(), + ), + unstaking_duration: None, + active_threshold: Some(ActiveThreshold::Percentage { + percent: Decimal::percent(1), + }), + }, + &[], + "cw721_voting", + None, + ) + .unwrap_err(); + + // Instantiate using factory succeeds + let funds = vec![Coin { + denom: "ujuno".to_string(), + amount: Uint128::new(100), + }]; + app.instantiate_contract( + module_id, + Addr::unchecked(CREATOR_ADDR), + &InstantiateMsg { + nft_contract: NftContract::Factory( + to_binary(&WasmMsg::Execute { + contract_addr: factory_addr.to_string(), + msg: to_binary( + &dao_test_custom_factory::msg::ExecuteMsg::NftFactoryWithFunds { + code_id: cw721_id, + cw721_instantiate_msg: Cw721InstantiateMsg { + name: "Test NFT".to_string(), + symbol: "TEST".to_string(), + minter: CREATOR_ADDR.to_string(), + }, + }, + ) + .unwrap(), + funds: funds.clone(), + }) + .unwrap(), + ), + unstaking_duration: None, + active_threshold: Some(ActiveThreshold::Percentage { + percent: Decimal::percent(1), + }), + }, + &funds, + "cw721_voting", + None, + ) + .unwrap(); +} + +#[test] +#[should_panic(expected = "Factory message must serialize to WasmMsg::Execute")] +fn test_unsupported_factory_msg() { + let mut app = App::default(); + let module_id = app.store_code(voting_cw721_staked_contract()); + let cw721_id = app.store_code(cw721_base_contract()); + + // Instantiate using factory succeeds + app.instantiate_contract( + module_id, + Addr::unchecked(CREATOR_ADDR), + &InstantiateMsg { + nft_contract: NftContract::Factory( + to_binary(&WasmMsg::Instantiate { + code_id: cw721_id, + msg: to_binary(&dao_test_custom_factory::msg::ExecuteMsg::NftFactory { + code_id: cw721_id, + cw721_instantiate_msg: Cw721InstantiateMsg { + name: "Test NFT".to_string(), + symbol: "TEST".to_string(), + minter: CREATOR_ADDR.to_string(), + }, + }) + .unwrap(), + admin: None, + label: "Test NFT".to_string(), + funds: vec![], + }) + .unwrap(), + ), + unstaking_duration: None, + active_threshold: Some(ActiveThreshold::Percentage { + percent: Decimal::percent(1), + }), + }, + &[], + "cw721_voting", + None, + ) + .unwrap(); +} + +#[test] +#[should_panic( + expected = "Error parsing into type dao_interface::nft::NftFactoryCallback: unknown field `denom`, expected `nft_contract`" +)] +fn test_factory_wrong_callback() { + let mut app = App::default(); + let module_id = app.store_code(voting_cw721_staked_contract()); + let _cw721_id = app.store_code(cw721_base_contract()); + let factory_id = app.store_code(dao_test_custom_factory()); + + // Instantiate factory + let factory_addr = app + .instantiate_contract( + factory_id, + Addr::unchecked(CREATOR_ADDR), + &dao_test_custom_factory::msg::InstantiateMsg {}, + &[], + "test factory".to_string(), + None, + ) + .unwrap(); + + // Instantiate using factory succeeds + app.instantiate_contract( + module_id, + Addr::unchecked(CREATOR_ADDR), + &InstantiateMsg { + nft_contract: NftContract::Factory( + to_binary(&WasmMsg::Execute { + contract_addr: factory_addr.to_string(), + msg: to_binary( + &dao_test_custom_factory::msg::ExecuteMsg::NftFactoryWrongCallback {}, + ) + .unwrap(), + funds: vec![], + }) + .unwrap(), + ), + unstaking_duration: None, + active_threshold: Some(ActiveThreshold::Percentage { + percent: Decimal::percent(1), + }), + }, + &[], + "cw721_voting", + None, + ) + .unwrap(); +} + +#[test] +#[should_panic(expected = "Invalid reply from sub-message: Missing reply data")] +fn test_factory_no_callback() { + let mut app = App::default(); + let module_id = app.store_code(voting_cw721_staked_contract()); + let _cw721_id = app.store_code(cw721_base_contract()); + let factory_id = app.store_code(dao_test_custom_factory()); + + // Instantiate factory + let factory_addr = app + .instantiate_contract( + factory_id, + Addr::unchecked(CREATOR_ADDR), + &dao_test_custom_factory::msg::InstantiateMsg {}, + &[], + "test factory".to_string(), + None, + ) + .unwrap(); + + // Instantiate using factory succeeds + app.instantiate_contract( + module_id, + Addr::unchecked(CREATOR_ADDR), + &InstantiateMsg { + nft_contract: NftContract::Factory( + to_binary(&WasmMsg::Execute { + contract_addr: factory_addr.to_string(), + msg: to_binary( + &dao_test_custom_factory::msg::ExecuteMsg::NftFactoryNoCallback {}, + ) + .unwrap(), + funds: vec![], + }) + .unwrap(), + ), + unstaking_duration: None, + active_threshold: Some(ActiveThreshold::Percentage { + percent: Decimal::percent(1), + }), + }, + &[], + "cw721_voting", + None, + ) + .unwrap(); +} + #[test] pub fn test_migrate_update_version() { let mut deps = mock_dependencies(); 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 df2629e5a..35ae643c8 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 @@ -338,7 +338,7 @@ fn test_factory() { let factory_addr = custom_factory.unwrap().contract_addr.to_string(); // Instantiate a new voting contract using the factory pattern - let mut msg = dao_interface::msg::InstantiateMsg { + let msg = dao_interface::msg::InstantiateMsg { dao_uri: None, admin: None, name: "DAO DAO".to_string(), @@ -403,6 +403,106 @@ fn test_factory() { initial_items: None, }; + // Instantiate DAO succeeds + let dao = DaoCore::new(&app, &msg, &accounts[0], &vec![]).unwrap(); + + // Query voting module + let voting_module: Addr = dao.query(&DaoQueryMsg::VotingModule {}).unwrap(); + let voting = + TokenVotingContract::new_with_values(&app, vp_contract.code_id, voting_module.to_string()) + .unwrap(); + + // Query denom + let denom = voting.query_denom().unwrap().denom; + + // Query token contract + let token_contract: Addr = voting.query(&QueryMsg::TokenContract {}).unwrap(); + + // Check the TF denom is as expected + assert_eq!(denom, format!("factory/{}/{}", token_contract, DENOM)); +} + +#[test] +fn test_factory_funds_pass_through() { + let app = OsmosisTestApp::new(); + let env = TestEnvBuilder::new(); + let TestEnv { + tf_issuer, + vp_contract, + proposal_single, + custom_factory, + accounts, + .. + } = env.full_dao_setup(&app); + + let factory_addr = custom_factory.unwrap().contract_addr.to_string(); + + // Instantiate a new voting contract using the factory pattern + let mut 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: factory_addr.clone(), + msg: to_binary( + &dao_test_custom_factory::msg::ExecuteMsg::TokenFactoryFactoryWithFunds( + NewTokenInfo { + token_issuer_code_id: tf_issuer.code_id, + subdenom: DENOM.to_string(), + metadata: None, + initial_balances: vec![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 fails because no funds to create the token were sent let err = DaoCore::new(&app, &msg, &accounts[0], &vec![]).unwrap_err(); @@ -426,7 +526,7 @@ fn test_factory() { to_binary(&WasmMsg::Execute { contract_addr: factory_addr, msg: to_binary( - &dao_test_custom_factory::msg::ExecuteMsg::TokenFactoryFactory( + &dao_test_custom_factory::msg::ExecuteMsg::TokenFactoryFactoryWithFunds( NewTokenInfo { token_issuer_code_id: tf_issuer.code_id, subdenom: DENOM.to_string(), @@ -456,20 +556,169 @@ fn test_factory() { }; // Creating the DAO now succeeds - let dao = DaoCore::new(&app, &msg, &accounts[0], &funds).unwrap(); + DaoCore::new(&app, &msg, &accounts[0], &funds).unwrap(); +} - // Query voting module - let voting_module: Addr = dao.query(&DaoQueryMsg::VotingModule {}).unwrap(); - let voting = - TokenVotingContract::new_with_values(&app, vp_contract.code_id, voting_module.to_string()) - .unwrap(); +#[test] +fn test_factory_no_callback() { + let app = OsmosisTestApp::new(); + let env = TestEnvBuilder::new(); + let TestEnv { + tf_issuer, + vp_contract, + proposal_single, + custom_factory, + accounts, + .. + } = env.full_dao_setup(&app); - // Query denom - let denom = voting.query_denom().unwrap().denom; + let factory_addr = custom_factory.unwrap().contract_addr.to_string(); - // Query token contract - let token_contract: Addr = voting.query(&QueryMsg::TokenContract {}).unwrap(); + // 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: factory_addr.clone(), + msg: to_binary( + &dao_test_custom_factory::msg::ExecuteMsg::TokenFactoryFactoryNoCallback{}, + ) + .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, + }; - // Check the TF denom is as expected - assert_eq!(denom, format!("factory/{}/{}", token_contract, DENOM)); + // Instantiate DAO fails because no funds to create the token were sent + let err = DaoCore::new(&app, &msg, &accounts[0], &vec![]).unwrap_err(); + + // Check error is no reply data + assert_eq!( + err, + RunnerError::ExecuteError { + msg: "failed to execute message; message index: 0: dispatch: submessages: dispatch: submessages: reply: Invalid reply from sub-message: Missing reply data: execute wasm contract failed".to_string(), + } + ); +} + +#[test] +fn test_factory_wrong_callback() { + let app = OsmosisTestApp::new(); + let env = TestEnvBuilder::new(); + let TestEnv { + tf_issuer, + vp_contract, + proposal_single, + custom_factory, + accounts, + .. + } = env.full_dao_setup(&app); + + let factory_addr = custom_factory.unwrap().contract_addr.to_string(); + + // 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: factory_addr.clone(), + msg: to_binary( + &dao_test_custom_factory::msg::ExecuteMsg::TokenFactoryFactoryWrongCallback{}, + ) + .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 fails because no funds to create the token were sent + let err = DaoCore::new(&app, &msg, &accounts[0], &vec![]).unwrap_err(); + + // Check error is wrong reply type + assert_eq!( + err, + RunnerError::ExecuteError { + msg: "failed to execute message; message index: 0: dispatch: submessages: dispatch: submessages: reply: Error parsing into type dao_interface::token::TokenFactoryCallback: unknown field `nft_contract`, expected `denom` or `token_contract`: execute wasm contract failed".to_string(), + } + ); } diff --git a/packages/dao-testing/src/test_tube/dao_dao_core.rs b/packages/dao-testing/src/test_tube/dao_dao_core.rs index 1e24998b3..3fc9b73e7 100644 --- a/packages/dao-testing/src/test_tube/dao_dao_core.rs +++ b/packages/dao-testing/src/test_tube/dao_dao_core.rs @@ -21,7 +21,7 @@ impl<'a> DaoCore<'a> { app: &'a OsmosisTestApp, instantiate_msg: &InstantiateMsg, signer: &SigningAccount, - funds: &Vec, + funds: &[Coin], ) -> Result { let wasm = Wasm::new(app); let code_id = wasm From 36f5d33eaf769cfad4c9f7db48a7ab0d98782e61 Mon Sep 17 00:00:00 2001 From: Jake Hartnell Date: Sat, 16 Sep 2023 20:16:13 -0700 Subject: [PATCH 20/30] Update InvalidActivePercentage error message for accuracy --- .../dao-voting-token-staked/src/tests/multitest/tests.rs | 8 ++++++-- packages/dao-voting/src/threshold.rs | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/contracts/voting/dao-voting-token-staked/src/tests/multitest/tests.rs b/contracts/voting/dao-voting-token-staked/src/tests/multitest/tests.rs index ab56d26f1..3498764ec 100644 --- a/contracts/voting/dao-voting-token-staked/src/tests/multitest/tests.rs +++ b/contracts/voting/dao-voting-token-staked/src/tests/multitest/tests.rs @@ -1174,7 +1174,9 @@ fn test_update_active_threshold() { } #[test] -#[should_panic(expected = "Active threshold percentage must be greater than 0 and less than 1")] +#[should_panic( + expected = "Active threshold percentage must be greater than 0 and not greater than 1" +)] fn test_active_threshold_percentage_gt_100() { let mut app = App::default(); @@ -1195,7 +1197,9 @@ fn test_active_threshold_percentage_gt_100() { } #[test] -#[should_panic(expected = "Active threshold percentage must be greater than 0 and less than 1")] +#[should_panic( + expected = "Active threshold percentage must be greater than 0 and not greater than 1" +)] fn test_active_threshold_percentage_lte_0() { let mut app = App::default(); diff --git a/packages/dao-voting/src/threshold.rs b/packages/dao-voting/src/threshold.rs index e27e46c7b..bbb2668cb 100644 --- a/packages/dao-voting/src/threshold.rs +++ b/packages/dao-voting/src/threshold.rs @@ -28,7 +28,7 @@ pub enum ActiveThresholdError { #[error("Absolute count threshold cannot be greater than the total token supply")] InvalidAbsoluteCount {}, - #[error("Active threshold percentage must be greater than 0 and less than 1")] + #[error("Active threshold percentage must be greater than 0 and not greater than 1")] InvalidActivePercentage {}, #[error("Active threshold count must be greater than zero")] From 67af2945f30905ecc52c1b2fddc273920b66d551 Mon Sep 17 00:00:00 2001 From: Jake Hartnell Date: Sat, 16 Sep 2023 20:58:35 -0700 Subject: [PATCH 21/30] Reuse validation logic, and fix bug that slipped through audit : ) --- .../dao-voting-cw721-staked/src/contract.rs | 56 ++++++++++--------- .../dao-voting-cw721-staked/src/error.rs | 17 ++---- .../src/testing/tests.rs | 16 ++++-- 3 files changed, 46 insertions(+), 43 deletions(-) diff --git a/contracts/voting/dao-voting-cw721-staked/src/contract.rs b/contracts/voting/dao-voting-cw721-staked/src/contract.rs index c3834f4a9..22f477573 100644 --- a/contracts/voting/dao-voting-cw721-staked/src/contract.rs +++ b/contracts/voting/dao-voting-cw721-staked/src/contract.rs @@ -1,8 +1,8 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - from_binary, to_binary, Addr, Binary, CosmosMsg, Decimal, Deps, DepsMut, Empty, Env, - MessageInfo, Reply, Response, StdError, StdResult, SubMsg, Uint128, Uint256, WasmMsg, + from_binary, to_binary, Addr, Binary, CosmosMsg, Deps, DepsMut, Empty, Env, MessageInfo, Reply, + Response, StdError, StdResult, SubMsg, Uint128, Uint256, WasmMsg, }; use cw2::{get_contract_version, set_contract_version, ContractVersion}; use cw721::{Cw721QueryMsg, Cw721ReceiveMsg, NumTokensResponse}; @@ -10,7 +10,10 @@ use cw_storage_plus::Bound; use cw_utils::{parse_reply_execute_data, parse_reply_instantiate_data, Duration}; use dao_hooks::nft_stake::{stake_nft_hook_msgs, unstake_nft_hook_msgs}; use dao_interface::{nft::NftFactoryCallback, voting::IsActiveResponse}; -use dao_voting::threshold::{ActiveThreshold, ActiveThresholdResponse}; +use dao_voting::threshold::{ + assert_valid_absolute_count_threshold, assert_valid_percentage_threshold, ActiveThreshold, + ActiveThresholdResponse, +}; use crate::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, NftContract, QueryMsg}; use crate::state::{ @@ -74,24 +77,21 @@ pub fn instantiate( if let Some(active_threshold) = msg.active_threshold.as_ref() { match active_threshold { ActiveThreshold::Percentage { percent } => { - if percent > &Decimal::percent(100) || percent.is_zero() { - return Err(ContractError::InvalidActivePercentage {}); - } + assert_valid_percentage_threshold(*percent)?; } ActiveThreshold::AbsoluteCount { count } => { - // Check Absolute count is not zero - if count.is_zero() { - return Err(ContractError::ZeroActiveCount {}); - } - - // Check Absolute count is less than the supply of NFTs for existing NFT contracts + // Check Absolute count is less than the supply of NFTs for existing + // NFT contracts. For new NFT contracts, we will check this in the reply. if let NftContract::Existing { ref address } = msg.nft_contract { let nft_supply: NumTokensResponse = deps .querier .query_wasm_smart(address, &Cw721QueryMsg::NumTokens {})?; - if count > &Uint128::new(nft_supply.count.into()) { - return Err(ContractError::InvalidActiveCount {}); - } + // Check the absolute count is less than the supply of NFTs and + // greater than zero. + assert_valid_absolute_count_threshold( + *count, + Uint128::new(nft_supply.count.into()), + )?; } } } @@ -441,17 +441,20 @@ pub fn execute_update_active_threshold( return Err(ContractError::Unauthorized {}); } + let config = CONFIG.load(deps.storage)?; if let Some(active_threshold) = new_active_threshold { match active_threshold { ActiveThreshold::Percentage { percent } => { - if percent > Decimal::percent(100) || percent.is_zero() { - return Err(ContractError::InvalidActivePercentage {}); - } + assert_valid_percentage_threshold(percent)?; } ActiveThreshold::AbsoluteCount { count } => { - if count.is_zero() { - return Err(ContractError::ZeroActiveCount {}); - } + let nft_supply: NumTokensResponse = deps + .querier + .query_wasm_smart(config.nft_address, &Cw721QueryMsg::NumTokens {})?; + assert_valid_absolute_count_threshold( + count, + Uint128::new(nft_supply.count.into()), + )?; } } ACTIVE_THRESHOLD.save(deps.storage, &active_threshold)?; @@ -708,14 +711,15 @@ pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result Uint128::new(supply.count.into()) { - return Err(ContractError::InvalidActiveCount {}); - } + // Check the count is not greater than supply and is not zero + assert_valid_absolute_count_threshold( + count, + Uint128::new(nft_supply.count.into()), + )?; } Ok(Response::new()) } diff --git a/contracts/voting/dao-voting-cw721-staked/src/error.rs b/contracts/voting/dao-voting-cw721-staked/src/error.rs index 017041216..ec5af5eeb 100644 --- a/contracts/voting/dao-voting-cw721-staked/src/error.rs +++ b/contracts/voting/dao-voting-cw721-staked/src/error.rs @@ -1,5 +1,6 @@ use cosmwasm_std::{Addr, StdError}; use cw_utils::ParseReplyError; +use dao_voting::threshold::ActiveThresholdError; use thiserror::Error; #[derive(Error, Debug, PartialEq)] @@ -8,19 +9,16 @@ pub enum ContractError { Std(#[from] StdError), #[error(transparent)] - ParseReplyError(#[from] ParseReplyError), - - #[error("Can not stake that which has already been staked")] - AlreadyStaked {}, + ActiveThresholdError(#[from] ActiveThresholdError), #[error(transparent)] HookError(#[from] cw_hooks::HookError), - #[error("Active threshold count is greater than supply")] - InvalidActiveCount {}, + #[error(transparent)] + ParseReplyError(#[from] ParseReplyError), - #[error("Active threshold percentage must be greater than 0 and less than 1")] - InvalidActivePercentage {}, + #[error("Can not stake that which has already been staked")] + AlreadyStaked {}, #[error("Invalid token. Got ({received}), expected ({expected})")] InvalidToken { received: Addr, expected: Addr }, @@ -55,9 +53,6 @@ pub enum ContractError { #[error("Factory message must serialize to WasmMsg::Execute")] UnsupportedFactoryMsg {}, - #[error("Active threshold count must be greater than zero")] - ZeroActiveCount {}, - #[error("Can't unstake zero NFTs.")] ZeroUnstake {}, } diff --git a/contracts/voting/dao-voting-cw721-staked/src/testing/tests.rs b/contracts/voting/dao-voting-cw721-staked/src/testing/tests.rs index df3aeb22f..db7978a39 100644 --- a/contracts/voting/dao-voting-cw721-staked/src/testing/tests.rs +++ b/contracts/voting/dao-voting-cw721-staked/src/testing/tests.rs @@ -452,7 +452,7 @@ fn test_instantiate_zero_active_threshold_count() { } #[test] -#[should_panic(expected = "Active threshold count is greater than supply")] +#[should_panic(expected = "Absolute count threshold cannot be greater than the total token supply")] fn test_instantiate_invalid_active_threshold_count_new_nft() { let mut app = App::default(); let cw721_id = app.store_code(cw721_base_contract()); @@ -492,7 +492,7 @@ fn test_instantiate_invalid_active_threshold_count_new_nft() { } #[test] -#[should_panic(expected = "Active threshold count is greater than supply")] +#[should_panic(expected = "Absolute count threshold cannot be greater than the total token supply")] fn test_instantiate_invalid_active_threshold_count_existing_nft() { let mut app = App::default(); let module_id = app.store_code(voting_cw721_staked_contract()); @@ -805,7 +805,7 @@ fn test_update_active_threshold() { let msg = ExecuteMsg::UpdateActiveThreshold { new_threshold: Some(ActiveThreshold::AbsoluteCount { - count: Uint128::new(100), + count: Uint128::new(1), }), }; @@ -829,13 +829,15 @@ fn test_update_active_threshold() { assert_eq!( resp.active_threshold, Some(ActiveThreshold::AbsoluteCount { - count: Uint128::new(100) + count: Uint128::new(1) }) ); } #[test] -#[should_panic(expected = "Active threshold percentage must be greater than 0 and less than 1")] +#[should_panic( + expected = "Active threshold percentage must be greater than 0 and not greater than 1" +)] fn test_active_threshold_percentage_gt_100() { let mut app = App::default(); let cw721_id = app.store_code(cw721_base_contract()); @@ -875,7 +877,9 @@ fn test_active_threshold_percentage_gt_100() { } #[test] -#[should_panic(expected = "Active threshold percentage must be greater than 0 and less than 1")] +#[should_panic( + expected = "Active threshold percentage must be greater than 0 and not greater than 1" +)] fn test_active_threshold_percentage_lte_0() { let mut app = App::default(); let cw721_id = app.store_code(cw721_base_contract()); From 8625e06124a4ddb809b7c0dbab9dfdfac9a1a774 Mon Sep 17 00:00:00 2001 From: Jake Hartnell Date: Tue, 19 Sep 2023 06:50:02 -0700 Subject: [PATCH 22/30] Fix possible to instantiate new collection with zero supply of NFTs, add tests We now validate supply regardless of what ActiveThreshold is set to. --- .../dao-voting-cw721-staked/src/contract.rs | 34 +++++--- .../src/testing/tests.rs | 87 +++++++++++++++++++ 2 files changed, 108 insertions(+), 13 deletions(-) diff --git a/contracts/voting/dao-voting-cw721-staked/src/contract.rs b/contracts/voting/dao-voting-cw721-staked/src/contract.rs index 22f477573..9df7c10d7 100644 --- a/contracts/voting/dao-voting-cw721-staked/src/contract.rs +++ b/contracts/voting/dao-voting-cw721-staked/src/contract.rs @@ -26,7 +26,7 @@ pub(crate) const CONTRACT_NAME: &str = "crates.io:dao-voting-cw721-staked"; pub(crate) const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); const INSTANTIATE_NFT_CONTRACT_REPLY_ID: u64 = 0; -const VALIDATE_ABSOLUTE_COUNT_FOR_NEW_NFT_CONTRACTS: u64 = 1; +const VALIDATE_SUPPLY_REPLY_ID: u64 = 1; const FACTORY_EXECUTE_REPLY_ID: u64 = 2; // We multiply by this when calculating needed power for being active @@ -690,7 +690,7 @@ pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result Result Err(ContractError::NftInstantiateError {}), } } - VALIDATE_ABSOLUTE_COUNT_FOR_NEW_NFT_CONTRACTS => { - // Check that absolute count is not greater than supply + VALIDATE_SUPPLY_REPLY_ID => { + // Check that NFTs have actually been minted, and that supply is greater than zero // NOTE: we have to check this in a reply as it is potentially possible // to include non-mint messages in `initial_nfts`. + // + // Load config for nft contract address + let collection_addr = CONFIG.load(deps.storage)?.nft_address; + + // Query the total supply of the NFT contract + let nft_supply: NumTokensResponse = deps + .querier + .query_wasm_smart(collection_addr, &Cw721QueryMsg::NumTokens {})?; + + // Check greater than zero + if nft_supply.count == 0 { + return Err(ContractError::NoInitialNfts {}); + } + + // If Active Threshold absolute count is configured, + // check the count is not greater than supply if let Some(ActiveThreshold::AbsoluteCount { count }) = ACTIVE_THRESHOLD.may_load(deps.storage)? { - // Load config for nft contract address - let collection_addr = CONFIG.load(deps.storage)?.nft_address; - - // Query the total supply of the NFT contract - let nft_supply: NumTokensResponse = deps - .querier - .query_wasm_smart(collection_addr, &Cw721QueryMsg::NumTokens {})?; - - // Check the count is not greater than supply and is not zero assert_valid_absolute_count_threshold( count, Uint128::new(nft_supply.count.into()), )?; } + Ok(Response::new()) } FACTORY_EXECUTE_REPLY_ID => { diff --git a/contracts/voting/dao-voting-cw721-staked/src/testing/tests.rs b/contracts/voting/dao-voting-cw721-staked/src/testing/tests.rs index db7978a39..f1ff9eeee 100644 --- a/contracts/voting/dao-voting-cw721-staked/src/testing/tests.rs +++ b/contracts/voting/dao-voting-cw721-staked/src/testing/tests.rs @@ -957,6 +957,93 @@ fn test_invalid_instantiate_msg() { ); } +#[test] +fn test_invalid_initial_nft_msg() { + let mut app = App::default(); + let module_id = app.store_code(voting_cw721_staked_contract()); + let cw721_id = app.store_code(cw721_base_contract()); + + let err = app + .instantiate_contract( + module_id, + Addr::unchecked(CREATOR_ADDR), + &InstantiateMsg { + nft_contract: NftContract::New { + code_id: cw721_id, + label: "Test NFT".to_string(), + msg: to_binary(&Cw721InstantiateMsg { + name: "Test NFT".to_string(), + symbol: "TEST".to_string(), + minter: CREATOR_ADDR.to_string(), + }) + .unwrap(), + initial_nfts: vec![to_binary(&Cw721ExecuteMsg::::Extension { + msg: Empty {}, + }) + .unwrap()], + }, + unstaking_duration: None, + active_threshold: None, + }, + &[], + "cw721_voting", + None, + ) + .unwrap_err(); + assert_eq!( + err.root_cause().to_string(), + "New NFT contract must be instantiated with at least one NFT".to_string() + ); +} + +#[test] +fn test_invalid_initial_nft_msg_wrong_absolute_count() { + let mut app = App::default(); + let module_id = app.store_code(voting_cw721_staked_contract()); + let cw721_id = app.store_code(cw721_base_contract()); + + let err = app + .instantiate_contract( + module_id, + Addr::unchecked(CREATOR_ADDR), + &InstantiateMsg { + nft_contract: NftContract::New { + code_id: cw721_id, + label: "Test NFT".to_string(), + msg: to_binary(&Cw721InstantiateMsg { + name: "Test NFT".to_string(), + symbol: "TEST".to_string(), + minter: CREATOR_ADDR.to_string(), + }) + .unwrap(), + initial_nfts: vec![ + to_binary(&Cw721ExecuteMsg::::Extension { msg: Empty {} }) + .unwrap(), + to_binary(&Cw721ExecuteMsg::::Mint { + owner: CREATOR_ADDR.to_string(), + token_uri: Some("https://example.com".to_string()), + token_id: "1".to_string(), + extension: Empty {}, + }) + .unwrap(), + ], + }, + unstaking_duration: None, + active_threshold: Some(ActiveThreshold::AbsoluteCount { + count: Uint128::new(2), + }), + }, + &[], + "cw721_voting", + None, + ) + .unwrap_err(); + assert_eq!( + err.root_cause().to_string(), + "Absolute count threshold cannot be greater than the total token supply".to_string() + ); +} + #[test] fn test_no_initial_nfts_fails() { let mut app = App::default(); From f01cc597c1ea8670096832d77e548a44062badd2 Mon Sep 17 00:00:00 2001 From: Jake Hartnell Date: Tue, 19 Sep 2023 07:31:51 -0700 Subject: [PATCH 23/30] Granular allowlisting of minters --- .../cw-tokenfactory-issuer/src/contract.rs | 2 +- .../cw-tokenfactory-issuer/src/execute.rs | 17 ++++- .../tests/cases/mint.rs | 67 ++++++++++++++++++- .../src/tests/test_tube/integration_tests.rs | 2 - 4 files changed, 83 insertions(+), 5 deletions(-) diff --git a/contracts/external/cw-tokenfactory-issuer/src/contract.rs b/contracts/external/cw-tokenfactory-issuer/src/contract.rs index 8d550284e..2883087c6 100644 --- a/contracts/external/cw-tokenfactory-issuer/src/contract.rs +++ b/contracts/external/cw-tokenfactory-issuer/src/contract.rs @@ -91,7 +91,7 @@ pub fn execute( } => execute::burn(deps, env, info, amount, address), ExecuteMsg::Deny { address, status } => execute::deny(deps, env, info, address, status), ExecuteMsg::Allow { address, status } => execute::allow(deps, info, address, status), - ExecuteMsg::Freeze { status } => execute::freeze(deps, info, status), + ExecuteMsg::Freeze { status } => execute::freeze(deps, env, info, status), ExecuteMsg::ForceTransfer { amount, from_address, diff --git a/contracts/external/cw-tokenfactory-issuer/src/execute.rs b/contracts/external/cw-tokenfactory-issuer/src/execute.rs index c8ff45f4a..09a84f248 100644 --- a/contracts/external/cw-tokenfactory-issuer/src/execute.rs +++ b/contracts/external/cw-tokenfactory-issuer/src/execute.rs @@ -6,7 +6,7 @@ use osmosis_std::types::osmosis::tokenfactory::v1beta1::{ use token_bindings::TokenFactoryMsg; use crate::error::ContractError; -use crate::helpers::check_before_send_hook_features_enabled; +use crate::helpers::{check_before_send_hook_features_enabled, check_is_not_frozen}; use crate::state::{ BeforeSendHookInfo, ALLOWLIST, BEFORE_SEND_HOOK_INFO, BURNER_ALLOWANCES, DENOM, DENYLIST, IS_FROZEN, MINTER_ALLOWANCES, @@ -50,6 +50,9 @@ pub fn mint( // Get token denom from contract let denom = DENOM.load(deps.storage)?; + // Check token is not frozen, or if from or to address is on allowlist + check_is_not_frozen(deps.as_ref(), info.sender.as_str(), &to_address, &denom)?; + // Create tokenfactory MsgMint which mints coins to the contract address let mint_tokens_msg = TokenFactoryMsg::mint_contract_tokens( denom.clone(), @@ -325,9 +328,13 @@ pub fn set_minter( /// contract), or add an airdrop contract to the allowlist so users can claim /// their tokens (but not yet trade them). /// +/// This issuer contract itself is added to the allowlist when freezing, to allow +/// for minting of tokens (if minters with allowances are also on the allowlist). +/// /// Must be the contract owner to call this method. pub fn freeze( deps: DepsMut, + env: Env, info: MessageInfo, status: bool, ) -> Result, ContractError> { @@ -340,6 +347,14 @@ pub fn freeze( // NOTE: Does not check if new status is same as old status IS_FROZEN.save(deps.storage, &status)?; + // Add the issue contract itself to the Allowlist, or remove + // if unfreezing to save storage. + if status { + ALLOWLIST.save(deps.storage, &env.contract.address, &status)?; + } else { + ALLOWLIST.remove(deps.storage, &env.contract.address); + } + Ok(Response::new() .add_attribute("action", "freeze") .add_attribute("status", status.to_string())) diff --git a/contracts/external/cw-tokenfactory-issuer/tests/cases/mint.rs b/contracts/external/cw-tokenfactory-issuer/tests/cases/mint.rs index 4b68bce12..594a07f4c 100644 --- a/contracts/external/cw-tokenfactory-issuer/tests/cases/mint.rs +++ b/contracts/external/cw-tokenfactory-issuer/tests/cases/mint.rs @@ -1,6 +1,8 @@ use cosmwasm_std::Uint128; use cw_tokenfactory_issuer::{msg::AllowanceInfo, ContractError}; -use osmosis_test_tube::{osmosis_std::types::cosmos::bank::v1beta1::QueryBalanceRequest, Account}; +use osmosis_test_tube::{ + osmosis_std::types::cosmos::bank::v1beta1::QueryBalanceRequest, Account, RunnerError, +}; use crate::test_env::{ test_query_over_default_limit, test_query_within_default_limit, TestEnv, TokenfactoryIssuer, @@ -100,6 +102,69 @@ fn used_up_allowance_should_be_removed_from_storage() { ); } +#[test] +fn granular_minting_permissions_when_frozen() { + let env = TestEnv::default(); + let owner = &env.test_accs[0]; + let denom = env.cw_tokenfactory_issuer.query_denom().unwrap().denom; + + let minter = &env.test_accs[1]; + let minter_two = &env.test_accs[2]; + let mint_to = &env.test_accs[3]; + + // Owner sets before send hook to enable advanced features + env.cw_tokenfactory_issuer + .set_before_send_hook(env.cw_tokenfactory_issuer.contract_addr.clone(), owner) + .unwrap(); + + // Owner freezes contract + env.cw_tokenfactory_issuer.freeze(true, owner).unwrap(); + + // Owner grants minter a mint allowance + env.cw_tokenfactory_issuer + .set_minter(&minter.address(), 1000000, owner) + .unwrap(); + + // Owner grants minter_two a mint allowance + env.cw_tokenfactory_issuer + .set_minter(&minter_two.address(), 1000000, owner) + .unwrap(); + + // Minter can't mint when frozen + let err = env + .cw_tokenfactory_issuer + .mint(&mint_to.address(), 100, minter) + .unwrap_err(); + assert_eq!( + err, + RunnerError::ExecuteError { + msg: format!("failed to execute message; message index: 0: The contract is frozen for denom \"{}\". Addresses need to be added to the allowlist to enable transfers to or from an account.: execute wasm contract failed", denom) + } + ); + + // Own puts minter on allowlist + env.cw_tokenfactory_issuer + .allow(&minter.address(), true, owner) + .unwrap(); + + // Minter can mint + env.cw_tokenfactory_issuer + .mint(&mint_to.address(), 100, minter) + .unwrap(); + + // Minter-two can't mint because not allowed + let err = env + .cw_tokenfactory_issuer + .mint(&mint_to.address(), 100, minter_two) + .unwrap_err(); + assert_eq!( + err, + RunnerError::ExecuteError { + msg: format!("failed to execute message; message index: 0: The contract is frozen for denom \"{}\". Addresses need to be added to the allowlist to enable transfers to or from an account.: execute wasm contract failed", denom) + } + ); +} + #[test] fn mint_less_than_or_eq_allowance_should_pass_and_deduct_allowance() { let cases = vec![ 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 35ae643c8..ed1441e23 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 @@ -564,7 +564,6 @@ fn test_factory_no_callback() { let app = OsmosisTestApp::new(); let env = TestEnvBuilder::new(); let TestEnv { - tf_issuer, vp_contract, proposal_single, custom_factory, @@ -646,7 +645,6 @@ fn test_factory_wrong_callback() { let app = OsmosisTestApp::new(); let env = TestEnvBuilder::new(); let TestEnv { - tf_issuer, vp_contract, proposal_single, custom_factory, From 1763c9006ebc8905e9cfa063becc0cd53b62223d Mon Sep 17 00:00:00 2001 From: Jake Hartnell Date: Mon, 25 Sep 2023 15:45:22 -0400 Subject: [PATCH 24/30] Validate unstaking duration on instantiation and update config --- .../dao-voting-cw721-staked/src/contract.rs | 8 ++++ .../dao-voting-cw721-staked/src/error.rs | 3 ++ .../src/testing/tests.rs | 46 +++++++++++++++++++ 3 files changed, 57 insertions(+) diff --git a/contracts/voting/dao-voting-cw721-staked/src/contract.rs b/contracts/voting/dao-voting-cw721-staked/src/contract.rs index 9df7c10d7..3fd4a52f5 100644 --- a/contracts/voting/dao-voting-cw721-staked/src/contract.rs +++ b/contracts/voting/dao-voting-cw721-staked/src/contract.rs @@ -10,6 +10,7 @@ use cw_storage_plus::Bound; use cw_utils::{parse_reply_execute_data, parse_reply_instantiate_data, Duration}; use dao_hooks::nft_stake::{stake_nft_hook_msgs, unstake_nft_hook_msgs}; use dao_interface::{nft::NftFactoryCallback, voting::IsActiveResponse}; +use dao_voting::duration::validate_duration; use dao_voting::threshold::{ assert_valid_absolute_count_threshold, assert_valid_percentage_threshold, ActiveThreshold, ActiveThresholdResponse, @@ -74,6 +75,10 @@ pub fn instantiate( DAO.save(deps.storage, &info.sender)?; + // Validate unstaking duration + validate_duration(msg.unstaking_duration)?; + + // Validate active threshold if configured if let Some(active_threshold) = msg.active_threshold.as_ref() { match active_threshold { ActiveThreshold::Percentage { percent } => { @@ -376,6 +381,9 @@ pub fn execute_update_config( return Err(ContractError::Unauthorized {}); } + // Validate unstaking duration + validate_duration(duration)?; + config.unstaking_duration = duration; CONFIG.save(deps.storage, &config)?; diff --git a/contracts/voting/dao-voting-cw721-staked/src/error.rs b/contracts/voting/dao-voting-cw721-staked/src/error.rs index ec5af5eeb..287c7a509 100644 --- a/contracts/voting/dao-voting-cw721-staked/src/error.rs +++ b/contracts/voting/dao-voting-cw721-staked/src/error.rs @@ -17,6 +17,9 @@ pub enum ContractError { #[error(transparent)] ParseReplyError(#[from] ParseReplyError), + #[error(transparent)] + UnstakingDurationError(#[from] dao_voting::duration::UnstakingDurationError), + #[error("Can not stake that which has already been staked")] AlreadyStaked {}, diff --git a/contracts/voting/dao-voting-cw721-staked/src/testing/tests.rs b/contracts/voting/dao-voting-cw721-staked/src/testing/tests.rs index f1ff9eeee..761619dda 100644 --- a/contracts/voting/dao-voting-cw721-staked/src/testing/tests.rs +++ b/contracts/voting/dao-voting-cw721-staked/src/testing/tests.rs @@ -197,6 +197,13 @@ fn test_update_config() -> anyhow::Result<()> { } ); + // Update config to invalid duration fails + let err = update_config(&mut app, &module, CREATOR_ADDR, Some(Duration::Time(0))).unwrap_err(); + assert_eq!( + err.root_cause().to_string(), + "Invalid unstaking duration, unstaking duration cannot be 0".to_string() + ); + // Update duration update_config(&mut app, &module, CREATOR_ADDR, Some(Duration::Time(1)))?; @@ -411,6 +418,45 @@ fn test_add_remove_hooks() -> anyhow::Result<()> { Ok(()) } +#[test] +fn test_instantiate_with_invalid_duration_fails() { + let mut app = App::default(); + let module_id = app.store_code(voting_cw721_staked_contract()); + let cw721_id = app.store_code(cw721_base_contract()); + + let err = app + .instantiate_contract( + module_id, + Addr::unchecked(CREATOR_ADDR), + &InstantiateMsg { + nft_contract: NftContract::New { + code_id: cw721_id, + label: "Test NFT".to_string(), + msg: to_binary(&Cw721InstantiateMsg { + name: "Test NFT".to_string(), + symbol: "TEST".to_string(), + minter: CREATOR_ADDR.to_string(), + }) + .unwrap(), + initial_nfts: vec![to_binary(&Cw721ExecuteMsg::::Extension { + msg: Empty {}, + }) + .unwrap()], + }, + unstaking_duration: None, + active_threshold: None, + }, + &[], + "cw721_voting", + None, + ) + .unwrap_err(); + assert_eq!( + err.root_cause().to_string(), + "New NFT contract must be instantiated with at least one NFT".to_string() + ); +} + #[test] #[should_panic(expected = "Active threshold count must be greater than zero")] fn test_instantiate_zero_active_threshold_count() { From f13d8d4133740b93e78c762d884769faa8ffdd20 Mon Sep 17 00:00:00 2001 From: Jake Hartnell Date: Mon, 25 Sep 2023 16:19:11 -0400 Subject: [PATCH 25/30] TokenContract query should return optional type --- .../schema/dao-voting-token-staked.json | 44 ++++++++++++------- .../dao-voting-token-staked/src/contract.rs | 2 +- .../voting/dao-voting-token-staked/src/msg.rs | 5 ++- 3 files changed, 32 insertions(+), 19 deletions(-) diff --git a/contracts/voting/dao-voting-token-staked/schema/dao-voting-token-staked.json b/contracts/voting/dao-voting-token-staked/schema/dao-voting-token-staked.json index 99b6ec837..078fd00b0 100644 --- a/contracts/voting/dao-voting-token-staked/schema/dao-voting-token-staked.json +++ b/contracts/voting/dao-voting-token-staked/schema/dao-voting-token-staked.json @@ -676,6 +676,19 @@ }, "additionalProperties": false }, + { + "type": "object", + "required": [ + "token_contract" + ], + "properties": { + "token_contract": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, { "type": "object", "required": [ @@ -770,19 +783,6 @@ } }, "additionalProperties": false - }, - { - "type": "object", - "required": [ - "token_contract" - ], - "properties": { - "token_contract": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false } ] }, @@ -1140,9 +1140,21 @@ }, "token_contract": { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Addr", - "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", - "type": "string" + "title": "Nullable_Addr", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ], + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + } + } }, "total_power_at_height": { "$schema": "http://json-schema.org/draft-07/schema#", diff --git a/contracts/voting/dao-voting-token-staked/src/contract.rs b/contracts/voting/dao-voting-token-staked/src/contract.rs index 5d9eadd1f..1fb2ec411 100644 --- a/contracts/voting/dao-voting-token-staked/src/contract.rs +++ b/contracts/voting/dao-voting-token-staked/src/contract.rs @@ -402,7 +402,7 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { QueryMsg::IsActive {} => query_is_active(deps), QueryMsg::ActiveThreshold {} => query_active_threshold(deps), QueryMsg::GetHooks {} => to_binary(&query_hooks(deps)?), - QueryMsg::TokenContract {} => to_binary(&TOKEN_ISSUER_CONTRACT.load(deps.storage)?), + QueryMsg::TokenContract {} => to_binary(&TOKEN_ISSUER_CONTRACT.may_load(deps.storage)?), } } diff --git a/contracts/voting/dao-voting-token-staked/src/msg.rs b/contracts/voting/dao-voting-token-staked/src/msg.rs index 90ac64f02..52c71baa9 100644 --- a/contracts/voting/dao-voting-token-staked/src/msg.rs +++ b/contracts/voting/dao-voting-token-staked/src/msg.rs @@ -1,7 +1,7 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; use cosmwasm_std::{Binary, Uint128}; use cw_utils::Duration; -use dao_dao_macros::{active_query, token_query, voting_module_query}; +use dao_dao_macros::{active_query, voting_module_query}; use dao_interface::token::NewTokenInfo; use dao_voting::threshold::{ActiveThreshold, ActiveThresholdResponse}; @@ -59,7 +59,6 @@ pub enum ExecuteMsg { #[active_query] #[voting_module_query] -#[token_query] #[cw_serde] #[derive(QueryResponses)] pub enum QueryMsg { @@ -78,6 +77,8 @@ pub enum QueryMsg { ActiveThreshold {}, #[returns(GetHooksResponse)] GetHooks {}, + #[returns(Option)] + TokenContract {}, } #[cw_serde] From ac38d6e1fd50b80cd3ba3cece9bc43d5ea10dfa7 Mon Sep 17 00:00:00 2001 From: Jake Hartnell Date: Thu, 28 Sep 2023 16:38:47 -0400 Subject: [PATCH 26/30] ModuleInstantiateInfo callback to accept ownership of new NFT contract Ownship transfer process is two steps now, so the DAO needs to accept the ownership transfer. --- .../dao-voting-cw721-staked/src/contract.rs | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/contracts/voting/dao-voting-cw721-staked/src/contract.rs b/contracts/voting/dao-voting-cw721-staked/src/contract.rs index 3fd4a52f5..202340f52 100644 --- a/contracts/voting/dao-voting-cw721-staked/src/contract.rs +++ b/contracts/voting/dao-voting-cw721-staked/src/contract.rs @@ -9,6 +9,7 @@ use cw721::{Cw721QueryMsg, Cw721ReceiveMsg, NumTokensResponse}; use cw_storage_plus::Bound; use cw_utils::{parse_reply_execute_data, parse_reply_instantiate_data, Duration}; use dao_hooks::nft_stake::{stake_nft_hook_msgs, unstake_nft_hook_msgs}; +use dao_interface::state::ModuleInstantiateCallback; use dao_interface::{nft::NftFactoryCallback, voting::IsActiveResponse}; use dao_voting::duration::validate_duration; use dao_voting::threshold::{ @@ -719,7 +720,7 @@ pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result Result::UpdateOwnership( + cw721_base::Action::AcceptOwnership {}, + ), + )?, + funds: vec![], + })], + })?; + + Ok(Response::new().set_data(callback)) } FACTORY_EXECUTE_REPLY_ID => { // Parse reply data From 22098680b0537cd2aef1a00fc15f8493eae7e607 Mon Sep 17 00:00:00 2001 From: Jake Hartnell Date: Fri, 29 Sep 2023 00:17:51 -0400 Subject: [PATCH 27/30] Audit fixups and improvements related to factories - Extends dao-test-custom-factory to mint NFTs - Validate ActiveThreshold in NFT factory test contract - Add ModuleInstantiateCallback to nft factory call backs - Fix transfer ownership in factory test contract - Add ModuleInstantiateCallback to nft factory callbacks - Test ownership set correctly in token factory factory - Test for module instantiate callback in NFT factory - Include note that custom factory contracts MUST handle validation logic The most important change here is the that both `dao-voting-cw721-staked` and `dao-voting-token-staked` implement ModuleInstantiateCallback now, which allows for more complicated setup possibilities. --- Cargo.lock | 2 + ci/integration-tests/Cargo.toml | 1 + .../test/dao-test-custom-factory/Cargo.toml | 3 +- contracts/test/dao-test-custom-factory/README | 2 +- .../dao-test-custom-factory/src/contract.rs | 158 +++++++++++++++++- .../test/dao-test-custom-factory/src/error.rs | 3 + .../test/dao-test-custom-factory/src/msg.rs | 21 ++- .../voting/dao-voting-cw721-staked/README.md | 2 + .../dao-voting-cw721-staked/src/contract.rs | 14 +- .../src/testing/tests.rs | 16 ++ .../voting/dao-voting-token-staked/README.md | 2 + .../dao-voting-token-staked/src/contract.rs | 40 +++-- .../src/tests/test_tube/integration_tests.rs | 20 ++- packages/dao-interface/src/nft.rs | 3 + packages/dao-interface/src/token.rs | 3 + 15 files changed, 258 insertions(+), 32 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 284445c3d..ff8b2b2f2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1977,6 +1977,7 @@ dependencies = [ "cw-tokenfactory-issuer", "cw-utils 1.0.1", "cw2 1.1.0", + "cw721 0.18.0", "cw721-base 0.18.0", "dao-dao-macros", "dao-interface", @@ -2915,6 +2916,7 @@ dependencies = [ "dao-interface", "dao-pre-propose-single", "dao-proposal-single", + "dao-test-custom-factory", "dao-voting 2.2.0", "dao-voting-cw20-staked", "dao-voting-cw721-staked", diff --git a/ci/integration-tests/Cargo.toml b/ci/integration-tests/Cargo.toml index 3d9e217b8..6291ec502 100644 --- a/ci/integration-tests/Cargo.toml +++ b/ci/integration-tests/Cargo.toml @@ -25,6 +25,7 @@ dao-dao-core = { workspace = true } dao-interface = { workspace = true } dao-pre-propose-single = { workspace = true } dao-proposal-single = { workspace = true } +dao-test-custom-factory = { workspace = true } dao-voting = { workspace = true } dao-voting-cw20-staked = { workspace = true } dao-voting-cw721-staked = { workspace = true } diff --git a/contracts/test/dao-test-custom-factory/Cargo.toml b/contracts/test/dao-test-custom-factory/Cargo.toml index 8c5dff781..47ffd045e 100644 --- a/contracts/test/dao-test-custom-factory/Cargo.toml +++ b/contracts/test/dao-test-custom-factory/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "dao-test-custom-factory" authors = ["Jake Hartnell"] -description = "A test contract for testing factory patterns in dao-voting-token-staked." +description = "A test contract for testing factory patterns in dao-voting-token-staked and dao-voting-cw721-staked." edition = { workspace = true } license = { workspace = true } repository = { workspace = true } @@ -21,6 +21,7 @@ cosmwasm-std = { workspace = true } cosmwasm-schema = { workspace = true } cosmwasm-storage = { workspace = true } cw2 = { workspace = true } +cw721 = { workspace = true } cw721-base = { workspace = true, features = ["library"] } cw-ownable = { workspace = true } cw-storage-plus = { workspace = true } diff --git a/contracts/test/dao-test-custom-factory/README b/contracts/test/dao-test-custom-factory/README index 9dc8b8b94..61c1265e5 100644 --- a/contracts/test/dao-test-custom-factory/README +++ b/contracts/test/dao-test-custom-factory/README @@ -1,2 +1,2 @@ # Test Custom Factory contract -Used for testing custom factories with `dao-voting-token-staked`. +Used for testing custom factories with `dao-voting-token-staked` and `dao-voting-cw721-staked`. This also serves an example for how to build custom factory contracts for token or NFT based DAOs. diff --git a/contracts/test/dao-test-custom-factory/src/contract.rs b/contracts/test/dao-test-custom-factory/src/contract.rs index 96127718f..7091c027a 100644 --- a/contracts/test/dao-test-custom-factory/src/contract.rs +++ b/contracts/test/dao-test-custom-factory/src/contract.rs @@ -1,11 +1,13 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - to_binary, Addr, Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response, StdResult, SubMsg, - Uint128, WasmMsg, + to_binary, Addr, Binary, CosmosMsg, Deps, DepsMut, Empty, Env, MessageInfo, Reply, Response, + StdResult, SubMsg, Uint128, WasmMsg, }; use cw2::set_contract_version; +use cw721::{Cw721QueryMsg, NumTokensResponse}; use cw721_base::InstantiateMsg as Cw721InstantiateMsg; +use cw_ownable::Ownership; use cw_storage_plus::Item; use cw_tokenfactory_issuer::msg::{ ExecuteMsg as IssuerExecuteMsg, InstantiateMsg as IssuerInstantiateMsg, @@ -13,6 +15,7 @@ use cw_tokenfactory_issuer::msg::{ use cw_utils::{one_coin, parse_reply_instantiate_data}; use dao_interface::{ nft::NftFactoryCallback, + state::ModuleInstantiateCallback, token::{InitialBalance, NewTokenInfo, TokenFactoryCallback}, voting::{ActiveThresholdQuery, Query as VotingModuleQueryMsg}, }; @@ -33,6 +36,8 @@ const INSTANTIATE_ISSUER_REPLY_ID: u64 = 1; const INSTANTIATE_NFT_REPLY_ID: u64 = 2; const DAO: Item = Item::new("dao"); +const INITIAL_NFTS: Item> = Item::new("initial_nfts"); +const NFT_CONTRACT: Item = Item::new("nft_contract"); const VOTING_MODULE: Item = Item::new("voting_module"); const TOKEN_INFO: Item = Item::new("token_info"); @@ -59,11 +64,27 @@ pub fn execute( ExecuteMsg::NftFactory { code_id, cw721_instantiate_msg, - } => execute_nft_factory(deps, env, info, cw721_instantiate_msg, code_id), + initial_nfts, + } => execute_nft_factory( + deps, + env, + info, + cw721_instantiate_msg, + code_id, + initial_nfts, + ), ExecuteMsg::NftFactoryWithFunds { code_id, cw721_instantiate_msg, - } => execute_nft_factory_with_funds(deps, env, info, cw721_instantiate_msg, code_id), + initial_nfts, + } => execute_nft_factory_with_funds( + deps, + env, + info, + cw721_instantiate_msg, + code_id, + initial_nfts, + ), ExecuteMsg::NftFactoryNoCallback {} => execute_nft_factory_no_callback(deps, env, info), ExecuteMsg::NftFactoryWrongCallback {} => { execute_nft_factory_wrong_callback(deps, env, info) @@ -80,6 +101,7 @@ pub fn execute( ExecuteMsg::TokenFactoryFactoryWrongCallback {} => { execute_token_factory_factory_wrong_callback(deps, env, info) } + ExecuteMsg::ValidateNftDao {} => execute_validate_nft_dao(deps, info), } } @@ -92,7 +114,11 @@ pub fn execute_nft_factory( info: MessageInfo, cw721_instantiate_msg: Cw721InstantiateMsg, code_id: u64, + initial_nfts: Vec, ) -> Result { + // Save voting module address + VOTING_MODULE.save(deps.storage, &info.sender)?; + // Query for DAO let dao: Addr = deps .querier @@ -101,13 +127,23 @@ pub fn execute_nft_factory( // Save DAO and TOKEN_INFO for use in replies DAO.save(deps.storage, &dao)?; + // Save initial NFTs for use in replies + INITIAL_NFTS.save(deps.storage, &initial_nfts)?; + + // Override minter to be the DAO address + let msg = to_binary(&Cw721InstantiateMsg { + name: cw721_instantiate_msg.name, + symbol: cw721_instantiate_msg.symbol, + minter: dao.to_string(), + })?; + // Instantiate new contract, further setup is handled in the // SubMsg reply. let msg = SubMsg::reply_on_success( WasmMsg::Instantiate { admin: Some(dao.to_string()), code_id, - msg: to_binary(&cw721_instantiate_msg)?, + msg, funds: vec![], label: "cw_tokenfactory_issuer".to_string(), }, @@ -124,11 +160,19 @@ pub fn execute_nft_factory_with_funds( info: MessageInfo, cw721_instantiate_msg: Cw721InstantiateMsg, code_id: u64, + initial_nfts: Vec, ) -> Result { // Validate one coin was sent one_coin(&info)?; - execute_nft_factory(deps, env, info, cw721_instantiate_msg, code_id) + execute_nft_factory( + deps, + env, + info, + cw721_instantiate_msg, + code_id, + initial_nfts, + ) } /// No callback for testing @@ -149,6 +193,7 @@ pub fn execute_nft_factory_wrong_callback( Ok(Response::new().set_data(to_binary(&TokenFactoryCallback { denom: "wrong".to_string(), token_contract: None, + module_instantiate_callback: None, })?)) } @@ -221,9 +266,59 @@ pub fn execute_token_factory_factory_wrong_callback( ) -> Result { Ok(Response::new().set_data(to_binary(&NftFactoryCallback { nft_contract: "nope".to_string(), + module_instantiate_callback: None, })?)) } +/// Example method called in the ModuleInstantiateCallback providing +/// an example for checking the DAO has been setup correctly. +pub fn execute_validate_nft_dao( + deps: DepsMut, + info: MessageInfo, +) -> Result { + // Load the collection and voting module address + let collection_addr = NFT_CONTRACT.load(deps.storage)?; + let voting_module = VOTING_MODULE.load(deps.storage)?; + + // Query the collection owner and check that it's the DAO. + let owner: Ownership = deps.querier.query_wasm_smart( + collection_addr.clone(), + &cw721_base::msg::QueryMsg::::Ownership {}, + )?; + match owner.owner { + Some(owner) => { + if owner != info.sender { + return Err(ContractError::Unauthorized {}); + } + } + None => return Err(ContractError::Unauthorized {}), + } + + // Query the total supply of the NFT contract + let nft_supply: NumTokensResponse = deps + .querier + .query_wasm_smart(collection_addr.clone(), &Cw721QueryMsg::NumTokens {})?; + + // Check greater than zero + if nft_supply.count == 0 { + return Err(ContractError::NoInitialNfts {}); + } + + // Query active threshold + let active_threshold: ActiveThresholdResponse = deps + .querier + .query_wasm_smart(voting_module, &ActiveThresholdQuery::ActiveThreshold {})?; + + // If Active Threshold absolute count is configured, + // check the count is not greater than supply. + // Percentage is validated in the voting module contract. + if let Some(ActiveThreshold::AbsoluteCount { count }) = active_threshold.active_threshold { + assert_valid_absolute_count_threshold(count, Uint128::new(nft_supply.count.into()))?; + } + + Ok(Response::new()) +} + #[cfg_attr(not(feature = "library"), entry_point)] pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { match msg { @@ -321,7 +416,6 @@ pub fn reply(deps: DepsMut, env: Env, msg: Reply) -> Result Result { // Parse nft address from instantiate reply let nft_address = parse_reply_instantiate_data(msg)?.contract_address; + // Save NFT contract for use in validation reply + NFT_CONTRACT.save(deps.storage, &deps.api.addr_validate(&nft_address)?)?; + + let initial_nfts = INITIAL_NFTS.load(deps.storage)?; + + // Add mint messages that will be called by the DAO in the + // ModuleInstantiateCallback + let mut msgs: Vec = initial_nfts + .iter() + .flat_map(|nft| -> Result { + Ok(CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: nft_address.clone(), + funds: vec![], + msg: nft.clone(), + })) + }) + .collect::>(); + + // Clear space + INITIAL_NFTS.remove(deps.storage); + + // After DAO mints NFT, it calls back into the factory contract + // To validate the setup. NOTE: other patterns could be used for this + // but factory contracts SHOULD validate setups. + msgs.push(CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: env.contract.address.to_string(), + msg: to_binary(&ExecuteMsg::ValidateNftDao {})?, + funds: vec![], + })); + + // Responses for `dao-voting-cw721-staked` MUST include a + // NftFactoryCallback. Ok(Response::new().set_data(to_binary(&NftFactoryCallback { nft_contract: nft_address.to_string(), + module_instantiate_callback: Some(ModuleInstantiateCallback { msgs }), })?)) } _ => Err(ContractError::UnknownReplyId { id: msg.id }), diff --git a/contracts/test/dao-test-custom-factory/src/error.rs b/contracts/test/dao-test-custom-factory/src/error.rs index 4a29782c7..f7d753d54 100644 --- a/contracts/test/dao-test-custom-factory/src/error.rs +++ b/contracts/test/dao-test-custom-factory/src/error.rs @@ -17,6 +17,9 @@ pub enum ContractError { #[error(transparent)] PaymentError(#[from] PaymentError), + #[error("New NFT contract must be instantiated with at least one NFT")] + NoInitialNfts {}, + #[error("Unauthorized")] Unauthorized {}, diff --git a/contracts/test/dao-test-custom-factory/src/msg.rs b/contracts/test/dao-test-custom-factory/src/msg.rs index 5d9e9f8e0..42128cd2e 100644 --- a/contracts/test/dao-test-custom-factory/src/msg.rs +++ b/contracts/test/dao-test-custom-factory/src/msg.rs @@ -1,4 +1,5 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; +use cosmwasm_std::Binary; use cw721_base::InstantiateMsg as Cw721InstantiateMsg; use dao_interface::token::NewTokenInfo; @@ -7,20 +8,32 @@ pub struct InstantiateMsg {} #[cw_serde] pub enum ExecuteMsg { - TokenFactoryFactory(NewTokenInfo), - TokenFactoryFactoryWithFunds(NewTokenInfo), - TokenFactoryFactoryNoCallback {}, - TokenFactoryFactoryWrongCallback {}, + /// Example NFT factory implementation NftFactory { code_id: u64, cw721_instantiate_msg: Cw721InstantiateMsg, + initial_nfts: Vec, }, + /// Example NFT factory implentation that execpts funds NftFactoryWithFunds { code_id: u64, cw721_instantiate_msg: Cw721InstantiateMsg, + initial_nfts: Vec, }, + /// Used for testing no callback NftFactoryNoCallback {}, + /// Used for testing wrong callback NftFactoryWrongCallback {}, + /// Example Factory Implementation + TokenFactoryFactory(NewTokenInfo), + /// Example Factory Implementation that accepts funds + TokenFactoryFactoryWithFunds(NewTokenInfo), + /// Used for testing no callback + TokenFactoryFactoryNoCallback {}, + /// Used for testing wrong callback + TokenFactoryFactoryWrongCallback {}, + /// Validate NFT DAO + ValidateNftDao {}, } #[cw_serde] diff --git a/contracts/voting/dao-voting-cw721-staked/README.md b/contracts/voting/dao-voting-cw721-staked/README.md index 8ea1d05e7..595faec5f 100644 --- a/contracts/voting/dao-voting-cw721-staked/README.md +++ b/contracts/voting/dao-voting-cw721-staked/README.md @@ -13,4 +13,6 @@ To support Stargaze NFTs and other custom NFT contracts or setups with minters ( **NOTE:** when using the factory pattern, it is important to only use a trusted factory contract, as all validation happens in the factory contract. +Those implementing custom factory contracts MUST handle any validation that is to happen, and the custom `WasmMsg::Execute` message MUST include `NftFactoryCallback` data respectively. + The [dao-test-custom-factory contract](../test/dao-test-custom-factory) provides an example of how this can be done and is used for tests. It is NOT production ready, but meant to serve as an example for building factory contracts. diff --git a/contracts/voting/dao-voting-cw721-staked/src/contract.rs b/contracts/voting/dao-voting-cw721-staked/src/contract.rs index 202340f52..982d40e78 100644 --- a/contracts/voting/dao-voting-cw721-staked/src/contract.rs +++ b/contracts/voting/dao-voting-cw721-staked/src/contract.rs @@ -175,6 +175,9 @@ pub fn instantiate( }; CONFIG.save(deps.storage, &config)?; + // Call factory contract. Use only a trusted factory contract, + // as this is a critical security component and valdiation of + // setup will happen in the factory. Ok(Response::new() .add_attribute("action", "intantiate") .add_submessage(SubMsg::reply_on_success( @@ -779,7 +782,16 @@ pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result Err(ContractError::NoFactoryCallback {}), } diff --git a/contracts/voting/dao-voting-cw721-staked/src/testing/tests.rs b/contracts/voting/dao-voting-cw721-staked/src/testing/tests.rs index 761619dda..2f2ada85d 100644 --- a/contracts/voting/dao-voting-cw721-staked/src/testing/tests.rs +++ b/contracts/voting/dao-voting-cw721-staked/src/testing/tests.rs @@ -1162,6 +1162,7 @@ fn test_factory() { symbol: "TEST".to_string(), minter: CREATOR_ADDR.to_string(), }, + initial_nfts: vec![], }) .unwrap(), funds: vec![], @@ -1225,6 +1226,13 @@ fn test_factory_with_funds_pass_through() { symbol: "TEST".to_string(), minter: CREATOR_ADDR.to_string(), }, + initial_nfts: vec![to_binary(&Cw721ExecuteMsg::::Mint { + owner: CREATOR_ADDR.to_string(), + token_uri: Some("https://example.com".to_string()), + token_id: "1".to_string(), + extension: Empty {}, + }) + .unwrap()], }, ) .unwrap(), @@ -1263,6 +1271,13 @@ fn test_factory_with_funds_pass_through() { symbol: "TEST".to_string(), minter: CREATOR_ADDR.to_string(), }, + initial_nfts: vec![to_binary(&Cw721ExecuteMsg::::Mint { + owner: CREATOR_ADDR.to_string(), + token_uri: Some("https://example.com".to_string()), + token_id: "1".to_string(), + extension: Empty {}, + }) + .unwrap()], }, ) .unwrap(), @@ -1304,6 +1319,7 @@ fn test_unsupported_factory_msg() { symbol: "TEST".to_string(), minter: CREATOR_ADDR.to_string(), }, + initial_nfts: vec![], }) .unwrap(), admin: None, diff --git a/contracts/voting/dao-voting-token-staked/README.md b/contracts/voting/dao-voting-token-staked/README.md index 2f1b9d693..288a6cda8 100644 --- a/contracts/voting/dao-voting-token-staked/README.md +++ b/contracts/voting/dao-voting-token-staked/README.md @@ -87,4 +87,6 @@ The `factory` pattern takes a single `WasmMsg::Execute` message that calls into **NOTE:** when using the factory pattern, it is important to only use a trusted factory contract, as all validation happens in the factory contract. +Those implementing custom factory contracts MUST handle any validation that is to happen, and the custom `WasmMsg::Execute` message MUST include `TokenFactoryCallback` data respectively. + The [dao-test-custom-factory contract](../test/dao-test-custom-factory) provides an example of how this can be done and is used for tests. It is NOT production ready, but meant to serve as an example for building factory contracts. diff --git a/contracts/voting/dao-voting-token-staked/src/contract.rs b/contracts/voting/dao-voting-token-staked/src/contract.rs index 1fb2ec411..93bb5c8e3 100644 --- a/contracts/voting/dao-voting-token-staked/src/contract.rs +++ b/contracts/voting/dao-voting-token-staked/src/contract.rs @@ -131,17 +131,22 @@ pub fn instantiate( msg, contract_addr, funds, - } => Ok(Response::new() - .add_attribute("action", "intantiate") - .add_attribute("token", "custom_factory") - .add_submessage(SubMsg::reply_on_success( - WasmMsg::Execute { - contract_addr, - msg, - funds, - }, - FACTORY_EXECUTE_REPLY_ID, - ))), + } => { + // Call factory contract. Use only a trusted factory contract, + // as this is a critical security component and valdiation of + // setup will happen in the factory. + Ok(Response::new() + .add_attribute("action", "intantiate") + .add_attribute("token", "custom_factory") + .add_submessage(SubMsg::reply_on_success( + WasmMsg::Execute { + contract_addr, + msg, + funds, + }, + FACTORY_EXECUTE_REPLY_ID, + ))) + } _ => Err(ContractError::UnsupportedFactoryMsg {}), }, } @@ -735,9 +740,18 @@ pub fn reply(deps: DepsMut, env: Env, msg: Reply) -> Result Err(ContractError::NoFactoryCallback {}), } 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 ed1441e23..07ef96891 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,12 +1,13 @@ use cosmwasm_std::{to_binary, Addr, Coin, Decimal, Uint128, WasmMsg}; -use cw_tokenfactory_issuer::msg::DenomUnit; +use cw_ownable::Ownership; +use cw_tokenfactory_issuer::msg::{DenomUnit, QueryMsg as IssuerQueryMsg}; use cw_utils::Duration; use dao_interface::{ msg::QueryMsg as DaoQueryMsg, state::{Admin, ModuleInstantiateInfo}, token::{InitialBalance, NewDenomMetadata, NewTokenInfo}, }; -use dao_testing::test_tube::dao_dao_core::DaoCore; +use dao_testing::test_tube::{cw_tokenfactory_issuer::TokenfactoryIssuer, dao_dao_core::DaoCore}; use dao_voting::{ pre_propose::PreProposeInfo, threshold::{ActiveThreshold, ActiveThresholdError, PercentageThreshold, Threshold}, @@ -420,6 +421,15 @@ fn test_factory() { // Check the TF denom is as expected assert_eq!(denom, format!("factory/{}/{}", token_contract, DENOM)); + + // Check issuer ownership is the DAO and the ModuleInstantiateCallback + // has successfully accepted ownership. + let issuer = + TokenfactoryIssuer::new_with_values(&app, tf_issuer.code_id, token_contract.to_string()) + .unwrap(); + let ownership: Ownership = issuer.query(&IssuerQueryMsg::Ownership {}).unwrap(); + let owner = ownership.owner.unwrap(); + assert_eq!(owner, dao.contract_addr); } #[test] @@ -628,7 +638,7 @@ fn test_factory_no_callback() { initial_items: None, }; - // Instantiate DAO fails because no funds to create the token were sent + // Instantiate DAO fails because no callback let err = DaoCore::new(&app, &msg, &accounts[0], &vec![]).unwrap_err(); // Check error is no reply data @@ -709,14 +719,14 @@ fn test_factory_wrong_callback() { initial_items: None, }; - // Instantiate DAO fails because no funds to create the token were sent + // Instantiate DAO fails because of wrong callback let err = DaoCore::new(&app, &msg, &accounts[0], &vec![]).unwrap_err(); // Check error is wrong reply type assert_eq!( err, RunnerError::ExecuteError { - msg: "failed to execute message; message index: 0: dispatch: submessages: dispatch: submessages: reply: Error parsing into type dao_interface::token::TokenFactoryCallback: unknown field `nft_contract`, expected `denom` or `token_contract`: execute wasm contract failed".to_string(), + msg: "failed to execute message; message index: 0: dispatch: submessages: dispatch: submessages: reply: Error parsing into type dao_interface::token::TokenFactoryCallback: unknown field `nft_contract`, expected one of `denom`, `token_contract`, `module_instantiate_callback`: execute wasm contract failed".to_string(), } ); } diff --git a/packages/dao-interface/src/nft.rs b/packages/dao-interface/src/nft.rs index 4f7281734..82e7da52e 100644 --- a/packages/dao-interface/src/nft.rs +++ b/packages/dao-interface/src/nft.rs @@ -1,6 +1,9 @@ use cosmwasm_schema::cw_serde; +use crate::state::ModuleInstantiateCallback; + #[cw_serde] pub struct NftFactoryCallback { pub nft_contract: String, + pub module_instantiate_callback: Option, } diff --git a/packages/dao-interface/src/token.rs b/packages/dao-interface/src/token.rs index 3bd8e6875..c735a15c4 100644 --- a/packages/dao-interface/src/token.rs +++ b/packages/dao-interface/src/token.rs @@ -5,6 +5,8 @@ use cosmwasm_std::Uint128; // We re-export them here for convenience. pub use osmosis_std::types::cosmos::bank::v1beta1::{DenomUnit, Metadata}; +use crate::state::ModuleInstantiateCallback; + #[cw_serde] pub struct InitialBalance { pub amount: Uint128, @@ -46,4 +48,5 @@ pub struct NewTokenInfo { pub struct TokenFactoryCallback { pub denom: String, pub token_contract: Option, + pub module_instantiate_callback: Option, } From 017b9d4292796c9d4fdb3258694d8a113d90e39f Mon Sep 17 00:00:00 2001 From: Jake Hartnell Date: Tue, 3 Oct 2023 11:13:58 -0700 Subject: [PATCH 28/30] Use tagged cw-multi-test release --- Cargo.lock | 48 +++++++++++-------- Cargo.toml | 6 +-- .../voting/dao-voting-token-staked/Cargo.toml | 3 +- 3 files changed, 31 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ff8b2b2f2..7b89de8a8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -858,19 +858,20 @@ dependencies = [ [[package]] name = "cw-multi-test" -version = "0.16.5" -source = "git+https://github.com/CosmWasm/cw-multi-test.git?rev=d38db7752b9f054c395d6108453f8b321e4cab02#d38db7752b9f054c395d6108453f8b321e4cab02" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d818f5323c80ed4890db7f89d65eda3f0261fe21878e628c27ea2d8de4b7ba4" dependencies = [ "anyhow", "cosmwasm-std", "cw-storage-plus 1.1.0", "cw-utils 1.0.1", "derivative", - "itertools", - "k256 0.11.6", - "prost 0.9.0", + "itertools 0.11.0", + "prost 0.12.1", "schemars", "serde", + "sha2 0.10.7", "thiserror", ] @@ -2948,6 +2949,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.9" @@ -3214,7 +3224,7 @@ version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f47f0b2f22adb341bb59e5a3a1b464dde033181954bd055b9ae86d6511ba465b" dependencies = [ - "itertools", + "itertools 0.10.5", "proc-macro2", "prost-types", "quote", @@ -3425,32 +3435,32 @@ dependencies = [ [[package]] name = "prost" -version = "0.9.0" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "444879275cb4fd84958b1a1d5420d15e6fcf7c235fe47f053c9c2a80aceb6001" +checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" dependencies = [ "bytes", - "prost-derive 0.9.0", + "prost-derive 0.11.9", ] [[package]] name = "prost" -version = "0.11.9" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" +checksum = "f4fdd22f3b9c31b53c060df4a0613a1c7f062d4115a2b984dd15b1858f7e340d" dependencies = [ "bytes", - "prost-derive 0.11.9", + "prost-derive 0.12.1", ] [[package]] name = "prost-derive" -version = "0.9.0" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9cc1a3263e07e0bf68e96268f37665207b49560d98739662cdfaae215c720fe" +checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" dependencies = [ "anyhow", - "itertools", + "itertools 0.10.5", "proc-macro2", "quote", "syn 1.0.109", @@ -3458,15 +3468,15 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.11.9" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" +checksum = "265baba7fabd416cf5078179f7d2cbeca4ce7a9041111900675ea7c4cb8a4c32" dependencies = [ "anyhow", - "itertools", + "itertools 0.10.5", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.34", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index c2f03267e..d9456cd60 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,7 +41,7 @@ cosmwasm-schema = { version = "1.2" } cosmwasm-std = { version = "1.2", features = ["ibc3"] } cosmwasm-storage = { version = "1.2" } cw-controllers = "1.1" -cw-multi-test = "0.16" +cw-multi-test = "0.17" cw-storage-plus = { version = "1.1" } cw-utils = "1.0" cw2 = "1.1" @@ -126,7 +126,3 @@ cw20-staked-balance-voting-v1 = { package = "cw20-staked-balance-voting", versio cw4-voting-v1 = { package = "cw4-voting", version = "0.1.0" } voting-v1 = { package = "dao-voting", version = "0.1.0" } stake-cw20-v03 = { package = "stake-cw20", version = "0.2.6" } - -# TODO remove when new release is tagged upstream -[patch.crates-io] -cw-multi-test = { git = "https://github.com/CosmWasm/cw-multi-test.git", rev = "d38db7752b9f054c395d6108453f8b321e4cab02" } diff --git a/contracts/voting/dao-voting-token-staked/Cargo.toml b/contracts/voting/dao-voting-token-staked/Cargo.toml index 083129510..5a66b2eb3 100644 --- a/contracts/voting/dao-voting-token-staked/Cargo.toml +++ b/contracts/voting/dao-voting-token-staked/Cargo.toml @@ -41,8 +41,7 @@ cw-tokenfactory-issuer = { workspace = true, features = ["library"] } [dev-dependencies] anyhow = { workspace = true } -# TODO use upstream when new release is tagged -cw-multi-test = { git = "https://github.com/CosmWasm/cw-multi-test.git", rev = "d38db7752b9f054c395d6108453f8b321e4cab02" } +cw-multi-test = { workspace = true } cw-tokenfactory-issuer = { workspace = true } dao-proposal-single = { workspace = true } dao-proposal-hook-counter = { workspace = true } From 6f40ceb1ddd4f2c1d9dc970914e5afafbb7830db Mon Sep 17 00:00:00 2001 From: Jake Hartnell Date: Tue, 3 Oct 2023 12:32:07 -0700 Subject: [PATCH 29/30] Add integration tests for dao-voting-cw721-staked --- Cargo.lock | 5 + .../voting/dao-voting-cw721-staked/Cargo.toml | 15 +- .../src/testing/integration_tests.rs | 158 +++++++++ .../src/testing/mod.rs | 10 + .../src/testing/test_tube_env.rs | 317 ++++++++++++++++++ .../dao-testing/src/test_tube/cw721_base.rs | 128 +++++++ packages/dao-testing/src/test_tube/mod.rs | 3 + 7 files changed, 635 insertions(+), 1 deletion(-) create mode 100644 contracts/voting/dao-voting-cw721-staked/src/testing/integration_tests.rs create mode 100644 contracts/voting/dao-voting-cw721-staked/src/testing/test_tube_env.rs create mode 100644 packages/dao-testing/src/test_tube/cw721_base.rs diff --git a/Cargo.lock b/Cargo.lock index 7b89de8a8..b76510e82 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2158,9 +2158,14 @@ dependencies = [ "dao-dao-macros", "dao-hooks", "dao-interface", + "dao-proposal-hook-counter", + "dao-proposal-single", "dao-test-custom-factory", "dao-testing", "dao-voting 2.2.0", + "osmosis-std", + "osmosis-test-tube", + "serde", "thiserror", ] diff --git a/contracts/voting/dao-voting-cw721-staked/Cargo.toml b/contracts/voting/dao-voting-cw721-staked/Cargo.toml index 39d24543a..1191b5a85 100644 --- a/contracts/voting/dao-voting-cw721-staked/Cargo.toml +++ b/contracts/voting/dao-voting-cw721-staked/Cargo.toml @@ -11,8 +11,16 @@ version = { workspace = true } crate-type = ["cdylib", "rlib"] [features] +# for more explicit tests, cargo test --features=backtraces backtraces = ["cosmwasm-std/backtraces"] +# use library feature to disable all instantiate/execute/query exports library = [] +# use test tube feature to enable test-tube integration tests, for example +# cargo test --features "test-tube" +test-tube = [] +# when writing tests you may wish to enable test-tube as a default feature +# default = ["test-tube"] + [dependencies] cosmwasm-std = { workspace = true } @@ -35,5 +43,10 @@ thiserror = { workspace = true } [dev-dependencies] anyhow = { workspace = true } cw-multi-test = { workspace = true } -dao-testing = { workspace = true } +dao-proposal-single = { workspace = true } +dao-proposal-hook-counter = { workspace = true } dao-test-custom-factory = { workspace = true } +dao-testing = { workspace = true, features = ["test-tube"] } +osmosis-std = { workpsace = true } +osmosis-test-tube = { workspace = true } +serde = { workspace = true } diff --git a/contracts/voting/dao-voting-cw721-staked/src/testing/integration_tests.rs b/contracts/voting/dao-voting-cw721-staked/src/testing/integration_tests.rs new file mode 100644 index 000000000..d87edace7 --- /dev/null +++ b/contracts/voting/dao-voting-cw721-staked/src/testing/integration_tests.rs @@ -0,0 +1,158 @@ +use cosmwasm_std::{to_binary, Addr, Coin, Decimal, Empty, Uint128, WasmMsg}; +use cw721_base::{ + msg::{ + ExecuteMsg as Cw721ExecuteMsg, InstantiateMsg as Cw721InstantiateMsg, + QueryMsg as Cw721QueryMsg, + }, + MinterResponse, +}; +use cw_utils::Duration; +use dao_interface::{ + msg::QueryMsg as DaoQueryMsg, + state::{Admin, ModuleInstantiateInfo}, +}; +use dao_testing::test_tube::{cw721_base::Cw721Base, dao_dao_core::DaoCore}; +use dao_voting::{ + pre_propose::PreProposeInfo, + threshold::{ActiveThreshold, PercentageThreshold, Threshold}, +}; +use osmosis_test_tube::{Account, OsmosisTestApp, RunnerError}; + +use crate::{ + msg::{InstantiateMsg, NftContract, QueryMsg}, + state::Config, + testing::test_tube_env::Cw721VotingContract, +}; + +use super::test_tube_env::{TestEnv, TestEnvBuilder}; + +#[test] +fn test_full_integration_with_factory() { + let app = OsmosisTestApp::new(); + let env = TestEnvBuilder::new(); + + // Setup defaults to creating a NFT DAO with the factory contract + // This does not use funds when instantiating the NFT contract. + // We will test that below. + let TestEnv { + vp_contract, + proposal_single, + custom_factory, + accounts, + cw721, + .. + } = env.setup(&app); + + // Test instantiating a DAO with a factory contract that requires funds + 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 { + nft_contract: NftContract::Factory( + to_binary(&WasmMsg::Execute { + contract_addr: custom_factory.contract_addr.clone(), + msg: to_binary( + &dao_test_custom_factory::msg::ExecuteMsg::NftFactoryWithFunds { + code_id: cw721.code_id, + cw721_instantiate_msg: Cw721InstantiateMsg { + name: "Test NFT".to_string(), + symbol: "TEST".to_string(), + minter: accounts[0].address(), + }, + initial_nfts: vec![to_binary( + &Cw721ExecuteMsg::::Mint { + owner: accounts[0].address(), + token_uri: Some("https://example.com".to_string()), + token_id: "1".to_string(), + extension: Empty {}, + }, + ) + .unwrap()], + }, + ) + .unwrap(), + funds: vec![Coin { + amount: Uint128::new(1000), + denom: "uosmo".to_string(), + }], + }) + .unwrap(), + ), + unstaking_duration: None, + active_threshold: Some(ActiveThreshold::Percentage { + percent: Decimal::percent(1), + }), + }) + .unwrap(), + admin: Some(Admin::CoreModule {}), + funds: vec![Coin { + amount: Uint128::new(1000), + denom: "uosmo".to_string(), + }], + label: "DAO DAO Voting Module".to_string(), + }, + proposal_modules_instantiate_info: vec![ModuleInstantiateInfo { + code_id: proposal_single.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, + }; + + // Instantiating without funds fails + let err = DaoCore::new(&app, &msg, &accounts[0], &[]).unwrap_err(); + + // Error is insufficient funds as no funds were sent + assert_eq!( + RunnerError::ExecuteError { + msg: "failed to execute message; message index: 0: dispatch: submessages: 0uosmo is smaller than 1000uosmo: insufficient funds".to_string() + }, + err + ); + + // Instantiate DAO succeeds with funds + let dao = DaoCore::new( + &app, + &msg, + &accounts[0], + &[Coin { + amount: Uint128::new(1000), + denom: "uosmo".to_string(), + }], + ) + .unwrap(); + + let vp_addr: Addr = dao.query(&DaoQueryMsg::VotingModule {}).unwrap(); + let vp_contract = + Cw721VotingContract::new_with_values(&app, vp_contract.code_id, vp_addr.to_string()) + .unwrap(); + + let vp_config: Config = vp_contract.query(&QueryMsg::Config {}).unwrap(); + let cw721_contract = + Cw721Base::new_with_values(&app, cw721.code_id, vp_config.nft_address.to_string()).unwrap(); + + // Check DAO was initialized to minter + let minter: MinterResponse = cw721_contract.query(&Cw721QueryMsg::Minter {}).unwrap(); + assert_eq!(minter.minter, Some(dao.contract_addr.to_string())); +} diff --git a/contracts/voting/dao-voting-cw721-staked/src/testing/mod.rs b/contracts/voting/dao-voting-cw721-staked/src/testing/mod.rs index ea43bc797..de0824f52 100644 --- a/contracts/voting/dao-voting-cw721-staked/src/testing/mod.rs +++ b/contracts/voting/dao-voting-cw721-staked/src/testing/mod.rs @@ -5,6 +5,16 @@ mod instantiate; mod queries; mod tests; +// Integrationg tests using an actual chain binary, requires +// the "test-tube" feature to be enabled +// cargo test --features test-tube +#[cfg(test)] +#[cfg(feature = "test-tube")] +mod integration_tests; +#[cfg(test)] +#[cfg(feature = "test-tube")] +mod test_tube_env; + use cosmwasm_std::Addr; use cw_multi_test::{App, Executor}; use cw_utils::Duration; diff --git a/contracts/voting/dao-voting-cw721-staked/src/testing/test_tube_env.rs b/contracts/voting/dao-voting-cw721-staked/src/testing/test_tube_env.rs new file mode 100644 index 000000000..cea9d24a1 --- /dev/null +++ b/contracts/voting/dao-voting-cw721-staked/src/testing/test_tube_env.rs @@ -0,0 +1,317 @@ +// The code is used in tests but reported as dead code +// see https://github.com/rust-lang/rust/issues/46379 +#![allow(dead_code)] + +use crate::{ + msg::{ExecuteMsg, InstantiateMsg, NftContract, QueryMsg}, + state::Config, +}; + +use cosmwasm_std::{to_binary, Addr, Coin, Decimal, Empty, WasmMsg}; +use cw_utils::Duration; +use dao_interface::{ + msg::QueryMsg as DaoQueryMsg, + state::{Admin, ModuleInstantiateInfo, ProposalModule}, +}; +use dao_voting::{ + pre_propose::PreProposeInfo, threshold::PercentageThreshold, threshold::Threshold, +}; + +use cw721_base::msg::{ExecuteMsg as Cw721ExecuteMsg, InstantiateMsg as Cw721InstantiateMsg}; +use dao_testing::test_tube::{ + cw721_base::Cw721Base, dao_dao_core::DaoCore, dao_proposal_single::DaoProposalSingle, + dao_test_custom_factory::CustomFactoryContract, +}; +use dao_voting::threshold::ActiveThreshold; +use osmosis_std::types::cosmwasm::wasm::v1::MsgExecuteContractResponse; +use osmosis_test_tube::{ + Account, Bank, Module, OsmosisTestApp, RunnerError, RunnerExecuteResult, RunnerResult, + SigningAccount, Wasm, +}; +use serde::de::DeserializeOwned; +use std::path::PathBuf; + +pub const DENOM: &str = "ucat"; +pub const JUNO: &str = "ujuno"; + +pub struct TestEnv<'a> { + pub app: &'a OsmosisTestApp, + pub dao: DaoCore<'a>, + pub proposal_single: DaoProposalSingle<'a>, + pub custom_factory: CustomFactoryContract<'a>, + pub vp_contract: Cw721VotingContract<'a>, + pub accounts: Vec, + pub cw721: Cw721Base<'a>, +} + +impl<'a> TestEnv<'a> { + pub fn bank(&self) -> Bank<'_, OsmosisTestApp> { + Bank::new(self.app) + } +} + +pub struct TestEnvBuilder {} + +impl TestEnvBuilder { + pub fn new() -> Self { + Self {} + } + + // Full DAO setup + pub fn setup(self, app: &'_ OsmosisTestApp) -> TestEnv<'_> { + let accounts = app + .init_accounts(&[Coin::new(1000000000000000u128, "uosmo")], 10) + .unwrap(); + + // Upload all needed code ids + let vp_contract_id = Cw721VotingContract::upload(app, &accounts[0]).unwrap(); + let proposal_single_id = DaoProposalSingle::upload(app, &accounts[0]).unwrap(); + let cw721_id = Cw721Base::upload(app, &accounts[0]).unwrap(); + + // Instantiate Custom Factory + let custom_factory = CustomFactoryContract::new( + app, + &dao_test_custom_factory::msg::InstantiateMsg {}, + &accounts[0], + ) + .unwrap(); + + 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_id, + msg: to_binary(&InstantiateMsg { + nft_contract: NftContract::Factory( + to_binary(&WasmMsg::Execute { + contract_addr: custom_factory.contract_addr.clone(), + msg: to_binary(&dao_test_custom_factory::msg::ExecuteMsg::NftFactory { + code_id: cw721_id, + cw721_instantiate_msg: Cw721InstantiateMsg { + name: "Test NFT".to_string(), + symbol: "TEST".to_string(), + minter: accounts[0].address(), + }, + initial_nfts: vec![to_binary( + &Cw721ExecuteMsg::::Mint { + owner: accounts[0].address(), + token_uri: Some("https://example.com".to_string()), + token_id: "1".to_string(), + extension: Empty {}, + }, + ) + .unwrap()], + }) + .unwrap(), + funds: vec![], + }) + .unwrap(), + ), + unstaking_duration: None, + active_threshold: Some(ActiveThreshold::Percentage { + percent: Decimal::percent(1), + }), + }) + .unwrap(), + admin: Some(Admin::CoreModule {}), + funds: vec![], + label: "DAO DAO Voting Module".to_string(), + }, + proposal_modules_instantiate_info: vec![ModuleInstantiateInfo { + code_id: proposal_single_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(); + + // Get voting module address, setup vp_contract helper + let vp_addr: Addr = dao.query(&DaoQueryMsg::VotingModule {}).unwrap(); + let vp_contract = + Cw721VotingContract::new_with_values(app, vp_contract_id, vp_addr.to_string()).unwrap(); + + let vp_config: Config = vp_contract.query(&QueryMsg::Config {}).unwrap(); + + let cw721 = + Cw721Base::new_with_values(app, cw721_id, vp_config.nft_address.to_string()).unwrap(); + + // Get proposal module address, setup proposal_single helper + let proposal_modules: Vec = dao + .query(&DaoQueryMsg::ProposalModules { + limit: None, + start_after: None, + }) + .unwrap(); + let proposal_single = DaoProposalSingle::new_with_values( + app, + proposal_single_id, + proposal_modules[0].address.to_string(), + ) + .unwrap(); + + TestEnv { + app, + dao, + vp_contract, + proposal_single, + custom_factory, + accounts, + cw721, + } + } +} + +#[derive(Debug)] +pub struct Cw721VotingContract<'a> { + pub app: &'a OsmosisTestApp, + pub contract_addr: String, + pub code_id: u64, +} + +impl<'a> Cw721VotingContract<'a> { + pub fn deploy( + app: &'a OsmosisTestApp, + instantiate_msg: &InstantiateMsg, + signer: &SigningAccount, + ) -> Result { + let wasm = Wasm::new(app); + + 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, + &[], + 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) + } + + pub fn instantiate( + app: &'a OsmosisTestApp, + code_id: u64, + instantiate_msg: &InstantiateMsg, + signer: &SigningAccount, + ) -> Result { + let wasm = Wasm::new(app); + let contract_addr = wasm + .instantiate( + code_id, + &instantiate_msg, + Some(&signer.address()), + None, + &[], + signer, + )? + .data + .address; + + Ok(Self { + app, + code_id, + contract_addr, + }) + } + + pub fn execute( + &self, + msg: &ExecuteMsg, + funds: &[Coin], + signer: &SigningAccount, + ) -> RunnerExecuteResult { + let wasm = Wasm::new(self.app); + wasm.execute(&self.contract_addr, msg, funds, signer) + } + + pub fn query(&self, msg: &QueryMsg) -> RunnerResult + where + T: ?Sized + DeserializeOwned, + { + let wasm = Wasm::new(self.app); + wasm.query(&self.contract_addr, 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("..") + .join("artifacts") + .join("dao_voting_cw721_staked.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("..") + .join("artifacts") + .join("dao_voting_cw721_staked-aarch64.wasm"), + ) + .unwrap(), + } + } +} diff --git a/packages/dao-testing/src/test_tube/cw721_base.rs b/packages/dao-testing/src/test_tube/cw721_base.rs new file mode 100644 index 000000000..84c80b8fe --- /dev/null +++ b/packages/dao-testing/src/test_tube/cw721_base.rs @@ -0,0 +1,128 @@ +use cosmwasm_std::{Coin, Empty}; +use cw721_base::{ + 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 Cw721Base<'a> { + pub app: &'a OsmosisTestApp, + pub code_id: u64, + pub contract_addr: String, +} + +impl<'a> Cw721Base<'a> { + pub fn new( + app: &'a OsmosisTestApp, + instantiate_msg: &InstantiateMsg, + signer: &SigningAccount, + ) -> Result { + let wasm = Wasm::new(app); + + 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, + &[], + 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("cw721_base.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("cw721_base-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 d24469e60..0af2999a4 100644 --- a/packages/dao-testing/src/test_tube/mod.rs +++ b/packages/dao-testing/src/test_tube/mod.rs @@ -8,6 +8,9 @@ #[cfg(feature = "test-tube")] pub mod cw_tokenfactory_issuer; +#[cfg(feature = "test-tube")] +pub mod cw721_base; + #[cfg(feature = "test-tube")] pub mod dao_dao_core; From ac1667cc1582ec5d391a8b2cfa5e88dee97758bf Mon Sep 17 00:00:00 2001 From: Jake Hartnell Date: Tue, 3 Oct 2023 17:17:26 -0700 Subject: [PATCH 30/30] Split up test tube and integration tests --- .github/workflows/integration_tests.yml | 13 ----- .github/workflows/test_tube.yml | 68 +++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 13 deletions(-) create mode 100644 .github/workflows/test_tube.yml diff --git a/.github/workflows/integration_tests.yml b/.github/workflows/integration_tests.yml index fe568df6f..fca7b1d82 100644 --- a/.github/workflows/integration_tests.yml +++ b/.github/workflows/integration_tests.yml @@ -25,16 +25,6 @@ jobs: target: wasm32-unknown-unknown override: true - - name: Clone libwasmv (needed for test-tube) - uses: actions/checkout@v2 - with: - repository: CosmWasm/wasmvm - path: ./wasmvm - ref: v1.3.0 - - - name: Install libwasmv - run: cd ./wasmvm && make build-rust && cd ../ - - name: Rust Dependencies Cache uses: actions/cache@v3 with: @@ -70,9 +60,6 @@ jobs: - name: Run Integration Tests run: just integration-test - - name: Run Test Tube Integration Tests - run: just test-tube - - name: Combine Test Gas Reports run: cd ci/integration-tests/ && jq -rs 'reduce .[] as $item ({}; . * $item)' gas_reports/*.json > gas_report.json diff --git a/.github/workflows/test_tube.yml b/.github/workflows/test_tube.yml new file mode 100644 index 000000000..ca15a4726 --- /dev/null +++ b/.github/workflows/test_tube.yml @@ -0,0 +1,68 @@ +name: Test Tube Tests + +on: + pull_request: + push: + branches: + - main + +jobs: + test: + name: Integration tests + runs-on: ubuntu-latest + env: + GAS_OUT_DIR: gas_reports + GAS_LIMIT: 100000000 + steps: + - name: Checkout sources + uses: actions/checkout@v3 + + - name: Install latest nightly toolchain + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: nightly-2023-02-02 + target: wasm32-unknown-unknown + override: true + + - name: Clone libwasmv (needed for test-tube) + uses: actions/checkout@v2 + with: + repository: CosmWasm/wasmvm + path: ./wasmvm + ref: v1.3.0 + + - name: Install libwasmv + run: cd ./wasmvm && make build-rust && cd ../ + + - name: Rust Dependencies Cache + uses: actions/cache@v3 + with: + path: | + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + artifacts/ + key: ${{ runner.os }}-cargo-with-artifacts-${{ hashFiles('**/Cargo.lock') }} + + - name: Set latest just version + run: echo "JUST_VERSION=$(cargo search just -q | sed -n -e '/^just[[:space:]]/p' | cut -d '"' -f 2)" >> $GITHUB_ENV + + - name: Get cached just + uses: actions/cache@v3 + with: + path: ~/.cargo/bin/just + key: ${{ runner.os }}-just-${{ env.JUST_VERSION }} + + - name: Install just + run: cargo install just || true + + - name: Run download deps + run: just download-deps + + - name: Run workspace optimize + run: just workspace-optimize + + - name: Run Test Tube Integration Tests + run: just test-tube