> ## 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 Decrypt

> Decrypt encrypted handles with on-chain verification

# Attested Decrypt

Attested Decrypt allows you to decrypt encrypted handles and verify the decryption on-chain using Ed25519 signature verification. Use this when you need to perform on-chain logic based on decrypted values.

## Import

```typescript theme={null}
import { decrypt } from '@inco/solana-sdk/attested-decrypt';
import { hexToBuffer, handleToBuffer, plaintextToBuffer } from '@inco/solana-sdk/utils';
import { Transaction, SYSVAR_INSTRUCTIONS_PUBKEY } from '@solana/web3.js';
```

## Basic Usage

```typescript theme={null}
import { decrypt } from '@inco/solana-sdk/attested-decrypt';
import { handleToBuffer, plaintextToBuffer } from '@inco/solana-sdk/utils';
import { Transaction, SYSVAR_INSTRUCTIONS_PUBKEY } from '@solana/web3.js';

// Step 1: Decrypt handles (requires wallet signature for authentication)
const result = await decrypt(['handle1', 'handle2'], {
  address: wallet.publicKey,
  signMessage: wallet.signMessage,
});

// Step 2: Build program instruction
const handleBuffers = result.handles.map(h => handleToBuffer(h));
const plaintextBuffers = result.plaintexts.map(p => plaintextToBuffer(p));

const programInstruction = await program.methods
  .verifyDecryption(result.handles.length, handleBuffers, plaintextBuffers)
  .accounts({
    authority: wallet.publicKey,
    instructions: SYSVAR_INSTRUCTIONS_PUBKEY,
  })
  .instruction();

// Step 3: Build transaction with Ed25519 instructions + program instruction
const tx = new Transaction();
result.ed25519Instructions.forEach(ix => tx.add(ix));
tx.add(programInstruction);

// Step 4: Send transaction
const { blockhash } = await connection.getLatestBlockhash();
tx.recentBlockhash = blockhash;
tx.feePayer = wallet.publicKey;

const signedTx = await wallet.signTransaction(tx);
const signature = await connection.sendRawTransaction(signedTx.serialize());
await connection.confirmTransaction(signature, "confirmed");
```

<Note>
  Attested Decrypt uses the same `decrypt()` function as [Attested Reveal](/svm/js-sdk/attestations/attested-reveal). The difference is that you use `result.ed25519Instructions` to build an on-chain verification transaction.
</Note>

## How It Works

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

    Client->>Covalidator: Request decryption for handles
    Covalidator->>Covalidator: Decrypt & sign attestation
    Covalidator-->>Client: Return plaintexts + Ed25519 signatures
    Client->>Client: Build TX with Ed25519 IX + program IX
    Client->>Solana: Submit transaction
    Solana->>Solana: Verify Ed25519 signatures
    Solana->>Solana: Execute program instruction
    Solana-->>Client: Return transaction result
```

1. Send encrypted handles to the covalidator
2. Covalidator decrypts and returns plaintexts with Ed25519 signatures
3. Build a transaction with Ed25519 verification instructions
4. Add your program instruction that uses the decrypted values
5. Submit transaction - Solana verifies signatures before executing your instruction

***

## Example: Verify Addition Result On-Chain

```typescript theme={null}
import { encryptValue } from '@inco/solana-sdk/encryption';
import { decrypt } from '@inco/solana-sdk/attested-decrypt';
import { hexToBuffer, handleToBuffer, plaintextToBuffer } from '@inco/solana-sdk/utils';
import { Transaction, SYSVAR_INSTRUCTIONS_PUBKEY } from '@solana/web3.js';

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

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

console.log("Addition tx:", addTx);

// Step 2: Get the result handle from logs
const resultHandle = await getHandleFromTx(addTx, "Addition result handle:");
console.log("Result handle:", resultHandle);

// Step 3: Decrypt and get Ed25519 instructions
// 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("Decrypted value:", result.plaintexts[0]);  // "150"
console.log("Ed25519 instructions count:", result.ed25519Instructions.length);

