This tutorial is intended to give a guide to migrate an Ethereum App to Klaytn. No previous Klaytn experience is needed. A simple blockchain app will be used as a sample to show how to migrate an Ethereum App to Klaytn.
We will focus only on the code modifications required to migrate an Ethereum App to Klaytn. If you need details on creating a Klaytn dApp, Please refer to CountDApp Tutorial.
We assume that you have basic knowledge on React. Sample code is made with React.
Basic knowledge and experience in Blockchain app is required, but no previous Klaytn experience is needed.
Testing Environment
CountDApp is tested in the following environment.
MacOS Mojave 10.14.5
Node 10.16.0 (LTS)
npm 6.9.0
Python 2.7.10
2. Klaytn has Ethereum compatibility
Klaytn runtime environment is compatible with Ethereum Virtual Machine and executes smart contracts written in Solidity. Klaytn's RPC APIs and other client libraries maintain almost identical API specifications with Ethereum's whenever available. Therefore, it is fairly straightforward to migrate Ethereum Apps to Klaytn. This helps developers easily move to a new blockchain platform.
To interact with the contract, we need to create an instance of the deployed contract. With the instance, we can read and write the contract's data.
Let's learn step by step how to migrate CountDApp from Ethereum to Klaytn!
5-1. Deploy Count contract on Klaytn
5-2. Create a contract instance
5-3. Interact with contract
5-1. Deploy Count contract on Klaytn
The first step is deploying Count contract on Klaytn and get the contract address. Most of the cases, you can use Etherem contracts on Klaytn without modification. See Porting Etherem Contract. In this guide, we will use Truffle to deploy the contract.
Change network properties in truffle-config.js to deploy the contract on Klaytn.
You can create a contract instance with the caver-js API. The contract instance creates a connection to Count contract. You can invoke contract methods through this instance.
The ABI (Application Binary Interface) used to create the Count contract instance allows the caver-js to invoke contract's methods as below. You can interact with Count contract as if it were a JavaScript object.
Read data (call)
CountContract.methods.count().call()
Write data (send)
CountContract.methods.plus().send({ ... })CountContract.methods.minus().send({ ... })
Once you created a contract instance as in the previous step, you don't need to modify any code in using the contract methods afterward. dApp migration has been completed!
Full code: Count component
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. Create contract instance **// ex:) new caver.klay.Contract(DEPLOYED_ABI, DEPLOYED_ADDRESS)// You can call contract method through this instance.// 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]// Need to integrate wallet for calling contract method.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