Deploy A Contract To Berachain Using Expo & WalletConnect

Deploy A Contract To Berachain Using Expo & WalletConnect

Build A Mobile Web3 dApp Using Expo React Native & WalletConnect To Deploy To Berachain

·

18 min read

Let's walk through the process of building a mobile web3 dApp with Expo, integrating WalletConnect's Web3Modal, and deploying a contract's bytecode to Berachain.

Deploy A Contract's Bytecode To Berachain With WalletConnect


Requirements

Before we proceed, make sure you have the following setup and installed on your local computer.

  1. NVM or Node v20.11.0

  2. Expo Go - Needed to get the app on your iPhone

  3. Xcode with iOS Simulator

  4. Berachain $BERA tokens - See Berachain Faucet

  5. WalletConnect Project ID - See WalletConnect Cloud

  6. MetaMask iOS App

  7. Your wallet configured with Berachain - See Connecting To Berachain

Make sure to check out our Berachain docs for more guides and more in-depth information on Berachain.


Initial Expo Project Setup

Our first step will be to setup a new Expo Blank TypeScript project and install some of our needed dependencies.

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

After we've built out the template for our Berachain Expo TypeScript app, we're going install some dependencies.

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 @babel/runtime nativewind@^4.0.1 tailwindcss react-native-css-interop expo-linking;

# NOTE: 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 additional support
# expo-application - for native app information
# @babel/runtime - needed to handle specific runtime for pnpm
# nativewind@^4.0.1 - to add native support for tailwind
# tailwindcss - for styling from tailwind
# react-native-css-interop - allows handling of css files
# expo-linking - to allow for links to open in Safari or mobile browser

Pnpm Package Manager Expo Fix

NOTE: It's recommended to do this step so that it will apply to all package managers.

With Expo, there are some additional steps we're going to take to support pnpm, which will also work with npm and yarn.

The first things we're going to do is separate is place our App.tsx in a new folder and create a new index.ts which will reference our main App component. The reason for the first is more so to do with styling (up next), and the index file is to make sure it gets around Expo's AppEntry.js which pnpm has trouble recognizing.

# FROM: ./berachain-walletconnect-expo

mkdir app;
mv App.tsx app;
touch index.ts;

We'll add the following in our index.ts to reference our App.tsx file.

File:./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);

Lastly, we're going to point our package.json to set the main attribute to this new index file.

File:./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": {
    "@babel/runtime": "^7.24.0",
    "@react-native-async-storage/async-storage": "^1.22.3",
    "@react-native-community/netinfo": "^11.3.1",
    "@walletconnect/react-native-compat": "^2.11.2",
    "@web3modal/wagmi-react-native": "^1.2.0",
    "expo": "~50.0.8",
    "expo-application": "^5.8.3",
    "expo-status-bar": "~1.11.1",
    "nativewind": "^4.0.1",
    "react": "18.2.0",
    "react-native": "0.73.4",
    "react-native-css-interop": "^0.0.34",
    "react-native-get-random-values": "^1.10.0",
    "react-native-modal": "^13.0.1",
    "react-native-svg": "^15.0.0",
    "tailwindcss": "^3.4.1",
    "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
}

Now if we run our app in iOS Simulator, we should see the following.

NOTE: If you are having issues with iOS Simulator, check out these additional iOS Simulator configuration.

# FROM: ./berachain-walletconnect-expo

pnpm ios;

iOS Simulator Successfully Running Expo With Pnpm


Berachain Styling Our Expo App

For our Expo app, we want to support Tailwind but in order to do that, we're going to take advantage of an Expo library called Nativewindv4.

To configure things correctly, we'll need to create a global stylesheet, that would apply to the entire app, and creating both a metro configuration and tailwind configuration file.

# FROM: ./berachain-walletconnect-expo

touch global.css;
npx expo customize metro.config.js;
npx tailwindcss init;

We'll now need to modify 4 files to get our styling working correctly.

First our default styling applied to the entire app.

File:./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;
  }

  .Deploy {
    @apply my-4 block w-full px-4;
  }
}

Configure out Tailwind configuration to look for specific files to work with these new stylesheets and support the Nativewind presets.

File:./tailwind.config.ts