// Step 4: Build program instruction
const handleBuffers = result.handles.map(h => handleToBuffer(h));
const plaintextBuffers = result.plaintexts.map(p => plaintextToBuffer(p));

const programInstruction = await program.methods
  .verifyDecryption(result.handles.length, handleBuffers, plaintextBuffers)
  .accounts({
    authority: wallet.publicKey,
    instructions: SYSVAR_INSTRUCTIONS_PUBKEY,
  })
  .instruction();

// Step 5: Build transaction
const tx = new Transaction();
result.ed25519Instructions.forEach(ix => tx.add(ix));
tx.add(programInstruction);

// Step 6: Send transaction
const { blockhash } = await connection.getLatestBlockhash();
tx.recentBlockhash = blockhash;
tx.feePayer = wallet.publicKey;

const signedTx = await wallet.signTransaction(tx);
const signature = await connection.sendRawTransaction(signedTx.serialize());
await connection.confirmTransaction(signature, "confirmed");

console.log("On-chain verification tx:", signature);
console.log("Verified on-chain: 100 + 50 = 150");
```

## Example: Verify Multiple Values On-Chain

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

console.log("Add handle:", addHandle);
console.log("Sub handle:", subHandle);

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

console.log("Decrypted values:", result.plaintexts);  // ["150", "50"]

// Build program instruction
const handleBuffers = result.handles.map(h => handleToBuffer(h));
const plaintextBuffers = result.plaintexts.map(p => plaintextToBuffer(p));

const programInstruction = await program.methods
  .verifyDecryption(result.handles.length, handleBuffers, plaintextBuffers)
  .accounts({
    authority: wallet.publicKey,
    instructions: SYSVAR_INSTRUCTIONS_PUBKEY,
  })
  .instruction();

// Build and send transaction
const tx = new Transaction();
result.ed25519Instructions.forEach(ix => tx.add(ix));
tx.add(programInstruction);

const { blockhash } = await connection.getLatestBlockhash();
tx.recentBlockhash = blockhash;
tx.feePayer = wallet.publicKey;

const signedTx = await wallet.signTransaction(tx);
const signature = await connection.sendRawTransaction(signedTx.serialize());
await connection.confirmTransaction(signature, "confirmed");

console.log("On-chain verification tx:", signature);
console.log("Verified on-chain:");
console.log("  100 + 50 =", result.plaintexts[0]);
console.log("  100 - 50 =", result.plaintexts[1]);
```

## Example: Verify e\_select Result On-Chain

```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 selectTx = await program.methods
  .testEselect(
    hexToBuffer(encryptedA),
    hexToBuffer(encryptedB),
    hexToBuffer(encryptedIfTrue),
    hexToBuffer(encryptedIfFalse),
    0
  )
  .accounts({
    authority: wallet.publicKey,
  })
  .rpc();

console.log("Select tx:", selectTx);

const resultHandle = await getHandleFromTx(selectTx, "Select result handle:");
console.log("Result handle:", resultHandle);

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

console.log("Decrypted select result:", result.plaintexts[0]);  // "999"

// Build program instruction
const handleBuffers = result.handles.map(h => handleToBuffer(h));
const plaintextBuffers = result.plaintexts.map(p => plaintextToBuffer(p));

const programInstruction = await program.methods
  .verifyDecryption(result.handles.length, handleBuffers, plaintextBuffers)
  .accounts({
    authority: wallet.publicKey,
    instructions: SYSVAR_INSTRUCTIONS_PUBKEY,
  })
  .instruction();

const tx = new Transaction();
result.ed25519Instructions.forEach(ix => tx.add(ix));
tx.add(programInstruction);

const { blockhash } = await connection.getLatestBlockhash();
tx.recentBlockhash = blockhash;
tx.feePayer = wallet.publicKey;

const signedTx = await wallet.signTransaction(tx);
const signature = await connection.sendRawTransaction(signedTx.serialize());
await connection.confirmTransaction(signature, "confirmed");

console.log("On-chain verification tx:", signature);
console.log("Verified on-chain: select(100>=50, 999, 0) =", result.plaintexts[0]);
```

