Try   HackMD

Walking through a simple contract written in Rust - Part 1

In this tutorial, we will be following the official documentation to build a simple Options contract since it is an excellent place to start - but we will go the full way from absolute scratch. I will explaining all my steps. Here is the original documentation.

Taken from Investopedia, here is what options mean

Options are financial instruments that are derivatives based on the value of underlying securities such as stocks.

Cargo is a Rust package manager. We will need cargo-generate, which allows us to set up Rust code using defined templates.

cargo install cargo-generate --features vendored-openssl

We will be using this repo to scaffold our project. For the

cargo generate --git https://github.com/CosmWasm/cosmwasm-template.git --name simple-option

This will generate a folder for us with the general cosmwasm template.

Cargo.toml     examples      LICENSE  Publishing.md  rustfmt.toml  src
Developing.md  Importing.md  NOTICE   README.md      schema

Architecture of the project

To go forward, it will be important to understand how the files work and interact with each other.

If you go to src folder, you'll find lib.rs: This is where your wasm bindings are contained. Think of it as a wrapper for the smart contract functionalities around Rust functions. We won't be needing to touch this during this tutorial.

To start, let's have a look at msg.rs. This file is the entry point of our development.

It might be a little intimidating reading Rust for the first time, so we will break it down step-by-step using comments.

use crate::state::State; 
use cosmwasm_std::{Coin, HumanAddr};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize}; //Serde is a framework for serializing and deserializing Rust data structures efficiently and generically.

#[derive()] is an attribute in Rust that allows new items to be automatically created for data strctures. InitMsg structure has initial value that initializes the smart contract using the code and feeds in the data. Using the derive attribute, we implement specified traits (think of them as abstract methods equivalent from C++) for the structure.

pub struct InitMsg {
    // owner, creator and collateral are to be taken from tx-context
    //collateral is funds sent along the message (contract)
    pub counter_offer: Vec<Coin>, //counter_offer is strike price
    pub expires: u64, //The 64-bit unsigned integer type
}
pub enum HandleMsg {
    /// Owner can transfer to a new owner
    Transfer { recipient: HumanAddr },
    /// Owner can post counter_offer on unexpired option to execute and get the collateral
    Execute {},
    /// Burn will release collateral if expired
    Burn {},
}

The HumanAddr would be the prefixed public address of the recipient.

QueryMsg is a way for client to query the Smart Contract state. We will setup Config{}. ConfigResponse = State might seem a little confusing, but we will go through it step by step.

State handles the state of the database where smart contract data is stored and accessed.

This means that our database state is handled by the State variable pub static CONFIG_KEY: &[u8] = b"config"; configures the instance key.

pub struct State {
    pub creator: HumanAddr,
    pub owner: HumanAddr,
    pub collateral: Vec<Coin>,
    pub counter_offer: Vec<Coin>,
    pub expires: u64,
}

The State structure allows for a creator, an owner, a collateral (coins sent with contract message), counter offer (strike price) and TTL.

pub fn config(storage: &mut dyn Storage) -> Singleton<State> {
    singleton(storage, CONFIG_KEY)
}

Here we define a function config for which the parameter is storage. Storage can be of two types: Singleton and Structured. In Singleton, the storage saves one instance of the structure and has one unique key. Structured storage is complex and structured with different types of data relations. While this tutorial uses only a singleton storage, more complicated dApps will require more complicated storage formats - thus structured. For now, let us create a function that can access the state as read-only.

pub fn config_read(storage: &dyn Storage) -> ReadonlySingleton<State> {
    singleton_read(storage, CONFIG_KEY)
}

Now over to understanding contract.rs in part 2 Let's get the party started!