> ## Documentation Index
> Fetch the complete documentation index at: https://fhenix-ci-version-drift-check.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Decrypt to View

> Reveal encrypted values locally for UI display using ACPs

Use `decryptForView` to reveal a confidential (encrypted) value locally in your app so you can display it in the UI.

Unlike [`decryptForTx`](/client-sdk/guides/decrypt-to-tx), this flow does **not** return an onchain-verifiable signature, and it is **not** meant to be published onchain.

## Flow

1. Read the encrypted handle (`ctHash`) from your contract.
2. Ensure you have an ACP that authorizes decryption of that value.
3. Call `decryptForView(ctHash, utype).execute()` to get the plaintext.

<Note>
  `decryptForView` always decrypts using an ACP (there is no `.withoutACP()` mode). If your protocol intends for the plaintext to become publicly visible onchain, use [`decryptForTx`](/client-sdk/guides/decrypt-to-tx) instead.
</Note>

## Prerequisites

1. [Create and connect a client](/client-sdk/guides/client-setup).
2. Know the encrypted handle (`ctHash`) and the encrypted type (`utype`).
3. Have a [ACP](/client-sdk/guides/acps) available for the connected `chainId + account`.

<Tip>
  **Getting `ctHash`**: In most apps, `ctHash` comes from reading a stored encrypted value, an event arg, or a return value from a `view` call.
</Tip>

<Tip>
  **Providing `utype`**: `utype` must match the ciphertext's underlying FHE type. The SDK uses it to convert the decrypted `bigint` into a convenient JS type.

  Supported `utype`s:

  * `FheTypes.Bool` to returns a `boolean`
  * `FheTypes.Uint160` (address) to returns a checksummed `0x...` string
  * `FheTypes.Uint8 | Uint16 | Uint32 | Uint64 | Uint128` to returns a `bigint`
</Tip>

## ACP setup

If you don't have an ACP yet, create one once after connecting:

```typescript theme={null}
await client.connect(publicClient, walletClient);

// Creates an ACP if needed, stores it, and selects it as the active ACP.
await client.acp.getOrCreateSelfACP();
```

## Decrypt for UI

Choose the pattern that matches how your app manages ACPs:

<CodeGroup>
  ```typescript Active ACP theme={null}
  await client.connect(publicClient, walletClient);
  await client.acp.getOrCreateSelfACP();

  const plaintext = await client
    .decryptForView(ctHash, FheTypes.Uint32)
    .execute();
  ```

  ```typescript ACP object theme={null}
  const acp = await client.acp.getOrCreateSelfACP();

  const plaintext = await client
    .decryptForView(ctHash, FheTypes.Uint64)
    .withACP(acp)
    .execute();
  ```

  ```typescript ACP hash theme={null}
  const plaintext = await client
    .decryptForView(ctHash, FheTypes.Uint8)
    .withACP(acpHash)
    .execute();
  ```
</CodeGroup>

## What `decryptForView` returns

Running `.execute()` resolves to a scalar JS value:

* Integer utypes (`Uint8`, `Uint16`, `Uint32`, `Uint64`, `Uint128`): a `bigint`
* `FheTypes.Bool`: a `boolean`
* `FheTypes.Uint160` (address): a checksummed `0x...` address string

## Builder API

### `.execute()` (required, call last)

Runs the decryption and returns a UI-friendly scalar value.

### `.withACP(...)` (optional)

Select which ACP to use:

* `.withACP()`: uses the active ACP
* `.withACP(acpHash)`: fetches a stored ACP by hash
* `.withACP(acp)`: uses the provided ACP object

If you don't call `.withACP(...)`, the active ACP is used by default.

### `.setAccount(address)` (optional)

Overrides the account used to resolve the active/stored ACP.

### `.setChainId(chainId)` (optional)

Overrides the chain used to resolve the Threshold Network URL and ACPs.

### `.onPoll(callback)` (optional)

Register a callback that fires once per poll attempt while `decryptForView` waits for the Threshold Network to return the sealed plaintext. Useful for surfacing decrypt progress in a UI.

```typescript theme={null}
const plaintext = await client
  .decryptForView(ctHash, FheTypes.Uint64)
  .onPoll(({ operation, requestId, attemptIndex, elapsedMs, intervalMs, timeoutMs }) => {
    console.log(`[${operation}] attempt ${attemptIndex} after ${elapsedMs}ms (next in ${intervalMs}ms, budget ${timeoutMs}ms)`);
  })
  .execute();
```

The callback receives:

| Field          | Type                        | Description                                                                                                                     |
| -------------- | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `operation`    | `'decrypt' \| 'sealoutput'` | Which Threshold Network flow is polling. For `decryptForView` this is `'sealoutput'`.                                           |
| `requestId`    | `string`                    | The Threshold Network request id. **May be the empty string** during submit-time retries, see `.set404RetryTimeout(...)` below. |
| `attemptIndex` | `number`                    | Zero-based poll attempt counter.                                                                                                |
| `elapsedMs`    | `number`                    | Time since the first submit attempt.                                                                                            |
| `intervalMs`   | `number`                    | Delay until the next poll.                                                                                                      |
| `timeoutMs`    | `number`                    | Overall budget shared by submit-retries and status-polling.                                                                     |

### `.set404RetryTimeout(timeoutMs)` (optional)

Configures how long `decryptForView` keeps retrying when the Threshold Network's submit endpoint responds with `404 Not Found` before a `requestId` is available. This typically happens on slower backends where the ciphertext isn't visible yet at submit time. Defaults to `10_000` ms.

```typescript theme={null}
const plaintext = await client
  .decryptForView(ctHash, FheTypes.Uint32)
  .set404RetryTimeout(20_000) // give a slower backend more time
  .execute();
```

Pass `0` to disable submit-time retries (`404` becomes a hard failure). Submit retries share the same overall timeout budget as status polling.

## Common UI patterns

<CodeGroup>
  ```typescript Format bigint for display theme={null}
  import { formatUnits } from 'viem';

  const decimals = 6;
  const display = formatUnits(amount, decimals);
  ```

  ```typescript Bigint → number (range-check) theme={null}
  const MAX_SAFE = BigInt(Number.MAX_SAFE_INTEGER);
  const asNumber = amount <= MAX_SAFE ? Number(amount) : undefined;
  ```

  ```typescript Booleans & addresses theme={null}
  const statusLabel = decryptedIsAllowed ? 'Allowed' : 'Not allowed';
  const shortOwner = `${decryptedOwner.slice(0, 6)}…${decryptedOwner.slice(-4)}`;
  ```
</CodeGroup>

## Common pitfalls

* **Missing ACP**: `decryptForView` will fail if there is no active ACP for the current `chainId + account`.
* **Wrong `utype`**: you must pass the correct FHE type for the ciphertext.
* **Wrong chain/account**: ACPs are scoped to `chainId + account`. If the user switches wallets or networks, create/select the correct ACP.
