Skip to content

Connection Modes

The SDK supports two ways to connect: Linked Mode and Standalone Mode.

Linked Mode

Derives your privacy keys from an EIP-712 signature. The user signs once with their existing wallet.

typescript
import { createWalletClient, custom } from 'viem';

const walletClient = createWalletClient({
  chain: mainnet,
  transport: custom(window.ethereum),
});

await client.connect({
  mode: 'linked',
  walletClient,
});

How It Works

MetaMask Wallet

      │ Signs EIP-712 message

┌─────────────────┐
│    Signature    │ ──────────────────────────────────────┐
└─────────────────┘                                       │

                                          ┌───────────────────────────┐
                                          │  spending_key = hash(sig) │
                                          └───────────────────────────┘

                              ┌────────────────────────────┼────────────────────────────┐
                              │                            │                            │
                              ▼                            ▼                            ▼
                    ┌─────────────────┐          ┌─────────────────┐          ┌─────────────────┐
                    │  viewing_key    │          │ nullifying_key  │          │      SPK        │
                    └─────────────────┘          └─────────────────┘          └─────────────────┘

Advantages

  • No seed phrase backup - Keys are derived from wallet signature
  • Familiar UX - Users just sign with MetaMask
  • Deterministic - Same wallet always derives same keys

Limitations

  • Requires connected wallet to use
  • Wallet must support EIP-712 typed data signing

Standalone Mode

Direct key input without wallet connection. Best for scripts and services.

From Spending Key

typescript
await client.connect({
  mode: 'standalone',
  spendingKey: BigInt('0x1234...'),
});

From Mnemonic

typescript
await client.connect({
  mode: 'standalone',
  mnemonic: 'word1 word2 word3 ... word12',
  accountIndex: 0,  // Optional, default 0
});

Advantages

  • Works in Node.js scripts
  • No wallet popup required
  • Can manage multiple accounts

Security Considerations

DANGER

Never expose spending keys in client-side code. Use standalone mode only in:

  • Server-side scripts
  • Secure backend services
  • Development/testing

Switching Modes

You can disconnect and reconnect with a different mode:

typescript
// Start in linked mode
await client.connect({ mode: 'linked', walletClient });

// Later, switch to standalone
client.disconnect();
await client.connect({ mode: 'standalone', spendingKey: ... });

Checking Connection State

typescript
// Check if connected
if (client.isConnected()) {
  console.log('Address:', client.getAddress());
}

// Disconnect
client.disconnect();

Session Persistence

The SDK does not persist keys automatically. You need to reconnect on page reload.

For linked mode, this just means re-signing. For standalone mode, you'd need to securely store and retrieve the key.

typescript
// On app load
if (savedSpendingKey) {
  await client.connect({ mode: 'standalone', spendingKey: savedSpendingKey });
} else {
  await client.connect({ mode: 'linked', walletClient });
}

Multi-Account Support

Both modes support account indices for managing multiple identities:

typescript
// Linked mode - different signature per account
await client.connect({ 
  mode: 'linked', 
  walletClient,
  accountIndex: 1,  // Second account
});

// Standalone mode - different derivation path
await client.connect({ 
  mode: 'standalone', 
  mnemonic: '...',
  accountIndex: 1,
});

Each account index produces completely separate keys and addresses.

Released under the MIT License.