This site requires Javascript to be enabled.

Introduction

The x/cw-errors module provides a standardized way for smart contracts to handle errors in relation to transactions initiated by the Archway protocol. With the introduction of the x/callback and x/cwica modules, it is now possible for the Archway protocol to execute smart contract actions via Sudo endpoints implemented by your contracts. However, when errors occur during these executions, they go unnoticed as they get lost in node logs. The x/cw-errors module aims to address this by providing mechanisms to notify contracts of such errors, which should enhance error awareness and response capabilities.

Possible error scenarios

x/callback

When the protocol executes a callback, there could be errors thrown due to:

  • The contract not having the expected sudo entrypoint
  • The callback execution consuming more gas than is allowed
  • There could be an error thrown by the contract

x/cwica

When a contract wants to execute an ICA transaction on another chain, there could be errors thrown due to:

  • The counterparty could not unmarshall the message, or did not recognize the message
  • The counterparty transaction execution failed
  • The IBC packet timing out

Opt-in to receive error callbacks

You have two methods for accessing errors from within your smart contracts: one is to have your contract subscribe to the CW-Errors module to receive callbacks for these errors, and the other is to query the CW-Errors module directly. This section focuses on how to subscribe to receive error callbacks.

Let's look back at the code in the ICA guide. The State struct has an errors field which is used to store any error information generated by the cw-errors module for any ICA actions executed after a error callback is executed.

pub struct State {  pub owner: Addr,  pub connection_id: String,  pub ica_address: String,  pub voted: bool,  pub errors: String,  pub timeout: bool,}

To receive an Error callback, the contract must implement the SudoMsg::Error message so it can receive and handle errors. This error callback is automatically executed by the Archway protocol. The following is an example:

#[cfg_attr(not(feature = "library"), entry_point)]pub fn sudo(deps: DepsMut, env: Env, msg: SudoMsg) -> StdResult<Response> {  match msg {    SudoMsg::Error { module_name, error_code, contract_address: _, input_payload, error_message } => sudo::error(deps, env, module_name, error_code, input_payload, error_message),  }}pub mod sudo {  use crate::msg::{ICAResponse, AccountRegistered};  use super::*;  pub fn error(deps: DepsMut, _env: Env, module_name: String, error_code: u32, _payload: String, error_message: String) -> StdResult<Response> {    let _ = STATE.update(deps.storage, |mut state| -> Result<_, ContractError> {      if module_name == "cwica" {        if error_code == 1 { // packet timeout error          state.timeout = true;        }        if error_code == 2 { // submittx execution error          state.errors = error_message;        }        else {          // unknown error        }      }      Ok(state)    });    Ok(Response::new())  }}

A few things to note:

  • Errors are stored in the cw-errors module state for a specified number of blocks. The duration is dependent on a module parameter which can be changed via governance.
  • The max gas allowed for these sudoErr executions will be as small as possible while still being useful. This execution is only meant for error handling and not for complex logic prosessing.

Before the protocol will execute such a callback in relation to errors generated from the ICA or Callback modules, the contract must register for a subscription to the CW-Errors module which might have a fee attached. Currently there is no fee being charged for subscribing but this can be changed via governance. From the CLI you can access the fee via archwayd q cwerrors params. The subscription is only valid up to a certain block height.

To register for such a subscription, the follwong CW-Errors module message must be executed:

service Msg {
  // SubscribeToError defines an operation which will register a contract for a sudo callback on errors
  rpc SubscribeToError(MsgSubscribeToError) returns (MsgSubscribeToErrorResponse);
}

message MsgSubscribeToError {
  // sender is the address of who is registering the contarcts for callback on error
  string sender = 1;
  // contract is the address of the contract that will be called on error
  string contract_address = 2;
  // fee is the subscription fee for the feature (current no fee is charged for this feature)
  cosmos.base.v1beta1.Coin fee = 3 ;
}

message MsgSubscribeToErrorResponse {
  // subscription_valid_till is the block height till which the subscription is valid
  int64 subscription_valid_till = 1;
}

This can be executed via:

  • CLI: archwayd tx cwerrors subscribe-to-error [contract-address] [fee-amount] [flags]
  • Stargate message with type URL: archway.cwerrors.v1.MsgSubscribeToError

Querying the error module

Another means of accessing errors being stored in the CW-Errors module state is by querying the state. This can be queried by the contract using the Stargate Querier. The type URL would be archway.cwerrors.v1.QueryErrorsRequest and can be found here:

service Query {
  // Errors queries all the errors for a given contract.
  rpc Errors(QueryErrorsRequest) returns (QueryErrorsResponse) {
    option (google.api.http).get = "/archway/cwerrors/v1/errors";
  }
}

message QueryErrorsRequest {
  // contract_address is the address of the contract whose errors to query for
  string contract_address = 1;
}

message QueryErrorsResponse {
  // errors defines all the contract errors which will be returned
  repeated SudoError errors = 1 [ (gogoproto.nullable) = false ];
}

The following is an example of how to query the CW-Errors module from your contract:

#[allow(clippy::derive_partial_eq_without_eq)]#[derive(Clone, PartialEq, ::prost::Message)]pub struct QueryErrorsRequest {    #[prost(string, tag = "1")]    pub contract_address: ::prost::alloc::string::String,}pub mod query {  use super::*;  pub fn query_cw_errors(    deps: Deps,    _env: Env,  ) -> StdResult<Option<QueryErrorsResponse>> {    let contract_address = env.contract.address.to_string();    let msg = QueryErrorsRequest {      contract_address: contract_address.clone(),    };    let res =deps.querier.query(&cosmwasm_std::QueryRequest::Stargate {      path: "/archway.cwerrors.v1.Query/Errors".to_string(),      data: Binary::from(prost::Message::encode_to_vec(&msg)),,    })?;    Ok(res)  }}

CW-Errors test contract

The CW-ICA test contract implements CW-Errors. You can find the test contract here.