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

# Components

> Build encryption and balance components for your dApp

# Components

This guide walks you through the key components that handle encryption, minting, and balance display.

## EncryptedInput Component

This component handles:

1. Taking user input
2. Encrypting the value with Inco SDK
3. Creating mint and token accounts if needed
4. Minting confidential tokens

### Full Implementation

```tsx theme={null}
"use client";

import { useState, useEffect, useRef } from "react";
import {
  useWallet,
  useConnection,
  useAnchorWallet,
} from "@solana/wallet-adapter-react";
import { encryptValue } from "@inco/solana-sdk/encryption";
import { hexToBuffer } from "@inco/solana-sdk/utils";
import {
  Keypair,
  PublicKey,
  SystemProgram,
  Transaction,
} from "@solana/web3.js";
import {
  fetchUserMint,
  fetchUserTokenAccount,
  getAllowancePda,
  extractHandle,
  getProgram,
} from "@/utils/constants";

export default function EncryptedInput() {
  const { publicKey, connected, sendTransaction } = useWallet();
  const { connection } = useConnection();
  const wallet = useAnchorWallet();

  // State
  const [mint, setMint] = useState<string | null>(null);
  const [account, setAccount] = useState<string | null>(null);
  const [value, setValue] = useState("");
  const [encrypted, setEncrypted] = useState("");
  const [txHash, setTxHash] = useState<string | null>(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  // Fetch existing accounts when wallet connects
  useEffect(() => {
    if (!publicKey) {
      setMint(null);
      setAccount(null);
      return;
    }

    (async () => {
      const m = await fetchUserMint(connection, publicKey);
      if (m) {
        setMint(m.pubkey.toBase58());
        const a = await fetchUserTokenAccount(connection, publicKey, m.pubkey);
        setAccount(a?.pubkey.toBase58() ?? null);
      }
    })();
  }, [publicKey, connection]);

  // Encrypt the input value
  const handleEncrypt = async () => {
    if (!value) return;
    setLoading(true);
    try {
      // Convert to smallest unit (6 decimals)
      const amount = BigInt(Math.floor(parseFloat(value) * 1e6));
      const encryptedHex = await encryptValue(amount);
      setEncrypted(encryptedHex);
    } catch (e: any) {
      setError(e.message || "Encryption failed");
    }
    setLoading(false);
  };

  // Mint tokens with the encrypted amount
  const handleMint = async () => {
    if (!publicKey || !wallet || !encrypted) {
      setError("Missing required data");
      return;
    }
    
    setLoading(true);
    setError(null);

    try {
      const program = getProgram(connection, wallet);
      const ciphertext = hexToBuffer(encrypted);
      let m = mint;
      let a = account;

      // Create mint and account if they don't exist
      if (!m || !a) {
        const signers: Keypair[] = [];
        const tx = new Transaction();

        if (!m) {
          const mintKp = Keypair.generate();
          m = mintKp.publicKey.toBase58();
          signers.push(mintKp);
          
          tx.add(
            await program.methods
              .initializeMint(6, publicKey, publicKey)
              .accounts({
                mint: mintKp.publicKey,
                payer: publicKey,
                systemProgram: SystemProgram.programId,
              })
              .instruction()
          );
        }

        if (!a) {
          const accountKp = Keypair.generate();
          a = accountKp.publicKey.toBase58();
          signers.push(accountKp);
          
          tx.add(
            await program.methods
              .initializeAccount()
              .accounts({
                account: accountKp.publicKey,
                mint: m,
                owner: publicKey,
                payer: publicKey,
                systemProgram: SystemProgram.programId,
              })
              .instruction()
          );
        }

        // Send initialization transaction
        tx.recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
        tx.feePayer = publicKey;
        tx.partialSign(...signers);
        
        await connection.confirmTransaction(
          await sendTransaction(tx, connection),
          "confirmed"
        );
        
        setMint(m);
        setAccount(a);
        
        // Wait for account to be available
        await new Promise((r) => setTimeout(r, 1000));
      }

      // Now mint the tokens
      const mintPk = new PublicKey(m);
      const accPk = new PublicKey(a);
      
      const accounts = {
        mint: mintPk,
        account: accPk,
        mintAuthority: publicKey,
        systemProgram: SystemProgram.programId,
      };

      // Simulate to get the handle
      const simTx = await program.methods
        .mintTo(ciphertext, 0)
        .accounts(accounts)
        .transaction();
      simTx.recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
      simTx.feePayer = publicKey;

      const sim = await connection.simulateTransaction(simTx, undefined, [accPk]);
      
      if (sim.value.err) {
        throw new Error(`Simulation failed: ${JSON.stringify(sim.value.err)}`);
      }

      // Extract handle from simulation
      const data = sim.value.accounts?.[0]?.data;
      if (!data) throw new Error("No simulation data");
      
      const handle = extractHandle(Buffer.from(data[0], "base64"));
      if (!handle) throw new Error("No handle found");

      // Derive allowance PDA
      const [allowancePda] = getAllowancePda(handle, publicKey);

      // Submit the actual transaction with allowance PDA
      const sig = await program.methods
        .mintTo(ciphertext, 0)
        .accounts(accounts)
        .remainingAccounts([
          { pubkey: allowancePda, isSigner: false, isWritable: true },
          { pubkey: publicKey, isSigner: false, isWritable: false },
        ])
        .rpc();

      setTxHash(sig);
      setValue("");
      setEncrypted("");
      
      // Notify other components
      window.dispatchEvent(new CustomEvent("token-minted"));
      
    } catch (e: any) {
      console.error(e);
      setError(e.message || "Minting failed");
    }
    
    setLoading(false);
  };

  return (
    <div className="mt-8 space-y-4">
      {/* Input Field */}
      <div>
        <label className="block text-sm font-medium mb-2">
          Amount to Mint
        </label>
        <div className="flex space-x-2">
          <input
            type="number"
            placeholder="Enter amount..."
            value={value}
            onChange={(e) => {
              setValue(e.target.value);
              setTxHash(null);
              setEncrypted("");
              setError(null);
            }}
            className="flex-1 p-3 border rounded-full"
          />
          <button
            onClick={handleEncrypt}
            disabled={loading || !value || !connected}
            className="px-6 py-3 bg-blue-600 text-white rounded-full 
                       hover:bg-blue-700 disabled:bg-gray-300"
          >
            {loading ? "..." : "Encrypt"}
          </button>
        </div>
      </div>

      {/* Encrypted Value Display */}
      {encrypted && (
        <>
          <div className="bg-gray-100 p-3 rounded-full flex items-center">
            <span className="text-sm font-mono truncate flex-1">
              {encrypted.slice(0, 20)}...
            </span>
            <button
              onClick={() => navigator.clipboard.writeText(encrypted)}
              className="ml-2 bg-blue-600 text-white px-3 py-1 rounded-full text-xs"
            >
              Copy
            </button>
          </div>
          
          <button
            onClick={handleMint}
            disabled={loading}
            className="w-full bg-blue-600 text-white py-2 rounded-full 
                       hover:bg-blue-700 disabled:bg-gray-300"
          >
            {loading ? "Processing..." : "Mint Tokens"}
          </button>
        </>
      )}

      {/* Error Display */}
      {error && (
        <p className="text-sm text-red-500 bg-red-50 p-3 rounded-xl">
          {error}
        </p>
      )}
      
      {/* Success Display */}
      {txHash && (
        <div className="bg-green-50 p-3 rounded-xl">
          <p className="text-sm text-green-800">
            ✅ Success!{" "}
            <a
              href={`https://explorer.solana.com/tx/${txHash}?cluster=devnet`}
              target="_blank"
              className="underline"
            >
              View transaction
            </a>
          </p>
        </div>
      )}
    </div>
  );
}
```

### Key Steps Explained

#### 1. Encrypt the Value

```tsx theme={null}
import { encryptValue } from "@inco/solana-sdk/encryption";

