Написание смарт-контракта с помощью CosmWasm (часть 3)


Введение

Добро пожаловать снова в мою серию статей. Извините за такое опоздание, в этой части мы откроем для себя контракт execute.
В предыдущей части я объяснил, что execute будет изменять данные смарт-контракта.
Давайте реализуем это.

Создайте сообщение для выполнения смарт-контрактов.

Сначала нам нужно определить ExecuteMsg и ExecuteResponse, обновив следующий код в 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,
}

Вход в полноэкранный режим Выход из полноэкранного режима

ExecuteMsg является перечислением и имеет обновления arm. Это arm просто помогает переменной counter увеличиваться на 1, когда мы выполняем контракт.

Давайте создадим файл: execute.rs в папке src. Наша логика будет находиться в этом файле.
Сначала импортируем модуль execute в lib.rs.

Мы реализуем функцию с именем try_update_counter (не используйте верблюжий регистр, по традиции в rust используется dash для соединения слов в имени функции или переменной) и передаем deps в качестве параметра. Функция возвращает Result<Response, ContractError>. Вся логика работы функции будет выглядеть следующим образом:

#[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))
}
Вход в полноэкранный режим Выход из полноэкранного режима

Да, думаю, все просто: получаем счетчик текущего состояния контракта current.counter, увеличиваем его на 1 и снова сохраняем в хранилище контракта, возвращаем в качестве ответа.

Но у нас возникает вопрос: где реализовать try_update_counter. Нам нужно открыть src / contract.rs и обновить его функцией под второй точкой входа immediate.

#[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),
    }
}

Вход в полноэкранный режим Выйти из полноэкранного режима

Итак, мы реализовали вторую точку входа нашего контракта! Не забудьте протестировать логику!
Далее раздел Testing module.

Тестирующий модуль

Все еще в файле: execute.rs. и обновите приведенный ниже код:

#[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);
    }
}
Вход в полноэкранный режим Выйти из полноэкранного режима

Мы создаем тест модуля и функцию юнит-теста test_execute.
Мы по-прежнему импортируем все зависимости и создаем два значения, одно для ответа от try_update_counterexpected_data и одно для сравнения с нашим состоянием — expected_number.

Мы также импортируем InstantiateMsg для создания нашего состояния, поэтому counter будет равен 0.
Один раз для выполнения, мы вызываем функцию execute, обновляем счетчик, равный 1 и assert_eq resp.data с expect_data.
Два раза для выполнения, мы также вызываем функцию execute, обновляем счетчик равный 2 и assert_eq current_state.counter с expect_number.

Очень просто для понимания.

Запустите cargo-test в терминале.

Если ваш вывод выглядит как показано ниже, все правильно:

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
Вход в полноэкранный режим Выход из полноэкранного режима

Выполнен 1 тест, и 1 прошел.

Резюме

Мы только что создали вторую точку входа смарт-контракта execute: ExecuteMsg, ExecuteResponse и try_update_counter.
Мы также создаем модуль для тестирования.

Я упомяну о последней точке входа: query в следующей части.
Спасибо всем, кто прочитал этот пост.

Оцените статью
devanswers.ru
Добавить комментарий