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.
Multi-Chain
This guide covers using ChainContextHandle to operate on multiple blockchains from a single React Native SDK instance.
Setup
Initialize the parent SDK against your primary chain, then open a chain context for each additional chain:
import {
PrivacyBoost,
PrivacyBoostConfig,
ChainContextConfig,
} from '@sunnyside-io/privacy-boost-react-native';
const sdk = new PrivacyBoost(
PrivacyBoostConfig.create({
serverUrl: 'https://op.example.com',
appId: 'my-app',
// chainId / shieldContractAddress / wethContractAddress
// are discovered from the server when omitted.
chainId: undefined,
shieldContractAddress: undefined,
wethContractAddress: undefined,
teePublicKey: undefined,
persistenceStorage: undefined,
persistenceUnlock: undefined,
}),
);
// After authenticating the parent SDK once, open per-chain contexts.
const ethereum = sdk.createChainContext({
serverUrl: 'https://eth.example.com',
chainId: 1n,
shieldContractAddress: undefined,
wethContractAddress: undefined,
rpcUrl: undefined,
teePublicKey: undefined,
timeoutMs: 30_000n,
});
const base = sdk.createChainContext({
serverUrl: 'https://base.example.com',
chainId: 8453n,
shieldContractAddress: undefined,
wethContractAddress: undefined,
rpcUrl: undefined,
teePublicKey: undefined,
timeoutMs: 30_000n,
});
ChainContextConfig.chainId is a bigint and is required. Pass undefined for the optional fields to let the SDK discover them from the server.
Authentication
Each chain context needs to authenticate before it can operate. Authentication shares your privacy keys from the parent SDK but obtains a chain-specific JWT and registration:
// Authenticate the parent SDK once — establishes identity keys.
await sdk.authenticate(walletDelegate, undefined, undefined);
// Authenticate each chain context with the same wallet.
const ethLogin = await ethereum.authenticate(walletDelegate);
const baseLogin = await base.authenticate(walletDelegate);
console.log('Ethereum chain id:', ethereum.chainId());
console.log('Ethereum authenticated:', ethereum.isAuthenticated());
console.log('Ethereum registered:', ethereum.isRegistered());
ChainContextHandle.authenticate() only takes a WalletDelegate — keySource and tokenProvider come from the parent SDK.
Operations
Once authenticated, a ChainContextHandle exposes the same per-chain operations as the parent SDK:
Deposits
const amount = sdk.parseAmount('1.0', 18);
const result = await ethereum.shield(
'0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', // WETH on Ethereum
amount,
);
console.log('Ethereum shield tx:', result.txHash);
Private Transfers
const identity = await ethereum.resolveIdentity?.('0x1234...abcd');
// resolveIdentity is on the parent SDK; ChainContextHandle inherits identity.
const recipient = sdk.resolveIdentity('0x1234...abcd');
const transfer = await base.send(
'0x...token-on-base',
sdk.parseAmount('0.5', 18),
recipient.privacyAddress,
);
console.log('Base transfer tx:', transfer.txHash);
Withdrawals
await ethereum.unshield(
'0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
sdk.parseAmount('0.25', 18),
'0x...destination',
);
Balances
const ethBalance = await ethereum.getBalance(
'0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
);
const baseBalance = await base.getBalance('0x...token-on-base');
console.log('Ethereum shielded:', ethBalance.shieldedBalance);
console.log('Base shielded:', baseBalance.shieldedBalance);
Status polling and history
const status = await ethereum.getShieldStatus(result.requestId);
const history = await base.getTransactionHistory(undefined, 20, undefined);
const notes = await ethereum.getUnspentNotes(undefined);
Cross-Chain Patterns
Parallel Operations
Chain contexts are independent — run operations concurrently:
const [ethResult, baseResult] = await Promise.all([
ethereum.shield('0x...', sdk.parseAmount('1.0', 18)),
base.shield('0x...', sdk.parseAmount('2.0', 18)),
]);
Multi-Chain Balance Aggregation
import type { ChainContextHandle } from '@sunnyside-io/privacy-boost-react-native';
async function totalShielded(
chains: { ctx: ChainContextHandle; token: string }[],
): Promise<bigint> {
const balances = await Promise.all(
chains.map(({ ctx, token }) => ctx.getBalance(token)),
);
return balances.reduce(
(sum, b) => sum + BigInt(b.shieldedBalance),
0n,
);
}
const total = await totalShielded([
{ ctx: ethereum, token: '0x...' },
{ ctx: base, token: '0x...' },
]);
Chain Selection
const chains = new Map<string, ChainContextHandle>([
['ethereum', ethereum],
['base', base],
]);
function getChain(name: string): ChainContextHandle {
const ctx = chains.get(name);
if (!ctx) throw new Error(`Unknown chain: ${name}`);
return ctx;
}
await getChain('ethereum').shield('0x...', sdk.parseAmount('1.0', 18));
React Hook Pattern
import { useEffect, useRef } from 'react';
import {
PrivacyBoost,
ChainContextHandle,
ChainContextConfig,
} from '@sunnyside-io/privacy-boost-react-native';
function useChainContext(
sdk: PrivacyBoost,
config: ChainContextConfig,
): ChainContextHandle {
const ref = useRef<ChainContextHandle | null>(null);
if (ref.current === null) {
ref.current = sdk.createChainContext(config);
}
useEffect(() => {
return () => {
ref.current?.clearSession();
};
}, []);
return ref.current;
}
Cleanup
Each chain context can drop its JWT independently while keeping the parent SDK identity intact:
ethereum.clearSession(); // drop just Ethereum's JWT
sdk.clearSession(); // drop the parent JWT (does not clear contexts)
sdk.logout(); // end the whole session and clear identity
Next Steps