본 튜토리얼은 React에 대한 기본 지식이 있다고 가정하고 진행합니다. 샘플 코드는 리액트로 작성되어 있습니다.
블록체인 애플리케이션에 대한 기본적인 지식과 경험이 필요하지만 Klaytn에 대한 사용 경험은 필요치 않습니다.
테스트 환경
CountDApp은 다음의 환경에서 테스트 되었습니다.
MacOS Mojave 10.14.5
Node 10.16.0 (LTS)
npm 6.9.0
Python 2.7.10
2. Klaytn has Ethereum compatibility
Klaytn의 런타임 환경은 이더리움 가상머신과 호환되어 솔리디티로 작성된 스마트 컨트랙트를 실행할 수 있습니다. Klaytn의 RPC API 및 기타 클라이언트 라이브러리들은 가능한 한 거의 동일하게 이더리움과 동일한 API 사양을 유지하고 있습니다. 따라서 이더리움 애플리케이션에서 Klaytn으로 이전하는 것은 매우 간단합니다. 이러한 점들은 개발자들이 새로운 블록체인 플랫폼으로 쉽게 옮길 수 있도록 합니다.
스마트 컨트랙트와 상호작용하기 위해서는 배포된 컨트랙트의 인스턴스를 생성해야 합니다. 생성한 인스턴스를 통해 컨트랙트의 데이터를 읽어오거나 컨트랙트에 데이터를 쓸 수 있습니다.
Let's learn step by step how to migrate CountDApp from Ethereum to Klaytn!
5-1. Klaytn에 Count 컨트랙트 배포
5-2. Create a contract instance
5-3. Interact with contract
5-1. Deploy Count contract on Klaytn
첫 번째 단계는 Count 컨트랙트를 Klaytn에 배포하고 컨트랙트 주소를 가져오는 것입니다. 대부분의 경우 Klaytn에서 이더리움 컨트랙트를 수정 없이 사용할 수 있습니다. 자세한 내용은 이더리움 컨트랙트 포팅을 참고하세요. 이 가이드에서는 트러플을 사용하여 컨트랙트를 배포하겠습니다.
truffle-config.js의 네트워크 속성을 변경하여 Klaytn에 컨트랙트를 배포합니다.
The ABI (Application Binary Interface) used to create the Count contract instance allows the caver-js to invoke contract's methods as below. 자바스크립트 객체처럼 Count 컨트랙트와 상호작용할 수 있습니다.
Read data (call)
CountContract.methods.count().call()
Write data (send)
CountContract.methods.plus().send({ ... })CountContract.methods.minus().send({ ... })
이전 단계에서처럼 컨트랙트 인스턴스를 생성하면, 컨트랙트 메서드를 사용하여 코드를 수정할 필요가 없습니다. dApp migration has been completed!
전체 코드: Count 컴포넌트
src/components/Count.js
import React, { Component } from'react'import cx from'classnames'import caver from'klaytn/caver'import'./Count.scss'classCountextendsComponent {constructor() {super()// ** 1. 컨트랙트 인스턴스 생성 **// 예시: new caver.klay.Contract(DEPLOYED_ABI, DEPLOYED_ADDRESS)// 이 인스턴스를 통해 컨트랙트 메서드를 호출할 수 있습니다.// Now you can access the instance by `this.countContract` variable.this.countContract =DEPLOYED_ABI&&DEPLOYED_ADDRESS&&newcaver.klay.Contract(DEPLOYED_ABI,DEPLOYED_ADDRESS)this.state = { count:'', lastParticipant:'', isSetting:false, } } intervalId =nullgetCount=async () => {// ** 2. Call contract method (CALL) **// ex:) this.countContract.methods.methodName(arguments).call()// You can call contract method (CALL) like above.// For example, your contract has a method called `count`.// You can call it like below:// ex:) this.countContract.methods.count().call()// It returns promise, so you can access it by .then() or, use async-await.constcount=awaitthis.countContract.methods.count().call()constlastParticipant=awaitthis.countContract.methods.lastParticipant().call()this.setState({ count, lastParticipant, }) }setPlus= () => {constwalletInstance=caver.klay.accounts.wallet &&caver.klay.accounts.wallet[0]// 컨트랙트 메서드 호출을 위해 지갑을 연동해야 합니다.if (!walletInstance) returnthis.setState({ settingDirection:'plus' })// 3. ** Call contract method (SEND) **// ex:) this.countContract.methods.methodName(arguments).send(txObject)// You can call contract method (SEND) like above.// For example, your contract has a method called `plus`.// You can call it like below:// ex:) this.countContract.methods.plus().send({// from: '0x952A8dD075fdc0876d48fC26a389b53331C34585', // PUT YOUR ADDRESS// gas: '200000',// })this.countContract.methods.plus().send({ from:walletInstance.address, gas:'200000', }).once('transactionHash', (txHash) => {console.log(` Sending a transaction... (Call contract's function 'plus') txHash: ${txHash} ` ) }).once('receipt', (receipt) => {console.log(` Received receipt! It means your transaction(calling plus function) is in klaytn block(#${receipt.blockNumber}) `, receipt)this.setState({ settingDirection:null, txHash:receipt.transactionHash, }) }).once('error', (error) => {alert(error.message)this.setState({ settingDirection:null }) }) }setMinus= () => {constwalletInstance=caver.klay.accounts.wallet &&caver.klay.accounts.wallet[0]// Need to integrate wallet for calling contract method.if (!walletInstance) returnthis.setState({ settingDirection:'minus' })// 3. ** Call contract method (SEND) **// ex:) this.countContract.methods.methodName(arguments).send(txObject)// You can call contract method (SEND) like above.// For example, your contract has a method called `minus`.// You can call it like below:// ex:) this.countContract.methods.minus().send({// from: '0x952A8dD075fdc0876d48fC26a389b53331C34585', // PUT YOUR ADDRESS// gas: '200000',// })// It returns event emitter, so after sending, you can listen on event.// Use .on('transactionHash') event,// : if you want to handle logic after sending transaction.// Use .once('receipt') event,// : if you want to handle logic after your transaction is put into block.// ex:) .once('receipt', (data) => {// console.log(data)// })this.countContract.methods.minus().send({ from:walletInstance.address, gas:'200000', }).once('transactionHash', (txHash) => {console.log(` Sending a transaction... (Call contract's function 'minus') txHash: ${txHash} ` ) }).once('receipt', (receipt) => {console.log(` Received receipt which means your transaction(calling minus function) is in klaytn block(#${receipt.blockNumber}) `, receipt)this.setState({ settingDirection:null, txHash:receipt.transactionHash, }) }).once('error', (error) => {alert(error.message)this.setState({ settingDirection:null }) }) }componentDidMount() {this.intervalId =setInterval(this.getCount,1000) }componentWillUnmount() {clearInterval(this.intervalId) }render() {const { lastParticipant,count,settingDirection,txHash } =this.statereturn ( <divclassName="Count"> {Number(lastParticipant) !==0&& ( <divclassName="Count__lastParticipant"> last participant: {lastParticipant} </div> )} <divclassName="Count__count">COUNT: {count}</div> <buttononClick={this.setPlus}className={cx('Count__button', {'Count__button--setting': settingDirection ==='plus', })} > + </button> <buttononClick={this.setMinus}className={cx('Count__button', {'Count__button--setting': settingDirection ==='minus', })} > - </button> {txHash && ( <divclassName="Count__lastTransaction"> <pclassName="Count__lastTransactionMessage"> You can check your last transaction in klaytnscope: </p> <atarget="_blank"href={`https://scope.klaytn.com/transaction/${txHash}`}className="Count__lastTransactionLink" > {txHash} </a> </div> )} </div> ) }}exportdefault Count