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

# Consumer

> Methods for requesting and performing CDR decryption.

## Consumer

The `Consumer` sub-client handles read requests, partial decryption collection from validators, and final decryption. Requires a `walletClient`.

```typescript theme={null}
const consumer = client.consumer;
```

<Note>
  `v0.1.1` also exposes `readVault` as an alias for `accessCDR`, and
  `readFileVault` as an alias for `downloadFile`.
</Note>

### Methods

* accessCDR
* downloadFile
* read
* collectPartials
* decryptDataKey

***

### accessCDR

High-level method that submits a read request, collects partial decryptions from validators, and combines them to recover the original data.

| Method      | Type                                                      |
| ----------- | --------------------------------------------------------- |
| `accessCDR` | `(params: AccessCDRParams) => Promise<AccessCDRResponse>` |

Parameters:

* `params.uuid`: `number` - The vault UUID
* `params.accessAuxData`: `` `0x${string}` `` - Auxiliary data passed to the read condition
* `params.requesterPubKey` *(optional)*: `` `0x${string}` `` - Uncompressed secp256k1 public key (65 bytes, `0x04` prefix). If omitted, the SDK generates an ephemeral keypair.
* `params.recipientPrivKey` *(optional)*: `Uint8Array` - 32-byte secp256k1 private key (for ECIES decryption of partials). If omitted, the SDK generates an ephemeral keypair.
* `params.globalPubKey` *(optional)*: `Uint8Array` - DKG global public key (from `observer.getGlobalPubKey()`). If omitted, the SDK queries it for you.
* `params.threshold` *(optional)*: `number` - Minimum partials needed (from `observer.getThreshold()`). If omitted, the SDK queries it for you.
* `params.timeoutMs` *(optional)*: `number` - Timeout for collecting partials (default: `60000`)
* `params.feeOverride` *(optional)*: `bigint` - Skip fee query
* `params.onInvalidPartial` *(optional)*: `(event, error) => void` - Called when a partial fails signature verification in `evm-events` mode

```typescript Example theme={null}
const { dataKey, txHash } = await client.consumer.accessCDR({
  uuid: 42,
  accessAuxData: "0x",
  timeoutMs: 120_000,
});

const secret = new TextDecoder().decode(dataKey);
console.log(`Read tx: ${txHash}`);
console.log(`Decrypted: ${secret}`);
```

<Note>
  If you want the shortest high-level path, you can omit
  `requesterPubKey`, `recipientPrivKey`, `globalPubKey`, and `threshold`
  and let `accessCDR()` fill them in.
</Note>

```typescript AccessCDRResponse theme={null}
interface AccessCDRResponse {
  dataKey: Uint8Array; // the recovered plaintext
  txHash: `0x${string}`; // read request transaction hash
}
```

***

### downloadFile

High-level method that reads the encrypted file key through CDR, downloads the
encrypted blob from a `StorageProvider`, and returns the decrypted file bytes.

Parameters:

* `params.uuid`: `number` - The vault UUID
* `params.accessAuxData`: `` `0x${string}` `` - Auxiliary data passed to the read condition
* `params.storageProvider`: `StorageProvider` - Backend used to fetch the encrypted content
* `params.requesterPubKey` *(optional)*: `` `0x${string}` `` - Explicit requester public key for the read flow
* `params.recipientPrivKey` *(optional)*: `Uint8Array` - Explicit recipient private key for the read flow
* `params.globalPubKey` *(optional)*: `Uint8Array` - DKG global public key. Auto-queried if omitted.
* `params.threshold` *(optional)*: `number` - Minimum partials needed. Auto-queried if omitted.
* `params.timeoutMs` *(optional)*: `number` - Timeout for validator partial collection
* `params.feeOverride` *(optional)*: `bigint` - Skip read fee query
* `params.onInvalidPartial` *(optional)*: `(event, error) => void` - Called when a partial fails signature verification in `evm-events` mode
* `params.skipCidVerification` *(optional)*: `boolean` - Skip CID integrity verification of the downloaded encrypted file

```typescript Example theme={null}
import { writeFile } from "node:fs/promises";

const { content } = await client.consumer.downloadFile({
  uuid: 42,
  accessAuxData: "0x",
  storageProvider,
  timeoutMs: 120_000,
});

await writeFile("./example.decrypted.pdf", Buffer.from(content));
console.log("Saved ./example.decrypted.pdf");
```

<Note>
  For Story license-gated reads, `accessAuxData` should encode the caller's
  license token IDs as `abi.encode(uint256[] licenseTokenIds)`.
</Note>

<Note>
  `downloadFile()` inherits the same optional key / threshold auto-management
  behavior as `accessCDR()`.
</Note>

<Note>
  `content` is the raw decrypted file bytes. Decode it as text only if the
  original file was text-based.
