[CosmWasm 101 와씀] 2편: Go CLI와 CosmJS를 활용하여 Counter 컨트랙트 배포하기

Suji Yoon
DSRV
Published in
36 min readJun 20, 2022

DSRV Dev Guild에서는 더 많은 개발자들과 Web3 인프라를 만들어가기 위해, 다양한 메인넷과 스마트 컨트랙트에 대한 가이드를 연재합니다.

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

[CosmWasm 101 시리즈]

  1. Counter 컨트랙트 톺아보기
  2. Counter 컨트랙트 배포하기 (Go CLI 및 CosmJS 활용)
  3. Frontend와 통신하기

지난 번 ‘[CosmWasm 101 시리즈] 1편: Counter 컨트랙트 톺아보기’에서는 WebAssembly가 무엇인지와 어떤 의미를 갖는지 알아보고, 실제로 Cosmwasm을 활용한 간단한 컨트랙트인 Counter 예제를 살펴보았습니다. 이번 시간에는 앞서서 작성한 컨트랙트 코드를 컴파일하고, 이렇게 생성된 동일한 wasm 파일을 활용하여 Cliffnet, Osmosis 테스트넷, Archway 테스트넷, Juno 테스트넷에 배포하는 과정을 설명하고자 합니다.

Wasm 코드 컴파일하기

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

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

rustup default stable 명령어는 stable 채널의 toolchain에서 컴파일을 진행하겠다는 뜻입니다. toolchain은 rust의 컴파일러를 뜻하고 rust는 stable, beta, nightly 라는 세 가지의 release channel을 가지고 있는데 이 중 stable 채널은 가장 최근에 릴리즈 된 버전을 말합니다. rustup은 이러한 각기 다른 버전들을 손쉽게 관리할 수 있도록 도와줍니다.

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

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 파일이 생성된 것을 확인하실 수 있습니다. 해당 파일은 실행 가능한 wasm 바이너리 파일입니다.

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

Rust 컴파일 최적화하기

블록체인 상에서 스마트 컨트랙트를 배포하고 실행하기 위해서는 가스(gas) 비용을 지불해야 합니다. 가스 비용을 줄이는 데는 다양한 방법이 존재하는데 그 중 하나는 wasm으로 컴파일 된 바이트코드의 크기를 최적화하는 것입니다. 또한 CosmWasm은 컴파일 최적화를 진행하지 않으면 exceeds limit 에러가 발생하면서 배포가 되지 않기 때문에 컴파일 최적화는 선택이 아닌 필수라고 할 수 있습니다.

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

ls -lh 명령어를 이용해 바이트 코드의 크기를 확인하는 화면

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

배포 시 발생하는 에러 메시지
Explorer에서 트랜잭션 해시값을 이용해 확인한 가스 비용 (최적화 하기 전) — 링크

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

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

다시 ls -lh 명령어를 사용해서 파일 크기를 확인해보면 156KB로 이전과 비교했을 때 훨씬 작아진 것을 확인할 수 있고, 배포도 정상적으로 진행되며 gas 비용을 확인했을 때 아래와 같이 1,070,212 으로 이전 값보다 훨씬 줄어들었습니다. 이렇게 최적화를 통해 컴파일 된 코드를 줄이는 것은 gas 비용을 줄이는 데 도움을 준다는 것을 확인할 수 있습니다.

Explorer에서 트랜잭션 해시값을 이용해 확인한 가스 비용 (최적화 진행 이후) — 링크

위의 방법 이외에도 CosmWasm에서는 최적화 툴로 rust-optimizer를 제공하는데, Docker가 설치되어 있다면 아래의 코드를 실행하여 rust-optimizer를 사용할 수 있습니다.

rust-optimizer는 CosmWasm 스마트 컨트랙트를 위해 만들어진 툴로 RUSTFLAGS와 wasm-opt를 함께 사용하여, RUSTFLAGS만 사용했을 때보다 조금 더 나은 최적화 성능을 보입니다. wasm-opt는 WebAssembly용 최적화 프로그램인 Binaryen IR을 실행하여 최적화를 진행합니다.

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

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

스마트 컨트랙트 배포하기

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

  1. 컴파일된 바이트코드의 업로드
  2. 새로운 컨트랙트를 인스턴스화 (instantiate)
  3. 컨트랙트의 실행

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

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

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

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

CosmWasm을 배포하는 방법에는 Go CLI인 wasmd를 이용한 방법과 CosmJS Node REPL을 이용하는 방법이 있습니다.

Cliffnet과 Osmosis Testnet에서는 Go CLI wasmd를 사용한 배포 방법을, Juno Testnet과 Archway Testnet에서는 CosmJS Node REPL을 사용한 배포 방법을 보여드리도록 하겠습니다.

