Overview

This guide shows how to integrate Privy authentication and embedded wallets with Crossmint Smart Wallets. Users authenticate with Privy, and their Privy embedded wallet serves as a signer for a Crossmint Smart Wallet.

1

Create an API Key

The first step for integrating Privy authentication is to obtain a client-side API key.

Navigate to the "Integrate" section on the left navigation bar, and ensure you're on the "API Keys" tab.

Within the Client-side keys section, click the "Create new key" button in the top right.

On the authorized origins section, enter http://localhost:3000 and click "Add origin".

Next, check the scopes labeled wallets.create, wallets.read, users.create.

Check the "JWT Auth" box.

Finally, create your key and save it for subsequent steps.

2

Configure Privy Authentication for your API Key

After the key is generated, you need to set it to require Privy auth tokens for authorization.

  1. Go to the Privy dashboard at https://dashboard.privy.io/
  2. Select an existing project or create a new one

When you’re ready to move to production/mainnet with Privy, you will need to complete the “Upgrade to production” steps in the Privy dashboard (Settings > Basic > Application state).

  1. Select the “Settings” link from left-hand navigation to find your Privy App ID.
  1. Copy the Privy App ID and return to the Crossmint developer console
  2. Within the JWT authentication section, select “3P Auth providers” option, and then select Privy
  3. Add your App ID and click the “Save JWT auth settings” button
3

Install Required Dependencies

Install the Crossmint React UI SDK and shadcn UI components:

npm install @crossmint/client-sdk-react-ui @privy-io/react-auth

Then, set up shadcn UI components:

npx shadcn-ui@latest init -d

Install the button and card components:

npx shadcn-ui@latest add button card
4

Configure Environment Variables

Add your Crossmint client-side API key and Privy App ID to your environment file:

.env.local
NEXT_PUBLIC_PRIVY_APP_ID=_YOUR_PRIVY_APP_ID_
NEXT_PUBLIC_CROSSMINT_API_KEY=_YOUR_CROSSMINT_API_KEY_
5

Create the Provider Components

First, create a CrossmintPrivyProvider component that will wrap your application:

components/providers/CrossmintPrivyProvider.tsx
'use client';

import {
  CrossmintProvider,
  CrossmintWalletProvider,
} from '@crossmint/client-sdk-react-ui';
import { PrivyProvider } from '@privy-io/react-auth';

const privyAppId = process.env.NEXT_PUBLIC_PRIVY_APP_ID ?? '';
const crossmintApiKey = process.env.NEXT_PUBLIC_CROSSMINT_API_KEY ?? '';

if (!privyAppId || !crossmintApiKey) {
  throw new Error(
    'NEXT_PUBLIC_PRIVY_APP_ID or NEXT_PUBLIC_CROSSMINT_API_KEY is not set'
  );
}

export default function CrossmintPrivyProvider({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <PrivyProvider
      appId={privyAppId}
      config={{
        embeddedWallets: {
          createOnLogin: 'all-users', // Privy Embedded Wallets as signers
        },
      }}>
      <CrossmintProvider apiKey={crossmintApiKey}>
        <CrossmintWalletProvider defaultChain="polygon-amoy">
          {children}
        </CrossmintWalletProvider>
      </CrossmintProvider>
    </PrivyProvider>
  );
}

Now create a custom hook that connects Privy with Crossmint:

hooks/usePrivyConnector.tsx
'use client';

import {
  useCrossmint,
  useWallet as useCrossmintSmartWallet,
} from '@crossmint/client-sdk-react-ui';
import { usePrivy, useWallets } from '@privy-io/react-auth';
import { useEffect, useState } from 'react';

export function usePrivyConnector() {
  const { ready, authenticated, getAccessToken } = usePrivy();
  const { setJwt } = useCrossmint();
  const { wallets: privyWallets, ready: privyReady } = useWallets();
  const {
    getOrCreateWallet: getOrCreateCrossmintSmartWallet,
    status: crossmintSmartWalletStatus,
    error: crossmintSmartWalletError,
    wallet: crossmintWallet,
  } = useCrossmintSmartWallet();

  // State to track loading status
  const [isLoading, setIsLoading] = useState(true);
  // State to store the Privy embedded wallet
  const [privyEmbeddedWallet, setPrivyEmbeddedWallet] = useState<any>(null);

  // Step 1: Sync Privy JWT with Crossmint
  useEffect(() => {
    const syncPrivyJwt = async () => {
      try {
        const privyJwt = await getAccessToken();
        setJwt(privyJwt ?? undefined);
      } catch (error) {
        setJwt(undefined);
        console.error('Failed to get Privy JWT:', error);
      }
    };

    if (ready && authenticated) {
      syncPrivyJwt();
    }
  }, [ready, authenticated, getAccessToken, setJwt]);

  // Step 2: Get Privy embedded wallet and create Crossmint wallet
  useEffect(() => {
    const createWalletWithPrivySigner = async () => {
      setIsLoading(true);

      if (!privyWallets?.length || !ready || !authenticated || !privyReady) {
        setIsLoading(false);
        return;
      }

      try {
        // Find the Privy embedded wallet
        const embeddedWallet = privyWallets.find(
          (wallet) => wallet.walletClientType === 'privy'
        );

        if (!embeddedWallet) {
          setIsLoading(false);
          return;
        }

        setPrivyEmbeddedWallet(embeddedWallet);

        // Get the signer from Privy
        const signer = await embeddedWallet.getEthereumProvider();

        // Create a Crossmint Smart Wallet using the Privy wallet as signer
        await getOrCreateCrossmintSmartWallet({
          type: 'evm-smart-wallet',
          signer,
        });
      } catch (error) {
        console.error('Failed to create Crossmint wallet:', error);
      } finally {
        setIsLoading(false);
      }
    };

    createWalletWithPrivySigner();
  }, [privyWallets, ready, authenticated, privyReady, getOrCreateCrossmintSmartWallet]);

  return {
    privyEmbeddedWallet,
    crossmintWallet,
    crossmintSmartWalletStatus,
    crossmintSmartWalletError,
    isLoading: isLoading || crossmintSmartWalletStatus === 'in-progress' || !privyReady,
  };
}
6

