v1-content
?
You are viewing an older version of the content, click here to switch to the current version

# CosmWasm

Discover how multi-chain smart contracts become possible with CosmWasm. The following sections are recommended as a preparation:

CosmWasm (opens new window) offers multi-chain solutions for smart contracts through an actor-model design focused on providing a library.

The actor model is a design pattern for reliable, distributed systems. It is the pattern underlying CosmWasm smart contracts.

Each actor has access to its own internal state and can only message other actors through a so-called dispatcher, which maintains the state and maps addresses to code and storage.

Want to read more on the actor model? See the CosmWasm documentation on the Actor Model for Contract Calls (opens new window).

CosmWasm's design makes the code agnostic to the details of underlying chains. It only requires a Cosmos SDK application to embed the Wasm module.

CosmWasm is adaptable to different development environments by design and makes it possible to connect chains. It is a solid platform to develop on because:

  • If you want to change chains, you can easily transfer smart contracts and decentralized applications (dApps).
  • If your application grows, you can launch your chain for the next version of your smart contract. You do not need to compile and deploy the binaries again.

# Install

Go must be installed to use the Cosmos SDK. You also need Rust to write smart contracts.

Go to rustup.rs (opens new window) to install Rust, or update your version with rustup update. Then, have it download and install the wasm32 compilation target:

Copy $ rustup target list --installed # if wasm32 is not listed above, run this $ rustup target add wasm32-unknown-unknown

wasmd is the easiest way to begin. It is forked from gaiad (the Gaia Daemon) (opens new window), which is a binary build with the Cosmos Hub, and includes the Wasm (opens new window) module.

Create a folder and clone the wasmd (opens new window) repository into it:

Copy $ git clone https://github.com/CosmWasm/wasmd.git $ cd wasmd $ git checkout v0.23.0 $ make install

Verify your installation:

Copy $ wasmd version

This returns:

Copy 0.23.0

If you cannot call wasmd, make sure your $GOPATH and $PATH are set correctly.

# Connect to a testnet

First test the wasmd client with the Cliffnet (opens new window) testnet. wasmd is configured via environment variables. Export the most recent environment from here (opens new window):

Copy $ curl https://raw.githubusercontent.com/CosmWasm/testnets/master/cliffnet-1/defaults.env -o cliffnet-1-defaults.env $ source cliffnet-1-defaults.env

Confirm you got it correctly:

Copy $ echo $CHAIN_ID

This returns:

Copy cliffnet-1

If you open another terminal window, do not forget to repeat this source command, as this is local to the session.

# Your accounts

Now add some keys:

Copy $ wasmd keys add alice $ wasmd keys add bob

What was created?

Copy $ wasmd keys show alice --address

This returns:

Copy wasm1jj7gzazxvgy56rj8kersuc44ehvep0uey85jdn

That is your address. Query your token balance:

Copy $ export alice=$(wasmd keys show alice --address) $ wasmd query bank balances $alice --node $RPC

This returns:

Copy pagination: {}

You have none. Time to ask the faucet (opens new window) to remedy this. To facilitate command-line actions, install jq (opens new window), which is a lightweight and flexible command-line JSON processor. Then prepare the request for alice:

Copy $ export json_request='{"denom":"upebble","address":"'$alice'"}' $ echo $json_request | jq

This returns:

Copy { "denom": "upebble", "address": "wasm1jj7gzazxvgy56rj8kersuc44ehvep0uey85jdn" }

upebble is the denomination of the testnet token. With the content of the request ready, call the faucet:

Copy $ curl -X POST --header "Content-Type: application/json" --data "$json_request" https://faucet.cliffnet.cosmwasm.com/credit

This returns:

Copy ok

Query your balance again:

Copy $ wasmd query bank balances $alice --node $RPC

This returns:

Copy balances: - amount: "100000000" denom: upebble pagination: {}

Repeat this process for bob.

# Compile a smart contract

Now that you have enough tokens to deploy a smart contract on Cliffnet, clone the contract samples away from your wasmd folder:

Copy $ git clone https://github.com/InterWasm/cw-contracts $ cd cw-contracts/contracts/nameservice $ cargo wasm

This returns:

Copy ... Compiling cw-nameservice v0.11.0 (/Users/me/cw-contracts/contracts/nameservice) Finished release [optimized] target(s) in 1m 20s

In this last command, wasm is an alias (opens new window) for wasm build --release --target wasm32-unknown-unknown.

You now have a compiled smart contract on file. You want to maintain your smart contract binary as small as possible, and have Rust compiled with default settings. Check the size of your build with:

Copy $ ls -lh target/wasm32-unknown-unknown/release/cw_nameservice.wasm

This returns:

Copy -rwxr-xr-x 2 me staff 1.8M target/wasm32-unknown-unknown/release/cw_nameservice.wasm

You can optimize the code with a Docker (opens new window) container based on an image provided by CosmWasm (opens new window) for production purposes:

Copy $ docker run --rm -v "$(pwd)":/code \ --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ cosmwasm/rust-optimizer:0.12.6
1

Apple M1

If you work with a machine using M1 architecture, you need to add the --platform linux/amd64 flag:

Copy $ docker run --rm --platform linux/amd64 -v "$(pwd)":/code \ --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ cosmwasm/rust-optimizer:0.12.6

Compare the result:

Copy $ ls -alh artifacts/cw_nameservice.wasm

This returns:

