@walletconnect/ethereum-provider
Ethereum Provider for WalletConnect Protocol.
Installation
npm i @walletconnect/ethereum-provider
Initialization
import { EthereumProvider } from "@walletconnect/ethereum-provider";
const provider = await EthereumProvider.init({
projectId,
chains,
optionalChains,
showQrModal,
methods,
events,
rpcMap,
metadata,
storage,
storageOptions,
qrModalOptions,
});
Display WalletConnectModal with QR code / Handle connection URI
await provider.connect({
chains,
rpcMap,
pairingTopic,
});
await provider.enable();
provider.on("display_uri", (uri: string) => {
});
await provider.connect();
await provider.enable();
Sending Requests
const result = await provider.request({ method: "eth_requestAccounts" });
provider.sendAsync({ method: "eth_requestAccounts" }, CallBackFunction);
Events
provider.on("chainChanged", handler);
provider.on("accountsChanged", handler);
provider.on("connect", handler);
provider.on("session_event", handler);
provider.on("display_uri", handler);
provider.on("disconnect", handler);
Supported WalletConnectModal options (qrModalOptions)
Please reference up to date documentation for WalletConnectModal
Usage with SSR Frameworks (e.g., Next.js)
The Ethereum Provider interacts with browser-specific APIs (like window, document, localStorage) which are not available during Server-Side Rendering (SSR). Attempting to import or initialize the provider directly in code that runs on the server will cause errors (e.g., ReferenceError: HTMLElement is not defined).
To use @walletconnect/ethereum-provider with frameworks like Next.js (using the App Router or Pages Router), you must ensure that the library is only imported and used on the client-side.
Recommended Approach (Next.js):
- Isolate Usage: Create a dedicated React component (e.g.,
WalletConnectProvider.tsx or Web3Provider.tsx) that handles the initialization and interaction with the EthereumProvider. Mark this component as a Client Component using the "use client"; directive at the top of the file.
- Dynamic Import: In the parent component (which could be a Server Component page or another Client Component), import your dedicated component dynamically with SSR disabled.
Example (src/app/page.tsx or similar):
"use client";
import dynamic from 'next/dynamic';
import { Suspense } from 'react';
const WalletConnectLogic = dynamic(
() => import('@/components/WalletConnectLogic'),
{
ssr: false,
}
);
export default function Home() {
return (
<div>
{/* Other page content */}
<Suspense fallback={<p>Loading WalletConnect...</p>}> {/* Optional Suspense boundary */}
<WalletConnectLogic />
</Suspense>
{/* Other page content */}
</div>
);
}
Example (src/components/WalletConnectLogic.tsx):
"use client";
import { EthereumProvider } from "@walletconnect/ethereum-provider";
import { useEffect, useState, useCallback } from "react";
const projectId = process.env.NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID!;
let provider: Awaited<ReturnType<typeof EthereumProvider.init>> | null = null;
export default function WalletConnectLogic() {
const [account, setAccount] = useState<string | null>(null);
const initialize = useCallback(async () => {
if (!projectId) {
throw new Error("Missing NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID");
}
if (provider) return;
try {
provider = await EthereumProvider.init({
projectId: projectId,
optionalChains: [1, 10, 137],
showQrModal: true,
metadata: { }
});
provider.on("connect", (info: { chainId: string }) => {
console.log("connect", info);
});
provider.on("accountsChanged", (accounts: string[]) => {
console.log("accountsChanged", accounts);
setAccount(accounts[0] ?? null);
});
if (provider.session && provider.accounts.length > 0) {
setAccount(provider.accounts[0]);
}
} catch (error) {
console.error("Failed to initialize WalletConnect Provider", error);
}
}, []);
useEffect(() => {
initialize();
return () => {
};
}, [initialize]);
const connectWallet = useCallback(async () => {
if (!provider) {
console.error("Provider not initialized");
return;
}
try {
await provider.connect();
} catch (error) {
console.error("Failed to connect:", error);
}
}, []);
return (
<div>
{/* Your Connect/Disconnect buttons, account display, etc. */}
{!account ? (
<button onClick={connectWallet}>Connect Wallet</button>
) : (
<p>Connected: {account}</p>
)}
{/* ... */}
</div>
);
}
This pattern ensures the provider code only runs in the browser, avoiding SSR compatibility issues. Similar approaches (lazy loading components that use browser APIs) exist for other SSR frameworks.