Skip to main content

Build first Contract

What is a gotabit smart contract

A "smart contract" is simply a program that runs on the GotaBit blockchain. It's a collection of code (its functions) and data (its state) that resides at a specific address on the GotaBit blockchain. GotaBit uses CosmWasm smart contract platform. It support WebAssembly (Wasm). So everyone can write smart contract in most of language(Recommend using Rust)

Build first contract

Let me explain a simple contract example written by Rust As smart contracts are Rust library crates, we will start with creating one:

cargo new --lib ./demo-contract

You created a simple Rust library, but it is not yet ready to be a smart contract. The first thing to do is to update the Cargo.toml file:

[package]
name = "demo-contract"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
crate-type = ["cdylib", "rlib"]

[dependencies]
cosmwasm-std = "1.1.0"
cw-storage-plus = "0.16.0"
serde = { version = "1.0.144", default-features = false, features = ["derive"] }
thiserror = "1.0.38" # derive(Error)

To start, we will go with three basic entry points:

  • instantiate which is called once per smart contract lifetime - you can think about it as a constructor or initializer of a contract.
  • execute for handling messages which are able to modify contract state - they are used to perform some actual actions.
  • query for handling messages requesting some information from a contract; unlike execute, they can never affect any contract state, and are used just like database queries.

Go to your src/lib.rs file Mini contract demo

use cosmwasm_std::{
entry_point, to_binary, Binary, Deps, DepsMut, Empty, Env, MessageInfo, Response, StdResult,
};
use cw_storage_plus::Item;
use serde::{Deserialize, Serialize};
use thiserror::Error;

/// Global config.
pub const CONFIG: Item<State> = Item::new("CONFIG");

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub struct State {
pub nonce: u32,
}

/// Contract instantiate entry point
#[entry_point]
pub fn instantiate(
deps: DepsMut,
_env: Env,
_info: MessageInfo,
_msg: Empty,
) -> StdResult<Response> {
CONFIG.save(deps.storage, &State { nonce: 0 })?;
Ok(Response::new())
}

/// Execute msg enum
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum ExecuteMsg {
Call {},
}

/// Error enum
#[derive(Error, Debug, PartialEq)]
pub enum ContractError {}

/// Execute entry point
#[entry_point]
pub fn execute(
deps: DepsMut,
_env: Env,
info: MessageInfo,
msg: ExecuteMsg,
) -> Result<Response, ContractError> {
match msg {
ExecuteMsg::Call {} => call_handle_fn(deps, info),
}
}

/// call counter handle function
pub fn call_handle_fn(deps: DepsMut, _info: MessageInfo) -> Result<Response, ContractError> {
let mut state = CONFIG.load(deps.storage).unwrap();
state.nonce += 1;
CONFIG.save(deps.storage, &state).unwrap();

Ok(Response::new())
}


#[derive(Serialize, Deserialize)]
struct QueryResp {
nonce: u32,
}

/// Query entry point
#[entry_point]
pub fn query(_deps: Deps, _env: Env, _msg: Empty) -> StdResult<Binary> {
let state = CONFIG.load(_deps.storage)?;
let resp = QueryResp { nonce: state.nonce };

to_binary(&resp)
}

And then we implement a simple smart contract support write/read.

Build with docker

After installing docker properly, we can use docker to build our smart contract. This is a Docker build with a locked set of dependencies to produce reproducible builds of cosmwasm smart contracts. It also does heavy optimization on the build size, using binary stripping and wasm-opt.

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

# list all wasm file
ls ./artifacts

And then if the build is successful, you can get the contract wasm file.