Skip to content

Commit

Permalink
Add support for message forwarding from proposal executes
Browse files Browse the repository at this point in the history
  • Loading branch information
ismellike committed Sep 6, 2024
1 parent a417f9d commit 31c5fcc
Show file tree
Hide file tree
Showing 15 changed files with 136 additions and 102 deletions.
36 changes: 26 additions & 10 deletions contracts/dao-dao-core/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
use cosmwasm_std::entry_point;
use cosmwasm_std::{
from_json, to_json_binary, Addr, Binary, CosmosMsg, Deps, DepsMut, Empty, Env, MessageInfo,
Order, Reply, Response, StdError, StdResult, SubMsg, WasmMsg,
Order, Reply, Response, StdError, StdResult, SubMsg, SubMsgResult, WasmMsg,
};
use cw2::{get_contract_version, set_contract_version, ContractVersion};
use cw_paginate_storage::{paginate_map, paginate_map_keys, paginate_map_values};
Expand All @@ -15,7 +15,7 @@ use dao_interface::{
GetItemResponse, PauseInfoResponse, ProposalModuleCountResponse, SubDao,
},
state::{
Admin, Config, ModuleInstantiateCallback, ModuleInstantiateInfo, ProposalModule,
Admin, CallbackMessages, Config, ModuleInstantiateInfo, ProposalModule,
ProposalModuleStatus,
},
voting,
Expand All @@ -30,9 +30,10 @@ use crate::state::{
pub(crate) const CONTRACT_NAME: &str = "crates.io:dao-dao-core";
pub(crate) const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");

const PROPOSAL_MODULE_REPLY_ID: u64 = 0;
const PROPOSAL_MODULE_INSTANTIATE_REPLY_ID: u64 = 0;
const VOTE_MODULE_INSTANTIATE_REPLY_ID: u64 = 1;
const VOTE_MODULE_UPDATE_REPLY_ID: u64 = 2;
const PROPOSAL_MODULE_EXECUTE_REPLY_ID: u64 = 3;

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn instantiate(
Expand Down Expand Up @@ -71,7 +72,7 @@ pub fn instantiate(
.proposal_modules_instantiate_info
.into_iter()
.map(|info| info.into_wasm_msg(env.contract.address.clone()))
.map(|wasm| SubMsg::reply_on_success(wasm, PROPOSAL_MODULE_REPLY_ID))
.map(|wasm| SubMsg::reply_on_success(wasm, PROPOSAL_MODULE_INSTANTIATE_REPLY_ID))
.collect();
if proposal_module_msgs.is_empty() {
return Err(ContractError::NoActiveProposalModules {});
Expand Down Expand Up @@ -228,7 +229,10 @@ pub fn execute_proposal_hook(

Ok(Response::default()
.add_attribute("action", "execute_proposal_hook")
.add_messages(msgs))
.add_submessages(
msgs.into_iter()
.map(|msg| SubMsg::reply_on_success(msg, PROPOSAL_MODULE_EXECUTE_REPLY_ID)),
))
}

pub fn execute_nominate_admin(
Expand Down Expand Up @@ -392,7 +396,7 @@ pub fn execute_update_proposal_modules(
let to_add: Vec<SubMsg<Empty>> = to_add
.into_iter()
.map(|info| info.into_wasm_msg(env.contract.address.clone()))
.map(|wasm| SubMsg::reply_on_success(wasm, PROPOSAL_MODULE_REPLY_ID))
.map(|wasm| SubMsg::reply_on_success(wasm, PROPOSAL_MODULE_INSTANTIATE_REPLY_ID))
.collect();

Ok(Response::default()
Expand Down Expand Up @@ -952,7 +956,7 @@ pub fn migrate(deps: DepsMut, env: Env, msg: MigrateMsg) -> Result<Response, Con
#[cfg_attr(not(feature = "library"), entry_point)]
pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result<Response, ContractError> {
match msg.id {
PROPOSAL_MODULE_REPLY_ID => {
PROPOSAL_MODULE_INSTANTIATE_REPLY_ID => {
let res = parse_reply_instantiate_data(msg)?;
let prop_module_addr = deps.api.addr_validate(&res.contract_address)?;
let total_module_count = TOTAL_PROPOSAL_MODULE_COUNT.load(deps.storage)?;
Expand All @@ -973,7 +977,7 @@ pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result<Response, ContractE

// Check for module instantiation callbacks
let callback_msgs = match res.data {
Some(data) => from_json::<ModuleInstantiateCallback>(&data)
Some(data) => from_json::<CallbackMessages>(&data)
.map(|m| m.msgs)
.unwrap_or_else(|_| vec![]),
None => vec![],
Expand All @@ -983,7 +987,6 @@ pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result<Response, ContractE
.add_attribute("prop_module".to_string(), res.contract_address)
.add_messages(callback_msgs))
}

VOTE_MODULE_INSTANTIATE_REPLY_ID => {
let res = parse_reply_instantiate_data(msg)?;
let vote_module_addr = deps.api.addr_validate(&res.contract_address)?;
Expand All @@ -999,7 +1002,7 @@ pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result<Response, ContractE

// Check for module instantiation callbacks
let callback_msgs = match res.data {
Some(data) => from_json::<ModuleInstantiateCallback>(&data)
Some(data) => from_json::<CallbackMessages>(&data)
.map(|m| m.msgs)
.unwrap_or_else(|_| vec![]),
None => vec![],
Expand All @@ -1017,6 +1020,19 @@ pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result<Response, ContractE

Ok(Response::default().add_attribute("voting_module", vote_module_addr))
}
PROPOSAL_MODULE_EXECUTE_REPLY_ID => match msg.result {
SubMsgResult::Ok(res) => {
let callback_msgs = match res.data {
Some(data) => from_json::<CallbackMessages>(&data)
.map(|m| m.msgs)
.unwrap_or_else(|_| vec![]),
None => vec![],
};

Ok(Response::default().add_messages(callback_msgs))
}
SubMsgResult::Err(_) => Ok(Response::default()),
},
_ => Err(ContractError::UnknownReplyID {}),
}
}
Expand Down
4 changes: 2 additions & 2 deletions contracts/external/dao-migrator/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use cosmwasm_std::{
use cw2::set_contract_version;
use dao_interface::{
query::SubDao,
state::{ModuleInstantiateCallback, ProposalModule},
state::{CallbackMessages, ProposalModule},
};

use crate::{
Expand Down Expand Up @@ -42,7 +42,7 @@ pub fn instantiate(
CORE_ADDR.save(deps.storage, &info.sender)?;

Ok(
Response::default().set_data(to_json_binary(&ModuleInstantiateCallback {
Response::default().set_data(to_json_binary(&CallbackMessages {
msgs: vec![WasmMsg::Execute {
contract_addr: env.contract.address.to_string(),
msg: to_json_binary(&MigrateV1ToV2 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use cosmwasm_std::{
};
use cw2::set_contract_version;

use dao_interface::state::ModuleInstantiateCallback;
use dao_interface::state::CallbackMessages;
use dao_pre_propose_approval_single::msg::{
ApproverProposeMessage, ExecuteExt as ApprovalExt, ExecuteMsg as PreProposeApprovalExecuteMsg,
};
Expand Down Expand Up @@ -59,7 +59,7 @@ pub fn instantiate(
let addr = deps.api.addr_validate(&msg.pre_propose_approval_contract)?;
PRE_PROPOSE_APPROVAL_CONTRACT.save(deps.storage, &addr)?;

Ok(resp.set_data(to_json_binary(&ModuleInstantiateCallback {
Ok(resp.set_data(to_json_binary(&CallbackMessages {
msgs: vec![
CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr: addr.to_string(),
Expand Down
29 changes: 18 additions & 11 deletions contracts/proposal/dao-proposal-condorcet/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
use cosmwasm_std::entry_point;
use cosmwasm_std::{
to_json_binary, Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response, StdResult,
SubMsgResult,
};

use cw2::set_contract_version;
Expand Down Expand Up @@ -267,18 +268,24 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult<Binary> {
pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result<Response, ContractError> {
let repl = TaggedReplyId::new(msg.id)?;
match repl {
TaggedReplyId::FailedProposalExecution(proposal_id) => {
let mut proposal = PROPOSAL.load(deps.storage, proposal_id as u32)?;
proposal.set_execution_failed();
PROPOSAL.save(deps.storage, proposal_id as u32, &proposal)?;
TaggedReplyId::ProposalExecution(proposal_id) => match msg.result {
SubMsgResult::Ok(res) => match res.data {
Some(data) => Ok(Response::new()
.add_attribute("proposal_execution_success", proposal_id.to_string())
.set_data(data)),
None => Ok(Response::new()
.add_attribute("proposal_execution_success", proposal_id.to_string())),
},
SubMsgResult::Err(error) => {
let mut proposal = PROPOSAL.load(deps.storage, proposal_id as u32)?;
proposal.set_execution_failed();
PROPOSAL.save(deps.storage, proposal_id as u32, &proposal)?;

Ok(Response::default()
.add_attribute("proposal_execution_failed", proposal_id.to_string())
.add_attribute(
"error",
msg.result.into_result().err().unwrap_or("None".to_string()),
))
}
Ok(Response::new()
.add_attribute("proposal_execution_failed", proposal_id.to_string())
.add_attribute("error", error))
}
},
_ => unimplemented!("pre-propose and hooks not yet supported"),
}
}
6 changes: 3 additions & 3 deletions contracts/proposal/dao-proposal-condorcet/src/proposal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,11 +164,11 @@ impl Proposal {
msg: to_json_binary(&dao_interface::msg::ExecuteMsg::ExecuteProposalHook { msgs })?,
funds: vec![],
};
let masked_id = mask_proposal_execution_proposal_id(self.id as u64);
Ok(if self.close_on_execution_failure {
let masked_id = mask_proposal_execution_proposal_id(self.id as u64);
SubMsg::reply_on_error(core_exec, masked_id)
SubMsg::reply_always(core_exec, masked_id)
} else {
SubMsg::new(core_exec)
SubMsg::reply_on_success(core_exec, masked_id)
})
}

Expand Down
56 changes: 31 additions & 25 deletions contracts/proposal/dao-proposal-multiple/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
use cosmwasm_std::entry_point;
use cosmwasm_std::{
to_json_binary, Addr, Attribute, Binary, Deps, DepsMut, Empty, Env, MessageInfo, Order, Reply,
Response, StdResult, Storage, SubMsg, WasmMsg,
Response, StdResult, Storage, SubMsg, SubMsgResult, WasmMsg,
};

use cw2::set_contract_version;
Expand Down Expand Up @@ -545,15 +545,14 @@ pub fn execute_execute(
})?,
funds: vec![],
};
let masked_proposal_id = mask_proposal_execution_proposal_id(proposal_id);
match config.close_proposal_on_execution_failure {
true => {
let masked_proposal_id = mask_proposal_execution_proposal_id(proposal_id);
Response::default().add_submessage(SubMsg::reply_on_error(
execute_message,
masked_proposal_id,
))
}
false => Response::default().add_message(execute_message),
true => Response::default()
.add_submessage(SubMsg::reply_always(execute_message, masked_proposal_id)),
false => Response::default().add_submessage(SubMsg::reply_on_success(
execute_message,
masked_proposal_id,
)),
}
} else {
Response::default()
Expand Down Expand Up @@ -986,22 +985,29 @@ pub fn query_info(deps: Deps) -> StdResult<Binary> {
pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result<Response, ContractError> {
let repl = TaggedReplyId::new(msg.id)?;
match repl {
TaggedReplyId::FailedProposalExecution(proposal_id) => {
PROPOSALS.update(deps.storage, proposal_id, |prop| match prop {
Some(mut prop) => {
prop.status = Status::ExecutionFailed;
Ok(prop)
}
None => Err(ContractError::NoSuchProposal { id: proposal_id }),
})?;

Ok(Response::new()
.add_attribute("proposal execution failed", proposal_id.to_string())
.add_attribute(
"error",
msg.result.into_result().err().unwrap_or("None".to_string()),
))
}
TaggedReplyId::ProposalExecution(proposal_id) => match msg.result {
SubMsgResult::Ok(res) => match res.data {
Some(data) => Ok(Response::new()
.add_attribute("proposal_execution_success", proposal_id.to_string())
.set_data(data)),
None => Ok(Response::new()
.add_attribute("proposal_execution_success", proposal_id.to_string())),
},
SubMsgResult::Err(error) => {
PROPOSALS.update(deps.storage, proposal_id, |prop| match prop {
Some(mut prop) => {
prop.status = Status::ExecutionFailed;

Ok(prop)
}
None => Err(ContractError::NoSuchProposal { id: proposal_id }),
})?;

Ok(Response::new()
.add_attribute("proposal_execution_failed", proposal_id.to_string())
.add_attribute("error", error))
}
},
TaggedReplyId::FailedProposalHook(idx) => {
let addr = PROPOSAL_HOOKS.remove_hook_by_index(deps.storage, idx)?;
Ok(Response::new().add_attribute("removed_proposal_hook", format!("{addr}:{idx}")))
Expand Down
53 changes: 30 additions & 23 deletions contracts/proposal/dao-proposal-single/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
use cosmwasm_std::entry_point;
use cosmwasm_std::{
to_json_binary, Addr, Attribute, Binary, Deps, DepsMut, Env, MessageInfo, Order, Reply,
Response, StdResult, Storage, SubMsg, WasmMsg,
Response, StdResult, Storage, SubMsg, SubMsgResult, WasmMsg,
};
use cw2::{get_contract_version, set_contract_version, ContractVersion};
use cw_hooks::Hooks;
Expand Down Expand Up @@ -433,13 +433,14 @@ pub fn execute_execute(
})?,
funds: vec![],
};
let masked_proposal_id = mask_proposal_execution_proposal_id(proposal_id);
match config.close_proposal_on_execution_failure {
true => {
let masked_proposal_id = mask_proposal_execution_proposal_id(proposal_id);
Response::default()
.add_submessage(SubMsg::reply_on_error(execute_message, masked_proposal_id))
}
false => Response::default().add_message(execute_message),
true => Response::default()
.add_submessage(SubMsg::reply_always(execute_message, masked_proposal_id)),
false => Response::default().add_submessage(SubMsg::reply_on_success(
execute_message,
masked_proposal_id,
)),
}
} else {
Response::default()
Expand Down Expand Up @@ -1069,23 +1070,29 @@ pub fn migrate(deps: DepsMut, _env: Env, msg: MigrateMsg) -> Result<Response, Co
pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result<Response, ContractError> {
let repl = TaggedReplyId::new(msg.id)?;
match repl {
TaggedReplyId::FailedProposalExecution(proposal_id) => {
PROPOSALS.update(deps.storage, proposal_id, |prop| match prop {
Some(mut prop) => {
prop.status = Status::ExecutionFailed;
TaggedReplyId::ProposalExecution(proposal_id) => match msg.result {
SubMsgResult::Ok(res) => match res.data {
Some(data) => Ok(Response::new()
.add_attribute("proposal_execution_success", proposal_id.to_string())
.set_data(data)),
None => Ok(Response::new()
.add_attribute("proposal_execution_success", proposal_id.to_string())),
},
SubMsgResult::Err(error) => {
PROPOSALS.update(deps.storage, proposal_id, |prop| match prop {
Some(mut prop) => {
prop.status = Status::ExecutionFailed;

Ok(prop)
}
None => Err(ContractError::NoSuchProposal { id: proposal_id }),
})?;

Ok(prop)
}
None => Err(ContractError::NoSuchProposal { id: proposal_id }),
})?;

Ok(Response::new()
.add_attribute("proposal_execution_failed", proposal_id.to_string())
.add_attribute(
"error",
msg.result.into_result().err().unwrap_or("None".to_string()),
))
}
Ok(Response::new()
.add_attribute("proposal_execution_failed", proposal_id.to_string())
.add_attribute("error", error))
}
},
TaggedReplyId::FailedProposalHook(idx) => {
let addr = PROPOSAL_HOOKS.remove_hook_by_index(deps.storage, idx)?;
Ok(Response::new().add_attribute("removed_proposal_hook", format!("{addr}:{idx}")))
Expand Down
Loading

0 comments on commit 31c5fcc

Please sign in to comment.