Table of contents

How to create a multi-chain wallet adapter and connection flow

https://www.dynamic.xyz/blog/multi-chain-wallet-connection-flow
How to create a multi-chain wallet adapter and connection flow
How to create a multi-chain wallet adapter and connection flow
Download

Overview

The wallet ecosystem has grown significantly over the last couple of years, with new wallets rolling out constantly.

In parallel, new SDKs libraries are being introduced, each with conflicting approaches around bridging and mobile support, and each working with only a subset of chains and wallets. This combination makes supporting wallets on your site a bit of a mess. 

This article will review some of the key wallet types and the protocols in use, as well as dive into the basics of how to integrate EVM and Solana wallets into your site or app.

Wallet Types and SDK Overview

Wallet types

Interaction type

  • Browser extension wallets (injected wallets) - wallets in this category include Metamask, Phantom, and others.
  • Mobile wallets - as the name suggests, these are mobile apps that serve as wallets.
  • Embedded wallets (Blocto, MyAlgo, Formatic, etc.) - these apps exist as either an SDK within your site or a popup, with an interaction similar to logging in with Google/Facebook (OAuth).
  • Browser-embedded wallets - include Brave and Opera Crypto browsers

Externally-owned account (EOA) vs. Smart contract wallets

‍While we do not cover it in this article, it is worth noting that Account Abstraction (AA) is another growing topic in the industry. Wallets like Argent and Sequence provide a great option for authentication and handling via EIP-1271, adding an extra layer of security. Check out our blog post on Account Abstraction for more information.

EVM protocols & libraries

As the most popular ecosystem around, EVM experiences constant innovation and changes, with wallets working across mobile, web, and both. 

Protocols and Wallets

  • Metamask SDK - a native way to connect a Metamask wallet to your app
  • Coinbase wallet SDK (previously wallet link) - a bridge by the Coinbase team that allows web-to-mobile interaction via a QR code.
  • Coinbase mobile SDK - Coinbase recently launched a native mobile SDK that eliminates bridges and instead uses a universal link for direct app-to-app communication.
  • WalletConnect (bridge) - a generalized bridge that allows communication for a long tail of wallets between desktop and mobile, desktop and desktop, and deep mobile  links.
  • Blocto - An example of an embedded wallet (each has its SDK implementation)

