Skip to content

Integration Guide

⚠️ Development Documentation - Production Use Not Recommended

This guide contains experimental features and integration patterns. Thoroughly test all implementations before considering production use.

Simple integration patterns and best practices for LazorKit.

Quick Setup

1. Provider Setup

// App.tsx
import { LazorkitProvider } from '@lazorkit/wallet';
 
const config = {
  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!,
};
 
export default function App() {
  return (
    <LazorkitProvider {...config}>
      <YourApp />
    </LazorkitProvider>
  );
}

2. Environment Variables

# .env
REACT_APP_RPC_URL=https://api.devnet.solana.com
REACT_APP_PAYMASTER_URL=your-paymaster-url
REACT_APP_PORTAL_URL=your-portal-url

Connection Patterns

Auto-Connect Hook

// hooks/useAutoConnect.ts
import { useEffect } from 'react';
import { useWallet } from '@lazorkit/wallet';
 
export function useAutoConnect() {
  const { connect, isConnected } = useWallet();
 
  useEffect(() => {
    if (!isConnected) {
      connect().catch(error => {
        // Ignore "no credentials" errors (new users)
        if (error.code !== 'NO_STORED_CREDENTIALS') {
          console.error('Auto-connect failed:', error);
        }
      });
    }
  }, [connect, isConnected]);
}
 
// Usage
function App() {
  useAutoConnect();
  return <YourApp />;
}

Error Handling Hook

// hooks/useWalletErrors.ts
export function useWalletErrors() {
  const getErrorMessage = (error) => {
    switch (error.code) {
      case 'NO_STORED_CREDENTIALS':
        return 'Ready to create your first wallet?';
      case 'USER_CANCELLED':
        return 'Authentication was cancelled';
      case 'CONNECTION_FAILED':
        return 'Connection failed. Please try again';
      case 'PASSKEY_CREATION_FAILED':
        return 'Failed to create passkey. Check browser settings';
      default:
        return error.message || 'Something went wrong';
    }
  };
 
  return { getErrorMessage };
}

UI Components

Simple Wallet Button

// components/WalletButton.tsx
import { useWallet } from '@lazorkit/wallet';
import { useWalletErrors } from '../hooks/useWalletErrors';
 
export function WalletButton() {
  const { connect, disconnect, account, isConnecting } = useWallet();
  const { getErrorMessage } = useWalletErrors();
  const [error, setError] = useState(null);
 
  const handleConnect = async () => {
    try {
      setError(null);
      await connect();
    } catch (err) {
      setError(getErrorMessage(err));
    }
  };
 
  if (account) {
    return (
      <div>
        <p className="text-sm">Connected: {account.smartWallet.slice(0, 8)}...</p>
        <button onClick={disconnect}>Disconnect</button>
      </div>
    );
  }
 
  return (
    <div>
      <button onClick={handleConnect} disabled={isConnecting}>
        {isConnecting ? 'Connecting...' : 'Connect Wallet'}
      </button>
      {error && <p className="text-red-500 text-sm">{error}</p>}
    </div>
  );
}

Wallet Status

// components/WalletStatus.tsx
export function WalletStatus() {
  const { account, isConnected } = useWallet();
 
  if (!isConnected) return <div>Not connected</div>;
 
  return (
    <div className="bg-green-50 p-3 rounded border">
      <div className="text-sm text-green-800">
        <div>✅ Wallet Connected</div>
        <div className="font-mono text-xs mt-1">
          {account.smartWallet.slice(0, 8)}...{account.smartWallet.slice(-8)}
        </div>
      </div>
    </div>
  );
}

Transaction Patterns

Simple Send Hook

// hooks/useSimpleSend.ts
import { useState } from 'react';
import { useWallet } from '@lazorkit/wallet';
 
export function useSimpleSend() {
  const { signAndSendTransaction, isSigning } = useWallet();
  const [error, setError] = useState(null);
 
  const send = async (instruction) => {
    try {
      setError(null);
      const signature = await signAndSendTransaction(instruction);
      return signature;
    } catch (err) {
      const message = err.code === 'USER_CANCELLED' 
        ? 'Transaction cancelled'
        : err.message || 'Transaction failed';
      setError(message);
      throw err;
    }
  };
 
  return { send, isSigning, error, clearError: () => setError(null) };
}

Transfer Component

// components/SimpleTransfer.tsx
import { SystemProgram, PublicKey, LAMPORTS_PER_SOL } from '@solana/web3.js';
 
export function SimpleTransfer() {
  const { smartWalletPubkey } = useWallet();
  const { send, isSigning, error } = useSimpleSend();
 
  const sendSOL = async () => {
    const instruction = SystemProgram.transfer({
      fromPubkey: smartWalletPubkey,
      toPubkey: new PublicKey(process.env.REACT_APP_RECIPIENT_ADDRESS!),
      lamports: 0.1 * LAMPORTS_PER_SOL,
    });
 
    try {
      const signature = await send(instruction);
      console.log('Sent:', signature);
    } catch (error) {
      console.error('Failed:', error);
    }
  };
 
  return (
    <div>
      <button onClick={sendSOL} disabled={isSigning}>
        {isSigning ? 'Sending...' : 'Send 0.1 SOL'}
      </button>
      {error && <p className="text-red-500">{error}</p>}
    </div>
  );
}

Error Handling

Common Error Patterns

const handleTransactionError = (error) => {
  switch (error.code) {
    case 'USER_CANCELLED':
      return 'Transaction cancelled by user';
    case 'INSUFFICIENT_FUNDS':
      return 'Not enough balance for transaction';
    case 'TRANSACTION_FAILED':
      return 'Transaction failed to execute';
    case 'NOT_CONNECTED':
      return 'Please connect your wallet first';
    default:
      return error.message || 'Transaction error occurred';
  }
};

External Payer Pattern

// Simple external payer setup
export function useExternalPayer() {
  const { buildSmartWalletTransaction } = useWallet();
 
  const sendWithExternalPayer = async (instruction) => {
    const { createSessionTx, executeSessionTx } = await buildSmartWalletTransaction(
      new PublicKey(process.env.REACT_APP_EXTERNAL_PAYER_ADDRESS!),
      instruction
    );
 
    // Send to your backend for signing
    const response = await fetch('/api/external-payer', {
      method: 'POST',
      body: JSON.stringify({
        sessionTx: Buffer.from(createSessionTx.serialize()).toString('base64'),
        executeTx: Buffer.from(executeSessionTx.serialize()).toString('base64'),
      }),
    });
 
    return response.json();
  };
 
  return { sendWithExternalPayer };
}

Best Practices

1. Loading States

Always show loading feedback:

const { isSigning } = useWallet();
 
<button disabled={isSigning}>
  {isSigning ? 'Signing...' : 'Send Transaction'}
</button>

2. Error Boundaries

Wrap wallet components:

function WalletErrorBoundary({ children }) {
  const [hasError, setHasError] = useState(false);
 
  if (hasError) {
    return (
      <div>
        <p>Wallet error occurred</p>
        <button onClick={() => setHasError(false)}>Try Again</button>
      </div>
    );
  }
 
  return children;
}

3. Environment Detection

const isMainnet = process.env.REACT_APP_RPC_URL?.includes('mainnet-beta');
const network = isMainnet ? 'mainnet' : 'devnet';

4. Simple State Management

// For basic apps, useState is sufficient
const [walletState, setWalletState] = useState({
  lastTransaction: null,
  preferences: { autoConnect: true }
});
 
// For complex apps, consider Zustand or Context