Skip to content

Getting Started

Welcome to LazorKit! This guide will help you integrate Web3 authentication with passkeys into your React application.

Overview

LazorKit enables seamless wallet functionality using WebAuthn passkeys, eliminating the need for traditional seed phrases or browser extensions. Users can authenticate using biometrics, security keys, or device authentication.

Prerequisites

  • Node.js: 18.x or higher (20.x LTS or 23.x recommended)
  • React: 18.x or higher
  • Modern Browser: Chrome 88+, Firefox 90+, Safari 15+, Edge 88+ (latest versions recommended)
  • HTTPS Environment: Required for WebAuthn (use localhost for development)
  • WebAuthn Support: Browser must support WebAuthn Level 2 and Passkeys

Basic Setup

1. Install LazorKit

npm install @lazorkit/wallet

2. Environment Configuration

Create environment variables for your application:

# .env.local or .env.development
REACT_APP_RPC_URL=https://api.devnet.solana.com
REACT_APP_PAYMASTER_URL=your-paymaster-service-url
REACT_APP_PORTAL_URL=your-portal-service-url
 
# Optional: For external payer integration
REACT_APP_EXTERNAL_PAYER_ADDRESS=your-external-payer-public-key
REACT_APP_API_ENDPOINT=your-backend-api-url
REACT_APP_API_KEY=your-api-authentication-key
REACT_APP_RECIPIENT_ADDRESS=default-recipient-address

3. Provider Setup

Wrap your application with LazorkitProvider:

import { LazorkitProvider } from '@lazorkit/wallet';
 
function App() {
  return (
    <LazorkitProvider
      rpcUrl={process.env.REACT_APP_RPC_URL || 'https://api.devnet.solana.com'}
      paymasterUrl={process.env.REACT_APP_PAYMASTER_URL}
      ipfsUrl={process.env.REACT_APP_PORTAL_URL}
    >
      <YourApp />
    </LazorkitProvider>
  );
}

3. Use the Wallet Hook

import { useWallet } from '@lazorkit/wallet';
import { SystemProgram, LAMPORTS_PER_SOL, PublicKey } from '@solana/web3.js';
 
function WalletComponent() {
  const {
    // State
    smartWalletPubkey,
    isConnected,
    isLoading,
    isConnecting,
    isSigning,
    error,
    account,
    
    // Actions
    connect,
    disconnect,
    signAndSendTransaction,
    createPasskeyOnly,
    createSmartWalletOnly,
    buildSmartWalletTransaction,
  } = useWallet();
 
  const handleConnect = async () => {
    try {
      await connect();
    } catch (error) {
      console.error('Connection failed:', error);
    }
  };
 
  const handleTransfer = async () => {
    if (!smartWalletPubkey) return;
 
    try {
      const instruction = SystemProgram.transfer({
        fromPubkey: smartWalletPubkey,
        toPubkey: new PublicKey('RECIPIENT_ADDRESS_HERE'),
        lamports: LAMPORTS_PER_SOL * 0.1,
      });
 
      const signature = await signAndSendTransaction(instruction);
      console.log('Transfer completed:', signature);
    } catch (error) {
      console.error('Transfer failed:', error);
    }
  };
 
  return (
    <div>
      {!isConnected ? (
        <button onClick={handleConnect} disabled={isConnecting}>
          {isConnecting ? 'Connecting...' : 'Connect Wallet'}
        </button>
      ) : (
        <div>
          <p>Connected: {smartWalletPubkey?.toString().slice(0, 8)}...</p>
          <p>Account: {account?.smartWallet.slice(0, 8)}...</p>
          <button onClick={handleTransfer} disabled={isSigning}>
            {isSigning ? 'Signing...' : 'Transfer 0.1 SOL'}
          </button>
          <button onClick={disconnect}>Disconnect</button>
        </div>
      )}
      
      {error && <p style={{ color: 'red' }}>Error: {error.message}</p>}
    </div>
  );
}

Key Concepts

Auto-Reconnection

LazorKit automatically handles reconnection using stored credentials:

// connect() tries reconnection first, then falls back to new connection
const account = await connect();