</Note>

***

### read

Submits a read request on-chain. The caller must satisfy the vault's read condition. Emits an event that validators listen for to begin generating partial decryptions.

| Method | Type                                            |
| ------ | ----------------------------------------------- |
| `read` | `(params: ReadParams) => Promise<ReadResponse>` |

Parameters:

* `params.uuid`: `number` - The vault UUID
* `params.accessAuxData`: `` `0x${string}` `` - Auxiliary data passed to the read condition
* `params.requesterPubKey`: `` `0x${string}` `` - Your ephemeral uncompressed secp256k1 public key
* `params.feeOverride` *(optional)*: `bigint` - Skip fee query

```typescript Example theme={null}
const { txHash } = await client.consumer.read({
  uuid: 42,
  accessAuxData: "0x",
  requesterPubKey,
});
```

```typescript ReadResponse theme={null}
interface ReadResponse {
  txHash: `0x${string}`;
}
```

***

### collectPartials

Polls the blockchain for `EncryptedPartialDecryptionSubmitted` events until enough partial decryptions have been collected.

| Method            | Type                                                                   |
| ----------------- | ---------------------------------------------------------------------- |
| `collectPartials` | `(params: CollectPartialsParams) => Promise<PartialDecryptionEvent[]>` |

Parameters:

* `params.uuid`: `number` - The vault UUID
* `params.minPartials`: `number` - Number of partials needed (= threshold)
* `params.fromBlock`: `bigint` - Block number where the read request was submitted
* `params.requesterPubKey` *(optional)*: `` `0x${string}` `` - Required in `cosmos-abci` mode
* `params.timeoutMs` *(optional)*: `number` - Timeout in milliseconds (default: `60000`)
* `params.pollIntervalMs` *(optional)*: `number` - Polling interval (default: `3000`)
* `params.onInvalidPartial` *(optional)*: `(event, error) => void` - Called when a partial fails signature verification in `evm-events` mode
* `params.attestationConfig` *(optional)*: `AttestationConfig` - Verifies each validator attestation and rejects invalid partials

<Warning>
  Throws `PartialCollectionTimeoutError` if the timeout is reached before enough partials are collected.
</Warning>

```typescript Example theme={null}
const receipt = await publicClient.getTransactionReceipt({ hash: readTxHash });

const partials = await client.consumer.collectPartials({
  uuid: 42,
  minPartials: threshold,
  fromBlock: receipt.blockNumber,
  timeoutMs: 120_000,
});

console.log(`Collected ${partials.length} partials`);
```

```typescript PartialDecryptionEvent theme={null}
interface PartialDecryptionEvent {
  validator: `0x${string}`;
  round: number;
  pid: number;                      // 1-based participant index
  encryptedPartial: `0x${string}`;  // AES-GCM encrypted
  ephemeralPubKey: `0x${string}`;   // 65 bytes, uncompressed secp256k1
  pubShare: `0x${string}`;          // 34 bytes, Ed25519 with prefix
  uuid: number;
  requesterPubKey?: `0x${string}`;  // optional: evm-events only
  signature?: `0x${string}`;        // optional: evm-events only
}
```

<Note>
  In `cosmos-abci` mode, `requesterPubKey` and `signature` are omitted because
  the keeper verifies signatures on ingress and does not persist those fields.
</Note>

***

### decryptDataKey

Decrypts the collected partial decryptions using ECIES, then combines them via TDH2 to recover the original plaintext.

| Method           | Type                                             |
| ---------------- | ------------------------------------------------ |
| `decryptDataKey` | `(params: DecryptParams) => Promise<Uint8Array>` |

Parameters:

* `params.ciphertext`: `TDH2Ciphertext` - The encrypted data (`{ raw, label }`)
* `params.partials`: `PartialDecryptionEvent[]` - Collected partial decryptions
* `params.recipientPrivKey`: `Uint8Array` - Your ephemeral secp256k1 private key (32 bytes)
* `params.globalPubKey`: `Uint8Array` - DKG global public key
* `params.label`: `Uint8Array` - 32-byte label (from `uuidToLabel(uuid)`)
* `params.threshold`: `number` - Minimum partials needed

<Warning>
  Throws `InsufficientPartialsError` if `partials.length < threshold`.
  Throws `InvalidCiphertextError` if the ciphertext is empty or malformed.
</Warning>

```typescript Example theme={null}
import { uuidToLabel } from "@piplabs/cdr-sdk";

const label = uuidToLabel(uuid);
const dataKey = await client.consumer.decryptDataKey({
  ciphertext: { raw: ciphertextBytes, label },
  partials,
  recipientPrivKey,
  globalPubKey,
  label,
  threshold,
});

const secret = new TextDecoder().decode(dataKey);
```
