메인 콘텐츠로 건너뛰기
이 섹션에서는 CosmWasm 스마트 컨트랙트 개발을 위한 환경 설정 방법을 설명합니다.

사전 요구 사항

시작하기 전에 rustup과 함께 최신 버전의 rustccargo가 설치되어 있는지 확인하세요. 현재 Rust v1.58.1+ 에서 테스트하고 있습니다. 또한 wasm32-unknown-unknown 타겟과 cargo-generate Rust crate가 설치되어 있어야 합니다. 다음 명령어로 버전을 확인할 수 있습니다:
rustc --version
cargo --version
rustup target list --installed
# wasm32가 위에 나열되지 않으면 다음을 실행하세요
rustup target add wasm32-unknown-unknown
# cargo-generate를 설치하려면 다음을 실행하세요
cargo install cargo-generate

목표

  • 카운터를 증가시키고 주어진 값으로 재설정하는 스마트 컨트랙트를 생성하고 상호작용합니다
  • CosmWasm 스마트 컨트랙트의 기본 사항을 이해하고, Injective에 배포하는 방법과 Injective 도구를 사용하여 상호작용하는 방법을 배웁니다

CosmWasm 컨트랙트 기본

스마트 컨트랙트는 블록체인에 내부 상태가 영구 저장되는 싱글톤 객체의 인스턴스로 간주할 수 있습니다. 사용자는 스마트 컨트랙트에 JSON 메시지를 보내 상태 변경을 트리거할 수 있으며, JSON 메시지 형식의 요청을 보내 상태를 쿼리할 수도 있습니다. 이러한 JSON 메시지는 MsgSendMsgExecuteContract와 같은 Injective 블록체인 메시지와 다릅니다. 스마트 컨트랙트 작성자로서 스마트 컨트랙트의 인터페이스를 구성하는 3가지 함수를 정의해야 합니다:
  • instantiate(): 컨트랙트 인스턴스화 중에 호출되어 초기 상태를 제공하는 생성자
  • execute(): 사용자가 스마트 컨트랙트의 메서드를 호출하려 할 때 호출됩니다
  • query(): 사용자가 스마트 컨트랙트에서 데이터를 가져오려 할 때 호출됩니다
샘플 카운터 컨트랙트에서는 하나의 instantiate, 하나의 query, 두 개의 execute 메서드를 구현합니다.

템플릿으로 시작하기

작업 디렉토리에서 다음 명령어를 실행하여 권장 폴더 구조와 빌드 옵션으로 스마트 컨트랙트를 빠르게 시작하세요:
cargo generate --git https://github.com/CosmWasm/cw-template.git --branch 1.0 --name my-first-contract
cd my-first-contract
이것은 스마트 컨트랙트의 기본 보일러플레이트와 구조를 제공하여 시작하는 데 도움이 됩니다. src/contract.rs 파일에서 표준 CosmWasm 진입점인 instantiate(), execute(), query()가 적절하게 노출되고 연결되어 있음을 확인할 수 있습니다.

State

CosmWasm State에 대해 더 자세히 알아보려면 문서를 참조하세요.
State는 스마트 컨트랙트 데이터가 저장되고 액세스되는 데이터베이스의 상태를 처리합니다. 시작 템플릿에는 다음과 같은 기본 상태, 싱글톤 구조체 State가 포함되어 있습니다:
  • count, execute() 메시지가 증가 또는 재설정하여 상호작용하는 32비트 정수
  • owner, MsgInstantiateContract의 발신자 address로, 특정 실행 메시지가 허용되는지 여부를 결정합니다
