Skip to main content

Multi-Chain

Privacy Boost supports operating across multiple EVM-compatible blockchains from a single SDK instance. Your cryptographic identity (keys) is shared across all chains, while each chain maintains its own authentication state, balances, and transaction history.

How It Works

The multi-chain architecture has two layers:
  1. Parent SDK (PrivacyBoost) — initialized once with your primary chain. Holds your identity keys.
  2. Chain clients (ChainClient) — created per chain via sdk.chain(). Each has its own server connection, JWT, and chain state, but shares the parent’s identity.
PrivacyBoost (identity keys, primary chain)
├── ChainClient (Optimism) — own auth, balances, transactions
├── ChainClient (Arbitrum) — own auth, balances, transactions
└── ChainClient (Base)     — own auth, balances, transactions
This means you authenticate once per chain, but your privacy address is consistent across all of them.

Quick Example

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

// Initialize SDK with your primary chain
const sdk = await PrivacyBoost.create({
  indexerUrl: 'https://op.example.com',
  appId: 'my-app',
});

// Create chain clients for other chains
const arbitrum = sdk.chain({ indexerUrl: 'https://arb.example.com' });
const base = sdk.chain({ indexerUrl: 'https://base.example.com' });

// Authenticate on each chain (uses shared identity keys)
await arbitrum.authenticate(walletAdapter);
await base.authenticate(walletAdapter);

// Operate independently per chain
await arbitrum.vault.shield({ tokenAddress: '0x...', amount: 1000n });
await base.vault.send({ to: '0x04...', tokenAddress: '0x...', amount: 500n });

ChainClient Configuration

Only indexerUrl is required. All other fields are auto-discovered from the chain’s indexer /api/v1/info endpoint.
ParameterTypeRequiredDescription
indexerUrlstringYesURL of this chain’s indexer/server
chainIdnumberNoEVM chain ID (auto-discovered)
shieldContractstringNoShield contract address (auto-discovered)
wethContractstringNoWETH contract address (for ETH wrapping)
rpcUrlstringNoRPC URL for on-chain reads
tokenRegistryAddressstringNoToken registry contract (auto-discovered)
teePublicKeystringNoTEE public key (auto-discovered)

Caching

Chain clients are cached by indexerUrl. Calling sdk.chain() with the same URL returns the same instance:
const a = sdk.chain({ indexerUrl: 'https://arb.example.com' });
const b = sdk.chain({ indexerUrl: 'https://arb.example.com' });
// a === b (same instance)
This means you can call sdk.chain() freely in components or functions without worrying about creating duplicate clients.

Per-Chain Authentication

Each chain client must be authenticated independently. The authentication uses shared identity keys from the parent SDK, but obtains a separate JWT for each chain’s server.
const arb = sdk.chain({ indexerUrl: 'https://arb.example.com' });

// Must authenticate before using vault/transactions
await arb.authenticate(walletAdapter);

// Now you can use chain-scoped operations
console.log('Chain ID:', arb.chainId);
console.log('Authenticated:', arb.isAuthenticated);
const balance = await arb.vault.getBalance('0x...');

Available Operations

A ChainClient provides the same core operations as the parent SDK:
ResourceOperations
vaultdeposit(), withdraw(), send(), getBalance(), getAllBalances()
transactionsTransaction history and status polling
auditAuditor-specific operations
// Deposit on a specific chain
await arb.vault.shield({ tokenAddress: '0x...', amount: 1000n });

// Check balance on that chain
const balance = await arb.vault.getBalance('0x...');

// Send privately on that chain
await arb.vault.send({ to: '0x04...', tokenAddress: '0x...', amount: 500n });

// Withdraw from that chain
await arb.vault.unshield({
  tokenAddress: '0x...',
  amount: 250n,
  recipientAddress: '0x...',
});

Identity Lookup

Look up a user’s privacy address on a specific chain:
const identity = await arb.searchAddress('0x1234...abcd');
console.log('Privacy address:', identity.privacyAddress);

Cleanup

Chain clients are disposed automatically when the parent SDK is disposed. You can also dispose individual clients:
// Dispose a single chain client
arb.clearSession(); // Clear JWT and pending transactions
arb.dispose();      // Release all resources

// Or dispose everything at once
sdk.dispose(); // Disposes parent + all chain clients

Next Steps

TypeScript Multi-Chain Guide

Detailed TypeScript examples and patterns

React Multi-Chain Guide

React hooks and component patterns for multi-chain

Configuration

SDK configuration and auto-discovery

Authentication

Authentication methods and wallet integration