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 (
How to enable, login to, or disable Microsoft SSH Server in Windows 10 ).
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:
-
Big Dipper Block Explorer: https://block-explorer.malaga-420.cosmwasm.com
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
.