// src/state.rs
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");
Injective 스마트 컨트랙트는 Injective의 네이티브 LevelDB(바이트 기반 키-값 저장소)를 통해 영구 상태를 유지할 수 있습니다. 따라서 영구 저장하려는 모든 데이터에는 데이터를 인덱싱하고 검색하는 데 사용할 수 있는 고유한 키가 할당되어야 합니다. 데이터는 원시 바이트로만 영구 저장될 수 있으므로, 구조나 데이터 유형의 개념은 직렬화 및 역직렬화 함수 쌍으로 표현되어야 합니다. 예를 들어, 객체는 바이트로 저장되어야 하므로 블록체인에 저장하기 위해 객체를 바이트로 인코딩하는 함수와 바이트를 컨트랙트 로직이 이해할 수 있는 데이터 유형으로 다시 디코딩하는 함수를 모두 제공해야 합니다. 다행히 CosmWasm은 cosmwasm-storage와 같은 유틸리티 크레이트를 제공하며, 이는 구조체 및 Rust 숫자와 같이 일반적으로 사용되는 유형에 대한 직렬화 및 역직렬화를 자동으로 제공하는 “singleton” 및 “bucket”과 같은 데이터 컨테이너에 대한 편리한 고수준 추상화를 제공합니다. 또한 cw-storage-plus 크레이트는 더 효율적인 스토리지 메커니즘에 사용할 수 있습니다. State 구조체가 countowner를 모두 보유하는 것을 주목하세요. 또한 derive 속성이 적용되어 몇 가지 유용한 트레이트를 자동 구현합니다:
  • Serialize: 직렬화를 제공합니다
  • Deserialize: 역직렬화를 제공합니다
  • Clone: 구조체를 복사 가능하게 만듭니다
  • Debug: 구조체를 문자열로 출력할 수 있게 합니다
  • PartialEq: 동등성 비교를 제공합니다
  • JsonSchema: JSON 스키마를 자동 생성합니다
Addrinj로 시작하는 사람이 읽을 수 있는 Injective 주소를 참조합니다. 예: inj1clw20s2uxeyxtam6f7m84vgae92s9eh7vygagt.

InstantiateMsg

InstantiateMsg는 사용자가 MsgInstantiateContract를 통해 블록체인에 컨트랙트를 인스턴스화할 때 컨트랙트에 제공됩니다. 이것은 컨트랙트에 구성과 초기 상태를 제공합니다. Injective 블록체인에서는 컨트랙트 코드 업로드와 컨트랙트 인스턴스화가 별도의 이벤트로 간주됩니다. 이는 Ethereum과 다릅니다. 이는 검증된 소수의 컨트랙트 아키타입이 동일한 기본 코드를 공유하지만 다른 파라미터로 구성된 여러 인스턴스로 존재할 수 있도록 하기 위함입니다(하나의 정식 ERC20과 해당 코드를 사용하는 여러 토큰을 상상해 보세요).

예제

컨트랙트의 경우, 컨트랙트 생성자는 JSON 메시지로 초기 상태를 제공해야 합니다. 아래 메시지 정의에서 메시지가 초기 카운트를 나타내는 하나의 파라미터 count를 보유하고 있음을 볼 수 있습니다.
{
  "count": 100
}

메시지 정의

// src/msg.rs

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

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

컨트랙트 로직

contract.rs에서 첫 번째 진입점 instantiate()를 정의합니다. 이 곳에서 컨트랙트가 인스턴스화되고 InstantiateMsg가 전달됩니다. 메시지에서 count를 추출하고 다음과 같이 초기 상태를 설정합니다:
  • count에 메시지의 count가 할당됩니다
  • ownerMsgInstantiateContract의 발신자가 할당됩니다
// src/contract.rs
#[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()))
}

ExecuteMsg

CosmWasm ExecuteMsg에 대해 더 자세히 알아보려면 문서를 참조하세요.
ExecuteMsgMsgExecuteContract를 통해 execute() 함수에 전달되는 JSON 메시지입니다. InstantiateMsg와 달리 ExecuteMsg는 스마트 컨트랙트가 사용자에게 노출하는 다양한 유형의 함수를 설명하기 위해 여러 가지 다른 유형의 메시지로 존재할 수 있습니다. execute() 함수는 이러한 다양한 유형의 메시지를 적절한 메시지 핸들러 로직으로 역다중화합니다. 두 개의 ExecuteMsg가 있습니다: IncrementReset.
  • Increment는 입력 파라미터가 없으며 count 값을 1 증가시킵니다.
  • Reset은 32비트 정수를 파라미터로 받아 count 값을 입력 파라미터로 재설정합니다.

