Getting Started with Local Wallet

Learn what a local wallet is, what its use cases are, and how you can use them to create invisible wallet experiences for users.

Getting Started with Local Wallet

In this guide, you will learn how to integrate a local wallet into your own projects to create a simple "guest checkout" experience for your users. One of the major issues that dApps face when onboarding users is that they require the user to have a preexisting externally owned account, otherwise known as an EOA. Local wallet solves this issue by automatically generating a wallet for each user upon selecting the "Local Wallet" option from the connect wallet button.

Local wallet option inside the Connect Wallet button

View the full source code for how to create a local wallet in a React app or script on GitHub.

What is a Local Wallet?

A local wallet is a low-level wallet that allows you to create wallets within your application or project. It is a non-custodial solution that simplifies the onboarding process by offering a "checkout as guest" experience. This improves the user experience for web3 apps in two ways:

  1. It enables non-web3 native users to get started easily without having to create a wallet with an external provider like MetaMask.
  2. It hides transaction confirmations from users. With a local wallet, you can generate wallets on the backend. These wallets are app-scoped meaning that each user has one wallet per app.

Additionally, the local wallet has UI functionality, available within the React and React Native SDKs, that allows users to export and optionally store the encrypted key for the local wallet on their browser, mobile, or other devices.

Use cases for Local Wallets

The local wallet is a low-level wallet, so can be modified to suit the use case making it extremely versatile. Here are a few interesting use cases for the local wallet:

  • Guest checkout experience for users who do not own a wallet.
  • As an admin wallet for a Smart Wallet.
  • Creating your own MetaMask or Coinbase Wallet EOA app.
  • Deploy & interact with contracts using scripts without having to use a private key.
  • Automatically generating a wallet for every user to conceal the intricacies of the wallet feature of web3 applications, such as web3 games or commerce sites.

How do Local Wallets work?

Let's imagine you are using a local wallet as part of your web3 application. When a user interacts with your app, they will need already to have their own EOA to connect to the app and sign transactions. This creates a barrier to onboarding new users to your application, particularly for users who are not familiar with Web3 user flows, such as connecting a wallet, signing transactions, and obtaining funds. Local wallet provides the solution by creating a wallet for your users in a way that works for your application by either allowing them to create a "guest" wallet or by creating a local wallet for each user automatically upon visiting the app.

Using Local Wallet in a Front End

Using a local wallet as an option to allow your users to connect to your front end is simple using our Connect Wallet UI component.

💡
An API key is required to use thirdweb's infrastructure services, such as storage, RPCs, and Smart Wallet infrastructure from within the SDK. If you haven't created a key yet, you can do so for free from the thirdweb dashboard. To use an API key with the React SDK, pass the clientId to the ThirdwebProvider.

To add a local wallet as an option, all you need to do is import localWallet from the React SDK package and provide it to the supportedWallets object in the ThirdwebProvider component in your app.ts file as follows:

import type { AppProps } from "next/app";
import { ThirdwebProvider, localWallet } from "@thirdweb-dev/react";
import "../styles/globals.css";

// This is the chain your dApp will work on.
// Change this to the chain your app is built for.
// You can also import additional chains from `@thirdweb-dev/chains` and pass them directly.
const activeChain = "mumbai";

function MyApp({ Component, pageProps }: AppProps) {
  return (
    <ThirdwebProvider
      clientId={process.env.CLIENT_ID}
      activeChain={activeChain}
      supportedWallets={[localWallet()]}
    >
      <Component {...pageProps} />
    </ThirdwebProvider>
  );
}

export default MyApp;

Here, we have pre-configured all of the settings for your local wallet. This includes setting the connector and the storage for the wallet's password and private key, which is automatically set to be the browser credential storage i.e., your browser keychain. The setting can be configured for your use case, more details of which can be found in the local wallet docs.

Combining the local wallet with gasless transactions can further improve your application's UX by not requiring that users have funds in their newly created wallet. To set up gasless transactions, set up either a Biconomy or OpenZeppelin Defender account and follow one of these guides:

Create an NFT Drop with Gasless Transactions using Biconomy
Learn How To Create A Gasless NFT Drop Using Biconomy and thirdweb’s React SDK Where Your Users Pay 0 Gas Fees For Minting.
Set Up Gasless Transactions with OpenZeppelin Defender
Learn How To Set Up Gasless Transactions Using OpenZeppelin Defender To Cover the Gas Prices For Your Users When Minting NFTs.

Once you have set up your relayer, add the gasless option to the ThirdwebProvider along with the localWallet:

import type { AppProps } from "next/app";
import { ThirdwebProvider, localWallet } from "@thirdweb-dev/react";
import "../styles/globals.css";

// This is the chain your dApp will work on.
// Change this to the chain your app is built for.
// You can also import additional chains from `@thirdweb-dev/chains` and pass them directly.
const activeChain = "mumbai";

function MyApp({ Component, pageProps }: AppProps) {
  return (
    <ThirdwebProvider
      clientId={process.env.CLIENT_ID}
      activeChain={activeChain}
      supportedWallets={[localWallet()]}
      sdkOptions={{
        gasless: {
          // By specifying a gasless configuration - all transactions will get forwarded to enable gasless transactions
          biconomy: {
            apiId: "biconomy-api-id", // your Biconomy API Id
            apiKey: "biconomy-api-key", // your Biconomy API Key
            deadlineSeconds: 123, // your Biconomy timeout preference
          },
        },
      }}
    >
      <Component {...pageProps} />
    </ThirdwebProvider>
  );
}