Flexible Wallet Creation

You can separate passkey creation from smart wallet deployment:

// Step 1: Create passkey only
const passkeyData = await createPasskeyOnly();
 
// Step 2: Later create smart wallet
const { smartWalletAddress, wallet } = await createSmartWalletOnly(passkeyData);

External Payer Support

For applications using external fee payers:

// Build unsigned transactions for external payer
const externalPayer = new PublicKey('YourExternalPayerAddress');
const { createSessionTx, executeSessionTx } = await buildSmartWalletTransaction(
  externalPayer,
  instruction
);
 
// Send to your backend for signing and submission

Event Handling

The SDK emits events for all operations:

// Events are automatically handled by the provider
// But you can listen to them directly if using the SDK directly
sdk.on('connect:success', (account) => {
  console.log('Connected:', account);
});

Transaction Patterns

Sign and Send (Recommended)

For most use cases where LazorKit handles the complete transaction flow:

const signature = await signAndSendTransaction(instruction);

Sign Only

For custom transaction management:

const signedTransaction = await signTransaction(instruction);
// Handle sending yourself

External Payer Pattern

For applications where backend services pay transaction fees:

const externalPayer = new PublicKey(process.env.REACT_APP_EXTERNAL_PAYER_ADDRESS!);
const { createSessionTx, executeSessionTx } = await buildSmartWalletTransaction(
  externalPayer,
  instruction
);
 
// Send serialized transactions to your backend
const response = await fetch(process.env.REACT_APP_API_ENDPOINT + '/sign-and-send', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${process.env.REACT_APP_API_KEY}`,
  },
  body: JSON.stringify({
    createSessionTx: Buffer.from(createSessionTx.serialize()).toString('base64'),
    executeSessionTx: Buffer.from(executeSessionTx.serialize()).toString('base64'),
  }),
});

Advanced Examples

Step-by-Step Onboarding

import { useState } from 'react';
 
function OnboardingExample() {
  const { createPasskeyOnly, createSmartWalletOnly } = useWallet();
  const [step, setStep] = useState<'passkey' | 'wallet' | 'complete'>('passkey');
  const [passkeyData, setPasskeyData] = useState(null);
 
  const handleCreatePasskey = async () => {
    try {
      const data = await createPasskeyOnly();
      setPasskeyData(data);
      setStep('wallet');
    } catch (error) {
      console.error('Passkey creation failed:', error);
    }
  };
 
  const handleCreateWallet = async () => {
    try {
      const { account } = await createSmartWalletOnly(passkeyData);
      console.log('Wallet created:', account.smartWallet);
      setStep('complete');
    } catch (error) {
      console.error('Wallet creation failed:', error);
    }
  };
 
  return (
    <div>
      {step === 'passkey' && (
        <button onClick={handleCreatePasskey}>Create Passkey</button>
      )}
      {step === 'wallet' && (
        <button onClick={handleCreateWallet}>Deploy Smart Wallet</button>
      )}
      {step === 'complete' && (
        <div>Wallet setup complete</div>
      )}
    </div>
  );
}

Error Handling

function ErrorHandlingExample() {
  const { connect, error } = useWallet();
 
  const handleConnect = async () => {
    try {
      await connect();
    } catch (error) {
      switch (error.code) {
        case 'NO_STORED_CREDENTIALS':
          // First time user
          break;
        case 'INVALID_CREDENTIALS':
          // Stored credentials invalid
          break;
        case 'USER_CANCELLED':
          // User cancelled authentication
          break;
        default:
          console.error('Connection failed:', error.message);
      }
    }
  };
 
  return (
    <div>
      <button onClick={handleConnect}>Connect</button>
      {error && <div>Error: {error.message}</div>}
    </div>
  );
}

Next Steps

Core Documentation Implementation Guides Kora Relayer Guides

Requirements

  • Node.js 18+
  • React 18+
  • HTTPS environment (required for WebAuthn)
  • Modern browser with WebAuthn support

Support

  • Documentation issues and improvements: GitHub Issues
  • Feature requests: GitHub Discussions
  • Bug reports: GitHub Issues