예제

Increment 모든 사용자가 현재 카운트를 1 증가시킬 수 있습니다.
{
  "increment": {}
}

Reset

소유자만 카운트를 특정 숫자로 재설정할 수 있습니다. 구현 세부 사항은 아래 로직을 참조하세요.
{
  "reset": {
    "count": 5
  }
}

메시지 정의

ExecuteMsg의 경우 enum을 사용하여 컨트랙트가 이해할 수 있는 다양한 유형의 메시지를 다중화할 수 있습니다. serde 속성은 속성 키를 snake case와 소문자로 다시 작성하므로, 직렬화 및 역직렬화 시 JSON에서 IncrementReset 대신 incrementreset을 사용하게 됩니다.
// src/msg.rs

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

로직

// src/contract.rs

#[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),
    }
}
이것은 execute() 메서드로, Rust의 패턴 매칭을 사용하여 수신된 ExecuteMsg를 적절한 처리 로직으로 라우팅합니다. 수신된 메시지에 따라 try_increment() 또는 try_reset() 호출을 디스패치합니다.
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"))
}
먼저, 키 state에 위치한 항목을 업데이트하기 위해 스토리지에 대한 가변 참조를 획득합니다. 그런 다음 새 상태로 Ok 결과를 반환하여 상태의 count를 업데이트합니다. 마지막으로, Response와 함께 Ok 결과를 반환하여 성공을 확인하며 컨트랙트 실행을 종료합니다.
// src/contract.rs

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"))
}
reset의 로직은 increment와 매우 유사하지만, 이번에는 먼저 메시지 발신자가 reset 함수를 호출할 수 있는 권한이 있는지 확인합니다(이 경우 컨트랙트 소유자여야 합니다).

QueryMsg

GetCount 쿼리 메시지에는 파라미터가 없으며 count 값을 반환합니다. 구현 세부 사항은 아래 로직을 참조하세요.

예제

템플릿 컨트랙트는 한 가지 유형의 QueryMsg만 지원합니다: GetCount 요청:
{
  "get_count": {}
}
반환값:
{
  "count": 5
}

메시지 정의

컨트랙트에서 데이터 쿼리를 지원하려면 QueryMsg 형식(요청을 나타냄)과 쿼리 출력 구조(이 경우 CountResponse)를 모두 정의해야 합니다. query()가 구조화된 JSON을 통해 사용자에게 정보를 다시 보내므로 응답의 형태를 알려야 하기 때문입니다. 자세한 내용은 JSON 스키마 생성을 참조하세요. src/msg.rs에 다음을 추가하세요:
// src/msg.rs
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum QueryMsg {
    // GetCount는 현재 카운트를 json 인코딩된 숫자로 반환합니다
    GetCount {},
}

// 각 쿼리 응답에 대한 사용자 정의 구조체 정의
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct CountResponse {
    pub count: i32,
}

로직

query()의 로직은 execute()와 유사하지만, query()는 최종 사용자가 트랜잭션을 만들지 않고 호출되므로 정보가 필요 없어 env 인수가 생략됩니다.
// src/contract.rs

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

단위 테스트

단위 테스트는 코드를 체인에 배포하기 전에 첫 번째 보증 수단으로 실행해야 합니다. 빠르게 실행되며 RUST_BACKTRACE=1 플래그를 사용하면 실패 시 유용한 백트레이스를 제공할 수 있습니다:
cargo unit-test // 유용한 백트레이스를 위해 RUST_BACKTRACE=1과 함께 실행하세요
단위 테스트 구현src/contract.rs에서 찾을 수 있습니다.

컨트랙트 빌드

