Add Wallet Onboard to your app
Connect wallets to retrieve balances, sign messages, send transactions, and more
In this guide, we’ll start from scratch and walk through how to use Wallet Onboard to connect crypto wallets to your app.
Enable Wallet Onboard and retrieve a Link Token
Go to Wallet Onboard Settings in the Plaid Dashboard. After accepting the Terms of Service, you will see a Sandbox Link Token on the page. Use this token to open Wallet Onboard later.
You also need to set your Allowed Domains for Wallet Onboard. Wallet Onboard will not work on any domains except for those you configure. Wallet Onboard will only work on HTTPS domains, except for localhost
.
When using a Sandbox Link Token from the dashboard, Wallet Onboard will only open on localhost:*
domains. When you are ready to use Wallet Onboard on any domain, you can request production access.
Install Link SDK
Select group for content switcherWallet Onboard uses Plaid's Link SDK, which is used by thousands of developers in some of the most popular finance and crypto products in the world.
Include the Plaid Link initialize script on each page of your site. It must always be loaded directly from https://cdn.plaid.com
, rather than included in a bundle or hosted yourself.
1<script src="https://cdn.plaid.com/link/v2/stable/link-initialize.js"></script>
Initialize Wallet Onboard
Opening Wallet Onboard for the first time involves configuring Link, opening Link, and eventually receiving a provider
in an onSuccess
callback.
1const onSuccess = useCallback<PlaidWeb3OnSuccess>(2 // provider is an EIP-1193 compatible JavaScript object https://eips.ethereum.org/EIPS/eip-11933 // provider can be used by other libraries to request more data4 async (provider, metadata) => {5 // metadata contains the connected wallet name6 const walletName = metadata.wallet.name;7 // provider is an EIP-1193 compatible JavaScript object https://eips.ethereum.org/EIPS/eip-11938 // provider can be used by other libraries to request more data9 const accounts = await provider.request({10 method: 'eth_accounts',11 });12 },13 [],14);1516const onExit = useCallback<PlaidLinkOnExit>((error, metadata) => {17 // Optional callback, called if the user exits without connecting a wallet18 // See https://plaid.com/docs/link/web/#onexit for details19}, []);2021const { open, ready } = useEthereumProvider({22 // retrieve from https://dashboard.plaid.com/team/wallet-onboard23 token: '',24 chain: {25 // RPC gateway URL to use for non-wallet methods26 rpcUrl: '',27 // EVM chain ID in hexadecimal format as described in https://eips.ethereum.org/EIPS/eip-69528 // See https://chainlist.org/ for a list of common chains29 chainId: '0x1',30 },31 onSuccess,32 onExit,33});3435return (36 <button onClick={() => open()} disabled={!ready}>37 Connect wallet38 </button>39);
Using a provider
The provider
returned by onSuccess
will be an EIP-1193-compatible object. The EIP-1193 Appendix has a complete definition of the methods available on the object, but in summary their APIs are:
provider.request(message)
- Send JSON RPC methods to the wallet and/or node gateway.provider.on(event, callback)
- Subscribe to events like account or connection changes.provider.removeListener(event, callback)
- Remove subscriptions added byprovider.on
Here are some examples of using the provider
:
1// Getting the connected accounts2const accounts: string[] = await provider.request({ method: 'eth_accounts' });34// Request a signed message5const account = accounts[0];6const message = Web3.utils.toHex('Hello!');7const signature: string = await provider.request({8 method: 'personal_sign',9 params: [message, accounts],10});11// The signature can be used to verify the signer using another library12// For example, using Web3.js:13const recoveredAccount: string = web3.eth.accounts.recover(message, signature);14const validSignature = recoveredAccount === account;1516// Send network currency (e.g. ETH)17const to: string = '0x...';18const from: string = account;19// value is denominated in e.g. wei for ETH20// Usually a library should be used to convert21// between the denominations22const value: string = Web3.utils.toWei('1', 'ether');23const txHash: string = await provider.request({24 method: 'eth_sendTransaction',25 params: [26 {27 to,28 from,29 value,30 },31 ],32});
For more documentation on the available JSON RPC methods, review the Ethereum.org docs and Plaid's provider
TypeScript definitions.
As shown above, many usages of the provider
are simplified by libraries such as Web3.js and Ethers.js. These libraries will use EIP-1193 providers to build other abstractions and implement more complicated numberic or cryptographic operations:
1// Example: Web3.js https://web3js.readthedocs.io/en/v1.7.3/2const web3 = new Web3(provider);3const address = (await web3.eth.getAccounts())[0];4const balance = await web3.eth.getBalance(address);56// Example: Ethers.js https://docs.ethers.io/v5/7const web3Provider = new providers.Web3Provider(provider);8const signer = web3Provider.getSigner();9const address = await signer.getAddress();10const balance = await web3Provider.getBalance(address);
Your app should listen for important provider
events and respond appropriately:
1const handleChainChanged = (chainId) => {2 if (isCompatibleChain(chainId)) {3 // update UI4 } else {5 // alert the user6 }7};8provider.on('chainChanged', handleChainChanged);910const handleAccountsChanged = (accounts) => {11 if (accounts.length > 0) {12 // update UI13 } else {14 // alert the user15 }16};17provider.on('accountsChanged', accountsChanged);
RPC URLs
An RPC URL is required for any interactions with the blockchain that cannot be processed by the wallet (e.g. signing transactions). In addition to running your own node and/or RPC HTTP gateway, you can check this list of providers of RPC gateways.
Chain Management
If the user selects an account in their wallet that is not the chainId
configured, Wallet Onboard will attempt to automatically switch to a compatible chain if the wallet supports that functionality (EIP-3326 and EIP-3085); if the wallet does not support that functionality, Link will instruct the user on the issue and suggest possible solutions.
Reinitialize previous connections
If a user has previously connected a wallet, your app can optionally detect that and reuse the connection without opening Link again:
1const config = { token: '', chain: { chainId, rpcUrl } };2const {3 open,4 ready,5 getCurrentEthereumProvider,6 isProviderActive,7} = useEthereumProvider(config);89useEffect(() => {10 if (!ready || !getCurrentEthereumProvider || !isProviderActive) {11 return;12 }13 (async () => {14 const provider = await getCurrentEthereumProvider(config.chain);15 if (!provider) {16 // UX to handle no existing connection17 return;18 }19 const isActive = await isProviderActive(provider);20 if (isActive) {21 // use provider22 } else {23 // UX to handle no existing connection24 }25 })();26}, [ready, getCurrentEthereumProvider, isProviderActive]);
The connection will persist for as long as possible on the same device. The user may disconnect your app via their wallet at any time, so be sure to check isProviderActive
and listen for disconnected
events per EIP-1193.
Disconnecting
Your app can optionally disconnect itself from the user's wallet:
1const { open, ready, disconnectEthereumProvider } = useEthereumProvider(config);23const disconnect = useCallback(() => {4 if (!disconnectEthereumProvider || !provider) {5 return;6 }78 async () => {9 await disconnectEthereumProvider(provider);10 };11}, [disconnectEthereumProvider, provider]);1213if (provider) {14 return <button onClick={disconnect}>Disconnect</button>;15}1617return (18 <button onClick={() => open()} disabled={!ready}>19 Connect wallet20 </button>21);
This is appropriate for any "sign out" flows your app may have.
Customization
Wallet Onboard supports some of the Link customizations available in the Dashboard:
More customizations, including Dark Themes and Wallet Select options, will be available soon.