2.8.1 • Published 30 days ago

@txnlab/use-wallet v2.8.1

Weekly downloads
-
License
MIT
Repository
github
Last release
30 days ago

@TxnLab/use-wallet

React hooks for using Algorand compatible wallets with web applications. Flexible and extensible, use-wallet supports a variety of wallets and connection protocols. It also provides a simple interface for connecting, disconnecting, switching between accounts and signing transactions.

Supported Providers

Demo

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

Quick Start

⚠️ If you're using create-react-app and 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 algorand-walletconnect-qrcode-modal @json-rpc-tools/utils @daffiwallet/connect

NPM

npm install @txnlab/use-wallet

Install peer dependencies (if needed)

npm install algosdk @blockshake/defly-connect @perawallet/connect @randlabs/myalgo-connect @walletconnect/client algorand-walletconnect-qrcode-modal @json-rpc-tools/utils @daffiwallet/connect

Set up the Wallet Provider

In app.js, initialize the Wallet Provider so that the useWallet hook can be used in the child components, and use the reconnectProviders function to restore sessions for users returning to the app.

import React from 'react'
import { reconnectProviders, initializeProviders, WalletProvider } from '@txnlab/use-wallet'

const walletProviders = initializeProviders()

export default function App() {
  // Reconnect the session when the user returns to the dApp
  React.useEffect(() => {
    reconnectProviders(walletProviders)
  }, [])

  return <WalletProvider value={walletProviders}>...</WalletProvider>
}

The reconnectProviders function is used to restore session states of wallets that rely on the WalletConnect protocol.

By default, all of the supported providers except for KMD are returned by useConnectWallet. An array can be passed to initializeProviders to determine which providers your dApp supports, as shown below.

import { initializeProviders, PROVIDER_ID } from '@txnlab/use-wallet'

const walletProviders = initializeProviders([PROVIDER_ID.KMD_WALLET, PROVIDER_ID.WALLET_CONNECT])

For more configuration options, see Provider Configuration.

Connect

Map through the providers object to list the providers and enable users to connect.

import React from 'react'
import { useWallet } from '@txnlab/use-wallet'

