Hello World

As always, the goal of this hello world exercise is to give you a feel of the technology, but also to superficially experience how the concepts you learned map to something more tangible. Don't hesitate to expand the collapsed sections if you want to dive deep on certain points, with a view to better understand the mechanics of it.

These first steps take inspiration from the CosmWasm docs' hello world, and from the Cosmos SDK's basic tutorial. The difference is that after you have downloaded all the packages and dependencies, you no longer need access to other online resources.

It offers two tracks, you can either do all your compilations and running natively on your computer, or achieve the same in Docker. If you choose the Docker path, that allows you to hold off installing anything other than Docker.

What will happen

Here are the steps you are going to complete:

  • Build the blockchain code.
  • Create a running Cosmos blockchain:
    • With a single running node.
    • With CosmWasm installed
  • Compile a smart contract code.
  • Store the code and deploy a smart contract instance.
  • Interact with it.

Let's get started.

Install pre-requisites. Either on your computer, or install Docker. Including JQ.

Build Your blockchain code

Clone wasmd, which is the simd of CosmWasm, at v0.53.2.

git clone https://github.com/CosmWasm/wasmd --branch v0.53.2
cd wasmd

Build the blockchain application.

make build

With this done, .build/wasmd or Docker's wasmd:0.53.2 is your blockchain executable.

Prepare your blockchain

After you have compiled your blockchain code, and before you can turn your focus to CosmWasm, you need a running blockchain. This step has you prepare the blockchain application, test keys and a genesis file.

Are you a validator?

If you have previously used wasmd, you may already have data in your ~/.wasmd folder. It is typically safe to erase the config sub-folder. But if you are a validator on one of the wasmd networks, this exercise is not safe, so stop right there or proceed at your own risks.

The Docker track also uses your local folder, by sharing it as a volume with the flag -v $HOME/.wasmd:/root/.wasmd, so that you can switch to the local track at a later stage. Make space in the ~/.wasmd folder:

rm -rf ~/.wasmd/config \
    ~/.wasmd/data \
    ~/.wasmd/wasm

Initialize the blockchain application's genesis file and other configuration files.

./build/wasmd init validator-1 \
    --chain-id learning-chain-1

If you are curious, you can see what was created in ~/.wasmd.

Then you can add a convenient but low-safety key for Alice, who shall become the validator operator.

./build/wasmd keys add alice \
    --keyring-backend test

Make a note of the seed phrase so that you can reuse it in a Node.js REPL console.

To become a validator, Alice needs an initial balance in the staking token. Give her one:

./build/wasmd genesis \
    add-genesis-account alice 100000000stake \
    --keyring-backend test

Have Alice create and sign the token-staking transaction that will make Alice an initial validator.

./build/wasmd genesis \
    gentx alice 70000000stake \
    --keyring-backend test \
    --chain-id learning-chain-1

The system tells you that a file has been created with a signed transaction in it. Add this signed transaction to the genesis file so that Alice starts indeed as a validator.

./build/wasmd genesis collect-gentxs

The blockchain preparation is now done, and you need not redo the above steps if you restart this exercise at a later date.

Run your blockchain

If you stopped wasmd earlier, you can come back here and restart wasmd where you left it off.

Run Alice's validating node to start the blockchain system and to interact with it.

./build/wasmd start

Your node is now running and ready to receive smart contracts.

Compile your smart contract

With a running chain, you can start interacting with the CosmWasm module. First you are going to download and compile a smart contract.

We will use the same nameservice smart contract of the long running exercise but at the intermediate add-first-library branch. In another terminal, start by cloning it and changing the directory.

git clone https://github.com/b9lab/cw-my-nameservice --branch add-first-library
cd cw-my-nameservice/contracts/nameservice

At this version of the contract, it compiles with Rust v1.80.1 to the Wasm target.

rustup install 1.80.1
rustup target add wasm32-unknown-unknown --toolchain 1.80.1
RUSTFLAGS='-C link-arg=-s' cargo +1.80.1 wasm

The compiled WebAssembly is located relative to the contract dir in ./target/wasm32-unknown-unknown/release/cw_my_nameservice.wasm. The file ends up at about 221 KB in size. In fact, the RUSTFLAGS='-C link-arg=-s' flag is there to reduce its size, always a concern in blockchain. You can remove the flag as a test, and you should see that it then ends up at 1.5 MB in size.

Copy the file to the ~/.wasmd so as to reuse it when storing the code on-chain. Make the folder if it is missing:

mkdir -p $HOME/.wasmd/wasm/code
cp $(pwd)/target/wasm32-unknown-unknown/release/cw_my_nameservice.wasm \
    $HOME/.wasmd/wasm/code/cw_my_nameservice.wasm

Note that the ~/.wasmd path is also accessible by the Docker container running the blockchain app, which makes it convenient for this exercise.

Store your contract code

With the bytecode of the smart contract ready, you are about to interact with the running chain again. Return to the wasmd directory, in a new shell.

Start with a simple initial CosmWasm query to see what code, if any, has already been stored.

./build/wasmd query wasm list-code

As expected, it returns:

code_infos: []
pagination: ...

It is time to store your first CosmWasm code with the tx wasm store command. Alice, who owns stake tokens, can do it.

./build/wasmd tx wasm store $HOME/.wasmd/wasm/code/cw_my_nameservice.wasm \
    --from alice --keyring-backend test \
    --gas-prices 0.25stake --gas auto --gas-adjustment 1.3 \
    --chain-id learning-chain-1 \
    --yes --output json --broadcast-mode sync

With --broadcast-mode sync, the command sends a transaction, but does not wait for it to be confirmed. Instead, you get succinct information, including the transaction hash:

{"height":"0","txhash":"34087EB0B74233E7E3C3AA9CE6EFCB4279130AF1C2BCAE992DD1E1D1775D02ED","codespace":"","code":0,"data":"","raw_log":"","logs":[],"info":"","gas_wanted":"0","gas_used":"0","tx":null,"timestamp":"","events":[]}

Make a note of this txhash, such as:

ns_store_txhash=34087EB0B74233E7E3C3AA9CE6EFCB4279130AF1C2BCAE992DD1E1D1775D02ED

With your specific value.

Verify your stored code

The newly stored code has a newly created id that you need to know in order to use it. The authoritative way is to retrieve the code information from the transaction's events itself. The event of interest has the type: "store_code":

./build/wasmd query tx $ns_store_txhash --output json \
    | jq '.events[] | select(.type == "store_code")'

Which returns something like:

{
  "type": "store_code",
  "attributes": [
    {
      "key": "code_checksum",
      "value": "98f9924c5fbe94dd6ad24d71f2352593e54aac6aabcfaa9b1bf000f64b33992d",
      "index": true
    },
    {
      "key": "code_id",
      "value": "1",
      "index": true
    },
    {
      "key": "msg_index",
      "value": "0",
      "index": true
    }
  ]
}

There is a code id, predictably at 1, and a code checksum.

The msg_index is here to assist you with identifying which code is which, in the rare case where you store two or more codes in a single transaction.

Make a note of the code id as you will use it during instantiation:

ns_code_id=$(./build/wasmd query tx $ns_store_txhash --output json \
    | jq -r '.events[] | select(.type == "store_code") .attributes[] | select(.key == "code_id") .value')

Does the code checksum match? Let's check:

sha256sum $HOME/.wasmd/wasm/code/cw_my_nameservice.wasm

You should see the same value as the one emitted in the event.

Deploy your contract instance

Now you have your bytecode stored on-chain, but no smart contract has been deployed using this code. You can confirm this with:

./build/wasmd query wasm list-contract-by-code $ns_code_id

This returns:

contracts: []
pagination: ...

Time to instantiate your first CosmWasm smart contract. The constructor requires a specific message:

msg.rs/InstantiateMsg
pub struct InstantiateMsg {
    pub minter: String,
}

Which has to be serialized as JSON. The minter is the account that will be allowed to register new names. To make it easy, you pick Alice as the minter. Get her address:

alice=$(./build/wasmd keys show alice \
    --keyring-backend test \
    --address)

The next action is to prepare the instantiate message by setting the right value:

ns_init_msg_1='{"minter":"'$alice'"}'

With the message ready, you can send the comand to instantiate your first smart contract:

./build/wasmd tx wasm instantiate $ns_code_id "$ns_init_msg_1" \
    --label "name service" --no-admin \
    --from alice --keyring-backend test \
    --chain-id learning-chain-1 \
    --gas-prices 0.25stake --gas auto --gas-adjustment 1.3 \
    --yes

Note that the meat of the command is instantiate $ns_code_id "$ns_init_msg_1", which would read as instantiate 1 "{"minter":"wasm1tev6xt4pgrnjpxwmvv7jrl8ph4x47wg5vcd0as"}".