***

## React Integration

```tsx theme={null}
import { useWallet, useConnection } from '@solana/wallet-adapter-react';
import { decrypt, AttestedDecryptError } from '@inco/solana-sdk/attested-decrypt';
import { handleToBuffer, plaintextToBuffer } from '@inco/solana-sdk/utils';
import { Transaction, SYSVAR_INSTRUCTIONS_PUBKEY } from '@solana/web3.js';
import { useState } from 'react';

function DecryptAndVerify({ handles }: { handles: string[] }) {
  const wallet = useWallet();
  const { connection } = useConnection();
  const [result, setResult] = useState<string[] | null>(null);
  const [txSignature, setTxSignature] = useState<string | null>(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

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

    setLoading(true);
    setError(null);

    try {
      // Step 1: Decrypt handles (requires wallet signature for authentication)
      const decryptResult = await decrypt(handles, {
        address: wallet.publicKey,
        signMessage: wallet.signMessage!,
      });
      setResult(decryptResult.plaintexts);

      // Step 2: Build program instruction
      const handleBuffers = decryptResult.handles.map(h => handleToBuffer(h));
      const plaintextBuffers = decryptResult.plaintexts.map(p => plaintextToBuffer(p));

      const programInstruction = await program.methods
        .verifyDecryption(decryptResult.handles.length, handleBuffers, plaintextBuffers)
        .accounts({
          authority: wallet.publicKey,
          instructions: SYSVAR_INSTRUCTIONS_PUBKEY,
        })
        .instruction();

      // Step 3: Build transaction
      const tx = new Transaction();
      decryptResult.ed25519Instructions.forEach(ix => tx.add(ix));
      tx.add(programInstruction);

      const { blockhash } = await connection.getLatestBlockhash();
      tx.recentBlockhash = blockhash;
      tx.feePayer = wallet.publicKey;

      // Step 4: Sign and send
      const signedTx = await wallet.signTransaction(tx);
      const signature = await connection.sendRawTransaction(signedTx.serialize());
      await connection.confirmTransaction(signature, 'confirmed');

      setTxSignature(signature);
    } catch (err) {
      if (err instanceof AttestedDecryptError) {
        setError(err.message);
      } else {
        setError('Failed to decrypt and verify');
      }
      console.error('Decrypt and verify failed:', err);
    } finally {
      setLoading(false);
    }
  };

  return (
    <div className="p-4 border rounded-lg">
      <h3 className="text-lg font-semibold mb-4">Decrypt & Verify On-Chain</h3>

      <button
        onClick={handleDecryptAndVerify}
        disabled={loading || !wallet.connected}
        className="px-4 py-2 bg-blue-500 text-white rounded disabled:opacity-50"
      >
        {loading ? 'Processing...' : 'Decrypt & Verify'}
      </button>

      {result && (
        <div className="mt-4">
          <h4 className="font-medium">Decrypted Values:</h4>
          <ul className="list-disc list-inside">
            {result.map((value, i) => (
              <li key={i}>Handle {i + 1}: {value}</li>
            ))}
          </ul>
        </div>
      )}

      {txSignature && (
        <p className="mt-2 text-green-600">
          Verified on-chain:{' '}
          <a
            href={`https://explorer.solana.com/tx/${txSignature}?cluster=devnet`}
            target="_blank"
            rel="noopener noreferrer"
            className="underline"
          >
            {txSignature.slice(0, 8)}...
          </a>
        </p>
      )}

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

export default DecryptAndVerify;
```

***

## 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:");
```

### 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
const accountData = program.coder.accounts.decode(
  "TokenAccount",
  accountInfo.data
);

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

// Now use the handle for attested decrypt with on-chain verification
const result = await decrypt([balanceHandle], {
  address: wallet.publicKey,
  signMessage: wallet.signMessage,
});

