> ## Documentation Index
> Fetch the complete documentation index at: https://sdk.umbraprivacy.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Stealth Pool Viewing Keys

> MVK hierarchy: master viewing key (BN254) derived via KMAC256, Poseidon-derived temporal and per-mint scoped keys. Grant auditors read access to specific Stealth Pool Note time windows or tokens.

## What Are Viewing Keys?

The **master viewing key** (MVK) is a 252-bit BN254 field element derived deterministically from your master seed. It sits at the root of a hierarchical key system that lets you grant scoped read access to your Stealth Pool activity without ever exposing the root key or any data outside the defined scope.

Viewing keys are designed for compliance and audit use cases:

* Provide an auditor or regulator access to transactions within a specific time window
* Grant a compliance officer access to all activity for a specific token
* Share full visibility at the master level with a tax advisor or legal counterparty
* Prove to an exchange that specific Stealth Pool Notes originated from you.

<Note>
  Viewing keys are read-only credentials. Possessing a viewing key does not allow burning Stealth Pool Notes or withdrawing balances. They can only be used to decrypt ciphertext data that was encrypted under the associated key — specifically the `pc_encrypted_*` (Poseidon stream cipher) fields on a note. They cannot decrypt the AES-GCM or Rescue ciphertexts.
</Note>

## The Viewing Key Hierarchy

All keys in the hierarchy are BN254 field elements. Each level is derived from its parent using the Poseidon hash function. A key at any level grants access to all data within its scope - and nothing above or adjacent to it in the tree.

```
Master Viewing Key
└── Mint Viewing Key  { e.g. USDC · USDT · wSOL · ... }
    └── Yearly Viewing Key  { e.g. 2024 · 2025 · ... }
        ├── Monthly Viewing Key  { Jan }
        │   ├── Daily Viewing Key  { Jan 01 }
        │   │   ├── Hourly Viewing Key  { 00h }
        │   │   │   ├── Minute Viewing Key  { 00m }
        │   │   │   │   └── Second Viewing Key  { 00s }
        │   │   │   └── ...
        │   │   └── ...
        │   └── ...
        ├── Monthly Viewing Key  { Feb }
        ├── Monthly Viewing Key  { Mar }
        └── ...  { Apr – Dec }
```

**Example scopes:**

* Monthly key for `2025-02`: sees all USDC mixer activity in February 2025
* Yearly key for `2025`: sees all USDC mixer activity across all of 2025
* Mint key for USDC: sees all USDC mixer activity, all time
* Master viewing key: sees everything across all tokens and all time

## Deriving Viewing Keys

Each level of the hierarchy has a corresponding deriver factory function. Pass `{ client }` to get a deriver, then call it with the appropriate parameters.

### Master Viewing Key

```typescript theme={null}
import { getMasterViewingKeyDeriver } from "@umbra-privacy/sdk/crypto/key-derivation";

// Derives the root of the hierarchy
const deriveMvk = getMasterViewingKeyDeriver({ client });
const mvk = await deriveMvk();

// mvk is a bigint (BN254 field element)
console.log("Master Viewing Key:", mvk.toString());
```

<Warning>
  The master viewing key gives unrestricted read access to all your mixer activity - past and future. Share it only with fully trusted parties. For audits with a limited scope, always use a scoped sub-key.
</Warning>

### Mint Viewing Key

```typescript theme={null}
import { getMintViewingKeyDeriver } from "@umbra-privacy/sdk/crypto/key-derivation";

const USDC = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";

// Derives the mint-specific viewing key
const deriveMintVk = getMintViewingKeyDeriver({ client });
const mintVk = await deriveMintVk(USDC);

// Scope: all time, but only USDC activity
console.log("USDC Viewing Key:", mintVk.toString());
```

### Yearly Viewing Key

```typescript theme={null}
import { getYearlyViewingKeyDeriver } from "@umbra-privacy/sdk/crypto/key-derivation";
import type { Year } from "@umbra-privacy/sdk/types";

const USDC = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";

// Derives the yearly viewing key for 2025 under the USDC mint key
const deriveYearlyVk = getYearlyViewingKeyDeriver({ client });
const yearlyVk = await deriveYearlyVk(USDC, 2025n as Year);

// Scope: all USDC activity in 2025 only
console.log("2025 USDC Viewing Key:", yearlyVk.toString());
```

### Monthly Viewing Key

```typescript theme={null}
import { getMonthlyViewingKeyDeriver } from "@umbra-privacy/sdk/crypto/key-derivation";
import type { Year, Month } from "@umbra-privacy/sdk/types";

const USDC = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";

// Derives the monthly viewing key for February 2025
const deriveMonthlyVk = getMonthlyViewingKeyDeriver({ client });
const monthlyVk = await deriveMonthlyVk(USDC, 2025n as Year, 2n as Month);

// Scope: USDC activity in February 2025 only
console.log("Feb 2025 USDC Viewing Key:", monthlyVk.toString());
```

### Daily Viewing Key

