Terra Academy で公開されているチュートリアルのスマートコントラクト、クライアントコードの理解をしてみたかった

## 概要

今回は、前回構築したテラチェーン上のスマートコントラクトのプログラム、
およびクライアントコードの中身を実際にみて、
`Solidity`や`Solana`との比較が少しできたらなと思っています。

どの程度理解できるかわかりませんが、物は試しということでやってみます。


## スマートコントラクト

`terrain`で作成したスマートコントラクトのプログラムの中身を見てみます。

今回は、コアとなっている

- src/contract.rs
- src/msg.rs
- src/state.rs

をみてみます。


#### src/msg.rs

```rust
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct InstantiateMsg {
    pub count: i32,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum ExecuteMsg {
    Increment {},
    Reset { count: i32 },
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum QueryMsg {
    // GetCount returns the current count as a json-encoded number
    GetCount {},
}

// We define a custom struct for each query response
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct CountResponse {
    pub count: i32,
}


```


#### src/state.rs

```rust
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

use cosmwasm_std::Addr;
use cw_storage_plus::Item;

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct State {
    pub count: i32,
    pub owner: Addr,
}

pub const STATE: Item<State> = Item::new("state");

```


#### src/contract.rs

```rust
#[cfg(not(feature = "library"))]
use cosmwasm_std::entry_point;
use cosmwasm_std::{to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult};
use cw2::set_contract_version;

use crate::error::ContractError;
use crate::msg::{CountResponse, ExecuteMsg, InstantiateMsg, QueryMsg};
use crate::state::{State, STATE};

// version info for migration info
const CONTRACT_NAME: &str = "crates.io:my_terra_dapp";
const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn instantiate(
    deps: DepsMut,
    _env: Env,
    info: MessageInfo,
    msg: InstantiateMsg,
) -> Result<Response, ContractError> {
    let state = State {
        count: msg.count,
        owner: info.sender.clone(),
    };
    set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
    STATE.save(deps.storage, &state)?;

    Ok(Response::new()
        .add_attribute("method", "instantiate")
        .add_attribute("owner", info.sender)
        .add_attribute("count", msg.count.to_string()))
}

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn execute(
    deps: DepsMut,
    _env: Env,
    info: MessageInfo,
    msg: ExecuteMsg,
) -> Result<Response, ContractError> {
    match msg {
        ExecuteMsg::Increment {} => try_increment(deps),
        ExecuteMsg::Reset { count } => try_reset(deps, info, count),
    }
}

pub fn try_increment(deps: DepsMut) -> Result<Response, ContractError> {
    STATE.update(deps.storage, |mut state| -> Result<_, ContractError> {
        state.count += 1;
        Ok(state)
    })?;

    Ok(Response::new().add_attribute("method", "try_increment"))
}

pub fn try_reset(deps: DepsMut, info: MessageInfo, count: i32) -> Result<Response, ContractError> {
    STATE.update(deps.storage, |mut state| -> Result<_, ContractError> {
        if info.sender != state.owner {
            return Err(ContractError::Unauthorized {});
        }
        state.count = count;
        Ok(state)
    })?;
    Ok(Response::new().add_attribute("method", "reset"))
}

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {
    match msg {
        QueryMsg::GetCount {} => to_binary(&query_count(deps)?),
    }
}

fn query_count(deps: Deps) -> StdResult<CountResponse> {
    let state = STATE.load(deps.storage)?;
    Ok(CountResponse { count: state.count })
}
```



## クライアントコード

チュートリアルで使われたクライアントコードは、`Nextjs`で構成されていました。
ウォレット接続の鍵となるプログラムは、

```frontend/src/index.tsx

import { getChainOptions, WalletProvider } from '@terra-money/wallet-provider';
import App from './App';
import ReactDOM from 'react-dom';
import './index.css';

getChainOptions().then((chainOptions) => {
  ReactDOM.render(
    <WalletProvider {...chainOptions}>
      <App />
    </WalletProvider>,
    document.getElementById('root'),
  );
});


```

の`getChainOptions`, `WalletProvider`で　`App`以下を`Provider`ラッピングしているところです。

次に、`LocalTerra`上にデプロイされたスマートコントラクトへの`contractAddress`を指定しているのは　`refs.terrain.json`です。

```refs.terrain.json
{
  "localterra": {
    "my_terra_dapp": {
      "codeId": "11",
      "contractAddresses": {
        "default": "terra14khjxsvmklm8kg97vhrn5wc2tz3w57na5seeke3qnwashe4pxd3qmljkek"
      }
    }
  }
}


```

さらに、`client program` は　`terrain`の

```
terrain contract:generateClient my_terra_dapp --build-schema
```
によって自動生成され、`frontend/src/contract/clients/MyTerraDappClient.ts`
として格納され、実際に`App.tsx`から呼び出されています。



## まとめ

今回は、Terra Academy で公開されているチュートリアルコードのスマートコントラクト、
およびクライアントコードの理解をしてみたかったのですが、
結局のところ既存のコードを眺めるだけになってしまいました。

やはり、簡単でもいいので自分でカスタマイズしたスマートコントラクト、およびクライアントコードを実装してみて初めて、もっと理解が深まると思ったので、近いうちにやってみたいです。


みなさんもよいTerraライフを。



今回はこの辺で。


@kenmaro