This site requires Javascript to be enabled.

Responses and Attributes in Cosmwasm

Explanation

Responses

A response is the final point of a Cosmwasm endpoint (e.g. instantiation and execution), and it has several uses.

It is responsible of returning the metadata when a certain endpoint is called. A response holds attributes which are key-value pairs that are attached when a message is executed that can be queried. It triggers events that can be then queried on a Tendermint or low-level. It can hold messages that the contract should execute when the function is completed. It can return data to the user, which is important in queries.

Attributes

Attributes are metadata that are used to log information about a certain endpoint to help with filtering and indexing data. The method attribute is normally added to represent an instantiation has been executed on the contract and can be used to filter the instantiation message.

#[cfg_attr(not(feature = "library"), entry_point)]pub fn instantiate(    deps: DepsMut,    _env: Env,    info: MessageInfo,    msg: InstantiateMsg,) -> Result<Response, ContractError> {    let state = State {        count: msg.count,        owner: info.sender.clone(),    };    set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;    STATE.save(deps.storage, &state)?;    Ok(Response::new()        .add_attribute("method", "instantiate")        .add_attribute("owner", info.sender)        .add_attribute("count", msg.count.to_string()))}

Messages

Responses can hold messages that the contract executes after all the code inside a function is successfully completed. These messages are synchronous and the message is part of the transaction itself. If the transaction or message fails to execute, then the whole execution fails and reverts. Similarly to the Send Tokens example, the message could be any CosmosMsg which includes Bank messages (sending native funds), Staking messages or Wasm messages (instantiating and executing).

Ok(Response::new().add_attribute("action", "increment")        .add_message(BankMsg::Send { to_address: to.into_string(), amount: vec![Coin{denom, amount}] }))This message in the response is executed by the smart contract after the logic of the function is completed.

SubMessages

A Submessage can be used instead of a message when a reply is expected as a callback from calling a certain function on another contract or any other message. A submessage is asynchronous meaning that even if the message fails on the recipient contract, the whole transaction does not fail but a reply could be sent to handle the error respectively. This is extremely important in IBC use cases where if the packet is rejected, we should not want the whole transaction to fail but only to get a reply of an aknowledgment of failure from the other contract.

