如何使用 WalletConnect 和 Expo 构建 Berachain 移动 dApp | 开发者必备
本文详细介绍了如何使用 Expo、WalletConnect 和 Web3Modal 构建一个 Berachain 上的移动 dApp。通过教程,读者将学习如何连接 MetaMask 钱包、签署消息、查看 $BERA 余额并直接从应用中部署智能合约。提供了完整的代码示例和开发支持资源,适合有志于在 Berachain 上开发移动应用的开发者。
🛠️ 构建 Berachain 上的移动 dApp
大多数 Web3 dApp 通常是为网页设计的,移动端体验较少受到关注。在本教程中,我们将改变这一点,使用 Expo 构建一个适用于 iOS 的应用,并与 Berachain 兼容。
我们的移动 dApp 将能够连接 MetaMask 移动钱包,签署消息,并将
HelloWorld contract 的字节码部署到 Berachain Artio(Berachain 测试网)。
为什么选择 Expo?
Expo 在 React Native 移动开发领域越来越受欢迎,开发者选择 Expo 而不是原生开发或单纯使用 React Native 的三个主要原因是:
- 支持多平台 —— iOS 和 Android
- 预构建的组件和功能
- Expo EAS(Expo 应用服务) —— 允许即时更新,避免每次更新都需要通过 AppStore 审核
WalletConnect 与移动端
WalletConnect 已经成为大多数 Web3 dApp 的核心工具,确保项目能够支持多个钱包,同时为开发者提供简便的工具,使他们能够快速集成钱包连接功能及其 SDK。
WalletConnect 还提供了专门针对移动设备的 SDK,使钱包连接和交互能够直接在移动设备上进行。在本教程中,我们将使用这个 SDK。
WalletConnect Web3Modal For React Native
📋 要求
在继续之前,请确保已安装或设置以下内容:
注意: 本教程将主要关注 iOS 平台
- NVM 或 Node v20.11.0
- Expo Go
- Xcode 和 iOS 模拟器
- iOS MetaMask Mobile Wallet
- Berachain $BERA 代币 —— (需要 Berachain 水龙头代币吗?)
💻 预备步骤:在 Sonoma 上运行 iOS 模拟器
在最新的 Mac OS Sonoma 和 Xcode 中,Apple 增加了一个额外步骤,开发者需要下载一个可能未预安装的特定运行时模拟器。
首先,运行 Xcode,进入 Window > Devices & Simulators。这将打开一个新窗口。在左上角选择 Simulators,然后在设备类型的下拉菜单中选择 iPhone 15 Pro Max,在操作系统版本中选择 Download more simulator runtimes…。
💻 预备步骤:在 Sonoma 上运行 iOS 模拟器
在最新的 Mac OS Sonoma 和 Xcode 中,Apple 增加了一个额外步骤,开发者需要下载一个可能未预安装的特定运行时模拟器。
首先,运行 Xcode,进入 Window > Devices & Simulators。这将打开一个新窗口。在左上角选择 Simulators,然后在设备类型( Device Type )的下拉菜单中选择 iPhone 15 Pro Max,在操作系统版本中选择 Download more simulator runtimes…。
Xcode Simulator Runtimes
这将打开另一个窗口,随后可以下载 iOS 17.2。
Xcode Simulator Runtimes Download Platforms
等待下载完成,并通过运行以下命令检查你的 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 Project ID
项目设置
现在我们已经有了 WalletConnect 项目 ID,让我们创建一个基础的 Expo 项目并添加所需的依赖项。
npx create-expo-app berachain-walletconnect-expo -t;
# [Expected Prompt & Output]:
# ? 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 配合时已知会有一些问题。我们建议尽可能使用 yarn 与 expo 搭配使用。
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;
# If in a monorepo / turbo repo, run the following right after in the dir
# pnpm install --ignore-workspace;
# @web3modal/wagmi-react-native - walletconnect react native web3modal
# wagmi@1.4.13 - web3 react hooks (currently only supports v1)
# viem@1.21.4 # js library for interacting with EVM (currently only support
# @react-native-async-storage/async-storage - for local device storage
# react-native-get-random-values - local random val generator
# react-native-svg - native svg support
# react-native-modal - modals support
# @react-native-community/netinfo - api for network info for device
# @walletconnect/react-native-compat - shims / polyfills for a
让我们测试一下应用是否正常工作 —— 提示:它不会 😅。
注意: 这是 pnpm 特有的问题。如果你是使用 yarn 完成的,那么这个问题不会出现。
# FROM: ./berachain-walletconnect-expo;
pnpm ios;
# [Expected Output]:
# (See Below)
Expo 无法正常工作 —— 预期情况
⚠️ PNPM Expo 修复方法
如果你使用的是 yarn,仍然可以进行这些修改,它将适用于所有包管理器。
为了解决这个特定的 pnpm 问题,我们需要对项目进行 3 项修改。
1 — 安装 Babel 运行时依赖
# 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
// Imports
// ========================================================
import { registerRootComponent } from "expo";
// Main App
// ========================================================
import App from "./app/App";
// registerRootComponent calls AppRegistry.registerComponent('main', () => App);
// It also ensures that whether you load the app in Expo Go or in a native build,
// the environment is set up appropriately
registerRootComponent(App);
让我们再次尝试使用 pnpm ios
,我们应该能使其正常工作。这同样适用于 yarn 或 npm。
项目样式设计
为了保持整个应用程序的样式一致性,我们将使用 Tailwind 与 Nativewind 进行样式设计。
接下来的几步虽然不多,但一旦设置完成,就能确保我们正确配置 Tailwind。
# FROM: ./berachain-walletconnect-expo;
pnpm add nativewind@^4.0.1 tailwindcss react-native-css-interop;
npx tailwindcss init;
# Move our main App.tsx to an app directory for better tailwind changes
mkdir app;
mv App.tsx app/;
对新生成的 tailwind.config.js
文件进行相应的修改。
文件路径:./tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
// NOTE: Update this to include the paths to all of your component files.
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;
}
}
修改 babel.config.js
以支持 Nativewind。
文件路径:./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;
对该配置文件进行一些修改。
文件路径:./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
// Imports
// ========================================================
import { StatusBar } from 'expo-status-bar';
import { Text, View } from 'react-native';
import '../global.css';
// Main App Component
// ========================================================
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>
);
};
再次运行我们的应用程序,应该会看到如下结果。
Berachain 移动 iOS dApp 使用 Tailwind 正常运行
我们还将添加一个 SVG 标志作为应用的独立组件。
# FROM: ./berachain-walletconnect-expo;
mkdir components;
mkdir components/Icons;
touch components/Icons/index.tsx;
将以下代码添加到新的图标文件中。
文件路径:./components/Icons/index.tsx
// Imports
// ========================================================
import Svg, { Path } from "react-native-svg";
// Icons
// ========================================================
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
// Imports
// ========================================================
import { StatusBar } from 'expo-status-bar';
import { Text, View } from 'react-native';
import '../global.css';
+ import { Logo } from '../components/Icons';
// Main App Component
// ========================================================
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 创建的新项目 ID。
文件路径:./.env
# Expo Metadata
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"
# Chain
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 配置的提供程序来包装主文件 App.tsx
,这些提供程序会将其配置添加到 wagmi 库中。
# FROM: ./berachain-walletconnect-expo;
mkdir providers;
touch providers/index.tsx;
touch providers/wagmi.tsx;
首先是配置我们的 wagmi 提供程序。这将确保我们可以利用所有的 wagmi hooks 以及 WalletConnect Web3Modal 内置的一些默认功能。
文件路径:./providers/wagmi.tsx
// Imports
// ========================================================
import "@walletconnect/react-native-compat";
import {
createWeb3Modal,
defaultWagmiConfig,
} from "@web3modal/wagmi-react-native";
import { defineChain } from "viem";
import { WagmiConfig } from "wagmi";
// Config
// ========================================================
// 1. Get projectId at https://cloud.walletconnect.com
const projectId = `${process.env.EXPO_PUBLIC_WALLET_CONNECT_PROJECT_ID}`;
if (!projectId) throw Error('Error: Missing `EXPO_PUBLIC_WALLET_CONNECT_PROJECT_ID`.');
// 2. Create config for our app - defined by our env vars
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. Configure our custom chain - Note this is needed for wagmi and viem v1
/**
* @dev Custom chain configuration
*/
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 supported chains
*/
const chains = [chainConfiguration];
/**
*
*/
const wagmiConfig = defaultWagmiConfig({ chains, projectId, metadata });
// 4. Create modal configuration
createWeb3Modal({
projectId,
chains,
wagmiConfig,
});
// Provider
// ========================================================
export default function Wagmi({ children }: { children: React.ReactNode }) {
return <WagmiConfig config={wagmiConfig}>{children}</WagmiConfig>
};
接下来,为了保持代码整洁,我们将修改主提供程序文件,它将包裹整个应用程序。这样可以保持 App.tsx
文件简洁,并且让我们能够更方便地通过主提供程序包装器添加更多的提供程序。
文件路径:./providers/index.tsx
// Imports
// ========================================================
import WagmiProvider from "./wagmi";
// Root Provider
// ========================================================
export default function RootProvider({ children }: { children: React.ReactNode }) {
return <WagmiProvider>{children}</WagmiProvider>
};
接着,我们需要将这个根提供程序包裹我们的 App.tsx
文件,并添加 Web3Modal 按钮。
文件路径:./app/App.tsx
// Imports // ======================================================== 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”; // Main App Component // ======================================================== 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> ); }
连接按钮
现在应用已经完全支持 WalletConnect,我们可以添加一个连接按钮的支持。我们将其放在一个单独的文件中,以便以后更容易修改。
# FROM: ./berachain-walletconnect-expo;
mkdir components/Connect;
touch components/Connect/index.tsx;
文件路径:./components/Connect/index.tsx
// Imports
// ========================================================
import { W3mButton } from "@web3modal/wagmi-react-native";
import { View } from "react-native";
// Component
// ========================================================
export default function Connect() {
return (
<View className="Connect">
{/* Customizing the web3modal button requires passing it certain props */}
<W3mButton
connectStyle={{
backgroundColor: "#2E1E1A",
}}
accountStyle={{
backgroundColor: "#2E1E1A",
}}
/>
</View>
);
}
让我们将这个按钮添加到 App.tsx
文件中。
// Imports
// ========================================================
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";
// Main App Component
// ========================================================
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 )按钮时,你会看到没有可用的钱包。
Berachain WalletConnect Web3Modal 无法在模拟器上连接钱包
这是我们可以利用手机上的 Expo Go 来连接现有钱包的部分。
注意: 请确保在手机上安装了 MetaMask 移动版,并配置 MetaMask 移动版,添加一个包含 Berachain 的新网络。
当我们运行 pnpm ios
时,会出现一个二维码。我们可以使用 Expo Go 应用扫描该二维码,将应用直接加载到手机中。
Warp Terminal 正在运行带有二维码的 Expo
现在,当我们使用 iPhone 的相机时,可以通过 Expo Go 连接并打开我们的应用。
使用 iPhone 连接到 Expo Go
当应用加载到手机上时,我们应该能够点击Connect ,会显示 MetaMask 作为选项,选择 MetaMask,通过 MetaMask 应用确认连接,然后返回应用,确认我们已成功连接。
Berachain Expo WalletConnect 成功连接到 MetaMask
显示 $BERA 余额
现在我们已经成功将账户与 MetaMask 连接起来,接下来让我们添加一个基本组件来显示我们在 Berachain 上的当前余额。
注意: 请确保事先将 MetaMask 钱包切换到 Berachain Artio 测试网。
我们的组件将检查账户是否已连接,并发出请求显示当前余额。
# FROM: ./berachain-walletconnect-expo;
mkdir components/Balance;
touch components/Balance/index.tsx;
让我们为新的 Balance 组件添加以下代码。
文件路径:./components/Balance/index.tsx
// Imports
// ========================================================
import { View, Text } from "react-native";
import { useAccount, useBalance } from "wagmi";
// Component
// ========================================================
export default function Balance() {
// Hooks
const { isConnected, address } = useAccount();
const { data, isError, isLoading } = useBalance({
address
});
// Return
/**
* If still loading, show a loading state
*/
if (isLoading)
return <Text className="Text">Fetching balance...</Text>;
/**
* Show error if having a problem fetching the balance
*/
if (isError)
return (
<Text className="mText">Error fetching balance</Text>
);
/**
* If not connected don't show anything
*/
if (!isConnected) return null;
/**
* Successfully connected
*/
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
// Imports
// ========================================================
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";
// Main App Component
// ========================================================
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 余额。
Berachain WalletConnect Expo 显示 $BERA 余额功能
钱包签名消息
除了基本的 RPC 请求之外,我们还希望实现一些基本的钱包交互。最简单的版本是使用 MetaMask 钱包签署消息并获取签名。
我们将构建一个输入框,该输入框接受消息,提示用户的 MetaMask 钱包对消息进行签名,并输出签名。
首先,我们将创建文件。
# FROM: ./berachain-walletconnect-expo;
mkdir components/SignMessage;
touch components/SignMessage/index.tsx;
为其添加功能。
文件路径:./components/SignMessage/index.tsx
// Imports
// ========================================================
import { useState } from "react";
import { View, Pressable, Text, TextInput } from "react-native";
import { useAccount, useSignMessage } from "wagmi";
// Component
// ========================================================
export default function SignMessage() {
// Hooks
const [message, setMessage] = useState("");
const [signature, setSignature] = useState("(Signature will show up here)");
const [error, setError] = useState("");
const { isConnected, address } = useAccount();
const { signMessageAsync } = useSignMessage();
// Functions
/**
* @dev Handles signing message
*/
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();
};
// Return
/**
* If not connected and no address, then don't show anything
*/
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>
);
}
我们将把签名消息的功能添加到 App.tsx
文件中。
文件路径:./app/App.tsx
// Imports
// ========================================================
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";
// Main App Component
// ========================================================
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>
);
}
我们现在应该能够看到输入消息的功能,提示钱包签署该消息,并查看生成的签名。
Berachain WalletConnect 钱包签名功能与 Ooga Booga 正常运行
部署合约
我想为这个应用程序添加的最后一项功能是能够直接从移动应用程序部署合约字节码。基本上,这会提示我们的钱包发起一笔交易,将合约部署到 Berachain 上。
首先,让我们创建一个新的 Deploy 组件。
# FROM: ./berachain-walletconnect-expo;
mkdir components/Deploy;
touch components/Deploy/index.tsx;
我们将为 Deploy 组件添加以下功能。
文件路径:./components/Deploy/index.tsx
// Imports
// ========================================================
import { useState } from "react";
import { Pressable, Text } from "react-native";
import { useAccount, useWaitForTransaction } from "wagmi";
import { encodeAbiParameters } from "viem";
import { openURL } from "expo-linking";
// Constants
// ========================================================
/**
* @dev ByteCode for HelloWorld Contract see https://github.com/berachain/guides/blob/main/apps/hardhat-viem-helloworld/contracts/HelloWorld.sol
*/
const CONTRACT_BYTECODE =
"0x60806040523480156200001157600080fd5b5060405162000da238038062000da283398181016040528101906200003791906200021e565b8060009081620000489190620004ba565b507fcbc299eeb7a1a982d3674880645107c4fe48c3227163794e48540a752272235433826040516200007c92919062000638565b60405180910390a1506200066c565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b620000f482620000a9565b810181811067ffffffffffffffff82111715620001165762000115620000ba565b5b80604052505050565b60006200012b6200008b565b9050620001398282620000e9565b919050565b600067ffffffffffffffff8211156200015c576200015b620000ba565b5b6200016782620000a9565b9050602081019050919050565b60005b838110156200019457808201518184015260208101905062000177565b60008484015250505050565b6000620001b7620001b1846200013e565b6200011f565b905082815260208101848484011115620001d657620001d5620000a4565b5b620001e384828562000174565b509392505050565b600082601f8301126200020357620002026200009f565b5b815162000215848260208601620001a0565b91505092915050565b60006020828403121562000237576200023662000095565b5b600082015167ffffffffffffffff8111156200025857620002576200009a565b5b6200026684828501620001eb565b91505092915050565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680620002c257607f821691505b602082108103620002d857620002d76200027a565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b600060088302620003427fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8262000303565b6200034e868362000303565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b60006200039b620003956200038f8462000366565b62000370565b62000366565b9050919050565b6000819050919050565b620003b7836200037a565b620003cf620003c682620003a2565b84845462000310565b825550505050565b600090565b620003e6620003d7565b620003f3818484620003ac565b505050565b5b818110156200041b576200040f600082620003dc565b600181019050620003f9565b5050565b601f8211156200046a576200043481620002de565b6200043f84620002f3565b810160208510156200044f578190505b620004676200045e85620002f3565b830182620003f8565b50505b505050565b600082821c905092915050565b60006200048f600019846008026200046f565b1980831691505092915050565b6000620004aa83836200047c565b9150826002028217905092915050565b620004c5826200026f565b67ffffffffffffffff811115620004e157620004e0620000ba565b5b620004ed8254620002a9565b620004fa8282856200041f565b600060209050601f8311600181146200053257600084156200051d578287015190505b6200052985826200049c565b86555062000599565b601f1984166200054286620002de565b60005b828110156200056c5784890151825560018201915060208501945060208101905062000545565b868310156200058c578489015162000588601f8916826200047c565b8355505b6001600288020188555050505b505050505050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000620005ce82620005a1565b9050919050565b620005e081620005c1565b82525050565b600082825260208201905092915050565b600062000604826200026f565b620006108185620005e6565b93506200062281856020860162000174565b6200062d81620000a9565b840191505092915050565b60006040820190506200064f6000830185620005d5565b8181036020830152620006638184620005f7565b90509392505050565b610726806200067c6000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063a41368621461003b578063fe50cc7214610057575b600080fd5b610055600480360381019061005091906102ad565b610075565b005b61005f6100c1565b60405161006c9190610375565b60405180910390f35b806000908161008491906105ad565b507fcbc299eeb7a1a982d3674880645107c4fe48c3227163794e48540a752272235433826040516100b69291906106c0565b60405180910390a150565b6060600080546100d0906103c6565b80601f01602080910402602001604051908101604052809291908181526020018280546100fc906103c6565b80156101495780601f1061011e57610100808354040283529160200191610149565b820191906000526020600020905b81548152906001019060200180831161012c57829003601f168201915b5050505050905090565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6101ba82610171565b810181811067ffffffffffffffff821117156101d9576101d8610182565b5b80604052505050565b60006101ec610153565b90506101f882826101b1565b919050565b600067ffffffffffffffff82111561021857610217610182565b5b61022182610171565b9050602081019050919050565b82818337600083830152505050565b600061025061024b846101fd565b6101e2565b90508281526020810184848401111561026c5761026b61016c565b5b61027784828561022e565b509392505050565b600082601f83011261029457610293610167565b5b81356102a484826020860161023d565b91505092915050565b6000602082840312156102c3576102c261015d565b5b600082013567ffffffffffffffff8111156102e1576102e0610162565b5b6102ed8482850161027f565b91505092915050565b600081519050919050565b600082825260208201905092915050565b60005b83811015610330578082015181840152602081019050610315565b60008484015250505050565b6000610347826102f6565b6103518185610301565b9350610361818560208601610312565b61036a81610171565b840191505092915050565b6000602082019050818103600083015261038f818461033c565b905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806103de57607f821691505b6020821081036103f1576103f0610397565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b6000600883026104597fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8261041c565b610463868361041c565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b60006104aa6104a56104a08461047b565b610485565b61047b565b9050919050565b6000819050919050565b6104c48361048f565b6104d86104d0826104b1565b848454610429565b825550505050565b600090565b6104ed6104e0565b6104f88184846104bb565b505050565b5b8181101561051c576105116000826104e5565b6001810190506104fe565b5050565b601f82111561056157610532816103f7565b61053b8461040c565b8101602085101561054a578190505b61055e6105568561040c565b8301826104fd565b50505b505050565b600082821c905092915050565b600061058460001984600802610566565b1980831691505092915050565b600061059d8383610573565b9150826002028217905092915050565b6105b6826102f6565b67ffffffffffffffff8111156105cf576105ce610182565b5b6105d982546103c6565b6105e4828285610520565b600060209050601f8311600181146106175760008415610605578287015190505b61060f8582610591565b865550610677565b601f198416610625866103f7565b60005b8281101561064d57848901518255600182019150602085019450602081019050610628565b8683101561066a5784890151610666601f891682610573565b8355505b6001600288020188555050505b505050505050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006106aa8261067f565b9050919050565b6106ba8161069f565b82525050565b60006040820190506106d560008301856106b1565b81810360208301526106e7818461033c565b9050939250505056fea264697066735822122051a137f3f2f370792efdafdfd52aa1721451dfaa2e804a5236730d97a26f237664736f6c63430008110033";
// Component
// ========================================================
export default function Deploy() {
// Hooks
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState('');
const [transactionHash, setTransactionHash] = useState<`0x${string}` | undefined>();
const { isConnected, address, connector } = useAccount();
// Functions
/**
* @dev hook that waits for a transaction hash
*/
const txResult = useWaitForTransaction({
hash: transactionHash
});
/**
* @dev handles deploying the contract
*/
const onPressDeployContract = async () => {
console.group('onPressDeployContract');
setError('');
setIsLoading(true);
try {
const provider = await connector?.getProvider(); // get's the provider from wagmi directly - needed for the walletconnection
console.log({ provider });
console.log({ request: provider?.request });
// Based on constructor - constructor(string memory _greeting) {
// encodes the function name and the input of `Hello World!`
const encodedData = encodeAbiParameters(
[{ name: "_greeting", type: "string" }],
["Hello World!"]
);
// Need slide(2) to remove 0x from encodedData at the beginning
const fullByteCode = `${CONTRACT_BYTECODE}${encodedData.slice(2)}`;
// Send eth transsaction
const tx = await provider.request({
method: "eth_sendTransaction",
params: [
{
from: address,
data: fullByteCode,
},
],
});
console.log({ tx });
// Set the state transaction hash for `useWaitForTransaction` to wait for it
setTransactionHash(tx);
} catch (error: unknown) {
console.error(error);
setError('Error could not deploy contract.')
}
setIsLoading(false);
console.groupEnd();
};
/**
* @dev function that handles opening url to the final transaction hash in a block explorer
*/
const onPressSeeTransaction = () => {
openURL(`${process.env.EXPO_PUBLIC_CHAIN_BLOCKEXPLORER_URL}/tx/${txResult?.data?.transactionHash}`);
};
/**
* If not connected and no address, then don't show anything
*/
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 服务器,并查看开发者频道以提出问题。
❤️ 别忘了通过分享这篇文章来表达你的喜爱。
原创文章,作者:Rama Ai,如若转载,请注明出处:https://www.dappchaser.com/build-a-berachain-mobile-dapp-with-walletconnect-expo/