Skip to content

Using smart wallets ​

Once you have configured smart wallets in the Privy Dashboard, you can use them in your application with just a few lines of code.

INFO

Looking to get started quickly? Check out our Smart Wallets starter repo

Setup ​

To set up your app with smart wallets, first import the SmartWalletsProvider component from @privy-io/react-auth/smart-wallets and wrap your app with it.

TIP

Depending on your setup, you may need to update moduleResolution in your tsconfig.json to be "Bundler" and module to be a valid module type like "ESNext" or "Node". This is necessary to ensure that the SmartWalletsProvider can be imported correctly.

json
{
  "compilerOptions": {
    "module": "ESNext",
    "moduleResolution": "Bundler",
    ...the rest of your compilerOptions
  }
  ...the rest of your tsconfig.json
}

The SmartWalletsProvider must wrap any component or page that will use smart wallets. We recommend rendering it as close to the root of your application as possible, nested within your PrivyProvider.

tsx
import {PrivyProvider} from '@privy-io/react-auth';
import {SmartWalletsProvider} from '@privy-io/react-auth/smart-wallets';

export default function Providers({children}: {children: React.ReactNode}) {
  return (
    <PrivyProvider appId="your-privy-app-id">
      <SmartWalletsProvider>
        {children}
      </SmartWalletsProvider>
    </PrivyProvider>
  );
}

TIP

Make sure that the networks you've configured for smart wallets in the Dashboard are also configured for your app's defaultChain and supportedChains.

Creating smart wallets ​

Once the SmartWalletsProvider component is rendered and a smart wallet configuration has been set up for your app in the Dashboard, Privy will automatically generate smart wallets for your users once they have an embedded wallet. The embedded wallet is used as the primary signer controlling the smart wallet.

You can configure your app to create embedded wallets automatically on login or manually; smart wallets will be created following the same configuration.

Getting the address ​

Once a smart wallet has been created for a user, you can get the address for the smart wallet by finding the account of type: 'smart_wallet' from the user's linkedAccounts array, and inspecting the entry's address field like so:

tsx
const {user} = usePrivy();
const smartWallet = user.linkedAccounts.find((account) => account.type === 'smart_wallet');
console.log(smartWallet.address);
// Logs the smart wallet's address
console.log(smartWallet.type);
// Logs the smart wallet type (e.g. 'safe', 'kernel', 'light_account', 'biconomy')

Signatures and transactions ​

To use the smart wallet to sign messages and send transactions, import the useSmartWallets hook and use the client returned by the hook. This client is a drop-in replacement for a viem WalletClient and supports many of the same methods, including signMessage, signTypedData, and sendTransaction.

tsx
import {useSmartWallets} from '@privy-io/react-auth/smart-wallets';
...
const {client} = useSmartWallets();

Signing messages ​

To sign messages with the smart wallet, simply call the client's signMessage or signTypedData method like so:

tsx
const {client} = useSmartWallets();
const signature = await client.signMessage({
  account: client.account,
  message: 'Hello world',
});

Sending transactions ​

To send transactions with the smart wallet, call the client's sendTransaction method with your desired transaction request. If your app has a paymaster URL registered in the Dashboard, Privy will automatically use that paymaster to attempt to sponsor the gas fees for the user's transaction.

tsx
const {client} = useSmartWallets();
const txHash = await client.sendTransaction({
  account: client.account,
  chain: base,
  to: 'insert-recipient-address',
  value: 0.1,
});

Batching transactions ​

Smart wallets support sending a batch of transactions in a single, atomic submission to the network.

To send a batched transactions with a smart wallet, call the client's sendTransaction method with a calls array the transactions to batch together. Each call may have the following fields:

FieldTypeDescription
tostringThe recipient of the transaction or the address of the smart contract being called.
valuebigintThe value in wei for the transaction.
datastringEncoded calldata for the transaction, if calling a smart contract. We suggest using viem's encodeFunctionData to prepare this value.

As an example, you might batch together a transaction to approve a USDC spender and to transfer USDC like so:

tsx
const {client} = useSmartWallets();
const txHash = await client.sendTransaction({
  account: client.account,
  calls: [
    // Approve transaction
    {
      to: USDC_ADDRESS,
      data: encodeFunctionData({
        abi: USDC_ABI,
        functionName: 'approve',
        args: ['insert-spender-address', BigInt(1e6)],
      }),
    },
    // Transfer transaction
    {
      to: USDC_ADDRESS,
      data: encodeFunctionData({
        abi: USDC_ABI,
        functionName: 'transfer',
        args: ['insert-recipient-address', BigInt(1e6)],
      }),
    },
  ],
});

Switching chains ​

Privy's smart wallets support switching between chains. To switch chains, call the client's switchChain method with the chain ID you wish to switch to.

tsx
import {base} from 'viem/chains';
const {client} = useSmartWallets();
await client.switchChain({
  id: base.id,
});
// Client will send transaction on Base
client.sendTransaction({
  ...
});

INFO

Remember to set up a smart wallet network configuration for each chain your app supports.

TIP

If configured defaultChain does not have a smart wallet network configuration, the smart wallet client will default to using the first configured chain that has a smart wallet network configuration.

Overriding paymaster context ​

Certain paymasters, like Alchemy and Biconomy, use an additional paymasterContext for gas sponsorship. Privy constructs this paymaster context based on either dashboard provided gas policy ID for Alchemy or a default set of values for Biconomy. However, you can override these defaults by passing a paymasterContext prop to the SmartWalletsProvider. See an example of how to set this below:

tsx
<SmartWalletsProvider
  config={{
    paymasterContext: {
      mode: 'SPONSORED',
      calculateGasLimits: true,
      expiryDuration: 300,
      sponsorshipInfo: {
        webhookData: {},
        smartAccountInfo: {
          name: 'BICONOMY',
          version: '2.0.0',
        },
      },
    },
  }}
>
  {children}
</SmartWalletsProvider>