This site requires Javascript to be enabled.

Introduction

The introduction of the CW-Fees module significantly enhances how dapps can cover transaction fees for accounts within the Archway ecosystem. The x/feegrant module which is a core component of the Cosmos SDK had limitations that prevented a smart contract from assuming the Fee Granter role. Now, the CW-Fees module enables this capability, providing considerable flexibility in how fees are handled.

To get a better understanding of how the Fee Granter system works, see Understanding grant allowances.

How does it work?

The CW-Fees module presents two crucial message entry points: RegisterAsGranter and UnregisterAsGranter. The RegisterAsGranter message allows a contract to establish itself as a fee granter. Upon registration, the network recognizes the contract as a qualified fee granter. Subsequently, the contract must handle incoming requests at the SudoMsg::CwGrant entry point to manage such requests.

The UnregisterAsGranter simply allows for removing a contract as a fee granter.

Register as granter

You can create an endpoint to set and remove the contract as a Granter but this can also be done in the instantiate method and will be executed when a contract is instantiated. Let's look at an example of how this can be done.

/// MsgRegisterAsGranter is used to register a a granter.#[allow(clippy::derive_partial_eq_without_eq)]#[derive(Clone, PartialEq, ::prost::Message)]pub struct MsgRegisterAsGranter {    #[prost(string, tag = "1")]    pub granting_contract: ::prost::alloc::string::String,}#[cfg_attr(not(feature = "library"), entry_point)]pub fn instantiate(  deps: DepsMut,  _env: Env,  _: MessageInfo,  msg: InstantiateMsg,) -> Result<Response, ContractError> {  let contract_address = env.contract.address.to_string();  let regsiter_msg = MsgRegisterAsGranter {    granting_contract: contract_address.clone(),  };  let register_stargate_msg = CosmosMsg::Stargate {    type_url: "/archway.cwfees.v1.MsgRegisterAsGranter".to_string(),    value: Binary::from(prost::Message::encode_to_vec(&regsiter_msg)),  };  Ok(Response::new()    .add_attribute("action", "register")    .add_message(register_stargate_msg))}

With this now registered, the contract can now act as a Fee Granter to pay the transaction fees onbehalf of other accounts. In the event of a transaction, the user can designate a registered CosmWasm contract as the Tx.AuthInfo.Fee.Granter.

Accept fee grant requests

In order for your contract to accept fee grant requests it will need to implement a new Sudo entrypoint. Please note that only the Archway protocol can make Sudo entrypoint calls. The following is an example.

// State variable holding all grantees that qualify for a grantpub const GRANTS: Map<&Addr, Empty> = Map::new("grants");#[cfg_attr(not(feature = "library"), entry_point)]pub fn sudo(  deps: DepsMut,  _env: Env,  msg: SudoMsg,) -> Result<Response, ContractError> {  return match msg {    SudoMsg::CwGrant(grant) => {      sudo_grant(deps, grant)    }  }}fn sudo_grant(deps: DepsMut, msg: CwGrant) -> Result<Response, ContractError> {  // in order to pay the fees all message senders need to be  // in the grants list.  for m in &msg.msgs {    let sender = deps.api.addr_validate(&m.sender)?;    if !GRANTS.has(deps.storage, &sender) {      return Err(ContractError::Unauthorized {})    }  }  Ok(Response::default())}

A CwGrant message is submitted to the sudo_grant function that provides information that can be used to to make an informed decision regarding the grant request. In this example we populate a GRANTS state variable with addresses that can receive grants and in the function that will execute the grant we first check that the calling account is in the list. If the contract approves (i.e., returns no errors), the runtime automatically manages the transfer of fees from the contract to the auth collector. If the contract decides to reject the grant, it must return an error response.

CW-Fees test contract

You can find more details in the CW-Fees test contract here.

Frontend interaction

When interacting with the frontend, you will need to generate the transaction request with the fee granter address set. If you are using arch3.js the following would work:

const fee = await signingClient.calculateFee(signerAddress, messages, '', 1.5, feeGranterAddress);      const broadcastResult = await signingClient.signAndBroadcast(signerAddress, messages, fee);

Since the calculateFee function doesn't inherently require setting a fee granter address, you must manually generate the fee with the feeGranterAddress and include it in the broadcast request. Behind the scenes, the protocol first checks whether a granter is registered under the default Cosmos SDK feegrant module using the provided feeGranterAddress. If no SDK feegrant is linked to that feeGranterAddress, the process then moves to the cw-fees module to see if a smart contract is associated with the feeGranterAddress, subsequently invoking the respective Sudo endpoint on the smart contract.