pub fn ibc_packet_receive(    deps: DepsMut,    _env: Env,    msg: IbcPacketReceiveMsg,) -> Result<IbcReceiveResponse, Never> {    // other parse code here...        let msg = Cw20ExecuteMsg::Transfer {        recipient,        amount: coin.amount,    };    let exec = WasmMsg::Execute {        contract_addr: coin.address,        msg: to_binary(&msg).unwrap(),        funds: vec![],    };    let sub_msg = SubMsg::reply_on_error(exec, SEND_TOKEN_ID);        // return submessage with pre-emptive success packet in the data field    IbcReceiveResponse::new()        .set_ack(ack_success())        .add_submessage(sub_msg)        .add_attributes(attributes)}

Example

To handle responses with CosmWasm, you can create the following files: lib.rs contract.rs msg.rs error.rs state.rs helpers.rs

lib.rs

pub mod contract;mod error;pub mod helpers;pub mod msg;pub mod state;pub use crate::error::ContractError;

contract.rs

#[cfg(not(feature = "library"))]use cosmwasm_std::entry_point;use cosmwasm_std::{to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult};use cw2::set_contract_version;use crate::error::ContractError;use crate::msg::{ExecuteMsg, GetCountResponse, InstantiateMsg, QueryMsg};use crate::state::{State, STATE};// version info for migration infoconst CONTRACT_NAME: &str = "crates.io:responses-attributes";const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");#[cfg_attr(not(feature = "library"), entry_point)]pub fn instantiate(    deps: DepsMut,    _env: Env,    info: MessageInfo,    msg: InstantiateMsg,) -> Result<Response, ContractError> {    let state = State {        count: msg.count,        owner: info.sender.clone(),    };    set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;    STATE.save(deps.storage, &state)?;    Ok(Response::new()        .add_attribute("method", "instantiate")        .add_attribute("owner", info.sender)        .add_attribute("count", msg.count.to_string()))}#[cfg_attr(not(feature = "library"), entry_point)]pub fn execute(    deps: DepsMut,    _env: Env,    info: MessageInfo,    msg: ExecuteMsg,) -> Result<Response, ContractError> {    match msg {        ExecuteMsg::Increment {} => execute::increment(deps),        ExecuteMsg::Reset { count } => execute::reset(deps, info, count),    }}pub mod execute {    use super::*;    pub fn increment(deps: DepsMut) -> Result<Response, ContractError> {        STATE.update(deps.storage, |mut state| -> Result<_, ContractError> {            state.count += 1;            Ok(state)        })?;        Ok(Response::new().add_attribute("action", "increment"))    }    pub fn reset(deps: DepsMut, info: MessageInfo, count: i32) -> Result<Response, ContractError> {        STATE.update(deps.storage, |mut state| -> Result<_, ContractError> {            if info.sender != state.owner {                return Err(ContractError::Unauthorized {});            }            state.count = count;            Ok(state)        })?;        Ok(Response::new().add_attribute("action", "reset"))    }}#[cfg_attr(not(feature = "library"), entry_point)]pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {    match msg {        QueryMsg::GetCount {} => to_binary(&query::count(deps)?),    } }pub mod query {    use super::*;    pub fn count(deps: Deps) -> StdResult<GetCountResponse> {        let state = STATE.load(deps.storage)?;        Ok(GetCountResponse { count: state.count })    }}#[cfg(test)]mod tests {    use super::*;    use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};    use cosmwasm_std::{coins, from_binary};    #[test]    fn proper_initialization() {        let mut deps = mock_dependencies();        let msg = InstantiateMsg { count: 17 };        let info = mock_info("creator", &coins(1000, "earth"));        // we can just call .unwrap() to assert this was a success        let res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap();        assert_eq!(0, res.messages.len());        // it worked, let's query the state        let res = query(deps.as_ref(), mock_env(), QueryMsg::GetCount {}).unwrap();        let value: GetCountResponse = from_binary(&res).unwrap();        assert_eq!(17, value.count);    }    #[test]    fn increment() {        let mut deps = mock_dependencies();        let msg = InstantiateMsg { count: 17 };        let info = mock_info("creator", &coins(2, "token"));        let _res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap();        // beneficiary can release it        let info = mock_info("anyone", &coins(2, "token"));        let msg = ExecuteMsg::Increment {};        let _res = execute(deps.as_mut(), mock_env(), info, msg).unwrap();        // should increase counter by 1        let res = query(deps.as_ref(), mock_env(), QueryMsg::GetCount {}).unwrap();        let value: GetCountResponse = from_binary(&res).unwrap();        assert_eq!(18, value.count);    }    #[test]    fn reset() {        let mut deps = mock_dependencies();        let msg = InstantiateMsg { count: 17 };        let info = mock_info("creator", &coins(2, "token"));        let _res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap();        // beneficiary can release it        let unauth_info = mock_info("anyone", &coins(2, "token"));        let msg = ExecuteMsg::Reset { count: 5 };        let res = execute(deps.as_mut(), mock_env(), unauth_info, msg);        match res {            Err(ContractError::Unauthorized {}) => {}            _ => panic!("Must return unauthorized error"),        }        // only the original creator can reset the counter        let auth_info = mock_info("creator", &coins(2, "token"));        let msg = ExecuteMsg::Reset { count: 5 };        let _res = execute(deps.as_mut(), mock_env(), auth_info, msg).unwrap();        // should now be 5        let res = query(deps.as_ref(), mock_env(), QueryMsg::GetCount {}).unwrap();        let value: GetCountResponse = from_binary(&res).unwrap();        assert_eq!(5, value.count);    }}

msg.rs

use cosmwasm_schema::{cw_serde, QueryResponses};#[cw_serde]pub struct InstantiateMsg {    pub count: i32,}#[cw_serde]pub enum ExecuteMsg {    Increment {},    Reset { count: i32 },}#[cw_serde]#[derive(QueryResponses)]pub enum QueryMsg {    // GetCount returns the current count as a json-encoded number    #[returns(GetCountResponse)]    GetCount {},}// We define a custom struct for each query response#[cw_serde]pub struct GetCountResponse {    pub count: i32,}

error.rs

use cosmwasm_std::StdError;use thiserror::Error;#[derive(Error, Debug)]pub enum ContractError {    #[error("{0}")]    Std(#[from] StdError),    #[error("Unauthorized")]    Unauthorized {},    // Add any other custom errors you like here.    // Look at https://docs.rs/thiserror/1.0.21/thiserror/ for details.}

state.rs

use schemars::JsonSchema;use serde::{Deserialize, Serialize};use cosmwasm_std::Addr;use cw_storage_plus::Item;#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]pub struct State {    pub count: i32,    pub owner: Addr,}pub const STATE: Item<State> = Item::new("state");

helpers.rs

use schemars::JsonSchema;use serde::{Deserialize, Serialize};use cosmwasm_std::{    to_binary, Addr, CosmosMsg, CustomQuery, Querier, QuerierWrapper, StdResult, WasmMsg, WasmQuery,};use crate::msg::{ExecuteMsg, GetCountResponse, QueryMsg};/// CwTemplateContract is a wrapper around Addr that provides a lot of helpers/// for working with this.#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]pub struct CwTemplateContract(pub Addr);impl CwTemplateContract {    pub fn addr(&self) -> Addr {        self.0.clone()    }    pub fn call<T: Into<ExecuteMsg>>(&self, msg: T) -> StdResult<CosmosMsg> {        let msg = to_binary(&msg.into())?;        Ok(WasmMsg::Execute {            contract_addr: self.addr().into(),            msg,            funds: vec![],        }        .into())    }    /// Get Count    pub fn count<Q, T, CQ>(&self, querier: &Q) -> StdResult<GetCountResponse>    where        Q: Querier,        T: Into<String>,        CQ: CustomQuery,    {        let msg = QueryMsg::GetCount {};        let query = WasmQuery::Smart {            contract_addr: self.addr().into(),            msg: to_binary(&msg)?,        }        .into();        let res: GetCountResponse = QuerierWrapper::<CQ>::new(querier).query(&query)?;        Ok(res)    }}

Credits: CosmWasm by example. You can check the code on Github or open it with VS code.