DEV Community

Vu
Vu

Posted on

Writing a smart contract with CosmWasm (Part 3)

Introduction

Welcome back to my series. Sorry for this belated, we will discover the execute contract in this part.
In the previous part, I explained that execute will change the smart contract data.
Let's implement this.

Create the message to execute smart contracts.

First, we need to define the ExecuteMsg and ExecuteResponse by updating the following code in the src/msg.rs:

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum ExecuteMsg {
    Update {},
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub struct ExecuteResponse {
    pub counter: i32,
}

Enter fullscreen mode Exit fullscreen mode

Image description

ExecuteMsg is an enum and has an Updates arm. This arm is simply to help counter variable increment 1 when we execute the contract.

Let's create a file: execute.rs in the src folder. Our logic will be in this file.
First, import module execute to lib.rs

We are implementing a function with the name try_update_counter (don't use camel case, convention in rust use dash to connect word in name of function or variable) and we pass the deps as a parameter. The function return a Result<Response, ContractError>. All logic of function will be as below:

#[cfg(not(feature = "library"))]
use cosmwasm_std::{to_binary, DepsMut, Response};

use crate::error::ContractError;
use crate::msg::ExecuteResponse;
use crate::state::{State, STATE};

pub fn try_update_counter(deps: DepsMut) -> Result<Response, ContractError> {
    let current_state = STATE.load(deps.storage)?;
    let mut current_counter = current_state.counter;

    current_counter += 1;

    let new_state = State {
        counter: current_counter,
    };
    STATE.save(deps.storage, &new_state)?;

    let resp = to_binary(&ExecuteResponse {
        counter: current_counter,
    })
    .unwrap();
    Ok(Response::new().set_data(resp))
}
Enter fullscreen mode Exit fullscreen mode

Image description

Yea, I think it's easy to read and understand: we get the counter in the current state of the contract current.counter, increment it by 1 and save it in the contract's store again, return it as a response.

But we have a wonder: where to implement try_update_counter. We need to open src / contract.rs and update it with the function below the immediate second entry point.

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn execute(
    deps: DepsMut,
    _env: Env,
    _info: MessageInfo,
    msg: ExecuteMsg,
) -> Result<Response, ContractError> {
    match msg {
        ExecuteMsg::Update {} => try_update_counter(deps),
    }
}

Enter fullscreen mode Exit fullscreen mode

Image description

Alright, we have implemented the second entry point of our contract! Don't forget to test the logic!
Next to the Testing module section.

Testing module

Still in the file: execute.rs. and update the below code to bottom:

#[cfg(test)]
mod tests {
    use crate::contract::{execute, instantiate};
    use crate::msg::{ExecuteMsg, ExecuteResponse};
    use crate::state::STATE;

    use crate::msg::InstantiateMsg;
    use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
    use cosmwasm_std::to_binary;

    const ADDR: &str = "addr";

    #[test]
    fn test_execute() {
        let mut deps = mock_dependencies();
        let env = mock_env();
        let info = mock_info(ADDR, &[]);
        let expect_data = to_binary(&ExecuteResponse { counter: 1 }).unwrap();
        let expect_number = 2;

        // instantiate msg
        let msg = InstantiateMsg {};
        let _resp = instantiate(deps.as_mut(), env.clone(), info.clone(), msg).unwrap();

        // execute one time
        let msg = ExecuteMsg::Update {};
        let resp = execute(deps.as_mut(), env.clone(), info.clone(), msg).unwrap();
        println!("Execute once!");
        assert_eq!(resp.data, Some(expect_data));

        // execute two time
        let msg = ExecuteMsg::Update {};
        let _resp = execute(deps.as_mut(), env, info, msg);
        let current_state = STATE.load(deps.as_mut().storage).unwrap();
        println!("Execute twice!");
        assert_eq!(current_state.counter, expect_number);
    }
}
Enter fullscreen mode Exit fullscreen mode

Image description

We create a module test and unit test function test_execute.
We still import all dependencies and create two values, one for a response from try_update_counter is expected_data and one compare with our state is expected_number.

We also import InstantiateMsg to create our state, of cause, counter will be 0.
One time to execute, we call execute function, update counter equal 1 and assert_eq resp.data with expect_data
Two time to execute, we also call execute function, update counter equal 2 and assert_eq current_state.counter with expect_number.

Very simple to understand.

Run cargo-test in the terminal

If your output is like the below, everything is right:

running 2 tests
test contract::tests::test_instantiate ... ok
test execute::tests::test_execute ... ok

test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests cw-starter

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Enter fullscreen mode Exit fullscreen mode

It run 1 test case, and 1 passed.

Summary

We just have created second entry point of smart contract is execute: ExecuteMsg, ExecuteResponse and try_update_counter.
We also create a module for testing this.

I will mention the last entry point naming: query in the next part.
Thanks, everyone to read the post.

Top comments (0)