export default MyApp;

Finally, in index.tsx add a ConnectWallet button, making sure you import it from the React SDK package, and local wallet will now appear as an option!

import { ConnectWallet } from "@thirdweb-dev/react";
import type { NextPage } from "next";
import styles from "../styles/Home.module.css";

const Home: NextPage = () => {
  return (
    <div className={styles.container}>
      <div className={styles.connect}>
        <ConnectWallet />
      </div>
    </div>
  );
};

export default Home;
Create a password for your local wallet from the connect wallet button UI

Using Local Wallet in a Script or Backend

To use a local wallet in a node script, we need to import LocalWallet from the Wallet SDK package. Again, the settings have all been preconfigured to keep this simple. But, if you wish to override these defaults, details of the configurable settings can be found in the local wallets docs.

Let's use a local wallet to deploy a prebuilt contract, an NFT drop, and create (or lazy mint) a batch of NFTs.

In this example, we will set up gasless transactions so that we do not need to send funds to our newly created wallet, requiring us to use another wallet or faucet. To setup gasless transactions, follow this guide, making sure to set up your relayer on your desired chain and fund your relayer sufficiently:

Set Up Gasless Transactions with OpenZeppelin Defender
Learn How To Set Up Gasless Transactions Using OpenZeppelin Defender To Cover the Gas Prices For Your Users When Minting NFTs.

Firstly, we need to set up a LocalWallet instance on the desired chain, in this example Mumbai, which we will need to import from the @thirdweb-dev/chains package.

We then need to call the generate function on the wallet object to create an instance of the wallet. generate returns a Promise object which resolves to a string containing the newly created wallet address.

Finally, we need to call the method connect to connect our new wallet to our application:

import { Mumbai } from "@thirdweb-dev/chains";
import { LocalWallet } from "@thirdweb-dev/wallets";

const main = async () => {
  const wallet = new LocalWallet({
    chain: Mumbai,
  });
  const walletAddress = await wallet.generate();
  await wallet.connect();
  console.log("Wallet address: ", walletAddress);
};

main();
💡
Again, an API key is required to use thirdweb's infrastructure services, such as storage, RPCs, and Smart Wallet infrastructure from within the Typescript SDK. If you haven't created a key yet, you can do so for free from the thirdweb dashboard.

To use an API key with the Typescript SDK in a backend, pass the secretKey to the ThirdwebSDK.

Now that we have a local wallet set up, we need to create a ThirdwebSDK instance, using the  ThirdwebSDK.fromWallet method:

import { ThirdwebSDK } from "@thirdweb-dev/sdk";

 const sdk = await ThirdwebSDK.fromWallet(wallet, "mumbai",{
   secretKey: process.env.TW_SECRET_KEY
   gasless: {
      // By specifying a gasless configuration - all transactions will get forwarded to enable gasless transactions
     openzeppelin: {
       relayerUrl: "<your-relayer-url>", // your OZ Defender relayer URL
     },
   },
 }
 );

To setup gasless transactions to avoid having to send funds to your local wallet, paste in the relayer URL that you created earlier, into the relayerUrl option, as a string. Again, in this example, we are using OpenZeppelin Defender to relay the transactions, but you could also use Biconomy.

Now that we have an SDK instance, we can deploy an NFT Drop contract by calling deployBuiltInContract

const contractAddress = await sdk.deployer.deployBuiltInContract("nft-drop", {
   name: "My Drop",
   primary_sale_recipient: walletAddress,
 });

 console.log("Contract address: ", contractAddress);

Finally, let's batch-mint some NFTs using createBatch:

import { readFileSync } from "fs";

const metadatas = [
    {
      name: "Blue Star",
      description: "A blue star NFT",
      image: readFileSync("assets/blue-star.png"),
    },
    {
      name: "Red Star",
      description: "A red star NFT",
      image: readFileSync("assets/red-star.png"),
    },
    {
      name: "Yellow Star",
      description: "A yellow star NFT",
      image: readFileSync("assets/yellow-star.png"),
    },
  ];

  await contract.createBatch(metadatas);
  console.log("Created batch successfully!");

Run this code using the command node index.mjs and you should see the following output:

We can now view this contract on the Dashboard by searching for the contract address in the search bar and importing the contract to your Dashboard:

Now, you will be able to view the NFTs that we batch-minted!

Amazing! We have successfully used a local wallet to deploy and interact with a contract from a node script gaslessly!

💡
If you are deploying a contract that requires access to the admin account, such as performing transactions with permissions at a later date, make sure to save your local wallet to storage using the method save which will save the wallet to storage as either an encrypted JSON object, a mnemonic, or a private key.

To use that same wallet again in a future script, use the load method to initialize the wallet from storage.

Wrapping Up

In this guide, we have learned what a local wallet is and how to use it in two examples:

  1. A front-end application to create a "checkout as guest" experience.
  2. A backend application to gaslessly deploy and interact with a contract without having to use a private key or create an EOA and obtain funds but instead using a temporary, local wallet.

If you have any questions jump into the thirdweb Discord and join 32,000+ other builders! Please let us know if you have any feature requests or guide requests here.