How to create smart Contract on Cosmos

Intermediate

 

How to create smart Contract on Cosmos

Setup

Go

You can set up Go following the official documentation. The latest versions of wasmd require go version v1.18+.

Rust

Assuming you have never worked with Rust, you will first need to install some tooling. The standard approach is to use rustup to maintain dependencies and handle updating multiple versions of cargo and rustc, which you will be using.

First, download and execute rustup-init.exe from rustup.rs or rust-lang.org.

Optionally:

Download and install gvim, and modify the environment variables to add \<gvim folder> to the PATH.

Download and install git for windows. Modify the environment variables to add \<git folder>\bin to PATH.

Turn on Developer Mode (Settings -> Update and Security: For Developers) and enable Device Discovery, to be able to access the Windows 10 server through ssh (

).

Install the wasm32 target:

rustup default stable
cargo version
# If this is lower than 1.55.0, update
rustup update stable

rustup target list --installed
rustup target add wasm32-unknown-unknown

 

wasmd

wasmd is the backbone of the CosmWasm platform. It is the implementation of a Cosmos zone with wasm smart contracts enabled.

Run the following commands to install wasmd:

git clone https://github.com/CosmWasm/wasmd.git
cd wasmd
# If you are updating wasmd, first update your local repository by fetching the remote tags available
git fetch --tags
# replace the v0.27.0 with the most stable version on https://github.com/CosmWasm/wasmd/releases
git checkout v0.27.0
make install

# verify the installation
wasmd version

 

 

Setting up command-line tools

We will be using a few command-line tools extensively:

 
 
apt install jq curl

 

Setting up Environment

You need an environment to run contracts. You can either run your node locally or connect to an existing network. For easy testing, the Malaga testnet is live. You can use the testnet to deploy and run your contracts.

To verify that the testnet is currently active, make sure the following URLs are working for you:

The testnet has two native tokens set up – Andalucía (uand) for becoming a validator and Málaga (umlg) for paying fees.

Available Block Explorers:

You can use the block explorer to explore transactions, addresses, validators and contracts.

When interacting with the testnet, you can either use wasmd which is a Go client, or the Node REPL. The Node REPL is recommended for contract operations, since JSON manipulation is not intuitive with the Shell/Go client.

Setting up wasmd Go CLI

Let’s configure the wasmd executable, point it to the testnet, create a wallet and ask for tokens from faucet:

First source the network configuration for the Malaga testnet in the shell:

source <(curl -sSL https://raw.githubusercontent.com/CosmWasm/testnets/master/malaga-420/defaults.env)

 

Set up wallet addresses:

# add wallets for testing
wasmd keys add wallet

wasmd keys add wallet2

 

You need some tokens in your address to interact with the network. If you are using local node you can skip this step. Requesting tokens from faucet:

JSON=$(jq -n --arg addr $(wasmd keys show -a wallet) '{"denom":"umlg","address":$addr}') && curl -X POST --header "Content-Type: application/json" --data "$JSON" https://faucet.malaga-420.cosmwasm.com/credit
JSON=$(jq -n --arg addr $(wasmd keys show -a wallet2) '{"denom":"umlg","address":$addr}') && curl -X POST --header "Content-Type: application/json" --data "$JSON" https://faucet.malaga-420.cosmwasm.com/credit

 

 

Export wasmd Parameters

wasmd client requires to be further configured in order to interact with different testnets. Each testnet has its own endpoints and system parameters.

An effective way to configure wasmd is to define the following environment variables, making use of the network configuration parameters we sourced earlier.

If you intend to use wasmd as your preferred client, we recommend you to set up these variables. Otherwise you will have to type in node, chain id and gas-price details with every command you execute.

# bash
export NODE="--node $RPC"
export TXFLAG="${NODE} --chain-id ${CHAIN_ID} --gas-prices 0.25${FEE_DENOM} --gas auto --gas-adjustment 1.3"

# zsh
export NODE=(--node $RPC)
export TXFLAG=($NODE --chain-id $CHAIN_ID --gas-prices 0.25$FEE_DENOM --gas auto --gas-adjustment 1.3)

 

If the commands above throws an error, it means you are utilizing a different type of shell. If there are no errors, try executing the following command:

wasmd query bank total $NODE

 

A response similar to the one below means that you can now interact with the node you have configured.

 
pagination:
  next_key: null
  total: "2"
supply:
- amount: "10006916235913"
  denom: uand
- amount: "10000000000000"
  denom: umlg

 

You can check that your credit request from the faucet has been successful by checking the balance of your wallet addresses by running the following commands:

wasmd query bank balances $(wasmd keys show -a wallet) $NODE
wasmd query bank balances $(wasmd keys show -a wallet2) $NODE

 

You can explore the details about various other wasmd commands by running

wasmd help

 

 

Setting up the CosmJS CLI client

Beyond the standard CLI tooling, CosmJS was developed as a flexible TypeScript library, which runs in Node.js as well as in modern browsers. Among other capabilities, the library supports smart contract queries and transactions. Along with this library, the @cosmjs/cli was developed to act as a super-charged Node console. It supports the keyword await, does type checking for helpful error messages, and preloads many CosmJS utilities. If you are comfortable with the Node console, you will probably find CosmJS CLI easier and more powerful than the wasmd Go CLI tooling.

To initialize a CosmJS CLI session, node.js 12+ and npx must be installed first.

Run the following command to start the Node REPL:

npx @cosmjs/cli@^0.28.1 --init https://raw.githubusercontent.com/InterWasm/cw-plus-helpers/main/base.ts --init https://raw.githubusercontent.com/InterWasm/cw-plus-helpers/main/cw20-base.ts

 

With that, you should observe the initialization of an interactive session.

Now, let us create a new wallet account, display the address and check the account balance

// Create or load account
const [address, client] = await useOptions(malagaOptions).setup("password",".new.key");
// Display the wallet address
client.getAccount(address);

 

 

{
  address: 'wasm1llrnvtvhe5up6erf7mv7uh35efea567f5gejjr',
  pubkey: null,
  accountNumber: 53,
  sequence: 0
}
// Check the wallet balance
client.getBalance(address,"umlg")
{ denom: 'umlg', amount: '100000000' }

 

Downloading and Compiling a Contract

In this section, we will download the code for a sample contract and compile it into a wasm binary executable.

If you haven’t already, please review the environment setup instructions first and either configure the Node.js REPL or the wasmd Go CLI before you proceed.

Compiling and Testing the Contract Code

Let’s download the repository in which we keep cw-contracts and compile the existing code for a simple name service contract that mimics a name service marketplace.

First, clone the repo and try to build the wasm bundle:

# Download the repository
git clone https://github.com/InterWasm/cw-contracts
cd cw-contracts
git checkout main
cd contracts/nameservice

# compile the wasm contract with stable toolchain
rustup default stable
cargo wasm

 

 

The compilation should output the file target/wasm32-unknown-unknown/release/cw_nameservice.wasm. With a quick ls -lh you can see that the file size is around 1.8 MB. This is a release build, but not stripped of all the unneeded code. To produce a much smaller version, you can run the following command which tells the compiler to strip the unused parts of the code out:

 

RUSTFLAGS='-C link-arg=-s' cargo wasm

 

After some compilation steps, you should see:

 
running 15 tests
test tests::tests::proper_init_no_fees ... ok
test tests::tests::fails_on_register_insufficient_fees ... ok
test coin_helpers::test::assert_sent_sufficient_coin_works ... ok
test tests::tests::fails_on_register_wrong_fee_denom ... ok
test tests::tests::fails_on_register_already_taken_name ... ok
test tests::tests::fails_on_transfer_from_nonowner ... ok
test tests::tests::fails_on_transfer_insufficient_fees ... ok
test tests::tests::fails_on_transfer_non_existent ... ok
test tests::tests::proper_init_with_fees ... ok
test tests::tests::register_available_name_and_query_works ... ok
test tests::tests::register_available_name_fails_with_invalid_name ... ok
test tests::tests::returns_empty_on_query_unregistered_name ... ok
test tests::tests::register_available_name_and_query_works_with_fees ... ok
test tests::tests::transfer_works ... ok
test tests::tests::transfer_works_with_fees ... ok

test result: ok. 15 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out;

 

RUST_BACKTRACE=1 will provide you with full stack traces on any error, which is super useful. This only works for unit tests (which test native rust code, not the compiled wasm). Also, if you want to know where cargo wasm and cargo unit-test come from, they are just aliases defined in the file .cargo/config located in the project directory. Take a look at the file contents to understand the cargo flags better.

Optimized Compilation

To reduce gas costs, the binary size should be as small as possible. This will result in a less costly deployment, and lower fees on every interaction. Luckily, there is tooling to help with this. You can optimize production code using the rust-optimizer. rust-optimizer produces reproducible builds of CosmWasm smart contracts. This means third parties can verify that the contract is actually the claimed code.

Navigate to the project root and run the following command:

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

 

 

On Windows, you can use the following command instead

 
docker run --rm -v ${pwd}:/code `
 --mount type=volume,source="$("$(Split-Path -Path $pwd -Leaf)")_cache",target=/code/target `
 --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry `
 cosmwasm/rust-optimizer:0.12.6

 

The binary will be under the folder artifacts and its size will be 138 kB.