Skip to main content
This guide walks through the two main CDR flows:
  • uploadCDR / accessCDR for small secrets stored directly on-chain
  • uploadFile / downloadFile for larger encrypted files stored off-chain

Prerequisites

  • CDR SDK setup complete with WASM initialized and client created

What Runs On-Chain vs Off-Chain

Encrypt a Secret

The diagram below shows the on-chain secret flow: allocate a vault, encrypt the secret locally with TDH2, and write the ciphertext to the vault.
CDR encryption flow showing vault allocation, local encryption, and writing the ciphertext to the vault
The simplest “owner-only” pattern uses your wallet (EOA) address as both the write and read condition. The CDR contract bypasses the condition check when msg.sender equals the configured condition address, so only that wallet can write or read the vault. Because the high-level uploadCDR() helper validates that condition addresses point at deployed condition contracts, EOA conditions are configured through the low-level allocate() call with skipConditionValidation: true.
Any EOA address works as a write or read condition; only that EOA can perform the matching action. To gate just one side, set your wallet address on that side and a condition contract (such as LicenseReadCondition) on the other. The high-level uploadCDR() helper expects deployed condition contracts on both sides and does not support EOA conditions, so use it for patterns like DATA Foundation license-gated reads (see IP Asset Vaults) and use the low-level allocate() + write() flow above for owner-only EOA conditions.
The value of the transaction must be exactly the same as the fee.
dataKey is the historical parameter name. In encryptDataKey() it can be any secret bytes, not just a cryptographic key.
Vault encrypted data is limited to 1024 bytes on Aeneid (maxEncryptedDataSize). TDH2 adds overhead, so the maximum plaintext is smaller. For larger content, use uploadFile() so only a small {cid, key} payload is written to the vault.

Decrypt a Secret

Decryption requires submitting a read request on-chain, collecting partial decryptions from validators, and combining them client-side.
CDR decryption flow showing ephemeral key generation, access control check, partial decryptions from validators, and client-side combination
accessCDR() auto-generates the ephemeral keypair and auto-queries globalPubKey when you omit them. The threshold is derived automatically from the partial-decryption bucket’s DKG round.
The timeout of the request on the server side is 200 blocks, which is approximately 7 minutes. If you’re not able to collect enough partials within this timeout, try another read request.

Encrypt and Download a File

CDR encryption flow showing vault allocation, local encryption, and writing the encrypted key plus data URL to the vault
CDR decryption flow showing ephemeral key generation, access control check, partial decryptions from validators, and client-side combination
Use the file workflow when the encrypted payload should live off-chain and only the encrypted file key plus pointer should be stored in the vault. Upload happens once by the data owner. Download happens later by an authorized reader who recovers the vault payload and then decrypts the stored file. The uploadFile() helper requires deployed condition contracts on both sides, so the example below uses DATA Foundation’s OwnerWriteCondition for the write side and LicenseReadCondition for the read side. License token holders can decrypt the file (see IP Asset Vaults for the end-to-end license setup). For an owner-only file flow, replicate the low-level steps shown earlier with your wallet (EOA) address as both conditions.
HeliaProvider is the only storage backend fully tested on Aeneid in the current release, and it requires Node.js 22+.
uploadFile() and downloadFile() work with raw file bytes. In a browser, start from a File object and convert it with new Uint8Array(await file.arrayBuffer()).

Storage Providers

The encrypted-file workflow supports four storage backends:
  • HeliaProvider for in-process IPFS. This is the best starting point for development and the only backend fully tested on Aeneid so far.
  • GatewayProvider for an external IPFS HTTP API plus a gateway URL.
  • StorachaProvider for Storacha / web3.storage.
  • SynapseProvider for Filecoin-backed storage via Synapse.
If you use HeliaProvider, pass the CID.parse function into the constructor as shown above to avoid class mismatches.

Step-by-Step (Low-Level)

If you need more control over the process, you can call each step individually.
These snippets continue from the variables in the examples above: walletClient, globalPubKey, requesterPubKey, recipientPrivKey, and dataKey.

Encrypt (Low-Level)

Decrypt (Low-Level)

Query DKG State

You can query DKG state and fees without a wallet or WASM initialization:

Understanding Fees

Each CDR operation has an on-chain fee: Fees are paid in native tokens (wei) and are sent as msg.value with each transaction.