Go CLI wasmd/osmosisd로 배포하기 — Malaga & Osmosis Testnet

Malaga

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

1. 환경 설정

  • 환경 설정

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

rustup을 설치한 후, 아래의 명령어를 실행합니다.

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

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

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

  • Set up Malaga

curl 명령어로 Malaga 네트워크의 configuration을 읽어온 후 source 명령어로 읽어온 값을 적용합니다.

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

  • wallet 생성

배포를 위한 wallet 을 생성합니다.

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

wallet 생성 명령어 실행 예시

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

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

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

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

지갑의 잔액을 확인하는 명령어 실행 예시

2. 배포

  • 바이트코드 업로드하기

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

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

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

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

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

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

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

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

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

echo $RES 실행 예시

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

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

  • 컨트랙트 인스턴스 생성하기

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

instantiate 명령어 실행 예시

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

Malaga explorer에서 해시값을 이용해 트랜잭션 검색한 결과

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

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

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

배포된 컨트랙트의 주소를 출력하는 명령어 실행 예시

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

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

  • 컨트랙트 실행하기

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

  1. get_count

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

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

2. increment

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

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

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

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

tx 명령어 실행 예시 (위 사진은 cliffnet 네트워크 예시이므로 본문의 명령어에서 amount 단위가 상이합니다.)

3. reset

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

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

축하합니다! 우리는 Cliffnet에서 컨트랙트를 배포하는 작업을 성공적으로 마무리했습니다🥳

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

Osmosis testnet

Osmosis는 Cosmos SDK로 만들어진 AMM(advanced automated market maker) 프로토콜 입니다. Osmosis에 대한 자세한 설명은 아래의 공식 docs에서 확인할 수 있습니다.

https://docs.osmosis.zone/overview/#introduction

우리는 Osmosis의 테스트넷인 osmo-test-4 에 스마트 컨트랙트를 배포해보도록 하겠습니다.

1. 환경 설정

  • Rust 환경 설정
  • Osmosis Testnet 환경 설정

Osmosis Installer를 사용하여 손쉽게 Osmosis testnet 환경을 설정할 수 있습니다.

다음 명령어를 입력하고 나타난 화면에서 Client Node와 Testnet 옵션을 차례대로 선택하여 설치하면 환경 설정이 완료됩니다.

Osmosis Installer 설치 예시
  • wallet 생성

Osmosis Testnet의 faucet을 받기 위해서는 Osmosis 디스코드에 참여해야 하지만, ATN(All That Node)를 사용해서 더욱 편리하게 faucet을 요청할 수 있습니다. 다음 링크로 들어가서 faucet을 받고 싶은 지갑의 주소를 입력한 후 Claim Your Tokens 버튼을 누르면 faucet이 요청되고 성공 시 아래와 같이 트랜잭션 주소가 출력됩니다. 지갑의 주소는 osmosisd keys show -a wallet 명령어를 통해 확인할 수 있습니다.

ATN에서 제공하는 Osmosis faucet을 이용한 화면

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

2. 배포

  • 바이트코드 업로드하기

이제 앞서 Cliffnet에 배포한 my_first_contract.wasm 바이너리 코드를 Osmosis Testnet에 똑같이 배포해보겠습니다. 배포를 위해서는 테스트넷의 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이 Public Node보다 높습니다.

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

ATN에서 API 키를 발급받는 방법

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

ATN의 Dashboard 화면

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

ATN의 API 키와 엔드포인트를 보여주는 화면

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

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

wasmdosmosisd 로 바뀌었을 뿐 그 외의 명령어는 Cliffnet 배포에서 설명한 것과 동일합니다.

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

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

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

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

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

Osmosis explorer에서 해시값을 이용해 트랜잭션을 검색한 결과

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

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

  1. get_count

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

2. increment

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

3. reset

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

다음은 https://osmosis-contracts.web.app/ 사이트의 GUI를 이용해서 컨트랙트 인스턴스를 생성해보겠습니다. 해당 사이트에 접속하면 앞서서 우리가 체인에 업로드한 코드를 확인할 수 있습니다. 방금 우리가 osmosisd 를 이용해 인스턴스화했던 my first contract 컨트랙트 인스턴스도 보입니다.

Osmosis contract explorer에서 업로드한 코드를 확인하는 화면

Osmosis wallet을 연결하여 로그인을 한 후, Create a Contract 버튼을 눌러 JSON 형식의 초기 상태를 입력하고 Instantiate Contract 버튼을 클릭하면 손쉽게 컨트랙트 인스턴스를 생성할 수 있습니다.

Instantiate Contract 버튼을 눌러 컨트랙트 인스턴스를 생성한 후의 화면

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

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

생성된 컨트랙트 인스턴스를 실행할 수 있는 화면
  1. get_count

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

Run query 버튼을 클릭해 get_count 쿼리 메시지를 실행한 화면

2. incremet

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

Execute contract 버튼을 클릭하여 increment 트랜잭션 메시지를 실행한 화면
increment 트랜잭션을 실행한 후 다시 get_count 쿼리를 통해 count 값을 가져온 화면

3. reset

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

Execute Contract 버튼을 클릭하여 reset 트랜잭션 메시지를 실행한 화면
reset 트랜잭션을 실행한 후 다시 get_count 쿼리를 통해 count 값을 가져온 화면

osmosisdGUI를 이용해 각각 생성한 컨트랙트들이 잘 실행된다면 우리는 Osmosis Testnet에서 성공적으로 스마트 컨트랙트 배포를 완료한 것입니다👏👏

CosmJS로 배포하기 — Juno Testnet & Archway Testnet

이번에는 CosmJS Node REPL을 사용하여 똑같은 wasm 바이너리 코드를 Juno Testnet과 Archway Testnet에 배포해보도록 하겠습니다. CosmJS는 Node.js와 모던 브라우저에서 실행되는 Typescript 라이브러리입니다. Go CLI 에서 JSON 형식의 데이터를 다루기 위해서는 CLI 용 json processor인 jq 등을 사용해야 하기 때문에, 스마트 컨트랙트와 관련된 작업을 할 때는 CosmJS가 훨씬 유용합니다.

Juno testnet

Juno는 Cosmos SDK로 만들어진 상호 운용이 가능한 스마트 컨트랙트 네트워크입니다. Juno에 대한 자세한 설명은 아래 공식 docs 에서 확인할 수 있습니다.

https://docs.junonetwork.io/juno/readme

우리는 Juno의 테스트넷인 uni-3 에 스마트 컨트랙트를 배포해보도록 하겠습니다.

1. 환경설정

  • @cosmjs/cli 설치하기

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

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

cosmjs-cli 창을 실행한 화면
  • Juno Testnet 환경 설정

Node REPL 콘솔창을 띄운 상태에서 Juno Testnet의 RPC Endpoint 주소를 다음과 같이 설정해 줍니다.

  • wallet 생성

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

Random.getBytes(count: number)는 암호화 방식으로 count 길이 만큼의 랜덤한 바이트를 반환합니다. Bip39.encode() 는 이렇게 생성된 raw entropy(랜덤으로 생성된 데이터) 바이트를 English mnemonic 으로 인코딩합니다. English mnemonic@cosmjs/crypto 에서 정의하는 클래스로 toString() 메소드를 이용하여 string 타입으로 변환할 수 있습니다.

생성된 mnemonic 출력 예시

이제 이렇게 생성한 mnemonic을 가지고 wallet을 만들 차례입니다. Secp256k1HdWallet 클래스를 import 해준 후 다음과 같이 wallet을 생성하고 address와 publickey 값을 받아옵니다.

Secp256k1HdWallet 는 비트코인과 이더리움을 비롯한 많은 암호화폐가 사용하는 ECDSA 규격 중 하나인 secp256k1 표준을 사용해서 만든 Wallet입니다. Secp256k1HdWallet.fromMnemonic()메소드는 mnemonic으로 wallet을 생성하고, 이때 option 값으로 생성될 지갑 주소의 prefix를 설정할 수 있습니다. defualt 값은 “cosmos”로 우리는 Juno Testnet에 배포를 진행할 것이기 때문에 “juno”를 prefix로 설정해줍니다.

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

wallet과 address 출력 예시

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

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

$request <address>
Juno 디스코드 #faucet 채널에서 faucet 요청 메시지 보내기

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

SigningCosmWasmClient.connectWithSigner() 메소드는 HttpEndPoint 주소와 wallet을 인자로 받아 SigningCosmWasmClient 를 반환합니다. 우리는 앞선 환경설정 단계에서 Juno Testnet의 RPC Endpoint 주소를 rpcEndpoint 변수에 담아주었습니다.

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

client 출력 예시

2. 배포

  • 바이트코드 업로드하기

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

wasm 출력 예시

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

GasPrice는 단일 가스 단위(single unit of gas)의 가격으로 일반적으로 가장 작은 토큰 단위의 일부이기 때문에 토큰 단위에 u 가 붙는 것을 볼 수 있습니다. GasPrice.fromString() 메소드를 통해 생성할 수 있고 calculateFee 메소드는 gasLimit과 gasPrice를 인자로 받아 가스 비용을 계산해 줍니다.

계산한 uploadFee 출력 예시

이제 SigningCosmWasmClient의 upload 메소드를 사용하여 앞서 읽어온 wasm 바이너리 코드를 체인 위에 업로드합니다.

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

upload 메소드가 반환하는 uploadReceipt 출력 예시
  • 컨트랙트 인스턴스 생성하기

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

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

instantiate 결과 리턴되는 transactionHash 출력 예시
Juno explorer 에서 해시값을 이용해 트랜잭션을 검색한 화면
  • 컨트랙트 실행하기

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

  1. get_count

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

get_count 쿼리를 실행한 출력값

2. increment

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

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

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

3. reset

reset 또한 내부 상태를 바꾸는 트랜잭션이므로 execute 메소드를 사용하여 컨트랙트를 실행합니다.

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

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

위의 명령이 잘 동작한다면 우리는 Juno Testnet에서 성공적으로 스마트 컨트랙트 배포를 완료한 것입니다.👏👏

Archway Testnet

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

https://docs.archway.io/

우리는 Archway의 테스트넷인 constantine-1에 스마트 컨트랙트를 배포해보도록 하겠습니다.

1. 환경설정

  • @cosmjs/cli 설치하기

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

  • Archway Testnet 환경 설정

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

./node_modules/.bin/cosmjs-cli

Archway Testnet의 RPC Endpoint 주소를 다음과 같이 설정해 줍니다.

  • wallet 생성

다음으로 Juno Testnet 에서 진행했던 것과 같이 random한 mnemonic을 만들어줍니다.

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

mnemonic과 wallet address 출력 예시

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

!faucet <address>
Juno 디스코드의 #faucet 채널에서 faucet 요청 메시지 보내기

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

2. 배포

  • 바이트코드 업로드하기

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

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

계산한 uploadFee 출력 예시

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

uploadReceipt 출력 예시
  • 컨트랙트 인스턴스 생성하기

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

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

instantiate 실행 결과 리턴된 transactionHash 출력 예시
Archway explorer에서 해시값을 이용해 트랜잭션을 검색한 화면
  • 컨트랙트 실행하기

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

  1. get_count
get_count 쿼리를 실행한 출력값

2. increment

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

3. reset

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

위의 명령이 잘 동작한다면 우리는 Archway Testnet에서 성공적으로 스마트 컨트랙트 배포를 완료한 것입니다.👏👏

글을 마무리하며

이번 시간에는 1편에서 살펴본 CosmWasm을 활용한 Counter 컨트랙트 예제 코드를 컴파일하고, 이렇게 생성한 동일한 wasm 파일을 Cliffnet, Osmosis Testnet, Juso Testnet, Archway Testnet 에 wasmdCosmJS를 사용하여 배포해보았습니다. 컴파일한 하나의 코드를 그대로 CosmWasm을 지원하는 여러 블록체인 네트워크에 올릴 수 있으며, 배포 명령어 또한 비슷하여 쉽게 배포가 가능하다는 점과 CosmWasm은 컨트랙트 코드를 업로드하는 과정과 새로운 컨트랙트를 초기화하고 인스턴스화 시키는 과정을 분리하였기 때문에 바이트코드의 ID만 알고 있으면 하나의 바이트코드를 공유할 수 있다는 장점을 느낄 수 있었습니다. 그러나 이더리움에 비해 배포 과정이 복잡하다고 느껴질 수 있는 점과 배포 방법의 다양성이 아직 부족한 점은 단점으로 파악되었습니다.

다음 시간에는 배포를 완료한 컨트랙트를 이용하여 프론트엔드와 통신하는 방법을 설명하고자 합니다. 이 글이 WebAssembly 공부를 시작하고자 했던 많은 개발자분들께서 CosmWasm에 쉽게 입문하는데 도움이 되었길 바라며, 다음 글로 또 찾아오도록 하겠습니다. 이 글을 읽는데 귀중한 시간을 할애해주셔서 감사합니다.

Written by
Suji Yoon of DSRV, Developer Evangelist Intern (Twitter @suji_forcrypto)

Updated by
Sigrid Jin of DSRV, Technical Writer & Developer Evangelist (Twitter @sigridjin_eth)

Reviewed by
Sigrid Jin of DSRV, Technical Writer & Developer Evangelist (Twitter @sigridjin_eth)
Owen Hwang of DSRV, Research Manager (Twitter @journeywith_eth)

Update History

  • (06/29) wasmvm 1.0 탑재를 위한 테스트넷 업데이트를 반영하여 Cliffnet 네트워크를 신규 Malaga 테스트넷으로 변경하고, 이에 따른 wasmd 버전을 v0.16.0에서 v0.27.0으로 업데이트 하였습니다.

--

--

Suji Yoon
DSRV
Writer for

Software Engineer Intern @DSRV / Ewhachain / Twitter: @suji_forcrypto