/** @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: [],
}

Modify our babel configuration to handle bundling things correctly with Nativewind.

File:./babel.config.js

module.exports = (api) => {
  api.cache(true);
  return {
    presets: [
      ["babel-preset-expo", { jsxImportSource: "nativewind" }],
      "nativewind/babel",
    ],
  };
};

Configure our metro configuration file to allow for Nativewindv4 to work as well.

File:./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' });

And then lastly we'll modify our original App component file to support the new styling and add an SVG logo to it.

File:./app/App.tsx

// Imports
// ========================================================
import { StatusBar } from 'expo-status-bar';
import { Text, View } from 'react-native';
import Svg, { Path } from "react-native-svg";
import '../global.css';

// Main App Component
// ========================================================
export default function App() {
  return (
    <View className="App">
      <StatusBar style="auto" />
      <Svg className="w-auto h-10 mx-auto" width="128" height="128" viewBox="0 0 1024 1024" fill="none">
        <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>
      <Text className="H1">Berachain WalletConnect Expo Example</Text>
      <Text className="Text">Demonstrating how to build mobile dApps</Text>
    </View>
  );
};

Getting Type Errors?

If you're getting type errors for className add the following file to your root to fix this.

# FROM: ./berachain-walletconnect-expo

touch nativewind-env.d.ts;

File:./nativewind-env.d.ts

/// <reference types="nativewind/types" />

Now if run our application, we should get the following Berachain styled result.

Berachain Styled Expo App


Configuring WalletConnect

NOTE: Make sure to sign up for a WalletConnect Project Id at https://cloud.walletconnect.com

The next step is to configure WalletConnect to allow for our app to connect to an EVM wallet.

The first step to this will be to create an new environment variable file that will we'll populate with some default values and our WalletConnect Project ID.

# FROM: ./berachain-walletconnect-expo

touch .env;
# And for good practice add `.env` to our `.gitignore`
echo ".env" >> .gitignore;

In the new dot environment file, add the following and notice that all the environment variables start with EXPO_PUBLIC_ to make sure it's exposed to the client for WalletConnect.

File:./.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"

Next we're going to add some of the default configurations for wagmi, the web3 library behind Web3Modal, with some defaults for WalletConnect.

File:./app/App.tsx

// Imports
// ========================================================
import { StatusBar } from "expo-status-bar";
import { Text, View } from "react-native";
import Svg, { Path } from "react-native-svg";
import "../global.css";
+ import "@walletconnect/react-native-compat";
+ import {
+  Web3Modal,
+  W3mButton,
+  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,
+ });

// Main App Component
// ========================================================
export default function App() {
 return (
+   <WagmiConfig config={wagmiConfig}>
   <View className="App">
   <StatusBar style="auto" />
   <Svg className="w-auto h-10 mx-auto" width="128" height="128" viewBox="0 0 1024 1024" fill="none">
    <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>
   <Text className="H1">Berachain WalletConnect Expo Example</Text>
   <Text className="Text">Demonstrating how to build mobile dApps</Text>
+    <Web3Modal />
+    <View className="Connect">
+    {/* Customizing the web3modal button requires passing it certain props */}
+     <W3mButton
+      connectStyle={{
+       backgroundColor: "#2E1E1A",
+      }}
+      accountStyle={{
+       backgroundColor: "#2E1E1A",
+      }}
+     />
+    </View>
  </View>
 </WagmiConfig>);
}

Now for this next step, we're going to run the app, but we're going to take advantage of Expo Go to load the app directly on our phone. To do this, make sure you have Expo Go installed on your phone and when the app is running, scan the QR code.

The reason we're doing this is to allow for the app to connect to our mobile MetaMask app.

# FROM: ./berachain-walletconnect-expo

pnpm ios;

Berachain Expo App Loaded On Expo Go

This will load our app directly on our phone and will result in something similar to the experience of the following:

Berachain WalletConnect Successfully Wallet Connection

This has setup our mobile Berachain App to work with WalletConnect successfully.


Deploy Contract ByteCode

For this next step, we're going to use this HelloWorld.sol contract and compile it into bytecode.

NOTE: Make sure that you MetaMask wallet is configured with Berachain and that you have some $BERA tokens in your wallet with the Faucet

If you want to see how to compile it, you should be able to do so with Remix, Hardhat, Foundry, or look at this ethers6 solc repo for the steps.

To make sure some space in our main App.tsx file, we're going to create a new components folder and add a new Deploy component.

# FROM: ./berachain-walletconnect-expo

mkdir components;
mkdir components/Deploy;
touch components/Deploy/index.tsx;

Let's add the following functionality which will also host a constant of our HelloWorld.sol's bytecode.

File:./components/Deploy/index.tsx

// Imports
// ========================================================
import { useState } from "react";
import { View, 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 (
    <View className="Deploy">
      <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}
    </View>
  );
}

Next let's add this to our main App.tsx just underneath the Connect View.

File:./app/App.tsx

// Imports
// ========================================================
import { StatusBar } from "expo-status-bar";
import { Text, View } from "react-native";
import Svg, { Path } from "react-native-svg";
import "../global.css";
import "@walletconnect/react-native-compat";
import {
 Web3Modal,
 W3mButton,
 createWeb3Modal,
 defaultWagmiConfig,
} from "@web3modal/wagmi-react-native";
import { defineChain } from "viem";
import { WagmiConfig } from "wagmi";
+ import Deploy from "../components/Deploy";

// 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,
});

// Main App Component
// ========================================================
export default function App() {
 return (
  <WagmiConfig config={wagmiConfig}>
   <View className="App">
   <StatusBar style="auto" />
   <Svg className="w-auto h-10 mx-auto" width="128" height="128" viewBox="0 0 1024 1024" fill="none">
    <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>
   <Text className="H1">Berachain WalletConnect Expo Example</Text>
   <Text className="Text">Demonstrating how to build mobile dApps</Text>
   <Web3Modal />
   <View className="Connect">
   {/* Customizing the web3modal button requires passing it certain props */}
    <W3mButton
     connectStyle={{
      backgroundColor: "#2E1E1A",
     }}
     accountStyle={{
      backgroundColor: "#2E1E1A",
     }}
    />
   </View>
+    <Deploy />
  </View>
 </WagmiConfig>);
}

With that setup, we should now see this interaction on our phone to successfully deploy a contract's bytecode onto Berachain via our mobile Expo app.

Berachain Expo App Successfully Deploying Contract Bytecode with WalletConnect


Full Code Repository

If you'd like to see the full code and more, check out the the Berachain WalletConnect Expo Guide Repository Code.


Looking Fore More?

Want to see more examples of different dApps deployed on Berachain? Check out the Berachain Guides GitHub Repository for multiple implementations.

Make sure to check out the Berachain Docs for more in-depth concepts.

Looking To Get Some Developer Support?

Join the Berachain Discord server and look for our team in the developer channels.

❤️ Remember to give this article some love.