LoginSignup
8

More than 3 years have passed since last update.

EthereumコントラクトとReactの連携

Last updated at Posted at 2018-12-20

追記 2020/3/4

web3のアップデートに対応し、TypeScript + React + Redux + Web3 + truffle + React-routerのボイラープレートを作成しました。ここにあります。qiita.gif

追記 2019/12/29

本記事よりもReact Truffe Boxの方が非常に優秀です。

この記事について

solidityでコントラクトは書けるけど,結局どうやってフロントエンドと繋げればいいの?
といった疑問を少しでも解決できたらいいなと思い,自身の体験を書いてみます.

開発環境

現在のEthereum上で動作しているDaapsで幅広く使用されているフレームワークであるtruffleでの開発を想定します.
フロントエンドは,truffleの公式サイトでも推されているReactを用います.

手順

もろもろインストール

  • npm init
  • npm install --save next react react-dom
  • npm install truffle-contract --save

package.jsonに以下を追記

package.json
 {
   // ...
   "scripts": {
     "dev": "next client", 
     "test": "echo \"Error: no test specified\" && exit 1"
   },
   // ...
 }

以下のようにclientフォルダとpagesフォルダなどを作成する.

スクリーンショット 2018-12-19 19.53.18.png

metamaskのチェック

client/web3/provider.js

 import Web3 from "web3"

 const provider = () => {
   // If the user has MetaMask:
   if (typeof web3 !== 'undefined') {
     return web3.currentProvider
   } else {
     console.error("You need to install MetaMask for this app to work!")
   }
 }

 export const eth = new Web3(provider()).eth

client/pages/index.jsを作成

client/pages/index.js
 import {eth,getInstance} from "../web3/provider"

 export default class IndexPage extends React.Component{
     render() {
         return (
          <h1>Hello</h1>
         )
     }
 }

npm run devすると,ブラウザを開いてlocalhost:3030にHelloが表示される.

truffle migrateの後にcontractのjsonファイルをコピーする必要があるので以下をpackage.jsonに追記

package.json
 {
   // ...
   "scripts": {
     "dev": "npm run artifacts && next client",
     "artifacts": "cp -r ./build/contracts/ ./client/web3/artifacts",
     "test": "echo \"Error: no test specified\" && exit 1"
   },

   // ...
 }

npm run artifactsを実行.
これで,jsファイルからコントラクトのインスタンスを呼べるようになった.

インスタンスの呼び方

index.js
import { eth, getInstance } from "../web3/provider"
export default class IndexPage extends React.Component {
    async componentDidMount() {
        const storage = await getInstance(Vote) 
        console.log(storage) 
    }
}

getInstanceweb3/provider.jsで定義

以下,getInstanceの定義

web3/provider.js
 export const getInstance = artifact => {
     const contractObj = contract(artifact)
     contractObj.setProvider(provider())
     return contractObj.deployed()
 }

関数を実行するには

  • 基本的にcontractInstance.function()で関数を呼ぶことができる.
client/pages/index.js
  import { eth, getInstance } from '../web3/provider'
  import Vote from "../web3/artifacts/Vote.json"
  import {SetVoterAddr} from "../web3/voters"

  export default class IndexPage extends React.Component {
    async componentDidMount() {
      const storage = await getInstance(Vote)
      const ownerAddr = await storage.ownerAddr.call()         
      console.log("Owner Address : ",ownerAddr)
    }
  }

これで,コントラクトで実装した関数をフロントエンドで呼ぶことができた.

フォームとの連携

フォーム

  • handleSubmitにawait function(this.state.value)を付け加える
voter.js
 export class SetVoterAddr extends React.Component{
   constructor(props){
     super(props);
     this.state = {value:''};
     this.handleChange = this.handleChange.bind(this);
     this.handleSubmit = this.handleSubmit.bind(this);
   }

   async handleChange(event){
     this.setState({value:event.target.value});
   }

   async handleSubmit(event){
     console.log("An address was submitted:" + this.state.value);
     event.preventDefault();
     await setVoterAddr(this.state.value)   // <-- add this line
     await test(this.state.value)
   }

   render(){
     return(
       <form onSubmit={this.handleSubmit}>
         <label>
           Voter Address:
           <input type="text" value={this.state.value} onChange={this.handleChange}/>
         </label>
         <input type="submit" value="Submit"/>
       </form>
     )
   }
 }

注意点:関数を呼び出すときはアカウントを指定しておかないとinvalid addressになる.

基本的な関数呼び出しの仕方

 // eth.getAccounts()でaddressをゲットしておかないといけない
 export const test = async(value)  => {
   const storage = await getInstance(Vote)
   const addresses = await eth.getAccounts()
   const tx = await storage.test(
     value,
   {
     from:addresses[0],
   })

   return tx
 }

まとめ

フロントエンドとコントラクトの繋ぎ方は他にもあるのですが,一例としてNext.jsを用いた例を説明しました.
ここまで載せたサンプルコードは,こちらにあります.

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8