> ## Documentation Index
> Fetch the complete documentation index at: https://fhenix-docs-deep-dive-rewrite.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Decryption Flow

> How CoFHE values get decrypted: decryptForTx with onchain publication, decryptForView with sealed output

Decryption in CoFHE is SDK-driven and happens offchain, inside [Teecryptor](/deep-dive/cofhe-components/teecryptor). A contract's role is to grant access and, when the result should go onchain, to read it back after publication.

<Note>
  **Contracts cannot request decryption onchain.** The TaskManager rejects decrypt tasks (`DecryptFunctionNotSupported`), and `FHE.decrypt` no longer exists in the FHE library. The coprocessor never pushes plaintexts into your contract.
</Note>

There are two SDK entry points, one per destination:

* **`decryptForTx`**: returns the plaintext with a Teecryptor signature you can publish onchain. Guide: [Decrypt to Tx](/client-sdk/guides/decrypt-to-tx).
* **`decryptForView`**: returns the plaintext sealed to your [permit](/client-sdk/guides/permits) (an ACP, Access Control Permission, onchain), for UI display and offchain reads. Guide: [Decrypt to View](/client-sdk/guides/decrypt-to-view).

<Note>
  A freshly computed handle may not be decryptable immediately: its ciphertext and commitment land shortly after the transaction. Until then Teecryptor answers with a retryable status and the SDK re-submits automatically.
</Note>

## The shared pipeline

Both entry points start the same way.

<Steps>
  <Step title="Contract grants access">
    The contract that owns the encrypted value marks the handle decryptable: `FHE.allow(handle, account)` for a specific account, or `FHE.allowGlobal(handle)` when the value may become public. Without an ACL grant, Teecryptor refuses the request.
  </Step>

  <Step title="SDK request">
    The application calls one of the two builders:

    ```typescript theme={null}
    const { ctHash, decryptedValue, signature } = await client
      .decryptForTx(ctHash)
      .withPermit()
      .execute();
    ```

    ```typescript theme={null}
    const balance = await client
      .decryptForView(ctHash, FheTypes.Uint32)
      .withPermit()
      .execute();
    ```

    The request goes to Teecryptor with the handle, the host chain id, and the permit. For publicly decryptable handles, `decryptForTx` can use `.withoutPermit()` instead.
  </Step>

  <Step title="Teecryptor verifies and decrypts">
    Teecryptor authorizes the request against the onchain ACL, fetches the ciphertext exactly as stored, verifies it against the onchain commitment, and decrypts it inside the attested enclave. See the [Teecryptor page](/deep-dive/cofhe-components/teecryptor) for the full pipeline.
  </Step>
</Steps>

From here the two paths diverge.

## The transaction path

For `decryptForTx`, Teecryptor returns the plaintext together with an ECDSA signature over a fixed 76-byte message (result, encryption type, chain id, ciphertext hash). The signing key lives only inside the enclave, and its address is registered onchain as the TaskManager's `decryptResultSigner`.

Anyone holding the signature submits it in a transaction:

```solidity theme={null}
FHE.publishDecryptResult(ctHash, result, signature);
```

The TaskManager recomputes the message hash, recovers the signer, and rejects anything not signed by `decryptResultSigner`. On success it stores the plaintext in PlaintextsStorage and emits a `DecryptionResult` event. `publishDecryptResultBatch` amortizes gas across multiple results.

<Note>
  Publication is permissionless: any relayer with a valid signature can deliver the result. `decryptForTx` itself costs no gas; gas is paid only by this publish transaction.
</Note>

Once published, any contract reads the plaintext:

```solidity theme={null}
uint64 value = FHE.getDecryptResult(handle);                     // reverts if not yet published
(uint64 value, bool ready) = FHE.getDecryptResultSafe(handle);   // non-reverting variant
```

To check a signature without storing the result, use the `verifyDecryptResult` family on the TaskManager.

## The view path

For `decryptForView`, Teecryptor never returns a bare plaintext. It encrypts the result to the permit's sealing key, and the SDK unseals it locally, so the plaintext is not exposed in transit. There is nothing to publish; the value goes straight to your application.

## Flow diagram

```mermaid theme={null}
%%{init: {"theme": "base", "themeVariables": {"fontFamily": "Menlo, Monaco, Consolas, monospace", "fontSize": "16px", "primaryColor": "#8FBAF5", "primaryBorderColor": "#2E7CF6", "primaryTextColor": "#0A1626", "lineColor": "#4C8DFF", "signalColor": "#4C8DFF", "signalTextColor": "#8FA3BF", "actorBkg": "#8FBAF5", "actorBorder": "#2E7CF6", "actorTextColor": "#0A1626", "actorLineColor": "#3D4654", "noteBkgColor": "#14171C", "noteBorderColor": "#3D4654", "noteTextColor": "#AFC3DE", "activationBkgColor": "#1E3A5F", "activationBorderColor": "#4C8DFF", "clusterBkg": "#14171C", "clusterBorder": "#3D4654", "titleColor": "#E7EAEE", "edgeLabelBackground": "#8FBAF5", "textColor": "#AFC3DE", "labelTextColor": "#E7EAEE", "tertiaryColor": "#14171C", "loopTextColor": "#AFC3DE", "labelBoxBkgColor": "#1E3A5F", "labelBoxBorderColor": "#4C8DFF"}, "sequence": {"actorFontFamily": "Menlo, Monaco, Consolas, monospace", "messageFontFamily": "Menlo, Monaco, Consolas, monospace", "noteFontFamily": "Menlo, Monaco, Consolas, monospace", "width": 220, "actorFontSize": 16, "messageFontSize": 16, "noteFontSize": 15}}}%%
sequenceDiagram
    participant App as Your app
    participant SDK as Client SDK
    participant Teecryptor
    participant Chain as Host chain

    App->>SDK: decryptForTx / decryptForView (ctHash)
    SDK->>Teecryptor: decrypt or sealoutput request (handle, chain id, acp?)
    Teecryptor->>Chain: ACL check (isAllowedWithPermission / isPubliclyAllowed)
    Teecryptor->>Teecryptor: verify commitment, decrypt in enclave
    alt decryptForTx
        Teecryptor-->>SDK: plaintext + signature
        SDK-->>App: { ctHash, decryptedValue, signature }
        App->>Chain: FHE.publishDecryptResult(ctHash, result, signature)
    else decryptForView
        Teecryptor-->>SDK: result sealed to the permit's sealing key
        SDK->>SDK: unseal locally
        SDK-->>App: plaintext
    end
```

## Comparison

|                          | `decryptForTx`                                                                                                                          | `decryptForView`                                   |
| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------- |
| **Returns**              | Plaintext + Teecryptor signature                                                                                                        | Plaintext (sealed in transit, unsealed by the SDK) |
| **Use case**             | Submit the decrypted value onchain                                                                                                      | Display in a UI, offchain reads                    |
| **Requires permit**      | Only if the handle is not publicly decryptable                                                                                          | Yes                                                |
| **Onchain verification** | <code style={{ whiteSpace: "nowrap" }}>publishDecryptResult</code> or <code style={{ whiteSpace: "nowrap" }}>verifyDecryptResult</code> | Not applicable                                     |
| **Gas cost**             | None for the decryption itself; gas only for the publish transaction                                                                    | None                                               |

## Future plans

Decryption is currently performed by Teecryptor inside a hardware-attested TEE. A multi-party Threshold Network is the planned successor; see [Future Plans](/deep-dive/research/future-plans).