export default function Connect() {
  const { providers, activeAccount } = useWallet()

  // Map through the providers.
  // Render account information and "connect", "set active", and "disconnect" buttons.
  // Finally, map through the `accounts` property to render a dropdown for each connected account.
  return (
    <div>
      {providers?.map((provider) => (
        <div key={'provider-' + provider.metadata.id}>
          <h4>
            <img width={30} height={30} alt="" src={provider.metadata.icon} />
            {provider.metadata.name} {provider.isActive && '[active]'}
          </h4>
          <div>
            <button onClick={provider.connect} disabled={provider.isConnected}>
              Connect
            </button>
            <button onClick={provider.disconnect} disabled={!provider.isConnected}>
              Disconnect
            </button>
            <button
              onClick={provider.setActiveProvider}
              disabled={!provider.isConnected || provider.isActive}
            >
              Set Active
            </button>
            <div>
              {provider.isActive && provider.accounts.length && (
                <select
                  value={activeAccount?.address}
                  onChange={(e) => provider.setActiveAccount(e.target.value)}
                >
                  {provider.accounts.map((account) => (
                    <option key={account.address} value={account.address}>
                      {account.address}
                    </option>
                  ))}
                </select>
              )}
            </div>
          </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.

The activeAccount is the primary account that is currently active and will be used to sign and send transactions.

Sign and send transactions

Construct a transaction using algosdk, and sign and send the transaction using the signTransactions and sendTransactions functions provided by the useWallet hook.

import React from 'react'
import {
  useWallet,
  DEFAULT_NODE_BASEURL,
  DEFAULT_NODE_TOKEN,
  DEFAULT_NODE_PORT
} from '@txnlab/use-wallet'
import algosdk from 'algosdk'

const algodClient = new algosdk.Algodv2(DEFAULT_NODE_TOKEN, DEFAULT_NODE_BASEURL, DEFAULT_NODE_PORT)

export default function Transact() {
  const { activeAddress, signTransactions, sendTransactions } = useWallet()

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

    const suggestedParams = await algodClient.getTransactionParams().do()

    const transaction = algosdk.makePaymentTxnWithSuggestedParamsFromObject({
      from,
      to,
      amount,
      suggestedParams
    })

    const encodedTransaction = algosdk.encodeUnsignedTransaction(transaction)

    const signedTransactions = await signTransactions([encodedTransaction])

    const waitRoundsToConfirm = 4

    const { id } = await sendTransactions(signedTransactions, waitRoundsToConfirm)

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

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

  return (
    <div>
      <button
        onClick={() => sendTransaction(activeAddress, activeAddress, 1000)}
        className="button"
      >
        Sign and send transactions
      </button>
    </div>
  )
}

Display account details

The activeAccount object can be used to display details for the currently active account. For convenience, the activeAddress property shows the currently active address.

import React from 'react'
import { useWallet } from '@txnlab/use-wallet'

export default function Account() {
  const { activeAccount } = useWallet()

  if (!activeAccount) {
    return <p>No account active.</p>
  }

  return (
    <div>
      <h4>Active Account</h4>
      <p>
        Name: <span>{activeAccount.name}</span>
      </p>
      <p>
        Address: <span>{activeAccount.address}</span>
      </p>
      <p>
        Provider: <span>{activeAccount.providerId}</span>
      </p>
    </div>
  )
}

Check connection status

The isActive and isReady properties can be used to check the status of the wallets. The isActive property determines whether or not an account is currently active. The isReady property shows if use-wallet has mounted and successfully read the connection status from the providers. These properties are useful when setting up client side access restrictions, for example, by redirecting a user if no wallet is active, as shown below.

const { isActive, isReady } = useWallet()

useEffect(() => {
  if (isReady && isActive) {
    allowAccess()
  }

  if (isReady && !isActive) {
    denyAccess()
  }
})

Provider Configuration

The initializeProviders functon accepts a configuration object that can be used to configure the nodes that the providers use to send transactions, as shown below.

const walletProviders = initializeProviders([], {
  network: 'devmodenet',
  nodeServer: 'http://algod',
  nodeToken: 'xxxxxxxxx',
  nodePort: '8080'
})

Passing an empty array as the first argument enables all of the default providers. The network property should be specified as betanet, testnet, mainnet or the name of your local development network.

For more custom configuration options, the providers can be configured individually by creating an object and passing it to the WalletProvider where the key contains the provider ID, and the value calls the init function of the provider client. See below for an example:

...

import {
  PROVIDER_ID,
  pera,
  myalgo,
} from "@txnlab/use-wallet";

const walletProviders = {
  [PROVIDER_ID.PERA]: pera.init({
    clientOptions: {
      shouldShowSignTxnToast: true,
    },
  }),
  [PROVIDER_ID.MYALGO]: myalgo.init({
    network: "devmodenet",
    algodOptions: ["xxxxxxxxx", "http://algod", "8080"],
    clientOptions: { disableLedgerNano: true },
  }),
};

...

<WalletProvider value={walletProviders}>
  ...
</WalletProvider>

Static Imports

By default, use-wallet dynamically imports all of the dependencies for the providiers, as well as algosdk, to reduce bundle size.

Some React frameworks, like Remix, do not support dynamic imports. To get around this, those dependencies can be imported in your application and passed to the useWallet provider. See below for an example.

...

import algosdk from "algosdk";
import MyAlgoConnect from "@randlabs/myalgo-connect";
import { PeraWalletConnect } from "@perawallet/connect";
import { DeflyWalletConnect } from "@blockshake/defly-connect";
import WalletConnect from "@walletconnect/client";
import QRCodeModal from "algorand-walletconnect-qrcode-modal";

const walletProviders = {
  [PROVIDER_ID.PERA]: pera.init({
    algosdkStatic: algosdk,
    clientStatic: PeraWalletConnect,
  }),
  [PROVIDER_ID.MYALGO]: myalgo.init({
    algosdkStatic: algosdk,
    clientStatic: MyAlgoConnect,
  }),
  [PROVIDER_ID.DEFLY]: defly.init({
    algosdkStatic: algosdk,
    clientStatic: DeflyWalletConnect,
  }),
  [PROVIDER_ID.EXODUS]: exodus.init({
    algosdkStatic: algosdk,
  }),
  [PROVIDER_ID.ALGOSIGNER]: algosigner.init({
    algosdkStatic: algosdk,
  }),
  [PROVIDER_ID.WALLETCONNECT]: walletconnect.init({
    algosdkStatic: algosdk,
    clientStatic: WalletConnect,
    modalStatic: QRCodeModal,
  }),
};

export default function App() {
  ...

  return (
    <WalletProvider value={walletProviders}>
      ...
    </WalletProvider>
  );
}

Note that some of the providers do not require static imports to be provided. This is usually the case of providers that are browser extensions.

Local Development

Install dependencies

yarn install

Demo in Storybook

yarn dev

To develop against a local version of use-wallet in your application, do the following:

Build the library

yarn build

Symlink the library

In the root of use-wallet directory, run:

yarn link

In the root of your application, run:

yarn link @txnlab/use-wallet

Symlink React

In the root of your application, run:

cd node_modules/react
yarn link

In the root of use-wallet directory, run:

yarn link react

Used By

Are you using @txnlab/use-wallet? We'd love to include you here. Let us know! Twitter | Discord | Email

Full list of Dependents

License

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

3.0.0-beta.5

30 days ago

3.0.0-beta.4

1 month ago

2.8.1

1 month ago

3.0.0-beta.3

1 month ago

3.0.0-beta.2

1 month ago

2.8.0

1 month ago

3.0.0-beta.1

1 month ago

2.7.0

3 months ago

2.6.1

3 months ago

2.6.0

3 months ago

2.6.2

3 months ago

2.5.0

4 months ago

2.4.0

4 months ago

2.3.1

5 months ago

2.3.0

5 months ago

2.2.0

7 months ago

2.1.2

8 months ago

2.1.1

9 months ago

2.1.3

8 months ago

2.1.0

9 months ago

2.0.0-alpha.5

11 months ago

2.0.0

11 months ago

2.0.0-rc.1

11 months ago

1.3.3

11 months ago

2.0.0-alpha.3

11 months ago

2.0.0-alpha.4

11 months ago

2.0.0-alpha.0

11 months ago

2.0.0-alpha.1

11 months ago

2.0.0-alpha.2

11 months ago

1.3.2

11 months ago

1.3.1

11 months ago

1.3.1-alpha.1

11 months ago

1.3.1-alpha.0

11 months ago

1.3.0

1 year ago

1.2.12

1 year ago

1.3.0-alpha.0

1 year ago

1.2.8

1 year ago

1.2.7

1 year ago

1.2.6

1 year ago

1.2.5

1 year ago

1.2.4

1 year ago

1.2.3

1 year ago

1.2.9

1 year ago

1.2.10

1 year ago

1.2.11

1 year ago

1.2.2

1 year ago

1.2.0

1 year ago

1.0.2

1 year ago

1.0.1

1 year ago

1.0.0

1 year ago

1.1.6

1 year ago

1.1.5

1 year ago

1.0.5

1 year ago

1.0.4

1 year ago

1.2.1

1 year ago

1.0.3

1 year ago

0.1.23

1 year ago

0.1.22

2 years ago

0.1.21

2 years ago

0.1.20

2 years ago

0.1.19

2 years ago

0.1.18

2 years ago

0.1.17

2 years ago

0.1.16

2 years ago

0.1.15

2 years ago

0.1.14

2 years ago

0.1.13

2 years ago

0.1.2

2 years ago

0.1.1

2 years ago

0.1.0

2 years ago

0.0.9

2 years ago

0.0.8

2 years ago

0.0.7

2 years ago

0.0.6

2 years ago

0.0.5

2 years ago

0.0.4

2 years ago

0.0.3

2 years ago

0.0.2

2 years ago

0.0.1

2 years ago