Upgrading your dApp to Wallet Connect V2

Upgrading your dApp to Wallet Connect V2 https://celo.academy/uploads/default/optimized/2X/c/cf5f5ef8caad60db9b193bcd8202d8edded414dd_2_1024x576.jpeg
none 0.0 0

Migrating to Wallet Connect v2

Wallet Connect v2 introduces a number of breaking changes and improvements to version 1.
On June 28th version 1 of Wallet Connect will no longer be supported and the v1.0 protocol will be shut down. This means that any dApp or wallet hoping to continue offering its
users WalletConnect features will need to migrate to version 2. This guide is to help developers on Celo migrate their dApps to version 2.

In this guide we will cover migrating dApps that use either react-celo or RainbowKit to their newer WalletConnect 2 supporting versions.

The first thing anyone adding WalletConnect 2 support will need is a WalletConnect project ID. A WalletConnect project ID can be quickly made for free by anyone with a WalletConnect account.
If you don’t have an account already it’s easy and free to sign up.

  • First either sign in or creat an account here.
  • Once signed in and on the dashboard, click the New Project button at the top right.
  • Enter in your dApp name and click Create.
  • You will then be taken to a page displaying the Project ID to use for your dApp.
  • Keep this ID handy for the next steps.

Migrating with react-celo

react-celo version 5 upgraded the internal WalletConnect dependencies to v2. Luckily you will only need to make a single change when upgrading to version 5 from version 4.

When using the CeloProvider imported from react-celo at the root of your application you will need to use the new project ID you’ve created as part of the dapp prop.
See the example below.

<CeloProvider
    dapp={{
        name: 'WCv2 Demo App',
        description: 'An example for version 5 of react-celo',
        url: 'https://myappexample.com',
        icon: 'https://myappexample.ico',
        // The above fields are the same as before, and the next field is our new required project ID
        walletConnectProjectId: 'the_id_for_your_WC_project',
    }}
>
    <YourApp />
</CeloProvider>

That’s it for react-celo! Everything else should work the same.

Migrating with RainbowKit and @celo/rainbowkit-celo

RainbowKit has WalletConnect v2 support in all its newest versions as well. We currently recommend using version 1 and above. The @celo/rainbowkit-celo package starting at version 1 has
a peer dependency for RainbowKit v1 as well (@rainbow-me/rainbowkit). This means you should upgrade both these packages (@celo/rainbowkit-celo, @rainbow-me/rainbowkit) to their latest
versions to add WalletConnect v2 support.

viem

We need to call out that RainbowKit v1 and up also has a peer dependency on wagmi v1 and up. The biggest change here is that the newer versions of wagmi use a library called viem
instead of ethers. viem is a great compact alternative to ethers that supports Celo. However, if you still want to continue using ethers in your dApp, or you want to migrate to
viem incrementally, it’s not a problem and can be done with a small change, that we’ll cover in this guide, in how you use the React hooks provided by wagmi.

Again you will need your WalletConnect project ID for this migration.

Code Changes

Again, the main updates you need to worry about are centered around the WC project ID.
First, we need to update the our the Celo connectors coming from rainbowkit-celo to use this.

// The importing from rainbowkit-celo hasn't changed
import celoGroups from "@celo/rainbowkit-celo/lists";
import { Alfajores, Celo, Cannoli } from "@celo/rainbowkit-celo/chains";

// Here is our project ID again
const walletConnectProjectId = "your_WC_project_id";

const connectors = celoGroups({
    chains,
    appName: (typeof document === "object" && document.title) || "Sample App",
    // This is the only real change we need to make
    projectId: walletConnectProjectId,
});

Then we give wagmi our updated connectors with a few name changes in the wagmi API.
The following example shows the wagmi changes as well as how it works with RainbowKitProvider.

import celoGroups from "@celo/rainbowkit-celo/lists";
import { Alfajores, Celo, Cannoli } from "@celo/rainbowkit-celo/chains";

// No changes to RainbowKitProvider
import { RainbowKitProvider } from "@rainbow-me/rainbowkit";

// Here createConfig was previously createClient
import { configureChains, createConfig, WagmiConfig } from "wagmi";