// Build verification transaction...
```

### Example: Verify Token Balance On-Chain

```typescript theme={null}
import { decrypt } from '@inco/solana-sdk/attested-decrypt';
import { handleToBuffer, plaintextToBuffer } from '@inco/solana-sdk/utils';
import { PublicKey, Transaction, SYSVAR_INSTRUCTIONS_PUBKEY } from '@solana/web3.js';

async function verifyTokenBalanceOnChain(
  connection: Connection,
  mint: PublicKey,
  owner: PublicKey
): Promise<{ balance: string; txSignature: 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 with Ed25519 verification (requires wallet signature)
  const result = await decrypt([balanceHandle], {
    address: owner,
    signMessage: wallet.signMessage,
  });

  // Build program instruction
  const handleBuffers = result.handles.map(h => handleToBuffer(h));
  const plaintextBuffers = result.plaintexts.map(p => plaintextToBuffer(p));

  const programInstruction = await program.methods
    .verifyDecryption(result.handles.length, handleBuffers, plaintextBuffers)
    .accounts({
      authority: owner,
      instructions: SYSVAR_INSTRUCTIONS_PUBKEY,
    })
    .instruction();

  // Build and send transaction
  const tx = new Transaction();
  result.ed25519Instructions.forEach(ix => tx.add(ix));
  tx.add(programInstruction);

  const { blockhash } = await connection.getLatestBlockhash();
  tx.recentBlockhash = blockhash;
  tx.feePayer = owner;

  const signedTx = await wallet.signTransaction(tx);
  const signature = await connection.sendRawTransaction(signedTx.serialize());
  await connection.confirmTransaction(signature, "confirmed");

  return {
    balance: result.plaintexts[0],
    txSignature: signature,
  };
}

// Usage
const { balance, txSignature } = await verifyTokenBalanceOnChain(
  connection,
  mintPubkey,
  wallet.publicKey
);
console.log("Verified balance:", balance);
console.log("Verification tx:", txSignature);
```

***

## API Reference

### `decrypt(handles, options)`

Decrypts encrypted handles and returns plaintexts with Ed25519 verification data.

**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[];  // Instructions for on-chain verification
}
```

### Utility Functions

```typescript theme={null}
import { hexToBuffer, handleToBuffer, plaintextToBuffer } from '@inco/solana-sdk/utils';

// Convert hex string to Buffer (for encrypted ciphertext)
const buffer = hexToBuffer(encryptedHex);

// Convert handle string to Buffer (for program instruction)
const handleBuf = handleToBuffer(handle);

// Convert plaintext string to Buffer (for program instruction)
const plaintextBuf = plaintextToBuffer(plaintext);
```

***

## 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('Attested decrypt 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. This is a Solana transaction size limit` | More than 10 handles            | Split into multiple calls of ≤10 handles |
| `At least one handle is required for decryption`                              | Empty handles array             | Pass at least one handle                 |
| `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       |
| `Covalidator signature verification failed. The response may be tampered`     | Ed25519 verification failed     | Response integrity compromised           |
| `Failed to build attested decrypt transaction`                                | Transaction construction failed | Check handles and instruction builder    |
| `Transaction failed: <details>`                                               | On-chain execution failed       | Check Solana explorer for details        |
| `Failed to create Ed25519 verification instruction: <details>`                | Ed25519 instruction failed      | Signature format may be invalid          |
| `Attested decryption failed: <details>`                                       | General failure                 | Check error details                      |

***

## When to Use Attested Decrypt vs Attested Reveal

| Use Case                                            | Use                                                         |
| --------------------------------------------------- | ----------------------------------------------------------- |
| Conditional on-chain logic based on decrypted value | **Attested Decrypt**                                        |
| Verify decryption proof on-chain                    | **Attested Decrypt**                                        |
| Display balance in UI                               | [Attested Reveal](/svm/js-sdk/attestations/attested-reveal) |
| Show transaction result to user                     | [Attested Reveal](/svm/js-sdk/attestations/attested-reveal) |