const amount = BigInt(Math.floor(parseFloat(value) * 1e6));
const encryptedHex = await encryptValue(amount);
```

#### 2. Create Accounts if Needed

First-time users need a mint and token account:

```tsx theme={null}
// Create mint
const mintKp = Keypair.generate();
tx.add(
  await program.methods
    .initializeMint(6, publicKey, publicKey)
    .accounts({
      mint: mintKp.publicKey,
      payer: publicKey,
      systemProgram: SystemProgram.programId,
    })
    .instruction()
);

// Create token account
const accountKp = Keypair.generate();
tx.add(
  await program.methods
    .initializeAccount()
    .accounts({
      account: accountKp.publicKey,
      mint: mintKp.publicKey,
      owner: publicKey,
      payer: publicKey,
      systemProgram: SystemProgram.programId,
    })
    .instruction()
);
```

#### 3. Simulate to Get Handle

Before minting, simulate to extract the handle:

```tsx theme={null}
const sim = await connection.simulateTransaction(simTx, undefined, [accPk]);
const data = sim.value.accounts?.[0]?.data;
const handle = extractHandle(Buffer.from(data[0], "base64"));
```

#### 4. Derive Allowance PDA and Mint

```tsx theme={null}
const [allowancePda] = getAllowancePda(handle, publicKey);

