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

# Decrypt to Transact

> Decrypt with a verifiable Threshold Network signature for on-chain use

Use `decryptForTx` to reveal a confidential (encrypted) value on-chain: it returns the plaintext together with a Threshold Network signature, so a contract can verify the reveal when you publish it in a transaction.

Common use cases:

* **Unshield a confidential token**: reveal the encrypted amount you're unshielding so the contract can finalize the public transfer.
* **Finalize a private auction / game move**: bids or moves are submitted encrypted, and the winner is revealed later in a verifiable way.

<Note>
  If you only need to show plaintext in your UI (and you do **not** need an on-chain-verifiable signature), use [`decryptForView`](/client-sdk/guides/decrypt-to-view) instead.
</Note>

## Prerequisites

1. [Create and connect a client](/client-sdk/guides/client-setup).
2. Know the on-chain encrypted handle (`ctHash`) you want to decrypt.
3. Determine whether the contract's ACL policy for this `ctHash` requires a [permit](/client-sdk/guides/permits).

<Note>
  `decryptForTx` does not take a `utype`. It always returns the plaintext as a `bigint` because the result is intended to be passed into a transaction. If you need UI-friendly decoding, use [`decryptForView`](/client-sdk/guides/decrypt-to-view).
</Note>

## Permit: when is it needed?

Often, `decryptForTx` is used to reveal a value that the protocol already considers OK to make public. In those cases, the contract's ACL policy can allow anyone to decrypt, and you can use `.withoutPermit()`.

Examples where a permit is **not** needed:

* **Unshielding**: the amount being unshielded is no longer meant to stay secret.
* **Auction/game reveal**: it doesn't matter who submits the reveal — only that the result is verified.

If the ACL policy restricts decryption, you must use `.withPermit(...)`.

## What `decryptForTx` returns

`.execute()` resolves to an object with:

* `ctHash: bigint | string` — the ciphertext handle you decrypted
* `decryptedValue: bigint` — the plaintext value (always a `bigint`)
* `signature: 0x${string}` — the Threshold Network signature as a hex string

## Decrypt (choose permit mode)

<CodeGroup>
  ```typescript No permit theme={null}
  const decryptResult = await client
    .decryptForTx(ctHash)
    .withoutPermit()
    .execute();

  decryptResult.decryptedValue;
  decryptResult.signature;
  ```

  ```typescript Active permit theme={null}
  const decryptResult = await client
    .decryptForTx(ctHash)
    .withPermit()
    .execute();
  ```

  ```typescript Explicit permit theme={null}
  const permit = await client.permits.getOrCreateSelfPermit();

  const decryptResult = await client
    .decryptForTx(ctHash)
    .withPermit(permit)
    .execute();
  ```
</CodeGroup>

After decrypting, see [Writing Decrypt Result to Contract](/client-sdk/guides/writing-decrypt-result) for how to publish or verify the result on-chain.

## Builder API

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

Runs the decryption and returns `{ ctHash, decryptedValue, signature }`.

### `.withPermit(...)` — required unless using `.withoutPermit()`

* `.withPermit()` — uses the active permit
* `.withPermit(permitHash)` — fetches a stored permit by hash
* `.withPermit(permit)` — uses the provided permit object

### `.withoutPermit()` — required unless using `.withPermit(...)`

Decrypt via global allowance (no permit). Only works if the contract's ACL policy allows anyone to decrypt that `ctHash`.

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

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

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

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

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

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

```typescript theme={null}
const decryptResult = await client
  .decryptForTx(ctHash)
  .withoutPermit()
  .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 `decryptForTx` this is always `'decrypt'`.                                          |
| `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 `decryptForTx` 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 decryptResult = await client
  .decryptForTx(ctHash)
  .withoutPermit()
  .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, so a larger value here trades poll time for submit-recovery time.

## Common pitfalls

* **Permit mode must be selected**: you must call exactly one of `.withPermit(...)` or `.withoutPermit()` before `.execute()`.
* **Wrong chain/account**: permits are scoped to `chainId + account`. If you get an ACL/permit error, double-check the connected chain and account.