Update Your Layout

Update your app’s layout to use the CrossmintPrivyProvider:

app/layout.tsx
import type { Metadata } from 'next';
import { Inter } from 'next/font/google';
import './globals.css';
import CrossmintPrivyProvider from '@/components/providers/CrossmintPrivyProvider';

const inter = Inter({ subsets: ['latin'] });

export const metadata: Metadata = {
  title: 'Crossmint + Privy Integration',
  description: 'Example of using Crossmint with Privy Auth',
};

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
      <body className={inter.className}>
        <CrossmintPrivyProvider>
          <main className="flex min-h-screen flex-col items-center justify-between p-24">
            {children}
          </main>
        </CrossmintPrivyProvider>
      </body>
    </html>
  );
}
7

Create UI Components

Create a component that uses the Privy connector and displays wallet information:

components/WalletCard.tsx
'use client';

import { usePrivy } from '@privy-io/react-auth';
import { Button } from '@/components/ui/button';
import { Card, CardContent } from '@/components/ui/card';
import { usePrivyConnector } from '@/hooks/usePrivyConnector';

export function WalletCard() {
  const { login, logout, authenticated } = usePrivy();
  const {
    privyEmbeddedWallet,
    crossmintWallet,
    crossmintSmartWalletStatus,
    crossmintSmartWalletError,
    isLoading,
  } = usePrivyConnector();

  return (
    <Card className="w-full max-w-lg">
      <CardContent className="space-y-4 pt-6">
        {!authenticated ? (
          <Button onClick={login} className="w-full">
            Log In with Privy
          </Button>
        ) : (
          <Button onClick={logout} className="w-full">
            Log Out
          </Button>
        )}

        <div className="space-y-2 pt-4">
          <div className="text-sm text-muted-foreground">
            <strong>Crossmint Smart Wallet:</strong>{' '}
            {crossmintWallet?.address || 'Not connected'}
          </div>
          <div className="text-sm text-muted-foreground">
            <strong>Privy Signer Wallet:</strong>{' '}
            {privyEmbeddedWallet?.address || 'Not connected'}
          </div>
          <div className="text-sm text-muted-foreground">
            <strong>Status:</strong>{' '}
            {isLoading ? 'Loading...' : crossmintSmartWalletStatus}
          </div>

          {crossmintSmartWalletError && (
            <div className="text-sm text-red-500">
              <strong>Error:</strong> {crossmintSmartWalletError?.message}
            </div>
          )}
        </div>
      </CardContent>
    </Card>
  );
}
8

Create Your Main Page

Create your main page that uses the WalletCard component:

app/page.tsx
import { WalletCard } from '@/components/WalletCard';

export default function Home() {
  return (
    <div className="flex flex-col items-center gap-8">
      <h1 className="text-3xl font-bold">Crossmint + Privy Integration</h1>
      <p className="text-center max-w-2xl text-muted-foreground">
        This example demonstrates how to use Privy's embedded wallet as a signer for Crossmint Smart Wallets.
        When you log in with Privy, an embedded wallet is created that serves as the signer for your Crossmint Smart Wallet.
      </p>
      <WalletCard />
    </div>
  );
}
9

Launch and Test

  1. Start your development server:
npm run dev
  1. Visit http://localhost:3000 in your browser
  2. Click the “Log In with Privy” button to start the authentication flow
  3. Follow the prompts to authenticate using Privy
  4. Once logged in, you should see both your Privy embedded wallet address and your Crossmint Smart Wallet address

How It Works

The integration between Privy and Crossmint involves these key components:

  1. Authentication Flow:

    • User logs in using Privy’s authentication
    • Privy creates an embedded wallet for the user
    • The Crossmint SDK uses the JWT from Privy to authenticate with Crossmint
  2. Wallet Creation:

    • The usePrivyConnector hook finds the Privy embedded wallet
    • It retrieves the Ethereum provider from the Privy wallet to use as a signer
    • It creates a Crossmint Smart Wallet using the Privy wallet as the signer
  3. Key Benefits:

    • Users leverage their existing Privy authentication
    • No new key management required
    • The Privy embedded wallet serves as a signer for the Crossmint Smart Wallet
    • Cross-chain support through Crossmint’s infrastructure

Learn More

For more information on working with Crossmint Smart Wallets, check out these resources: