Documentation Index
Fetch the complete documentation index at: https://docs.privacyboost.io/llms.txt
Use this file to discover all available pages before exploring further.
Advanced
This guide covers patterns for developers who’ve built the basics and need custom hooks, server-side rendering support, or testing utilities.
Custom Hooks
Build reusable hooks that combine SDK functionality with application logic.
useDepositFlow
Encapsulates shield state management:
import { useState, useCallback } from 'react';
import { useVault } from '@sunnyside-io/privacy-boost-react';
function useDepositFlow(tokenAddress: string) {
const { shield } = useVault();
const [loading, setLoading] = useState(false);
const [step, setStep] = useState<string | null>(null);
const [error, setError] = useState<string | null>(null);
const execute = useCallback(async (amount: string) => {
setLoading(true);
setError(null);
try {
const result = await shield({
tokenAddress,
amount,
onProgress: ({ step }) => setStep(step),
});
return result;
} catch (err: any) {
if (err.code !== 'TRANSACTION_REJECTED') {
setError(err.message);
}
throw err;
} finally {
setLoading(false);
setStep(null);
}
}, [deposit, tokenAddress]);
return { execute, loading, step, error };
}
usePollingBalance
Auto-refresh balances on an interval:
import { useEffect, useCallback, useRef } from 'react';
import { useBalances } from '@sunnyside-io/privacy-boost-react';
function usePollingBalance(intervalMs = 30000) {
const { balances, refresh } = useBalances();
const intervalRef = useRef<ReturnType<typeof setInterval>>();
useEffect(() => {
intervalRef.current = setInterval(refresh, intervalMs);
return () => clearInterval(intervalRef.current);
}, [refresh, intervalMs]);
return balances;
}
SSR Support
The SDK uses WebAssembly, which requires special handling in server-side rendering environments.
Next.js App Router
'use client'; // Required — SDK can only run in the browser
import { PrivacyBoostProvider } from '@sunnyside-io/privacy-boost-react';
export default function PrivacyLayout({ children }: { children: React.ReactNode }) {
return (
<PrivacyBoostProvider config={config}>
{children}
</PrivacyBoostProvider>
);
}
Add WASM support to next.config.js:
/** @type {import('next').NextConfig} */
const nextConfig = {
webpack: (config) => {
config.experiments = {
...config.experiments,
asyncWebAssembly: true,
};
return config;
},
};
module.exports = nextConfig;
Next.js Pages Router
Use dynamic imports to prevent server-side loading:
import dynamic from 'next/dynamic';
const PrivacyWallet = dynamic(() => import('../components/PrivacyWallet'), {
ssr: false,
loading: () => <div>Loading wallet...</div>,
});
export default function WalletPage() {
return <PrivacyWallet />;
}
Hydration Mismatch Prevention
If you conditionally render based on auth state, use a client-only check:
import { useState, useEffect } from 'react';
function useIsClient() {
const [isClient, setIsClient] = useState(false);
useEffect(() => setIsClient(true), []);
return isClient;
}
function WalletUI() {
const isClient = useIsClient();
const { isAuthenticated } = useAuth();
if (!isClient) return <div>Loading...</div>;
// Now safe to render based on auth state
}
Testing
Mock SDK
Create a mock SDK for unit tests:
import type { PrivacyBoost } from '@sunnyside-io/privacy-boost';
function createMockSDK(): Partial<PrivacyBoost> {
return {
auth: {
authenticate: jest.fn().mockResolvedValue({ status: 'authenticated', privacyAddress: '0x04...' }),
isAuthenticated: jest.fn().mockReturnValue(true),
getPrivacyAddress: jest.fn().mockReturnValue('0x04...'),
logout: jest.fn(),
clearSession: jest.fn(),
} as any,
vault: {
shield: jest.fn().mockResolvedValue({ txHash: '0x...' }),
unshield: jest.fn().mockResolvedValue({ txHash: '0x...' }),
send: jest.fn().mockResolvedValue({ txHash: '0x...' }),
getBalance: jest.fn().mockResolvedValue(1000000000000000000n),
getAllBalances: jest.fn().mockResolvedValue([]),
} as any,
};
}
Mock Provider
Wrap components in a test provider:
import { PrivacyBoostProvider } from '@sunnyside-io/privacy-boost-react';
function TestProvider({ children }: { children: React.ReactNode }) {
const mockSDK = createMockSDK();
return (
<PrivacyBoostProvider sdk={mockSDK as any}>
{children}
</PrivacyBoostProvider>
);
}
// Usage in tests
render(
<TestProvider>
<YourComponent />
</TestProvider>
);
Testing useAuth
import { renderHook, act } from '@testing-library/react-hooks';
import { useAuth } from '@sunnyside-io/privacy-boost-react';
test('authenticateWithWalletAdapter sets isAuthenticated', async () => {
const { result } = renderHook(() => useAuth(), { wrapper: TestProvider });
await act(async () => {
await result.current.authenticateWithWalletAdapter();
});
expect(result.current.isAuthenticated).toBe(true);
expect(result.current.privacyAddress).toBeDefined();
});
Testing useVault
test('shield calls vault.shield', async () => {
const { result } = renderHook(() => useVault(), { wrapper: TestProvider });
await act(async () => {
await result.current.shield({
tokenAddress: '0x...',
amount: '1.0',
});
});
expect(mockSDK.vault.shield).toHaveBeenCalledWith(
expect.objectContaining({ tokenAddress: '0x...' })
);
});
Next Steps