Libraries

  • Ethers.js - a core library that lets developers interact with EVM via javascript (Dynamic's EVM SDK is built on ethers.js, for example).
  • Wagmi - wagmi is a React hooks library (that we proudly support). Based on ethers.js.
  • Web3js - a competing library to ethers.js.
  • Dynamic - a multi-chain library that supports React or headless interactions

Solana protocols & libraries

The Solana ecosystem has taken a slightly different approach, with some unified standards similar to EVM, and some key differences.

Protocols and Wallets

  • Solana wallet adapter - an adapter that manages an ever-growing list of wallets on Solana. 
  • Wallet standard - a browser extension standard that allows any wallet to automatically show up on supported dapps. 
  • WalletConnect - a subset of wallets, especially those that originated with EVM, now support connection to Solana dapps via WalletConnect v2.
  • Mobile wallet adapter (android) - a universal link solution that allows connections from mobile to mobile on Solana.
  • Phantom deep linking- phantom specifically allows a way to create deep links from dapps to their app, similar to Coinbase’s mobile SDK.

Libraries

  • Solana web3js - a library similar to ethers.js, but focusing  on Solana
  • Dynamic - A multi-chain library that supports React and headless interactions.

Bringing things together

After reviewing the basics, you can start combining things together. Specifically, in this guide we’ll focus on creating a simple wallet integration with Metamask and Glow to prove multi-chain support. You can see the full codebase here.

Assumptions

  • For simplicity, we will ignore specialized interactions for wallets that support multi-chain (e.g., Coinbase wallet) or embedded wallets.  
  • We will focus on a Metamask (ETH) + Glow (Solana) integration to prove cross-chain support.
  • We will also not cover UI elements and interactions, mobile support, or multiple wallets that conflict with each other on the same chain; each has its complexity and quirks.

1. build a connector class for Metamask

<script>import { ethers } from "ethers";

export class Metamask {
  constructor() {
    this.ethersProvider = this.isInstalled()
      ? new ethers.providers.Web3Provider(window.ethereum)
      : null; // if it's not installed, we don't instantiate an ethers Web3Provider
  }
  
  getProviderName() {
    return "Metamask";
  }

  isInstalled() {
    return Boolean(window?.ethereum?.isMetaMask);
  }
} </script> 

2. Add methods to connect and get the address

<script>async connect() {
  if (!this.ethersProvider) return;
  await this.ethersProvider.send("eth_requestAccounts", []);
}

async getAddress() {
  if (!this.ethersProvider) return;
  return (await this.ethersProvider.send("eth_accounts", []))[0];
}</script> 

3. Add event listeners to detect wallet connection, disconnection, and account change

<script>setConnectionEventListeners(fn) {
  if (!this.ethersProvider) return;
  this.ethersProvider.provider.on("connect", async () =>
    fn(await this.getAddress())
  );
  this.ethersProvider.provider.on("disconnect", async () => {
    fn(await this.getAddress());
  });
  this.ethersProvider.provider.on("accountsChanged", async () => {
    fn(await this.getAddress());
  });
} </script> 

4. Build a simple wallet connector component

<script>// components/WalletConnector.jsx

import { useEffect, useState } from "react";

export const WalletConnector = ({ provider }) => {
  const [address, setAddress] = useState("");

  useEffect(() => {
    async function getAndSetAddress() {
      setAddress(await provider.getAddress());
      provider.setConnectionEventListeners(setAddress);
    }

    getAndSetAddress();
  }, [provider]);

  useEffect(() => {}, [provider]);

  const InstalledView = () =>
    address ? (
      <div>
        Connected with {provider.getProviderName()} using address: {address}
      </div>
    ) : (
      <button onClick={() => provider.connect()}>
        Connect with {provider.getProviderName()}
      </button>
    );

  const UninstalledView = () => (
    <div>{provider.getProviderName()} is not installed</div>
  );

  return provider.isInstalled() ? <InstalledView /> : <UninstalledView />;
};</script> 

5. Use the connector in your app UI

<script>import { Metamask } from "./connectors/ethereum/Metamask";
import { WalletConnector } from "./components/WalletConnector";

export default function App() {
  return (
    <div className="App">
      <h1>Multi wallet example</h1>
      <WalletConnector provider={new Metamask()} />
    </div>
  );
} </script> 

6. Add a connector for Glow (Solana)

// src/connectors/solana/glow.js

export class Glow {
  constructor() {
    this.provider = this.isInstalled() ? window.glowSolana : null;
  }

  async connect() {
    await this.provider.connect();
  }

  async getAddress() {
    return this.provider?.publicKey?.toString();
  }

  getProviderName() {
    return "Glow";
  }

  isInstalled() {
    return window.glowSolana?.isGlow;
  }

  setConnectionEventListeners(fn) {
    if (!this.provider) return;
    this.provider.on("connect", () => {
      this.getAddress().then((address) => fn(address));
    });

    this.provider.on("disconnect", () => fn(""));
  }

7. Add the Solana connector to your UI

<script> // App.js

// ...
import { Glow } from "./connectors/solana/Glow";

export default function App() {
  return (
    // ... 
      <WalletConnector provider={new Glow()} />
    // ...
  );
} </script> 

8. Putting it all together

What’s next

Once you have completed the above, you can start expanding your implementation to support additional wallets. We recommend starting with Coinbase Wallet and WalletConnect as your next step. Take into account that the libraries are ever-changing, with new approaches being introduced monthly for how wallets interact with dapps and sites.

As you explore building your multi-chain authentication, here are a few additional libraries to look at across chains:

How can Dynamic help?

Instead of creating a multi-chain adapter on your own, you can use Dynamic’s free version to support an application that requires connect-only functionality.

Dynamic also offers a multi-chain authentication platform that takes care of all edge cases above, and tackles more advanced use cases around session management, multi-wallet support, smart wallet support, and much more.

To get started, visit our quickstart guide. Once you set up Dynamic’s basic integration you can easily access wallets across chains with a few lines of code. To do so, you would fetch the address and check if the wallet is connected with Dynamic:

const { primaryWallet } = useDynamicContext();
const publicAddress = await primaryWallet?.address;

Lastly, you can also explore some of our more advanced features:

Share this article

https://www.dynamic.xyz/blog/multi-chain-wallet-connection-flow
Yoni & Justin

Yoni is the co-founder and CTO of Dynamic. Justin is a founder engineer at Dynamic.

Related articles

Dynamic takes minutes to set up

(Oh, and we also offer a free multi-chain wallet adapter)

Get started