Copy -rw-r--r-- 1 me staff 138K artifacts/cw_nameservice.wasm

# Upload a smart contract binary

Time to store the smart contract binaries on the blockchain:

Copy $ export result=$(wasmd tx wasm store artifacts/cw_nameservice.wasm --from alice --node $RPC --chain-id cliffnet-1 --gas-prices 0.01upebble --gas auto --gas-adjustment 1.3 --output json --broadcast-mode block --yes) $ export code_id=$(echo $result | jq -r '.logs[0].events[-1].attributes[0].value') $ echo $code_id

The response returns a code_id value (for instance 1391), which uniquely identifies your newly uploaded binary in the blockchain. Record this in order to instantiate a name service with this binary in the next steps.

# Instantiate your smart contract

You have uploaded some code, but do not yet have any smart contract instance. Now to instantiate a new smart contract that uses this code. Look at the aptly-named instantiate function in the name server contract:

Copy #[cfg_attr(not(feature = "library"), entry_point)] pub fn instantiate( deps: DepsMut, _env: Env, _info: MessageInfo, msg: InstantiateMsg, ) -> Result<Response, StdError> { let config_state = Config { purchase_price: msg.purchase_price, transfer_price: msg.transfer_price, }; config(deps.storage).save(&config_state)?; Ok(Response::default()) } contracts nameservice src contract.rs View source

Among the parameters the function expects are msg.purchase_price and msg.transfer_price (opens new window). Both have the type cosmwasm_std::Coin (opens new window), which looks very similar to Cosmos SDK's Coin (opens new window). This is no coincidence. With this knowledge, instantiate a new name service with a purchase_price and transfer_price:

Copy $ wasmd tx wasm instantiate $code_id '{"purchase_price":{"amount":"100","denom":"upebble"},"transfer_price":{"amount":"999","denom":"upebble"}}' --from alice --no-admin --node $RPC --chain-id cliffnet-1 --gas-prices 0.01upebble --gas auto --gas-adjustment 1.3 --label "CosmWasm tutorial name service" --broadcast-mode block --yes

Note the code_id that refers to which binary to use for the instantiation. Check that the name service instance was successfully created with:

Copy $ wasmd query wasm list-contract-by-code $code_id --node $RPC --output json

You can find the contract address in the response. Make it a variable too:

Copy $ export contract_address=$(wasmd query wasm list-contract-by-code $code_id --node $RPC --output json | jq -r ".contracts[0]")

Use this to fetch more information with the following command:

Copy $ wasmd query wasm contract $contract_address --node $RPC

# Call your smart contract

With your instance now running, you can call other functions on it.

# Register a name

Looking into the contract code, you can find the execute function:

Copy #[cfg_attr(not(feature = "library"), entry_point)] pub fn execute( deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg, ) -> Result<Response, ContractError> { match msg { ExecuteMsg::Register { name } => execute_register(deps, env, info, name), ExecuteMsg::Transfer { name, to } => execute_transfer(deps, env, info, name, to), } } contracts nameservice src contract.rs View source

There are two execute message types. These are used to register or transfer a name within the name service. Start by registering (opens new window) a new name with your instance:

Copy $ wasmd tx wasm execute $contract_address '{"register":{"name":"fred"}}' --amount 100upebble --from alice --node $RPC --chain-id cliffnet-1 --gas-prices 0.01upebble --gas auto --gas-adjustment 1.3 --broadcast-mode block --yes

# Verify the name registration

With the transaction posted, it is time to verify that the name was registered. In the contract you can find the query function:

Copy #[cfg_attr(not(feature = "library"), entry_point)] pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult<Binary> { match msg { QueryMsg::ResolveRecord { name } => query_resolver(deps, env, name), QueryMsg::Config {} => to_binary(&config_read(deps.storage).load()?), } } contracts nameservice src contract.rs View source

There are two query message types. Note that you now have deps: Deps instead of deps: DepsMut. This indicates that the execution of the function does not mutate the state. This is what to use with functions that implement a query type of service.

Verify the registration with ResolveRecord (opens new window):

Copy $ wasmd query wasm contract-state smart $contract_address '{"resolve_record": {"name": "fred"}}' --node $RPC --output json

The response gives you the wallet address owning the registered name, which should be alice.

# Transfer a name

Now create another transaction to transfer the name to the second wallet bob. First prepare the query with the address of your other wallet:

Copy $ export json_request='{"transfer":{"name":"fred","to":"'$bob'"}}'

Then send the transaction:

Copy $ wasmd tx wasm execute $contract_address "$json_request" --amount 999upebble --from alice --node $RPC --chain-id cliffnet-1 --gas-prices 0.01upebble --gas auto --gas-adjustment 1.3 --broadcast-mode block --yes

Under the hood, the execution used transfer_price, which you set at the instantiation.

Check again with a resolve_record query to confirm that the transfer was successful. Experiment with another transfer from bob to alice, and pay attention to which wallet can perform which transaction.

CosmWasm offers good documentation (opens new window). This section is a summary of the Getting Started section (opens new window). Store the env script from here (opens new window) in case you wish to test it on your local node. Also look at the contract semantics (opens new window).

You can find more information in the CosmWasm Developer Academy (opens new window) and modular tutorials in the Wasm tutorials (opens new window). You can also find various hands-on videos on the workshops (opens new window) page.

# Next up

At this point, you have:

Head to the next chapter to discover the Inter-Blockchain Communication Protocol.