diff --git a/checksum b/checksum index 2ec67374..2ef21e65 100644 --- a/checksum +++ b/checksum @@ -1,2 +1,2 @@ -fc76a0e370c9e947d6e33d018585137d566f32d1a1fb06b1b49e695e5984a57e cw_croncat.wasm -348ce203ce7a18c2e28f001139c1f7a215f7a569c735825dc819dc79692aaffb cw_rules.wasm +2d2b8d5bb188d7a29b24a00c5fcb87c1e2e4f7c53c0f4bf176e2dc2024bd2cc6 cw_croncat.wasm +307b8919d8fcae59dd7336319c6220fe5c477488b59bf043158fba67a292234d cw_rules.wasm diff --git a/contracts/cw-croncat/src/balancer.rs b/contracts/cw-croncat/src/balancer.rs index 87644e4d..970dcf39 100644 --- a/contracts/cw-croncat/src/balancer.rs +++ b/contracts/cw-croncat/src/balancer.rs @@ -44,9 +44,6 @@ pub struct RoundRobinBalancer { } impl RoundRobinBalancer { - pub fn default() -> RoundRobinBalancer { - RoundRobinBalancer::new(BalancerMode::ActivationOrder) - } pub fn new(mode: BalancerMode) -> RoundRobinBalancer { RoundRobinBalancer { mode } } @@ -292,3 +289,9 @@ impl<'a> Balancer<'a> for RoundRobinBalancer { Ok(()) } } + +impl Default for RoundRobinBalancer { + fn default() -> RoundRobinBalancer { + RoundRobinBalancer::new(BalancerMode::ActivationOrder) + } +} diff --git a/contracts/cw-croncat/src/helpers.rs b/contracts/cw-croncat/src/helpers.rs index 3d963207..e39dadaf 100644 --- a/contracts/cw-croncat/src/helpers.rs +++ b/contracts/cw-croncat/src/helpers.rs @@ -177,11 +177,7 @@ impl<'a> CwCroncat<'a> { // It's possible there are more "covered tasks" than total tasks, // so use saturating subtraction to hit zero and not go below let total_tasks_needing_agents = total_tasks.saturating_sub(num_tasks_covered); - let remainder = if total_tasks_needing_agents % max_tasks == 0 { - 0 - } else { - 1 - }; + let remainder = u64::from(total_tasks_needing_agents % max_tasks != 0); total_tasks_needing_agents / max_tasks + remainder } else { 0 diff --git a/contracts/cw-croncat/src/owner.rs b/contracts/cw-croncat/src/owner.rs index 5c5d28a5..4a77b751 100644 --- a/contracts/cw-croncat/src/owner.rs +++ b/contracts/cw-croncat/src/owner.rs @@ -207,7 +207,7 @@ impl<'a> CwCroncat<'a> { // Querier guarantees to returns up-to-date data, including funds sent in this handle message // https://github.com/CosmWasm/wasmd/blob/master/x/wasm/internal/keeper/keeper.go#L185-L192 - let state_balances = deps.querier.query_all_balances(&env.contract.address)?; + let state_balances = deps.querier.query_all_balances(env.contract.address)?; let mut has_fund_err = false; let messages: Result, ContractError> = balances diff --git a/contracts/cw-rules/src/contract.rs b/contracts/cw-rules/src/contract.rs index 19dcc84d..fb7fb215 100644 --- a/contracts/cw-rules/src/contract.rs +++ b/contracts/cw-rules/src/contract.rs @@ -1,5 +1,7 @@ use cw_rules_core::msg::{QueryConstruct, QueryConstructResponse}; -use cw_rules_core::types::{CheckOwnerOfNft, CheckProposalStatus, CroncatQuery, HasBalanceGte}; +use cw_rules_core::types::{ + CheckOwnerOfNft, CheckPassedProposals, CheckProposalStatus, CroncatQuery, HasBalanceGte, +}; // use schemars::JsonSchema; // use serde::{Deserialize, Serialize}; @@ -21,7 +23,7 @@ use cw_rules_core::msg::{ExecuteMsg, InstantiateMsg, QueryMsg, QueryResponse}; //use cosmwasm_std::from_binary; //use crate::msg::QueryMultiResponse; -use crate::types::dao::{ProposalResponse, QueryDao, Status}; +use crate::types::dao::{ProposalListResponse, ProposalResponse, QueryDao, Status}; use generic_query::GenericQuery; // version info for migration info @@ -87,6 +89,9 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { proposal_id, status, )?), + QueryMsg::CheckPassedProposals(CheckPassedProposals { dao_address }) => { + to_binary(&query_dao_proposals(deps, dao_address)?) + } QueryMsg::GenericQuery(query) => to_binary(&generic_query(deps, query)?), QueryMsg::SmartQuery(query) => to_binary(&smart_query(deps, query)?), QueryMsg::QueryConstruct(QueryConstruct { queries }) => { @@ -203,6 +208,36 @@ fn query_dao_proposal_status( }) } +// Check for passed proposals +// Return the first passed proposal +fn query_dao_proposals(deps: Deps, dao_address: String) -> StdResult { + let dao_addr = deps.api.addr_validate(&dao_address)?; + // Query the amount of proposals + let proposal_count = deps + .querier + .query_wasm_smart(dao_addr.clone(), &QueryDao::ProposalCount {})?; + let res: ProposalListResponse = deps.querier.query_wasm_smart( + dao_addr, + &QueryDao::ListProposals { + start_after: None, + limit: Some(proposal_count), + }, + )?; + + for proposal_response in &res.proposals { + if proposal_response.proposal.status == Status::Passed { + return Ok(QueryResponse { + result: true, + data: to_binary(&proposal_response.id)?, + }); + } + } + Ok(QueryResponse { + result: false, + data: to_binary(&res.proposals)?, + }) +} + // // // GOAL: // // // Parse a generic query response, and inject input for the next query // // fn query_chain(deps: Deps, env: Env) -> StdResult { @@ -256,6 +291,9 @@ fn query_construct(deps: Deps, queries: Vec) -> StdResult query_dao_proposal_status(deps, dao_address, proposal_id, status), + CroncatQuery::CheckPassedProposals(CheckPassedProposals { dao_address }) => { + query_dao_proposals(deps, dao_address) + } CroncatQuery::GenericQuery(query) => generic_query(deps, query), CroncatQuery::SmartQuery(query) => smart_query(deps, query), }?; diff --git a/contracts/cw-rules/src/tests/daodao.rs b/contracts/cw-rules/src/tests/daodao.rs index 89b261fa..f7bb78e1 100644 --- a/contracts/cw-rules/src/tests/daodao.rs +++ b/contracts/cw-rules/src/tests/daodao.rs @@ -1,7 +1,7 @@ use cosmwasm_std::{to_binary, Addr, Binary, Uint128}; use cw20::Cw20Coin; use cw_multi_test::{next_block, App, Executor}; -use cw_rules_core::types::{CheckProposalStatus, Status}; +use cw_rules_core::types::{CheckPassedProposals, CheckProposalStatus, Status}; use cwd_core::state::ProposalModule; use cwd_proposal_multiple::proposal::MultipleChoiceProposal; use cwd_proposal_single::proposal::SingleChoiceProposal; @@ -665,3 +665,386 @@ fn test_dao_multiple_proposal_ready() { } ); } + +#[test] +fn test_single_check_passed_proposals() { + let mut app = App::default(); + let code_id = app.store_code(cw_rules_contract()); + + let instantiate = InstantiateMsg {}; + let contract_addr = app + .instantiate_contract( + code_id, + Addr::unchecked(CREATOR_ADDR), + &instantiate, + &[], + "cw-rules", + None, + ) + .unwrap(); + + let proposal_module_code_id = app.store_code(single_proposal_contract()); + let threshold = Threshold::AbsolutePercentage { + percentage: PercentageThreshold::Majority {}, + }; + let max_voting_period = cw_utils::Duration::Height(6); + let instantiate_govmod = cwd_proposal_single::msg::InstantiateMsg { + threshold, + max_voting_period, + min_voting_period: None, + only_members_execute: false, + allow_revoting: false, + close_proposal_on_execution_failure: true, + pre_propose_info: cwd_voting::pre_propose::PreProposeInfo::AnyoneMayPropose {}, + }; + let governance_addr = instantiate_with_staking_active_threshold( + &mut app, + proposal_module_code_id, + to_binary(&instantiate_govmod).unwrap(), + None, + None, + ); + let governance_modules: Vec = app + .wrap() + .query_wasm_smart( + governance_addr, + &cwd_core::msg::QueryMsg::ProposalModules { + start_after: None, + limit: None, + }, + ) + .unwrap(); + + assert_eq!(governance_modules.len(), 1); + let govmod_single = governance_modules.into_iter().next().unwrap().address; + + let govmod_config: cwd_proposal_single::state::Config = app + .wrap() + .query_wasm_smart( + govmod_single.clone(), + &cwd_proposal_single::msg::QueryMsg::Config {}, + ) + .unwrap(); + let dao = govmod_config.dao; + let voting_module: Addr = app + .wrap() + .query_wasm_smart(dao, &cwd_core::msg::QueryMsg::VotingModule {}) + .unwrap(); + let staking_contract: Addr = app + .wrap() + .query_wasm_smart( + voting_module.clone(), + &cwd_voting_cw20_staked::msg::QueryMsg::StakingContract {}, + ) + .unwrap(); + let token_contract: Addr = app + .wrap() + .query_wasm_smart( + voting_module, + &cwd_interface::voting::Query::TokenContract {}, + ) + .unwrap(); + + // Stake some tokens so we can propose + let msg = cw20::Cw20ExecuteMsg::Send { + contract: staking_contract.to_string(), + amount: Uint128::new(2000), + msg: to_binary(&cw20_stake::msg::ReceiveMsg::Stake {}).unwrap(), + }; + app.execute_contract( + Addr::unchecked(CREATOR_ADDR), + token_contract.clone(), + &msg, + &[], + ) + .unwrap(); + app.update_block(next_block); + + // Create 100 new proposals + for num in 1..101 { + let mut title = "Cron".to_string(); + title.push(num.into()); + let mut description = "Cat".to_string(); + description.push(num.into()); + app.execute_contract( + Addr::unchecked(CREATOR_ADDR), + govmod_single.clone(), + &cwd_proposal_single::msg::ExecuteMsg::Propose { + title, + description, + msgs: vec![], + proposer: None, + }, + &[], + ) + .unwrap(); + } + + // Neither proposal has passed + let res: QueryResponse = app + .wrap() + .query_wasm_smart( + contract_addr.clone(), + &QueryMsg::CheckPassedProposals(CheckPassedProposals { + dao_address: govmod_single.to_string(), + }), + ) + .unwrap(); + assert!(!res.result); + + // Approve even proposals + for num in 1..51 { + app.execute_contract( + Addr::unchecked(CREATOR_ADDR), + govmod_single.clone(), + &cwd_proposal_single::msg::ExecuteMsg::Vote { + proposal_id: 2 * num as u64, + vote: Vote::Yes, + }, + &[], + ) + .unwrap(); + } + + // Query passed proposals and execute them + for num in 1..51 { + let index: u64 = 2 * num; + + // Check that CheckPassedProposals returns index as the number of the first passed proposal + let res: QueryResponse = app + .wrap() + .query_wasm_smart( + contract_addr.clone(), + &QueryMsg::CheckPassedProposals(CheckPassedProposals { + dao_address: govmod_single.to_string(), + }), + ) + .unwrap(); + assert_eq!( + res, + QueryResponse { + result: true, + data: to_binary(&index).unwrap() + } + ); + + // Execute the proposal + app.execute_contract( + Addr::unchecked(CREATOR_ADDR), + govmod_single.clone(), + &cwd_proposal_single::msg::ExecuteMsg::Execute { proposal_id: index }, + &[], + ) + .unwrap(); + } + + // All passed proposals are executed + // There shouldn't be any passed proposals + let res: QueryResponse = app + .wrap() + .query_wasm_smart( + contract_addr.clone(), + &QueryMsg::CheckPassedProposals(CheckPassedProposals { + dao_address: govmod_single.to_string(), + }), + ) + .unwrap(); + assert!(!res.result,); +} + +#[test] +fn test_multiple_check_passed_proposals() { + let mut app = App::default(); + let code_id = app.store_code(cw_rules_contract()); + let instantiate = InstantiateMsg {}; + let contract_addr = app + .instantiate_contract( + code_id, + Addr::unchecked(CREATOR_ADDR), + &instantiate, + &[], + "cw-rules", + None, + ) + .unwrap(); + + let proposal_module_code_id = app.store_code(multiple_proposal_contract()); + let voting_strategy = VotingStrategy::SingleChoice { + quorum: PercentageThreshold::Majority {}, + }; + let max_voting_period = cw_utils::Duration::Height(6); + let instantiate_govmod = cwd_proposal_multiple::msg::InstantiateMsg { + voting_strategy, + max_voting_period, + min_voting_period: None, + only_members_execute: false, + allow_revoting: false, + close_proposal_on_execution_failure: true, + pre_propose_info: cwd_voting::pre_propose::PreProposeInfo::AnyoneMayPropose {}, + }; + let governance_addr = instantiate_with_staking_active_threshold( + &mut app, + proposal_module_code_id, + to_binary(&instantiate_govmod).unwrap(), + None, + None, + ); + let governance_modules: Vec = app + .wrap() + .query_wasm_smart( + governance_addr, + &cwd_core::msg::QueryMsg::ProposalModules { + start_after: None, + limit: None, + }, + ) + .unwrap(); + + assert_eq!(governance_modules.len(), 1); + let govmod_single = governance_modules.into_iter().next().unwrap().address; + + let govmod_config: cwd_proposal_multiple::state::Config = app + .wrap() + .query_wasm_smart( + govmod_single.clone(), + &cwd_proposal_multiple::msg::QueryMsg::Config {}, + ) + .unwrap(); + let dao = govmod_config.dao; + let voting_module: Addr = app + .wrap() + .query_wasm_smart(dao, &cwd_core::msg::QueryMsg::VotingModule {}) + .unwrap(); + let staking_contract: Addr = app + .wrap() + .query_wasm_smart( + voting_module.clone(), + &cwd_voting_cw20_staked::msg::QueryMsg::StakingContract {}, + ) + .unwrap(); + let token_contract: Addr = app + .wrap() + .query_wasm_smart( + voting_module, + &cwd_interface::voting::Query::TokenContract {}, + ) + .unwrap(); + + // Stake some tokens so we can propose + let msg = cw20::Cw20ExecuteMsg::Send { + contract: staking_contract.to_string(), + amount: Uint128::new(2000), + msg: to_binary(&cw20_stake::msg::ReceiveMsg::Stake {}).unwrap(), + }; + app.execute_contract( + Addr::unchecked(CREATOR_ADDR), + token_contract.clone(), + &msg, + &[], + ) + .unwrap(); + app.update_block(next_block); + + // Create 100 new proposals + for num in 1..101 { + let mut title = "Cron".to_string(); + title.push(num.into()); + let mut description = "Cat".to_string(); + description.push(num.into()); + app.execute_contract( + Addr::unchecked(CREATOR_ADDR), + govmod_single.clone(), + &cwd_proposal_multiple::msg::ExecuteMsg::Propose { + title, + description, + choices: MultipleChoiceOptions { + options: vec![ + MultipleChoiceOption { + description: "a".to_string(), + msgs: None, + }, + MultipleChoiceOption { + description: "b".to_string(), + msgs: None, + }, + ], + }, + proposer: None, + }, + &[], + ) + .unwrap(); + } + + // Neither proposal has passed + let res: QueryResponse = app + .wrap() + .query_wasm_smart( + contract_addr.clone(), + &QueryMsg::CheckPassedProposals(CheckPassedProposals { + dao_address: govmod_single.to_string(), + }), + ) + .unwrap(); + assert!(!res.result,); + + // Vote on even proposals + for num in 1..51 { + app.execute_contract( + Addr::unchecked(CREATOR_ADDR), + govmod_single.clone(), + &cwd_proposal_multiple::msg::ExecuteMsg::Vote { + proposal_id: 2 * num as u64, + vote: MultipleChoiceVote { option_id: 0 }, + }, + &[], + ) + .unwrap(); + } + + // Query passed proposals and execute them + for num in 1..51 { + let index: u64 = 2 * num; + + // Check that CheckPassedProposals returns index as the number of the first passed proposal + let res: QueryResponse = app + .wrap() + .query_wasm_smart( + contract_addr.clone(), + &QueryMsg::CheckPassedProposals(CheckPassedProposals { + dao_address: govmod_single.to_string(), + }), + ) + .unwrap(); + + // Execute the proposal + assert_eq!( + res, + QueryResponse { + result: true, + data: to_binary(&index).unwrap() + } + ); + + app.execute_contract( + Addr::unchecked(CREATOR_ADDR), + govmod_single.clone(), + &cwd_proposal_multiple::msg::ExecuteMsg::Execute { proposal_id: index }, + &[], + ) + .unwrap(); + } + + // All passed proposals are executed + // There shouldn't be any passed proposals + let res: QueryResponse = app + .wrap() + .query_wasm_smart( + contract_addr.clone(), + &QueryMsg::CheckPassedProposals(CheckPassedProposals { + dao_address: govmod_single.to_string(), + }), + ) + .unwrap(); + assert!(!res.result,); +} diff --git a/contracts/cw-rules/src/types.rs b/contracts/cw-rules/src/types.rs index c14b0430..f16a7e17 100644 --- a/contracts/cw-rules/src/types.rs +++ b/contracts/cw-rules/src/types.rs @@ -10,7 +10,14 @@ pub mod dao { #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum QueryDao { - Proposal { proposal_id: u64 }, + Proposal { + proposal_id: u64, + }, + ListProposals { + start_after: Option, + limit: Option, + }, + ProposalCount {}, } #[derive(Serialize, Deserialize, Clone, PartialEq, Eq, JsonSchema, Debug)] @@ -19,7 +26,12 @@ pub mod dao { pub id: u64, pub proposal: AnyChoiceProposal, } - // + + #[derive(Serialize, Deserialize, Clone, PartialEq, Eq, JsonSchema, Debug)] + pub struct ProposalListResponse { + pub proposals: Vec, + } + #[derive(Serialize, Deserialize, Clone, PartialEq, Eq, JsonSchema, Debug)] pub struct AnyChoiceProposal { pub status: Status, diff --git a/packages/cw-croncat-core/schema/croncat.json b/packages/cw-croncat-core/schema/croncat.json index 4474d6af..74db5cef 100644 --- a/packages/cw-croncat-core/schema/croncat.json +++ b/packages/cw-croncat-core/schema/croncat.json @@ -512,6 +512,17 @@ } } }, + "CheckPassedProposals": { + "type": "object", + "required": [ + "dao_address" + ], + "properties": { + "dao_address": { + "type": "string" + } + } + }, "CheckProposalStatus": { "type": "object", "required": [ @@ -723,6 +734,18 @@ }, "additionalProperties": false }, + { + "type": "object", + "required": [ + "check_passed_proposals" + ], + "properties": { + "check_passed_proposals": { + "$ref": "#/definitions/CheckPassedProposals" + } + }, + "additionalProperties": false + }, { "type": "object", "required": [ diff --git a/packages/cw-croncat-core/schema/execute_msg.json b/packages/cw-croncat-core/schema/execute_msg.json index a582f8c3..7c8b1476 100644 --- a/packages/cw-croncat-core/schema/execute_msg.json +++ b/packages/cw-croncat-core/schema/execute_msg.json @@ -595,6 +595,17 @@ } } }, + "CheckPassedProposals": { + "type": "object", + "required": [ + "dao_address" + ], + "properties": { + "dao_address": { + "type": "string" + } + } + }, "CheckProposalStatus": { "type": "object", "required": [ @@ -806,6 +817,18 @@ }, "additionalProperties": false }, + { + "type": "object", + "required": [ + "check_passed_proposals" + ], + "properties": { + "check_passed_proposals": { + "$ref": "#/definitions/CheckPassedProposals" + } + }, + "additionalProperties": false + }, { "type": "object", "required": [ diff --git a/packages/cw-croncat-core/schema/get_agent_tasks_response.json b/packages/cw-croncat-core/schema/get_agent_tasks_response.json index 3bf92210..4b6624e6 100644 --- a/packages/cw-croncat-core/schema/get_agent_tasks_response.json +++ b/packages/cw-croncat-core/schema/get_agent_tasks_response.json @@ -219,6 +219,17 @@ } } }, + "CheckPassedProposals": { + "type": "object", + "required": [ + "dao_address" + ], + "properties": { + "dao_address": { + "type": "string" + } + } + }, "CheckProposalStatus": { "type": "object", "required": [ @@ -430,6 +441,18 @@ }, "additionalProperties": false }, + { + "type": "object", + "required": [ + "check_passed_proposals" + ], + "properties": { + "check_passed_proposals": { + "$ref": "#/definitions/CheckPassedProposals" + } + }, + "additionalProperties": false + }, { "type": "object", "required": [ diff --git a/packages/cw-croncat-core/schema/get_task_response.json b/packages/cw-croncat-core/schema/get_task_response.json index 8e8d0293..7735e1e0 100644 --- a/packages/cw-croncat-core/schema/get_task_response.json +++ b/packages/cw-croncat-core/schema/get_task_response.json @@ -219,6 +219,17 @@ } } }, + "CheckPassedProposals": { + "type": "object", + "required": [ + "dao_address" + ], + "properties": { + "dao_address": { + "type": "string" + } + } + }, "CheckProposalStatus": { "type": "object", "required": [ @@ -430,6 +441,18 @@ }, "additionalProperties": false }, + { + "type": "object", + "required": [ + "check_passed_proposals" + ], + "properties": { + "check_passed_proposals": { + "$ref": "#/definitions/CheckPassedProposals" + } + }, + "additionalProperties": false + }, { "type": "object", "required": [ diff --git a/packages/cw-croncat-core/schema/get_tasks_by_owner_response.json b/packages/cw-croncat-core/schema/get_tasks_by_owner_response.json index 8ed0a1b4..8ae8ed9c 100644 --- a/packages/cw-croncat-core/schema/get_tasks_by_owner_response.json +++ b/packages/cw-croncat-core/schema/get_tasks_by_owner_response.json @@ -215,6 +215,17 @@ } } }, + "CheckPassedProposals": { + "type": "object", + "required": [ + "dao_address" + ], + "properties": { + "dao_address": { + "type": "string" + } + } + }, "CheckProposalStatus": { "type": "object", "required": [ @@ -426,6 +437,18 @@ }, "additionalProperties": false }, + { + "type": "object", + "required": [ + "check_passed_proposals" + ], + "properties": { + "check_passed_proposals": { + "$ref": "#/definitions/CheckPassedProposals" + } + }, + "additionalProperties": false + }, { "type": "object", "required": [ diff --git a/packages/cw-croncat-core/schema/get_tasks_response.json b/packages/cw-croncat-core/schema/get_tasks_response.json index 4edac6ab..9914fb29 100644 --- a/packages/cw-croncat-core/schema/get_tasks_response.json +++ b/packages/cw-croncat-core/schema/get_tasks_response.json @@ -215,6 +215,17 @@ } } }, + "CheckPassedProposals": { + "type": "object", + "required": [ + "dao_address" + ], + "properties": { + "dao_address": { + "type": "string" + } + } + }, "CheckProposalStatus": { "type": "object", "required": [ @@ -426,6 +437,18 @@ }, "additionalProperties": false }, + { + "type": "object", + "required": [ + "check_passed_proposals" + ], + "properties": { + "check_passed_proposals": { + "$ref": "#/definitions/CheckPassedProposals" + } + }, + "additionalProperties": false + }, { "type": "object", "required": [ diff --git a/packages/cw-croncat-core/schema/get_tasks_with_queries_response.json b/packages/cw-croncat-core/schema/get_tasks_with_queries_response.json index fb43ddde..2b321cac 100644 --- a/packages/cw-croncat-core/schema/get_tasks_with_queries_response.json +++ b/packages/cw-croncat-core/schema/get_tasks_with_queries_response.json @@ -133,6 +133,17 @@ } } }, + "CheckPassedProposals": { + "type": "object", + "required": [ + "dao_address" + ], + "properties": { + "dao_address": { + "type": "string" + } + } + }, "CheckProposalStatus": { "type": "object", "required": [ @@ -231,6 +242,18 @@ }, "additionalProperties": false }, + { + "type": "object", + "required": [ + "check_passed_proposals" + ], + "properties": { + "check_passed_proposals": { + "$ref": "#/definitions/CheckPassedProposals" + } + }, + "additionalProperties": false + }, { "type": "object", "required": [ diff --git a/packages/cw-croncat-core/schema/query_msg.json b/packages/cw-croncat-core/schema/query_msg.json index 8e9f7906..ce0158ea 100644 --- a/packages/cw-croncat-core/schema/query_msg.json +++ b/packages/cw-croncat-core/schema/query_msg.json @@ -438,6 +438,17 @@ } } }, + "CheckPassedProposals": { + "type": "object", + "required": [ + "dao_address" + ], + "properties": { + "dao_address": { + "type": "string" + } + } + }, "CheckProposalStatus": { "type": "object", "required": [ @@ -649,6 +660,18 @@ }, "additionalProperties": false }, + { + "type": "object", + "required": [ + "check_passed_proposals" + ], + "properties": { + "check_passed_proposals": { + "$ref": "#/definitions/CheckPassedProposals" + } + }, + "additionalProperties": false + }, { "type": "object", "required": [ diff --git a/packages/cw-rules-core/examples/rules_schema.rs b/packages/cw-rules-core/examples/rules_schema.rs index 65fcba7e..a948dc91 100644 --- a/packages/cw-rules-core/examples/rules_schema.rs +++ b/packages/cw-rules-core/examples/rules_schema.rs @@ -6,7 +6,9 @@ use cw_rules_core::{ msg::{ ExecuteMsg, InstantiateMsg, QueryConstruct, QueryMsg, QueryMultiResponse, QueryResponse, }, - types::{CheckOwnerOfNft, CheckProposalStatus, CroncatQuery, HasBalanceGte}, + types::{ + CheckOwnerOfNft, CheckPassedProposals, CheckProposalStatus, CroncatQuery, HasBalanceGte, + }, }; fn main() { @@ -26,6 +28,7 @@ fn main() { export_schema(&schema_for!(HasBalanceGte), &out_dir); export_schema(&schema_for!(CheckOwnerOfNft), &out_dir); export_schema(&schema_for!(CheckProposalStatus), &out_dir); + export_schema(&schema_for!(CheckPassedProposals), &out_dir); export_schema_with_title(&schema_for!(QueryResponse), &out_dir, "RuleResponse"); export_schema_with_title( @@ -55,6 +58,11 @@ fn main() { &out_dir, "CheckProposalStatusResponse", ); + export_schema_with_title( + &schema_for!(QueryResponse), + &out_dir, + "CheckPassedProposalsResponse", + ); export_schema_with_title( &schema_for!(QueryResponse), &out_dir, diff --git a/packages/cw-rules-core/schema/check_passed_proposals.json b/packages/cw-rules-core/schema/check_passed_proposals.json new file mode 100644 index 00000000..388ce2d2 --- /dev/null +++ b/packages/cw-rules-core/schema/check_passed_proposals.json @@ -0,0 +1,13 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "CheckPassedProposals", + "type": "object", + "required": [ + "dao_address" + ], + "properties": { + "dao_address": { + "type": "string" + } + } +} diff --git a/packages/cw-rules-core/schema/check_passed_proposals_response.json b/packages/cw-rules-core/schema/check_passed_proposals_response.json new file mode 100644 index 00000000..3a4edb68 --- /dev/null +++ b/packages/cw-rules-core/schema/check_passed_proposals_response.json @@ -0,0 +1,23 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "CheckPassedProposalsResponse", + "type": "object", + "required": [ + "data", + "result" + ], + "properties": { + "data": { + "$ref": "#/definitions/Binary" + }, + "result": { + "type": "boolean" + } + }, + "definitions": { + "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" + } + } +} diff --git a/packages/cw-rules-core/schema/croncat_query.json b/packages/cw-rules-core/schema/croncat_query.json index f17d959f..e34513a5 100644 --- a/packages/cw-rules-core/schema/croncat_query.json +++ b/packages/cw-rules-core/schema/croncat_query.json @@ -62,6 +62,18 @@ }, "additionalProperties": false }, + { + "type": "object", + "required": [ + "check_passed_proposals" + ], + "properties": { + "check_passed_proposals": { + "$ref": "#/definitions/CheckPassedProposals" + } + }, + "additionalProperties": false + }, { "type": "object", "required": [ @@ -143,6 +155,17 @@ } } }, + "CheckPassedProposals": { + "type": "object", + "required": [ + "dao_address" + ], + "properties": { + "dao_address": { + "type": "string" + } + } + }, "CheckProposalStatus": { "type": "object", "required": [ diff --git a/packages/cw-rules-core/schema/query_construct.json b/packages/cw-rules-core/schema/query_construct.json index 231f5895..48485b41 100644 --- a/packages/cw-rules-core/schema/query_construct.json +++ b/packages/cw-rules-core/schema/query_construct.json @@ -69,6 +69,17 @@ } } }, + "CheckPassedProposals": { + "type": "object", + "required": [ + "dao_address" + ], + "properties": { + "dao_address": { + "type": "string" + } + } + }, "CheckProposalStatus": { "type": "object", "required": [ @@ -167,6 +178,18 @@ }, "additionalProperties": false }, + { + "type": "object", + "required": [ + "check_passed_proposals" + ], + "properties": { + "check_passed_proposals": { + "$ref": "#/definitions/CheckPassedProposals" + } + }, + "additionalProperties": false + }, { "type": "object", "required": [ diff --git a/packages/cw-rules-core/schema/query_msg.json b/packages/cw-rules-core/schema/query_msg.json index b198f5be..27afc641 100644 --- a/packages/cw-rules-core/schema/query_msg.json +++ b/packages/cw-rules-core/schema/query_msg.json @@ -86,6 +86,18 @@ }, "additionalProperties": false }, + { + "type": "object", + "required": [ + "check_passed_proposals" + ], + "properties": { + "check_passed_proposals": { + "$ref": "#/definitions/CheckPassedProposals" + } + }, + "additionalProperties": false + }, { "type": "object", "required": [ @@ -179,6 +191,17 @@ } } }, + "CheckPassedProposals": { + "type": "object", + "required": [ + "dao_address" + ], + "properties": { + "dao_address": { + "type": "string" + } + } + }, "CheckProposalStatus": { "type": "object", "required": [ @@ -277,6 +300,18 @@ }, "additionalProperties": false }, + { + "type": "object", + "required": [ + "check_passed_proposals" + ], + "properties": { + "check_passed_proposals": { + "$ref": "#/definitions/CheckPassedProposals" + } + }, + "additionalProperties": false + }, { "type": "object", "required": [ diff --git a/packages/cw-rules-core/src/msg.rs b/packages/cw-rules-core/src/msg.rs index c8b026aa..de802dcf 100644 --- a/packages/cw-rules-core/src/msg.rs +++ b/packages/cw-rules-core/src/msg.rs @@ -1,4 +1,6 @@ -use crate::types::{CheckOwnerOfNft, CheckProposalStatus, CroncatQuery, HasBalanceGte}; +use crate::types::{ + CheckOwnerOfNft, CheckPassedProposals, CheckProposalStatus, CroncatQuery, HasBalanceGte, +}; use generic_query::GenericQuery; //use cw_croncat_core::types::Rule; //use cosmwasm_std::Coin; @@ -30,6 +32,7 @@ pub enum QueryMsg { HasBalanceGte(HasBalanceGte), CheckOwnerOfNft(CheckOwnerOfNft), CheckProposalStatus(CheckProposalStatus), + CheckPassedProposals(CheckPassedProposals), GenericQuery(GenericQuery), // Full evaluations QueryConstruct(QueryConstruct), diff --git a/packages/cw-rules-core/src/types.rs b/packages/cw-rules-core/src/types.rs index 75afeda0..36a0e6b5 100644 --- a/packages/cw-rules-core/src/types.rs +++ b/packages/cw-rules-core/src/types.rs @@ -32,6 +32,7 @@ pub enum CroncatQuery { HasBalanceGte(HasBalanceGte), CheckOwnerOfNft(CheckOwnerOfNft), CheckProposalStatus(CheckProposalStatus), + CheckPassedProposals(CheckPassedProposals), GenericQuery(GenericQuery), SmartQuery(SmartQueryHead), } @@ -55,3 +56,8 @@ pub struct CheckProposalStatus { pub proposal_id: u64, pub status: Status, } + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] +pub struct CheckPassedProposals { + pub dao_address: String, +} diff --git a/scripts/uni-testnet/create-auto-execute-dao-task.sh b/scripts/uni-testnet/create-auto-execute-dao-task.sh new file mode 100644 index 00000000..f0d4b5bc --- /dev/null +++ b/scripts/uni-testnet/create-auto-execute-dao-task.sh @@ -0,0 +1,79 @@ +set -e + +SH_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/$(basename "${BASH_SOURCE[0]}")" +SH_DIR="$(cd -P "$(dirname "${SH_PATH}")";pwd)" +. $SH_DIR/base/init-vars.sh + +if [ -z "$1" ]; then + echo "Must provide contract address" + exit 1 +elif [ -z "$2" ]; then + echo "Must provide user address" + exit 1 +elif [ -z "$3" ]; then + echo "Must provide rules address" + exit 1 +elif [ -z "$4" ]; then + echo "Must provide dao address" + exit 1 +elif [ -z "$5" ]; then + echo "Must provide agent address" + exit 1 +fi + +CONTRACT="$1" +USER="$2" +RULES="$3" +DAO="$4" +AGENT="$5" + +EXECUTE_MSG='{"execute":{"proposal_id":""}}' +ENCODED_EXECUTE_MSG=$(printf $EXECUTE_MSG | base64) + +DAODAO='{ + "create_task": { + "task": { + "interval": "Immediate", + "cw20_coins": [], + "stop_on_fail": false, + "actions": [ + { + "msg": { + "wasm": { + "execute": { + "contract_addr": "'$DAO'", + "msg": "'$ENCODED_EXECUTE_MSG'", + "funds": [] + } + } + }, + "gas_limit": 300000 + } + ], + "queries": [ + { + "check_passed_proposals": { + "dao_address": "'$DAO'" + } + } + ], + "transforms": [ + { + "action_idx": 0, + "query_idx": 0, + "action_path": [ + { + "key": "execute" + }, + { + "key": "proposal_id" + } + ], + "query_response_path": [] + } + ] + } + } +}' + +junod tx wasm execute $CONTRACT "$DAODAO" --amount 1000000ujunox --from "$USER" $TXFLAG -y diff --git a/scripts/uni-testnet/proxy-call-with-query.sh b/scripts/uni-testnet/proxy-call-with-query.sh new file mode 100644 index 00000000..022a75fc --- /dev/null +++ b/scripts/uni-testnet/proxy-call-with-query.sh @@ -0,0 +1,28 @@ +#!/bin/bash +set -e + +SH_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/$(basename "${BASH_SOURCE[0]}")" +SH_DIR="$(cd -P "$(dirname "${SH_PATH}")";pwd)" +. $SH_DIR/base/init-vars.sh + +if [ -z "$1" ] +then + echo "Must provide contract address" + exit 1 +elif [ -z "$2" ] +then + echo "Must provide address of the agent" + exit 1 +elif [ -z "$3" ] +then + echo "Must provide the task hash" + exit 1 +else + CONTRACT="$1" + AGENT="$2" + HASH="$3" +fi + + +PROXY_CALL='{"proxy_call":{"task_hash": "'$HASH'"}}' +junod tx wasm execute $CONTRACT "$PROXY_CALL" --from $AGENT $TXFLAG -y diff --git a/typescript/build/codegen.js b/typescript/build/codegen.js index 783655d9..2e8fbe3c 100644 --- a/typescript/build/codegen.js +++ b/typescript/build/codegen.js @@ -31,18 +31,6 @@ const DEFAULT_CONFIG = { outputName: OutputType.contracts, outputDir: CONTRACTS_OUTPUT_DIR, }, - { - name: OutputType.contracts, - paths: [`../contracts/${OutputType.croncat}`], - outputName: OutputType.contracts, - outputDir: CONTRACTS_OUTPUT_DIR, - }, - { - name: OutputType.contracts, - paths: [`../contracts/${OutputType.rules}`], - outputName: OutputType.contracts, - outputDir: CONTRACTS_OUTPUT_DIR, - }, { name: OutputType.packages, paths: [`../${OutputType.packages}`], @@ -109,6 +97,7 @@ function main() { const { name, paths, outputName, outputDir } = root; for (const path of paths) { const schemaDirectories = yield getSchemaDirectories(path); + console.log(schemaDirectories); for (const [directory, contractName] of schemaDirectories) { compilationSpecs.push({ contractName: contractName, diff --git a/typescript/build/contracts/cw-rules-core/CwRulesCore.client.js b/typescript/build/contracts/cw-rules-core/CwRulesCore.client.js index e82a0128..cd7b4ac0 100644 --- a/typescript/build/contracts/cw-rules-core/CwRulesCore.client.js +++ b/typescript/build/contracts/cw-rules-core/CwRulesCore.client.js @@ -59,6 +59,13 @@ class CwRulesCoreQueryClient { } }); }); + this.checkPassedProposals = ({ daoAddress }) => __awaiter(this, void 0, void 0, function* () { + return this.client.queryContractSmart(this.contractAddress, { + check_passed_proposals: { + dao_address: daoAddress + } + }); + }); this.genericQuery = ({ contractAddr, msg, ordering, pathToValue, value }) => __awaiter(this, void 0, void 0, function* () { return this.client.queryContractSmart(this.contractAddress, { generic_query: { @@ -96,6 +103,7 @@ class CwRulesCoreQueryClient { this.hasBalanceGte = this.hasBalanceGte.bind(this); this.checkOwnerOfNft = this.checkOwnerOfNft.bind(this); this.checkProposalStatus = this.checkProposalStatus.bind(this); + this.checkPassedProposals = this.checkPassedProposals.bind(this); this.genericQuery = this.genericQuery.bind(this); this.queryConstruct = this.queryConstruct.bind(this); this.smartQuery = this.smartQuery.bind(this); diff --git a/typescript/contracts/cw-croncat-core/CwCroncatCore.client.ts b/typescript/contracts/cw-croncat-core/CwCroncatCore.client.ts index 63571744..c0b4af52 100644 --- a/typescript/contracts/cw-croncat-core/CwCroncatCore.client.ts +++ b/typescript/contracts/cw-croncat-core/CwCroncatCore.client.ts @@ -6,7 +6,7 @@ import { CosmWasmClient, SigningCosmWasmClient, ExecuteResult } from "@cosmjs/cosmwasm-stargate"; import { StdFee } from "@cosmjs/amino"; -import { Addr, Uint128, Timestamp, Uint64, SlotType, AgentStatus, CosmosMsgForEmpty, BankMsg, StakingMsg, DistributionMsg, Binary, IbcMsg, WasmMsg, GovMsg, VoteOption, Boundary, Interval, CroncatQuery, Balance, NativeBalance, Status, ValueOrdering, ValueIndex, PathToValue, SmartQueries, Croncat, Agent, GenericBalance, Cw20CoinVerified, Coin, GetBalancesResponse, GetConfigResponse, GasPrice, GetAgentIdsResponse, AgentResponse, AgentTaskResponse, GetSlotHashesResponse, GetSlotIdsResponse, TaskResponse, ActionForEmpty, Empty, IbcTimeout, IbcTimeoutBlock, HasBalanceGte, CheckOwnerOfNft, CheckProposalStatus, GenericQuery, SmartQueryHead, SmartQuery, GetWalletBalancesResponse, Task, BoundaryValidated, Transform, TaskRequest, Cw20Coin, ExecuteMsg, Cw20ReceiveMsg, GetAgentResponse, GetAgentTasksResponse, GetTaskHashResponse, GetTaskResponse, GetTasksByOwnerResponse, GetTasksResponse, GetTasksWithQueriesResponse, TaskWithQueriesResponse, InstantiateMsg, QueryMsg, ValidateIntervalResponse } from "./CwCroncatCore.types"; +import { Addr, Uint128, Timestamp, Uint64, SlotType, AgentStatus, CosmosMsgForEmpty, BankMsg, StakingMsg, DistributionMsg, Binary, IbcMsg, WasmMsg, GovMsg, VoteOption, Boundary, Interval, CroncatQuery, Balance, NativeBalance, Status, ValueOrdering, ValueIndex, PathToValue, SmartQueries, Croncat, Agent, GenericBalance, Cw20CoinVerified, Coin, GetBalancesResponse, GetConfigResponse, GasPrice, GetAgentIdsResponse, AgentResponse, AgentTaskResponse, GetSlotHashesResponse, GetSlotIdsResponse, TaskResponse, ActionForEmpty, Empty, IbcTimeout, IbcTimeoutBlock, HasBalanceGte, CheckOwnerOfNft, CheckProposalStatus, CheckPassedProposals, GenericQuery, SmartQueryHead, SmartQuery, GetWalletBalancesResponse, Task, BoundaryValidated, Transform, TaskRequest, Cw20Coin, ExecuteMsg, Cw20ReceiveMsg, GetAgentResponse, GetAgentTasksResponse, GetTaskHashResponse, GetTaskResponse, GetTasksByOwnerResponse, GetTasksResponse, GetTasksWithQueriesResponse, TaskWithQueriesResponse, InstantiateMsg, QueryMsg, ValidateIntervalResponse } from "./CwCroncatCore.types"; export interface CwCroncatCoreReadOnlyInterface { contractAddress: string; getConfig: () => Promise; diff --git a/typescript/contracts/cw-croncat-core/CwCroncatCore.types.ts b/typescript/contracts/cw-croncat-core/CwCroncatCore.types.ts index 742b185f..dd7dc709 100644 --- a/typescript/contracts/cw-croncat-core/CwCroncatCore.types.ts +++ b/typescript/contracts/cw-croncat-core/CwCroncatCore.types.ts @@ -169,6 +169,8 @@ export type CroncatQuery = { check_owner_of_nft: CheckOwnerOfNft; } | { check_proposal_status: CheckProposalStatus; +} | { + check_passed_proposals: CheckPassedProposals; } | { generic_query: GenericQuery; } | { @@ -348,6 +350,10 @@ export interface CheckProposalStatus { status: Status; [k: string]: unknown; } +export interface CheckPassedProposals { + dao_address: string; + [k: string]: unknown; +} export interface GenericQuery { contract_addr: string; msg: Binary; diff --git a/typescript/contracts/cw-rules-core/CwRulesCore.client.ts b/typescript/contracts/cw-rules-core/CwRulesCore.client.ts index 1f05237e..ffffc0bd 100644 --- a/typescript/contracts/cw-rules-core/CwRulesCore.client.ts +++ b/typescript/contracts/cw-rules-core/CwRulesCore.client.ts @@ -6,7 +6,7 @@ import { CosmWasmClient, SigningCosmWasmClient, ExecuteResult } from "@cosmjs/cosmwasm-stargate"; import { StdFee } from "@cosmjs/amino"; -import { Binary, CheckOwnerOfNftResponse, CheckOwnerOfNft, CheckProposalStatusResponse, Status, CheckProposalStatus, CroncatQuery, Balance, Uint128, NativeBalance, Addr, ValueOrdering, ValueIndex, PathToValue, SmartQueries, HasBalanceGte, Coin, Cw20CoinVerified, GenericQuery, SmartQueryHead, SmartQuery, ExecuteMsg, GenericQueryResponse, GetBalanceResponse, GetCw20BalanceResponse, HasBalanceGteResponse, InstantiateMsg, QueryConstructResponse, QueryConstruct, QueryMsg, QueryMultiResponse, RuleResponse, SmartQueryResponse } from "./CwRulesCore.types"; +import { Binary, CheckOwnerOfNftResponse, CheckOwnerOfNft, CheckPassedProposalsResponse, CheckPassedProposals, CheckProposalStatusResponse, Status, CheckProposalStatus, CroncatQuery, Balance, Uint128, NativeBalance, Addr, ValueOrdering, ValueIndex, PathToValue, SmartQueries, HasBalanceGte, Coin, Cw20CoinVerified, GenericQuery, SmartQueryHead, SmartQuery, ExecuteMsg, GenericQueryResponse, GetBalanceResponse, GetCw20BalanceResponse, HasBalanceGteResponse, InstantiateMsg, QueryConstructResponse, QueryConstruct, QueryMsg, QueryMultiResponse, RuleResponse, SmartQueryResponse } from "./CwRulesCore.types"; export interface CwRulesCoreReadOnlyInterface { contractAddress: string; getBalance: ({ @@ -48,6 +48,11 @@ export interface CwRulesCoreReadOnlyInterface { proposalId: number; status: Status; }) => Promise; + checkPassedProposals: ({ + daoAddress + }: { + daoAddress: string; + }) => Promise; genericQuery: ({ contractAddr, msg, @@ -94,6 +99,7 @@ export class CwRulesCoreQueryClient implements CwRulesCoreReadOnlyInterface { this.hasBalanceGte = this.hasBalanceGte.bind(this); this.checkOwnerOfNft = this.checkOwnerOfNft.bind(this); this.checkProposalStatus = this.checkProposalStatus.bind(this); + this.checkPassedProposals = this.checkPassedProposals.bind(this); this.genericQuery = this.genericQuery.bind(this); this.queryConstruct = this.queryConstruct.bind(this); this.smartQuery = this.smartQuery.bind(this); @@ -175,6 +181,17 @@ export class CwRulesCoreQueryClient implements CwRulesCoreReadOnlyInterface { } }); }; + checkPassedProposals = async ({ + daoAddress + }: { + daoAddress: string; + }): Promise => { + return this.client.queryContractSmart(this.contractAddress, { + check_passed_proposals: { + dao_address: daoAddress + } + }); + }; genericQuery = async ({ contractAddr, msg, diff --git a/typescript/contracts/cw-rules-core/CwRulesCore.types.ts b/typescript/contracts/cw-rules-core/CwRulesCore.types.ts index 0a517add..03b05442 100644 --- a/typescript/contracts/cw-rules-core/CwRulesCore.types.ts +++ b/typescript/contracts/cw-rules-core/CwRulesCore.types.ts @@ -16,6 +16,15 @@ export interface CheckOwnerOfNft { token_id: string; [k: string]: unknown; } +export interface CheckPassedProposalsResponse { + data: Binary; + result: boolean; + [k: string]: unknown; +} +export interface CheckPassedProposals { + dao_address: string; + [k: string]: unknown; +} export interface CheckProposalStatusResponse { data: Binary; result: boolean; @@ -40,6 +49,8 @@ export type CroncatQuery = { check_owner_of_nft: CheckOwnerOfNft; } | { check_proposal_status: CheckProposalStatus; +} | { + check_passed_proposals: CheckPassedProposals; } | { generic_query: GenericQuery; } | { @@ -155,6 +166,8 @@ export type QueryMsg = { check_owner_of_nft: CheckOwnerOfNft; } | { check_proposal_status: CheckProposalStatus; +} | { + check_passed_proposals: CheckPassedProposals; } | { generic_query: GenericQuery; } | {