Scaffold Series - Part 1 Wallet Adapter

Become a Solana blockchain developer. Let's unroll The Solana Path together with this top-down approach tutorial.

Scaffold Series - Part 1 Wallet Adapter

About The Scaffold Series (Don’t Skip This Part!)

I want to emphasize that the goal here is not to upload as much knowledge as possible into your beautiful mind. But also to give you the tools and mindset to navigate The Solana Path and shape you into a Solana dev.

I will be using a top-down approach. I will only explain concepts you can see and play with right now, so that's not a reference.

Instead of forcing accounts, transactions, and all kinds of weird things into your brain, we will do like in sports, where you will get to play with the ball first (the code) and then start to dig a bit deeper.

Questions open the mind, while answers close it, the goal is to give you the right questions to unroll the Solana path.

To be a good dev (and to live sanely), your goal is not only to learn more and more things but instead focus on reading between the lines, improve your pattern recognition and acquire the proper mindset for a specific technology.

You are accepted to the dojo if...
We will be using NextJS and React, so I assume you can code in any language or have basic Javascript skills.

πŸ™Š Please DO NOT listen to the Imposter Syndrome voice telling you to take a course on NextJS or React right now! We are fine as we are. Of course, sharpen your NextJS or React skill later if needed.

This is not for you if...

You are already building on Solana; come back later for new chapters πŸ˜—.

The chapters:

  1. Getting a Base Floor - Wallet Adapter (this article).
  2. Your First Query - Cookbook & web3.js
  3. Transaction101 - Programs & Transactions

--

About The Authors


Scaffold Starter - 1 Wallet Adapter

Greetings! First, clap me five for coming here and taking steps to become a Solana Builder! πŸ‘Š This series is for you if you have a basic understanding of web3 and wallets or if you come from another chain such as Ethereum.

We will use one single project to grow our skills together as we unroll new concepts. It will be our quick experimentation playground.

About the code
I will keep the HTML and React stuff to a minimum to keep it short and on point. Rest assured that the entire working project is on GitHub here: https://github.com/mwrites/dapp-scaffold

πŸ€ What Will I Get From Part 1?

Okay, you are in! Now, what will you get from this part?

  • [ ] Understand the role of wallets.
  • [ ] Learn about the Solana environments.
  • [ ] Discover how to support new wallets
  • [ ] Build a starter project for your next successful idea!

πŸš€ What Are We Building Today?

Let's start our journey! After all this chitchat, please look at the below gif. The first step to the world of blockchains is to open the web3 door. What door? Well, to communicate with blockchains, you need a wallet. So, our goal in this part is to let users connect their wallets to our dapp.

Installation

First, you will need a wallet app. Let's install Phantom. Once you have done that, let's switch network to devnet:

Also, let's start on the same foot. I set up a starter project where I just glued all the HTML and dependencies for you:

git clone -b starter https://github.com/mwrites/dapp-scaffold.git

If you are starting from a new project, here are the dependencies I am using:

{
  ...
  "dependencies": {
    ...
    "@solana/wallet-adapter-base": "^0.9.3",
    "@solana/wallet-adapter-react": "^0.15.3",
    "@solana/wallet-adapter-react-ui": "^0.9.5",
    "@solana/wallet-adapter-wallets": "^0.15.3",
    "@solana/web3.js": "^1.31.0",
    "react": "17.0.2",
    "react-dom": "17.0.2"
  },
  ...
}

A crucial step is to make sure you are using react 17. As of the time of writing, react 18 is not supported yet.

Solana Wallet Adapter

As you can see from above, the library we need here is @solana/wallet-adapter. Wallet Adapter is simply a library that will help us easily integrate what we have shown in the gif above:

  • Wallet Adapter gives you the UI I have shown you in the gif out of the box!
  • On top of that, it covers all the use cases for you, connection, disconnection, and autoconnection.

Adding The Wallet Adapter Button

Okay, how to use this wallet adapter and add it to our dapp? Let's look at the setup instructions. If anything, we want to look for where the button is, then we can unroll the rest of the configuration. Basically, Wallet Adapter library provides a react component <WalletMultiButton /> that we can use to display a button with the wallet connection state.

Let's add this to our AppBar, open src/components/AppBar.tsx:

import { WalletMultiButton } from "@solana/wallet-adapter-react-ui";

export const AppBar: FC = props => {
  return (
    <div>

      {/* NavBar / Header */}
      <div className="navbar flex flex-row md:mb-2 shadow-lg bg-neutral text-neutral-content">
        ...
        
        {/* Wallet & Settings */}
        <div className="navbar-end">
          <WalletMultiButton />
        </div>
      </div>
      ...
    </div>
  );
};

Run:

yarn dev

We should now see the "Select Wallet" button appear in the top right of our dapp page.

Configuring Wallet Adapter

That's a good start but not functional yet. We need to help the WalletAdapter library with some information to make the button work. Mainly, we need to let the library know:

  1. What network should the user connect to.
  2. What wallet apps do we want to support (Phantom, sollet, etc...).

