Skip to main content

State Management

This page’s content is being updated
This guide covers the internal state management system of the Privacy Boost TypeScript SDK.

Overview

The SDK uses Zustand for state management. This provides reactive state updates that can be subscribed to from any part of your application.

Available Stores

StorePurpose
useSDKStoreSDK initialization state
useWalletStoreWallet connection and authentication
useBalanceStoreToken balance cache
useTransactionStoreTransaction history and pending txs
useTokenStoreToken metadata cache
useContactStoreContacts storage

SDK Store

Tracks SDK initialization:
import { useSDKStore } from '@testinprod-io/privacy-boost';

const { initialized, loading, error } = useSDKStore();

if (loading) {
  console.log('SDK is initializing...');
}

if (error) {
  console.log('Initialization failed:', error);
}

if (initialized) {
  console.log('SDK is ready');
}

Wallet Store

Tracks wallet connection and authentication:
import { useWalletStore } from '@testinprod-io/privacy-boost';

const {
  isConnected,
  isAuthenticated,
  address,
  chainId,
  privacyAddress,
  mpk,
} = useWalletStore();

Subscribing to Changes

// Subscribe to specific state changes
const unsubscribe = useWalletStore.subscribe(
  (state) => state.isConnected,
  (isConnected) => {
    console.log('Connection changed:', isConnected);
  }
);

// Cleanup
unsubscribe();

Balance Store

Caches token balances:
import { useBalanceStore } from '@testinprod-io/privacy-boost';

const {
  balances,        // Map<tokenAddress, TokenBalance>
  loading,
  lastUpdated,
} = useBalanceStore();

// Get balance for specific token
const tokenBalance = balances.get('0x...token-address');
if (tokenBalance) {
  console.log('Shielded:', tokenBalance.shielded);
  console.log('Wallet:', tokenBalance.wallet);
}

Balance Actions

import { useBalanceStore } from '@testinprod-io/privacy-boost';

const { updateBalance, clearBalances } = useBalanceStore.getState();

// Manually update a balance
updateBalance('0x...token', {
  shielded: 1000000000000000000n,
  wallet: 500000000000000000n,
});

// Clear all cached balances
clearBalances();

Transaction Store

Tracks transaction history and pending transactions:
import { useTransactionStore } from '@testinprod-io/privacy-boost';

const {
  transactions,     // Transaction[]
  pending,         // Transaction[]
  loading,
} = useTransactionStore();

// Get recent transactions
const recent = transactions.slice(0, 10);

// Get pending transactions
for (const tx of pending) {
  console.log(`Pending: ${tx.txHash} - ${tx.type}`);
}

Transaction Actions

const { addTransaction, updateTransaction, clearTransactions } =
  useTransactionStore.getState();

// Add a new transaction
addTransaction({
  txHash: '0x...',
  type: 'deposit',
  status: 'pending',
  tokenAddress: '0x...',
  amount: 1000000000000000000n,
  createdAt: Date.now(),
  updatedAt: Date.now(),
});

// Update transaction status
updateTransaction('0x...txHash', {
  status: 'completed',
});

Token Store

Caches token metadata:
import { useTokenStore } from '@testinprod-io/privacy-boost';

const { tokens, getToken } = useTokenStore();

// Get cached token
const token = getToken('0x...token-address');
if (token) {
  console.log(`${token.symbol} (${token.decimals} decimals)`);
}

Contact Store

Stores contacts locally:
import { useContactStore } from '@testinprod-io/privacy-boost';

const { contacts, addContact, removeContact, updateContact } = useContactStore();

// List all contacts
for (const contact of contacts) {
  console.log(`${contact.name}: ${contact.privacyAddress}`);
}

React Integration

Using Stores in Components

import { useWalletStore, useBalanceStore } from '@testinprod-io/privacy-boost';

function WalletInfo() {
  // Reactive - re-renders on state change
  const { isAuthenticated, privacyAddress } = useWalletStore();
  const { balances } = useBalanceStore();

  if (!isAuthenticated) {
    return <div>Not authenticated</div>;
  }

  return (
    <div>
      <p>Address: {privacyAddress}</p>
      <p>Tokens: {balances.size}</p>
    </div>
  );
}

Selecting Specific State

function Balance({ tokenAddress }: { tokenAddress: string }) {
  // Only re-renders when this specific balance changes
  const balance = useBalanceStore(
    (state) => state.balances.get(tokenAddress)
  );

  if (!balance) return null;

  return <span>{balance.shielded.toString()}</span>;
}

Using Outside React

// Get current state
const state = useWalletStore.getState();
console.log('Current address:', state.address);

// Subscribe to changes
const unsubscribe = useWalletStore.subscribe((state) => {
  console.log('Wallet state changed:', state);
});

Custom Store Composition

Create derived state from multiple stores:
import { useMemo } from 'react';
import { useWalletStore, useBalanceStore } from '@testinprod-io/privacy-boost';

function useTotalBalance() {
  const { isAuthenticated } = useWalletStore();
  const { balances } = useBalanceStore();

  return useMemo(() => {
    if (!isAuthenticated) return 0n;

    let total = 0n;
    for (const balance of balances.values()) {
      total += balance.shielded;
    }
    return total;
  }, [isAuthenticated, balances]);
}

Persistence

The contact store persists to localStorage by default:
// Contacts are automatically saved and restored
// You can clear persisted data:
localStorage.removeItem('privacy-boost-contacts');

Debugging

Enable Zustand devtools:
import { devtools } from 'zustand/middleware';

// Stores support devtools integration
// Open Redux DevTools to inspect state

Best Practices

1. Select Only What You Need

// BAD - Re-renders on any state change
const state = useWalletStore();

// GOOD - Only re-renders when address changes
const address = useWalletStore((state) => state.address);

2. Use Shallow Comparison for Objects

import { shallow } from 'zustand/shallow';

// Re-renders only when these specific values change
const { address, chainId } = useWalletStore(
  (state) => ({ address: state.address, chainId: state.chainId }),
  shallow
);

3. Memoize Derived State

const sortedBalances = useMemo(() => {
  return Array.from(balances.values()).sort(
    (a, b) => Number(b.shielded - a.shielded)
  );
}, [balances]);

4. Clean Up Subscriptions

useEffect(() => {
  const unsubscribe = useWalletStore.subscribe((state) => {
    // Handle state changes
  });

  return () => unsubscribe();
}, []);

Next Steps