Basic usage of cosmwasm and cw20

Updated at

2024.3.9

Created at

2021.3.30

I participated in HackAtom RU for 2 weeks. I regretted not taking notes about the previous HackAtom V, so I noted the use of cosmwasm and cw20.

Each client of the blockchains using Cosmos SDK

  • gaiad: for Cosmos Hub (and the blockchains built by Cosmos SDK)
  • wasmd: for CosmWasm
  • secretcli: for SecretNetwork

Since different blockchains support different client versions, it is necessary to select a version that matches the testnet to be used during development.

terminal
// Install gaiad
$ git clone -b <latest-release-tag> https://github.com/cosmos/gaia.git
$ cd gaia && make install

// Install wasmd
$ git clone -b <latest-release-tag> https://github.com/cosmwasm/wasmd.git
$ cd wasmd && make install && make test

After the update, gaiacli and wasmcli have been integrated into gaiad and wasmd. Secretcli` remains independent.

Set environment variables for ease of operation later. This example assumes that the Musselnet test net is used.

terminal
$ nano ~/.cosmwasm-env
$ source ~/.cosmwasm-env

The settings are as follows:

.cosmwasm-env
export MUSSELNET_NODE=(--node https://rpc.musselnet.cosmwasm.com:443)
export MUSSELNET_TXFLAG=($MUSSELNET_NODE --chain-id musselnet-4 --gas-prices 0.025ucosm --gas auto --gas-adjustment 1.3)

Control an account

terminal
// Create a new account
$ wasmd keys add fred

// Show the address of the account named "fred"
$ wasmd keys show -a fred

// Details of the account on musselnet-4
// "q" is the shorthand of "query"
$ wasmd q account $(wasmd keys show -a fred) $MUSSELNET_NODE

// Get the balance of the native token on musselnet-4
$ wasmd q bank balances $(wasmd keys show -a fred) $MUSSELNET_NODE

Faucet

Different chains use different methods of faucet, some use curl, some use REST, and so on.

On Musselnet, the CosmWasm testnet, you can receive a fee token by POSTing JSON with curl; you must ask for the staking token in discord.

terminal
$ JSON=$(jq -n --arg addr $(wasmd keys show -a fred) '{"denom":"umayo","address":$addr}')
// "-H" is the shorthand of "--header"
$ curl -X POST -H "Content-Type: application/json" --data "$JSON" https://faucet.musselnet.cosmwasm.com/credit

Usage of a contract

Compile with optimizer

When deploying a contract to the testnet, it must be compiled using the Optimizer container. Simply running cargo wasm will not be accepted.

Optimizers are prepared differently for each contract, and this time we used cosmwasm/workspace-optimizer since we used cosmwasm-plus as a base.

The OS/ARCH of the Docker container is linux/amd64, so M1 Mac (with arm64-enabled Docker Desktop RC 2) could not run it with an error (Rosetta 2 is also not supported). Please use Intel CPU or something.

In the case of cosmwasm-plus, the command is as follows

terminal
$ 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/workspace-optimizer:0.10.7

If you run it in the root directory, it will compile for all directories under . /contracts/, it will compile all directories under . /artifacts/. The rust-optimizer used in cosmwasm-examples has a part to describe what to compile, so it is possible to process only specific contracts, but it does not seem to be possible with this command. It is not possible to do so with this command. It takes a fair amount of time.

Store

After compilation is complete, save the contract on the chain.

terminal
$ RES=$(wasmd tx wasm store ./artifacts/CONTRACT.wasm --from fred $MUSSELNET_TXFLAG -y)
gas used: 1234567
$ CODE_ID=$(echo $RES | jq -r '.logs[0].events[0].attributes[-1].value')

CODE_ID is a number assigned in the order of contract upload.

Deployment is not yet complete at this point. Because the contract has not been initialized, no contract address has been assigned.

Instantiate

Initializes a contract and makes it available for use. The initialized contract is specified by its ID (CODE_ID).

terminal
$ INIT='{}'
$ wasmd tx wasm instantiate $CODE_ID $INIT --label 'hoge' $MUSSELNET_TXFLAG -y

INIT message must contain a message for the contract. If the message is empty, no value is passed, as shown above. If you want to transfer a native token at initialization, use --amount. The contents of the label can be specified freely.

Always use the JSON format when passing messages to contracts, not just here. If you use double quotation marks when entering JSON as a string in the terminal, you need to escape them when specifying the string inside JSON. When single quotation marks are used, there is (almost) no problem (because only double quotation marks can be used to identify key and value in JSON).

Once initialized, the contract address can be obtained as follows.

terminal
$ CONTRACT=$(wasmd query wasm list-contract-by-code $CODE_ID $MUSSELNET_NODE -o json | jq -r '.contract_infos[0].address')

Execute

terminal
$ EXECUTE='{}'
$ wasmd tx wasm execute $CONTRACT $EXECUTE --from fred $MUSSELNET_TXFLAG -y

EXECUTE message is changed according to the contract's runtime message. Again, use --amount, etc. as needed for native tokens.

Query

terminal
$ QUERY='{}'
$ wasmd query wasm contract-state smart $CONTRACT $QUERY $MUSSELNET_NODE

Since the query does not require gas, the parameters set in MUSSELNET_TXFLAG and the execution account specified by --from are not necessary.

CW20トークン発行・操作

Try to mint CW20 token using cw20-base. You can check the require parameters in ./cw20-base/src/msg.rs.

terminal
$ git clone https://github.com/CosmWasm/cosmwasm-plus.git && cd cosmwasm-plus

// Compile at the root directory
$ 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/workspace-optimizer:0.10.7

// Store the code
$ RES=$(wasmd tx wasm store ./artifacts/cw20-base.wasm --from fred $MUSSELNET_TXFLAG -y)
gas used: 1234567
$ CODE_ID=$(echo $RES | jq -r '.logs[0].events[0].attributes[-1].value')

// Instantiate
$ INIT=$(jq -n --arg fred $(wasmd keys show -a fred) '{"name":"test","symbol":"TST","decimals":6,"initial_balances":{"address":$fred,"amount":"10000"}}')
$ wasmd tx wasm instantiate $CODE_ID $INIT --label 'test' $MUSSELNET_TXFLAG -y
$ CONTRACT=$(wasmd query wasm list-contract-by-code $CODE_ID $MUSSELNET_NODE -o json | jq -r '.contract_infos[0].address')

// Query fred's balance of the CW20 token
$ QUERY=$(jq -n --arg fred $(wasmd keys show -a fred) '{"balance":{"address":$fred}}')
$ wasmd query wasm contract-state smart $CONTRACT $QUERY $MUSSELNET_NODE

// Execute the contract
$ EXECUTE='{"transfer":{"recipient":"wasmADDRESSOFRECIPIENT","amount":"1000"}}'
$ wasmd tx wasm execute $CONTRACT $EXECUTE --from fred $MUSSELNET_TXFLAG -y

The key in the message is CamelCase, but when input in JSON, snake_case is used. This is probably due to the following sentence.

sample.rs
#[serde(rename_all = "snake_case")]

For CW20 remittance, only amount is required, while denom is required for native token remittance, because the CW20 contract is specified and denom is necessarily fixed.

Note that amount is declared as Uint128 type on the contract, so it must be passed as a string when given as JSON. Note that the type of decimals given as a number is u8.

Executing a contract using CW20

When a native token is transferred to a contract for processing, it can be passed as --amount, which is processed as info.sent_funds in the contract. In this case, the transaction is handled as info.sent_funds in the contract.

However, this is not the case with CW20. If you want to handle an already issued CW20 in another contract, use the CW20 contract's Send message to throw a message to the other contract.

For example, to use a CW20-ICS20 contract to transfer CW20 to another Cosmos blockchain via an IBC, the following is used.

terminal
$ MSG=$(jq -n --arg fred $(gaiad keys show -a fred) '{"channel":"channel-0","remote_address":$fred,"timeout":3600}' | base64) 
$ EXECUTE=$(jq -n --arg msg $MSG '{"send":{"contract":"wasmALTERNATIVECONTRACT","amount":"1000","msg":$msg}}')
$ wasmd tx wasm execute $CONTRACT $EXECUTE --from fred $MUSSELNET_TXFLAG -y

Reference

mktia's note

Research & Engineering / Blockchain / Web Dev

© 2017-2025 mktia. All rights reserved.