const sig = await program.methods
  .mintTo(ciphertext, 0)
  .accounts(accounts)
  .remainingAccounts([
    { pubkey: allowancePda, isSigner: false, isWritable: true },
    { pubkey: publicKey, isSigner: false, isWritable: false },
  ])
  .rpc();
```

***

## Balance Component

This component displays and decrypts the user's confidential token balance.

### Full Implementation

```tsx theme={null}
"use client";

import { useState, useEffect } from "react";
import { useWallet, useConnection } from "@solana/wallet-adapter-react";
import { decrypt } from "@inco/solana-sdk/attested-decrypt";
import {
  fetchUserMint,
  fetchUserTokenAccount,
  extractHandle,
} from "@/utils/constants";

export default function Balance() {
  const { publicKey, connected, signMessage } = useWallet();
  const { connection } = useConnection();
  
  const [balance, setBalance] = useState<string>();
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  // Read and decrypt balance
  const handleReadBalance = async () => {
    if (!connected || !publicKey || !signMessage) return;
    
    setLoading(true);
    setError(null);

    try {
      // Find user's mint
      const mint = await fetchUserMint(connection, publicKey);
      if (!mint) {
        setBalance("No mint");
        return;
      }

      // Find user's token account
      const acc = await fetchUserTokenAccount(
        connection,
        publicKey,
        mint.pubkey
      );
      if (!acc) {
        setBalance("No account");
        return;
      }

      // Extract the encrypted handle
      const handle = extractHandle(acc.data);
      
      // Handle zero balance
      if (handle === BigInt(0)) {
        setBalance("0");
        return;
      }

      // Decrypt the balance
      const result = await decrypt([handle.toString()], {
        address: publicKey,
        signMessage,
      });

      // Convert from smallest unit (6 decimals)
      const rawBalance = BigInt(result.plaintexts?.[0] ?? "0");
      setBalance((Number(rawBalance) / 1e6).toString());
      
    } catch (e) {
      console.error(e);
      setError(e instanceof Error ? e.message : "Failed to read balance");
    } finally {
      setLoading(false);
    }
  };

  // Reset on wallet change
  useEffect(() => {
    setBalance(undefined);
    setError(null);
  }, [publicKey]);

  // Listen for mint events
  useEffect(() => {
    const onMint = () => setBalance(undefined);
    window.addEventListener("token-minted", onMint);
    return () => window.removeEventListener("token-minted", onMint);
  }, []);

  return (
    <div className="mt-8 space-y-4">
      <div className="flex items-center justify-between">
        <div>
          <span className="text-sm font-medium">Balance:</span>
          <span className="ml-2 font-mono">
            {balance ?? "****"}
          </span>
        </div>
        
        <button
          onClick={handleReadBalance}
          disabled={loading || !connected}
          className="bg-gray-600 text-white py-2 px-4 rounded-full 
                     hover:bg-gray-700 disabled:bg-gray-300"
        >
          {loading ? "Loading..." : "Reveal"}
        </button>
      </div>
      
      {error && (
        <p className="text-sm text-red-500">{error}</p>
      )}
    </div>
  );
}
```

### Key Steps Explained

#### 1. Fetch Account Data

```tsx theme={null}
const mint = await fetchUserMint(connection, publicKey);
const acc = await fetchUserTokenAccount(connection, publicKey, mint.pubkey);
```

#### 2. Extract Handle

```tsx theme={null}
const handle = extractHandle(acc.data);
```

<Warning>
  The offset in `extractHandle` is specific to the Inco Token program. Adjust based on your program's account layout.
</Warning>

#### 3. Decrypt with Wallet Signature

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

const result = await decrypt([handle.toString()], {
  address: publicKey,
  signMessage,  // From useWallet hook
});

const balance = result.plaintexts[0];
```

<Note>
  The `signMessage` function is required for attested reveal. The wallet will prompt the user to sign a message that proves ownership of the address.
</Note>

***

## Component Communication

Components communicate via custom events:

```tsx theme={null}
// In EncryptedInput - emit after minting
window.dispatchEvent(new CustomEvent("token-minted"));

// In Balance - listen for mint events
useEffect(() => {
  const onMint = () => setBalance(undefined);
  window.addEventListener("token-minted", onMint);
  return () => window.removeEventListener("token-minted", onMint);
}, []);
```

This ensures the balance display updates after minting new tokens.

## Summary

| Component        | Purpose                        | SDK Functions Used            |
| ---------------- | ------------------------------ | ----------------------------- |
| `EncryptedInput` | Encrypt values and mint tokens | `encryptValue`, `hexToBuffer` |
| `Balance`        | Display and decrypt balance    | `decrypt`                     |
| `Header`         | Wallet connection UI           | Wallet adapter hooks          |

You now have a complete understanding of how to build a confidential token dApp with Next.js!
