Integrating with Web3-React
This tutorial is a step-by-step guide on how to integrate multiple wallets such as Coinbase Wallet, Metamask, and Wallet Connect into your dapp using the web3-react library.
To explore a running version of the finished product, fork our CodeSandbox.
This guide assumes you have a React application already setup and running. If you are more comfortable jumping straight into code, below is the final working example of a multi-wallet modal integration. We encourage you to fork the sandbox and reconfigure it to suit the needs of your dapp setup.
Prerequisites
- A working React application, set up using
npx create-react-app <app-name>
or similar
Setup Web3-react and Wallet connectors
Step 1: Install ethers and web3-react
Install ethers.js as a required dependency for web3-react. If you are building your dapp with web3.js, you can additionally install the library.
yarn add ethers
yarn add web3 # optional
yarn add @web3-react/core
Step 2: Import and setup Web3ReactProvider
In your index.js file, import the Web3ReactProvider
from web3-react and a Web3Provider
from ethers.js or web3.js. Define a getLibrary
function that returns an instance of the Web3Provider
. Wrap the Web3ReactProvider
around your app root component to make the provider globally accessible throughout your dapp.
import { Web3ReactProvider } from '@web3-react/core'
import { Web3Provider } from "@ethersproject/providers";
function getLibrary(provider) {
return new Web3Provider(provider);
}
ReactDOM.render(
<Web3ReactProvider getLibrary={getLibrary}>
<App />
</Web3ReactProvider>,
document.getElementById('root')
);
Step 3: Import and instantiate Wallet connectors
Install the wallet connectors of your choice. Here we install Coinbase Wallet, Wallet Connect, and an Injected connector (used to connect with Metamask).
This tutorial uses the latest non-beta version of web3-react, web3-react v6.
While v6 uses the correct Coinbase Wallet SDK version, it still refers to Coinbase Wallet SDK by its previous name,
walletlink
. Future versions of web3-react will use the updated naming convention.
yarn add @web3-react/walletlink-connector # Coinbase Wallet
yarn add @web3-react/walletconnect-connector # Wallet Connect
yarn add @web3-react/injected-connector # Injected (e.g. Metamask)
In your App.js file, instantiate the connectors to integrate into your dapp. Each connector has its own set of required parameters to pass in, such as a fallback JSON RPC URL or default chain ID.
import { WalletLinkConnector } from "@web3-react/walletlink-connector";
import { WalletConnectConnector } from "@web3-react/walletconnect-connector";
import { InjectedConnector } from "@web3-react/injected-connector";
const CoinbaseWallet = new WalletLinkConnector({
url: `https://mainnet.infura.io/v3/${process.env.INFURA_KEY}`,
appName: "Web3-react Demo",
supportedChainIds: [1, 3, 4, 5, 42],
});
const WalletConnect = new WalletConnectConnector({
rpcUrl: `https://mainnet.infura.io/v3/${process.env.INFURA_KEY}`,
bridge: "https://bridge.walletconnect.org",
qrcode: true,
});
const Injected = new InjectedConnector({
supportedChainIds: [1, 3, 4, 5, 42]
});
Seeing errors? Check out the Troubleshooting section below for help.
Connect and disconnect from Wallet
In your App.js file add an import for the useWeb3React
hook, which provides a set of methods to activate and deactivate the connection to the wallet of your choice.
import { useWeb3React } from '@web3-react/core'
Inside your App function, add your activate and deactivate methods:
function App() {
const { activate, deactivate } = useWeb3React();
...
}
Then, in your App's HTML template, add a button for each wallet and bind the methods onto your UI components.
<button onClick={() => { activate(CoinbaseWallet) }}>Coinbase Wallet</button>
<button onClick={() => { activate(WalletConnect) }}>Wallet Connect</button>
<button onClick={() => { activate(Injected) }}>Metamask</button>
<button onClick={deactivate}>Disconnect</button>
That's it! You should now be able to seamlessly connect to Coinbase Wallet and other wallets from your dapp.
Access connection, account, network information
To access information about the user's connection status, Ethereum address, and connected network chain ID, you'll need to import additional variables from the useWeb3React
hook.
Again, you'll do this by adding the active, chainId, and account methods inside your App function and binding the methods in your HTML template:
/*
active: boolean indicating connection to user’s wallet
account: connected user's public wallet address
chainId: chain id of the currently connected network
*/
function App() {
# add this line
const { active, chainId, account } = useWeb3React();
...
return (
...
# add these 3 lines
<div>Connection Status: {active}</div>
<div>Account: {account}</div>
<div>Network ID: {chainId}</div>
...
);
}
Switch networks or add custom networks
Web3-React does not have built-in support for Ethereum interactions. In order to add or switch networks, you must directly make a request (via EIP-3085 or EIP-3326) to the Web3Provider
. This provider is accessible via the library
context variable in the useWeb3React
hook.
Here is an example of requesting to switch networks and adding the network as a fallback if it is not already present on the user’s wallet:
const { library } = useWeb3React();
// example of switching or adding network with Harmony Mainnet
const switchNetwork = async () => {
try {
await library.provider.request({
method: "wallet_switchEthereumChain",
params: [{ chainId: "0x63564c40" }],
});
} catch (switchError) {
// 4902 error code indicates the chain is missing on the wallet
if (switchError.code === 4902) {
try {
await library.provider.request({
method: "wallet_addEthereumChain",
params: [
{
chainId: "0x63564c40",
rpcUrls: ["https://api.harmony.one"],
chainName: "Harmony Mainnet",
nativeCurrency: { name: "ONE", decimals: 18, symbol: "ONE" },
blockExplorerUrls: ["https://explorer.harmony.one"],
iconUrls: ["https://harmonynews.one/wp-content/uploads/2019/11/slfdjs.png"]
}
],
});
} catch (error) {
console.error(error)
}
}
}
};
To learn more about how to add this functionality, see the demo CodeSandbox.
As with the example above, any Ethereum interactions, such as sending a transaction or making a contract call, can be done by directly sending a request through the library of your choice. An example of of signing and verifying personal signatures can also be found in the demo CodeSandbox. See the signMessage function as a place to start.
Troubleshooting
I run into the following error: Module not found: Error: Can't resolve <'buffer'/'util'/...>
Due to the removal of default polyfills in webpack5, you must install the following utilities:
yarn add buffer
yarn add util
yarn add stream-browserify
yarn add assert
Then, add the following code snippet to your webpack.config.js:
resolve: {
fallback: {
'fs': false,
'stream': require.resolve('stream-browserify'),
'buffer': require.resolve('buffer/'),
'util': require.resolve('util/'),
'assert': require.resolve('assert/'),
},
}
If you are using an application built on create-react-app
locally, you must run npm run eject
to be able to customize your webpack configuration.
The wallet connection does not persist upon refreshing the browser
Web3Modal provides a built-in option for you to automatically cache the connected provider.
// set cacheProvider parameter as true when instantiating web3modal
const web3Modal = new Web3Modal({
cacheProvider: true, // optional
providerOptions // required
});
// hook to automatically connect to the cached provider
useEffect(() => {
if (web3Modal.cachedProvider) {
connectWallet();
}
}, []);>
I want to give the user the option to "disconnect" from my dapp
Unfortunately, there is no built-in way of disconnecting a user from your dapp - the user may only choose to do so from within their wallet apps. However, you can mimic the behavior by clearing the state and cache of your application when a chooses to disconnect.
const refreshState = () => {
setAccount();
setChainId();
};
const disconnect = async () => {
await web3Modal.clearCachedProvider();
refreshState();
};
<button onClick={disconnect}>Disconnect</button>