Expand description
§CosmWasm MultiTest
CosmWasm MultiTest is designed to simulate a blockchain environment in pure Rust. This allows to run unit tests that involve contract 🡘 contract, and contract 🡘 module interactions. CosmWasm MultiTest is not intended to be a full blockchain application, but to simulate the Cosmos SDK x/wasm module close enough to gain confidence in multi-contract deployments, before testing them on a live blockchain.
The following sections explains some of the design for those who want to use the API, as well as those who want to take a look under the hood of CosmWasm MultiTest.
§Key APIs
§App
The main entry point to the system is called App, which represents a blockchain application. It maintains an idea of block height and time, which can be updated to simulate multiple blocks. You can use application’s update_block method to increment the timestamp by 5 seconds and the height by 1 (simulating a new block) or you can write any other mutator of BlockInfo to advance more.
App exposes an entry point execute that allows to execute
any CosmosMsg and wraps it in an atomic transaction.
That is, only if execute returns a success, then the state will be committed.
It returns the data and a list of Events on successful execution
or an Err(String)
on error. There are some helper methods tied to the Executor trait
that create the CosmosMsg for you to provide a less verbose API.
App’s methods like instantiate_contract,
execute_contract, and send_tokens are exposed
for your convenience in writing tests.
Each method executes one CosmosMsg atomically, as if it was submitted by a user.
You can also use execute_multi if you wish to execute multiple messages together
that revert the state as a whole in case of any failure.
The other key entry point to App is the Querier interface that it implements. In particular, you can use wrap to get a QuerierWrapper, which provides all kinds of interesting APIs to query the blockchain, like query_all_balances and query_wasm_smart. Putting this all together, you have one Storage wrapped into an application, where you can execute contracts and bank, query them easily, and update the current BlockInfo, in an API that is not very verbose or cumbersome. Under the hood it will process all messages returned from contracts, move bank tokens and call into other contracts.
You can easily create an App for use in your testcode like shown below. Having a single utility function for creating and configuring the App is the common pattern while testing contracts with CosmWasm MultiTest.
use cw_multi_test::App;
fn mock_app() -> App {
App::default()
}
The App maintains the root Storage, and the BlockInfo for the current block. It also contains a Router (discussed below), which can process any CosmosMsg variant by passing it to the proper keeper.
Note: App properly handles submessages and reply blocks.
Note: While the API currently supports custom messages, we don’t currently have an implementation of the default keeper, except of experimental CachingCustomHandler.
§Contracts
Before you can call contracts, you must instantiate them. And to instantiate them, you need a code_id
.
In wasmd
, this code_id
points to some stored Wasm code that is then run. In multitest, we use it to
point to a Box<dyn Contract>
that should be run. That is, you need to implement the Contract trait
and then add the contract to the App via store_code function.
The Contract trait defines the major entry points to any CosmWasm contract: instantiate, execute, query, sudo, reply (for submessages) and migrate.
In order to easily implement Contract from some existing contract code, we use the ContractWrapper struct, which takes some function pointers and combines them. You can take a look at test_helpers module for some examples or how to do so (and useful mocks for some test cases). Here is an example of wrapping a CosmWasm contract into a Contract trait to be added to an App:
use cosmwasm_std::Empty;
use cw1_whitelist::contract::{execute, instantiate, query};
use cw_multi_test::{App, Contract, ContractWrapper};
pub fn contract_whitelist() -> Box<dyn Contract<Empty>> {
Box::new(ContractWrapper::new(execute, instantiate, query))
}
let mut app = App::default();
let code_id = app.store_code(contract_whitelist());
// use this code_id to instantiate a contract
§Modules
There is only one root Storage, stored inside App. This is wrapped into a transaction, and then passed down to other functions to work with. The code that modifies the Storage is divided into modules much like the CosmosSDK. Currently, the message processing logic is divided into one module for every CosmosMsg variant. Bank handles BankMsg and BankQuery, Wasm handles WasmMsg and WasmQuery, etc.
§Router
The Router groups all modules in the system into one “macro-module” that can handle any CosmosMsg. While Bank handles BankMsg, and Wasm handles WasmMsg, we need to combine them into a larger composite to let them process messages from App. This is the whole concept of the Router. If you take a look at the execute method, you will see it is quite straightforward.
Note that the only way one module can call or query another module is by dispatching messages via the Router. This allows us to implement an independent Wasm in a way that it can process SubMsg that call into Bank. You can see an example of that in send method of the WasmKeeper, where it moves bank tokens from one account to another.
Modules§
- custom_
handler - Custom message and query handler
- error
- Error definitions
Structs§
- Accepting
Module - Always accepting module
- App
- Blockchain application simulator
- AppBuilder
- Utility to build App in stages. When particular properties are not explicitly set, then default values are used.
- AppResponse
- A set of data returned as a response of a contract entry point,
such as
instantiate
,execute
ormigrate
. - Bank
Keeper - A structure representing a default bank keeper.
- Contract
Data - Contract data includes information about contract,
equivalent of
ContractInfo
inwasmd
interface. - Contract
Wrapper - This structure wraps the Contract trait implementor and provides generic access to the contract’s entry-points.
- Distribution
Keeper - A structure representing a default distribution keeper.
- Failing
Module - Always failing module
- Router
- The Router plays a critical role in managing and directing transactions within the Cosmos blockchain.
- Simple
Address Generator - Default contract address generator used in WasmKeeper.
- Stake
Keeper - A structure representing a default stake keeper.
- Staking
Info - A structure containing some general staking parameters.
- Stargate
Accepting - Always accepting handler for
Stargate
/Any
message variants andStargate
/Grpc
queries. - Stargate
Failing - Always failing handler for
Stargate
/Any
message variants andStargate
/Grpc
queries. - Wasm
Keeper - A structure representing a default wasm keeper.
- Wasm
Sudo - A structure representing a privileged message.
Enums§
- Bank
Sudo - A message representing privileged actions in bank module.
- Staking
Sudo - Staking privileged action definition.
- SudoMsg
- We use it to allow calling into modules from another module in sudo mode. Things like gov proposals belong here.
Traits§
- Address
Generator - Common address generator interface.
- Bank
- This trait defines the interface for simulating banking operations.
- Checksum
Generator - This trait defines a method to calculate checksum based on the creator’s address and a unique code identifier.
- Contract
- This trait serves as a primary interface for interacting with contracts.
- Cosmos
Router - A trait representing the Cosmos based chain’s router.
- Distribution
- A trait defining a behavior of the distribution keeper.
- Executor
- A trait defining a default behavior of the message executor.
- Gov
- This trait implements the interface of the governance module.
- Ibc
- This trait implements the interface for IBC functionalities.
- Into
Addr - Defines conversions to Addr, this conversion is format agnostic and should be aligned with the format generated by MockApi.
- Into
Bech32 - Defines conversions to
Bech32
compatible addresses. - Into
Bech32m - Defines conversions to
Bech32m
compatible addresses. - Module
- General module
- Staking
- A trait defining a behavior of the stake keeper.
- Stargate
- Interface of handlers for processing
Stargate
/Any
message variants andStargate
/Grpc
queries. - Wasm
- This trait implements the interface of the Wasm module.
Functions§
- custom_
app - Creates new default
App
implementation working with customized exec and query messages. Outside theApp
implementation to make type elision better. - next_
block - Advances the blockchain environment to the next block in tests, enabling developers to simulate time-dependent contract behaviors and block-related triggers efficiently.
- no_init
- No-op application initialization function.
Type Aliases§
- Basic
App - A type alias for the default-built App. It simplifies storage and handling in typical scenarios, streamlining the use of the App structure in standard test setups.
- Basic
AppBuilder - This is essential to create a custom app with custom module.
- GovAccepting
Module - Implementation of the always accepting governance module.
- GovFailing
Module - Implementation of the always failing governance module.
- IbcAccepting
Module - Implementation of the always accepting IBC module.
- IbcFailing
Module - implementation of the always failing IBC module.
- Mock
ApiBech32 - Implementation of the
cosmwasm_std::Api
trait that uses Bech32 format for humanizing canonical addresses. - Mock
ApiBech32m - Implementation of the
cosmwasm_std::Api
trait that uses Bech32m format for humanizing canonical addresses.