本記事は下記の翻訳となります。
『Build A Berachain Mobile dApp With WalletConnect & Expo』
🛠️ Berachain でモバイル dApp を構築しよう
一般的に web3 の dApp は、モバイル向けにはあまり力を入れて作られていません。このチュートリアルでは、Expoを使ってBerachainで動作する iOS アプリを構築することで、この状況を変えていきます。
このモバイル dApp では、MetaMask モバイルウォレットを接続し、メッセージに署名し、HelloWorld コントラクトのバイトコードを Berachain Artio (Berachain テストネット) にデプロイすることができます。
WalletConnect Web3Modal と Expo を使用した Berachain モバイル dApp
なぜ Expo なのか?
Expo は React Native の分野でモバイル開発に人気が高まっており、開発者がネイティブ開発や単なる React Native よりも Expo を選ぶ主な理由は3つあります:
- 複数のプラットフォームをサポート - iOS と Android
- 事前にビルドされたコンポーネントと機能
- 更新のたびに AppStore の承認を得る必要がなく、即時更新が可能な Expo EAS (Expo Application Service)
WalletConnect とモバイル
WalletConnect は、ほとんどの web3 dApp の定番となっており、多くのウォレットをサポートしつつ、SDK を通じて開発者にウォレット接続機能をすぐに実装できるツールを提供しています。
また WalletConnect には、モバイルデバイス上で直接ウォレット接続やインタラクションを可能にするモバイル専用の SDK があります。このチュートリアルではこの SDK を使用します。
React Native 向け WalletConnect Web3Modal
📋 要件
続行する前に、以下のインストールまたはセットアップが必要です。
注意: このチュートリアルでは iOS に焦点を当てます
- NVM または Node
v20.11.0
- Expo Go
- iOS シミュレータ付きの Xcode
- iOS MetaMask モバイルウォレット
- Berachain の
$BERA
トークン - (Berachain フォーセットトークンが必要ですか?)
💻 事前準備 - Sonoma での iOS シミュレータ
最新の Mac OS Sonoma と Xcode では、Apple は開発者に事前にインストールされていない可能性のある特定のランタイムシミュレータをダウンロードする追加手順を設けました。
まず、Xcode を起動し、Window > Devices & Simulators に移動します。新しいウィンドウが開きます。左上の Simulators を選択し、Device Type のドロップダウンから iPhone 15 Pro Max
を、OS Version から Download more simulator runtimes…
を選択します。
Xcode シミュレータランタイム
これにより別のウィンドウが開き、iOS 17.2
をダウンロードできます。
Xcode シミュレータランタイムのプラットフォームダウンロード
このダウンロードが完了するまで待ち、以下のコマンドを実行して Simulator.app が正常にダウンロードされたことを確認します:
open /Applications/Xcode.app/Contents/Developer/Applications/Simulator.app;
iOS シミュレータが正常に動作している状態
📱Berachain モバイル dApp の構築
iOS シミュレータのセットアップが完了したので、モバイルアプリケーション全体を構築する準備が整いました。
WalletConnect プロジェクト ID
コーディングを始める前に、https://cloud.walletconnect.com/app で WalletConnect プロジェクト ID をセットアップする必要があります。
すでにプロジェクト ID をお持ちの場合は、このステップをスキップできます。
WalletConnect Cloud サインイン に移動して新しいアカウントを作成します。
https://cloud.walletconnect.com/sign-in
ダッシュボードで、新しいプロジェクトを作成します。
WalletConnect - プロジェクト作成
プロジェクトの詳細を入力します。
WalletConnect - プロジェクト詳細
後で必要になるプロジェクト ID を取得します。
WalletConnect プロジェクト ID
プロジェクトのセットアップ
WalletConnect プロジェクト ID を取得したので、ベースとなる expo プロジェクトを作成し、必要な依存関係を追加しましょう。
npx create-expo-app berachain-walletconnect-expo -t;
# [期待されるプロンプトと出力]:
# ? Choose a template: › - Use arrow-keys. Return to submit.
# Blank
# ❯ Blank (TypeScript) - blank app with TypeScript enabled
# Navigation (TypeScript)
# Blank (Bare)
# ...
# ✅ Your project is ready!
#
# To run your project, navigate to the directory and run one of the following npm commands.
#
# - cd berachain-walletconnect-expo
# - npm run android
# - npm run ios
# - npm run web
必要な依存関係をインストールします。
注意: ここでは
pnpm
を使用しますが、expo との相性に問題があることが知られています。可能な限り expo ではyarn
の使用を推奨します。
cd berachain-walletconnect-expo;
pnpm add @web3modal/wagmi-react-native wagmi@1.4.13 viem@1.21.4 @react-native-async-storage/async-storage react-native-get-random-values react-native-svg react-native-modal @react-native-community/netinfo @walletconnect/react-native-compat expo-application expo-linking;
# モノレポ / turbo repo の場合は、直後にディレクトリで以下を実行
# pnpm install --ignore-workspace;
# @web3modal/wagmi-react-native - walletconnect react native web3modal
# wagmi@1.4.13 - web3 react hooks (現在 v1 のみサポート)
# viem@1.21.4 # EVMと対話するためのJSライブラリ (現在 v1 のみサポート)
# @react-native-async-storage/async-storage - ローカルデバイスストレージ用
# react-native-get-random-values - ローカルの乱数生成器
# react-native-svg - ネイティブsvgサポート
# react-native-modal - モーダルサポート
# @react-native-community/netinfo - デバイスのネットワーク情報用API
# @walletconnect/react-native-compat - 追加サポート用のシム/ポリフィル
# expo-application - ネイティブアプリ情報用
# expo-linking - ブラウザを開くための外部リンクを許可
アプリが正しく動作するかテストしてみましょう - ヒント: うまくいきません 😅
注意: これは
pnpm
特有の問題です。yarn
で全ての作業を行った場合、この問題は発生しません。
# FROM: ./berachain-walletconnect-expo;
pnpm ios;
# [期待される出力]:
# (以下を参照)
Expo が動作していない - 期待される状態
⚠️ PNPM Expo の修正
yarn
を使用している場合でも、これらの修正を行うことができ、全てのパッケージマネージャーで動作します。
この pnpm
特有の問題を修正するには、プロジェクトに3つの変更を加える必要があります。
1 — Babel Runtime 依存関係のインストール
# FROM: ./berachain-walletconnect-expo;
pnpm add -D @babel/runtime;
2 — Package JSON のアプリエントリポイントの修正
ファイル: ./package.json
{
"name": "berachain-walletconnect-expo",
"version": "1.0.0",
- "main": "node_modules/expo/AppEntry.js",
+ "main": "index.ts",
"scripts": {
"start": "expo start",
"android": "expo start --android",
"ios": "expo start --ios",
"web": "expo start --web"
},
"dependencies": {
"@react-native-async-storage/async-storage": "^1.22.1",
"@react-native-community/netinfo": "^11.3.0",
"@walletconnect/react-native-compat": "^2.11.1",
"@web3modal/wagmi-react-native": "^1.2.0",
"expo": "~50.0.7",
"expo-application": "^5.8.3",
"expo-status-bar": "~1.11.1",
"react": "18.2.0",
"react-native": "0.73.4",
"react-native-get-random-values": "^1.10.0",
"react-native-modal": "^13.0.1",
"react-native-svg": "^14.1.0",
"viem": "1.21.4",
"wagmi": "1.4.13"
},
"devDependencies": {
"@babel/core": "^7.20.0",
"@types/react": "~18.2.45",
"typescript": "^5.1.3"
},
"private": true
}
3 — 新しいエントリファイルの追加と App.tsx
の移動
# FROM: ./berachain-walletconnect-expo;
touch index.ts;
mkdir app;
mv App.tsx app;
ファイル: ./index.ts
// インポート
// ========================================================
import { registerRootComponent } from "expo";
// メインアプリ
// ========================================================
import App from "./app/App";
// registerRootComponent は AppRegistry.registerComponent('main', () => App) を呼び出します
// また、Expo Go またはネイティブビルドでアプリを読み込む場合に
// 環境が適切に設定されることを保証します
registerRootComponent(App);
では再度 pnpm ios
を試してみましょう。これで動作するはずです。また、yarn
や npm
でも同様に動作するはずです。
PNPM 修正後の Expo iOS シミュレータが動作している状態
プロジェクトのスタイリング
スタイリングには、アプリ全体で一貫したスタイリングを維持するために、Nativewindを使用したTailwindを使用します。
次の手順は複数ありますが、一度セットアップすれば、Tailwind が正しく設定されます。
# FROM: ./berachain-walletconnect-expo;
pnpm add nativewind@^4.0.1 tailwindcss react-native-css-interop;
npx tailwindcss init;
# tailwind の変更を適用しやすくするために、main App.tsx を app ディレクトリに移動
mkdir app;
mv App.tsx app/;
新しく生成された tailwind.config.js
ファイルに適切な修正を加えます。
ファイル: ./tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
// 注意: これを全てのコンポーネントファイルのパスを含むように更新してください
content: [
"./app/**/*.{js,jsx,ts,tsx}",
"./components/**/*.{js,jsx,ts,tsx}"
],
presets: [require("nativewind/preset")],
theme: {
extend: {},
},
plugins: [],
}
新しい global.css
を作成し、Tailwind スタイルを追加します。
# FROM: ./berachain-walletconnect-expo;
touch global.css;
ファイル: ./global.css
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
.App {
@apply bg-[#F47226] text-[#2E1E1A] flex w-full h-full items-center justify-center;
}
.H1 {
@apply text-lg mb-2 block font-semibold text-[#121312] text-center;
}
.Text {
@apply mb-2 text-[#2E1E1A] block;
}
.TextInput {
@apply bg-white text-base h-12 px-2 align-text-top rounded w-full mb-4;
}
.TextError {
@apply text-red-800 bg-red-200 mb-2 text-base p-4;
}
.Button {
@apply bg-[#2E1E1A] h-12 flex items-center justify-center rounded-lg mb-4 w-full;
}
.Code {
@apply block bg-[#ff843d] whitespace-nowrap overflow-scroll mb-4 text-[#874c2a] text-base h-12 leading-[3rem] px-2 rounded w-full;
}
.Connect {
@apply my-4 block;
}
.Balance {
@apply block w-full px-8;
}
.SignMessage {
@apply block w-full px-8;
}
.Deploy {
@apply block w-full px-8;
}
}
nativewind
をサポートするために babel.config.js
を修正します。
ファイル: ./babel.config.js
module.exports = (api) => {
api.cache(true);
return {
presets: [
["babel-preset-expo", { jsxImportSource: "nativewind" }],
"nativewind/babel",
],
};
};
ビルドを助けるための新しい metro.config.js
ファイルを生成します。
# FROM: ./berachain-walletconnect-expo;
npx expo customize metro.config.js;
そのconfig ファイルに修正を加えます。
ファイル: ./metro.config.js
// Learn more https://docs.expo.io/guides/customizing-metro
const { getDefaultConfig } = require('expo/metro-config');
+ const { withNativeWind } = require('nativewind/metro');
/** @type {import('expo/metro-config').MetroConfig} */
- const config = getDefaultConfig(__dirname);
+ const config = getDefaultConfig(__dirname, { isCSSEnabled: true })
- module.exports = config;
+ module.exports = withNativeWind(config, { input: './global.css' });
メイン App.tsx
を修正し、すべてを以下のコードに置き換えます。
ファイル: ./app/App.tsx
// インポート
// ========================================================
import { StatusBar } from 'expo-status-bar';
import { Text, View } from 'react-native';
import '../global.css';
// メインのAppコンポーネント
// ========================================================
export default function App() {
return (
<View className="App">
<StatusBar style="auto" />
<Text className="H1">Berachain WalletConnect Expo Example</Text>
<Text className="Text">Demonstrating how to build mobile dApps</Text>
</View>
);
};
アプリを再度実行すると、以下のような表示が確認できるはずです。
Tailwind が動作している Berachain モバイル iOS dApp
また、別のコンポーネントとして SVG ロゴをアプリに追加しましょう。
# FROM: ./berachain-walletconnect-expo;
mkdir components;
mkdir components/Icons;
touch components/Icons/index.tsx;
新しいアイコンファイルに以下のコードを追加します。
ファイル: ./components/Icons/index.tsx
// インポート
// ========================================================
import Svg, { Path } from "react-native-svg";
// アイコン
// ========================================================
export const Logo = ({ className = '', width = 128, height = 128 }) => <Svg className={className} width={width} height={height} viewBox="0 0 1024 1024" fill="none" xmlns="http://www.w3.org/2000/svg">
<Path d="M477.298 474.489C476.946 472.605 476.506 470.802 475.801 469.084C476.066 468.675 534.55 392.929 480.381 346.092C426.299 299.252 363.145 360.339 362.793 360.667C352.751 357.718 342.622 355.918 332.581 355.099C332.581 355.099 332.581 355.099 332.493 355.099C311.882 351.906 282.99 355.099 282.99 355.099C273.037 355.918 262.996 357.718 253.042 360.585C252.69 360.258 189.536 299.17 135.454 346.01C81.3722 392.847 139.77 468.675 140.034 469.002C139.418 470.802 138.889 472.605 138.537 474.407C132.724 506.833 92.8228 516.823 92.8228 573.325C92.8228 630.891 134.485 676.256 219.572 676.256H254.452C254.628 676.42 268.106 694.27 295.938 695.335C295.938 695.335 302.369 695.99 317.165 695.499C346.673 695.499 361.031 676.583 361.12 676.338H395.999C481.085 676.338 522.748 630.972 522.748 573.407C523.012 516.987 483.111 506.915 477.298 474.489Z" fill="#F9F4D5" />
<Path d="M692.83 628.84V584.622C720.048 575.287 739.867 546.3 739.867 511.99C739.867 477.679 720.048 448.61 692.83 439.355V395.137C718.463 386.376 737.577 360.092 739.69 328.319H708.51V341.83C708.51 352.229 702.167 361.4 692.83 365.986V359.682C692.83 350.429 684.726 342.894 674.773 342.894H673.188C663.234 342.894 655.133 350.429 655.133 359.682V365.986C645.796 361.4 639.453 352.311 639.453 341.83V328.319H608.272C610.386 360.092 629.5 386.376 655.133 395.137V439.355C628.003 448.61 608.096 477.598 608.096 511.99C608.096 546.382 627.915 575.369 655.133 584.622V628.84C629.5 637.602 610.386 663.888 608.272 695.658H639.453V682.148C639.453 671.748 645.796 662.577 655.133 657.992V664.297C655.133 673.55 663.234 681.083 673.188 681.083H674.773C684.726 681.083 692.83 673.55 692.83 664.297V657.992C702.167 662.577 708.51 671.666 708.51 682.148V695.658H739.69C737.577 663.888 718.463 637.602 692.83 628.84ZM639.453 531.315V492.583C639.453 482.183 645.796 473.012 655.133 468.427V474.73C655.133 483.983 663.234 491.518 673.188 491.518H674.773C684.726 491.518 692.83 483.983 692.83 474.73V468.427C702.167 473.012 708.51 482.101 708.51 492.583V531.315C708.51 541.714 702.167 550.885 692.83 555.471V549.165C692.83 539.912 684.726 532.38 674.773 532.38H673.188C663.234 532.38 655.133 539.912 655.133 549.165V555.471C645.884 550.885 639.453 541.796 639.453 531.315Z" fill="#F9F4D5" />
<Path d="M884.142 535.49V488.407C911.358 479.07 931.176 450.083 931.176 415.773C931.176 381.462 911.358 352.393 884.142 343.14V328.319H846.53V343.14C819.315 352.475 799.496 381.462 799.496 415.773C799.496 450.083 819.315 479.152 846.53 488.407V535.49C819.315 544.825 799.496 573.813 799.496 608.123C799.496 642.433 819.315 671.502 846.53 680.755V695.576H884.142V680.755C911.358 671.42 931.176 642.433 931.176 608.123C931.176 573.813 911.358 544.825 884.142 535.49ZM830.765 435.097V396.366C830.765 385.966 837.106 376.795 846.442 372.21V378.515C846.442 387.768 854.546 395.301 864.5 395.301H865.997C875.95 395.301 884.054 387.768 884.054 378.515V372.21C893.391 376.795 899.731 385.884 899.731 396.366V435.097C899.731 445.497 893.391 454.668 884.054 459.254V452.95C884.054 443.697 875.95 436.162 865.997 436.162H864.412C854.458 436.162 846.354 443.697 846.354 452.95V459.254C837.194 454.668 830.765 445.579 830.765 435.097ZM899.819 627.53C899.819 637.929 893.479 647.1 884.142 651.686V642.433C884.142 633.18 876.038 625.648 866.085 625.648H864.588C854.634 625.648 846.53 633.18 846.53 642.433V651.686C837.194 647.1 830.853 638.011 830.853 627.53V588.798C830.853 578.398 837.194 569.227 846.53 564.642V567.998C846.53 577.253 854.634 584.786 864.588 584.786H866.173C876.126 584.786 884.23 577.253 884.23 567.998V564.642C893.567 569.227 899.907 578.316 899.907 588.798V627.53H899.819Z" fill="#F9F4D5" />
</Svg>
次に、メインの App.tsx
を修正してロゴを含めます。
ファイル: ./app/App.tsx
// インポート
// ========================================================
import { StatusBar } from 'expo-status-bar';
import { Text, View } from 'react-native';
import '../global.css';
+ import { Logo } from '../components/Icons';
// メインAppコンポーネント
// ========================================================
export default function App() {
return (
<View className="App">
<StatusBar style="auto" />
+ <Logo className="w-auto h-10 mx-auto" />
<Text className="H1">Berachain WalletConnect Expo Example</Text>
<Text className="Text">Demonstrating how to build mobile dApps</Text>
</View>
);
};
Berachain ロゴを含む Berachain モバイル Web3 Expo アプリ
WalletConnect Web3Modal
次のステップでは、Expo アプリに WalletConnect の Web3Modal サポートを追加します。
最初のステップは環境変数の設定です。
注意: これを git リポジトリにコミットしないように注意してください
# FROM: ./berachain-walletconnect-expo;
touch .env;
以下の内容を追加し、WALLET_CONNECT_PROJECT_ID
を WalletConnect Cloud で新しく作成したプロジェクトのものに置き換えることを忘れないでください。
ファイル: ./.env
# Expo メタデータ
EXPO_PUBLIC_METADATA_NAME="Berachain WalletConnect Expo"
EXPO_PUBLIC_METADATA_DESCRIPTION="Berachain WalletConnect Expo Example"
EXPO_PUBLIC_METADATA_URL="https://berachain.com"
EXPO_PUBLIC_METADATA_ICONS="https://avatars.githubusercontent.com/u/96059542"
EXPO_PUBLIC_METADATA_REDIRECT_NAME="YOUR_APP_SCHEME://"
EXPO_PUBLIC_METADATA_REDIRECT_UNIVERSAL="YOUR_APP_UNIVERSAL_LINK.com"
# WalletConnect - See https://cloud.walletconnect.com
EXPO_PUBLIC_WALLET_CONNECT_PROJECT_ID="YOUR_PROJECT_ID"
# チェーン
EXPO_PUBLIC_CHAIN_ID=80085
EXPO_PUBLIC_CHAIN_NAME="berachainTestnet"
EXPO_PUBLIC_CHAIN_NETWORK="Berachain"
EXPO_PUBLIC_CHAIN_NATIVECURRENCY_DECIMALS=18
EXPO_PUBLIC_CHAIN_NATIVECURRENCY_NAME="Bera Token"
EXPO_PUBLIC_CHAIN_NATIVECURRENCY_SYMBOL="BERA"
EXPO_PUBLIC_CHAIN_RPC_URL="https://rpc.ankr.com/berachain_testnet"
EXPO_PUBLIC_CHAIN_BLOCKEXPLORER_NAME="Beratrail"
EXPO_PUBLIC_CHAIN_BLOCKEXPLORER_URL="https://artio.beratrail.io"
これが設定できたら、WalletConnect から wagmi
ライブラリに独自の設定を追加するプロバイダーでメインの App.tsx
をラップします。
# FROM: ./berachain-walletconnect-expo;
mkdir providers;
touch providers/index.tsx;
touch providers/wagmi.tsx;
最初に wagmi プロバイダーを設定します。これにより、wagmi のすべてのフックと WalletConnect Web3Modal に組み込まれたデフォルト機能を利用できます。
ファイル: ./providers/wagmi.tsx
// インポート
// ========================================================
import "@walletconnect/react-native-compat";
import {
createWeb3Modal,
defaultWagmiConfig,
} from "@web3modal/wagmi-react-native";
import { defineChain } from "viem";
import { WagmiConfig } from "wagmi";
// 設定
// ========================================================
// 1. https://cloud.walletconnect.com でプロジェクトIDを取得
const projectId = `${process.env.EXPO_PUBLIC_WALLET_CONNECT_PROJECT_ID}`;
if (!projectId) throw Error('Error: Missing `EXPO_PUBLIC_WALLET_CONNECT_PROJECT_ID`.');
// 2. アプリの設定を作成 - 環境変数で定義
const metadata = {
name: `${process.env.EXPO_PUBLIC_METADATA_NAME}`,
description: `${process.env.EXPO_PUBLIC_METADATA_DESCRIPTION}`,
url: `${process.env.EXPO_PUBLIC_METADATA_URL}`,
icons: [`${process.env.EXPO_PUBLIC_METADATA_ICONS}`],
redirect: {
native: `${process.env.EXPO_PUBLIC_METADATA_REDIRECT_NATIVE}`,
universal: `${process.env.EXPO_PUBLIC_METADATA_REDIRECT_UNIVERSAL}`,
},
};
// 3. カスタムチェーンの設定 - wagmi と viem v1 で必要
/**
* @dev カスタムチェーン設定
*/
const chainConfiguration = defineChain({
id: parseInt(`${process.env.EXPO_PUBLIC_CHAIN_ID}`),
name:`${process.env.EXPO_PUBLIC_CHAIN_NAME}`,
network: `${process.env.EXPO_PUBLIC_CHAIN_NETWORK}`,
nativeCurrency: {
decimals: parseInt(`${process.env.EXPO_PUBLIC_CHAIN_NATIVECURRENCY_DECIMALS}`),
name: `${process.env.EXPO_PUBLIC_CHAIN_NATIVECURRENCY_NAME}`,
symbol:`${process.env.EXPO_PUBLIC_CHAIN_NATIVECURRENCY_SYMBOL}`,
},
rpcUrls: {
default: {
http: [`${process.env.EXPO_PUBLIC_CHAIN_RPC_URL}`],
},
public: {
http: [`${process.env.EXPO_PUBLIC_CHAIN_RPC_URL}`],
},
},
blockExplorers: {
default: {
name: `${process.env.EXPO_PUBLIC_CHAIN_BLOCKEXPLORER_NAME}`,
url: `${process.env.EXPO_PUBLIC_CHAIN_BLOCKEXPLORER_URL}`
},
},
});
/**
* @dev サポートされているチェーン
*/
const chains = [chainConfiguration];
/**
*
*/
const wagmiConfig = defaultWagmiConfig({ chains, projectId, metadata });
// 4. モーダル設定の作成
createWeb3Modal({
projectId,
chains,
wagmiConfig,
});
// プロバイダー
// ========================================================
export default function Wagmi({ children }: { children: React.ReactNode }) {
return <WagmiConfig config={wagmiConfig}>{children}</WagmiConfig>
};
次に、コードを整理するために、アプリケーション全体をラップするメインのプロバイダーファイルを修正します。これにより App.tsx
ファイルをクリーンに保ち、メインのプロバイダーラッパーで簡単に新しいプロバイダーを追加できるようになります。
ファイル: ./providers/index.tsx
// インポート
// ========================================================
import WagmiProvider from "./wagmi";
// ルートプロバイダー
// ========================================================
export default function RootProvider({ children }: { children: React.ReactNode }) {
return <WagmiProvider>{children}</WagmiProvider>
};
次に、このルートプロバイダーで App.tsx
をラップし、web3modal ボタンも追加します。
ファイル: ./app/App.tsx
// インポート
// ========================================================
import { StatusBar } from "expo-status-bar";
import { Text, View } from "react-native";
import "../global.css";
import { Logo } from "../components/Icons";
+ import RootProvider from "../providers";
+ import { Web3Modal } from "@web3modal/wagmi-react-native";
// メインAppコンポーネント
// ========================================================
export default function App() {
return (
+ <RootProvider>
<View className="App">
<StatusBar style="auto" />
+ <Web3Modal />
<Logo className="w-auto h-10 mx-auto" />
<Text className="H1">Berachain WalletConnect Expo Example</Text>
<Text className="Text">Demonstrating how to build mobile dApps</Text>
</View>
+ </RootProvider>
);
}
Connect ボタン
アプリが WalletConnect を完全にサポートしたので、接続ボタンのサポートを追加できます。これを個別に修正しやすくするために、別のファイルに配置します。
# FROM: ./berachain-walletconnect-expo;
mkdir components/Connect;
touch components/Connect/index.tsx;
ファイル: ./components/Connect/index.tsx
// インポート
// ========================================================
import { W3mButton } from "@web3modal/wagmi-react-native";
import { View } from "react-native";
// コンポーネント
// ========================================================
export default function Connect() {
return (
<View className="Connect">
{/* web3modal ボタンのカスタマイズには特定のプロップを渡す必要があります */}
<W3mButton
connectStyle={{
backgroundColor: "#2E1E1A",
}}
accountStyle={{
backgroundColor: "#2E1E1A",
}}
/>
</View>
);
}
このボタンを App.tsx
ファイルに追加しましょう。
// インポート
// ========================================================
import { StatusBar } from "expo-status-bar";
import { Text, View } from "react-native";
import "../global.css";
import { Logo } from "../components/Icons";
+ import Connect from "../components/Connect";
import RootProvider from "../providers";
import { Web3Modal } from "@web3modal/wagmi-react-native";
// メインAppコンポーネント
// ========================================================
export default function App() {
return (
<RootProvider>
<View className="App">
<StatusBar style="auto" />
<Web3Modal />
<Logo className="w-auto h-10 mx-auto" />
<Text className="H1">Berachain WalletConnect Expo Example</Text>
<Text className="Text">Demonstrating how to build mobile dApps</Text>
+ <Connect />
</View>
</RootProvider>
);
}
これで接続ボタンのサポートができましたが、シミュレータには MetaMask がインストールされていないため、モバイル上でウォレットに接続する方法がないことにすぐに気付くでしょう。
Berachain WalletConnect Web3Modal が動作している状態
Connect ボタンをクリックすると、利用可能なウォレットが0個であることがわかります。
シミュレータ上で Berachain WalletConnect Web3Modal がウォレットに接続できない状態
ここで Expo Go をスマートフォンで活用して、既存のウォレットに接続できます。
注意: スマートフォンに MetaMask Mobile がインストールされており、MetaMask モバイルに Berachain の新しいネットワークを設定 されていることを確認してください。
pnpm ios
を実行すると QR コードが表示されます。この QR コードを Expo Go アプリ で使用して、アプリを直接スマートフォンにロードできるはずです。
QR コードを表示している Warp ターミナル画面
iPhone のカメラを使用すると、Expo Go でアプリに接続して開くことができます。
iPhone で Expo Go に接続している状態
アプリがスマートフォンにロードされたら、Connect をタップし、オプションとして MetaMask が表示され、それを選択し、MetaMask アプリで接続を確認し、アプリに戻って接続が確認できるはずです。
MetaMask に正常に接続している Berachain Expo WalletConnect
$BERA 残高の表示
MetaMask でアカウントの接続に成功したので、Berachain の現在の残高を表示する基本的なコンポーネントを追加しましょう。
注意: 事前に MetaMask ウォレットを Berachain Artio テストネットネットワークに切り替えておいてください。
このコンポーネントは、アカウントが既に接続されているかを確認し、現在の残高を表示するリクエストを行います。
# FROM: ./berachain-walletconnect-expo;
mkdir components/Balance;
touch components/Balance/index.tsx;
新しい Balance
コンポーネントに以下のコードを追加しましょう。
ファイル: ./components/Balance/index.tsx
// インポート
// ========================================================
import { View, Text } from "react-native";
import { useAccount, useBalance } from "wagmi";
// コンポーネント
// ========================================================
export default function Balance() {
// フック
const { isConnected, address } = useAccount();
const { data, isError, isLoading } = useBalance({
address
});
// 返値
/**
* まだ読み込み中の場合、ローディング状態を表示
*/
if (isLoading)
return <Text className="Text">Fetching balance...</Text>;
/**
* 残高の取得に問題がある場合、エラーを表示
*/
if (isError)
return (
<Text className="mText">Error fetching balance</Text>
);
/**
* 接続されていない場合は何も表示しない
*/
if (!isConnected) return null;
/**
* 正常に接続された場合
*/
return (
<View className="Balance">
<Text className="Text">Balance</Text>
<Text className="Code">{(parseInt((data?.value ?? '').toString()) / 1000000000000000000).toFixed(2)} $BERA</Text>
</View>
);
}
次に、Balance コンポーネントを App.tsx
に追加します。
ファイル: ./app/App.tsx
// インポート
// ========================================================
import { StatusBar } from "expo-status-bar";
import { Text, View } from "react-native";
import "../global.css";
import { Logo } from "../components/Icons";
import Connect from "../components/Connect";
+ import Balance from "../components/Balance";
import RootProvider from "../providers";
import { Web3Modal } from "@web3modal/wagmi-react-native";
// メインAppコンポーネント
// ========================================================
export default function App() {
return (
<RootProvider>
<View className="App">
<StatusBar style="auto" />
<Web3Modal />
<Logo className="w-auto h-10 mx-auto" />
<Text className="H1">Berachain WalletConnect Expo Example</Text>
<Text className="Text">Demonstrating how to build mobile dApps</Text>
<Connect />
+ <Balance />
</View>
</RootProvider>
);
}
MetaMask と接続すると、$BERA
残高が表示されるはずです。
$BERA
残高機能を表示している Berachain WalletConnect Expo
ウォレットメッセージ署名
基本的な RPC リクエストの上に、基本的なウォレットのインタラクションも実装したいと思います。最も単純なバージョンは、MetaMask ウォレットでメッセージに署名し、署名を取得することです。
メッセージを受け付け、ユーザーの MetaMask ウォレットにメッセージの署名を要求し、署名を出力する入力を構築します。
まず、ファイルを作成します。
# FROM: ./berachain-walletconnect-expo;
mkdir components/SignMessage;
touch components/SignMessage/index.tsx;
機能を追加します。
ファイル: ./components/SignMessage/index.tsx
// インポート
// ========================================================
import { useState } from "react";
import { View, Pressable, Text, TextInput } from "react-native";
import { useAccount, useSignMessage } from "wagmi";
// コンポーネント
// ========================================================
export default function SignMessage() {
// フック
const [message, setMessage] = useState("");
const [signature, setSignature] = useState("(Signature will show up here)");
const [error, setError] = useState("");
const { isConnected, address } = useAccount();
const { signMessageAsync } = useSignMessage();
// 関数
/**
* @dev メッセージ署名を処理する
*/
const onPressSignMessage = async () => {
console.group("onPressSignMessage");
setError("");
try {
const signature = await signMessageAsync({
message,
});
setSignature(signature);
} catch (error: unknown) {
console.error({ error });
setError("Error signing message.");
}
console.groupEnd();
};
// 返値
/**
* 接続されておらず、アドレスがない場合は何も表示しない
*/
if (!isConnected || !address) return null;
return (
<View className="SignMessage">
<Text className="Text">Sign Message</Text>
<TextInput
className="TextInput"
placeholder="Message to sign"
onChangeText={setMessage}
value={message}
/>
<Text className="Text">Signature Generated</Text>
<Text
className="Code"
>{signature}</Text>
<Pressable
className="Button"
onPress={onPressSignMessage}
>
<Text className="text-white text-base">Sign Message</Text>
</Pressable>
{error ? (
<Text className="TextError">
{error}
</Text>
) : null}
</View>
);
}
Sign Message 機能を App.tsx
に追加します。
ファイル: ./app/App.tsx
// インポート
// ========================================================
import { StatusBar } from "expo-status-bar";
import { Text, View } from "react-native";
import "../global.css";
import { Logo } from "../components/Icons";
import Connect from "../components/Connect";
import Balance from "../components/Balance";
+ import SignMessage from "../components/SignMessage";
import RootProvider from "../providers";
import { Web3Modal } from "@web3modal/wagmi-react-native";
// メインAppコンポーネント
// ========================================================
export default function App() {
return (
<RootProvider>
<View className="App">
<StatusBar style="auto" />
<Web3Modal />
<Logo className="w-auto h-10 mx-auto" />
<Text className="H1">Berachain WalletConnect Expo Example</Text>
<Text className="Text">Demonstrating how to build mobile dApps</Text>
<Connect />
<Balance />
+ <SignMessage />
</View>
</RootProvider>
);
}
これでメッセージを入力し、ウォレットにメッセージの署名を要求し、生成された署名を確認する機能が表示されるはずです。
"Ooga Booga" で動作している Berachain WalletConnect ウォレット署名
コントラクトのデプロイ
このアプリに追加したい最後の機能は、モバイルアプリから直接コントラクトのバイトコードをデプロイする機能です。これは基本的にウォレットに Berachain にコントラクトをデプロイするトランザクションを開始するよう促します。
まず、新しい Deploy コンポーネントを作成しましょう。
# FROM: ./berachain-walletconnect-expo;
mkdir components/Deploy;
touch components/Deploy/index.tsx;
Deploy コンポーネントに以下の機能を追加します。
ファイル: ./components/Deploy/index.tsx
// インポート
// ========================================================
import { useState } from "react";
import { Pressable, Text } from "react-native";
import { useAccount, useWaitForTransaction } from "wagmi";
import { encodeAbiParameters } from "viem";
import { openURL } from "expo-linking";
// 定数
// ========================================================
/**
* @dev HelloWorld コントラクトのバイトコード
* 参照: https://github.com/berachain/guides/blob/main/apps/hardhat-viem-helloworld/contracts/HelloWorld.sol
*/
const CONTRACT_BYTECODE =
"0x60806040523480156200001157600080fd5b5060405162000da238038062000da283398181016040528101906200003791906200021e565b8060009081620000489190620004ba565b507fcbc299eeb7a1a982d3674880645107c4fe48c3227163794e48540a752272235433826040516200007c92919062000638565b60405180910390a1506200066c565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b620000f482620000a9565b810181811067ffffffffffffffff82111715620001165762000115620000ba565b5b80604052505050565b60006200012b6200008b565b9050620001398282620000e9565b919050565b600067ffffffffffffffff8211156200015c576200015b620000ba565b5b6200016782620000a9565b9050602081019050919050565b60005b838110156200019457808201518184015260208101905062000177565b60008484015250505050565b6000620001b7620001b1846200013e565b6200011f565b905082815260208101848484011115620001d657620001d5620000a4565b5b620001e384828562000174565b509392505050565b600082601f8301126200020357620002026200009f565b5b815162000215848260208601620001a0565b91505092915050565b60006020828403121562000237576200023662000095565b5b600082015167ffffffffffffffff8111156200025857620002576200009a565b5b6200026684828501620001eb565b91505092915050565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680620002c257607f821691505b602082108103620002d857620002d76200027a565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b600060088302620003427fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8262000303565b6200034e868362000303565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b60006200039b620003956200038f8462000366565b62000370565b62000366565b9050919050565b6000819050919050565b620003b7836200037a565b620003cf620003c682620003a2565b84845462000310565b825550505050565b600090565b620003e6620003d7565b620003f3818484620003ac565b505050565b5b818110156200041b576200040f600082620003dc565b600181019050620003f9565b5050565b601f8211156200046a576200043481620002de565b6200043f84620002f3565b810160208510156200044f578190505b620004676200045e85620002f3565b830182620003f8565b50505b505050565b600082821c905092915050565b60006200048f600019846008026200046f565b1980831691505092915050565b6000620004aa83836200047c565b9150826002028217905092915050565b620004c5826200026f565b67ffffffffffffffff811115620004e157620004e0620000ba565b5b620004ed8254620002a9565b620004fa8282856200041f565b600060209050601f8311600181146200053257600084156200051d578287015190505b6200052985826200049c565b86555062000599565b601f1984166200054286620002de565b60005b828110156200056c5784890151825560018201915060208501945060208101905062000545565b868310156200058c578489015162000588601f8916826200047c565b8355505b6001600288020188555050505b505050505050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000620005ce82620005a1565b9050919050565b620005e081620005c1565b82525050565b600082825260208201905092915050565b600062000604826200026f565b620006108185620005e6565b93506200062281856020860162000174565b6200062d81620000a9565b840191505092915050565b60006040820190506200064f6000830185620005d5565b8181036020830152620006638184620005f7565b90509392505050565b610726806200067c6000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063a41368621461003b578063fe50cc7214610057575b600080fd5b610055600480360381019061005091906102ad565b610075565b005b61005f6100c1565b60405161006c9190610375565b60405180910390f35b806000908161008491906105ad565b507fcbc299eeb7a1a982d3674880645107c4fe48c3227163794e48540a752272235433826040516100b69291906106c0565b60405180910390a150565b6060600080546100d0906103c6565b80601f01602080910402602001604051908101604052809291908181526020018280546100fc906103c6565b80156101495780601f1061011e57610100808354040283529160200191610149565b820191906000526020600020905b81548152906001019060200180831161012c57829003601f168201915b5050505050905090565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6101ba82610171565b810181811067ffffffffffffffff821117156101d9576101d8610182565b5b80604052505050565b60006101ec610153565b90506101f882826101b1565b919050565b600067ffffffffffffffff82111561021857610217610182565b5b61022182610171565b9050602081019050919050565b82818337600083830152505050565b600061025061024b846101fd565b6101e2565b90508281526020810184848401111561026c5761026b61016c565b5b61027784828561022e565b509392505050565b600082601f83011261029457610293610167565b5b81356102a484826020860161023d565b91505092915050565b6000602082840312156102c3576102c261015d565b5b600082013567ffffffffffffffff8111156102e1576102e0610162565b5b6102ed8482850161027f565b91505092915050565b600081519050919050565b600082825260208201905092915050565b60005b83811015610330578082015181840152602081019050610315565b60008484015250505050565b6000610347826102f6565b6103518185610301565b9350610361818560208601610312565b61036a81610171565b840191505092915050565b6000602082019050818103600083015261038f818461033c565b905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806103de57607f821691505b6020821081036103f1576103f0610397565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b6000600883026104597fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8261041c565b610463868361041c565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b60006104aa6104a56104a08461047b565b610485565b61047b565b9050919050565b6000819050919050565b6104c48361048f565b6104d86104d0826104b1565b848454610429565b825550505050565b600090565b6104ed6104e0565b6104f88184846104bb565b505050565b5b8181101561051c576105116000826104e5565b6001810190506104fe565b5050565b601f82111561056157610532816103f7565b61053b8461040c565b8101602085101561054a578190505b61055e6105568561040c565b8301826104fd565b50505b505050565b600082821c905092915050565b600061058460001984600802610566565b1980831691505092915050565b600061059d8383610573565b9150826002028217905092915050565b6105b6826102f6565b67ffffffffffffffff8111156105cf576105ce610182565b5b6105d982546103c6565b6105e4828285610520565b600060209050601f8311600181146106175760008415610605578287015190505b61060f8582610591565b865550610677565b601f198416610625866103f7565b60005b8281101561064d57848901518255600182019150602085019450602081019050610628565b8683101561066a5784890151610666601f891682610573565b8355505b6001600288020188555050505b505050505050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006106aa8261067f565b9050919050565b6106ba8161069f565b82525050565b60006040820190506106d560008301856106b1565b81810360208301526106e7818461033c565b9050939250505056fea264697066735822122051a137f3f2f370792efdafdfd52aa1721451dfaa2e804a5236730d97a26f237664736f6c63430008110033";
```tsx
// コンポーネント
// ========================================================
export default function Deploy() {
// フック
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState('');
const [transactionHash, setTransactionHash] = useState<`0x${string}` | undefined>();
const { isConnected, address, connector } = useAccount();
// 関数
/**
* @dev トランザクションハッシュを待つフック
*/
const txResult = useWaitForTransaction({
hash: transactionHash
});
/**
* @dev コントラクトのデプロイを処理する
*/
const onPressDeployContract = async () => {
console.group('onPressDeployContract');
setError('');
setIsLoading(true);
try {
const provider = await connector?.getProvider(); // wagmi から直接プロバイダーを取得 - walletconnection に必要
console.log({ provider });
console.log({ request: provider?.request });
// コンストラクタに基づく - constructor(string memory _greeting) {
// 関数名と 'Hello World!' という入力をエンコードします
const encodedData = encodeAbiParameters(
[{ name: "_greeting", type: "string" }],
["Hello World!"]
);
// encodedData の先頭から 0x を除くために slice(2) を使用
const fullByteCode = `${CONTRACT_BYTECODE}${encodedData.slice(2)}`;
// ETH トランザクションの送信
const tx = await provider.request({
method: "eth_sendTransaction",
params: [
{
from: address,
data: fullByteCode,
},
],
});
console.log({ tx });
// `useWaitForTransaction` で待機するためのトランザクションハッシュを設定
setTransactionHash(tx);
} catch (error: unknown) {
console.error(error);
setError('Error could not deploy contract.')
}
setIsLoading(false);
console.groupEnd();
};
/**
* @dev ブロックエクスプローラーで最終的なトランザクションハッシュを開くURLを処理する関数
*/
const onPressSeeTransaction = () => {
openURL(`${process.env.EXPO_PUBLIC_CHAIN_BLOCKEXPLORER_URL}/tx/${txResult?.data?.transactionHash}`);
};
/**
* 接続されておらず、アドレスがない場合は何も表示しない
*/
if (!isConnected || !address) return null;
return (
<>
<Text className="Text">Deploy Contract</Text>
<Pressable
disabled={isLoading}
className={"Button"}
onPress={onPressDeployContract}>
<Text className="text-white text-base">
Deploy
</Text>
</Pressable>
{isLoading && <Text className="Code">Loading...</Text>}
{!isLoading && txResult?.data?.transactionHash
? <Pressable
className="Button"
onPress={onPressSeeTransaction}>
<Text className="text-white text-base">
See Successful Transaction
</Text>
</Pressable>
: null}
{error ? <Text className="TextError">{error}</Text> : null}
</>
);
}
このプロセスをたどると、Deploy をタップし、ウォレットにトランザクションの確認を求められ、トランザクションの完了を待ち、ブロックエクスプローラーでトランザクションを確認できるはずです。
コントラクトを正常にデプロイした Berachain WalletConnect
🐻 完全なコードリポジトリ
最終的なコードを確認し、他のガイドを見たい場合は、Berachain WalletConnect Expo ガイドコード をご確認ください。
🛠️ もっと開発したい方へ
Berachain でさらに開発を進め、より多くの例を見たい場合は、Berachain GitHub ガイドリポジトリをご覧ください。NextJS、Hardhat、Viem、Foundry などを含む様々な実装例があります。
より詳細な情報を得たい場合は、Berachain ドキュメントをご覧ください。
開発サポートをお探しですか?
Berachain Discord サーバーに参加して、開発者チャンネルで質問してください。
https://discord.com/invite/berachain
❤️ この記事が気に入りましたら、シェアしていただけると嬉しいです。
【Sunrise とは】
Sunrise は Proof of Liquidity(PoL)と Fee Abstraction(手数料抽象化)を備えたデータ可用性レイヤーです。 私たちは DA の体験を再構築し、多様なエコシステムからのモジュラー型流動性を活用してロールアップを立ち上げています。
【Social Links】
【お問合せ】
Sunrise へのお問い合わせはこちらから 👉 Google Form