> For the complete documentation index, see [llms.txt](https://docs.zama.org/protocol/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.zama.org/protocol/sdk/guides/delegated-decryption.md).

# Delegated decryption

Delegation lets one address grant another address the right to decrypt its confidential balances. The delegate never receives the delegator's private keys — they use their own transport key pair and a delegated EIP-712 flow to prove they have permission.

Common use cases:

* **Portfolio dashboards** — a read-only service decrypts balances across wallets without holding keys.
* **Auditors** — a third party verifies holdings without the token owner being online.

This guide uses `sdk.delegations` and `token.decryptBalanceAs`. Before starting, make sure your project is set up following the [Configuration](/protocol/sdk/guides/configuration.md) guide.

## Example

A complete delegation flow — grant, then decrypt as delegate (the SDK rides out ACL propagation for you):

{% tabs %}
{% tab title="SDK" %}

```ts
import { createConfig, ZamaSDK } from "@zama-fhe/sdk";
import { sepolia } from "@zama-fhe/sdk/chains";

const sdk = new ZamaSDK(config); // config from createConfig()
const token = sdk.createToken("0xConfidentialToken");

// 1. Delegator grants decryption rights
const { txHash } = await sdk.delegations.delegateDecryption({
  contractAddress: token.address,
  delegateAddress: "0xDelegate",
});

// 2. Delegate reads the delegator's balance — no wait needed. Propagation
//    usually completes within ~10 blocks (a few seconds), and the SDK retries
//    across that window internally.
const balance = await token.decryptBalanceAs({ delegatorAddress: "0xDelegator" });
```

{% endtab %}
{% endtabs %}

## Steps

### 1. Grant delegation

The token owner calls `sdk.delegations.delegateDecryption` to allow a delegate to decrypt their balance for a specific contract.

{% tabs %}
{% tab title="SDK" %}

```ts
// Permanent delegation (no expiration)
await sdk.delegations.delegateDecryption({
  contractAddress: token.address,
  delegateAddress: "0xDelegate",
});

// Delegation with an expiration date
await sdk.delegations.delegateDecryption({
  contractAddress: token.address,
  delegateAddress: "0xDelegate",
  expirationDate: new Date("2027-12-31T00:00:00Z"),
});
```

{% endtab %}
{% endtabs %}

Both calls return `{ txHash, receipt }`.

{% hint style="warning" %}
The expiration date must be **at least 1 hour in the future**. Passing a closer date throws `DelegationExpirationTooSoonError` before the transaction is sent.
{% endhint %}

Each call grants delegation for a single `(contractAddress, delegateAddress)` pair and submits one on-chain transaction.

### 2. ACL propagation (handled for you)

After the delegation transaction is mined, the Zama Gateway (on Arbitrum) syncs the ACL state via cross-chain event propagation — usually within \~10 blocks (a few seconds). You don't need to wait or poll: the delegated-decrypt path rides out that window with a bounded internal retry (\~30s), so a decrypt issued right after granting simply waits for sync.

{% hint style="info" %}
`DelegationNotPropagatedError` only surfaces if propagation outlasts the retry budget (rare) — or if you opt out of the wait with `waitForPropagation: false` on `sdk.decryption.delegatedDecryptValues` to fail fast instead.
{% endhint %}

### 3. Decrypt as delegate

The delegate calls `token.decryptBalanceAs` to read the delegator's balance. The delegate signs with their own wallet, and the relayer verifies the on-chain delegation before decrypting.

{% tabs %}
{% tab title="SDK" %}

```ts
const balance = await token.decryptBalanceAs({ delegatorAddress: "0xDelegator" });
```

{% endtab %}
{% endtabs %}

When the balance holder differs from the delegator, pass `accountAddress` explicitly:

```ts
const balance = await token.decryptBalanceAs({
  delegatorAddress: "0xDelegator",
  accountAddress: "0xBalanceHolder",
});
```

Clear values are cached in storage, keyed by `(accountAddress, token, encryptedValue)`. Every on-chain balance change produces a new encrypted value, so stale cache entries are never served.

### 4. Batch decryption across tokens (optional)

Decrypt balances across multiple tokens in a single call:

{% tabs %}
{% tab title="SDK" %}

```ts
import { Token } from "@zama-fhe/sdk";

const tokens = addresses.map((a) => sdk.createToken(a));

const balances = await Token.batchDecryptBalancesAs(tokens, { delegatorAddress: "0xDelegator" });

// balances is a Map<Address, bigint>
for (const [address, balance] of balances) {
  console.log(`${address}: ${balance}`);
}
```

{% endtab %}
{% endtabs %}

Handle errors for individual tokens with `onError`:

```ts
const balances = await Token.batchDecryptBalancesAs(tokens, {
  delegatorAddress: "0xDelegator",
  maxConcurrency: 3,
  onError: (err, addr) => {
    console.error(addr, err);
    return 0n;
  },
});
```

### 5. Revoke delegation (optional)

```ts
await sdk.delegations.revokeDelegation({
  contractAddress: token.address,
  delegateAddress: "0xDelegate",
});
```

### 6. Handle errors (optional)

Delegation operations can throw several error types. The most common:

{% tabs %}
{% tab title="SDK" %}

```ts
import {
  DelegationNotPropagatedError,
  DelegationExpirationTooSoonError,
  SigningRejectedError,
  DecryptionFailedError,
  TransactionRevertedError,
} from "@zama-fhe/sdk";

try {
  await sdk.delegations.delegateDecryption({
    contractAddress: token.address,
    delegateAddress: "0xDelegate",
  });
} catch (error) {
  if (error instanceof DelegationExpirationTooSoonError) {
    // expiration date is less than 1 hour in the future
  } else if (error instanceof TransactionRevertedError) {
    // on-chain transaction failed
  }
}

try {
  const balance = await token.decryptBalanceAs({ delegatorAddress: "0xDelegator" });
} catch (error) {
  if (error instanceof SigningRejectedError) {
    // user cancelled the wallet prompt — do not retry automatically
  } else if (error instanceof DelegationNotPropagatedError) {
    // delegation still hadn't synced after the SDK's internal retry — rare; retry shortly
  } else if (error instanceof DecryptionFailedError) {
    // delegated decryption failed
  }
}
```

{% endtab %}
{% endtabs %}

See [Handle errors](/protocol/sdk/guides/handle-errors.md) for full error-handling patterns and [Error types](/protocol/sdk/api-references/sdk/errors.md) for the complete list.

## Next steps

* [Delegations reference](/protocol/sdk/api-references/sdk/delegation.md) — full `Delegations` namespace API
* [useDelegateDecryption](/protocol/sdk/api-references/react/usedelegatedecryption.md) — React hook to grant delegation
* [useDecryptBalanceAs](/protocol/sdk/api-references/react/usedecryptbalanceas.md) — React hook to decrypt as a delegate
* [useDelegationStatus](/protocol/sdk/api-references/react/usedelegationstatus.md) — React hook to query delegation status


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.zama.org/protocol/sdk/guides/delegated-decryption.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
