🌊

【Solidity × React.js × Ethers.js】ReadOnlyなdAppsの開発

2022/03/22に公開1

変数を読み取って表示するだけの雑dAppsが作りたい!

雑なdApps記事が欲しい!ということで、変数を呼び出すだけのdAppsを作ります。

【スマートコントラクト側】 Solidity

思想

ただ変数を定義するコントラクトをSolidityで作成します。

コード

para.sol
pragma solidity ^0.8.4;  
contract read {  
    uint public para = 20;
}

解説

コントラクトで定義されたpublic変数は自動で、getter関数(読み上げのみ)が生成されます。
https://docs.soliditylang.org/en/v0.8.10/contracts.html#getter-functions

【フロントエンド側】 React.js × Ethers.js

思想

基本的には以下の流れで実装していきます。

  1. Metamaskの確認
  2. Metamaskの接続
  3. 変数のgetter関数を実行

コード

App.js
import { useState } from "react";
import { ethers } from "ethers";

const App = () => {
    //コントラクトアドレスを指定
    const contractAddress = "0x6579087893F1a0B2428A73A7934C2d7c037D5380";

    //コントラクトのAbiを指定
    const paraAbi = [
        {
            inputs: [],
            name: "para",
            outputs: [
                {
                    internalType: "uint256",
                    name: "",
                    type: "uint256",
                },
            ],
            stateMutability: "view",
            type: "function",
        },
    ];

    //各変数の設定
    const [errorMessage, setErrorMessage] = useState(null);
    const [account, setAccount] = useState(null);
    const [piyo, setPiyo] = useState("");

    //メタマスクの接続の設定
    const [provider, setProvider] = useState(null);
    const [signer, setSigner] = useState(null);
    const [contract, setContract] = useState(null);

    //メタマスクの接続
    const connectWalletHandler = () => {
        if (window.ethereum && window.ethereum.isMetaMask) {
            window.ethereum
                .request({ method: "eth_requestAccounts" })
                .then((result) => {
                    accountChangedHandler(result[0]);
                })
                .catch((error) => {
                    setErrorMessage(error.message);
                });
        } else {
            console.log("Need to install MetaMask");
            setErrorMessage(
                "Please install MetaMask browser extension to interact"
            );
        }
    };

    //メタマスクのウォレットアドレスが変わった時に実行する関数
    const accountChangedHandler = (newAccount) => {
        setAccount(newAccount);
        updateEthers();
    };

    //接続されているチェーンが切り替わった時に実行する関数
    const chainChangedHandler = () => {
        window.location.reload();
    };

    //アカウントが切り替わった時のイベントを拾う
    window.ethereum.on("accountsChanged", accountChangedHandler);

    //接続されているチェーンが切り替わった時のイベントを拾う
    window.ethereum.on("chainChanged", chainChangedHandler);

    //provider,signer,contractの設定
    const updateEthers = () => {
        let tempProvider = new ethers.providers.Web3Provider(window.ethereum);
        setProvider(tempProvider);

        let tempSigner = tempProvider.getSigner();
        setSigner(tempSigner);

        let tempContract = new ethers.Contract(
            contractAddress,
            paraAbi,
            tempSigner
        );
        setContract(tempContract);
    };

    //パラメータを読み出す関数
    const readPara = async () => {
        let val = await contract.para().toString();
        setPara(val);
    };

    return (
        <div>
            <div>Hello Contract!</div>
            <button onClick={connectWalletHandler}>Connect Wallet</button>
            <div>
                <h3>Address: {account}</h3>
            </div>
            <button onClick={readPara}>read para</button>
            {para}
        </div>
    );
}

export default App;

解説

エラー1

最初は以下のように実行しようとしたが上手くいかなかった...

const readPara = async () => {
	let val = await contract.para();
	console.log(val);
};

console.logの内容

▼ BigNumber {_hex: '0x14', _isBigNumber: true}
	_hex: "0x14"
	_isBigNumber: true
▶︎ [[Prototype]]: Object

なんかよくわからんものが出てきた...

エラー1の解決策

戻り値はBigNumberという形式らしい...
https://docs.ethers.io/v5/api/utils/bignumber/

BigNumber.toString( ) ⇒ stringsource

Returns the value of BigNumber as a base-10 string.

ということで以下のようにすることで スマコン内で指定した"20"を受け取ることができます。

//パラメータを読み出す関数
const readPara = async () => {
	let val = await contract.para().toString();
        setPara(val);
};

エラー2

以下のコード

const readPara = async () => {
	let val = await contract.para();
	setPara(val);
};

return(
	<div>{para}</div>
);

エラーコード

Error: Objects are not valid as a React child (found: object with keys {id, name, slug}).
If you meant to render a collection of children, use an array instead.

エラー2の解決策

<div>{para}</div>の中身がオブジェクト形式であると以下のエラーが出るので、中身の確認重要
https://zenn.dev/maztak/articles/d62eb0f9844dc5

Discussion

TakaoTakao

para is undefined.

const [piyo, setPiyo] = useState("");