Skip to main content

Uploader

The Uploader sub-client handles vault allocation, TDH2 encryption, and writing encrypted data on-chain. Requires a walletClient.
const uploader = client.uploader;
Use uploadCDR() for small secrets stored directly in the vault and uploadFile() when the encrypted bytes should live in an external storage backend.
v0.1.1 also exposes createVault as an alias for uploadCDR, and createFileVault as an alias for uploadFile.

Methods

  • uploadCDR
  • uploadFile
  • allocate
  • write
  • encryptDataKey

uploadCDR

High-level method that allocates a vault, encrypts your data, and writes the ciphertext in a single call.
MethodType
uploadCDR(params: UploadCDRParams) => Promise<UploadCDRResponse>
Parameters:
  • params.dataKey: Uint8Array - The secret payload bytes to encrypt. Despite the name, this can be arbitrary data, not only a cryptographic key.
  • params.globalPubKey: Uint8Array - The DKG global public key (from observer.getGlobalPubKey())
  • params.updatable: boolean - Whether the vault can be rewritten after initial write
  • params.writeConditionAddr: `0x${string}` - Address of the write condition contract
  • params.readConditionAddr: `0x${string}` - Address of the read condition contract
  • params.writeConditionData: `0x${string}` - ABI-encoded data passed to the write condition
  • params.readConditionData: `0x${string}` - ABI-encoded data passed to the read condition
  • params.accessAuxData: `0x${string}` - Auxiliary data passed to conditions during write
  • params.allocateFeeOverride (optional): bigint - Skip fee query and use this value
  • params.writeFeeOverride (optional): bigint - Skip fee query and use this value
Example
import { encodeAbiParameters } from "viem";

const OWNER_CONDITION = "0x4C9bFC96d7092b590D497A191826C3dA2277c34B";
const ownerConditionData = encodeAbiParameters(
  [{ type: "address" }],
  [walletClient.account!.address],
);

const globalPubKey = await client.observer.getGlobalPubKey();
const dataKey = new TextEncoder().encode("my secret");

const { uuid, ciphertext, txHashes } = await client.uploader.uploadCDR({
  dataKey,
  globalPubKey,
  updatable: false,
  writeConditionAddr: OWNER_CONDITION,
  readConditionAddr: OWNER_CONDITION,
  writeConditionData: ownerConditionData,
  readConditionData: ownerConditionData,
  accessAuxData: "0x",
});

console.log(`Vault UUID: ${uuid}`);
console.log(`Allocate tx: ${txHashes.allocate}`);
console.log(`Write tx: ${txHashes.write}`);
Keep uploadCDR() payloads small enough that the resulting TDH2 ciphertext fits the vault limit (observer.getMaxEncryptedDataSize(), which is 1024 bytes on Aeneid).
UploadCDRResponse
interface UploadCDRResponse {
  uuid: number;
  ciphertext: TDH2Ciphertext;
  txHashes: {
    allocate: `0x${string}`;
    write: `0x${string}`;
  };
}

uploadFile

High-level method that encrypts file bytes locally, uploads the encrypted blob through a StorageProvider, and writes the encrypted file key plus content pointer to CDR in one call. Parameters:
  • params.content: Uint8Array - File bytes to encrypt and upload
  • params.storageProvider: StorageProvider - Backend used for upload and download
  • params.globalPubKey: Uint8Array - DKG global public key
  • params.updatable: boolean - Whether the vault can be rewritten
  • params.writeConditionAddr: `0x${string}` - Address of the write condition contract
  • params.readConditionAddr: `0x${string}` - Address of the read condition contract
  • params.writeConditionData: `0x${string}` - ABI-encoded write condition data
  • params.readConditionData: `0x${string}` - ABI-encoded read condition data
  • params.accessAuxData: `0x${string}` - Auxiliary data passed to conditions during write
  • params.allocateFeeOverride (optional): bigint - Skip the allocate fee query
  • params.writeFeeOverride (optional): bigint - Skip the write fee query
Example
import { HeliaProvider } from "@piplabs/cdr-sdk";
import { readFile } from "node:fs/promises";
import { createHelia } from "helia";
import { unixfs } from "@helia/unixfs";
import { CID } from "multiformats/cid";
import { encodeAbiParameters } from "viem";

const helia = await createHelia();
const storage = new HeliaProvider({
  helia,
  unixfs: unixfs(helia),
  CID: (s) => CID.parse(s),
});

