React hooks for using Algorand compatible wallets with web applications.
Preview a basic implementation in Storybook or check out this example.
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
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
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.
// 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.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>
{provider.isActive && provider.accounts.length && (
<select
value={provider.activeAccount?.address}
onChange={(e) => provider.selectAccount(e.target.value)}
>
{provider.accounts.map((account) => (
<option value={account.address}>{account.address}</option>
))}
</select>
)}
</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.
By default, all of the supported providers except for KMD
are returned by useConnectWallet
. A configuration object can be passed to determine which providers your dApp suports, as shown below.
import { useConnectWallet, PROVIDER_ID } from "@txnlab/use-wallet";
...
const { providers } = useConnectWallet({
providers: [
PROVIDER_ID.MYALGO_WALLET,
PROVIDER_ID.PERA_WALLET,
PROVIDER_ID.KMD_WALLET,
],
});
import React from "react";
import { useWallet } from "@txnlab/use-wallet";
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>
);
};
By default, wallets will connect to Algorand MainNet. You can change this behavior by setting the following environment variables:
-
NODE_NETWORK
(defaults tomainnet
, and can be set totestnet
,betanet
, or the name of a local network running in dev mode) -
NODE_SERVER
-
NODE_TOKEN
-
NODE_PORT
Example .env
file:
NODE_NETWORK=devmodenet
NODE_SERVER=http://algod
NODE_TOKEN=xxxxxxxxx
NODE_PORT=8080
In Create React App and Next.js projects, you'll need to add a prefix to these environment variables to expose them to the browser.
-
REACT_APP_
in Create React App -
NEXT_PUBLIC_
in Next.js
Example .env
file in Create React App:
REACT_APP_NODE_NETWORK=devmodenet
REACT_APP_NODE_SERVER=http://algod
REACT_APP_NODE_TOKEN=xxxxxxxxx
REACT_APP_NODE_PORT=8080
-
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
-
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; };
-
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" },
yarn install
yarn storybook
yarn build
See the LICENSE file for license rights and limitations (MIT)