Once again, you get a transaction hash. Make a note of it with your own hash, for instance:

ns_instantiate_txhash_1=9881879B2A7663638D6DA81D6F9ECC7DBF8AA12B105817B06A761749A22764E1

Retrieve your contract address

At this stage, what is important is the address at which your contract instance resides. This is the address you will use to interact with your instantiated contract. The authoritative way to get this information is to get it from the events, more precisely at the event of type "instantiate":

./build/wasmd query tx $ns_instantiate_txhash_1 \
    --output json | jq '.events[] | select(.type == "store_code") .attributes[] | select(.key == "code_id") .value'

This returns something like:

{
  "type": "instantiate",
  "attributes": [
    {
      "key": "_contract_address",
      "value": "wasm14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s0phg4d",
      "index": true
    },
    {
      "key": "code_id",
      "value": "1",
      "index": true
    },
    {
      "key": "msg_index",
      "value": "0",
      "index": true
    }
  ]
}

Here too, msg_index assists you when you have more than one instantiation in one transaction. Your smart contract address is wasm14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s0phg4d, which you can retrieve with:

ns_addr1=$(./build/wasmd query tx $ns_instantiate_txhash_1 \
    --output json | jq -r '.events[] | select(.type == "instantiate") .attributes[] | select(.key == "_contract_address") .value')

Note how the address is much longer than a regular address, like Alice's. With the contract instantiated, you can query a few things about it.

Send a transaction to your contract

The smart contract you just instantiated is made to register names. As your first transaction, you will register the name "queen-of-the-hill" and map it to Alice. What message does the execute function expect? It expects this:

Execute message in msg.rs
pub enum ExecuteMsg {
    Register { name: String, owner: Addr },
}

CosmWasm serializes an enum such as ExecuteMsg by prefixing the value proper with the type, here Register. Since you want to register Alice at the name, your register message, with its two fields, is:

ns_register_queen_to_alice='{"register":{"name":"queen-of-the-hill","owner":"'$alice'"}}'

As can be seen in the execute_register function, only the minter can send an ExecuteMsg::Register message:

Minter gatekeeping in contract.rs
MINTER
    .assert_owner(deps.storage, &info.sender)
    .map_err(ContractError::from_minter(&info.sender))?;

And as you recall from the instantiation message, Alice is the minter. So Alice has to send this transaction. This smart contract does not need funds, but as a vehicle to demonstrate the concept, you attach funds of 100 stake to the call:

./build/wasmd tx wasm execute $ns_addr1 "$ns_register_queen_to_alice" \
    --amount 100stake \
    --from alice --keyring-backend test \
    --chain-id learning-chain-1 \
    --gas-prices 0.25stake --gas auto --gas-adjustment 1.3 \
    --yes

Once more, make a note of the transaction hash. For instance:

ns_register_queen_to_alice_txhash=7966EBDD3766243FFFFE70D0A360305DE11B0BE77A305470D23D376B65432451

Send a query to your contract

Has the name been duly registered, and is there a convenient way to verify? Yes, first create the resolve message so that it follows the expected query type:

msg.rs/QueryMsg
pub enum QueryMsg {
    ResolveRecord { name: String },
}

This is another enum which again is serialized by prefixing with the snake-case variant:

ns_resolve_queen='{"resolve_record":{"name":"queen-of-the-hill"}}'

Then you pass it as a query to the smart contract:

./build/wasmd query wasm contract-state smart $ns_addr1 "$ns_resolve_queen"

Which returns as expected:

data:
  address: wasm1tev6xt4pgrnjpxwmvv7jrl8ph4x47wg5vcd0as

Congratulations! You have updated the name service smart contract and confirmed it.

Conclusion

Here is a summary of what you accomplished:

  • You compiled a blockchain that supports CosmWasm.
  • You initialized and ran it.
  • You compiled a smart contract.
  • You stored a bytecode on-chain.
  • You instantiated a smart contract using your bytecode.
  • You had your smart contract save information in its state with the use of a transaction.
  • You interrogated your smart contract about its state with the use of a query.

If you did not on the first pass, go back and expand the collapsed sections to learn more.

If you are keen on doing a couple of other hello-world-like exercises before plunging into smart contract writing, try Neutron's Remix IDE's tutorial or WasmKit's tutorial.

If you feel ready to start an exercise that will take you from creating your own smart contract, to testing it and managing it, head to the next section: first contract.