const walletConnectProjectId = "your_WC_project_id";

const connectors = celoGroups({
    chains,
    appName: (typeof document === "object" && document.title) || "Sample App",
    projectId: walletConnectProjectId,
});

// Here publicClient was previously called provider
const { chains, publicClient } = configureChains(
    [Alfajores, Celo, Cannoli],
    [
        jsonRpcProvider({
            rpc: (chain) => ({ http: chain.rpcUrls.default.http[0] }),
        }),
    ]
);

const wagmiConfig = createConfig({
    autoConnect: true,
    connectors,
    // Using the new publicClient field and value instead of provider
    publicClient: publicClient,
});

function MyApp() {
    return (
        <WagmiConfig config={wagmiConfig}>
            <RainbowKitProvider chains={chains} coolMode={true}>
                <YourMainAppComponent />
            </RainbowKitProvider>
        </WagmiConfig>
    );
}

And those are all the changes you’ll need if you’re ready to start using viem. If your dApp uses ethers then continue on to the next section.

Keeping ethers

With the new version of wagmi when you import and use useSigner and useProvider you’ll be getting the viem signer and provider instead of ethers.
However, we can remake these React hooks to return ethers signers and providers instead.

First, create a file where we can put our new hooks. This file will likely be used all across your dApp so put it somewhere you can easily access it from anywhere.

Here’s what the file will contain, this comes from a guide posted by the wagmi team.
(Note: This is for ethers v5. The wagmi guide also includes an ethers v6 guide if needed.)

import * as React from 'react'
import { type PublicClient, usePublicClient, type WalletClient, useWalletClient } from 'wagmi'
import { providers } from 'ethers'
import { type HttpTransport } from 'viem'

function publicClientToProvider(publicClient: PublicClient) {
    const { chain, transport } = publicClient
    const network = {
        chainId: chain.id,
        name: chain.name,
        ensAddress: chain.contracts?.ensRegistry?.address,
    }
    if (transport.type === 'fallback')
        return new providers.FallbackProvider(
            (transport.transports as ReturnType<HttpTransport>[]).map(
                ({ value }) => new providers.JsonRpcProvider(value?.url, network),
            ),
        )
    return new providers.JsonRpcProvider(transport.url, network)
}

/** Hook to convert a viem Public Client to an ethers.js Provider. */
export function useEthersProvider({ chainId }: { chainId?: number } = {}) {
    const publicClient = usePublicClient({ chainId })
    return React.useMemo(() => publicClientToProvider(publicClient), [publicClient])
}

function walletClientToSigner(walletClient: WalletClient) {
    const { account, chain, transport } = walletClient
    const network = {
        chainId: chain.id,
        name: chain.name,
        ensAddress: chain.contracts?.ensRegistry?.address,
    }
    const provider = new providers.Web3Provider(transport, network)
    const signer = provider.getSigner(account.address)
    return signer
}

/** Hook to convert a viem Wallet Client to an ethers.js Signer. */
export function useEthersSigner({ chainId }: { chainId?: number } = {}) {
    const { data: walletClient } = useWalletClient({ chainId })
    return React.useMemo(
        () => (walletClient ? walletClientToSigner(walletClient) : undefined),
        [walletClient],
    )
}

Now that you have this you can import these two hooks instead of the two from wagmi see the following example.

// This is what would have been imported previously
// import { useProvider, useSigner } from 'wagmi'

// Now instead we import our custom hooks
import { useEthersProvider, useEthersSigner } from 'your-custom-hooks-file'

function Component() {
  // This is the old way to get an ethers signer from wagmi
  // const signer = useSigner()

  // Now we use our custom useEthersSigner hook
  const signer = useEthersSigner()


  // This is the old way to get an ethers provider from wagmi
  // const provider = useProvider()

  // Now we use our custom useEthersProvider hook
  const provider = useEthersProvider()
}

The signer and provider gotten from our hooks are the same signer and provider you would normally get from ethers and can be used the same way.

We should note that an additional change to wagmi version 1 is that both useSigner and useProvider have been replaced with their viem counterparts.
If you choose to use viem in your dApp then useSigner will become useWalletClient and useProvider will become usePublicClient.

5 Likes