Skip to main content

Consumer

The Consumer sub-client handles read requests, partial decryption collection from validators, and final decryption. Requires a walletClient.
const consumer = client.consumer;
v0.1.1 also exposes readVault as an alias for accessCDR, and readFileVault as an alias for downloadFile.

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.
MethodType
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
Example
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}`);
If you want the shortest high-level path, you can omit requesterPubKey, recipientPrivKey, globalPubKey, and threshold and let accessCDR() fill them in.
AccessCDRResponse
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
Example
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");
For Story license-gated reads, accessAuxData should encode the caller’s license token IDs as abi.encode(uint256[] licenseTokenIds).
downloadFile() inherits the same optional key / threshold auto-management behavior as accessCDR().
content is the raw decrypted file bytes. Decode it as text only if the original file was text-based.

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.
MethodType
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
Example
const { txHash } = await client.consumer.read({
  uuid: 42,
  accessAuxData: "0x",
  requesterPubKey,
});
ReadResponse
interface ReadResponse {
  txHash: `0x${string}`;
}

collectPartials

Polls the blockchain for EncryptedPartialDecryptionSubmitted events until enough partial decryptions have been collected.
MethodType
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
Throws PartialCollectionTimeoutError if the timeout is reached before enough partials are collected.
Example
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`);
PartialDecryptionEvent
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
}
In cosmos-abci mode, requesterPubKey and signature are omitted because the keeper verifies signatures on ingress and does not persist those fields.

decryptDataKey

Decrypts the collected partial decryptions using ECIES, then combines them via TDH2 to recover the original plaintext.
MethodType
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
Throws InsufficientPartialsError if partials.length < threshold. Throws InvalidCiphertextError if the ciphertext is empty or malformed.
Example
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);