이제 컨트랙트를 이해하고 테스트했으므로 다음 명령어를 실행하여 컨트랙트를 빌드할 수 있습니다. 이것은 다음 단계에서 컨트랙트를 최적화하기 전에 사전 오류를 확인합니다.
cargo wasm
다음으로, 체인에 업로드할 코드를 준비하기 위해 컨트랙트를 최적화해야 합니다.
프로덕션용 Wasm 바이트코드 준비에 대한 자세한 내용을 읽어보세요.
CosmWasm에는 작고 일관된 빌드 출력을 생성할 수 있는 최적화 컴파일러인 rust-optimizer가 있습니다. 도구를 사용하는 가장 쉬운 방법은 게시된 Docker 이미지를 사용하는 것입니다. 최신 x86 버전은 여기에서, ARM 버전은 여기에서 확인하세요. Docker가 실행 중인 상태에서 다음 명령어를 실행하여 컨트랙트 코드를 /code에 마운트하고 출력을 최적화하세요(먼저 디렉토리로 cd하지 않으려면 $(pwd) 대신 절대 경로를 사용할 수 있습니다):
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.12
ARM64 머신을 사용하는 경우 ARM64용으로 빌드된 docker 이미지를 사용해야 합니다:
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-arm64:0.12.12
CosmWasm은 ARM64 버전의 컴파일러가 Intel/AMD 버전과 다른 Wasm 아티팩트를 생성하기 때문에 사용을 권장하지 않습니다. 릴리스/프로덕션의 경우 Intel/AMD 옵티마이저로 빌드된 컨트랙트만 사용하는 것이 좋습니다. CosmWasm의 참고 사항은 여기를 참조하세요.
명령 실행 중 Unable to update registry `crates-io` 오류가 발생할 수 있습니다. 컨트랙트 디렉토리 내의 Cargo.toml 파일에 다음 줄을 추가하고 명령을 다시 실행해 보세요:
[net]
git-fetch-with-cli = true
자세한 내용은 The Cargo Book을 참조하세요.
이것은 PROJECT_NAME.wasm과 Wasm 파일의 Sha256 해시가 포함된 checksums.txt가 있는 artifacts 디렉토리를 생성합니다. Wasm 파일은 결정론적으로 컴파일됩니다(동일한 git 커밋에서 동일한 docker를 실행하는 다른 사람도 동일한 Sha256 해시를 가진 동일한 파일을 얻어야 합니다).

injectived 설치

injectived는 Injective에 연결하고 Injective 블록체인과 상호작용할 수 있게 해주는 명령줄 인터페이스 및 데몬입니다. CLI를 사용하여 로컬에서 스마트 컨트랙트와 상호작용하려면 injectived가 설치되어 있어야 합니다. injectived 설치 가이드의 설치 지침을 따르세요. 또는 이 튜토리얼을 더 쉽게 만들기 위해 Docker 이미지가 준비되어 있습니다.
바이너리에서 injectived를 설치하면 docker 명령을 무시하세요. 공개 엔드포인트 섹션에서 메인넷 및 테스트넷과 상호작용하기 위한 올바른 —node 정보를 찾을 수 있습니다.
이 명령을 실행하면 docker 컨테이너가 무기한 실행됩니다.
docker run --name="injective-core-staging" \
-v=<directory_to_which_you_cloned_cw-template>/artifacts:/var/artifacts \
--entrypoint=sh public.ecr.aws/l9h3g6c6/injective-core:staging \
-c "tail -F anything"
참고: directory_to_which_you_cloned_cw-template은 절대 경로여야 합니다. CosmWasm/cw-counter 디렉토리 내에서 pwd 명령을 실행하면 절대 경로를 쉽게 찾을 수 있습니다. 새 터미널을 열고 Docker 컨테이너에 들어가 체인을 초기화합니다:
docker exec -it injective-core-staging sh
나중에 필요한 jq 종속성을 추가하는 것으로 시작하겠습니다:
# "injective-core-staging" 컨테이너 내부에서
apk add jq
이제 로컬 체인 초기화를 진행하고 testuser라는 테스트 사용자를 추가할 수 있습니다(프롬프트가 나타나면 비밀번호로 12345678을 사용하세요). 테스트 사용자를 사용하여 나중에 테스트넷에서 메시지에 서명하는 데 사용될 새 개인 키를 생성합니다:
# "injective-core-staging" 컨테이너 내부에서
injectived keys add testuser
출력
- name: testuser
  type: local
  address: inj1exjcp8pkvzqzsnwkzte87fmzhfftr99kd36jat
  pubkey: '{"@type":"/injective.crypto.v1beta1.ethsecp256k1.PubKey","key":"Aqi010PsKkFe9KwA45ajvrr53vfPy+5vgc3aHWWGdW6X"}'
  mnemonic: ""

**Important** write this mnemonic phrase in a safe place.
It is the only way to recover your account if you ever forget your password.

wash wise evil buffalo fiction quantum planet dial grape slam title salt dry and some more words that should be here
주소를 기록하거나 env 변수로 내보내세요. 진행하려면 필요합니다:
# "injective-core-staging" 컨테이너 내부에서
export INJ_ADDRESS= <your inj address>
Injective 테스트 faucet을 사용하여 최근 생성한 테스트 주소에 테스트넷 자금을 요청할 수 있습니다.
이제 Injective 테스트넷에 testuser를 성공적으로 만들었습니다. faucet에서 testnet 자금을 요청한 후 잔액도 보유해야 합니다. 확인하려면 Injective 테스트넷 익스플로러에서 주소를 검색하여 잔액을 확인하세요. 또는 은행 잔액 쿼리나 curl로 확인할 수 있습니다:
curl -X GET "https://sentry.testnet.lcd.injective.network/cosmos/bank/v1beta1/balances/<your_INJ_address>" -H "accept: application/json"

Wasm 컨트랙트 업로드

이제 이전 단계에서 컴파일한 .wasm 파일을 Injective 테스트넷에 업로드할 시간입니다. 메인넷의 절차는 다르며 거버넌스 제안이 필요합니다.
# "injective-core-staging" 컨테이너 내부에서 또는 로컬로 injectived를 실행하는 경우 컨트랙트 디렉토리에서
yes 12345678 | injectived tx wasm store artifacts/my_first_contract.wasm \
--from=$(echo $INJ_ADDRESS) \
--chain-id="injective-888" \
--yes --fees=1000000000000000inj --gas=2000000 \
--node=https://testnet.sentry.tm.injective.network:443
출력:
code: 0
codespace: ""
data: ""
events: []
gas_used: "0"
gas_wanted: "0"
height: "0"
info: ""
logs: []
raw_log: '[]'
timestamp: ""
tx: null
txhash: 912458AA8E0D50A479C8CF0DD26196C49A65FCFBEEB67DF8A2EA22317B130E2C
Injective 테스트넷 익스플로러에서 주소를 확인하고, 체인에 코드를 저장할 때 반환된 txhash로 트랜잭션을 찾으세요. 트랜잭션 유형은 MsgStoreCode여야 합니다. Injective 테스트넷에 저장된 모든 코드는 Code에서 볼 수 있습니다.
방금 저장한 코드를 찾는 방법에는 여러 가지가 있습니다:
  • Injective 익스플로러 코드 목록에서 TxHash를 찾으세요. 가장 최근 것일 가능성이 높습니다.
  • injectived를 사용하여 트랜잭션 정보를 쿼리하세요.