```typescript theme={null}
import { getDailyViewingKeyDeriver } from "@umbra-privacy/sdk/crypto/key-derivation";
import type { Year, Month, Day } from "@umbra-privacy/sdk/types";

const USDC = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";

// Derives the daily viewing key for February 14, 2025
const deriveDailyVk = getDailyViewingKeyDeriver({ client });
const dailyVk = await deriveDailyVk(USDC, 2025n as Year, 2n as Month, 14n as Day);

// Scope: USDC activity on Feb 14, 2025 only
console.log("Feb 14 2025 USDC Viewing Key:", dailyVk.toString());
```

## Exporting and Sharing a Viewing Key

Viewing keys are `bigint` values (BN254 field elements). Export them as decimal or hex strings for out-of-band sharing with an auditor:

```typescript theme={null}
// Export as decimal string (lossless, unambiguous)
function exportViewingKey(key: bigint): string {
  return key.toString();
}

// Export as hex string (compact)
function exportViewingKeyHex(key: bigint): string {
  return "0x" + key.toString(16).padStart(64, "0");
}

// Import back from decimal string
function importViewingKey(decimal: string): bigint {
  return BigInt(decimal);
}

// Import back from hex string
function importViewingKeyHex(hex: string): bigint {
  return BigInt(hex);
}
```

### Sharing Example

```typescript theme={null}
import { getMonthlyViewingKeyDeriver } from "@umbra-privacy/sdk/crypto/key-derivation";
import type { Year, Month } from "@umbra-privacy/sdk/types";

// 1. Choose a scope - e.g., monthly key for an audit of Q1 2025
const USDC = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
const deriveMonthlyVk = getMonthlyViewingKeyDeriver({ client });

const q1Keys = await Promise.all([
  deriveMonthlyVk(USDC, 2025n as Year, 1n as Month), // January
  deriveMonthlyVk(USDC, 2025n as Year, 2n as Month), // February
  deriveMonthlyVk(USDC, 2025n as Year, 3n as Month), // March
]);

// 2. Export as strings
const q1Strings = q1Keys.map((key) => key.toString());

// 3. Share with auditor out-of-band (email, secure document, etc.)
const auditPackage = {
  granterAddress: client.signer.address,
  token: USDC,
  period: "Q1 2025",
  viewingKeys: {
    "2025-01": q1Strings[0],
    "2025-02": q1Strings[1],
    "2025-03": q1Strings[2],
  },
};

console.log(JSON.stringify(auditPackage, null, 2));
```

## How Key Derivation Works

Viewing keys are derived using the Poseidon hash function over BN254. Starting from the master viewing key:

```
MintViewingKey(mint) = Poseidon(MVK, mint_low_u128, mint_high_u128)

YearlyViewingKey(mint, year) = Poseidon(MintViewingKey(mint), year)

MonthlyViewingKey(mint, year, month) = Poseidon(YearlyViewingKey(mint, year), month)

DailyViewingKey(mint, year, month, day) = Poseidon(MonthlyViewingKey(mint, year, month), day)
```

The mint address is split into two 128-bit halves before being passed to Poseidon, as BN254 field elements are 252-bit values and a 256-bit pubkey would exceed the field size.

This structure means:

* You cannot derive a parent key from a child key - the tree is one-directional
* Child keys can always be re-derived from the parent at any time
* Two separate mint keys cannot be linked without the master viewing key

## Security Properties

* **One-directional** - a grantee who holds a monthly key cannot derive the yearly key, the master viewing key, or any key for another mint or time period
* **Unlinkable** - viewing keys for different months or mints are computationally unrelated without the master key
* **Deterministic** - the same wallet on the same network always produces the same viewing key hierarchy; keys can be re-derived at any time
* **Non-transactional** - sharing a viewing key does not create any on-chain state; it is a purely off-chain credential

## What Auditors Can Do With a Viewing Key

An auditor who holds a viewing key can:

* Scan the Stealth Pool (Indexed Merkle Tree) for notes that were encrypted under the corresponding key.
* Decrypt the note `pc_encrypted_*` payloads (Poseidon stream cipher) to learn the amount and recipient.
* Verify that specific transactions occurred within the defined scope.

An auditor cannot:

* Access any data outside the scope of the key they hold.
* Burn Stealth Pool Notes — viewing keys are read-only, not spending keys.
* Link the activity to any adjacent scope without the parent key.
* Decrypt the X25519 + AES-GCM ciphertext (`aes_encrypted_data`) or the Rescue ciphertext (`rc_encrypted_*`) — those use independent keys.

<Note>
  No opinionated `getViewingKey…NoteScannerFunction` factory is shipped — auditors compose one from the shipped primitives. Pattern (\~50 lines): for each note, call `getSecondViewingKeyDeriver` for the granted scope, then call `getPoseidonDecryptor` with the Second Viewing Key as the cipher key and the keystream `Poseidon([transactionViewingKey, counter, 2n])` to recover the plaintext.
</Note>
