I was able to use the latest WalletConnect/AppKit libraries, not to migrate, but to start a new dApp implementation.
I haven’t tested everything, but pairing, getting the addresses, and signing a transaction already works. It also doesn’t support the custom wallets like Paytaca like the original reference, but I’ll try adding that eventually.
Set up
// config.ts
import type { AppKitNetwork } from "@reown/appkit/networks";
import type { InferredCaipNetwork } from "@reown/appkit-common";
import UniversalProvider from "@walletconnect/universal-provider";
import { AppKit, createAppKit } from "@reown/appkit/core";
export const projectId = process.env.NEXT_PUBLIC_REOWN_PROJECT_ID;
// you can configure your own network
const bch: InferredCaipNetwork = {
id: "bch",
chainNamespace: "bch" as const,
caipNetworkId: "bch:bitcoincash",
name: "Bitcoin Cash",
nativeCurrency: { name: "Bitcoin Cash", symbol: "BCH", decimals: 8 },
rpcUrls: { default: { http: [] } },
};
export const networks = [bch] as [AppKitNetwork, ...AppKitNetwork[]];
let provider: UniversalProvider | undefined;
let modal: AppKit | undefined;
export async function initializeProvider() {
if (!provider) {
provider = await UniversalProvider.init({
projectId,
metadata: {
name: "Project name",
description: "Project description",
url: "https://example.com",
icons: ["https://example.com/favicon.ico"],
},
});
}
return provider;
}
export function initializeModal(universalProvider?: UniversalProvider) {
if (!projectId) {
throw new Error("Project ID is not defined");
}
if (!modal && universalProvider != null) {
modal = createAppKit({
projectId,
networks,
/* @ts-expect-error https://docs.reown.com/appkit/next/core/installation#others-networks-appkit-core-2 */
universalProvider,
manualWCControl: true,
features: {
analytics: true, // Optional - defaults to your Cloud configuration
},
});
}
return modal;
}
// Provider.tsx
"use client";
import React, {
useState,
useEffect,
PropsWithChildren,
useContext,
} from "react";
import { initializeProvider, initializeModal } from "./config"; // previous config file
import UniversalProvider from "@walletconnect/universal-provider";
const requiredNamespaces = {
bch: {
chains: ["bch:bitcoincash"],
methods: ["bch_getAddresses", "bch_signTransaction", "bch_signMessage"],
events: ["addressesChanged"],
},
};
interface Context {
provider: UniversalProvider | null;
session: UniversalProvider["session"] | null;
isLoading: boolean;
isInitialized: boolean;
connect: () => void;
disconnect: () => void;
addresses: string[] | null;
}
const AppKitContext = React.createContext<Context>({
provider: null,
session: null,
addresses: null,
isLoading: false,
isInitialized: false,
connect: () => {},
disconnect: () => {},
});
export const useAppKit = () => useContext(AppKitContext);
export function AppKitProvider(props: PropsWithChildren) {
const [isInitialized, setIsInitialized] = useState(false);
const [provider, setProvider] = useState<Context["provider"]>(null);
const [session, setSession] = useState<Context["session"]>(null);
const [isLoading, setIsLoading] = useState(false);
const [addresses, setAddresses] = useState<string[] | null>(null);
// Initialize the Provider and AppKit on component mount, and check for existing session
useEffect(() => {
const init = async () => {
const dataProvider = await initializeProvider();
setProvider(dataProvider);
const modal = initializeModal(dataProvider);
modal?.subscribeEvents(({ data }) => {
switch (data.event) {
case "MODAL_OPEN":
setIsLoading(false);
default:
return;
}
});
if (dataProvider.session) {
// check if there is a session
setSession(dataProvider.session);
}
setIsInitialized(true);
};
init();
}, []);
useEffect(() => {
if (session == null || provider == null || isInitialized === false) return;
(async () => {
try {
const addresses = (await provider.client.request({
chainId: "bch:bitcoincash",
topic: session.topic,
request: {
method: "bch_getAddresses",
params: {},
},
})) as string[];
setAddresses(addresses);
} catch (e) {
console.error(e);
}
})();
}, [session, provider, isInitialized]);
useEffect(() => {
// Handler for when WalletConnect generates a connection URI
// Opens the AppKit modal with the URI and shows the connecting view
if (provider == null) return;
const handleDisplayUri = (uri: string) => {
const modal = initializeModal(provider);
modal?.open({ uri, view: "ConnectingWalletConnectBasic" });
};
// Handler for when a wallet successfully connects
// Updates the session state and closes the modal
const handleConnect = async ({
session,
}: {
session: Context["session"];
}) => {
setSession(session);
const modal = initializeModal(provider);
await modal?.close();
};
const handleDisconnect = async () => {
setSession(null);
};
// Subscribe to WalletConnect events
provider?.on("display_uri", handleDisplayUri); // Listen for connection URI
provider?.on("connect", handleConnect); // Listen for successful connections
provider?.on("disconnect", handleDisconnect);
return () => {
provider?.removeListener("display_uri", handleDisplayUri);
provider?.removeListener("connect", handleConnect);
provider?.removeListener("disconnect", handleDisconnect);
};
}, [provider]);
const connect = async () => {
setIsLoading(true);
try {
if (!provider) {
throw new Error("Provider is not initialized");
}
await provider.connect({
namespaces: requiredNamespaces,
});
} catch (error) {
console.error("Failed to connect:", error);
} finally {
setIsLoading(false);
}
};
const disconnect = async () => {
setIsLoading(true);
try {
if (!provider) {
throw new Error("Provider is not initialized");
}
await provider.disconnect();
} catch (error) {
console.error("Failed to disconnect:", error);
} finally {
setSession(null);
setIsLoading(false);
}
};
return (
<AppKitContext.Provider
{...props}
value={{
provider,
session,
isLoading,
connect,
disconnect,
isInitialized,
addresses,
}}
/>
);
}
Usage
const {
connect,
disconnect,
addresses,
isLoading,
isInitialized,
session
} = useAppKit();
// open modal
connect()
// request transaction signature
await provider.client.request({
chainId: "bch:bitcoincash",
topic: session?.topic,
request: {
method: "bch_signTransaction",
params: JSON.parse(stringify(wcTransactionObj)),
},
});