React hooks for using Algorand compatible wallets in dApps

@TxnLab/use-wallet

React hooks for using Algorand compatible wallets with web applications.

Demo

Preview a basic implementation in Storybook or check out this example.

Quick Start

⚠️ If you’re using Webpack 5 (most newer React projects), you will need to install polyfills. Follow these directions.

Yarn

yarn add @txnlab/use-wallet

Install peer dependencies (if needed)

yarn add algosdk @blockshake/defly-connect @perawallet/connect @randlabs/myalgo-connect @walletconnect/client

NPM

npm install @txnlab/use-wallet

Install peer dependencies (if needed)

npm install algosdk @blockshake/defly-connect @perawallet/connect @randlabs/myalgo-connect @walletconnect/client

Set up the wallet providers

import React from "react";
import { useConnectWallet } from "@txnlab/use-wallet";

function App() {
  const { providers, reconnectProviders, accounts, activeAccount } =
    useConnectWallet();

  // Reconnect the session when the user returns to the dApp
  React.useEffect(() => {
    reconnectProviders();
  }, []);

  // Use these properties to display connected accounts to users.
  // They are reactive and presisted to local storage.
  React.useEffect(() => {
    console.log("connected accounts", accounts);
    console.log("active account", activeAccount);
  });

  // Map through the providers, and render account information and "connect", "set active", and "disconnect" buttons
  return (
    <div>
      {providers.map((provider) => (
        <div key={"provider-" + provider.id}>
          <h4>
            <img width={30} height={30} src={provider.icon} />
            {provider.name} {provider.isActive && "[active]"}
          </h4>
          <div>
            <button onClick={provider.connect} disabled={provider.isConnected}>
              Connect
            </button>
            <button onClick={provider.disconnect} disabled={!provider.isConnected}>
              Disonnect
            </button>
            <button onClick={provider.setActive} disabled={!provider.isConnected || provider.isActive}>
              Set Active
            </button>
          </div>
        </div>
      ))}
    </div>
  );
}

Each provider has two connection states: isConnected and isActive.

isConnected means that the user has authorized the provider to talk to the dApp. The connection flow does not need to be restarted when switching to this wallet from a different one.

isActive indicates that the provider is currently active and will be used to sign and send transactions when using the useWallet hook.

Sign and send transactions

function Wallet() {
  const { activeAccount, signTransactions, sendTransactions } = useWallet();

  const sendTransaction = async (
    from?: string,
    to?: string,
    amount?: number
  ) => {
    if (!from || !to || !amount) {
      throw new Error("Missing transaction params.");
    }

    const params = await algodClient.getTransactionParams().do();

  // Construct a transaction to be signed and sent. 
    const transaction = algosdk.makePaymentTxnWithSuggestedParamsFromObject({
      from,
      to,
      amount,
      suggestedParams: params,
    });

    // Encode the transactions into a byte array.
    const encodedTransaction = algosdk.encodeUnsignedTransaction(transaction);

    // Sign the transactions.
    const signedTransactions = await signTransactions([encodedTransaction]);

    // Send the transactions.
    const { id } = await sendTransactions(signedTransactions);

    console.log("Successfully sent transaction. Transaction ID: ", id);
  };

  if (!activeAccount) {
    return <p>Connect an account first.</p>;
  }

  return (
    <div>
      {
        <button
          onClick={() =>
            sendTransaction(
              activeAccount?.address,
              activeAccount?.address,
              1000
            )
          }
          className="button"
        >
          Sign and send transactions
        </button>
      }
    </div>
  );
};

Webpack 5

  1. Install react-app-rewired and the missing polyfills.

    yarn add --dev react-app-rewired crypto-browserify stream-browserify assert stream-http https-browserify os-browserify url buffer process
  2. Create config-overrides.js in the root of your project and add the following:

    const webpack = require("webpack");
    
    module.exports = function override(config) {
      const fallback = config.resolve.fallback || {};
      Object.assign(fallback, {
        crypto: require.resolve("crypto-browserify"),
        stream: require.resolve("stream-browserify"),
        assert: require.resolve("assert"),
        http: require.resolve("stream-http"),
        https: require.resolve("https-browserify"),
        os: require.resolve("os-browserify"),
        url: require.resolve("url"),
      });
      config.resolve.fallback = fallback;
      config.plugins = (config.plugins || []).concat([
        new webpack.ProvidePlugin({
          process: "process/browser",
          Buffer: ["buffer", "Buffer"],
        }),
      ]);
      return config;
    };
  3. Change your scripts in package.json to the following:

    "scripts": {
        "start": "react-app-rewired start",
        "build": "react-app-rewired build",
        "test": "react-app-rewired test",
        "eject": "react-scripts eject"
    },

Local Development

Install dependencies

yarn install

Demo in Storybook

yarn storybook

Build the library

yarn build

License

See the LICENSE file for license rights and limitations (MIT)

GitHub

View Github