Wallet Integration Guide
Complete guide to the modern wallet system with automatic initialization and state restoration.
Architecture Overview
The wallet system is built on three core components working together to provide seamless wallet integration.
๐ช Wallet Store
Zustand-based global state management for wallet connections and Tezos instances.
๐ช useTezos Hook
React hook that provides easy access to wallet functionality and auto-initialization.
๐ WalletProvider
React provider that ensures wallet initialization happens at the app level.
System Flow
App Startup โ WalletProvider โ initializeWallets() โ Check Existing Connections
1. Beacon Wallet Check:
- Initialize BeaconWallet instance
- Call wallet.client.getActiveAccount()
- If account exists โ set address in store
2. Kukai Check:
- Initialize KukaiEmbed instance
- Check kukai.user.pkh for existing session
- If session exists โ set address in store
3. State Ready:
- Components receive wallet state via useTezos()
- UI reflects connection status automaticallyWallet Store Details
The Zustand store manages all wallet state and provides methods for connection management.
Store Interface
interface WalletState {
// Core instances
Tezos: TezosToolkit; // Main Taquito instance
wallet: BeaconWallet | null; // Beacon wallet instance
kukai: KukaiEmbed | null; // Kukai wallet instance
// Connection state
address: string | null; // Connected wallet address
isInitialized: boolean; // Initialization status
// Methods
initializeWallets: () => Promise<void>; // Auto-init on startup
connectWallet: () => Promise<void>; // Connect Beacon wallet
connectKukai: () => Promise<void>; // Connect Kukai wallet
disconnectWallet: () => Promise<void>; // Disconnect all wallets
}Key Features
- โ Auto-initialization: Checks for existing connections on app start
- โ Multi-wallet support: Handles both Beacon and Kukai simultaneously
- โ Network awareness: Automatically configures for testnet/mainnet
- โ Error handling: Graceful degradation when wallets fail to initialize
- โ Type safety: Full TypeScript support with proper error types
Initialization Process
Understanding how wallet initialization works helps you debug connection issues and optimize performance.
Initialization Flow
async initializeWallets() {
try {
// 1. Import wallet libraries dynamically
const { BeaconWallet } = await import('@taquito/beacon-wallet');
const { KukaiEmbed, Networks } = await import('kukai-embed');
// 2. Initialize Beacon Wallet
const wallet = new BeaconWallet({
name: 'Tezos Boilerplate',
preferredNetwork: ENV === 'dev' ? NetworkType.GHOSTNET : NetworkType.MAINNET
});
// 3. Set as Tezos provider
Tezos.setWalletProvider(wallet);
// 4. Check for existing active account
const activeAccount = await wallet.client.getActiveAccount();
if (activeAccount) {
// User was previously connected โ restore state
set({ address: activeAccount.address });
}
// 5. Initialize Kukai
const kukai = new KukaiEmbed({
net: ENV === 'dev' ? Networks.ghostnet : Networks.mainnet
});
await kukai.init();
// 6. Check for existing Kukai session
if (kukai.user?.pkh) {
// Kukai session exists โ restore state
if (!activeAccount) { // Only if no Beacon connection
set({ address: kukai.user.pkh });
}
}
// 7. Mark as initialized
set({ isInitialized: true });
} catch (error) {
console.error('Wallet initialization failed:', error);
set({ isInitialized: true }); // Still mark as initialized to prevent retries
}
}Using the useTezos Hook
The useTezos hook is your main interface to the wallet system. It handles initialization and provides a clean API.
Hook Implementation
export const useTezos = () => {
const store = useWalletStore();
// Auto-initialize on first use
useEffect(() => {
if (!store.isInitialized) {
store.initializeWallets();
}
}, [store.isInitialized, store.initializeWallets]);
return {
Tezos: store.Tezos,
wallet: store.wallet,
address: store.address,
kukai: store.kukai,
isInitialized: store.isInitialized,
connectWallet: store.connectWallet,
connectKukai: store.connectKukai,
disconnectWallet: store.disconnectWallet
};
};Hook Usage Patterns
| Check initialization | if (!isInitialized) return <Loading /> |
| Check connection | if (address) โ show connected state |
| Connect wallet | await connectWallet() โ handles permissions |
| Send transaction | await Tezos.wallet.transfer(...).send() |
| Get balance | await Tezos.tz.getBalance(address) |
| Disconnect | await disconnectWallet() โ clears all state |
State Restoration
One of the key features is automatic state restoration - your users stay connected across page refreshes.
๐ How State Restoration Works
- User connects wallet in your dApp
- Wallet stores connection info in browser storage (handled by Beacon/Kukai)
- User refreshes page or navigates away and back
- WalletProvider runs initializeWallets() on app start
- System checks for existing wallet connections
- If found, automatically restores the connected state
- UI immediately shows connected state - no re-login required!
Testing State Restoration
// Test the restoration flow:
1. Connect wallet in your dApp
2. Open browser dev tools โ Application tab โ Local Storage
3. Look for beacon: or kukai: entries (these persist the connection)
4. Refresh the page (Cmd+R / Ctrl+R)
5. Watch the wallet button - it should show "Disconnect" immediately
6. No login prompt should appear
// If restoration fails:
// - Check browser console for errors
// - Verify WalletProvider is wrapping your app
// - Ensure useTezos is called in components that need wallet stateBest Practices
Follow these patterns to get the most out of the wallet system.
โ Do
- โข Check isInitialized before showing wallet buttons
- โข Use the address to determine connection state
- โข Handle connection errors gracefully
- โข Show loading states during operations
- โข Use Tezos.wallet for transaction operations
โ Don't
- โข Create multiple TezosToolkit instances
- โข Initialize wallets outside the store
- โข Block UI while waiting for initialization
- โข Assume wallet state without checking
- โข Forget to handle disconnection