Getting Started with Privy

Let's get started with a basic example using Privy

Pre-requisites

1. Set Up Authorization Keys

What is an authorization key?

The authorization key is a user created by you, the client application, for the Enclave team, that allows us to issue transaction policies that enforce resource locks on users that wish to enable Magicspend++.

You can create an authorization key on the Privy dashboard by following the steps mentioned here.

Once you have created the authorization key, share the all key details with the Enclave team to enable integration. Also note down it's public key and API key name, these values are required for the next step.

2. Creating Users and Wallets with Key Quorums

Modify the existing user creation logic in your onboarding flow to provision a wallet with a key quorum that includes both the end user and the authorization key created in the previous step.

The key quorum structure is such that each unique user has a wallet owned by a key quorum with 2 members: the end user and the authorization key that issues policies on the user's wallet.

Given below is some sample code that can be used as reference:

Creating a wallet with authorization key

import { usePrivy } from '@privy-io/react-auth';

const WalletComponent = () => {
    const { getAccessToken, authenticated } = usePrivy();
    
    const authPublicKey = process.env.NEXT_PUBLIC_ENCLAVE_AUTH_PUBLIC_KEY!;
    
    if (!authPublicKey) {
        console.log('⚠️ Authorization key not found in environment variables, skipping');
        return;
    }

    const createWalletWithQuorum = async (chainType: 'ethereum' | 'solana') => {
        const token = await getAccessToken();
        
        const response = await fetch('/api/wallets/create', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'Authorization': `Bearer ${token}`,
            },
            body: JSON.stringify({ chainType }),
        });
        
        const result = await response.json();
        return result.wallet;
    };
};

Server-side implementation

import { usePrivy } from '@privy-io/react-auth';

const WalletComponent = () => {
    const { getAccessToken, authenticated } = usePrivy();
    
    const authPublicKey = process.env.NEXT_PUBLIC_ENCLAVE_AUTH_PUBLIC_KEY!;
    
    if (!authPublicKey) {
        console.log('⚠️ Authorization key not found in environment variables, skipping');
        return;
    }

    const createWalletWithQuorum = async (chainType: 'ethereum' | 'solana') => {
        const token = await getAccessToken();
        
        const response = await fetch('/api/wallets/create', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'Authorization': `Bearer ${token}`,
            },
            body: JSON.stringify({ chainType }),
        });
        
        const result = await response.json();
        return result.wallet;
    };
};

Server-side implementation
import { NextResponse } from 'next/server';
import { getAccessTokenFromRequest, verifyAccessToken } from '@/lib/privy-server';

const PRIVY_APP_ID = process.env.NEXT_PUBLIC_PRIVY_APP_ID;
const PRIVY_APP_SECRET = process.env.NEXT_PUBLIC_PRIVY_APP_SECRET;
const PRIVY_AUTHORIZATION_PUBLIC_KEY = process.env.PRIVY_AUTHORIZATION_PUBLIC_KEY;
const PRIVY_API_URL = 'https://api.privy.io/v1';

export async function POST(request: Request) {
    // Extract and verify token
    const token = getAccessTokenFromRequest(request);
    if (!token) {
        return NextResponse.json({ error: 'No access token' }, { status: 401 });
    }

    const claims = await verifyAccessToken(token);
    if (!claims) {
        return NextResponse.json({ error: 'Invalid token' }, { status: 401 });
    }

    const userId = claims.userId;
    const { chainType } = await request.json();

    // Format public key
    const formattedPublicKey = PRIVY_AUTHORIZATION_PUBLIC_KEY!.replace(/\\n/g, '\n');
    
    // Create 1-of-2 key quorum
    const keyQuorumResponse = await fetch(`${PRIVY_API_URL}/key_quorums`, {
        method: 'POST',
        headers: {
            'Authorization': `Basic ${Buffer.from(`${PRIVY_APP_ID}:${PRIVY_APP_SECRET}`).toString('base64')}`,
            'privy-app-id': PRIVY_APP_ID!,
            'Content-Type': 'application/json',
        },
        body: JSON.stringify({
            public_keys: [formattedPublicKey],
            user_ids: [userId],
            authorization_threshold: 1,
        }),
    });

    const keyQuorum = await keyQuorumResponse.json();
    
    // Create wallet owned by key quorum
    const walletResponse = await fetch(`${PRIVY_API_URL}/wallets`, {
        method: 'POST',
        headers: {
            'Authorization': `Basic ${Buffer.from(`${PRIVY_APP_ID}:${PRIVY_APP_SECRET}`).toString('base64')}`,
            'privy-app-id': PRIVY_APP_ID!,
            'Content-Type': 'application/json',
        },
        body: JSON.stringify({
            chain_type: chainType,
            owner_id: keyQuorum.id,
        }),
    });

    const wallet = await walletResponse.json();
    
    return NextResponse.json({
        success: true,
        wallet: {
            id: wallet.id,
            address: wallet.address,
            chainType: wallet.chain_type,
        }
    });
}

Next let's see how we can fetch a quote

Fetching a quote

Last updated