코즘와즘 101 와씀 2편: Go CLI·코즘JS 활용해 카운터 콘트랙트 배포하기
이 기사를 공유합니다
DSRV
DSRV 2022년 9월13일 12:10
출처=Mohammad Rahmani/Unsplash
출처=Mohammad Rahmani/Unsplash

DSRV 개발자 길드에서는 더 많은 개발자들과 웹3 인프라를 만들어가기 위해, 다양한 메인넷과 스마트계약에 대한 가이드를 연재합니다.

 

[코즘와즘 101 시리즈]

 

1. 카운터 콘트랙트 톺아보기

2. 카운터 콘트랙트 배포하기 (Go CLI 및 코즘JS 활용)

3. 프런트엔드와 통신하기



지난번 ‘코즘와즘 101 와씀 1편: 카운터 콘트랙트 톺아보기’에서는 웹 어셈블리가 무엇인지와 어떤 의미를 갖는지 알아보고, 실제로 코즘와즘을 활용한 간단한 콘트랙트인 카운터 예제를 살펴보았습니다.

이번 시간에는 앞서서 작성한 콘트랙트 코드를 컴파일하고, 이렇게 생성된 동일한 와즘 파일을 활용하여 클리프넷, 오스모시스 테스트넷, 아크웨이 테스트넷, 주노 테스트넷에 배포하는 과정을 설명하고자 합니다.



와즘 코드 컴파일하기

지난 글의 실습 코드를 다음의 명령어로 다시 다운로드해 보겠습니다. 저희가 마련한 동일한 예제 코드를 다음의 저장소에서 확인하실 수 있습니다.

cargo generate --git https://github.com/DSRV-DevGuild/cw-template --name my-first-contract
cd my-first-contract

스마트계약을 배포하기 위해서는 러스트(Rust)로 작성한 와즘 코드를 컴파일하여 실행 가능한 바이너리 파일로 만들어야 합니다. 아래의 명령어를 입력하면 컴파일이 진행됩니다.

rustup default stable
cargo wasm

rustup default stable 명령어는 스테이블(stable) 채널의 툴체인(toolchain)에서 컴파일을 진행하겠다는 뜻입니다. 툴체인(toolchain)은 러스트(rust)의 컴파일러를 뜻하고 러스트(rust)는 스테이블(stable), 베타(beta), 나이틀리(nightly) 라는 세 가지의 릴리스 채널(release channel)을 가지고 있습니다.

이 중 스테이블(stable) 채널은 가장 최근에 릴리스된 버전을 말합니다. rustup은 이러한 각기 다른 버전들을 손쉽게 관리할 수 있도록 도와줍니다.

카고(Cargo)는 러스트(Rust)의 패키지 매니저로 의존 라이브러리 다운로드 및 빌드를 대신 수행해 줍니다. [.cargo/config] 파일에서 와즘 컴파일 옵션을 다음과 같이 확인할 수 있습니다.

wasm = "build --release --target wasm32-unknown-unknown"

cargo wasm 명령어를 실행하면 위의 컨피규레이션(config) 파일 안의 옵션에 따라 cargo build --release —-target wasm32-unknown-unknown 명령이 실행됩니다.

카고(cargo)를 이용하여 생성된 러스트(rust) 프로젝트는 cargo build 명령어로 빌드를 할 수 있고, --release는 릴리스 프로파일(release profile)을 사용한다는 뜻으로 Cargo.toml 파일에서 아래와 같이 해당 값을 확인할 수 있습니다.

--target은 컴파일된 바이트코드의 디렉터리 위치를 지정하는 명령어로 컴파일이 성공했다면 target/wasm32-unknown-unknown/release/my_first_contract.wasm 파일이 생성된 것을 확인하실 수 있습니다. 해당 파일은 실행 가능한 와즘 바이너리 파일입니다.

[profile.release]
opt-level = 3   # 최적화 수준 설정
debug = false   # 디버그 레벨 설정
rpath = false   # 런타임 동적 연결 검색 경로 설정
lto = true      # LLVM link time optimization 설정
debug-assertions = false  # 런타임 validation 설정
codegen-units = 1  # code generation unit 개수 설정
panic = 'abort'    # panic strategy 설정
incremental = false    # incremental compilation 설정
overflow-checks = true # 런타임 integer 오버플로우 체크 설정

프로파일(profile) 변수에 대한 더욱 자세한 설명이 알고 싶다면 공식 문서를 참고해 주세요.



러스트(Rust) 컴파일 최적화하기

블록체인 상에서 스마트계약을 배포하고 실행하기 위해서는 가스(gas) 비용을 지불해야 합니다. 가스 비용을 줄이는 데는 다양한 방법이 존재하는데 그중 하나는 와즘으로 컴파일 된 바이트코드의 크기를 최적화하는 것입니다.

또한 코즘와즘은 컴파일 최적화를 진행하지 않으면 exceeds limit 에러가 발생하면서 배포가 되지 않기 때문에 컴파일 최적화는 선택이 아닌 필수라고 할 수 있습니다.

먼저 앞서 컴파일한 my_first_contract.wasm 파일의 크기를 확인하고자 합니다. 해당 파일이 있는 디렉터리에서 ls -lh 명령어를 통해 컴파일한 바이트코드의 크기를 확인해 보면 1.8MB 인 것을 알 수 있습니다.

ls -lh 명령어를 이용해 바이트 코드의 크기를 확인하는 화면. 출처=DSRV 미디엄
ls -lh 명령어를 이용해 바이트 코드의 크기를 확인하는 화면. 출처=DSRV 미디엄

해당 코드를 배포한 후, gas 비용을 확인해 보겠습니다. (배포 과정은 뒤에서 자세히 다룰 예정입니다.) 배포 시 위에서 언급했던 exceeds limit 에러가 발생하고, 트랜잭션 해시값을 통해 가스 비용을 확인하면 475만9648이 소모된 것을 확인할 수 있습니다.

익스플로러에서 트랜잭션 해시값을 이용해 확인한 가스 비용 (최적화하기 전). 출처=DSRV 미디엄
익스플로러에서 트랜잭션 해시값을 이용해 확인한 가스 비용 (최적화하기 전). 출처=DSRV 미디엄

이번에는 RUSTFLAGS를 사용하여 컴파일러가 사용하지 않는 모든 코드를 지워 훨씬 작은 크기의 코드를 만들어 봅시다.

RUSTFLAGS='-C link-arg=-s' cargo wasm

RUSTFLAGS는 rust 컴파일러에 옵션 값을 전달하는 환경 변수로 -C link-args=<val> 은 링커 호출에 추가할 추가 인수를 지정합니다. -s 는 스트리핑 바이너리(stripping binary) 옵션입니다.

다시 ls -lh 명령어를 사용해서 파일 크기를 확인해 보면 156KB로 이전과 비교했을 때 훨씬 작아진 것을 확인할 수 있고, 배포도 정상적으로 진행되며 가스 비용을 확인했을 때 아래와 같이 107만0212로 이전 값보다 훨씬 줄어들었습니다.

이렇게 최적화를 통해 컴파일 된 코드를 줄이는 것은 gas 비용을 줄이는 데 도움을 준다는 것을 확인할 수 있습니다.

익스플로러에서 트랜잭션 해시값을 이용해 확인한 가스 비용 (최적화 진행 이후). 출처=DSRV 미디엄
익스플로러에서 트랜잭션 해시값을 이용해 확인한 가스 비용 (최적화 진행 이후). 출처=DSRV 미디엄

위의 방법 이외에도 코즘와즘에서는 최적화 툴로 rust-optimizer를 제공하는데, 다커(Docker)가 설치되어 있다면 아래의 코드를 실행하여 러스트-옵티마이저(rust-optimizer)를 사용할 수 있습니다.

러스트-옵티마이저(rust-optimizer)는 코즘와즘 스마트계약을 위해 만들어진 툴로 러스트플래그즈(RUSTFLAGS)와 wasm-opt를 함께 사용하여, 러스트플래그즈(RUSTFLAGS)만 사용했을 때보다 조금 더 나은 최적화 성능을 보입니다. wasm-opt는 웹 어셈블리용 최적화 프로그램인 Binaryen IR을 실행하여 최적화를 진행합니다.

sudo 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.6

위 코드를 실행하면 artifacts 디렉터리 안에 위와 똑같은 이름의(my_first_contract.wasm) 바이너리 파일이 생성됩니다. 이렇게 생성된 바이너리 파일의 크기는 125K 로 러스트플래그즈(RUSTFLAGS)만 사용했을 때보다 조금 더 작아진 크기를 확인할 수 있습니다.

이제부터는 컴파일 과정을 통해 생성한 와즘 바이너리 코드를 코즘와즘을 지원하는 다양한 블록체인 네트워크에 배포해 보도록 하겠습니다.





스마트계약 배포하기

개발자가 스마트계약을 배포하는 과정을 다음의 세 가지로 나누어 볼 수 있습니다. 지난 1편에서 살펴보았던 장표를 다시 볼까요.

 

1. 컴파일된 바이트코드의 업로드

2. 새로운 콘트랙트를 인스턴스화 (instantiate)

3. 콘트랙트의 실행



이더리움에서는 상기한 과정 중 1번과 2번이 서로 통합되어 콘트랙트가 배포될 때 constructor()가 호출되면서 새로운 콘트랙트의 인스턴스화가 진행됩니다. 그래서 서로 다른 콘트랙트가 동일한 .sol을 컴파일 하였더라도, 동일한 바이트 코드를 각각의 상태(state)에 저장하고 있는 꼴이 됩니다.

코즘와즘은 콘트랙트 바이트코드를 업로드하는 과정과 새로운 콘트랙트를 초기화하고 인스턴스화 시키는 과정을 분리하여 서로 다른 콘트랙트가 하나의 바이트 코드를 공유할 수 있도록 하였습니다.

업로드된 바이트코드의 아이디(ID)를 이용해서 콘트랙트를 인스턴스화할 수 있고, 제이슨(JSON) 형태의 인자를 통해 콘트랙트의 초기 상태도 설정할 수 있습니다.

본문에서는 코즘와즘을 지원하는 다양한 네트워크에서 앞서 컴파일한 콘트랙트 바이트코드를 업로드하고, 업로드된 바이트코드의 ID를 이용해 콘트랙트를 인스턴스화하고 마지막으로 생성된 콘트랙트에 트랜잭션을 날려서 콘트랙트가 잘 실행되는지 확인해 볼 것입니다.

코즘와즘을 배포하는 방법에는 고(Go) CLI(Command Line Interface)인 wasmd를 이용한 방법과 CosmJS 노드(Node) REPL(Read Eval Print Loop)을 이용하는 방법이 있습니다.

클리프넷과 오스모시스에서는 Go CLI wasmd를 사용한 배포 방법을, 주노 테스트넷과 아크웨이 테스트넷에서는 CosmJS 노드(Node) REPL을 사용한 배포 방법을 보여드리도록 하겠습니다.



Go CLI wasmd/osmosisd로 배포하기 — 말라가 & 오스모시스 테스트넷

 

말라가(Malaga)

말라가는 코즘와즘에서 자체적으로 제공하는 퍼블릭 테스트넷입니다. 먼저 Go CLI인 wasmd를 사용해서 콘트랙트를 배포해 보도록 하겠습니다.



1. 환경 설정

 

· 환경 설정

wasmd를 설치하기 위해서는 Go 버전 v1.15 이상이 필요합니다. 자신의 운영체제에 맞추어 공식 문서를 따라 Go를 설치해 줍니다.

러스트업을 설치한 후, 아래의 명령어를 실행합니다.

rustup default stable
cargo version
# If this is lower than 1.50.0+, update
rustup update stable

rustup target list --installed
rustup target add wasm32-unknown-unknown

이제 wasmd를 설치할 준비가 끝났습니다. 아래의 명령어를 실행해 wasmd를 설치합니다. wasmd는 코즘와즘 플랫폼의 백본으로 와즘(wasm) 스마트계약을 개발하고 편집하기 위해 필요합니다.

git clone https://github.com/CosmWasm/wasmd.git
cd wasmd

git checkout v0.27.0
make install

# verify the installation
wasmd version

만약 make install 명령에서 오류가 발생한 경우, $PATH 환경 변수에 $HOME/go/bin 이 추가되어 있는지 확인하고 추가되어 있지 않다면 추가해 줍니다.

마지막으로 제이슨(JSON)에서 데이터(data)를 추출하는 것을 도와주는 jq 오픈소스를 운영체제에 맞게 다운로드합니다. 아래에 기재된 것 이외에 다른 운영체제를 사용하신다면 공식 문서를 참고해 주세요.

# Linux
sudo apt-get install jq

# Mac
brew install jq

· Set up Malaga

curl 명령어로 말라가 네트워크의 컨피규레이션(configuration)을 읽어온 후 source 명령어로 읽어온 값을 적용합니다.

source <(curl -sSL https://raw.githubusercontent.com/CosmWasm/testnets/master/malaga-420/defaults.env)

해당 컨피규레이션에는 다음과 같이 말라가의 다양한 정보가 들어있습니다. 우리는 이 중에서 $NODE와 $TXFLAG를 사용하게 됩니다. 해당 값에 대한 자세한 설명은 아래에서 다시 언급하도록 하겠습니다.

export CHAIN_ID="malaga-420"
export TESTNET_NAME="malaga-420"
export FEE_DENOM="umlg"
export STAKE_DENOM="uand"
export BECH32_HRP="wasm"
export WASMD_VERSION="v0.27.0"
export CONFIG_DIR=".wasmd"
export BINARY="wasmd"

export COSMJS_VERSION="v0.28.1"
export GENESIS_URL="https://raw.githubusercontent.com/CosmWasm/testnets/master/malaga-420/config/genesis.json"

export RPC="https://rpc.malaga-420.cosmwasm.com:443"
export API="https://api.malaga-420.cosmwasm.com"
export FAUCET="https://faucet.malaga-420.cosmwasm.com"

export NODE=(--node $RPC)
export TXFLAG=($NODE --chain-id $CHAIN_ID --gas-prices 0.25umlg --gas auto --gas-adjustment 1.3)

· 지갑(wallet) 생성

배포를 위한 지갑(wallet)을 생성합니다.

# add wallets for testing
wasmd keys add wallet

지갑을 생성하면 다음과 같이 생성한 지갑의 정보를 YAML 포맷 형태로 확인할 수 있습니다.

- name: wallet
  type: local
  address: wasm1evvnsrte3rdghy506vu4c87x0s5wx0hpppqdd6
  pubkey: '{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"A2aKoMPLbUnXkN2zJF/q4lIH/34ybelQSRTg3d9Js86T"}'
  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.

table shell potato spike paddle very asthma raise glare noodle vibrant chuckle indicate spell perfect craft step net radio yellow minor deal blur hybrid

지갑 생성 명령어 실행 예시. 출처=DSRV 미디엄
지갑 생성 명령어 실행 예시. 출처=DSRV 미디엄

지갑을 생성한 후에는 콘트랙트를 배포할 때 드는 가스 비용을 지불하기 위한 토큰이 필요합니다. 필요한 토큰의 이름은 네트워크마다 다른데, 말라가에서는 가스 비용을 지불하기 위해 MLG(umlg) 토큰이 필요합니다. 다음 명령어로 faucet을 요청해 생성한 지갑에 토큰을 받을 수 있습니다.

JSON=$(jq -n --arg addr $(wasmd keys show -a wallet) '{"denom":"umlg","address":$addr}') && curl -X POST --header "Content-Type: application/json" --data "$JSON" https://faucet.malaga-420.cosmwasm.com/credit

wasmd keys show -a wallet 명령어는 앞서 생성한 지갑의 주소를 반환합니다.

jq 라이브러리를 사용해 $addr 변수에 지갑의 주솟값을 저장하고, '{"denom":"umlg","address":$addr}'을 $JSON 변수에 저장합니다.

그리고 Faucet을 제공해 주는 URL에 $JSON 값과 함께 POST 명령을 보내 Faucet을 요청합니다. Faucet 요청이 성공해서 지갑에 토큰이 지급되었다면 아래의 명령어를 통해 확인할 수 있습니다.

wasmd query bank balances $(wasmd keys show -a wallet) $NODE

지갑의 잔액을 확인하는 명령어 실행 예시. 출처=DSRV 미디엄
지갑의 잔액을 확인하는 명령어 실행 예시. 출처=DSRV 미디엄

2. 배포

 

· 바이트코드 업로드하기

앞선 환경설정에서 말라가의 컨피규레이션을 설정했으면 자동으로 $NODE와 $TXFLAG 변수가 설정이 되어 있을 것입니다. echo $NODE, echo $TXFLAG를 통해 값을 확인해 주시고 만약 설정되어 있지 않다면 아래의 명령어를 통해 설정해 주세요.

# bash
export NODE="--node $RPC"
export TXFLAG="${NODE} --chain-id ${CHAIN_ID} --gas-prices 0.25umlg --gas auto --gas-adjustment 1.3"

# zsh
export NODE=(--node $RPC)
export TXFLAG=($NODE --chain-id $CHAIN_ID --gas-prices 0.25umlg --gas auto --gas-adjustment 1.3)

$NODE 는 말 그대로 노드의 네트워크를 지정해 주는 변수입니다.

위의 컨피규레이션 파일에서 RPC="<https://rpc.malaga-420.cosmwasm.com:443>" 인 것을 확인할 수 있는데 이는 말라가 네트워크의 RPC 엔드 포인트를 뜻합니다.

$TXFLAG는 이후 진행할 배포 과정에서 지불하게 되는 가스 비용과 관련된 사항을 저장한 변수입니다. --gas-prices 는 단일 가스 단위(single unit of gas)의 가격으로 일반적으로 가장 작은 토큰 단위의 일부이기 때문에 토큰 단위 앞에 u 가 붙는 것을 볼 수 있습니다.

--gas 는 가스비 제한(limit)을 말합니다. 디폴트(default) 값은 200000 이고, auto 옵션을 사용하면 자동으로 추정치를 계산할 수 있습니다. --gas-adjustment는 auto 옵션을 사용했을 때 가스 비용을 적게 계산한 경우를 대비하여 가스 비용을 크게 해줍니다. 위의 경우에는 예상 가스 비용의 1.3배를 가스비 제한(gas limit)으로 설정하게 됩니다.

wasmd를 클라이언트로 사용하는 경우 실행하는 모든 명령에 대해 노드 유형, 체인 ID 및 가스 가격 세부 정보를 정의해야 하기 때문에 이렇게 변수를 사용하면 훨씬 편리해집니다.

이제 다음의 명령어를 통해 와즘 바이너리 코드를 블록체인 위에 업로드하게 됩니다. 앞서서 코드를 컴파일 할 때, cargo wasm 을 사용했는지, rust-optimizer를 사용했는지에 따라 .wasm 파일이 위치한 경로가 다르므로 알맞은 경로를 지정해 줍니다. (이후에 진행하게 되는 과정에서도 알맞은 경로를 지정해 주어야 합니다.)

# using cargo wasm
RES=$(wasmd tx wasm store target/wasm32-unknown-unknown/release/my_first_contract.wasm --from wallet $TXFLAG -y --output json -b block)

# using rust-optimizer
RES=$(wasmd tx wasm store artifacts/my_first_contract.wasm --from wallet $TXFLAG -y --output json -b block)

wasmd tx wasm store는 wasm 파일을 체인 위에 store(업로드) 하는 트랜잭션을 생성합니다. --from 옵션으로 가스 비용을 지불할 지갑을 전달하고 --output 옵션으로 결과를 제이슨 형태로 받아오도록 지정해 줍니다. -y 는 컨펌(confirm) 과정을 스킵하는 옵션이고, -b block은 트랜잭션 브로드캐스팅 모드(Transaction broadcasting mode)를 뜻합니다.

$RES 값을 출력해 보면 다음과 같습니다.

echo $RES 실행 예시. 출처=DSRV 미디엄
echo $RES 실행 예시. 출처=DSRV 미디엄

$RES의 “logs” 를 자세히 살펴보면 다음과 같습니다.

"logs":[
      {
         "msg_index":0,
         "log":"",
         "events":[
            {
               "type":"message",
               "attributes":[
                  {
                     "key":"action",
                     "value":"/cosmwasm.wasm.v1.MsgStoreCode"
                  },
                  {
                     "key":"module",
                     "value":"wasm"
                  },
                  {
                     "key":"sender",
                     "value":"wasm1w9acys35f5zyjcghwy4p6ug8lvasltghef34em"
                  }
               ]
            },
            {
               "type":"store_code",
               "attributes":[
                  {
                     "key":"code_id",
                     "value":"223"
                  }
               ]
            }
         ]
      }
   ]

따라서 jq를 이용해 $RES에서 다음과 같이 CODE_ID를 추출할 수 있습니다. $CODE_ID 값이 정상적으로 출력된다면 업로드에 성공한 것입니다.

CODE_ID=$(echo $RES | jq -r '.logs[0].events[-1].attributes[0].value')

echo $CODE_ID

· 콘트랙트 인스턴스 생성하기

이제 성공적으로 바이너리 코드를 말라가에 올렸으니 콘트랙트의 새로운 인스턴스를 만들어야 합니다. 먼저 인스턴스의 초기 상태를 INIT 변수에 지정해 주고 instantiate 명령어를 실행합니다.

INIT='{"count": 2}'

wasmd tx wasm instantiate $CODE_ID "$INIT" \
    --from wallet --label "my first contract" $TXFLAG -y --no-admin

instantiate 명령어 실행 예시. 출처=DSRV 미디엄
instantiate 명령어 실행 예시. 출처=DSRV 미디엄

실행이 성공적으로 완료되었다면 위와 같이 txhash 값이 출력됩니다. 해당 해시값을 말라가 익스플로러에서 검색해서 배포를 확인할 수 있습니다.

말라가 익스플로러에서 해시값을 이용해 트랜잭션 검색한 결과. 출처=DSRV 미디엄
말라가 익스플로러에서 해시값을 이용해 트랜잭션 검색한 결과. 출처=DSRV 미디엄

다음으로, 배포된 콘트랙트의 정보를 가져오기 위해 아래의 명령어를 실행합니다.

query wasm 은 와즘 모듈에 대한 쿼리 커맨드를 뜻하고, list-contract-by-code는 해당 $CODE_ID에 올라와 있는 모든 와즘 바이트코드(초기화한 콘트랙트 인스턴스)의 주소를 리스트로 보여줍니다.

wasmd query wasm list-contract-by-code $CODE_ID $NODE --output json

--output 옵션으로 제이슨(json)을 지정해 주었기 때문에 위 명령어를 실행했을 때의 결과는 다음과 같습니다.

배포된 콘트랙트의 주소를 출력하는 명령어 실행 예시. 출처=DSRV 미디엄
배포된 콘트랙트의 주소를 출력하는 명령어 실행 예시. 출처=DSRV 미디엄

위의 결과에서 contracts 값을 가져오기 위해 jq를 사용하여 제이슨(JSON)을 파싱합니다.

CONTRACT=$(wasmd query wasm list-contract-by-code $CODE_ID $NODE --output json | jq -r '.contracts[-1]')

echo $CONTRACT

$CONTRACT 값이 정상적으로 출력된다면 콘트랙트 인스턴스 생성에 성공한 것입니다.

 

· 콘트랙트 실행하기

우리는 말라가에 컴파일한 와즘 바이너리 코드를 업로드하고 콘트랙트 인스턴스까지 생성을 완료했습니다. 이제 마지막으로 배포한 콘트랙트가 잘 동작하는지 실행시켜 보도록 하겠습니다.



1. get_count

카운트(count) 값을 가져오는 get_count 쿼리를 날려보면서 콘트랙트가 제대로 실행되는지 확인할 수 있습니다. 초기화된 상태에서 쿼리를 날려서 값을 가져온다면 인스턴스를 생성할 때 지정해 줬던 초기 상태 {"data":{"count":2}}값이 그대로 출력될 것입니다.

QUERY='{"get_count":{}}'
wasmd query wasm contract-state smart $CONTRACT "$QUERY" $NODE --output json

contract-state smart 는 $CONTRACT 주소에 $QUERY 데이터를 전달하면서 콘트랙트를 실행하고, 리턴되는 결괏값을 프린트하는 명령입니다.

 

2. increment

이번에는 카운트(count) 값을 +1 증가시키는 increment 트랜잭션을 날려보겠습니다. 해당 트랜잭션은 콘트랙트의 내부 상태를 바꾸는 트랜잭션이기 때문에 gas fee를 지불해야 합니다. 이후 다시 get_count 쿼리를 통해 카운트,(count) 값을 가져오면 이전 count 값에서 +1이 증가한 것을 확인할 수 있습니다.

TRY_INCREMENT='{"increment": {}}'
wasmd tx wasm execute $CONTRACT "$TRY_INCREMENT" --amount 100umlg --from wallet $TXFLAG -y

tx 명령어는 트랜잭션을 생성하고, wasm execute는 $CONTRACT 주소에 해당하는 콘트랙트에 $TRY_INCREMENT 명령어를 실행합니다.

--amount는 명령과 함께 해당 콘트랙트에 보낼 토큰의 양을 뜻하며 해당 옵션이 없어도 명령은 동작합니다. 그 외의 옵션은 바이트코드 업로드하기에서 지정해 줬던 옵션과 동일합니다.

tx 명령을 실행하면 아래와 같이 txhash 값이 출력됩니다.

tx 명령어 실행 예시 (위 사진은 클리프넷 네트워크 예시이므로 본문의 명령어에서 amount 단위가 상이합니다.). 출처=DSRV 미디엄
tx 명령어 실행 예시 (위 사진은 클리프넷 네트워크 예시이므로 본문의 명령어에서 amount 단위가 상이합니다.). 출처=DSRV 미디엄

3. reset

마지막으로 reset 트랜잭션을 날려보겠습니다. reset 트랜잭션 또한 콘트랙트의 내부 상태를 바꾸는 트랜잭션이기 때문에 가스비(gas fee)를 지불해야 합니다.

이후 다시 get_count 쿼리를 통해 count 값을 가져오면 지정한 값으로 카운트 값이 다시 설정된 것을 확인할 수 있습니다.

RESET='{"reset": {"count": 123}}'
wasmd tx wasm execute $CONTRACT "$RESET" --amount 100umlg --from wallet $TXFLAG -y

축하합니다! 우리는 클리프넷에서 콘트랙트를 배포하는 작업을 성공적으로 마무리했습니다.

이제 다른 블록체인에도 동일한 바이너리 코드를 배포해 보겠습니다.

 

출처=DSRV 미디엄
출처=DSRV 미디엄

오스모시스 테스트넷

오스모시스는 코스모스(Cosmos) SDK로 만들어진 AMM(advanced automated market maker) 프로토콜입니다. 오스모시스에 대한 자세한 설명은 공식 문서에서 확인할 수 있습니다.

우리는 오스모시스의 테스트넷인 osmo-test-4 에 스마트계약을 배포해 보도록 하겠습니다.

 

1. 환경 설정

 

· 러스트 환경 설정

# 1. Set 'stable' as the default release channel:

rustup default stable

# 2. Add WASM as the compilation target:

rustup target add wasm32-unknown-unknown

# 3. Install the following packages to generate the contract:

cargo install cargo-generate --features vendored-openssl
cargo install cargo-run-script



· 오스모시스 환경 설정

오스모시스 인스톨러(Osmosis Installer)를 사용하여 손쉽게 오스모시스 테스트넷 환경을 설정할 수 있습니다.

다음 명령어를 입력하고 나타난 화면에서 클라이언트 노드와 테스트넷 옵션을 차례대로 선택하여 설치하면 환경 설정이 완료됩니다.

curl -sL https://get.osmosis.zone/install > i.py && python3 i.py

출처=DSRV 미디엄
출처=DSRV 미디엄
출처=DSRV 미디엄
출처=DSRV 미디엄
오스모시스 인스톨러 설치 예시. 출처=DSRV 미디엄
오스모시스 인스톨러 설치 예시. 출처=DSRV 미디엄

 

· 지갑 생성

osmosisd keys add wallet

오스모시스의 faucet을 받기 위해서는 오스모시스 디스코드에 참여해야 하지만, ATN(All That Node)를 사용해서 더욱 편리하게 faucet을 요청할 수 있습니다.

다음 링크로 들어가서 faucet을 받고 싶은 지갑의 주소를 입력한 후 'Claim Your Tokens' 버튼을 누르면 faucet이 요청되고 성공 시 아래와 같이 트랜잭션 주소가 출력됩니다. 지갑의 주소는 osmosisd keys show -a wallet 명령어를 통해 확인할 수 있습니다.

ATN에서 제공하는 오스모시스 faucet을 이용한 화면. 출처=DSRV 미디엄
ATN에서 제공하는 오스모시스 faucet을 이용한 화면. 출처=DSRV 미디엄

faucet을 요청한 후에는 아래 명령어로 잔액을 확인할 수 있습니다.

osmosisd query bank balances $(osmosisd keys show -a wallet)



2. 배포

 

· 바이트코드 업로드하기

이제 앞서 클리프넷에 배포한 my_first_contract.wasm 바이너리 코드를 오스모시스 테스트넷에 똑같이 배포해 보겠습니다. 배포를 위해서는 테스트넷의 RPC 엔드 포인트 주소가 필요한데, 우리는 ATN(All That Node)에서 제공하는 RPC 엔드 포인트를 사용할 것입니다.

ATN에서 제공하는 RPC 엔드 포인트를 사용할 때, API 키를 발급받아서 사용할 수도 있고 API 키 없이도 사용할 수 있는데 둘 다 같은 노드(같은 머신)를 사용하지만 request limit 이 다릅니다.

API 키가 없이 사용하는 경우에는 자동으로 퍼블릭 노드(Public Node)로 간주되며 IP에 따라 사용량을 제한하고 request limit이 상대적으로 더 제한적입니다. API 키와 함께 사용하는 경우에는 프라이빗 프리 노드(Private Free Node)로 간주되며 API_KEY에 따라 사용량이 추적되고 request limit이 퍼블릭 노드보다 높습니다.

우리는 API 키를 발급받아서 사용해 보도록 하겠습니다. ATN에 들어가 구글 아이디를 이용해 'Sign up'을 진행하고 프로토콜의 'Osmosis' 탭에 들어가 'New Project'를 클릭하여 프로젝트를 생성합니다.

출처=DSRV 미디엄
출처=DSRV 미디엄
출처=DSRV 미디엄
출처=DSRV 미디엄
ATN에서 API 키를 발급받는 방법. 출처=DSRV 미디엄
ATN에서 API 키를 발급받는 방법. 출처=DSRV 미디엄

그러면 다음과 같이 'Dashboard'에서 방금 생성한 프로젝트를 확인할 수 있습니다. 'Osmosis' 탭을 눌러 들어갑니다.

ATN의 'Dashboard' 화면. 출처=DSRV 미디엄
ATN의 'Dashboard' 화면. 출처=DSRV 미디엄

그러면 다음과 같이 API 키와 테스트넷 엔드 포인트를 확인할 수 있습니다.

ATN의 API 키와 엔드 포인트를 보여주는 화면. 출처=DSRV 미디엄
ATN의 API 키와 엔드 포인트를 보여주는 화면. 출처=DSRV 미디엄

다음으로, 클리프넷에서 배포를 진행했을 때와 같이 배포의 편리성을 위해 $NODE 변수에 오스모시스 테스트넷의 RPC 엔드 포인트 주소를, $TXFLAG 변수에 가스 비용과 관련된 사항을 다음과 같이 저장합니다. 이때, RPC 엔드 포인트 주소 뒤에 각자의 API 키를 복사하여 붙여 넣어야 합니다.

# bash
export NODE="--node https://osmosis-testnet-rpc.allthatnode.com:26657/{API KEY}"
export TXFLAG="${NODE} --chain-id osmo-test-4 --gas-prices 0.025uosmo --gas auto --gas-adjustment 1.3"

# zsh
export NODE=(--node https://osmosis-testnet-rpc.allthatnode.com:26657/{API KEY})
export TXFLAG=($NODE --chain-id osmo-test-4 --gas-prices 0.025uosmo --gas auto --gas-adjustment 1.3)

이제 다음의 명령어를 통해 블록체인 위에 코드를 업로드하고 CODE_ID를 추출합니다. $CODE_ID 값이 정상적으로 출력된다면 업로드에 성공한 것입니다.

RES=$(osmosisd tx wasm store artifacts/my_first_contract.wasm --from wallet $TXFLAG -y --output json -b block)

CODE_ID=$(echo $RES | jq -r '.logs[0].events[-1].attributes[0].value')
echo $CODE_ID

wasmd가 osmosisd 로 바뀌었을 뿐 그 외의 명령어는 클리프넷 배포에서 설명한 것과 동일합니다.

 

· 콘트랙트 인스턴스 생성하기 & 콘트랙트 실행하기 — osmosisd와 GUI 활용

오스모시스 테스트넷에서 콘트랙트의 인스턴스를 생성하는 방법에는 osmosisd 를 이용하는 방법과 https://osmosis-contracts.web.app/ 사이트에서 GUI를 이용하는 방법 두 가지가 있습니다.

먼저 osmosisd를 이용하는 방법을 살펴보겠습니다.

콘트랙트의 인스턴스를 만들기 위해 초기 상태를 설정하고 instantiate 명령을 실행합니다.

INIT='{"count":2}'

osmosisd tx wasm instantiate $CODE_ID "$INIT" \
    --from wallet --label "my first contract" $TXFLAG -y --no-admin

실행이 성공적으로 완료되었다면 출력된 txhash 값을 오스모시스 익스플로러에 검색해서 배포를 확인할 수 있습니다.

오스모시스 익스플로러에서 해시값을 이용해 트랜잭션을 검색한 결과. 출처=DSRV 미디엄
오스모시스 익스플로러에서 해시값을 이용해 트랜잭션을 검색한 결과. 출처=DSRV 미디엄

배포된 콘트랙트의 정보를 가져오기 위해 아래의 명령어를 실행합니다.

CONTRACT=$(osmosisd query wasm list-contract-by-code $CODE_ID --output json | jq -r '.contracts[0]')

echo $CONTRACT

이제 배포한 콘트랙트가 잘 동작하는지 실행시켜 보도록 하겠습니다.

 

1. get_count

get_count 쿼리를 날려 값을 확인해 보면 초기 상태 {"data":{"count":2}} 가 그대로 출력될 것입니다.

QUERY='{"get_count":{}}'

osmosisd query wasm contract-state smart $CONTRACT "$QUERY" --output json

 

2. increment

아래의 명령어로 increment 트랜잭션을 날린 후 다시 get_count 쿼리를 통해 값을 확인하면 이전 카운트 값에서 1이 증가한 것을 확인할 수 있습니다.

TRY_INCREMENT='{"increment": {}}'

osmosisd tx wasm execute $CONTRACT "$TRY_INCREMENT" --from wallet $TXFLAG -y



3. reset

아래의 명령어로 reset 트랜잭션을 날린 후 다시 get_count 쿼리를 통해 값을 확인하면 지정한 값으로 카운트 값이 다시 설정되었음을 확인할 수 있습니다.

RESET='{"reset": {"count": 123}}'

osmosisd tx wasm execute $CONTRACT "$RESET" --from wallet $TXFLAG -y

다음은 https://osmosis-contracts.web.app/ 사이트의 GUI를 이용해서 콘트랙트 인스턴스를 생성해 보겠습니다. 해당 사이트에 접속하면 앞서서 우리가 체인에 업로드한 코드를 확인할 수 있습니다.

방금 우리가 osmosisd 를 이용해 인스턴스화했던 my first contract 콘트랙트 인스턴스도 보입니다.

오스모시스 콘트랙트 익스플로러에서 업로드한 코드를 확인하는 화면. 출처=DSRV 미디엄
오스모시스 콘트랙트 익스플로러에서 업로드한 코드를 확인하는 화면. 출처=DSRV 미디엄

오스모시스 지갑을 연결하여 로그인을 한 후, 'Create a Contract' 버튼을 눌러 제이슨(JSON) 형식의 초기 상태를 입력하고 'Instantiate Contract' 버튼을 클릭하면 손쉽게 콘트랙트 인스턴스를 생성할 수 있습니다.

'Instantiate Contract' 버튼을 눌러 콘트랙트 인스턴스를 생성한 후의 화면. 출처=DSRV 미디엄
'Instantiate Contract' 버튼을 눌러 콘트랙트 인스턴스를 생성한 후의 화면. 출처=DSRV 미디엄

이제 생성한 콘트랙트 인스턴스에 트랜잭션을 날려 콘트랙트가 잘 실행되는지 확인해 보도록 하겠습니다. 생성된 콘트랙트의 주소를 클릭하면 'Contract' 창으로 들어갈 수 있습니다.

'Read Contract' 섹션에서는 쿼리를 날릴 수 있고, 'Write Contract' 섹션에서는 콘트랙트의 내부 상태를 바꾸는 트랜잭션을 날릴 수 있습니다.

생성된 콘트랙트 인스턴스를 실행할 수 있는 화면. 출처=DSRV 미디엄
생성된 콘트랙트 인스턴스를 실행할 수 있는 화면. 출처=DSRV 미디엄



1. get_count

'Read Contract' 섹션을 열어서 메시지(Message)를 입력하고 'Run query' 버튼을 클릭하면 초기 상태 값을 그대로 출력하는 것을 확인할 수 있습니다.

'Run query' 버튼을 클릭해 get_count 쿼리 메시지를 실행한 화면. 출처=DSRV 미디엄
'Run query' 버튼을 클릭해 get_count 쿼리 메시지를 실행한 화면. 출처=DSRV 미디엄

 

2. incremet

'Write Contract' 섹션을 열어서 메시지(Message)와 지불할 OSMO를 입력하고 'Execute Contract' 버튼을 클릭하면 트랜잭션이 성공적으로 실행됩니다. 이후 다시 get_count를 실행해 보면 이전 카운트 값에서 +1 이 증가한 것을 확인할 수 있습니다.



'Execute contract' 버튼을 클릭하여 increment 트랜잭션 메시지를 실행한 화면. 출처=DSRV 미디엄
'Execute contract' 버튼을 클릭하여 increment 트랜잭션 메시지를 실행한 화면. 출처=DSRV 미디엄
increment 트랜잭션을 실행한 후 다시 get_count 쿼리를 통해 카운트 값을 가져온 화면. 출처=DSRV 미디엄
increment 트랜잭션을 실행한 후 다시 get_count 쿼리를 통해 카운트 값을 가져온 화면. 출처=DSRV 미디엄

 

3. reset

'Write Contract' 섹션을 열어서 메시지(Message)와 지불할 OSMO를 입력하고 'Execute Contract' 버튼을 클릭하면 트랜잭션이 성공적으로 실행됩니다. 이후 다시 get_count를 실행해 보면 지정한 값으로 카운트 값이 다시 설정된 것을 확인할 수 있습니다.

'Execute Contract' 버튼을 클릭하여 reset 트랜잭션 메시지를 실행한 화면. 출처=DSRV 미디엄
'Execute Contract' 버튼을 클릭하여 reset 트랜잭션 메시지를 실행한 화면. 출처=DSRV 미디엄
reset 트랜잭션을 실행한 후 다시 get_count 쿼리를 통해 카운트 값을 가져온 화면. 출처=DSRV 미디엄
reset 트랜잭션을 실행한 후 다시 get_count 쿼리를 통해 카운트 값을 가져온 화면. 출처=DSRV 미디엄

osmosisd 와 GUI를 이용해 각각 생성한 콘트랙트들이 잘 실행된다면 우리는 오스모시스 테스트넷에서 성공적으로 스마트계약 배포를 완료한 것입니다.

 



코즘JS로 배포하기 — 주노 테스트넷(Juno Testnet) & 아크웨이 테스트넷(Archway Testnet)

이번에는 코즘JS Node REPL을 사용하여 똑같은 와즘 바이너리 코드를 주노 테스트넷과 아크웨이 테스트넷에 배포해 보도록 하겠습니다.

코즘JS는 Node.js와 모던 브라우저에서 실행되는 타입스크립트(Typescript) 라이브러리입니다. Go CLI에서 제이슨(JSON) 형식의 데이터를 다루기 위해서는 CLI 용 제이슨 프로세서(json processor)인 jq 등을 사용해야 하기 때문에, 스마트계약과 관련된 작업을 할 때는 코즘JS가 훨씬 유용합니다.

출처=DSRV 미디엄
출처=DSRV 미디엄



주노 테스트넷

주노는 코스모스 SDK로 만들어진 상호 운용이 가능한 스마트계약 네트워크입니다. 주노에 대한 자세한 설명은 공식 문서에서 확인할 수 있습니다.

우리는 주노의 테스트넷인 uni-3 에 스마트계약을 배포해 보도록 하겠습니다.

 

1. 환경설정

 

· @cosmjs/cli 설치하기

먼저 @cosmjs/cli를 현재 디렉터리 (my-first-contract)에서 yarn이나 npm을 이용해 설치하고 실행시켜 봅니다.

yarn add @cosmjs/cli --dev
# or
npm install @cosmjs/cli --save-dev

# run cosmjs-cli
./node_modules/.bin/cosmjs-cli

다음과 같은 화면이 뜬다면 성공적으로 설치를 완료한 것입니다.

cosmjs-cli 창을 실행한 화면. 출처=DSRV 미디엄
cosmjs-cli 창을 실행한 화면. 출처=DSRV 미디엄



· 주노 테스트넷 환경 설정

Node REPL 콘솔 창을 띄운 상태에서 주노 테스트넷의 RPC 엔드 포인트 주소를 다음과 같이 설정해 줍니다.

const rpcEndpoint = "https://rpc.uni.junonetwork.io:443";



· 지갑 생성

다음으로 지갑을 생성해야 하는데, mnemonic을 랜덤으로 만들어 해당 mnemonic을 가지고 지갑을 생성하고자 합니다. 우선 랜덤으로 mnemonic을 만들기 위한 모듈을 import 해준 후 다음과 같이 mnemonic을 생성합니다.

import { Bip39, Random } from "@cosmjs/crypto";

const mnemonic = Bip39.encode(Random.getBytes(16)).toString();

Random.getBytes(count: number)는 암호화 방식으로 count 길이만큼의 랜덤한 바이트를 반환합니다. Bip39.encode() 는 이렇게 생성된 raw entropy(랜덤으로 생성된 데이터) 바이트를 English mnemonic 으로 인코딩합니다.

English mnemonic은 @cosmjs/crypto 에서 정의하는 클래스로 toString() 메소드를 이용하여 string 타입으로 변환할 수 있습니다.

생성된 mnemonic 출력 예시. 출처=DSRV 미디엄
생성된 mnemonic 출력 예시. 출처=DSRV 미디엄

이제 이렇게 생성한 mnemonic을 가지고 지갑을 만들 차례입니다. Secp256k1HdWallet 클래스를 import 해준 후 다음과 같이 지갑을 생성하고 주소와 퍼블릭 키 값을 받아옵니다.

import { Secp256k1HdWallet } from "@cosmjs/amino";

const wallet = await Secp256k1HdWallet.fromMnemonic(mnemonic, {prefix: "juno"});

const [{ address, pubkey }] = await wallet.getAccounts();

Secp256k1HdWallet는 BTC(비트코인)와 ETH(이더리움)를 비롯한 많은 가상자산이 사용하는 ECDSA 규격 중 하나인 secp256k1 표준을 사용해서 만든 지갑입니다.

Secp256k1HdWallet.fromMnemonic() 메소드는 mnemonic으로 지갑을 생성하고, 이때 옵션(option) 값으로 생성될 지갑 주소의 프리픽스(prefix)를 설정할 수 있습니다. 디폴트(defualt) 값은 “cosmos”로 우리는 주노 테스트넷에 배포를 진행할 것이기 때문에 “juno”를 프리픽스(prefix)로 설정해 줍니다.

Secp256k1HdWallet의 getAccounts() 메소드는 지갑의 AccountData를 반환합니다.

지갑과 주소 출력 예시. 출처=DSRV 미디엄
지갑과 주소 출력 예시. 출처=DSRV 미디엄

이제 이렇게 생성한 지갑의 주소에 콘트랙트를 배포할 때 드는 가스 비용을 지불하기 위한 junox 토큰이 필요합니다. 주노 테스트넷의 faucet을 요청하기 위해서는 Juno 디스코드에 참여해야 합니다.

디스코드의 #faucet 채널에서 아래의 메시지를 보내 테스트넷 토큰을 요청할 수 있습니다. 앞서 얻은 address 값을 복사하여 <address>에 넣어주세요.

$request <address>

주노 디스코드 #faucet 채널에서 faucet 요청 메시지 보내기. 출처=DSRV 미디엄
주노 디스코드 #faucet 채널에서 faucet 요청 메시지 보내기. 출처=DSRV 미디엄

지갑에 토큰이 들어왔다면 이제 클라이언트를 생성해야 합니다. 클라이언트는 네트워크에 대한 인터페이스의 역할을 합니다.

import { SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate";

const client = await SigningCosmWasmClient.connectWithSigner(rpcEndpoint, wallet);

SigningCosmWasmClient.connectWithSigner() 메소드는 HttpEndPoint 주소와 지갑을 인자로 받아 SigningCosmWasmClient를 반환합니다. 우리는 앞선 환경설정 단계에서 주노 테스트넷의 RPC 엔드 포인트 주소를 rpcEndpoint 변수에 담아주었습니다.

만약 위 과정에서 'Error: Account does not exist on chain. Send some tokens there before trying to query sequence.'라는 에러가 발생한다면 faucet 요청이 제대로 진행되지 않은 것이므로 다시 faucet을 요청하도록 합니다.

클라이언트 출력 예시. 출처=DSRV 미디엄
클라이언트 출력 예시. 출처=DSRV 미디엄

 

2. 배포

 

· 바이트코드 업로드하기

먼저 컴파일한 와즘 바이너리 파일을 fs 모듈을 사용해서 읽어옵니다.

import * as fs from "fs";

const wasm = fs.readFileSync("artifacts/my_first_contract.wasm");

와즘 출력 예시. 출처=DSRV 미디엄
와즘 출력 예시. 출처=DSRV 미디엄

코드를 체인 위에 업로드하기 전에 먼저 가스 가격을 설정해야 합니다.

import { calculateFee, GasPrice } from "@cosmjs/stargate";

const gasPrice = GasPrice.fromString("0.025ujunox");

const uploadFee = calculateFee(20_000_000, gasPrice);

와즘 출력 예시. 출처=DSRV 미디엄
계산한 uploadFee 출력 예시

이제 싱잉 코즘와즘 클라이언트(SigningCosmWasmClient)의 upload 메소드를 사용하여 앞서 읽어온 와즘(wasm) 바이너리 코드를 체인 위에 업로드합니다.

const uploadReceipt = await client.upload(address, wasm, uploadFee, "Upload contract");

console.info("Upload succeeded. Receipt:", uploadReceipt);

upload 메소드의 인자로는 (senderAddress, wasmCode, fee, memo) 가 차례로 들어갑니다. 해당 메소드는 code ID를 포함한 receipt를 아래와 같이 반환합니다.

upload 메소드가 반환하는 uploadReceipt 출력 예시
upload 메소드가 반환하는 uploadReceipt 출력 예시

 

· 콘트랙트 인스턴스 생성하기

이제 새로운 콘트랙트 인스턴스를 생성해 보겠습니다. Go CLI를 이용하여 배포했을 때와 마찬가지로 인스턴스의 초기 상태를 msg 변수에 제이슨(JSON) 형식으로 지정해 주고, 앞선 uploadFee와 같이 instantiateFee를 계산하고 instantiate 명령을 실행합니다.

const msg = { "count" : 2 };

const instantiateFee = calculateFee(500_000, gasPrice);

const {transactionHash, contractAddress} = await client.instantiate(address,uploadReceipt.codeId,msg,"My first contract",instantiateFee,{ memo: `Create instance` },);

instantiate 메소드의 인자로는 (senderAddrss, code_Id, INIT_msg, label, fee, option?) 이 차례로 들어갑니다. 실행이 성공적으로 완료되었다면 받아온 transactionHash 값을 주노 익스플로러(Juno explorer)에 검색해서 배포를 확인할 수 있습니다.

instantiate 결과 리턴되는 transactionHash 출력 예시. 출처=DSRV 미디엄
instantiate 결과 리턴되는 transactionHash 출력 예시. 출처=DSRV 미디엄
주노 익스플로러에서 해시값을 이용해 트랜잭션을 검색한 화면. 출처=DSRV 미디엄
주노 익스플로러에서 해시값을 이용해 트랜잭션을 검색한 화면. 출처=DSRV 미디엄



· 콘트랙트 실행하기

이제 마지막으로 다른 네트워크에 배포했을 때와 마찬가지로 트랜잭션을 날려서 배포한 콘트랙트가 잘 동작하는지 실행시켜보도록 하겠습니다.

 

1. get_count

콘트랙트 내부의 상태 변화 없이 값을 읽어오는 쿼리의 경우에는 queryContractSmart 메소드를 사용합니다. 다음과 같이 인자로 위에서 생성한 콘트랙트 인스턴스의 주솟값과, 쿼리 메시지를 전달합니다.

await client.queryContractSmart(contractAddress, {"get_count": {}});

get_count 쿼리를 실행한 출력값. 출처=DSRV 미디엄
get_count 쿼리를 실행한 출력값. 출처=DSRV 미디엄

 

2. increment

콘트랙트 내부의 상태를 바꾸는 트랜잭션의 경우에는 execute 메소드를 사용하며, senderAddress와 가스 비용을 계산해서 인자로 함께 전달해 줍니다.

const executeFee = calculateFee(300_000, gasPrice);

await client.execute(address, contractAddress, { "increment": {} }, executeFee);

다시 get_count 쿼리를 실행해 카운트 값을 가져오면 아래와 같이 이전 카운트 값에서 +1이 증가한 것을 확인할 수 있습니다.

reset 트랜잭션을 실행하고 다시 get_count 쿼리를 실행한 출력값. 출처=DSRV 미디엄
reset 트랜잭션을 실행하고 다시 get_count 쿼리를 실행한 출력값. 출처=DSRV 미디엄

위의 명령이 잘 동작한다면 우리는 주노 테스트넷에서 성공적으로 스마트계약 배포를 완료한 것입니다.

출처=DSRV 미디엄
출처=DSRV 미디엄



아크웨이 테스트넷

아크웨이는 개발자에게 보상을 제공하는 인센티브화된 스마트계약 플랫폼으로 위에서 설명한 다른 네트워크들과 마찬가지로 코스모스 SDK를 이용해서 만든 네트워크입니다. 아크웨이에 대한 자세한 설명은 공식 문서에서 확인할 수 있습니다.

우리는 아크웨이의 테스트넷인 constantine-1에 스마트계약을 배포해 보도록 하겠습니다.

 

1. 환경설정

 

· @cosmjs/cli 설치하기

주노 테스트넷에 배포할 때 설치한 @cosmjs/cli를 똑같이 사용하여 진행합니다. 혹시 설치를 하지 않으셨다면 주노 테스트넷의 환경설정을 참고해 주세요.

 

· 아크웨이 테스트넷 환경 설정

아래의 명령어를 통해 새롭게 cosmjs-cli를 실행하여 Node REPL 콘솔을 열어줍니다.

./node_modules/.bin/cosmjs-cli

아크웨이 테스트넷의 RPC 엔드 포인트 주소를 다음과 같이 설정해 줍니다.

const rpcEndpoint = "https://rpc.constantine-1.archway.tech:443"



· 지갑 생성

다음으로 주노 테스트넷에서 진행했던 것과 같이 random한 mnemonic을 만들어줍니다.

import { Bip39, Random } from "@cosmjs/crypto";

const mnemonic = Bip39.encode(Random.getBytes(16)).toString();

그리고 이렇게 생성된 mnemonic을 이용하여 지갑을 생성합니다.

import { Secp256k1HdWallet } from "@cosmjs/amino";

const wallet = await Secp256k1HdWallet.fromMnemonic(mnemonic, {prefix: "archway"});

const [{ address, pubkey }] = await wallet.getAccounts();

mnemonic과 지갑 주소 출력 예시. 출처=DSRV 미디엄
mnemonic과 지갑 주소 출력 예시. 출처=DSRV 미디엄

 

이제 아크웨이 테스트넷의 const 토큰을 받기 위해 faucet을 요청해야 하는데 아크웨이 디스코드의 #faucet 채널에서 아래의 메시지를 보내야 합니다. 앞서 얻은 주솟값을 복사하여 <address>에 넣어주세요.

!faucet <address>

디스코드의 #faucet 채널에서 faucet 요청 메시지 보내기. 출처=DSRV 미디엄
디스코드의 #faucet 채널에서 faucet 요청 메시지 보내기. 출처=DSRV 미디엄

지갑에 토큰이 들어왔다면 이제 클라이언트를 생성해야 합니다. 클라이언트는 네트워크에 대한 인터페이스의 역할을 합니다.

import { SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate";

const client = await SigningCosmWasmClient.connectWithSigner(rpcEndpoint, wallet);



2. 배포

 

· 바이트코드 업로드하기

먼저 컴파일한 와즘 바이트코드를 fs 모듈을 사용해서 읽어옵니다.

import * as fs from "fs";

const wasm = fs.readFileSync("artifacts/my_first_contract.wasm");

코드를 체인 위에 업로드하기 전에 먼저 가스 격을 설정해야 합니다.

import { calculateFee, GasPrice } from "@cosmjs/stargate";

const gasPrice = GasPrice.fromString("0.025uconst");

const uploadFee = calculateFee(1_500_000, gasPrice);

계산한 uploadFee 출력 예시. 출처=DSRV 미디엄
계산한 uploadFee 출력 예시. 출처=DSRV 미디엄

이제 upload 메소드를 이용해 읽어온 와즘 코드를 체인 위에 업로드합니다.

const uploadReceipt = await client.upload(address, wasm, uploadFee, "Upload contract");

console.info("Upload succeeded. Receipt:", uploadReceipt);

uploadReceipt 출력 예시. 출처=DSRV 미디엄
uploadReceipt 출력 예시. 출처=DSRV 미디엄

 



· 콘트랙트 인스턴스 생성하기

이제 새로운 콘트랙트 인스턴스를 생성해 보겠습니다. Go CLI를 이용하여 배포했을 때와 마찬가지로 인스턴스의 초기 상태를 msg 변수에 제이슨(JSON) 형식으로 지정해 주고, 앞선 uploadFee와 같이 instantiateFee를 계산하고 instantiate 명령을 실행합니다.

const msg = { "count" : 2 };

const instantiateFee = calculateFee(500_000, gasPrice);

const {transactionHash, contractAddress} = await client.instantiate(
    address,
    uploadReceipt.codeId,
    msg,
    "My first contract",
    instantiateFee,
    { memo: `Create instance` },
);

실행이 성공적으로 완료되었다면 받아온 transactionHash 값을 아크웨이 익스플로러(Archway Explorer)에 검색해서 배포를 확인할 수 있습니다.

instantiate 실행 결과 리턴된 transactionHash 출력 예시. 출처=DSRV 미디엄
instantiate 실행 결과 리턴된 transactionHash 출력 예시. 출처=DSRV 미디엄
아크웨이 익스플로러에서 해시값을 이용해 트랜잭션을 검색한 화면. 출처=DSRV 미디엄
아크웨이 익스플로러에서 해시값을 이용해 트랜잭션을 검색한 화면. 출처=DSRV 미디엄



· 콘트랙트 실행하기

이제 마지막으로 다른 네트워크에 배포했을 때와 마찬가지로 트랜잭션을 날려서 배포한 콘트랙트가 잘 동작하는지 실행시켜보도록 하겠습니다.

 

1. get_count

await client.queryContractSmart(contractAddress, {"get_count": {}});

get_count 쿼리를 실행한 출력값. 출처=DSRV 미디엄
get_count 쿼리를 실행한 출력값. 출처=DSRV 미디엄



2. increment

const executeFee = calculateFee(300_000, gasPrice);

await client.execute(address, contractAddress, { "increment": {} }, executeFee);

increment 트랜잭션을 실행하고 다시 get_count 쿼리를 실행한 출력값. 출처=DSRV 미디엄
increment 트랜잭션을 실행하고 다시 get_count 쿼리를 실행한 출력값. 출처=DSRV 미디엄

 

3. reset

await client.execute(address, contractAddress, { "reset": {"count":123} }, executeFee);

reset 트랜잭션을 실행하고 다시 get_count 쿼리를 실행한 출력값
reset 트랜잭션을 실행하고 다시 get_count 쿼리를 실행한 출력값

위의 명령이 잘 동작한다면 우리는 아크웨이 테스트넷에서 성공적으로 스마트 콘트랙트 배포를 완료한 것입니다.

 



글을 마무리하며

이번 시간에는 1편에서 살펴본 코즘와즘을 활용한 카운터 콘트랙트 예제 코드를 컴파일하고, 이렇게 생성한 동일한 와즘 파일을 클리프넷, 오스모시스 테스트넷, 주노 테스트넷, 아크웨이 테스트넷에 wasmd와 CosmJS를 사용하여 배포해 보았습니다.

컴파일한 하나의 코드를 그대로 코즘와즘을 지원하는 여러 블록체인 네트워크에 올릴 수 있으며, 배포 명령어 또한 비슷하여 쉽게 배포가 가능하다는 점과 코즘와즘은 콘트랙트 코드를 업로드하는 과정과 새로운 콘트랙트를 초기화하고 인스턴스화 시키는 과정을 분리하였기 때문에 바이트코드의 ID만 알고 있으면 하나의 바이트코드를 공유할 수 있다는 장점을 느낄 수 있었습니다.

그러나 이더리움에 비해 배포 과정이 복잡하다고 느껴질 수 있는 점과 배포 방법의 다양성이 아직 부족한 점은 단점으로 파악되었습니다.

다음 시간에는 배포를 완료한 콘트랙트를 이용하여 프런트엔드와 통신하는 방법을 설명하고자 합니다.

이 글이 웹 어셈블리 공부를 시작하고자 했던 많은 개발자분들께서 코즘와즘에 쉽게 입문하는 데 도움이 되었길 바라며, 다음 글로 또 찾아오도록 하겠습니다. 이 글을 읽는 데 귀중한 시간을 할애해 주셔서 감사합니다.

저자: 윤수지 DSRV 데브 에반젤리스트 인턴

유의사항: 이 글은 정보 전달을 위한 목적으로 작성되었으며, 특정 프로젝트에 대한 투자 권고, 법률적 자문 등 목적으로 하지 않습니다. 모든 투자의 책임은 개인에게 있으며, 이로 발생된 결과에 대해 어떤 부분에서도 DSRV는 책임을 지지 않습니다. 본문이 포괄하는 내용들은 특정 자산에 대한 투자를 추천하는 것이 아니며, 언제나 본문의 내용만을 통한 의사결정은 지양하시길 바랍니다.

안녕하세요, 웹3 시대의 인프라를 만들어가는 DSRV입니다!

웹3 개발, 어디서부터 시작해야 할지 고민이셨나요? 지식 콘텐츠부터 실전 코딩까지 더 많은 개발자들과 탄탄한 웹3 인프라를 만들어가기 위해, DSRV는 다양한 메인넷과 스마트계약에 대한 가이드를 제공합니다.

정보를 찾기 어려워 쉽사리 시작하지 못했던 웹3 댑(DApp, 탈중앙화애플리케이션) 및 콘트랙트 개발, 이제 개발자 플레이그라운드(Dev Playground)와 함께 차근차근 알아가보아요.

이번 코즘와즘(CosmWasm) 101 시리즈는 간단한 클리커 게임(Clicker Game)을 만들기 위해 필요한 콘트랙트 설명, 배포, 리액트(React) 프런트엔드 연결까지 필요한 모든 내용을 실습을 통해 차근차근 이해할 수 있도록 구성되었습니다.

웹3와 블록체인에 관심이 많은 웹 개발자라면 누구나 함께할 수 있습니다.

 

더 자세한 내용은 '코인데스크 프리미엄'에서 읽을 수 있습니다.

 

제보, 보도자료는 contact@coindeskkorea.com



댓글삭제
삭제한 댓글은 다시 복구할 수 없습니다.
그래도 삭제하시겠습니까?
댓글 0
댓글쓰기
계정을 선택하시면 로그인·계정인증을 통해
댓글을 남기실 수 있습니다.