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- API Reference - Complete method documentation
- Type Definitions - TypeScript interfaces and types
- Installation - Environment setup and configuration
- Authentication Guide - Passkey flows and patterns
- Integration Guide - Production patterns and external payer setup
- Transaction Handling - Transaction workflows
- Wallet Adapter Integration - Compatible with existing dApps
- Kora Quick Reference - Basic methods (getPayer, sign, signAndSend)
- Kora Overview - Fee abstraction overview
- LazorKit + Kora - Complete integration
- Official Kora Docs - Full setup and configuration
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