🌈

既存プロジェクトに RainbowKit + wagmi を導入した時の覚書

2024/02/23に公開

Next.jsで作ったサイトにethersを使ってウォレット接続ボタンを置いてたんですが、RainbowKitってやつに変えてみました。すると、コントラクトの読み書きはwagmiに頼ることに。

公開済みのサイトは1つじゃないし今後また別のサイトに実装することもありそうなので、どうやったか書いておくことにします。
ぐぐってもサンプル全然出てこなくて時間かかっちゃった_(:3」z)_

下準備〜導入

基本的に👇ここ見ながらやりました。
https://www.rainbowkit.com/ja/docs/installation

  1. 既存プロジェクトをいじる前に、WalletConnectにサインインしてプロジェクトを作り、IDを入手しておく。

https://cloud.walletconnect.com/

  1. これを既存プロジェクトで実行
npm install @rainbow-me/rainbowkit wagmi viem@2.x @tanstack/react-query
  1. コピペする(手順1で取得したプロジェクトIDを使用)
config.js
import { /* 任意のチェーン */ } from '@wagmi/core/chains'
import { getDefaultConfig } from '@rainbow-me/rainbowkit';

export const config = getDefaultConfig({
  appName: "プロジェクト名",
  projectId: "プロジェクトのID",
  chains: [/* 任意のチェーン */],
  ssr: true,
});
_app.js
import '@rainbow-me/rainbowkit/styles.css';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { WagmiProvider } from 'wagmi';
import { /* 任意のチェーン */ } from 'wagmi/chains';
import { RainbowKitProvider } from '@rainbow-me/rainbowkit';
import { config } from '../wagmi/config'

const queryClient = new QueryClient();

チュートリアルだとconfigも_app.jsに書いてあるんですが、コントラクト読み書きする時に同じものが必要になるので外に出しといた方が良いです。

使えるチェーンはここ👇で探せます。
https://wagmi.sh/react/api/chains

  1. Componentを次のように包む
_app.js
<WagmiProvider config={config}>
  <QueryClientProvider client={queryClient}>
    <RainbowKitProvider initialChain={/* 接続と同時にネットワーク切り替えたい時はここで指定 */}>
      {/* Component */}
    </RainbowKitProvider>
  </QueryClientProvider>
</WagmiProvider>

と、ここでエラーが出て動かない…
error - Error: No QueryClient set, use QueryClientProvider to set one

いろいろぐぐってもこれという解決法が見つからなかったんですけど、いろいろバージョン上げてるうちに出なくなりました。 なんだったのぉ(´・ω・`)

nodeのバージョンアップ方法👇
https://zakkuri.life/node-update/

私の場合は n latest で言うこと聞いてくれなかったので sudo n latest ってしました。

あとは、node_modulesを丸ごと更新したりとか👇
npm update

他に何かしたっけ・・・?
一体何が幸いしたのか、もはやわかりません(´・ω・`)

ConnectWalletボタンを置く

資料こちら👇
https://www.rainbowkit.com/ja/docs/connect-button

改造したくなったらこちら👇
https://www.rainbowkit.com/ja/docs/custom-connect-button

ここはコピペするだけで特に言及することないので、サラッと流します。

コントラクトからデータを取得

👇表示と同時にデータ取得したい時はuseReadContract
https://wagmi.sh/react/api/hooks/useReadContract

import { useReadContract } from 'wagmi'
import abi from '../abi.json';

const Test = () => {
  const result = useReadContract({
    abi,
    address: 'コントラクトのアドレス',
    functionName: '関数名',
    args: [/*引数*/],
  })
  console.log("useReadContract:"+result.status);

  return result.status == "success" ?
    (<>入手したデータ:{ result.data }</>):
    (<>読み込み中 または失敗</>);
};
export default Test;

👇ボタン押したタイミングとかにデータ取得したい時はreadContract
https://wagmi.sh/core/api/actions/readContract

import { readContract } from '@wagmi/core'
import { config } from '../config' //下準備で作ったやつ
import abi from '../abi.json';

const Test2 = () => {
  const readHandler = async () => {
    await readContract(config, {
      abi,
      address: 'コントラクトのアドレス',
      functionName: '関数名',
      args: [/*引数*/],
    }).then(result => {
      console.log("success: "+result);
    }).catch(error => {
      console.log("error: "+error);
    });
  };
  return (<><button onClick={readHandler}>Read Contract</button></>);
};
export default Test2;

コントラクトの処理を実行する

資料こちら👇
https://wagmi.sh/core/api/actions/writeContract

import { writeContract } from '@wagmi/core'
import { config } from '../config' //下準備で作ったやつ
import abi from '../abi.json';

const Test3 = () => {
  const writeHandler = async () => {
    await writeContract(config, {
      abi,
      address: 'コントラクトのアドレス',
      functionName: '関数名',
      args: [/*引数*/],
    }).then(result => {
      console.log("success: "+result);
    }).catch(error => {
      console.log("error: "+error);
    });
  }
  return (<button onClick={writeHandler}>Write Contract</button>);
};
export default Test3;

別のやり方もあるみたいだけど試してないです👇
https://wagmi.sh/react/guides/write-to-contract

イベントを拾いたいときは

例えばこのような👇コントラクトがあって・・・

event sample_event(uint256 hyaku);
function sample_write() external {
    emit sample_event(100);
}

「sample_write 実行後に sample_event で hyaku を受け取りたい!」なんて時は、writeContractも使って、以下のように書きます。

import { writeContract,watchContractEvent } from '@wagmi/core'
import { config } from '../config' //下準備で作ったやつ
import abi from '../abi.json';

const Test4 = () => {
  const writeHandler = async () => {
    const unwatch = watchContractEvent(config, {
      address: 'コントラクトのアドレス',
      abi,
      eventName: 'sample_event',
      onLogs(logs) {
        unwatch();//イベントを待つのをやめる
        console.log("hyaku="+logs[0].args.hyaku);
      },
      onError(error) { 
        unwatch();//イベントを待つのをやめる
      },
    })
    await writeContract(config, {
      abi,
      address: 'コントラクトのアドレス',
      functionName: 'sample_write',
      args: [],
    }).then(result => {
      console.log("success: "+result);
    }).catch(error => {
      console.log("error: "+error);
    });
  }
  return (<button onClick={writeHandler}>Write Contract</button>);
};
export default Test4;

公式からそのままコピペすると watchContractEvent実行直後にunwatch呼んでますが、それ、完全に罠です!
「もうイベント待つのやめるわ」宣言するの早すぎぃ(´・ω・`) もうちょっと待と!
https://wagmi.sh/core/api/actions/watchContractEvent


以上で、自分のやりたいことはできるようになりました。
また別のことを調べたら今回みたいにまとめるかもしれません。

Discussion