diff --git a/Cargo.toml b/Cargo.toml index 7e37d28..5a419c7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,8 +32,8 @@ neutron-query = { path = "packages/neutron-query" } #cw-orch = { version = "0.22.2" } -cw-orch = { package = "cw-orch-interchain", git = "ssh://git@github.com/AbstractSDK/cw-orch-interchain.git", tag = "v0.22.0" } -#cw-orch-interchain = { version = "0.1.0" } +cw-orch = "0.22" +cw-orch-interchain = { version = "0.1.0" } const_format = "0.2.32" interchain-gov = { path = "contracts/interchain-gov" } diff --git a/contracts/interchain-gov/Cargo.toml b/contracts/interchain-gov/Cargo.toml index 833f8df..682401a 100644 --- a/contracts/interchain-gov/Cargo.toml +++ b/contracts/interchain-gov/Cargo.toml @@ -78,8 +78,9 @@ interchain-gov = { workspace = true } abstract-client = { workspace = true } abstract-adapter = { workspace = true, features = ["test-utils"] } speculoos = { workspace = true } -#cw-orch-interchain = { workspace = true } - +cw-orch-interchain = { workspace = true } +env_logger = { workspace = true } +dotenv = { workspace = true } abstract-cw-orch-polytone = "2.0.0" abstract-interface = { workspace = true } -abstract-interchain-tests = { git = "https://github.com/AbstractSDK/abstract", version = "0.22.1", branch = "removemm" } \ No newline at end of file +abstract-interchain-tests = "0.22" \ No newline at end of file diff --git a/contracts/interchain-gov/src/contract.rs b/contracts/interchain-gov/src/contract.rs index 814fede..daa6fbb 100644 --- a/contracts/interchain-gov/src/contract.rs +++ b/contracts/interchain-gov/src/contract.rs @@ -1,11 +1,15 @@ -use crate::{error::InterchainGovError, handlers, msg::{InterchainGovExecuteMsg, InterchainGovInstantiateMsg, InterchainGovQueryMsg}, ADAPTER_VERSION, MY_ADAPTER_ID, ibc_callbacks, replies}; +use crate::{ + error::InterchainGovError, + handlers, ibc_callbacks, + msg::{InterchainGovExecuteMsg, InterchainGovInstantiateMsg, InterchainGovQueryMsg}, + replies, ADAPTER_VERSION, MY_ADAPTER_ID, +}; -use abstract_adapter::AdapterContract; -use cosmwasm_std::{Empty, Response}; use crate::dependencies::IBC_CLIENT_DEP; -use crate::ibc_callbacks::{REGISTER_VOTE_ID, PROPOSE_CALLBACK_ID, FINALIZE_CALLBACK_ID}; +use crate::ibc_callbacks::{FINALIZE_CALLBACK_ID, PROPOSE_CALLBACK_ID, REGISTER_VOTE_ID}; use crate::msg::InterchainGovSudoMsg; - +use abstract_adapter::AdapterContract; +use cosmwasm_std::{Empty, Response}; /// The type of the adapter that is used to build your Adapter and access the Abstract SDK features. pub type InterchainGov = AdapterContract< @@ -14,7 +18,7 @@ pub type InterchainGov = AdapterContract< InterchainGovExecuteMsg, InterchainGovQueryMsg, Empty, - InterchainGovSudoMsg + InterchainGovSudoMsg, >; /// The type of the result returned by your Adapter's entry points. pub type AdapterResult = Result; @@ -27,7 +31,7 @@ const INTERCHAIN_GOV: InterchainGov = InterchainGov::new(MY_ADAPTER_ID, ADAPTER_ .with_ibc_callbacks(&[ (PROPOSE_CALLBACK_ID, ibc_callbacks::proposal_callback), (FINALIZE_CALLBACK_ID, ibc_callbacks::finalize_callback), - (REGISTER_VOTE_ID, ibc_callbacks::vote_result_callback) + (REGISTER_VOTE_ID, ibc_callbacks::vote_result_callback), ]) // WE need a reply for every member .with_replies(&[ diff --git a/contracts/interchain-gov/src/error.rs b/contracts/interchain-gov/src/error.rs index 1349439..b55edf0 100644 --- a/contracts/interchain-gov/src/error.rs +++ b/contracts/interchain-gov/src/error.rs @@ -94,19 +94,17 @@ pub enum InterchainGovError { #[error("Votes still pending for {prop_id} on {chains:?}")] VotesStillPending { prop_id: ProposalId, - chains: Vec + chains: Vec, }, #[error("Gov Votes still pending for {prop_id} on {chains:?}")] GovVotesStillPending { prop_id: ProposalId, - chains: Vec + chains: Vec, }, #[error("Missing vote results for {prop_id}, should call RequestVoteResults first")] - MissingVoteResults { - prop_id: ProposalId, - }, + MissingVoteResults { prop_id: ProposalId }, #[error("Votes already finalized {0}")] VotesAlreadyFinalized(String), @@ -117,11 +115,11 @@ pub enum InterchainGovError { #[error("Unrequested vote result for {chain:?}, {prop_id}")] UnrequestedVote { chain: ChainName, - prop_id: ProposalId + prop_id: ProposalId, }, #[error("Existing vote result for {chain:?}, {prop_id}")] ExistingVoteResult { chain: ChainName, - prop_id: ProposalId + prop_id: ProposalId, }, } diff --git a/contracts/interchain-gov/src/handlers/execute.rs b/contracts/interchain-gov/src/handlers/execute.rs index 361b582..1e3eec5 100644 --- a/contracts/interchain-gov/src/handlers/execute.rs +++ b/contracts/interchain-gov/src/handlers/execute.rs @@ -1,40 +1,35 @@ use abstract_adapter::objects::chain_name::ChainName; use abstract_adapter::objects::module::ModuleInfo; use abstract_adapter::sdk::{AbstractSdkResult, Execution, IbcInterface, TransferInterface}; -use abstract_adapter::std::AbstractResult; use abstract_adapter::std::ibc::CallbackInfo; +use abstract_adapter::std::AbstractResult; use abstract_adapter::traits::AbstractResponse; use abstract_adapter::traits::ModuleIdentification; use base64::Engine; -use cosmwasm_std::{coins, CosmosMsg, DepsMut, Env, MessageInfo, Order, StdResult, Storage, SubMsg, to_json_binary, WasmQuery}; +use cosmwasm_std::{ + coins, to_json_binary, CosmosMsg, DepsMut, Env, MessageInfo, Order, StdResult, Storage, SubMsg, + WasmQuery, +}; use ibc_sync_state::DataState; use neutron_query::gov::create_gov_proposal_keys; use neutron_query::icq::IcqInterface; use neutron_query::QueryType; +use crate::ibc_callbacks::{FINALIZE_CALLBACK_ID, PROPOSE_CALLBACK_ID, REGISTER_VOTE_ID}; +use crate::msg::InterchainGovQueryMsg; +use crate::msg::{InterchainGovIbcCallbackMsg, InterchainGovIbcMsg}; +use crate::state::{ + Governance, GovernanceVote, Members, Proposal, ProposalAction, ProposalId, ProposalMsg, + ProposalOutcome, TallyResult, Vote, FINALIZED_PROPOSALS, GOV_VOTE_QUERIES, MEMBERS, + MEMBERS_STATE_SYNC, PENDING_REPLIES, PROPOSAL_STATE_SYNC, TEMP_REMOTE_GOV_MODULE_ADDRS, VOTE, + VOTE_RESULTS, +}; use crate::{ contract::{AdapterResult, InterchainGov}, - InterchainGovError, msg::InterchainGovExecuteMsg, + InterchainGovError, }; -use crate::ibc_callbacks::{FINALIZE_CALLBACK_ID, PROPOSE_CALLBACK_ID, REGISTER_VOTE_ID}; -use crate::msg::{InterchainGovIbcCallbackMsg, InterchainGovIbcMsg}; -use crate::msg::InterchainGovQueryMsg; -use crate::state::{GOV_VOTE_QUERIES, Governance, GovernanceVote, MEMBERS, Members, MEMBERS_STATE_SYNC, PENDING_REPLIES, Proposal, PROPOSAL_STATE_SYNC, ProposalAction, ProposalId, ProposalMsg, TallyResult, TEMP_REMOTE_GOV_MODULE_ADDRS, VOTE, Vote, VOTE_RESULTS}; - -// fn tally(deps: DepsMut, env: Env, app: InterchainGov, prop_id: String) -> AdapterResult { -// let (prop, _state) = PROPOSAL_STATE_SYNC.load(deps.storage, prop_id.clone())?; -// -// if !prop.expiration.is_expired(&env.block) { -// return Err(InterchainGovError::ProposalStillOpen(prop_id.clone())); -// } -// -// // let external_members = load_external_members(deps.storage, &env)?; -// Ok(app -// .response("tally_proposal") -// .add_attribute("prop_id", prop_id)) -// } pub fn execute_handler( deps: DepsMut, @@ -44,19 +39,166 @@ pub fn execute_handler( msg: InterchainGovExecuteMsg, ) -> AdapterResult { match msg { - InterchainGovExecuteMsg::Propose { proposal } => propose(deps, env, info, adapter, proposal), - InterchainGovExecuteMsg::Finalize { prop_id } => finalize(deps, env, info, adapter, prop_id), - InterchainGovExecuteMsg::VoteProposal { prop_id, vote, governance } => do_vote(deps, env, adapter, prop_id, vote, governance), - InterchainGovExecuteMsg::RequestVoteResults { prop_id } => request_vote_results(deps, env, adapter, prop_id), - InterchainGovExecuteMsg::RequestGovVoteDetails { prop_id } => request_gov_vote_details(deps, env, adapter, prop_id), - InterchainGovExecuteMsg::TestAddMembers { members } => test_add_members(deps, adapter, members), - InterchainGovExecuteMsg::TemporaryRegisterRemoteGovModuleAddrs { modules } => temporary_register_remote_gov_module_addrs(deps, adapter, modules), - _ => todo!() + InterchainGovExecuteMsg::Propose { proposal } => { + propose(deps, env, info, adapter, proposal) + } + InterchainGovExecuteMsg::Finalize { prop_id } => { + finalize(deps, env, info, adapter, prop_id) + } + InterchainGovExecuteMsg::VoteProposal { + prop_id, + vote, + governance, + } => do_vote(deps, env, adapter, prop_id, vote, governance), + InterchainGovExecuteMsg::RequestVoteResults { prop_id } => { + request_vote_results(deps, env, adapter, prop_id) + } + InterchainGovExecuteMsg::RequestGovVoteDetails { prop_id } => { + request_gov_vote_details(deps, env, adapter, prop_id) + } + InterchainGovExecuteMsg::TestAddMembers { members } => { + test_add_members(deps, adapter, members) + } + InterchainGovExecuteMsg::TemporaryRegisterRemoteGovModuleAddrs { modules } => { + temporary_register_remote_gov_module_addrs(deps, adapter, modules) + } + InterchainGovExecuteMsg::Execute { prop_id } => execute_prop(deps, env, adapter, prop_id), + _ => todo!(), } } +// Execute a proposal after we got all the votes +fn execute_prop( + deps: DepsMut, + env: Env, + app: InterchainGov, + prop_id: String, +) -> Result { + // check existing vote results + let existing_vote_results = VOTE_RESULTS + .prefix(prop_id.clone()) + .range(deps.storage, None, None, Order::Ascending) + .collect::)>>>()?; + + // If we don't have any vote results, we need to query them + if existing_vote_results.is_empty() { + return Err(InterchainGovError::MissingVoteResults { + prop_id: prop_id.clone(), + }); + } else { + // if we have pending votes, check they're all resolved + if existing_vote_results.iter().any(|(_, vote)| vote.is_none()) { + return Err(InterchainGovError::VotesStillPending { + prop_id: prop_id.clone(), + chains: existing_vote_results + .into_iter() + .map(|(chain, _)| chain.clone()) + .collect(), + }); + } + } + + let mut votes_for: u8 = 0; + let mut votes_against: u8 = 0; + + let this_vote = VOTE.load(deps.storage, prop_id.clone())?; + if this_vote.vote == Vote::Yes { + votes_for += 1; + } else { + votes_against += 1; + } + + // Then get prop and check if it passed + existing_vote_results + .iter() + .for_each(|(_, vote)| match vote { + Some(vote) => { + if vote.vote == Vote::Yes { + votes_for += 1; + } else { + votes_against += 1; + } + } + None => panic!("Vote Null checked before."), + }); + + let outcome = if votes_for > votes_against { + ProposalOutcome { + passed: true, + votes_for, + votes_against, + } + } else { + ProposalOutcome { + passed: false, + votes_for, + votes_against, + } + }; + + let prop = PROPOSAL_STATE_SYNC.load(deps.storage, prop_id.clone())?.0; + // TODO: store each vote per chain + FINALIZED_PROPOSALS.save( + deps.storage, + prop_id.clone(), + &(prop.clone(), outcome.clone()), + )?; + let external_members = MEMBERS_STATE_SYNC.external_members(deps.storage, &env)?; + + // Execute the prop + match prop.action { + ProposalAction::UpdateMembers { members } => { + // If new members exclude self, update members to only be self + if !members.members.contains(&ChainName::new(&env)) { + MEMBERS_STATE_SYNC.save_members(deps.storage, &Members::new(&env))?; + } + MEMBERS_STATE_SYNC.save_members(deps.storage, &members)?; + } + ProposalAction::Signal => {} + } + + // Send mgs to other members to report vote outcome + + PROPOSAL_STATE_SYNC + .set_outstanding_finalization_acks(deps.storage, external_members.members.clone())?; + + let ibc_client = app.ibc_client(deps.as_ref()); + let exec_msg = InterchainGovIbcMsg::ProposalResult { + prop_hash: prop_id.clone(), + outcome: outcome, + }; + let mut msgs = vec![]; + let target_module = this_module(&app)?; + for host in external_members.members.iter() { + let callback = CallbackInfo::new( + PROPOSE_CALLBACK_ID, + Some(to_json_binary( + &InterchainGovIbcCallbackMsg::ProposalResult { + proposed_to: host.clone(), + prop_hash: prop_id.clone(), + }, + )?), + ); + msgs.push(ibc_client.module_ibc_action( + host.to_string(), + target_module.clone(), + &exec_msg, + Some(callback.clone()), + )?); + } + return Ok(app + .response("propose_members") + .add_messages(msgs) + .add_attribute("prop_id", prop_id)); +} + /// Reach out to external members and request their local vote results -fn request_vote_results(deps: DepsMut, env: Env, app: InterchainGov, prop_id: ProposalId) -> AdapterResult { +fn request_vote_results( + deps: DepsMut, + env: Env, + app: InterchainGov, + prop_id: ProposalId, +) -> AdapterResult { let (prop, state) = load_proposal(deps.storage, &prop_id)?; // We can only tally upon expiration @@ -70,7 +212,10 @@ fn request_vote_results(deps: DepsMut, env: Env, app: InterchainGov, prop_id: Pr // } // First, make sure that all votes are in by checking whether we have vote results - let existing_vote_results = VOTE_RESULTS.prefix(prop_id.clone()).range(deps.storage, None, None, Order::Ascending).collect::)>>>()?; + let existing_vote_results = VOTE_RESULTS + .prefix(prop_id.clone()) + .range(deps.storage, None, None, Order::Ascending) + .collect::)>>>()?; // If we don't have any vote results, we need to query them if !existing_vote_results.is_empty() { @@ -78,42 +223,61 @@ fn request_vote_results(deps: DepsMut, env: Env, app: InterchainGov, prop_id: Pr return if existing_vote_results.iter().any(|(_, vote)| vote.is_none()) { Err(InterchainGovError::VotesStillPending { prop_id: prop_id.clone(), - chains: existing_vote_results.into_iter().map(|(chain, _)| chain.clone()).collect(), + chains: existing_vote_results + .into_iter() + .map(|(chain, _)| chain.clone()) + .collect(), }) } else { // happy path error Err(InterchainGovError::VotesAlreadyFinalized(prop_id.clone())) - } + }; }; // Ask everyone to give us their votes let external_members = load_external_members(deps.storage, &env)?; - let vote_queries = external_members.iter().map(|host| -> AbstractSdkResult { - let ibc_client = app.ibc_client(deps.as_ref()); - let module_addr = TEMP_REMOTE_GOV_MODULE_ADDRS.load(deps.storage, host)?; - let query = ibc_client.ibc_query(host.to_string(), WasmQuery::Smart { - contract_addr: module_addr, - msg: to_json_binary(&InterchainGovQueryMsg::Vote { - prop_id: prop_id.clone(), - })?, - }, CallbackInfo::new(REGISTER_VOTE_ID, None))?; - + let vote_queries = external_members + .iter() + .map(|host| -> AbstractSdkResult { + let ibc_client = app.ibc_client(deps.as_ref()); + let module_addr = TEMP_REMOTE_GOV_MODULE_ADDRS.load(deps.storage, host)?; + let query = ibc_client.ibc_query( + host.to_string(), + WasmQuery::Smart { + contract_addr: module_addr, + msg: to_json_binary(&InterchainGovQueryMsg::Vote { + prop_id: prop_id.clone(), + })?, + }, + CallbackInfo::new(REGISTER_VOTE_ID, None), + )?; - // Mark the vote result as pending - VOTE_RESULTS.save(deps.storage, (prop_id.clone(), host), &None)?; + // Mark the vote result as pending + VOTE_RESULTS.save(deps.storage, (prop_id.clone(), host), &None)?; - Ok(query) - }).collect::>>()?; + Ok(query) + }) + .collect::>>()?; // let external_members = load_external_members(deps.storage, &env)?; - Ok(app.response("query_vote_results").add_attribute("prop_id", prop_id).add_messages(vote_queries)) + Ok(app + .response("query_vote_results") + .add_attribute("prop_id", prop_id) + .add_messages(vote_queries)) } - /// REuest external members actual governance vote details -fn request_gov_vote_details(deps: DepsMut, env: Env, app: InterchainGov, prop_id: ProposalId) -> AdapterResult { +fn request_gov_vote_details( + deps: DepsMut, + env: Env, + app: InterchainGov, + prop_id: ProposalId, +) -> AdapterResult { // check existing vote results - let existing_vote_results = VOTE_RESULTS.prefix(prop_id.clone()).range(deps.storage, None, None, Order::Ascending).collect::)>>>()?; + let existing_vote_results = VOTE_RESULTS + .prefix(prop_id.clone()) + .range(deps.storage, None, None, Order::Ascending) + .collect::)>>>()?; // If we don't have any vote results, we need to query them if existing_vote_results.is_empty() { @@ -125,13 +289,19 @@ fn request_gov_vote_details(deps: DepsMut, env: Env, app: InterchainGov, prop_id if existing_vote_results.iter().any(|(_, vote)| vote.is_none()) { return Err(InterchainGovError::VotesStillPending { prop_id: prop_id.clone(), - chains: existing_vote_results.into_iter().map(|(chain, _)| chain.clone()).collect(), - }) + chains: existing_vote_results + .into_iter() + .map(|(chain, _)| chain.clone()) + .collect(), + }); } } // First, make sure that all votes are in by checking whether we have vote results - let exsting_query_results = GOV_VOTE_QUERIES.prefix(prop_id.clone()).range(deps.storage, None, None, Order::Ascending).collect::)>>>()?; + let exsting_query_results = GOV_VOTE_QUERIES + .prefix(prop_id.clone()) + .range(deps.storage, None, None, Order::Ascending) + .collect::)>>>()?; // If we don't have any vote results, we need to query them if !exsting_query_results.is_empty() { @@ -139,57 +309,76 @@ fn request_gov_vote_details(deps: DepsMut, env: Env, app: InterchainGov, prop_id return if exsting_query_results.iter().any(|(_, vote)| vote.is_none()) { Err(InterchainGovError::GovVotesStillPending { prop_id: prop_id.clone(), - chains: exsting_query_results.into_iter().map(|(chain, _)| chain.clone()).collect(), + chains: exsting_query_results + .into_iter() + .map(|(chain, _)| chain.clone()) + .collect(), }) } else { // happy path error Err(InterchainGovError::GovVotesAlreadyQueried(prop_id.clone())) - } + }; }; // TODO: check pending replies (could be overwritten) + // TODO: Only do ICQ for cosmos-sdk govs - let external_members = load_external_members(deps.storage, &env)?; - let query_sender = env.contract.address.clone(); + // let external_members = load_external_members(deps.storage, &env)?; + // let query_sender = env.contract.address.clone(); // loop through the external members and register interchain queries for their votes // These will call the sudo endpoint on our contract - let gov_queries = external_members.iter().enumerate().map(|(index, host)| { - let icq = app.neutron_icq(deps.as_ref())?; - let query = icq.register_interchain_query(&query_sender, host.clone(), QueryType::KV, create_gov_proposal_keys(vec![])?, vec![], 0)?; - // store the query as pending - GOV_VOTE_QUERIES.save(deps.storage, (prop_id.clone(), host), &None)?; - let reply_id = index as u64; - - PENDING_REPLIES.save(deps.storage, reply_id, &(host.clone(), prop_id.clone()))?; + // let gov_queries = external_members.iter().enumerate().map(|(index, host)| { + // let icq = app.neutron_icq(deps.as_ref())?; + // let query = icq.register_interchain_query(&query_sender, host.clone(), QueryType::KV, create_gov_proposal_keys(vec![])?, vec![], 0)?; + // // store the query as pending + // GOV_VOTE_QUERIES.save(deps.storage, (prop_id.clone(), host), &None)?; + // let reply_id = index as u64; - Ok(SubMsg::reply_always(query, reply_id)) - }).collect::>>()?; + // PENDING_REPLIES.save(deps.storage, reply_id, &(host.clone(), prop_id.clone()))?; + // Ok(SubMsg::reply_always(query, reply_id)) + // }).collect::>>()?; // we should do this all at once - let withdraw_msg = app.bank(deps.as_ref()).transfer(coins(100000u128, "untrn"), &env.contract.address)?; - let withdraw_msg: CosmosMsg = app.executor(deps.as_ref()).execute(vec![withdraw_msg])?.into(); + // let withdraw_msg = app.bank(deps.as_ref()).transfer(coins(100000u128, "untrn"), &env.contract.address)?; + // let withdraw_msg: CosmosMsg = app.executor(deps.as_ref()).execute(vec![withdraw_msg])?.into(); - Ok(app.response("request_gov_vote_details").add_attribute("prop_id", prop_id).add_message(withdraw_msg).add_submessages(gov_queries)) + // Ok(app.response("request_gov_vote_details").add_attribute("prop_id", prop_id).add_message(withdraw_msg).add_submessages(gov_queries)) + Ok(app.response("action")) } -fn check_existing_votes AdapterResult<()>>(deps: &DepsMut, prop_id: &ProposalId, has_all_votes_handler: E) -> AdapterResult<()> { -// First, make sure that all votes are in by checking whether we have vote results - +fn check_existing_votes AdapterResult<()>>( + deps: &DepsMut, + prop_id: &ProposalId, + has_all_votes_handler: E, +) -> AdapterResult<()> { + // First, make sure that all votes are in by checking whether we have vote results Ok(()) } -fn load_proposal(storage: &mut dyn Storage, prop_id: &String) -> Result<(Proposal, Option), InterchainGovError> { - let (prop, _vote) = PROPOSAL_STATE_SYNC.load(storage, prop_id.clone()).map_err(|_| InterchainGovError::ProposalNotFound(prop_id.clone()))?; +fn load_proposal( + storage: &mut dyn Storage, + prop_id: &String, +) -> Result<(Proposal, Option), InterchainGovError> { + let (prop, _vote) = PROPOSAL_STATE_SYNC + .load(storage, prop_id.clone()) + .map_err(|_| InterchainGovError::ProposalNotFound(prop_id.clone()))?; let data_state = PROPOSAL_STATE_SYNC.data_state(storage, prop_id.clone()); Ok((prop, data_state)) } // This currently allows for overriding votes -fn do_vote(deps: DepsMut, env: Env, app: InterchainGov, prop_id: String, vote: Vote, governance: Governance) -> AdapterResult { +fn do_vote( + deps: DepsMut, + env: Env, + app: InterchainGov, + prop_id: String, + vote: Vote, + governance: Governance, +) -> AdapterResult { println!("Voting on proposal: {:?} with vote: {:?}", prop_id, vote); PROPOSAL_STATE_SYNC.assert_finalized(deps.storage, prop_id.clone())?; @@ -207,13 +396,13 @@ fn do_vote(deps: DepsMut, env: Env, app: InterchainGov, prop_id: String, vote: V */ // TODO: check governance? - VOTE.save(deps.storage, prop_id.clone(), &GovernanceVote::new(governance, vote.clone()))?; - PROPOSAL_STATE_SYNC.finalize_kv_state( + VOTE.save( deps.storage, prop_id.clone(), - Some((prop, vote)), + &GovernanceVote::new(governance, vote.clone()), )?; - + PROPOSAL_STATE_SYNC.finalize_kv_state(deps.storage, prop_id.clone(), Some((prop, vote)))?; + Ok(app .response("vote_proposal") .add_attribute("prop_id", prop_id)) @@ -261,18 +450,20 @@ fn propose( )?; match prop.action { // If goal is to update members, send an IBC packet to members to update their state - ProposalAction::UpdateMembers { members } => { + ProposalAction::UpdateMembers { mut members } => { MEMBERS_STATE_SYNC.initiate_members(deps.storage, &env, members.clone())?; // send msgs to new members - let ibc_client = app.ibc_client(deps.as_ref()); let exec_msg = InterchainGovIbcMsg::JoinGov { members: members.clone(), }; + // filter out self + members.members.retain(|c| c != &ChainName::new(&env)); + let mut msgs = vec![]; let target_module = this_module(&app)?; - for host in external_members.members.iter() { + for host in members.members.iter() { let callback = CallbackInfo::new( PROPOSE_CALLBACK_ID, Some(to_json_binary(&InterchainGovIbcCallbackMsg::JoinGov { @@ -286,6 +477,7 @@ fn propose( Some(callback.clone()), )?); } + return Ok(app .response("propose_members") .add_messages(msgs) @@ -342,12 +534,20 @@ fn propose( }) .collect::>>()?; - Ok(app.response("propose").add_attribute("prop_id", prop_id).add_messages(propose_msgs)) + Ok(app + .response("propose") + .add_attribute("prop_id", prop_id) + .add_messages(propose_msgs)) } /// Helper to load external members fn load_external_members(storage: &mut dyn Storage, env: &Env) -> AdapterResult> { - Ok(MEMBERS.load(storage)?.members.into_iter().filter(|m| m != &ChainName::new(env)).collect::>()) + Ok(MEMBERS + .load(storage)? + .members + .into_iter() + .filter(|m| m != &ChainName::new(env)) + .collect::>()) } /// Send finalization message over IBC @@ -394,21 +594,22 @@ pub fn finalize( Ok(msg) }) .collect::>>()?; - + Ok(app .response("finalize") .add_attribute("prop_id", prop_id) .add_messages(finalize_messages)) } - fn this_module(app: &InterchainGov) -> AbstractResult { ModuleInfo::from_id(app.module_id(), app.version().into()) } - - -fn temporary_register_remote_gov_module_addrs(deps: DepsMut, app: InterchainGov, modules: Vec<(ChainName, String)>) -> AdapterResult { +fn temporary_register_remote_gov_module_addrs( + deps: DepsMut, + app: InterchainGov, + modules: Vec<(ChainName, String)>, +) -> AdapterResult { for (chain, addr) in modules { TEMP_REMOTE_GOV_MODULE_ADDRS.save(deps.storage, &chain, &addr)?; } diff --git a/contracts/interchain-gov/src/handlers/mod.rs b/contracts/interchain-gov/src/handlers/mod.rs index cf60809..1964d65 100644 --- a/contracts/interchain-gov/src/handlers/mod.rs +++ b/contracts/interchain-gov/src/handlers/mod.rs @@ -3,5 +3,8 @@ pub mod instantiate; pub mod module_ibc; pub mod sudo; -pub use self::{execute::execute_handler, instantiate::instantiate_handler, query::query_handler, module_ibc::module_ibc_handler, sudo::sudo_handler}; +pub use self::{ + execute::execute_handler, instantiate::instantiate_handler, module_ibc::module_ibc_handler, + query::query_handler, sudo::sudo_handler, +}; pub mod query; diff --git a/contracts/interchain-gov/src/handlers/module_ibc.rs b/contracts/interchain-gov/src/handlers/module_ibc.rs index 2109d6b..e3a8a23 100644 --- a/contracts/interchain-gov/src/handlers/module_ibc.rs +++ b/contracts/interchain-gov/src/handlers/module_ibc.rs @@ -6,7 +6,10 @@ use cosmwasm_std::{from_json, DepsMut, Env}; use crate::contract::{AdapterResult, InterchainGov}; use crate::msg::InterchainGovIbcMsg; -use crate::state::{Vote, ALLOW_JOINING_GOV, MEMBERS_STATE_SYNC, PROPOSAL_STATE_SYNC}; +use crate::state::{ + Members, ProposalAction, Vote, ALLOW_JOINING_GOV, FINALIZED_PROPOSALS, MEMBERS_STATE_SYNC, + PROPOSAL_STATE_SYNC, +}; use crate::{InterchainGovError, MY_ADAPTER_ID}; pub fn module_ibc_handler( @@ -39,7 +42,7 @@ pub fn module_ibc_handler( let allowed_gov = ALLOW_JOINING_GOV.load(deps.storage)?; // assert that the governance members are the whitelisted members - if members != allowed_gov { + if !members.members.iter().all(|a| allowed_gov.members.contains(a)) { return Err(InterchainGovError::UnauthorizedIbcMessage {}); } @@ -64,18 +67,43 @@ pub fn module_ibc_handler( // update proposal state to "proposed". Member will vote `NoVote` on the proposal by default PROPOSAL_STATE_SYNC.propose_kv_state(deps.storage, prop_hash, (prop, Vote::NoVote))?; - Ok(app .response("module_ibc") .add_attribute("action", "propose")) } InterchainGovIbcMsg::FinalizeProposal { prop_hash: prop_id } => { PROPOSAL_STATE_SYNC.finalize_kv_state(deps.storage, prop_id, None)?; - Ok(app .response("module_ibc") .add_attribute("action", "finalize")) } + InterchainGovIbcMsg::ProposalResult { + prop_hash: prop_id, + outcome, + } => { + let prop = PROPOSAL_STATE_SYNC.load(deps.storage, prop_id.clone())?.0; + // TODO: store each vote per chain + FINALIZED_PROPOSALS.save( + deps.storage, + prop_id.clone(), + &(prop.clone(), outcome.clone()), + )?; + + // Execute the prop + match prop.action { + ProposalAction::UpdateMembers { members } => { + // If new members exclude self, update members to only be self + if !members.members.contains(&ChainName::new(&env)) { + MEMBERS_STATE_SYNC.save_members(deps.storage, &Members::new(&env))?; + } + MEMBERS_STATE_SYNC.save_members(deps.storage, &members)?; + } + _ => {} + } + Ok(app + .response("module_ibc") + .add_attribute("action", "proposal_result")) + } _ => Err(InterchainGovError::UnauthorizedIbcMessage {}), } } diff --git a/contracts/interchain-gov/src/handlers/query.rs b/contracts/interchain-gov/src/handlers/query.rs index 30bb9b1..f675837 100644 --- a/contracts/interchain-gov/src/handlers/query.rs +++ b/contracts/interchain-gov/src/handlers/query.rs @@ -1,12 +1,14 @@ -use abstract_adapter::objects::chain_name::ChainName; use crate::{ contract::{AdapterResult, InterchainGov}, msg::{ConfigResponse, InterchainGovQueryMsg, MapState, ProposalStateResponse}, - state::PROPOSAL_STATE_SYNC, + state::{MEMBERS_STATE_SYNC, PROPOSAL_STATE_SYNC}, }; +use abstract_adapter::objects::chain_name::ChainName; -use crate::msg::{MembersResponse, ProposalResponse, ProposalsResponse, VoteResponse, VoteResultsResponse}; -use crate::state::{GovernanceVote, MEMBERS, ProposalId, VOTE, VOTE_RESULTS}; +use crate::msg::{ + MembersResponse, ProposalResponse, ProposalsResponse, VoteResponse, VoteResultsResponse, +}; +use crate::state::{GovernanceVote, ProposalId, MEMBERS, VOTE, VOTE_RESULTS}; use cosmwasm_std::{to_json_binary, Binary, Deps, Env, Order, StdResult}; use ibc_sync_state::DataState; @@ -19,9 +21,13 @@ pub fn query_handler( match msg { InterchainGovQueryMsg::Config {} => to_json_binary(&query_config(deps)?), InterchainGovQueryMsg::Members {} => to_json_binary(&query_members(deps)?), - InterchainGovQueryMsg::Proposal { prop_id } => to_json_binary(&query_proposal(deps, prop_id)?), + InterchainGovQueryMsg::Proposal { prop_id } => { + to_json_binary(&query_proposal(deps, prop_id)?) + } InterchainGovQueryMsg::Vote { prop_id } => to_json_binary(&query_vote(deps, env, prop_id)?), - InterchainGovQueryMsg::VoteResults { prop_id } => to_json_binary(&query_vote_results(deps, env, prop_id)?), + InterchainGovQueryMsg::VoteResults { prop_id } => { + to_json_binary(&query_vote_results(deps, env, prop_id)?) + } InterchainGovQueryMsg::ListProposalStates {} => to_json_binary(&query_props_state(deps)?), InterchainGovQueryMsg::Proposals { proposal_ids } => { to_json_binary(&query_proposals(deps, proposal_ids)?) @@ -125,8 +131,8 @@ fn query_proposal(deps: Deps, prop_id: ProposalId) -> AdapterResult AdapterResult { - let members = MEMBERS.load(deps.storage)?; - // TODO: fix + let members = MEMBERS_STATE_SYNC.load_members(deps.storage)?; + Ok(MembersResponse { members }) } @@ -135,24 +141,21 @@ fn query_config(_deps: Deps) -> AdapterResult { } fn query_vote(deps: Deps, env: Env, prop_id: ProposalId) -> StdResult { - let GovernanceVote { - vote, - governance - } = VOTE.load(deps.storage, prop_id.clone())?; + let GovernanceVote { vote, governance } = VOTE.load(deps.storage, prop_id.clone())?; Ok(VoteResponse { vote, governance, prop_id, - chain: ChainName::new(&env) + chain: ChainName::new(&env), }) } fn query_vote_results(deps: Deps, env: Env, prop_id: ProposalId) -> StdResult { - let results = VOTE_RESULTS.prefix(prop_id.clone()).range(deps.storage, None, None, Order::Ascending).collect::)>>>()?; + let results = VOTE_RESULTS + .prefix(prop_id.clone()) + .range(deps.storage, None, None, Order::Ascending) + .collect::)>>>()?; - Ok(crate::msg::VoteResultsResponse { - prop_id, - results - }) + Ok(crate::msg::VoteResultsResponse { prop_id, results }) } diff --git a/contracts/interchain-gov/src/handlers/sudo.rs b/contracts/interchain-gov/src/handlers/sudo.rs index 872cbeb..626a1ec 100644 --- a/contracts/interchain-gov/src/handlers/sudo.rs +++ b/contracts/interchain-gov/src/handlers/sudo.rs @@ -1,16 +1,21 @@ use abstract_adapter::sdk::AbstractResponse; use cosmwasm_schema::cw_serde; -use cosmwasm_std::{Binary, DepsMut, Env, MessageInfo, Order, StdError, StdResult, Storage, to_json_binary, Uint128}; +use cosmwasm_std::{ + to_json_binary, Binary, DepsMut, Env, MessageInfo, Order, StdError, StdResult, Storage, Uint128, +}; use cw_storage_plus::Item; +use neutron_query::icq::IcqInterface; use serde::de::DeserializeOwned; use serde::Serialize; -use neutron_query::icq::IcqInterface; -use crate::{contract::{AdapterResult, InterchainGov}, InterchainGovError, msg::InterchainGovInstantiateMsg, state::{Members, MEMBERS}}; use crate::msg::InterchainGovSudoMsg; -use crate::state::{GOV_VOTE_QUERIES, PENDING_QUERIES, StorageKey, TallyResult}; - - +use crate::state::{StorageKey, TallyResult, GOV_VOTE_QUERIES, PENDING_QUERIES}; +use crate::{ + contract::{AdapterResult, InterchainGov}, + msg::InterchainGovInstantiateMsg, + state::{Members, MEMBERS}, + InterchainGovError, +}; pub fn sudo_handler( deps: DepsMut, @@ -24,18 +29,24 @@ pub fn sudo_handler( let query_details = PENDING_QUERIES.load(deps.storage, query_id)?; PENDING_QUERIES.remove(deps.storage, query_id); - let query_res = app.neutron_icq(deps.as_ref())?.query_registered_query_result(query_id)?; + let query_res = app + .neutron_icq(deps.as_ref())? + .query_registered_query_result(query_id)?; // Map into the proper type - let retyped_kv_results = query_res.kv_results.into_iter().map(|kv| { - let key = kv.key; - let value = kv.value; - neutron_query::neutron_sdk::bindings::types::StorageValue { - storage_prefix: kv.storage_prefix, - key: Binary(key), - value: Binary(value), - } - }).collect::>(); + let retyped_kv_results = query_res + .kv_results + .into_iter() + .map(|kv| { + let key = kv.key; + let value = kv.value; + neutron_query::neutron_sdk::bindings::types::StorageValue { + storage_prefix: kv.storage_prefix, + key: Binary(key), + value: Binary(value), + } + }) + .collect::>(); let gov_props: neutron_query::neutron_sdk::interchain_queries::v045::types::GovernmentProposal = neutron_query::neutron_sdk::interchain_queries::types::KVReconstruct::reconstruct(retyped_kv_results.as_slice()).map_err(|e| InterchainGovError::Std(StdError::generic_err(e.to_string())))?; @@ -56,7 +67,11 @@ pub fn sudo_handler( no_with_veto: tally_result.no_with_veto, }; - GOV_VOTE_QUERIES.save(deps.storage, (query_details.1.clone(), &query_details.0), &Some(tally))?; + GOV_VOTE_QUERIES.save( + deps.storage, + (query_details.1.clone(), &query_details.0), + &Some(tally), + )?; } else { unimplemented!("No proposals found") } diff --git a/contracts/interchain-gov/src/ibc_callbacks/finalize.rs b/contracts/interchain-gov/src/ibc_callbacks/finalize.rs index c6fc81f..0fe3ca0 100644 --- a/contracts/interchain-gov/src/ibc_callbacks/finalize.rs +++ b/contracts/interchain-gov/src/ibc_callbacks/finalize.rs @@ -30,7 +30,7 @@ pub fn finalize_callback( prop_hash: _prop_id, } => { PROPOSAL_STATE_SYNC.apply_ack(deps.storage, proposed_to)?; - + if !PROPOSAL_STATE_SYNC.has_outstanding_acks(deps.storage)? { // finalize my proposal MEMBERS_STATE_SYNC.finalize_members(deps.storage, None)?; diff --git a/contracts/interchain-gov/src/ibc_callbacks/mod.rs b/contracts/interchain-gov/src/ibc_callbacks/mod.rs index 2276776..99fb622 100644 --- a/contracts/interchain-gov/src/ibc_callbacks/mod.rs +++ b/contracts/interchain-gov/src/ibc_callbacks/mod.rs @@ -7,4 +7,6 @@ pub const PROPOSE_CALLBACK_ID: &str = "propose_callback"; pub const FINALIZE_CALLBACK_ID: &str = "finalize_callback"; pub const REGISTER_VOTE_ID: &str = "register_vote"; -pub use self::{proposal::proposal_callback, finalize::finalize_callback, vote_result::vote_result_callback}; +pub use self::{ + finalize::finalize_callback, proposal::proposal_callback, vote_result::vote_result_callback, +}; diff --git a/contracts/interchain-gov/src/ibc_callbacks/proposal.rs b/contracts/interchain-gov/src/ibc_callbacks/proposal.rs index e102fa8..52457ce 100644 --- a/contracts/interchain-gov/src/ibc_callbacks/proposal.rs +++ b/contracts/interchain-gov/src/ibc_callbacks/proposal.rs @@ -23,12 +23,14 @@ pub fn proposal_callback( result: CallbackResult::Execute { result: Ok(_), .. }, } => { let callback_msg: InterchainGovIbcCallbackMsg = from_json(callback_msg)?; + println!("Decoded callback: {:?}", callback_msg); + match callback_msg { InterchainGovIbcCallbackMsg::JoinGov { proposed_to } => { MEMBERS_STATE_SYNC.apply_ack(deps.storage, proposed_to)?; + // finalize my proposal if !MEMBERS_STATE_SYNC.has_outstanding_acks(deps.storage)? { - // finalize my proposal MEMBERS_STATE_SYNC.finalize_members(deps.storage, None)?; } } @@ -50,6 +52,12 @@ pub fn proposal_callback( // return finalize(deps, env, info, app, prop_id); } } + InterchainGovIbcCallbackMsg::ProposalResult { + prop_hash: _, + proposed_to, + } => { + PROPOSAL_STATE_SYNC.apply_ack(deps.storage, proposed_to)?; + } _ => unimplemented!(), } } diff --git a/contracts/interchain-gov/src/ibc_callbacks/vote_result.rs b/contracts/interchain-gov/src/ibc_callbacks/vote_result.rs index 06669a3..8e6bcfe 100644 --- a/contracts/interchain-gov/src/ibc_callbacks/vote_result.rs +++ b/contracts/interchain-gov/src/ibc_callbacks/vote_result.rs @@ -1,11 +1,11 @@ use abstract_adapter::sdk::AbstractResponse; use abstract_adapter::std::ibc::{CallbackResult, IbcResponseMsg}; -use cosmwasm_std::{DepsMut, Env, from_json, MessageInfo, QueryRequest, to_json_string, WasmQuery}; +use cosmwasm_std::{from_json, to_json_string, DepsMut, Env, MessageInfo, QueryRequest, WasmQuery}; use crate::contract::{AdapterResult, InterchainGov}; -use crate::InterchainGovError; use crate::msg::{InterchainGovQueryMsg, VoteResponse}; use crate::state::{GovernanceVote, VOTE_RESULTS}; +use crate::InterchainGovError; /// Get a callback when a proposal is finalized /// TODO: figure out how to abstract this state transition @@ -16,7 +16,6 @@ pub fn vote_result_callback( app: InterchainGov, ibc_msg: IbcResponseMsg, ) -> AdapterResult { - println!("finalize_callback"); match ibc_msg.result.clone() { @@ -29,24 +28,17 @@ pub fn vote_result_callback( // Retrieve the prop id from the original message let prop_id = match query { - QueryRequest::Wasm(wasm) => { - match wasm { - WasmQuery::Smart { - contract_addr, - msg, - } => { - let msg: InterchainGovQueryMsg = from_json(msg)?; - match msg { - InterchainGovQueryMsg::Vote { prop_id } => { - prop_id - }, - _ => unimplemented!("InterchainGovQueryMsg") - } - }, - _ => unimplemented!("WasmQuery") + QueryRequest::Wasm(wasm) => match wasm { + WasmQuery::Smart { contract_addr, msg } => { + let msg: InterchainGovQueryMsg = from_json(msg)?; + match msg { + InterchainGovQueryMsg::Vote { prop_id } => prop_id, + _ => unimplemented!("InterchainGovQueryMsg"), + } } + _ => unimplemented!("WasmQuery"), }, - _ => unimplemented!("QueryRequest") + _ => unimplemented!("QueryRequest"), }; // Get the result of the query @@ -59,34 +51,38 @@ pub fn vote_result_callback( // Update the response from the chain let chain = query_result.chain; - VOTE_RESULTS.update(deps.storage, (prop_id.clone(), &chain), |prev_res| -> Result, InterchainGovError> { - match prev_res { - Some(prev) => { - match prev { - Some(prev) => { - Err(InterchainGovError::ExistingVoteResult { - prop_id: prop_id.clone(), - chain: chain.clone(), - }) - }, + VOTE_RESULTS.update( + deps.storage, + (prop_id.clone(), &chain), + |prev_res| -> Result, InterchainGovError> { + match prev_res { + Some(prev) => match prev { + Some(prev) => Err(InterchainGovError::ExistingVoteResult { + prop_id: prop_id.clone(), + chain: chain.clone(), + }), None => { println!("Vote response added"); - Ok(Some(GovernanceVote::new(query_result.governance, query_result.vote))) + Ok(Some(GovernanceVote::new( + query_result.governance, + query_result.vote, + ))) } - } - }, - None => { - Err(InterchainGovError::UnrequestedVote { + }, + None => Err(InterchainGovError::UnrequestedVote { prop_id: prop_id.clone(), chain: chain.clone(), - }) + }), } - } - })?; - }, - CallbackResult::Execute { initiator_msg, result } => { + }, + )?; + } + CallbackResult::Execute { + initiator_msg, + result, + } => { unreachable!("vote_result Execute callback") - }, + } CallbackResult::FatalError(e) => { println!("Fatal error"); return Err(InterchainGovError::IbcFailed(e)); diff --git a/contracts/interchain-gov/src/lib.rs b/contracts/interchain-gov/src/lib.rs index 2a88d28..25fcfb1 100644 --- a/contracts/interchain-gov/src/lib.rs +++ b/contracts/interchain-gov/src/lib.rs @@ -5,9 +5,8 @@ pub mod error; mod handlers; mod ibc_callbacks; pub mod msg; -pub mod state; pub mod replies; - +pub mod state; pub use contract::interface::InterchainGovInterface; pub use error::InterchainGovError; diff --git a/contracts/interchain-gov/src/msg.rs b/contracts/interchain-gov/src/msg.rs index 6b22a17..70d164f 100644 --- a/contracts/interchain-gov/src/msg.rs +++ b/contracts/interchain-gov/src/msg.rs @@ -1,6 +1,6 @@ use crate::{ contract::InterchainGov, - state::{Members}, + state::{Members, ProposalOutcome}, }; use abstract_adapter::objects::chain_name::ChainName; @@ -8,7 +8,11 @@ use cosmwasm_schema::QueryResponses; use ibc_sync_state::{DataState, StateChange}; // This is used for type safety and re-exporting the contract endpoint structs. -abstract_adapter::adapter_msg_types!(InterchainGov, InterchainGovExecuteMsg, InterchainGovQueryMsg); +abstract_adapter::adapter_msg_types!( + InterchainGov, + InterchainGovExecuteMsg, + InterchainGovQueryMsg +); use crate::state::{Governance, GovernanceVote, Proposal, ProposalId, ProposalMsg, Vote}; /// App instantiate message @@ -46,7 +50,7 @@ pub enum InterchainGovExecuteMsg { prop_id: String, }, RequestGovVoteDetails { - prop_id: String + prop_id: String, }, SetAcceptGovInvite { /// only accept invites for groups with these members @@ -56,10 +60,10 @@ pub enum InterchainGovExecuteMsg { prop_id: String, }, TestAddMembers { - members: Members + members: Members, }, TemporaryRegisterRemoteGovModuleAddrs { - modules: Vec<(ChainName, String)> + modules: Vec<(ChainName, String)>, }, } @@ -82,6 +86,11 @@ pub enum InterchainGovIbcMsg { FinalizeProposal { prop_hash: String, }, + /// Sends the proposal results to the other chains. + ProposalResult { + prop_hash: String, + outcome: ProposalOutcome, + }, } #[non_exhaustive] @@ -98,6 +107,10 @@ pub enum InterchainGovIbcCallbackMsg { prop_hash: String, proposed_to: ChainName, }, + ProposalResult { + prop_hash: String, + proposed_to: ChainName, + }, } /// App query messages @@ -130,22 +143,16 @@ pub enum InterchainGovQueryMsg { }, #[returns(VoteResultsResponse)] - VoteResults { - prop_id: ProposalId, - }, - + VoteResults { prop_id: ProposalId }, } /// App sudo messages #[cosmwasm_schema::cw_serde] pub enum InterchainGovSudoMsg { #[serde(rename = "kv_query_result")] - KVQueryResult { - query_id: u64, - } + KVQueryResult { query_id: u64 }, } - #[cosmwasm_schema::cw_serde] pub struct ConfigResponse {} @@ -191,5 +198,5 @@ pub struct VoteResponse { #[cosmwasm_schema::cw_serde] pub struct VoteResultsResponse { pub prop_id: ProposalId, - pub results: Vec<(ChainName, Option)> -} \ No newline at end of file + pub results: Vec<(ChainName, Option)>, +} diff --git a/contracts/interchain-gov/src/replies/icq.rs b/contracts/interchain-gov/src/replies/icq.rs index 5c098c3..b501747 100644 --- a/contracts/interchain-gov/src/replies/icq.rs +++ b/contracts/interchain-gov/src/replies/icq.rs @@ -1,12 +1,13 @@ -use abstract_adapter::sdk::AbstractResponse; -use cosmwasm_std::{DepsMut, Env, from_json, Reply, StdError, StdResult, SubMsgResponse, SubMsgResult}; use crate::contract::{AdapterResult, InterchainGov}; +use crate::state::{PENDING_QUERIES, PENDING_REPLIES}; use crate::InterchainGovError; +use abstract_adapter::sdk::AbstractResponse; +use cosmwasm_std::{ + from_json, DepsMut, Env, Reply, StdError, StdResult, SubMsgResponse, SubMsgResult, +}; use neutron_query::neutron_sdk::bindings::msg::MsgRegisterInterchainQueryResponse; -use crate::state::{PENDING_QUERIES, PENDING_REPLIES}; pub fn icq_reply(deps: DepsMut, _env: Env, app: InterchainGov, reply: Reply) -> AdapterResult { - println!("icq_reply: {:?}", reply); // remove the pending reply @@ -24,12 +25,14 @@ pub fn icq_reply(deps: DepsMut, _env: Env, app: InterchainGov, reply: Reply) -> } pub fn get_query_id(msg_result: SubMsgResult) -> StdResult { - let res: MsgRegisterInterchainQueryResponse = from_json(msg_result - .into_result() - .map_err(StdError::generic_err)? - .data - .ok_or_else(|| StdError::generic_err("no result"))? - .as_slice())?; + let res: MsgRegisterInterchainQueryResponse = from_json( + msg_result + .into_result() + .map_err(StdError::generic_err)? + .data + .ok_or_else(|| StdError::generic_err("no result"))? + .as_slice(), + )?; Ok(res.id) -} \ No newline at end of file +} diff --git a/contracts/interchain-gov/src/replies/mod.rs b/contracts/interchain-gov/src/replies/mod.rs index e2d518b..f258d9f 100644 --- a/contracts/interchain-gov/src/replies/mod.rs +++ b/contracts/interchain-gov/src/replies/mod.rs @@ -1,4 +1,3 @@ mod icq; - -pub use self::{icq::icq_reply}; +pub use self::icq::icq_reply; diff --git a/contracts/interchain-gov/src/state.rs b/contracts/interchain-gov/src/state.rs index 7b6a7c2..fbdc9bf 100644 --- a/contracts/interchain-gov/src/state.rs +++ b/contracts/interchain-gov/src/state.rs @@ -37,10 +37,13 @@ pub const VOTE: Map = Map::new("vote"); pub const VOTES: Map = Map::new("votes"); /// Remote vote results, None = requested -pub const VOTE_RESULTS: Map<(ProposalId, &ChainName), Option> = Map::new("vote_results"); +pub const VOTE_RESULTS: Map<(ProposalId, &ChainName), Option> = + Map::new("vote_results"); /// Pending vote queries -pub const GOV_VOTE_QUERIES: Map<(ProposalId, &ChainName), Option> = Map::new("pending_queries"); -pub const TEMP_REMOTE_GOV_MODULE_ADDRS: Map<&ChainName, String> = Map::new("temp_remote_gov_module_addrs"); +pub const GOV_VOTE_QUERIES: Map<(ProposalId, &ChainName), Option> = + Map::new("pending_queries"); +pub const TEMP_REMOTE_GOV_MODULE_ADDRS: Map<&ChainName, String> = + Map::new("temp_remote_gov_module_addrs"); /// Map queryid -> (chain, prop_id) pub const PENDING_REPLIES: Map = Map::new("pending_replies"); @@ -50,6 +53,9 @@ const PROPOSALS: Map = Map::new("props"); pub const PROPOSAL_STATE_SYNC: MapStateSyncController<'_, ProposalId, (Proposal, Vote)> = MapStateSyncController::new(PROPOSALS); +pub const FINALIZED_PROPOSALS: Map = + Map::new("finalized_props"); + /// Local members to local data status /// Remote member statuses @@ -80,6 +86,10 @@ pub mod members_sync_state { self.members.load(storage) } + pub fn save_members(&self, storage: &mut dyn Storage, members: &Members) -> StdResult<()> { + self.members.save(storage, members) + } + pub fn external_members( &self, storage: &dyn Storage, @@ -155,9 +165,9 @@ pub mod members_sync_state { // Uses initialized / proposed state None members: Option, ) -> SyncStateResult<()> { - let members = { + let (members, set) = { if let Some(members) = members { - members + (members, true) } else { let members: Result = match self.load_state_change(storage)? { StateChange::Proposal(members) => Ok(from_json(&members)?), @@ -166,14 +176,14 @@ pub mod members_sync_state { state: "Backup".to_string(), }), }; - members? + (members?, false) } }; self.members.save(storage, &members)?; self.item_state_controller - .finalize_item_state(storage, MEMBERS_KEY.to_string())?; + .finalize_item_state(storage, MEMBERS_KEY.to_string(), set)?; Ok(()) } @@ -307,36 +317,38 @@ pub enum Vote { Yes, No, NoVote, - Ratio(u64, u64) } #[non_exhaustive] #[cw_serde] pub enum Governance { CosmosSDK { - proposal_id: u64 + proposal_id: u64, }, DaoDao { dao_address: String, - proposal_id: u64 + proposal_id: u64, }, - Manual {} + Manual {}, } #[cw_serde] pub struct GovernanceVote { pub vote: Vote, - pub governance: Governance + pub governance: Governance, } impl GovernanceVote { pub fn new(governance: Governance, vote: Vote) -> Self { - GovernanceVote { - vote, - governance - } + GovernanceVote { vote, governance } } } +#[cw_serde] +pub struct ProposalOutcome { + pub passed: bool, + pub votes_for: u8, + pub votes_against: u8, +} /// Tally result from the other chain #[cw_serde] @@ -347,7 +359,6 @@ pub struct TallyResult { pub no_with_veto: Uint128, } - impl Vote { pub fn new(vote: bool) -> Self { if vote { diff --git a/contracts/interchain-gov/tests/integration.rs b/contracts/interchain-gov/tests/integration.rs index 7265e45..49c00c2 100644 --- a/contracts/interchain-gov/tests/integration.rs +++ b/contracts/interchain-gov/tests/integration.rs @@ -14,13 +14,15 @@ use abstract_adapter::std::{adapter, adapter::AdapterRequestMsg, objects::namesp use abstract_client::{AbstractClient, Account, Application, Environment, Publisher}; use abstract_cw_orch_polytone::Polytone; // Use prelude to get all the necessary imports +use cw_orch_interchain::{ChannelCreator, MockBech32InterchainEnv, Starship}; +use cw_orch_interchain::InterchainEnv; use cw_orch::{anyhow, prelude::*}; // use cw_orch_interchain::{prelude::*}; use speculoos::prelude::*; use abstract_interchain_tests::setup::ibc_connect_polytone_and_abstract; -use cw_orch::mock::cw_multi_test::{AppResponse}; +use cw_orch::mock::cw_multi_test::AppResponse; use cw_orch::tokio::runtime::Runtime; use cw_utils::Expiration; use interchain_gov::state::{ProposalAction, ProposalId, ProposalMsg}; @@ -28,8 +30,10 @@ use interchain_gov::state::{ProposalAction, ProposalId, ProposalMsg}; const A_CHAIN_ID: &str = "neutron-1"; const B_CHAIN_ID: &str = "juno-1"; +const C_CHAIN_ID: &str = "stargaze-1"; const A_CHAIN_ADDR: &str = "neutron18k2uq7srsr8lwrae6zr0qahpn29rsp7tu2m2ea"; const B_CHAIN_ADDR: &str = "juno18k2uq7srsr8lwrae6zr0qahpn29rsp7tw83nyx"; +const C_CHAIN_ADDR: &str = "stars14cl2dthqamgucg9sfvv4relp3aa83e40pxn500"; struct TestEnv { // FEEDBACK: the trait `Clone` is not implemented for `Publisher` @@ -56,7 +60,10 @@ impl TestEnv { publisher.publish_adapter::>( InterchainGovInstantiateMsg { accept_proposal_from_gov: Members { - members: vec![ChainName::from_chain_id(A_CHAIN_ID), ChainName::from_chain_id(B_CHAIN_ID)], + members: vec![ + ChainName::from_chain_id(A_CHAIN_ID), + ChainName::from_chain_id(B_CHAIN_ID), + ], }, }, )?; @@ -119,7 +126,11 @@ impl Environment for TestEnv { const TEST_PROP_LEN: u64 = 1000; impl TestEnv { - pub fn execute_gov_for(&self, request: InterchainGovExecuteMsg, for_acc: Option<&Account>) -> anyhow::Result { + pub fn execute_gov_for( + &self, + request: InterchainGovExecuteMsg, + for_acc: Option<&Account>, + ) -> anyhow::Result { // let acc = for_acc.unwrap_or(self.gov.account()); // Ok(self.gov.call_as(&acc.manager()?).execute(&AdapterRequestMsg { // // Ok(self.gov.call_as(&acc.manager()?).execute(&AdapterRequestMsg { @@ -127,10 +138,15 @@ impl TestEnv { // request, // }.into(), None)?) let acc = for_acc.unwrap_or(self.gov.account()); - Ok(acc.as_ref().manager.execute_on_module(MY_ADAPTER_ID, adapter::ExecuteMsg::::Module(adapter::AdapterRequestMsg { - proxy_address: None, - request - }))?) + Ok(acc.as_ref().manager.execute_on_module( + MY_ADAPTER_ID, + adapter::ExecuteMsg::::Module( + adapter::AdapterRequestMsg { + proxy_address: None, + request, + }, + ), + )?) // Ok(self.gov.execute(&AdapterRequestMsg { // // Ok(self.gov.call_as(&acc.manager()?).execute(&AdapterRequestMsg { // proxy_address: Some(acc.proxy()?.to_string()), @@ -139,13 +155,21 @@ impl TestEnv { } pub fn execute_gov(&self, request: InterchainGovExecuteMsg) -> anyhow::Result { - self.execute_gov_for(request, None) + self.execute_gov_for(request, None) } - fn propose_proposal(&self, title: &str, actions: Vec) -> anyhow::Result<(Env::Response, ProposalId)> { - let test_prop = test_proposal(title, actions, self.environment().block_info()?.height + TEST_PROP_LEN); + fn propose_proposal( + &self, + title: &str, + action: ProposalAction, + ) -> anyhow::Result<(Env::Response, ProposalId)> { + let test_prop = test_proposal( + title, + action, + self.environment().block_info()?.height + TEST_PROP_LEN, + ); let res = self.execute_gov(InterchainGovExecuteMsg::Propose { - proposal: test_prop.clone() + proposal: test_prop.clone(), })?; let id: String = test_prop.hash(); @@ -159,6 +183,28 @@ impl TestEnv { Ok((res, prop_id)) } + // Propose a member change + fn propose_first_member_proposal( + &self, + title: &str, + action: ProposalAction, + ) -> anyhow::Result<(Env::Response, ProposalId)> { + let test_prop = test_proposal( + title, + action, + self.environment().block_info()?.height + TEST_PROP_LEN, + ); + let res = self.execute_gov(InterchainGovExecuteMsg::Propose { + proposal: test_prop.clone(), + })?; + + let id: String = test_prop.hash(); + + let props = self.gov.list_proposals()?.proposals; + let prop_id = props.into_iter().find(|p| p.prop_id == id).unwrap().prop_id; + Ok((res, prop_id)) + } + fn finalize_proposal(&self, prop_id: ProposalId) -> anyhow::Result { let res = self.execute_gov(InterchainGovExecuteMsg::Finalize { prop_id })?; @@ -175,46 +221,35 @@ impl TestEnv { Ok(()) } - fn test_register_gov_modules(&self, govs: Vec<(ChainName, InterchainGovInterface)>) -> anyhow::Result<()> { - let modules = govs.into_iter().map(|(chain_name, gov)| { - (chain_name, gov.address().unwrap().to_string()) - }).collect(); - self.execute_gov(InterchainGovExecuteMsg::TemporaryRegisterRemoteGovModuleAddrs { - modules - })?; + fn test_register_gov_modules( + &self, + govs: Vec<(ChainName, InterchainGovInterface)>, + ) -> anyhow::Result<()> { + let modules = govs + .into_iter() + .map(|(chain_name, gov)| (chain_name, gov.address().unwrap().to_string())) + .collect(); + self.execute_gov( + InterchainGovExecuteMsg::TemporaryRegisterRemoteGovModuleAddrs { modules }, + )?; Ok(()) } } -fn test_proposal(title: impl Into, actions: Vec, exp_height: u64) -> ProposalMsg { +fn test_proposal( + title: impl Into, + action: ProposalAction, + exp_height: u64, +) -> ProposalMsg { ProposalMsg { title: title.into(), description: "This is a test proposal".to_string(), min_voting_period: None, expiration: Expiration::AtHeight(exp_height), - action: ProposalAction::Signal, + action, } } -#[test] -fn successful_install() -> anyhow::Result<()> { - // let mock = MockBech32::new("mock"); - // let env = TestEnv::setup(mock)?; - // let adapter = env.gov.clone(); - - // let config = adapter.config()?; - // assert_eq!(config, ConfigResponse {}); - - // // Check single member - // let members = adapter.members()?; - // assert_eq!(members.members.len(), 1); - // // check that the single member is the current chain - // let current_chain_id = env.chain_name(); - // assert_that!(members.members[0]).is_equal_to(current_chain_id); - - Ok(()) -} - mod propose { use super::*; @@ -245,15 +280,15 @@ mod propose { })?; // Create proposal - let (res, prop_id) = a_env.propose_proposal("happy_propose", vec![])?; + let (res, prop_id) = a_env.propose_proposal("happy_propose", ProposalAction::Signal)?; interchain.wait_ibc(A_CHAIN_ID, res)?; let proposals = a_gov.list_proposal_states()?; - assert_that!(proposals.state).has_length(0); + assert_that!(proposals.state.len()).is_equal_to(0); let proposals = b_gov.list_proposal_states()?; - assert_that!(proposals.state).has_length(1); + assert_that!(proposals.state.len()).is_equal_to(1); // check the state. // should be proposed on A and pending on B @@ -265,17 +300,14 @@ mod propose { } mod query_vote_results { - use interchain_gov::state::{Governance, Vote}; use super::*; + use interchain_gov::state::{Governance, Vote}; #[test] fn happy_query() -> anyhow::Result<()> { let interchain = MockBech32InterchainEnv::new(vec![ (A_CHAIN_ID, A_CHAIN_ADDR), - ( - B_CHAIN_ID, - B_CHAIN_ADDR - ), + (B_CHAIN_ID, B_CHAIN_ADDR), ]); let a_env = TestEnv::setup(interchain.chain(A_CHAIN_ID)?)?; @@ -286,7 +318,6 @@ mod query_vote_results { ibc_connect_polytone_and_abstract(&interchain, A_CHAIN_ID, B_CHAIN_ID)?; a_env.test_register_gov_modules(vec![(b_env.chain_name(), b_env.gov.clone())])?; - let a_gov = a_env.gov.clone(); let b_gov = b_env.gov.clone(); @@ -299,7 +330,7 @@ mod query_vote_results { })?; // Create proposal - let (res, prop_id) = a_env.propose_proposal("happy_propose", vec![])?; + let (res, prop_id) = a_env.propose_proposal("happy_propose", ProposalAction::Signal)?; interchain.wait_ibc(A_CHAIN_ID, res)?; // Finalize a proposal let res = a_env.finalize_proposal(prop_id.clone())?; @@ -336,12 +367,10 @@ mod query_vote_results { let vote_results = a_gov.vote_results(prop_id.clone())?; println!("Vote results: {:?}", vote_results); - Ok(()) } } - mod finalize { use super::*; @@ -372,7 +401,7 @@ mod finalize { })?; // Propose a proposal - let (res, prop_id) = a_env.propose_proposal("happy_finalize", vec![])?; + let (res, prop_id) = a_env.propose_proposal("happy_finalize", ProposalAction::Signal)?; interchain.wait_ibc(A_CHAIN_ID, res)?; a_env.assert_prop_state(prop_id.clone(), None)?; @@ -415,7 +444,7 @@ fn starship_test() -> anyhow::Result<()> { })?; // Propose a proposal - let (res, prop_id) = a_env.propose_proposal("happy_finalize", vec![])?; + let (res, prop_id) = a_env.propose_proposal("happy_finalize", ProposalAction::Signal)?; interchain.wait_ibc(A_CHAIN_ID, res)?; a_env.assert_prop_state(prop_id.clone(), Some(DataState::Proposed))?; @@ -428,15 +457,24 @@ fn starship_test() -> anyhow::Result<()> { a_env.assert_prop_state(prop_id.clone(), None)?; b_env.assert_prop_state(prop_id, None)?; -Ok(()) + Ok(()) } mod members { + use std::env; + + use abstract_interchain_tests::setup::ibc_connect_abstract; + use cw_orch_interchain::MockBech32InterchainEnv; + use interchain_gov::{msg::InterchainGovExecuteMsgFns, state::Vote}; + use super::*; #[test] fn happy_member_add() -> anyhow::Result<()> { + env::set_var("RUST_LOG", "debug"); + env_logger::init(); + let interchain = MockBech32InterchainEnv::new(vec![ (A_CHAIN_ID, A_CHAIN_ADDR), (B_CHAIN_ID, B_CHAIN_ADDR), @@ -449,31 +487,136 @@ mod members { b_env.enable_ibc()?; ibc_connect_polytone_and_abstract(&interchain, A_CHAIN_ID, B_CHAIN_ID)?; - let _a_gov = a_env.gov.clone(); - let _b_gov = b_env.gov.clone(); + let a_gov = a_env.gov.clone(); + let b_gov = b_env.gov.clone(); - a_env.execute_gov(InterchainGovExecuteMsg::TestAddMembers { - members: vec![b_env.chain_name(), a_env.chain_name()].into(), - })?; + // Propose a proposal + let (res, prop_id) = a_env.propose_first_member_proposal( + "happy_finalize", + ProposalAction::UpdateMembers { + members: vec![b_env.chain_name(), a_env.chain_name()].into(), + }, + )?; + let res = interchain.wait_ibc(A_CHAIN_ID, res)?; + dbg!(&res.packets[0].outcome); + a_env.assert_prop_state(prop_id.clone(), None)?; + b_env.assert_prop_state(prop_id.clone(), None)?; - b_env.execute_gov(InterchainGovExecuteMsg::TestAddMembers { - members: vec![a_env.chain_name(), b_env.chain_name()].into(), - })?; + let a_members = dbg!(a_gov.members()?); + assert_eq!(a_members.members.members.len(), 2); + let b_members = dbg!(b_gov.members()?); + assert_eq!(b_members.members.members.len(), 2); + Ok(()) + } + + #[test] + fn happy_member_add_3() -> anyhow::Result<()> { + env::set_var("RUST_LOG", "info"); + env_logger::init(); + + let interchain = MockBech32InterchainEnv::new(vec![ + (A_CHAIN_ID, A_CHAIN_ADDR), + (B_CHAIN_ID, B_CHAIN_ADDR), + (C_CHAIN_ID, C_CHAIN_ADDR), + ]); + use cw_orch_interchain::InterchainEnv; + let a_env = TestEnv::setup(interchain.chain(A_CHAIN_ID)?)?; + let b_env = TestEnv::setup(interchain.chain(B_CHAIN_ID)?)?; + let c_env = TestEnv::setup(interchain.chain(C_CHAIN_ID)?)?; + + a_env.enable_ibc()?; + b_env.enable_ibc()?; + c_env.enable_ibc()?; + + a_env.test_register_gov_modules(vec![(b_env.chain_name(), b_env.gov.clone())])?; + + // ibc_connect_polytone_and_abstract(&interchain, A_CHAIN_ID, B_CHAIN_ID)?; + // ibc_connect_polytone_and_abstract(&interchain, A_CHAIN_ID, C_CHAIN_ID)?; + // ibc_connect_polytone_and_abstract(&interchain, B_CHAIN_ID, A_CHAIN_ID)?; + // ibc_connect_polytone_and_abstract(&interchain, B_CHAIN_ID, C_CHAIN_ID)?; + // ibc_connect_polytone_and_abstract(&interchain, C_CHAIN_ID, A_CHAIN_ID)?; + // ibc_connect_polytone_and_abstract(&interchain, C_CHAIN_ID, B_CHAIN_ID)?; + + ibc_connect_abstract(&interchain, A_CHAIN_ID, B_CHAIN_ID)?; + ibc_connect_abstract(&interchain, A_CHAIN_ID, C_CHAIN_ID)?; + ibc_connect_abstract(&interchain, B_CHAIN_ID, A_CHAIN_ID)?; + ibc_connect_abstract(&interchain, B_CHAIN_ID, C_CHAIN_ID)?; + ibc_connect_abstract(&interchain, C_CHAIN_ID, A_CHAIN_ID)?; + ibc_connect_abstract(&interchain, C_CHAIN_ID, B_CHAIN_ID)?; + + + let a_gov = a_env.gov.clone(); + let b_gov = b_env.gov.clone(); + let c_gov = c_env.gov.clone(); // Propose a proposal - let (res, prop_id) = a_env.propose_proposal("happy_finalize", vec![])?; - interchain.wait_ibc(A_CHAIN_ID, res)?; + let (res, prop_id) = a_env.propose_first_member_proposal( + "happy_finalize", + ProposalAction::UpdateMembers { + members: vec![b_env.chain_name(), a_env.chain_name()].into(), + }, + )?; + let res = interchain.wait_ibc(A_CHAIN_ID, res)?; + dbg!(&res.packets[0].outcome); + + let a_members = dbg!(a_gov.members()?); + assert_eq!(a_members.members.members.len(), 2); + let b_members = dbg!(b_gov.members()?); + assert_eq!(b_members.members.members.len(), 2); + + a_env.assert_prop_state(prop_id.clone(), None)?; + b_env.assert_prop_state(prop_id.clone(), None)?; + + // A and B are friends now. + // Propose to add C + + let (res, prop_id) = a_env.propose_proposal( + "happy_finalize 2", + ProposalAction::UpdateMembers { + members: vec![b_env.chain_name(), a_env.chain_name(), c_env.chain_name()].into(), + }, + )?; + + let res = interchain.wait_ibc(A_CHAIN_ID, res)?; + dbg!(&res.packets[0].outcome); a_env.assert_prop_state(prop_id.clone(), None)?; b_env.assert_prop_state(prop_id.clone(), Some(DataState::Proposed))?; + + let a = a_gov.finalize(prop_id.clone())?; + let res = interchain.wait_ibc(A_CHAIN_ID, a)?; + dbg!(&res.packets[0].outcome); - // Finalize a proposal - let res = a_env.finalize_proposal(prop_id.clone())?; - let _analysis = interchain.wait_ibc(A_CHAIN_ID, res)?; - // TODO: this errs a_env.assert_prop_state(prop_id.clone(), None)?; - b_env.assert_prop_state(prop_id, None)?; + b_env.assert_prop_state(prop_id.clone(), None)?; + c_env.assert_prop_state(prop_id.clone(), None)?; + + // find proposal + let prop = a_gov.proposal(prop_id.clone())?; + assert_eq!(prop.prop_id, prop_id.clone()); + let prop = b_gov.proposal(prop_id.clone())?; + assert_eq!(prop.prop_id, prop_id.clone()); + + a_gov.vote_proposal(interchain_gov::state::Governance::Manual { }, prop_id.clone(), Vote::Yes)?; + b_gov.vote_proposal(interchain_gov::state::Governance::Manual { }, prop_id.clone(), Vote::Yes)?; + + // Wait the test blocks after voting + a_env.wait_blocks(TEST_PROP_LEN + 1)?; + b_env.wait_blocks(TEST_PROP_LEN + 1)?; + c_env.wait_blocks(TEST_PROP_LEN + 1)?; + + let res = a_gov.request_vote_results(prop_id.clone())?; + let res = interchain.wait_ibc(A_CHAIN_ID, res)?; + dbg!(&res.packets[0].outcome); + + // a_gov.execute_proposal(prop_id.clone())?; + let a_members = dbg!(a_gov.members()?); + assert_eq!(a_members.members.members.len(), 3); + // let b_members = dbg!(b_gov.members()?); + // assert_eq!(b_members.members.members.len(), 3); + // let c_members = dbg!(c_gov.members()?); + // assert_eq!(c_members.members.members.len(), 3); Ok(()) } } diff --git a/packages/ibc-sync-state/src/item.rs b/packages/ibc-sync-state/src/item.rs index c4afab0..aac68dc 100644 --- a/packages/ibc-sync-state/src/item.rs +++ b/packages/ibc-sync-state/src/item.rs @@ -99,7 +99,8 @@ impl ItemStateSyncController { &self, storage: &mut dyn Storage, key: impl Into, - ) -> SyncStateResult { + set: bool, + ) -> SyncStateResult<()> { let key = key.into(); // remove any of the states if let Some(change) = self @@ -108,16 +109,18 @@ impl ItemStateSyncController { { self.state_status_map .remove(storage, (key.clone(), DataState::Initiated.to_num())); - return Ok(change); + return Ok(()); } else if let Some(change) = self .state_status_map .may_load(storage, (key.clone(), DataState::Proposed.to_num()))? { self.state_status_map .remove(storage, (key.clone(), DataState::Proposed.to_num())); - return Ok(change); - } else { + return Ok(()); + } else if !set { return Err(SyncStateError::NoProposedState); + } else { + Ok(()) } } } diff --git a/packages/neutron-query/src/gov.rs b/packages/neutron-query/src/gov.rs index 301e931..3d279cd 100644 --- a/packages/neutron-query/src/gov.rs +++ b/packages/neutron-query/src/gov.rs @@ -1,5 +1,5 @@ use cosmos_anybuf::types::neutron::interchainqueries::KVKey; -use cosmwasm_std::{StdResult}; +use cosmwasm_std::StdResult; /// Key for Proposals in the **gov** module's storage /// @@ -24,11 +24,11 @@ pub fn create_gov_proposal_keys(proposals_ids: Vec) -> StdResult for id in proposals_ids { let kv_key = KVKey { path: GOV_STORE_KEY.to_string(), - key: create_gov_proposal_key(id)? + key: create_gov_proposal_key(id)?, }; kv_keys.push(kv_key) } Ok(kv_keys) -} \ No newline at end of file +} diff --git a/packages/neutron-query/src/icq.rs b/packages/neutron-query/src/icq.rs index ea39869..dfdc6ab 100644 --- a/packages/neutron-query/src/icq.rs +++ b/packages/neutron-query/src/icq.rs @@ -1,20 +1,15 @@ //! # Neutron Interchain Query (ICQ) +use crate::{QueryType, ICQ_PROTOCOL}; use abstract_adapter::objects::chain_name::ChainName; use abstract_adapter::objects::UncheckedChannelEntry; use abstract_adapter::std::AbstractError; -use abstract_sdk::{AbstractSdkError, AbstractSdkResult, TransferInterface}; use abstract_sdk::features::AccountIdentification; -use cosmos_anybuf::{ - interfaces::InterChainQueries, - neutron::Neutron - - , -}; +use abstract_sdk::{AbstractSdkError, AbstractSdkResult, TransferInterface}; use cosmos_anybuf::interfaces::TransactionFilterItem; use cosmos_anybuf::types::neutron::interchainqueries::{KVKey, QueryResult, RegisteredQuery}; +use cosmos_anybuf::{interfaces::InterChainQueries, neutron::Neutron}; use cosmwasm_schema::schemars::_serde_json::to_string; use cosmwasm_std::{Addr, CosmosMsg, Deps, StdError}; -use crate::{ICQ_PROTOCOL, QueryType}; /// An interface to the Neutron Interchain Query Module pub trait IcqInterface: AccountIdentification + TransferInterface { @@ -35,10 +30,7 @@ pub trait IcqInterface: AccountIdentification + TransferInterface { &'a self, deps: cosmwasm_std::Deps<'a>, ) -> AbstractSdkResult> { - Ok(NeutronIcq { - base: self, - deps, - }) + Ok(NeutronIcq { base: self, deps }) } } @@ -59,7 +51,6 @@ impl IcqInterface for T where T: AccountIdentification + TransferInterface {} // } // } - /// This struct provides methods to grant fee allowances and interact with the feegrant module. /// /// # Example @@ -79,12 +70,14 @@ pub struct NeutronIcq<'a, T: AccountIdentification + TransferInterface> { } impl<'a, T: AccountIdentification + TransferInterface> NeutronIcq<'a, T> { - - pub fn connection_id( - &self, - chain: ChainName - ) -> AbstractSdkResult { - self.base.ans_host(self.deps)?.query_channel(&self.deps.querier, &UncheckedChannelEntry::new(chain.as_str(), ICQ_PROTOCOL).check()?).map_err(|e| AbstractSdkError::Abstract(AbstractError::AnsHostError(e))) + pub fn connection_id(&self, chain: ChainName) -> AbstractSdkResult { + self.base + .ans_host(self.deps)? + .query_channel( + &self.deps.querier, + &UncheckedChannelEntry::new(chain.as_str(), ICQ_PROTOCOL).check()?, + ) + .map_err(|e| AbstractSdkError::Abstract(AbstractError::AnsHostError(e))) } pub fn register_interchain_query( @@ -93,13 +86,19 @@ impl<'a, T: AccountIdentification + TransferInterface> NeutronIcq<'a, T> { chain: ChainName, query_type: QueryType, keys: Vec, - transactions_filter: Vec , + transactions_filter: Vec, update_period: u64, ) -> AbstractSdkResult { let connection_id = self.connection_id(chain)?; - let register_msg = Neutron::register_interchain_query(sender, query_type.to_string(), keys, to_string(&transactions_filter) - .map_err(|e| StdError::generic_err(e.to_string()))?, connection_id, update_period); + let register_msg = Neutron::register_interchain_query( + sender, + query_type.to_string(), + keys, + to_string(&transactions_filter).map_err(|e| StdError::generic_err(e.to_string()))?, + connection_id, + update_period, + ); Ok(register_msg) } @@ -109,22 +108,19 @@ impl<'a, T: AccountIdentification + TransferInterface> NeutronIcq<'a, T> { /// # Arguments /// /// * `query_id` - The id of the query - pub fn remove_interchain_query(&self, sender: &Addr, query_id: u64) -> AbstractSdkResult { + pub fn remove_interchain_query( + &self, + sender: &Addr, + query_id: u64, + ) -> AbstractSdkResult { Ok(Neutron::remove_interchain_query(sender, query_id)) } - - pub fn query_registered_query( - &self, - query_id: u64, - ) -> AbstractSdkResult { + pub fn query_registered_query(&self, query_id: u64) -> AbstractSdkResult { Ok(Neutron::query_registered_query(&self.deps.querier, query_id)?.registered_query) } - pub fn query_registered_query_result( - &self, - query_id: u64 - ) -> AbstractSdkResult { + pub fn query_registered_query_result(&self, query_id: u64) -> AbstractSdkResult { Ok(Neutron::query_registered_query_result(&self.deps.querier, query_id)?.result) } } diff --git a/packages/neutron-query/src/lib.rs b/packages/neutron-query/src/lib.rs index b2d389c..99bd2e7 100644 --- a/packages/neutron-query/src/lib.rs +++ b/packages/neutron-query/src/lib.rs @@ -1,7 +1,7 @@ use cosmwasm_schema::cw_serde; -pub mod icq; pub mod gov; +pub mod icq; pub use neutron_sdk; @@ -26,4 +26,4 @@ impl ToString for QueryType { QueryType::TX => "tx".to_string(), } } -} \ No newline at end of file +}