트랜잭션을 쿼리하려면 txhash를 사용하고 컨트랙트가 배포되었는지 확인하세요.
injectived query tx 912458AA8E0D50A479C8CF0DD26196C49A65FCFBEEB67DF8A2EA22317B130E2C --node=https://testnet.sentry.tm.injective.network:443
출력을 더 자세히 살펴보면 업로드된 컨트랙트의 code_id290임을 알 수 있습니다:
- events:
  - attributes:
    - key: access_config
      value: '{"permission":"Everybody","address":""}'
    - key: checksum
      value: '"+OdoniOsDJ1T9EqP2YxobCCwFAqNdtYA4sVGv7undY0="'
    - key: code_id
      value: '"290"'
    - key: creator
      value: '"inj1h3gepa4tszh66ee67he53jzmprsqc2l9npq3ty"'
    type: cosmwasm.wasm.v1.EventCodeStored
  - attributes:
    - key: action
      value: /cosmwasm.wasm.v1.MsgStoreCode
    - key: module
      value: wasm
    - key: sender
      value: inj1h3gepa4tszh66ee67he53jzmprsqc2l9npq3ty
    type: message
  - attributes:
    - key: code_id
      value: "290"
    type: store_code
code_id를 ENV 변수로 내보내세요—컨트랙트를 인스턴스화하는 데 필요합니다. 이 단계를 건너뛰고 나중에 수동으로 추가할 수 있지만 ID를 기록해 두세요.
export CODE_ID= <code_id of your stored contract>

컨트랙트 인스턴스화

이제 코드가 Injective에 있으므로 상호작용하기 위해 컨트랙트를 인스턴스화할 시간입니다.
참고: CosmWasm에서 컨트랙트 코드 업로드와 컨트랙트 인스턴스화는 별도의 이벤트로 간주됩니다.
컨트랙트를 인스턴스화하려면 이전 단계에서 얻은 code_id와 함께 JSON 인코딩된 초기화 인수 및 레이블(목록에서 이 컨트랙트의 사람이 읽을 수 있는 이름)로 다음 CLI 명령을 실행하세요.
INIT='{"count":99}'
yes 12345678 | injectived tx wasm instantiate $CODE_ID $INIT \
--label="CounterTestInstance" \
--from=$(echo $INJ_ADDRESS) \
--chain-id="injective-888" \
--yes --fees=1000000000000000inj \
--gas=2000000 \
--no-admin \
--node=https://testnet.sentry.tm.injective.network:443
출력:
code: 0
codespace: ""
data: ""
events: []
gas_used: "0"
gas_wanted: "0"
height: "0"
info: ""
logs: []
raw_log: '[]'
timestamp: ""
tx: null
txhash: 01804F525FE336A5502E3C84C7AE00269C7E0B3DC9AA1AB0DDE3BA62CF93BE1D
컨트랙트 주소와 메타데이터를 찾을 수 있는 방법:
injectived query wasm contract inj1ady3s7whq30l4fx8sj3x6muv5mx4dfdlcpv8n7 --node=https://testnet.sentry.tm.injective.network:443

컨트랙트 쿼리

앞서 알 수 있듯이, 유일한 QueryMsg는 get_count입니다.
GET_COUNT_QUERY='{"get_count":{}}'
injectived query wasm contract-state smart inj1ady3s7whq30l4fx8sj3x6muv5mx4dfdlcpv8n7 "$GET_COUNT_QUERY" \
--node=https://testnet.sentry.tm.injective.network:443 \
--output json
출력:
{"data":{"count":99}}
컨트랙트를 인스턴스화할 때 설정한 대로 count가 99임을 알 수 있습니다.
동일한 컨트랙트를 쿼리하면 다른 응답을 받을 수 있습니다. 다른 사람이 컨트랙트와 상호작용하여 카운트를 증가시키거나 재설정했을 수 있기 때문입니다.

컨트랙트 실행

