Skip to main content
Learn how to create and manage Sub Accounts that provide app-specific wallet accounts embedded directly in your application.

Overview

Sub Accounts allow you to provision dedicated wallet accounts for your users within your application. These accounts are controlled by the user’s main Base Account and provide an enhanced user experience as the user’s interactions produce no passkey prompts or popups. Users can manage all their Sub Accounts at account.base.app.
If you would like to see a live demo of Sub Accounts in action, check out our Sub Accounts Demo.

What you’ll achieve

By the end of this guide, you will:
  • Understand how Sub Accounts work with Base Account
  • Create new Sub Accounts for users
  • Retrieve and manage existing Sub Accounts
  • Implement Sub Account in your Privy project
The code snippets in this guide are based on the following example project:
Base Account Privy Templatehttps://github.com/base/base-account-privy

Setup

Follow the Setup guide to set up Privy with Base Account.

Implementation

Component Setup

"use client";

import { useState, useMemo } from "react";
import { useWallets } from "@privy-io/react-auth";

const SubAccounts = () => {
  const { wallets } = useWallets();
  const [subAccounts, setSubAccounts] = useState<
    {
      address: string;
      factory: string;
      factoryData: string;
    }[]
  >([]);
  const [isLoading, setIsLoading] = useState(false);

  // Find the Base Account wallet
  const baseAccount = useMemo(() => {
    return wallets.find((wallet) => wallet.walletClientType === 'base_account');
  }, [wallets]);

  const handleGetSubAccounts = async () => {
    if (!baseAccount) return;

    setIsLoading(true);
    try {
      // Switch to Base Sepolia (or Base Mainnet - use 8453 for mainnet)
      await baseAccount.switchChain(84532);
      const provider = await baseAccount.getEthereumProvider();

      // Get existing Sub Accounts
      const response = await provider.request({
        method: 'wallet_getSubAccounts',
        params: [{
          account: baseAccount.address,
          domain: window.location.origin
        }]
      });

      const { subAccounts: existingSubAccounts } = response;
      setSubAccounts(existingSubAccounts || []);
    } catch (error) {
      console.error("Error getting Sub Accounts:", error);
    } finally {
      setIsLoading(false);
    }
  };

  const handleAddSubAccount = async () => {
    if (!baseAccount) return;

    setIsLoading(true);
    try {
      // Switch to Base Sepolia (or Base Mainnet - use 8453 for mainnet)
      await baseAccount.switchChain(84532);
      const provider = await baseAccount.getEthereumProvider();

      // Create new Sub Account
      await provider.request({
        method: 'wallet_addSubAccount',
        params: [{
          version: '1',
          account: {
            type: 'create',
            keys: [{
              type: 'address',
              publicKey: baseAccount.address
            }]
          }
        }]
      });

      // Refresh the Sub Accounts list
      await handleGetSubAccounts();
    } catch (error) {
      console.error("Error creating Sub Account:", error);
    } finally {
      setIsLoading(false);
    }
  };

  return (
    <div className="space-y-4">
      <div className="flex gap-4">
        <button 
          onClick={handleGetSubAccounts}
          disabled={!baseAccount || isLoading}
          className="px-4 py-2 bg-blue-600 text-white rounded disabled:opacity-50"
        >
          Get Sub Accounts
        </button>
        <button 
          onClick={handleAddSubAccount}
          disabled={!baseAccount || isLoading}
          className="px-4 py-2 bg-green-600 text-white rounded disabled:opacity-50"
        >
          Create Sub Account
        </button>
      </div>

      {subAccounts.length > 0 && (
        <div>
          <h4 className="font-medium mb-2">Existing Sub Accounts:</h4>
          <div className="space-y-2">
            {subAccounts.map((subAccount, index) => (
              <div key={index} className="p-3 border rounded-md">
                <p><strong>Address:</strong> {subAccount.address}</p>
                <p><strong>Factory:</strong> {subAccount.factory}</p>
                <p><strong>Factory Data:</strong> {subAccount.factoryData}</p>
              </div>
            ))}
          </div>
        </div>
      )}
    </div>
  );
};

Key Methods

Getting Sub Accounts

Use wallet_getSubAccounts to retrieve existing Sub Accounts for a domain:
Get Sub Accounts (components/sections/sub-accounts.tsx)
const response = await provider.request({
  method: 'wallet_getSubAccounts',
  params: [{
    account: baseAccount.address,
    domain: window.location.origin
  }]
});

Creating Sub Accounts

Use wallet_addSubAccount to create new Sub Accounts:
Create Sub Account (components/sections/sub-accounts.tsx)
await provider.request({
  method: 'wallet_addSubAccount',
  params: [{
    version: '1',
    account: {
      type: 'create',
      keys: [{
        type: 'address',
        publicKey: baseAccount.address
      }]
    }
  }]
});

Network Configuration

Sub accounts work on both Base Mainnet and Base Sepolia:
  • Base Mainnet: Chain ID 8453
  • Base Sepolia: Chain ID 84532

Explore further

I