const OWNER_CONDITION = "0x4C9bFC96d7092b590D497A191826C3dA2277c34B";
const ownerConditionData = encodeAbiParameters(
  [{ type: "address" }],
  [walletClient.account!.address],
);

const fileBytes = await readFile("./example.pdf");
const globalPubKey = await client.observer.getGlobalPubKey();
const { uuid, cid } = await client.uploader.uploadFile({
  content: new Uint8Array(fileBytes),
  storageProvider: storage,
  globalPubKey,
  updatable: false,
  writeConditionAddr: OWNER_CONDITION,
  readConditionAddr: OWNER_CONDITION,
  writeConditionData: ownerConditionData,
  readConditionData: ownerConditionData,
  accessAuxData: "0x",
});

console.log(`Vault UUID: ${uuid}`);
console.log(`Stored CID: ${cid}`);
HeliaProvider is the only storage backend fully tested on Aeneid in the current release. GatewayProvider, StorachaProvider, and SynapseProvider are implemented but were not yet end-to-end validated in the release run.
uploadFile() keeps the file bytes off-chain. The vault stores a TDH2 ciphertext of a small JSON payload containing { cid, key }.
In browser code, pass file bytes from new Uint8Array(await file.arrayBuffer()) instead of readFile(...).

allocate

Creates a new CDR vault on-chain with the specified access control conditions.
MethodType
allocate(params: AllocateParams) => Promise<AllocateResponse>
Parameters:
  • params.updatable: boolean - Whether the vault can be rewritten
  • params.writeConditionAddr: `0x${string}` - Write condition contract address
  • params.readConditionAddr: `0x${string}` - Read condition contract address
  • params.writeConditionData: `0x${string}` - ABI-encoded write condition data
  • params.readConditionData: `0x${string}` - ABI-encoded read condition data
  • params.feeOverride (optional): bigint - Skip fee query
  • params.skipConditionValidation (optional): boolean - Skip interface validation when intentionally using an EOA condition address
Example
const userAddress = walletClient.account!.address;

const { txHash, uuid } = await client.uploader.allocate({
  updatable: false,
  writeConditionAddr: userAddress,
  readConditionAddr: userAddress,
  writeConditionData: "0x",
  readConditionData: "0x",
  skipConditionValidation: true,
});

console.log(`Vault ${uuid} allocated at tx: ${txHash}`);
uploadCDR() and uploadFile() do not expose skipConditionValidation, so use a real condition contract with those high-level helpers.
AllocateResponse
interface AllocateResponse {
  txHash: `0x${string}`;
  uuid: number; // parsed from VaultAllocated event
}

write

Writes encrypted data to an existing vault. The caller must satisfy the vault’s write condition.
MethodType
write(params: WriteParams) => Promise<WriteResponse>
Parameters:
  • params.uuid: number - The vault UUID
  • params.accessAuxData: `0x${string}` - Auxiliary data passed to the write condition
  • params.encryptedData: `0x${string}` - Hex-encoded TDH2 ciphertext
  • params.feeOverride (optional): bigint - Skip fee query
Example
import { toHex } from "viem";

const { txHash } = await client.uploader.write({
  uuid: 42,
  accessAuxData: "0x",
  encryptedData: toHex(ciphertext.raw),
});
WriteResponse
interface WriteResponse {
  txHash: `0x${string}`;
}

encryptDataKey

Locally encrypts data using TDH2 threshold encryption. No blockchain interaction.
MethodType
encryptDataKey(params: EncryptParams) => Promise<TDH2Ciphertext>
Parameters:
  • params.dataKey: Uint8Array - The plaintext data to encrypt
  • params.globalPubKey: Uint8Array - DKG global public key (34 bytes)
  • params.label: Uint8Array - 32-byte label binding ciphertext to a vault (use uuidToLabel(uuid))
Example
import { uuidToLabel } from "@piplabs/cdr-sdk";

const label = uuidToLabel(uuid);
const ciphertext = await client.uploader.encryptDataKey({
  dataKey: new TextEncoder().encode("secret"),
  globalPubKey,
  label,
});

console.log(ciphertext.raw); // Uint8Array - serialized TDH2 ciphertext
console.log(ciphertext.label); // Uint8Array - the label used
TDH2Ciphertext
interface TDH2Ciphertext {
  raw: Uint8Array;   // serialized ciphertext (cb-mpc format)
  label: Uint8Array; // 32-byte context binding
}