This is the core package that contains all of the UI and logic to be able to seamlessly connect user's wallets to your app and track the state of those wallets.

The core package. Onboard no longer contains any wallet specific code, so wallets need to be passed in upon initialization.

Install the core module:
npm i @web3-onboard/core
If you would like to support all wallets, then you can install all of the wallet modules:
npm i @web3-onboard/injected-wallets @web3-onboard/ledger @web3-onboard/trezor @web3-onboard/keepkey @web3-onboard/walletconnect @web3-onboard/walletlink @web3-onboard/torus @web3-onboard/portis @web3-onboard/mew @web3-onboard/gnosis @web3-onboard/fortmatic @web3-onboard/magic
  • MEW wallet currently fails to install on M1 macs
  • All wallet modules (except for injected-wallets) require extra dependencies and may require polyfilling the node built in modules for the browser. See the Build Environments section for more info

Onboard needs to be initialized with an options object before the API can be used:
type InitOptions {
wallets: WalletInit[]
chains: Chain[]
appMetadata?: AppMetadata
i18n?: i18nOptions
accountCenter?: AccountCenterOptions
apiKey?: string
notify?: Partial<NotifyOptions> | Partial<Notify>
connect?: Partial<ConnectModalOptions>

wallets An array of wallet modules that you would like to be presented to the user to select from when connecting a wallet. A wallet module is an abstraction that allows for easy interaction without needing to know the specifics of how that wallet works and are separate packages that can be included. A list of wallet module packages that can be installed can be found here.
chains An array of Chains that your app supports:
type Chain = {
id: ChainId // hex encoded string, eg '0x1' for Ethereum Mainnet
namespace?: 'evm' // string indicating chain namespace. Defaults to 'evm' but will allow other chain namespaces in the future
rpcUrl: string // used for network requests (eg Alchemy or Infura)
label: string // used for display, eg Ethereum Mainnet
token: TokenSymbol // the native token symbol, eg ETH, BNB, MATIC
color?: string; // styling for the chain icons
icon?: string; // svg icon to customize the icon for the chain
providerConnectionInfo?: ConnectionInfo;
publicRpcUrl?: string;
blockExplorerUrl?: string;
Not sure about the chainIDs of the chains you want to support? We recommend looking them up on ChainList and converting the decimal to hex before passing to web3-onboard. At least one chain must be set before attempting to connect a wallet.
appMetadata An object that defines your app:
type AppMetadata = {
// app name
name: string
// SVG icon string, with height or width (whichever is larger) set to 100% or a valid image URL
icon: string
// Optional wide format logo (ie icon and text) to be displayed in the sidebar of connect modal. Defaults to icon if not provided
logo?: string
// description of app
description?: string
// url to a getting started guide for app
gettingStartedGuide?: string
// url that points to more information about app
explore?: string
// if your app only supports injected wallets and when no injected wallets detected, recommend the user to install some
recommendedInjectedWallets?: RecommendedInjectedWallets[]
type RecommendedInjectedWallets = {
name: string // display name
url: string // link to download wallet
i18n An object that defines the display text for different locales. Can also be used to override the default text. To override the default text, pass in an object for the en locale.
type Locale = string // eg 'en', 'es'
type i18nOptions = Record<Locale, i18n>
// example customizing copy for onboard initialization
i18n: {
en: {
notify: {
watched: {
"txConfirmed": "you paid a foo {formattedValue} {asset}!"
To access the current list of text values that can be internationalized or replaced, please check out the en.json file in the linked folder. Onboard is using the ICU syntax for formatting under the hood.

connect An object that allows for customization of the Connect Modal and accepts the type ConnectModalOptions.
type ConnectModalOptions = {
showSidebar?: boolean

accountCenter An object that defines whether the account center UI is enabled and its position on the screen. Account center has a minimal version that is enabled by default on mobile, you can choose to enable the minimal version on desktop as well. Note: To fully disable the Account Center both desktop and mobile need to be set to enabled: false
type AccountCenterOptions = {
desktop: {
position?: AccountCenterPosition // default: 'topRight'
enabled?: AccountCenter['enabled'] // default: true
minimal?: boolean // default: false
containerElement?: string // defines the DOM container element for svelte to attach
// **NOTE: containerElement must be a DOM element with a styleSheet property attached.
// This property can normally be omitted from the config and allowed to default to document.body
mobile: {
position?: AccountCenterPosition // default: 'topRight'
enabled?: AccountCenter['enabled'] // default: true
minimal?: boolean // default: true on mobile
containerElement?: string // defines the DOM container element for svelte to attach
// **NOTE: containerElement must be a DOM element with a styleSheet property attached.
// This property can normally be omitted from the config and allowed to default to document.body
type AccountCenter = {
enabled: boolean
position: AccountCenterPosition
expanded: boolean
type AccountCenterPosition =
| 'topRight'
| 'bottomRight'
| 'bottomLeft'
| 'topLeft'

notify Notify provides transaction notifications for all connected wallets on the currently selected blockchain. When switching chains the previous chain listeners remain active for 60 seconds to allow capture and report of an remaining transactions that may be in flight. Transaction notifications include all transaction states - for more info please see Transaction Events - State Changes.
Transaction notifications are captured if a DAppID is provided in the Onboard config (note: DApp notifications can still be sent without this DAppID using the notifications functions within the Onboard API). An object that defines whether transaction notifications will display defaults to true if an API key is provided. This object contains an enabled flag prop and an optional transactionHandler which is a callback that can disable or allow customizations of notifications. Currently notifications are positioned in the same location as the account center (either below, if the Account Center is positioned along the top, or above if positioned on the bottom of the view). The transactionHandler can react off any property of the Ethereum TransactionData returned to the callback from the event (see console.log in example init). In turn, it can return a Custom Notification object to define the verbiage, styling, or add functionality:
  • Notification.message - to completely customize the message shown
  • Notification.eventCode - handle codes in your own way - see codes here under the notify prop default en file here
  • Notification.type - icon type displayed (see NotificationType below for options)
  • Notification.autoDismiss - time (in ms) after which the notification will be dismissed. If set to 0 the notification will remain on screen until the user dismisses the notification, refreshes the page or navigates away from the site with the notifications
  • - add link to the transaction hash. For instance, a link to the transaction on etherscan
  • Notification.onClick() - onClick handler for when user clicks the notification element
Notify can also be styled by using the CSS variables found below. These are set up to allow maximum customization with base styling variables setting the global theme (i.e. --onboard-grey-600) along with more precise component level styling variables available (--notify-onboard-grey-600) with the latter taking precedent if defined.
If notifications are enabled the notifications can be handled through onboard app state as seen below:
const wallets ='notifications')
const { unsubscribe } = wallets.subscribe(update =>
console.log('transaction notifications: ', update)
// unsubscribe when updates are no longer needed
export type NotifyOptions = {
enabled: boolean // default: true
* Callback that receives all transaction events
* Return a custom notification based on the event
* Or return false to disable notification for this event
* Or return undefined for a default notification
transactionHandler?: (
event: EthereumTransactionData
) => TransactionHandlerReturn
* Position of notifications that defaults to the same position as the
* Account Center (if enabled) of the top right if AC is disabled
* and notifications are enabled (enabled by default with API key)
* 'topRight' | 'bottomRight' | 'bottomLeft' | 'topLeft'
position?: NotificationPosition
export type TransactionHandlerReturn = CustomNotification | boolean | void
export type CustomNotification = Partial<Omit<Notification, 'id' | 'startTime'>>
export type Notification = {
id: string
key: string
type: NotificationType
network: Network
startTime?: number
eventCode: string
message: string
autoDismiss: number
link?: string
onClick?: (event: Event) => void
export type NotificationType = 'pending' | 'success' | 'error' | 'hint'
export declare type Network = 'main' | 'testnet' | 'ropsten' | 'rinkeby' | 'goerli' | 'kovan' | 'xdai' | 'bsc-main' | 'matic-main' | 'fantom-main' | 'matic-mumbai' | 'local';
export interface UpdateNotification {
(notificationObject: CustomNotification): {
dismiss: () => void
update: UpdateNotification
Notify can be used to deliver custom DApp notifications by passing a CustomNotification object to the customNotification action. This will return an UpdateNotification type. This UpdateNotification will return an update function that can be passed a new CustomNotification to update the existing notification. The customNotification method also returns a dismiss method that is called without any parameters to dismiss the notification.
const { update, dismiss } =
type: 'pending',
'This is a custom DApp pending notification to use however you want',
autoDismiss: 0
() =>
eventCode: 'dbUpdateSuccess',
message: 'Updated status for custom notification',
type: 'success',
autoDismiss: 8000

Putting it all together, here is an example initialization with the injected wallet modules:
import Onboard from '@web3-onboard/core'
import injectedModule from '@web3-onboard/injected-wallets'
const injected = injectedModule()
const onboard = Onboard({
wallets: [injected],
chains: [
id: '0x1',
token: 'ETH',
label: 'Ethereum Mainnet',
id: '0x3',
token: 'tROP',
label: 'Ethereum Ropsten Testnet',
id: '0x4',
token: 'rETH',
label: 'Ethereum Rinkeby Testnet',
id: '0x38',
token: 'BNB',
label: 'BNB Chain',
rpcUrl: ''
id: '0x89',
token: 'MATIC',
label: 'Matic Mainnet',
rpcUrl: ''
id: '0xfa',
token: 'FTM',
label: 'Fantom Mainnet',
rpcUrl: ''
apiKey: yourApiKey, // get this for free at
appMetadata: {
name: 'Token Swap',
icon: myIcon, // svg string icon
description: 'Swap tokens for other tokens',
recommendedInjectedWallets: [
{ name: 'MetaMask', url: '' },
{ name: 'Coinbase', url: '' }
accountCenter: {
desktop: {
position: 'topRight',
enabled: true,
minimal: true
mobile: {
position: 'topRight',
enabled: true,
minimal: true
i18n: {
en: {
connect: {
selectingWallet: {
header: 'custom text header'

To initiate a user to select and connect a wallet you can call the connectWallet function on an initialized Onboard instance. It will return a Promise that will resolve when the user either successfully connects a wallet, or when they dismiss the UI. The resolved value from the promise will be the latest state of the wallets array. The order of the wallets array is last to first, so the most recently selected wallet will be the first item in the array and can be thought of as the "primary wallet". If no wallet was selected, then the wallets array will have the same state as it had before calling connectWallet.

async function connectWallet() {
const wallets = await onboard.connectWallet()

A common UX pattern is to remember the wallet(s) that a user has previously connected by storing them in localStorage and then automatically selecting them for the user next time they visit your app. You could enable this in your app by first syncing the wallets array to localStorage:
const walletsSub ='wallets')
const { unsubscribe } = walletsSub.subscribe(wallets => {
const connectedWallets ={ label }) => label)
// Don't forget to unsubscribe when your app or component un mounts to prevent memory leaks
// unsubscribe()
Now that you have the most recent wallets connected saved in local storage, you can auto select those wallet(s) when your app loads:
const previouslyConnectedWallets = JSON.parse(
if (previouslyConnectedWallets) {
// Connect the most recently connected wallet (first in the array)
await onboard.connectWallet({ autoSelect: previouslyConnectedWallets[0] })
// You can also auto connect "silently" and disable all onboard modals to avoid them flashing on page load
await onboard.connectWallet({
autoSelect: { label: previouslyConnectedWallets[0], disableModals: true }
// OR - loop through and initiate connection for all previously connected wallets
// note: This UX might not be great as the user may need to login to each wallet one after the other
// for (walletLabel in previouslyConnectedWallets) {
// await onboard.connectWallet({ autoSelect: walletLabel })
// }

A wallet can be disconnected, which will cleanup any background operations the wallet may be doing and will also remove it from the Onboard wallets array:
// disconnect the first wallet in the wallets array
const [primaryWallet] = onboard.state.get().wallets
await onboard.disconnectWallet({ label: primaryWallet.label })
The disconnectWallet method takes the wallet.label value and returns a Promise that resolves to the current state of the wallets array.

Onboard currently keeps track of the following state:
  • wallets: The wallets connected to Onboard
  • chains: The chains that Onboard has been initialized with
  • accountCenter: The current state of the account center UI
  • walletModules: The wallet modules that are currently set and will be rendered in the wallet selection modal
type AppState = {
wallets: WalletState[]
chains: Chain[]
accountCenter: AccountCenter
walletModules: WalletModule[]
type Chain {
namespace?: 'evm'
id: ChainId
rpcUrl: string
label: string
token: TokenSymbol
color?: string
icon?: string
type WalletState = {
label: string
icon: string
provider: EIP1193Provider
accounts: Account[]
chains: ConnectedChain[]
instance?: unknown
type Account = {
address: string
ens: {
name?: string
avatar?: string
contentHash?: string
getText?: (key: string) => Promise<string | undefined>
balance: Record<TokenSymbol, string>
type ConnectedChain = {
namespace: 'evm'
id: ChainId
type ChainId = string
type TokenSymbol = string
type AccountCenter = {
enabled: boolean
position: AccountCenterPosition
expanded: boolean
type AccountCenterPosition =
| 'topRight'
| 'bottomRight'
| 'bottomLeft'
| 'topLeft'
The current state of Onboard can be accessed at any time using the state.get() method:
const currentState = onboard.state.get()
State can also be subscribed to using the method. The select method will return an RXJS Observable. Understanding of RXJS observables is not necessary to subscribe to state updates, but allows for composable functionality if wanted. The key point to understand is that if you subscribe for updates, remember to unsubscribe when you are finished to prevent memory leaks.
To subscribe to all state updates, call the select method with no arguments:
const state =
const { unsubscribe } = state.subscribe(update =>
console.log('state update: ', update)
// remember to unsubscribe when updates are no longer needed
// unsubscribe()
Specific top level slices of state can be subcribed to. For example you may want to just subscribe to receive updates to the wallets array only:
const wallets ='wallets')
const { unsubscribe } = wallets.subscribe(update =>
console.log('wallets update: ', update)
// unsubscribe when updates are no longer needed

setWalletModules For updating the wallets that are displayed in the wallet selection modal. This can be used if the wallets you want to support is conditional on another user's action within your app. The setWalletModules action is called with an updated array of wallets (the same wallets that are passed in on initialization)
const onboard = Onboard({
wallets: [injected, trezor, ledger],
chains: [
id: '0x1',
token: 'ETH',
label: 'Ethereum Mainnet',
rpcUrl: `${INFURA_ID}`
// then after a user action, you may decide to only display hardware wallets on the next call to onboard.connectWallet
onboard.state.actions.setWalletModules([ledger, trezor])
updatedBalances You may decide to get updated balances for connected wallets after a user action by calling the updatedBalances function, which expects a conditional array of addresses.
onboard.state.actions.updateBalances() // update all balances for all connected addresses
onboard.state.actions.updateBalances(['0xfdadfadsadsadsadasdsa']) // update balance for one address
]) // update balance for two addresses
setPrimaryWallet The primary wallet (first in the list of connected wallets) and primary account (first in the list of connected accounts for a wallet) can be set by using the setPrimaryWallet function. The wallet that is set needs to be passed in for the first parameter and if you would like to set the primary account, the address of that account also needs to be passed in:
// set the second wallet in the wallets array as the primary
// set the second wallet in the wallets array as the primary wallet
// as well as setting the third account in that wallet as the primary account

When initializing Onboard you define a list of chains/networks that your app supports. If you would like to prompt the user to switch to one of those chains, you can use the setChain method on an initialized instance of Onboard:
type SetChain = (options: SetChainOptions) => Promise<boolean>
type SetChainOptions = {
chainId: string // hex encoded string
chainNamespace?: 'evm' // defaults to 'evm' (currently the only valid value, but will add more in future updates)
wallet?: string // the wallet.label of the wallet to set chain
const success = await onboard.setChain({ chainId: '0x89' })
The setChain method takes an options object with a chainId property hex encoded string for the chain id to switch to. The chain id must be one of the chains that Onboard was initialized with. If the wallet supports programatically adding and switching the chain, then the user will be prompted to do so, if not, then a modal will be displayed indicating to the user that they need to switch chains to continue. The setChain method returns a promise that resolves when either the user has confirmed the chain switch, or has dismissed the modal and resolves with a boolean indicating if the switch network was successful or not. The setChain method will by default switch the first wallet (the most recently connected) in the wallets array. A specific wallet can be targeted by passing in the wallet.label in the options object as the wallet parameter.

The Onboard styles can customized via CSS variables. The following properties and their default properties can be customized by adding these variables to the :root in your CSS file:
:root {
--onboard-white: white;
--onboard-black: black;
--onboard-primary-1: #2f80ed;
--onboard-primary-100: #eff1fc;
--onboard-primary-200: #d0d4f7;
--onboard-primary-300: #b1b8f2;
--onboard-primary-400: #929bed;
--onboard-primary-500: #6370e5;
--onboard-primary-600: #454ea0;
--onboard-primary-700: #323873;
--onboard-gray-100: #ebebed;
--onboard-gray-200: #c2c4c9;
--onboard-gray-300: #999ca5;
--onboard-gray-400: #707481;
--onboard-gray-500: #33394b;
--onboard-gray-600: #242835;
--onboard-gray-700: #1a1d26;
--onboard-success-100: #d1fae3;
--onboard-success-200: #baf7d5;
--onboard-success-300: #a4f4c6;
--onboard-success-400: #8df2b8;
--onboard-success-500: #5aec99;
--onboard-success-600: #18ce66;
--onboard-success-700: #129b4d;
--onboard-danger-100: #ffe5e6;
--onboard-danger-200: #ffcccc;
--onboard-danger-300: #ffb3b3;
--onboard-danger-400: #ff8080;
--onboard-danger-500: #ff4f4f;
--onboard-danger-600: #cc0000;
--onboard-danger-700: #660000;
--onboard-warning-100: #ffefcc;
--onboard-warning-200: #ffe7b3;
--onboard-warning-300: #ffd780;
--onboard-warning-400: #ffc74c;
--onboard-warning-500: #ffaf00;
--onboard-warning-600: #cc8c00;
--onboard-warning-700: #664600;
/* FONTS */
--onboard-font-family-normal: Sofia Pro;
--onboard-font-family-semibold: Sofia Pro Semibold;
--onboard-font-family-light: Sofia Pro Light;
--onboard-font-size-1: 3rem;
--onboard-font-size-2: 2.25rem;
--onboard-font-size-3: 1.5rem;
--onboard-font-size-4: 1.25rem;
--onboard-font-size-5: 1rem;
--onboard-font-size-6: 0.875rem;
--onboard-font-size-7: 0.75rem;
--onboard-spacing-1: 3rem;
--onboard-spacing-2: 2rem;
--onboard-spacing-3: 1.5rem;
--onboard-spacing-4: 1rem;
--onboard-spacing-5: 0.5rem;
--onboard-border-radius-1: 24px;
--onboard-border-radius-2: 20px;
--onboard-border-radius-3: 16px;
--onboard-shadow-0: none;
--onboard-shadow-1: 0px 4px 12px rgba(0, 0, 0, 0.1);
--onboard-shadow-2: inset 0px -1px 0px rgba(0, 0, 0, 0.1);
/* *if not set will fallback to variables with `--onboard` prefix shown above */
/* COLORS */
--account-select-modal-white: white;
--account-select-modal-black: black;
--account-select-modal-primary-100: #eff1fc;
--account-select-modal-primary-200: #d0d4f7;
--account-select-modal-primary-300: #b1b8f2;
--account-select-modal-primary-500: #6370e5;
--account-select-modal-primary-600: #454ea0;
--account-select-modal-gray-100: #ebebed;
--account-select-modal-gray-200: #c2c4c9;
--account-select-modal-gray-300: #999ca5;
--account-select-modal-gray-500: #33394b;
--account-select-modal-gray-700: #1a1d26;
--account-select-modal-danger-500: #ff4f4f;
/* FONTS */
--account-select-modal-font-family-normal: Sofia Pro;
--account-select-modal-font-family-light: Sofia Pro Light;
--account-select-modal-font-size-5: 1rem;
--account-select-modal-font-size-7: .75rem;
--account-select-modal-font-line-height-1: 24px;
--account-select-modal-margin-4: 1rem;
--account-select-modal-margin-5: 0.5rem;
/* NOTIFY */

Many of the wallet modules require dependencies that are not normally included in browser builds (namely the node built-in modules such as crypto, buffer, util etc). These modules are used by web3 libraries and wallets for interacting with popular blockchain networks.
Here's the list of node polyfills you need to install as dependencies:
Polyfills needed
@web3-onboard/core @web3-onboard/injected-wallets
assert, stream-browserify
@web3-onboard/coinbase @web3-onboard/walletconnect @web3-onboard/web3auth @web3-onboard/torus
assert, buffer, crypto-browserify, https-browserify, os-browserify, stream-browserify, stream-http, util, url
@web3-onboard/portis @web3-onboard/mew
assert, buffer, crypto-browserify, https-browserify, os-browserify, stream-browserify,
@web3-onboard/ledger @web3-onboard/trezor @web3-onboard/keepkey @web3-onboard/gnosis @web3-onboard/fortmatic @web3-onboard/magic
assert, buffer, crypto-browserify, https-browserify, os-browserify, stream-browserify,
To help resolve dependency issues you might face, we have compiled a configurations list for various bundlers like Webpack 5, SvelteKit, Create React App, Vite and Nuxt.

Everything should just work since the node builtins are automatically bundled in v4

You'll need to add some dev dependencies with the following command:
npm i --save-dev assert buffer crypto-browserify stream-http https-browserify os-browserify process stream-browserify util stream
Then add the following to your webpack.config.js file:
const webpack = require('webpack')
module.exports = {
resolve: {
alias: {
assert: 'assert',
buffer: 'buffer',
crypto: 'crypto-browserify',
http: 'stream-http',
https: 'https-browserify',
os: 'os-browserify/browser',
process: 'process/browser',
stream: 'stream-browserify',
util: 'util'
experiments: {
asyncWebAssembly: true
plugins: [
new webpack.ProvidePlugin({
process: 'process/browser',
Buffer: ['buffer', 'Buffer']
If you're running a Next.js or still get the "Uncaught ReferenceError: Buffer is not defined in React" error with react-scripts v5 / webpack 5 after the above config, add the following to your App. import {Buffer} from 'buffer';
window.Buffer = window.Buffer || require("buffer").Buffer;
If using create-react-app
CRACO provides an easy way to override webpack config which is obfuscated in Create React App built applications.
The above webpack 5 example can be used in the craco.config.js file at the root level in this case.

Add the following dev dependencies:
npm i --save-dev rollup-plugin-polyfill-node
Then add the following to your svelte.config.js file:
import adapter from '@sveltejs/adapter-auto'
import preprocess from 'svelte-preprocess'
import nodePolyfills from 'rollup-plugin-polyfill-node'
const MODE = process.env.NODE_ENV
const development = MODE === 'development'
/** @type {import('@sveltejs/kit').Config} */
const config = {
preprocess: preprocess(),
kit: {
adapter: adapter(),
vite: {
plugins: [
development &&
include: [
new RegExp('node_modules/.vite/.*js')
http: true,
crypto: true
resolve: {
alias: {
crypto: 'crypto-browserify',
stream: 'stream-browserify',
assert: 'assert'
build: {
rollupOptions: {
plugins: [nodePolyfills({ crypto: true, http: true })]
commonjsOptions: {
transformMixedEsModules: true
export default config

Add the following dev dependencies:
yarn add --dev @esbuild-plugins/node-globals-polyfill
Then add the following to your vite.config.js file:
import { defineConfig } from 'vite'
import { NodeGlobalsPolyfillPlugin } from '@esbuild-plugins/node-globals-polyfill'
//if you're using a create-vite framework template, you can also
//import the vite plugin for that specific framework and add it in vite.config.js
//for more support.
//For example, for vite.js react-ts template, you need to:
//import react from '@vitejs/plugin-react'
// and then add plugins: [react()] below
export default defineConfig({
resolve: {
alias: {
stream: "stream-browserify",
crypto: 'crypto-browserify',
assert: 'assert'
optimizeDeps: {
esbuildOptions: {
// Node.js global to browser globalThis
define: {
global: "globalThis",
// Enable esbuild polyfill plugins
plugins: [
process: true,
buffer: true,

Add the following to your nuxt.config.js:
build: {
standalone: true,

Setup a local Hardhat node by following the documentation found here:
Next run npx hardhat node to run the local Hardhat node. This will display a list of accounts and private keys to work/test with
This will expose a JSON-RPC interface to Hardhat Network at
Hardhat currently uses the ChainId of 31337
Click the ‘Show/hide’ test networks button to show test networks which will take you to an ‘Advanced’ settings page.
From there turn ‘Show test networks’ on. Also turn on ‘Customize transaction nonce’.
Select the network dropdown within MetaMask and select Localhost 8545.
Open MetaMask and select the Icon within the wallet and then select Import Account
Add the Private Key from the Hardhat account displayed in the terminal that you want to work with. You should now see the account and balance shown in the terminal within MetaMask.
Add the Hardhat network to the chains array within the Web3-Onboard initialization object as such
chains: [
id: 31337,
token: 'ETH',
label: 'Hardhat',
rpcUrl: ''
Open your W3O application and connect to MetaMask. If not on the Hardhat network use the Account Center to select Hardhat or change networks through MetaMask.
You are now connected! Sign messages, send transactions to other Hardhat network accounts, etc... In the same way you would on any EVM network/chain.
Copy link
On this page
Connecting a Wallet
Disconnecting a Wallet
Actions to Modify State
Setting the User's Chain/Network
Custom Styling
Build Environments
Connecting Web3-Onboard to a local Hardhat Node