This site requires Javascript to be enabled.

Submessages

Messages are used to interact with both SDK modules and CW smart contracts. Since messages are executed in a 'set-and-forget' manner, you will not receive a response regarding whether the call was successful or not.

However, getting the result of your call can be very useful in the following cases:

  • Instantiating a new contract and getting the contract address
  • Executing an action and asserting that the result was successful (e.g. making sure that a certain token amount has been transferred to your contract)
  • Handling the error from your cross-contract call instead of rolling back the transaction

To get the result of the message sent from your smart contract, you will need to dispatch a submessage. You can read more on the semantics of submessages and how submessage execution is ordered here.

Creating a submessage

A submessage wraps a CosmosMsg in a SubMsg struct:

pub struct SubMsg<T> {    pub id: u64,                // reply_id that will be used to handle the reply    pub msg: CosmosMsg<T>,      // message to be sent    pub gas_limit: Option<u64>, // gas limit for the submessage    pub reply_on: ReplyOn,      // a flag to determine when the reply should be sent}

You can find the source code for the SubMsg struct here.

Now, let's look at an example of instantiating a cw20 token using a submessage:

const INSTANTIATE_REPLY_ID = 1u64;// Creating a message to create a new cw20 tokenlet instantiate_tx = WasmMsg::Instantiate {    admin: None,    code_id: msg.cw20_code_id,    msg: to_binary(&Cw20InstantiateMsg {        name: "new token".to_string(),        symbol: "nToken".to_string(),        decimals: 6,        initial_balances: vec![],        mint: Some(MinterResponse {            minter: env.contract.address.to_string(),            cap: None,        }),    })?,    funds: vec![],    label: "".to_string(),};// Creating a submessage that wraps the message abovelet submessage = SubMsg::reply_on_success(instantiate_tx.into(), INSTANTIATE_REPLY_ID);// Creating a response with the submessagelet response = Response::new().add_submessage(submessage);

Reply strategies

Submessages offer four different reply options for the other contract to provide:

pub enum ReplyOn {    /// Always perform a callback after SubMsg is processed    Always,    /// Only callback if SubMsg returned an error, no callback on success case    Error,    /// Only callback if SubMsg was successful, no callback on error case    Success,    /// Never make a callback - this is like the original CosmosMsg semantics    Never,}

Note that previously we created the submessage using the SubMsg::reply_on_success shorthand. However, we can also create a submessage and explicitly specifying the reply strategy.

let submessage = SubMsg {    gas_limit: None,    id: INSTANTIATE_REPLY_ID,    reply_on: ReplyOn::Success,    msg: instantiate_tx.into()}

Handling a reply

In order to handle the reply from the other contract, the calling contract must implement a new entry point. Here are two examples of how to handle the replies:

Instantiating a new contract

#[cfg_attr(not(feature = "library"), entry_point)]pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> StdResult<Response> {    match msg.id {        INSTANTIATE_REPLY_ID => handle_instantiate_reply(deps, msg),        id => Err(StdError::generic_err(format!("Unknown reply id: {}", id))),    }}fn handle_instantiate_reply(deps: DepsMut, msg: Reply) -> StdResult<Response> {    // Handle the msg data and save the contract address    // See: https://github.com/CosmWasm/cw-plus/blob/main/packages/utils/src/parse_reply.rs    let res = parse_reply_instantiate_data(msg)?;    // Save res.contract_address    Ok(Response::new())}

Handling a reply from a token transfer

#[cfg_attr(not(feature = "library"), entry_point)]pub fn reply(deps: DepsMut, msg: Reply) -> StdResult<Response> {    match msg.id {        CW20_TRANSFER_REPLY_ID => handle_transfer_reply(deps, msg),        id => Err(StdError::generic_err(format!("Unknown reply id: {}", id))),    }}fn handle_transfer_reply(deps: DepsMut, msg: Reply) -> StdResult<Response> {    // Handle the msg data and save the contract address    // See: https://github.com/CosmWasm/cw-plus/blob/main/packages/utils/src/parse_reply.rs    let data = msg.result.into_result().map_err(StdError::generic_err);    // Search for the transfer event    // If there are multiple transfers, you will need to find the right event to handle    let transfer_event = msg        .events        .iter()        .find(|e| {            e.attributes                .iter()                .any(|attr| attr.key == "action" && attr.value == "transfer")        })        .ok_or_else(|| StdError::generic_err(format!("unable to find transfer action"))?;    // Do whatever you want with the attributes in the transfer event    // Reference to the full event: https://github.com/CosmWasm/cw-plus/blob/main/contracts/cw20-base/src/contract.rs#L239-L244    Ok(Response::new())}

Propagation of context between contracts

To stop reentrancy attacks, CosmWasm does not allow context to be stored in the contract memory. There are two ways to propagate state between contracts:

  • All events returned by the submessage can be read from the Reply message
  • Storing a temporary state using cw_storage_plus::Item and loading it into the reply handler

Drop Camp is here!

Join the queue and be one of the first to get in!.

Go Camping ↗