Time for some react skills; we can achieve this by wrapping the WalletButton component with some configurations (I am just adapting the code from https://github.com/solana-labs/wallet-adapter#setup)

 <ConnectionProvider endpoint={endpoint}>
	<WalletProvider wallets={wallets} onError={onError} autoConnect>
	    <ReactUIWalletModalProvider>
                <WalletMultiButton />
	    </ReactUIWalletModalProvider>
	</WalletProvider>
</ConnectionProvider>
  • endpoint: is the Solana network to connect to.
  • wallets: is the list of wallets we want to support.
  • onError: this is a callback to handle errors.

We could do all of this in our AppBar.tsx, but instead, let's do some Marie Kondo here and make this more reusable, create a new folder and a new file Β src/contexts/WalletContextProvider.tsx:

import { FC, ReactNode, useCallback, useMemo } from 'react';
import { WalletError } from '@solana/wallet-adapter-base';
import { ConnectionProvider, WalletProvider } from '@solana/wallet-adapter-react';
import { WalletModalProvider as ReactUIWalletModalProvider } from '@solana/wallet-adapter-react-ui';


const WalletContextProvider: FC<{ children: ReactNode }> = ({ children }) => {
    const endpoint = ""
    const wallets = [];

    const onError = useCallback(
        (error: WalletError) => {
            console.error(error);
        },
        []
    );

    return (
        <ConnectionProvider endpoint={endpoint}>
            <WalletProvider wallets={wallets} onError={onError} autoConnect>
                <ReactUIWalletModalProvider>{children}</ReactUIWalletModalProvider>
            </WalletProvider>
        </ConnectionProvider>
    );
};

export default WalletContextProvider;

Great, this is the skeleton to make it work. But, first, we have to fill in endpoint and wallets.

PS: No, that's not a sharingan.

Currently, there are 3 blockchains environments in Solana:

export declare enum WalletAdapterNetwork {
    Mainnet = "mainnet-beta",
    Testnet = "testnet",
    Devnet = "devnet"
}
  1. Mainnet: The live environment (and where real SOL are spent!)
  2. Testnet: is where the Solana team releases and tests new fixes and features to the blockchain. This is where you can test your dapp with the new Solana features.
  3. Devnet: is the stable testing environment where you can develop your dapp.

So let's start by configuring the blockchain connection with the endpoint:

// 1. Update the imports
...
import { clusterApiUrl } from '@solana/web3.js';
import { WalletAdapterNetwork, WalletError } from '@solana/wallet-adapter-base';
...

const WalletContextProvider: FC<{ children: ReactNode }> = ({ children }) => {
	// 2. Construct the endpoint with clusterApiUrl and WalletAdapterNetwork
    const network = WalletAdapterNetwork.Devnet;
    const endpoint = useMemo(() => clusterApiUrl(network), [network]);
    ...
	return (
        <ConnectionProvider endpoint={endpoint}>
	        ...
        </ConnectionProvider>
    );
}

The wallet library can now connect to the blockchain, yes! The second part is to get the user's public key. That's what a wallet adapter will do for us.

We just need to declare which wallets we want to support. Wallet apps behave differently depending on their implementation. hopefully, thanks to the adapter library, it is just a matter of adding an import:

...
import { WalletModalProvider as ReactUIWalletModalProvider } from '@solana/wallet-adapter-react-ui';
import {
    PhantomWalletAdapter,
    SolflareWalletAdapter,
    SolletExtensionWalletAdapter,
    SolletWalletAdapter,
    TorusWalletAdapter,
} from '@solana/wallet-adapter-wallets';


const WalletContextProvider: FC<{ children: ReactNode }> = ({ children }) => {
    ...
    const wallets = useMemo(
        () => [
            new WalletAdapter(),
            new SolflareWalletAdapter(),
            new SolletWalletAdapter({ network }),
            new SolletExtensionWalletAdapter({ network }),
            new TorusWalletAdapter(),
        ],
        [network]
    );

	return (
        <ConnectionProvider endpoint={endpoint}>
            <WalletProvider wallets={wallets} onError={onError} autoConnect>
                ...
            </WalletProvider>
        </ConnectionProvider>
    );
};

export default WalletContextProvider;

Now, you know why it is called "Wallet Adapter"! 😁 There are many more wallets supported by the day. Here's the complete list.

If you are developing a new wallet app, you are welcome to contribute to the library!

Using The WalletContextProvider

Okay, we have stitched the network and the wallet configuration into the WalletContextProvider. Now, let's reap the result of our hard work. Head to src/pages/_app.tsx Β and sandwich wrap that <AppBar /> with our shiny wallet configuration:

...
import import WalletContextProvider from 'contexts/WalletContextProvider';


const App: FC<AppProps> = ({ Component, pageProps }) => {
    return (
        <WalletContextProvider>
            ...
            <AppBar/>
            ...
        </WalletContextProvider>
    );
};
You might have forgotten that the first thing we did was to put our wallet button inside the <AppBar/>. I won't deduct points from you because it's just some react stuff πŸ˜‰

πŸ’‘ Quiz

  • [ ] What does it mean to connect the user's wallet with our dapp?
  • [ ] When would you use testnet instead of devnet?
  • [ ] How can you support a new type of wallet?
  • [ ] Come up with an idea of what you could build in Solana!

πŸ† Achievement - Wallet Connection

Congratulations!! Users can now connect their wallets to our DApp!! πŸ’ͺ

We had to detour a bit with some React skills, but I hope you are as excited as I am because now that we can connect to the user's wallet, we can start interacting with Solana!

If you are ready to implement it into your project, there are starters examples for every sauce here -> https://github.com/solana-labs/wallet-adapter#starter-projects.

The final code is here: https://github.com/solana-labs/dapp-scaffold/commit/f4803f84096b39a1f088fa9301a99b42b7b66252

πŸŽ™ Optional: Your Turn To Get The Mic!

Can you figure out how to implement the auto connection of the wallet?


🎬 See You In Next Episode


Going Further

Where To Ask Questions?

How To Stay Up To Date With Solana?