이제 카운터를 증가시켜 컨트랙트와 상호작용해 보겠습니다.
INCREMENT='{"increment":{}}'
yes 12345678 | injectived tx wasm execute inj1ady3s7whq30l4fx8sj3x6muv5mx4dfdlcpv8n7 "$INCREMENT" --from=$(echo $INJ_ADDRESS) \
--chain-id="injective-888" \
--yes --fees=1000000000000000inj --gas=2000000 \
--node=https://testnet.sentry.tm.injective.network:443 \
--output json
count를 쿼리하면 다음을 볼 수 있습니다:
{"data":{"count":100}}
**yes 12345678 |**는 비밀번호를 injectived tx wasm execute의 입력으로 자동으로 파이프(전달)하여 수동으로 입력할 필요가 없습니다.
카운터를 재설정하려면:
RESET='{"reset":{"count":999}}'
yes 12345678 | injectived tx wasm execute inj1ady3s7whq30l4fx8sj3x6muv5mx4dfdlcpv8n7 "$RESET" \
--from=$(echo $INJ_ADDRESS) \
--chain-id="injective-888" \
--yes --fees=1000000000000000inj --gas=2000000 \
--node=https://testnet.sentry.tm.injective.network:443 \
--output json
이제 컨트랙트를 다시 쿼리하면 카운트가 제공된 값으로 재설정되었음을 알 수 있습니다:
{"data":{"count":999}}

Cosmos 메시지

사용자 정의 스마트 컨트랙트 로직 정의 외에도 CosmWasm은 컨트랙트가 기본 Cosmos SDK 기능과 상호작용할 수 있도록 합니다. 일반적인 사용 사례 중 하나는 Cosmos SDK의 bank 모듈을 사용하여 컨트랙트에서 지정된 주소로 토큰을 보내는 것입니다.

예제: Bank Send

BankMsg::Send 메시지를 사용하면 컨트랙트가 다른 주소로 토큰을 전송할 수 있습니다. 이는 보상 분배 또는 사용자에게 자금 반환과 같은 다양한 시나리오에서 유용할 수 있습니다.
참고: 자금을 보내고 동시에 다른 컨트랙트의 함수를 실행하려면 BankMsg::Send를 사용하지 마세요. 대신 WasmMsg::Execute를 사용하고 해당 funds 필드를 설정하세요.

메시지 구성

컨트랙트의 execute 함수 내에서 BankMsg::Send 메시지를 구성할 수 있습니다. 이 메시지는 수신자 주소와 보낼 금액을 지정해야 합니다. 이 메시지를 구성하는 방법의 예는 다음과 같습니다:
use cosmwasm_std::{BankMsg, Coin, Response, MessageInfo};

pub fn try_send(
    info: MessageInfo,
    recipient_address: String,
    amount: Vec<Coin>,
) -> Result<Response, ContractError> {
    let send_message = BankMsg::Send {
        to_address: recipient_address,
        amount,
    };

    let response = Response::new().add_message(send_message);
    Ok(response)
}

스마트 컨트랙트에서의 사용

컨트랙트에서 이 bank send 기능을 처리하기 위해 ExecuteMsg enum에 새 변형을 추가할 수 있습니다. 예를 들어:
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum ExecuteMsg {
    // ... 다른 메시지 ...
    SendTokens { recipient: String, amount: Vec<Coin> },
}
그런 다음 execute 함수에서 이 메시지를 처리하는 케이스를 추가할 수 있습니다:
#[cfg_attr(not(feature = "library"), entry_point)]
pub fn execute(
    deps: DepsMut,
    env: Env,
    info: MessageInfo,
    msg: ExecuteMsg,
) -> Result<Response, ContractError> {
    match msg {
        // ... 다른 메시지 처리 ...
        ExecuteMsg::SendTokens { recipient, amount } => try_send(info, recipient, amount),
    }
}

테스트

다른 스마트 컨트랙트 함수와 마찬가지로 bank send 기능이 예상대로 작동하는지 확인하기 위해 단위 테스트를 추가해야 합니다. 여기에는 다양한 토큰 금액 전송 및 오류 처리와 같은 다양한 시나리오 테스트가 포함됩니다. 로컬 Injective 체인을 포함한 통합 테스트 실행에는 test-tube를 사용할 수 있습니다. 축하합니다! 첫 번째 Injective 스마트 컨트랙트를 만들고 상호작용했으며 이제 Injective에서 CosmWasm 개발을 시작하는 방법을 알게 되었습니다. 웹 UI 생성 가이드는 컨트랙트용 프론트엔드 생성을 계속하세요.