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

# Attested Reveal

> Decrypt encrypted handles for off-chain display

# Attested Reveal

Attested Reveal allows you to decrypt encrypted handles and display the plaintext values off-chain (e.g., show a user's balance in your UI) without any on-chain transaction.

## Import

```typescript theme={null}
import { decrypt } from '@inco/solana-sdk/attested-decrypt';
import { hexToBuffer } from '@inco/solana-sdk/utils';
```

## Basic Usage

```typescript theme={null}
import { decrypt } from '@inco/solana-sdk/attested-decrypt';

// Decrypt handles and get plaintext values (requires wallet signature)
const result = await decrypt(['handle1', 'handle2'], {
  address: wallet.publicKey,
  signMessage: wallet.signMessage,
});

// Use plaintexts for off-chain display
console.log(result.plaintexts);  // ['150', '50']
```

<Note>
  Attested Reveal uses the same `decrypt()` function as [Attested Decrypt](/svm/js-sdk/attestations/attested-decrypt). The difference is that you only use `result.plaintexts` for display, without building an on-chain verification transaction.
</Note>

## How It Works

```mermaid theme={null}
sequenceDiagram
    participant Client
    participant Covalidator

    Client->>Covalidator: Request decryption for handles
    Covalidator->>Covalidator: Decrypt & sign attestation
    Covalidator-->>Client: Return plaintexts + signatures
    Client->>Client: Display plaintexts in UI
```

1. Send encrypted handles to the covalidator
2. Covalidator decrypts and returns plaintext values with signatures
3. Display the plaintext values in your UI

***

## Example: Reveal Arithmetic Result

```typescript theme={null}
import { encryptValue } from '@inco/solana-sdk/encryption';
import { decrypt } from '@inco/solana-sdk/attested-decrypt';
import { hexToBuffer } from '@inco/solana-sdk/utils';

// Step 1: Perform encrypted operation on-chain
const encryptedA = await encryptValue(BigInt(100));
const encryptedB = await encryptValue(BigInt(50));

const tx = await program.methods
  .testAddition(hexToBuffer(encryptedA), hexToBuffer(encryptedB), 0)
  .accounts({
    authority: wallet.publicKey,
  })
  .rpc();

// Step 2: Get the result handle from transaction logs
const resultHandle = await getHandleFromTx(tx, "Addition result handle:");

// Step 3: Decrypt and display (off-chain only)
// signMessage can be from wallet adapter (e.g., Phantom) or tweetnacl for testing

const result = await decrypt([resultHandle], {
  address: wallet.publicKey,
  signMessage: wallet.signMessage,  // or: async (msg) => nacl.sign.detached(msg, keypair.secretKey)
});

console.log("Result:", result.plaintexts[0]);  // "150"
```

## Example: Reveal Multiple Values

```typescript theme={null}
// Get handles from multiple operations
const addHandle = await getHandleFromTx(addTx, "Addition result handle:");
const subHandle = await getHandleFromTx(subTx, "Subtraction result handle:");

// Decrypt multiple handles at once (requires wallet signature)
const result = await decrypt([addHandle, subHandle], {
  address: wallet.publicKey,
  signMessage: wallet.signMessage,
});

console.log("100 + 50 =", result.plaintexts[0]);  // "150"
console.log("100 - 50 =", result.plaintexts[1]);  // "50"
```

## Example: Reveal Comparison Result

```typescript theme={null}
const encryptedA = await encryptValue(BigInt(100));
const encryptedB = await encryptValue(BigInt(50));

const tx = await program.methods
  .testComparison(hexToBuffer(encryptedA), hexToBuffer(encryptedB), 0)
  .accounts({
    authority: wallet.publicKey,
  })
  .rpc();

const resultHandle = await getHandleFromTx(tx, "Comparison (A >= B) result handle:");

const result = await decrypt([resultHandle], {
  address: wallet.publicKey,
  signMessage: wallet.signMessage,
});

// Boolean results: "1" = true, "0" = false
const isGreaterOrEqual = result.plaintexts[0] === "1";
console.log("100 >= 50:", isGreaterOrEqual);  // true
```

## Example: Reveal e\_select Result

```typescript theme={null}
const encryptedA = await encryptValue(BigInt(100));
const encryptedB = await encryptValue(BigInt(50));
const encryptedIfTrue = await encryptValue(BigInt(999));
const encryptedIfFalse = await encryptValue(BigInt(0));

const tx = await program.methods
  .testEselect(
    hexToBuffer(encryptedA),
    hexToBuffer(encryptedB),
    hexToBuffer(encryptedIfTrue),
    hexToBuffer(encryptedIfFalse),
    0
  )
  .accounts({
    authority: wallet.publicKey,
  })
  .rpc();

const resultHandle = await getHandleFromTx(tx, "Select result handle:");

const result = await decrypt([resultHandle], {
  address: wallet.publicKey,
  signMessage: wallet.signMessage,
});

console.log("select(100>=50, 999, 0) =", result.plaintexts[0]);  // "999"
```

***

## Client-Side Integration

```tsx theme={null}
import { useWallet } from '@solana/wallet-adapter-react';
import { decrypt } from '@inco/solana-sdk/attested-decrypt';
import { useState } from 'react';

function RevealValue({ handle }: { handle: string }) {
  const wallet = useWallet();
  const [value, setValue] = useState<string | null>(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const reveal = async () => {
    if (!wallet.publicKey || !wallet.signMessage) {
      setError('Please connect your wallet');
      return;
    }

    setLoading(true);
    setError(null);
    try {
      const result = await decrypt([handle], {
        address: wallet.publicKey,
        signMessage: wallet.signMessage,
      });
      setValue(result.plaintexts[0]);
    } catch (err) {
      setError('Failed to reveal value');
      console.error('Reveal failed:', err);
    } finally {
      setLoading(false);
    }
  };

  return (
    <div className="p-4 border rounded-lg">
      {value !== null ? (
        <p className="text-2xl font-bold">{value}</p>
      ) : (
        <p className="text-gray-500">Value hidden</p>
      )}

      <button
        onClick={reveal}
        disabled={loading}
        className="mt-4 px-4 py-2 bg-blue-500 text-white rounded disabled:opacity-50"
      >
        {loading ? 'Revealing...' : 'Reveal Value'}
      </button>

      {error && <p className="mt-2 text-red-500">{error}</p>}
    </div>
  );
}
```

***

## Getting Handles

There are two ways to get encrypted handles for decryption:

### Option 1: From Transaction Logs

When your program emits handles in logs after an operation:

```typescript theme={null}
async function getHandleFromTx(
  txSignature: string,
  logPrefix: string
): Promise<string> {
  // Wait for transaction to be confirmed
  await new Promise(resolve => setTimeout(resolve, 2000));

  const txDetails = await connection.getTransaction(txSignature, {
    commitment: "confirmed",
    maxSupportedTransactionVersion: 0,
  });

  const logs = txDetails?.meta?.logMessages || [];

  for (const log of logs) {
    if (log.includes(logPrefix)) {
      const match = log.match(/(\d+)/);
      if (match) return match[1];
    }
  }

  throw new Error(`Handle not found in logs for prefix: ${logPrefix}`);
}

// Usage
const handle = await getHandleFromTx(txSignature, "Addition result handle:");
const result = await decrypt([handle], {
  address: wallet.publicKey,
  signMessage: wallet.signMessage,
});
```

### Option 2: From On-Chain Account (e.g., Token Balance PDA)

When the encrypted handle is stored in an account (like a confidential token balance):

```typescript theme={null}
import { PublicKey } from '@solana/web3.js';

// Derive the token account PDA
const [tokenAccountPda] = PublicKey.findProgramAddressSync(
  [
    Buffer.from("token_account"),
    mintPubkey.toBuffer(),
    ownerPubkey.toBuffer(),
  ],
  INCO_TOKEN_PROGRAM_ID
);

// Fetch the account data
const accountInfo = await connection.getAccountInfo(tokenAccountPda);

if (!accountInfo) {
  throw new Error("Token account not found");
}

// Parse the account data to extract the encrypted balance handle
// The offset depends on your account structure
const accountData = program.coder.accounts.decode(
  "TokenAccount",
  accountInfo.data
);

// Get the encrypted balance handle (stored as u128 or similar)
const balanceHandle = accountData.encryptedBalance.toString();

// Decrypt the balance
const result = await decrypt([balanceHandle], {
  address: wallet.publicKey,
  signMessage: wallet.signMessage,
});
console.log("Decrypted balance:", result.plaintexts[0]);
```

### Example: Reveal Confidential Token Balance

```typescript theme={null}
import { decrypt } from '@inco/solana-sdk/attested-decrypt';
import { PublicKey } from '@solana/web3.js';

async function revealTokenBalance(
  connection: Connection,
  mint: PublicKey,
  owner: PublicKey,
  signMessage: (message: Uint8Array) => Promise<Uint8Array>
): Promise<string> {
  // Derive Associated Token Account (ATA) PDA
  const [ata] = PublicKey.findProgramAddressSync(
    [
      owner.toBuffer(),
      INCO_TOKEN_PROGRAM_ID.toBuffer(),
      mint.toBuffer(),
    ],
    ASSOCIATED_TOKEN_PROGRAM_ID
  );

  // Fetch and parse account
  const accountInfo = await connection.getAccountInfo(ata);
  if (!accountInfo) {
    throw new Error("Token account not found");
  }

  const tokenAccount = program.coder.accounts.decode(
    "IncoTokenAccount",
    accountInfo.data
  );

  // Extract encrypted balance handle
  const balanceHandle = tokenAccount.amount.toString();

  // Decrypt (wallet signature required)
  const result = await decrypt([balanceHandle], {
    address: owner,
    signMessage, // Pass signMessage function from wallet
  });
  return result.plaintexts[0];
}

// Usage
const balance = await revealTokenBalance(
  connection,
  mintPubkey,
  wallet.publicKey,
  wallet.signMessage
);
console.log("Your balance:", balance);
```

***

## API Reference

### `decrypt(handles, options)`

Decrypts encrypted handles and returns plaintext values.

**Parameters:**

* `handles`: `string[]` - Array of encrypted handles as decimal strings (max 10)
* `options`: `DecryptOptions` - Wallet authentication options

<Note>
  Handles must be passed as decimal string representations (e.g., `"123456789012345678901234567890"`), not hex strings. When reading a handle from an on-chain account (stored as `u128`), convert it to a decimal string using `.toString()`.
</Note>

**Returns:** `Promise<DecryptResult>`

### Types

#### `DecryptOptions`

```typescript theme={null}
interface DecryptOptions {
  address: PublicKey | string;  // Wallet public key (base58 string or PublicKey)
  signMessage: (message: Uint8Array) => Promise<Uint8Array>;  // Message signing function
}
```

The `signMessage` function can come from:

* **Wallet adapter** (e.g., Phantom): `wallet.signMessage`
* **tweetnacl for testing**: `async (msg) => nacl.sign.detached(msg, keypair.secretKey)`

For complete examples, see the [lightning-rod-solana tests](https://github.com/Inco-fhevm/lightning-rod-solana/blob/main/tests/inco-token.ts).

#### `DecryptResult`

```typescript theme={null}
interface DecryptResult {
  plaintexts: string[];      // Decrypted values as strings
  handles: string[];         // Original handles
  signatures: string[];      // Ed25519 signatures (base58)
  ed25519Instructions: TransactionInstruction[];  // For on-chain verification (not used in Reveal)
}
```

***

## Error Handling

```typescript theme={null}
import { decrypt, AttestedDecryptError } from '@inco/solana-sdk/attested-decrypt';

try {
  const result = await decrypt(handles, {
    address: wallet.publicKey,
    signMessage: wallet.signMessage,
  });
} catch (error) {
  if (error instanceof AttestedDecryptError) {
    console.error('Reveal failed:', error.message);
  }
}
```

### Errors

| Error Message                               | Cause                    | Solution                             |
| ------------------------------------------- | ------------------------ | ------------------------------------ |
| `No handles provided for decryption`        | Empty handles array `[]` | Pass at least one handle             |
| `Maximum 10 handles per transaction`        | More than 10 handles     | Split into multiple calls            |
| `Covalidator API request failed: <details>` | Covalidator error        | Check network; handle may be invalid |
| `Covalidator returned empty plaintext`      | Missing plaintext        | Handle may be invalid or not found   |
