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

# Inco Integration

> Learn how to create a private payment flow using Para SDK with Inco's confidential wrapper for encrypted token transfers.

This guide demonstrates how to create a seamless private payment flow using Para SDK with Inco's confidential wrapper. The app allows users to deposit, check balances, send confidential transfers, and withdraw with encrypted amounts.

<Note>Only transaction amounts are encrypted. Sender and receiver addresses are not.</Note>

## Overview

Inco offers confidential transaction capabilities where token amounts are encrypted, providing privacy for:

* **Confidential Transfers:** Token amounts are encrypted. The sender, recipient addresses, and asset are visible, but the transfer amount remains private.
* **Private Balance:** User balances are stored encrypted and can only be decrypted by the account owner through cryptographic attestation.

## Prerequisites

* Node.js 20+
* Next.js project
* pnpm package manager
* A Para API key from the [Para Developer Portal](https://developer.getpara.com)
* WalletConnect project ID

## Environment Setup

Create a `.env` file in the project root:

```bash theme={null}
# Contract Addresses (Base Sepolia)
NEXT_PUBLIC_ERC20_CONTRACT_ADDRESS=0x2a7f20a455b35ea3cff416f71ddb30e0edf5c9fe
NEXT_PUBLIC_ENCRYPTED_ERC20_CONTRACT_ADDRESS=0x3b9cabcf20eda599c5df823f46eb2eabe99bda40

# Inco Configuration
NEXT_PUBLIC_INCO_ENV=demonet

# Para configuration
NEXT_PUBLIC_PARA_API_KEY=your-para-api-key
NEXT_PUBLIC_PARA_ENVIRONMENT=BETA

# WalletConnect (used by Para for external wallets)
NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID=your-walletconnect-project-id
```

<Note>To obtain values of `NEXT_PUBLIC_PARA_API_KEY` and `NEXT_PUBLIC_PARA_ENVIRONMENT` visit [Para](https://www.getpara.com/).</Note>

## Installation

```bash theme={null}
pnpm install @getpara/react-sdk@^2.6.0 @inco/js@^0.7.7 wagmi@^2.19.5 viem@^2.44.4
```

## Para Wallet Integration

### 1. Configuration Setup

```tsx theme={null}
// src/config/constants.ts
import { Environment } from "@getpara/react-sdk";

export const API_KEY = process.env.NEXT_PUBLIC_PARA_API_KEY ?? "";
export const ENVIRONMENT = (process.env.NEXT_PUBLIC_PARA_ENVIRONMENT as Environment) || Environment.BETA;

if (!API_KEY) {
  throw new Error("Missing NEXT_PUBLIC_PARA_API_KEY environment variable");
}
```

### 2. Para Provider Setup

```tsx theme={null}
// src/context/para-provider.tsx
"use client";

import { useState } from "react";
import { ParaProvider as Provider } from "@getpara/react-sdk";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { cookieStorage, createStorage, http } from "wagmi";
import { baseSepolia as chain } from "wagmi/chains";
import { createPublicClient } from "viem";

import { API_KEY, ENVIRONMENT } from "@/config/constants";

const BASE_SEPOLIA_RPC_URL = "your-base-sepolia-rpc-url";

export const baseSepolia = {
  ...chain,
  rpcUrls: {
    default: {
      http: [BASE_SEPOLIA_RPC_URL],
    },
  },
};

export const publicClient = createPublicClient({
  chain: baseSepolia,
  transport: http(BASE_SEPOLIA_RPC_URL),
});

export function ParaProvider({
  children,
}: {
  children: React.ReactNode;
}) {
  const [queryClient] = useState(() => new QueryClient());

  return (
    <QueryClientProvider client={queryClient}>
      <Provider
        paraClientConfig={{
          apiKey: API_KEY,
          env: ENVIRONMENT,
        }}
        externalWalletConfig={{
          wallets: ["METAMASK", "COINBASE", "WALLETCONNECT", "RAINBOW"],
          evmConnector: {
            config: {
              chains: [baseSepolia],
              transports: {
                [chain.id]: http(BASE_SEPOLIA_RPC_URL),
              },
              storage: createStorage({
                storage: cookieStorage,
              }),
            },
          },
          walletConnect: {
            projectId:
              process.env.NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID || "",
          },
        }}
        config={{ appName: "Comfy" }}
        paraModalConfig={{
          authLayout: ["AUTH:FULL", "EXTERNAL:FULL"],
        }}
      >
        {children}
      </Provider>
    </QueryClientProvider>
  );
}
```

### 3. Root Layout Integration

```tsx theme={null}
// src/app/layout.tsx
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
import { ParaProvider } from "@/context/para-provider";

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
      <body className="antialiased">
        <ParaProvider>{children}</ParaProvider>
      </body>
    </html>
  );
}
```

### 4. Connect Wallet Component

```tsx theme={null}
// src/components/connect-wallet.tsx
"use client";

import { useModal } from "@getpara/react-sdk";
import { useAccount } from "wagmi";

export default function ConnectWallet() {
  const { openModal } = useModal();
  const { address, isConnected } = useAccount();

  return (
    <div>
      {isConnected ? (
        <p>Connected to {address}</p>
      ) : (
        <button onClick={() => openModal()}>Connect Wallet</button>
      )}
    </div>
  );
}
```

## Core Features

### 1. Minting ERC20 Tokens

```tsx theme={null}
// src/components/mint-erc20.tsx
import React from "react";
import { parseUnits } from "viem";
import { useAccount, useWriteContract } from "wagmi";
import { publicClient } from "@/context/para-provider";

const MintERC20 = () => {
  const { writeContractAsync } = useWriteContract();
  const { address } = useAccount();
  const amountWithDecimals = parseUnits("1000", 18);

  const mintERC20 = async () => {
    const txHash = await writeContractAsync({
      address: process.env
        .NEXT_PUBLIC_ERC20_CONTRACT_ADDRESS as `0x${string}`,
      abi: [
        {
          inputs: [
            {
              internalType: "address",
              name: "userAddress",
              type: "address",
            },
            {
              internalType: "uint256",
              name: "amount",
              type: "uint256",
            },
          ],
          name: "mint",
          outputs: [],
          stateMutability: "nonpayable",
          type: "function",
        },
      ],
      functionName: "mint",
      args: [address, amountWithDecimals],
    });

    await publicClient?.waitForTransactionReceipt({ hash: txHash });
  };

  return (
    <div>
      <h3>Mint ERC20</h3>
      <button onClick={mintERC20}>Mint 1000 ERC20</button>
    </div>
  );
};

export default MintERC20;
```

### 2. Balance Checking

```tsx theme={null}
// src/components/encrypted-balance.tsx
import React, { useState } from 'react';
import { type Address } from 'viem';
import { useAccount, useBalance, useWalletClient } from 'wagmi';
import { getEncryptedBalance } from '@/lib/balance-utils';
import { publicClient } from '@/context/para-provider';

const BalanceDisplay = () => {
  const { data: walletClient } = useWalletClient();
  const { address } = useAccount();
  const [encryptedBalance, setEncryptedBalance] = useState('0');

  const ERC20Balance = useBalance({
    address: address as Address,
    token: process.env.NEXT_PUBLIC_ERC20_CONTRACT_ADDRESS as Address,
  });

  const handleFetchBalance = async () => {
    if (address && publicClient && walletClient) {
      const result = await getEncryptedBalance({
        address,
        publicClient,
        walletClient,
        encryptedERC20Address: process.env.NEXT_PUBLIC_ENCRYPTED_ERC20_CONTRACT_ADDRESS as Address,
        incoEnv: process.env.NEXT_PUBLIC_INCO_ENV as 'testnet' | 'devnet',
      });
      if (result) setEncryptedBalance(result);
    }
  };

  return (
    <div>
      <h3>ERC20 Balance</h3>
      <p>{ERC20Balance?.data?.formatted} {ERC20Balance?.data?.symbol}</p>
      <h3>Encrypted Balance</h3>
      <p>{encryptedBalance} Tokens</p>
      <button onClick={handleFetchBalance}>Fetch Encrypted Balance</button>
    </div>
  );
};

export default BalanceDisplay;
```

### 3. Wrapping Tokens (Deposit)

```tsx theme={null}
// src/components/wrap.tsx
'use client';

import { useAccount, useWalletClient } from 'wagmi';
import { type Address } from 'viem';
import { approveTokens, wrapTokens } from '@/lib/wrap-utils';
import { publicClient } from '@/context/para-provider';

export default function WrapTokens() {
  const { data: walletClient } = useWalletClient();
  const { address } = useAccount();

  const handleWrap = async () => {
    if (!walletClient || !address) return;

    const amount = '0.01';

    // Step 1: Approve tokens
    const approveTx = await approveTokens({
      walletClient,
      address,
      amount,
      erc20Address: process.env.NEXT_PUBLIC_ERC20_CONTRACT_ADDRESS as Address,
      encryptedERC20Address: process.env.NEXT_PUBLIC_ENCRYPTED_ERC20_CONTRACT_ADDRESS as Address,
    });

    await publicClient?.waitForTransactionReceipt({ hash: approveTx });

    // Step 2: Wrap tokens
    const wrapTx = await wrapTokens({
      walletClient,
      address,
      amount,
      encryptedERC20Address: process.env.NEXT_PUBLIC_ENCRYPTED_ERC20_CONTRACT_ADDRESS as Address,
    });

    await publicClient?.waitForTransactionReceipt({ hash: wrapTx });
  };

  return (
    <div>
      <h3>Wrap Tokens</h3>
      <button onClick={handleWrap}>Wrap 0.01 Tokens</button>
    </div>
  );
}
```

### 4. Confidential Transfer

```tsx theme={null}
// src/components/confidential-transfer.tsx
import React, { useState } from "react";
import { Address } from "viem";
import { useAccount, useWalletClient } from "wagmi";

import { confidentialTransfer } from "@/lib/confidential-send";
import { publicClient } from "@/context/para-provider";

const ConfidentialTransfer = () => {
  const [recipient, setRecipient] = useState<Address>("0x");
  const { address } = useAccount();
  const { data: walletClient } = useWalletClient();

  const transferConfidentialERC20 = async () => {
    const txHash = await confidentialTransfer({
      amount: "0.01",
      address: address,
      recipient: recipient,
      walletClient: walletClient,
      publicClient: publicClient,
      encryptedERC20Address:
        process.env.NEXT_PUBLIC_ENCRYPTED_ERC20_CONTRACT_ADDRESS as Address,
      incoEnv: "testnet",
    });

    await publicClient?.waitForTransactionReceipt({ hash: txHash });
  };

  return (
    <div>
      <input
        type="text"
        value={recipient}
        onChange={(e) => setRecipient(e.target.value as Address)}
        placeholder="Recipient address"
      />
      <button onClick={transferConfidentialERC20}>
        Transfer 0.01 confidential ERC20 to {recipient}
      </button>
    </div>
  );
};

export default ConfidentialTransfer;
```

### 5. Unwrapping Tokens (Withdraw)

```tsx theme={null}
// src/components/unwrap.tsx
import { unwrapTokens } from "@/lib/unwrap-utils";
import React from "react";
import { useAccount, useWalletClient } from "wagmi";
import { publicClient } from "@/context/para-provider";

const Unwrap = () => {
  const { data: walletClient } = useWalletClient();
  const { address } = useAccount();

  const handleUnwrap = async () => {
    if (!walletClient || !address) return;

    const unwrapTx = await unwrapTokens({
      publicClient,
      walletClient,
      address,
      amount: "0.01",
      encryptedERC20Address:
        process.env.NEXT_PUBLIC_ENCRYPTED_ERC20_CONTRACT_ADDRESS,
      incoEnv: process.env.NEXT_PUBLIC_INCO_ENV,
    });
  };

  return (
    <div>
      <h3>Unwrap</h3>
      <button onClick={handleUnwrap}>Unwrap 0.01 Tokens</button>
    </div>
  );
};

export default Unwrap;
```

## Utility Functions

### Inco Lite Utilities

```tsx theme={null}
// src/lib/inco-lite.ts
import { Lightning, AttestedComputeSupportedOps } from '@inco/js/lite';
import { handleTypes } from '@inco/js';
import { baseSepolia } from 'viem/chains';
import { type PublicClient, type WalletClient, bytesToHex, pad, toHex } from 'viem';

type IncoEnv = 'testnet' | 'devnet';

async function getConfig(env: IncoEnv) {
  return await Lightning.latest(env, baseSepolia.id);
}

export async function encryptValue({
  value,
  address,
  contractAddress,
  env,
}: {
  value: bigint;
  address: `0x${string}`;
  contractAddress: `0x${string}`;
  env: IncoEnv;
}): Promise<`0x${string}`> {
  const inco = await getConfig(env);

  const encrypted = await inco.encrypt(value, {
    accountAddress: address,
    dappAddress: contractAddress,
    handleType: handleTypes.euint256,
  });

  return encrypted as `0x${string}`;
}

export async function decryptValue({
  walletClient,
  handle,
  env,
}: {
  walletClient: WalletClient;
  handle: string;
  env: IncoEnv;
}): Promise<bigint> {
  const inco = await getConfig(env);

  const decrypted = await inco.attestedDecrypt(walletClient, [handle]);

  return decrypted[0].plaintext.value;
}

export async function attestedCompute({
  walletClient,
  lhsHandle,
  op,
  rhsPlaintext,
  env,
}: {
  walletClient: WalletClient;
  lhsHandle: `0x${string}`;
  op: AttestedComputeSupportedOps;
  rhsPlaintext: bigint;
  env: IncoEnv;
}) {
  const inco = await getConfig(env);

  const result = await inco.attestedCompute(
    walletClient,
    lhsHandle,
    op,
    rhsPlaintext
  );

  const plaintext = result.plaintext;
  const attestation = {
    handle: result.attestation.handle,
    value: encodePlaintext(plaintext),
  };

  const signatures = result.attestation.covalidatorSignatures.map((sig) =>
    bytesToHex(sig)
  );

  return { plaintext, attestation, signatures };
}

function encodePlaintext(value: unknown): `0x${string}` {
  if (typeof value === 'boolean') {
    return value ? `0x${'0'.repeat(63)}1` : `0x${'0'.repeat(64)}`;
  }
  if (typeof value === 'bigint') {
    return pad(toHex(value), { size: 32 });
  }
  throw new Error('Unsupported plaintext type');
}

export async function getFee(env: IncoEnv, publicClient: PublicClient): Promise<bigint> {
  const inco = await getConfig(env);

  const fee = await publicClient.readContract({
    address: inco.executorAddress,
    abi: [
      {
        type: "function",
        inputs: [],
        name: "getFee",
        outputs: [{ name: "", internalType: "uint256", type: "uint256" }],
        stateMutability: "pure",
      },
    ],
    functionName: "getFee",
  });

  return fee;
}
```

### Balance Utilities

```tsx theme={null}
// src/lib/balance-utils.ts
import { formatEther, type Address, type PublicClient, type WalletClient } from 'viem';
import { decryptValue } from './inco-lite';
import { baseSepolia } from '@/context/para-provider';

export const getEncryptedBalance = async ({
  address,
  publicClient,
  walletClient,
  encryptedERC20Address,
  incoEnv,
}: {
  address: Address;
  publicClient: PublicClient;
  walletClient: WalletClient;
  encryptedERC20Address: Address;
  incoEnv: 'testnet' | 'devnet';
}) => {
  try {
    if (!publicClient || !walletClient || !address) {
      return '0';
    }

    const encryptedHandle = await publicClient.readContract({
      address: encryptedERC20Address,
      abi: [
        {
          inputs: [{ internalType: 'address', name: 'account', type: 'address' }],
          name: 'balanceOf',
          outputs: [{ internalType: 'bytes32', name: '', type: 'bytes32' }],
          stateMutability: 'view',
          type: 'function',
        },
      ],
      functionName: 'balanceOf',
      args: [address],
    });

    if (encryptedHandle === '0x' + '0'.repeat(64)) {
      return '0';
    }

    const decrypted = await decryptValue({
      walletClient: { ...walletClient, chain: baseSepolia },
      handle: encryptedHandle,
      env: incoEnv,
    });

    return formatEther(decrypted);
  } catch (error) {
    console.error('Failed to fetch encrypted balance:', error);
    return '0';
  }
};
```

### Wrap Utilities

```tsx theme={null}
// src/lib/wrap-utils.ts
import { parseEther, type Address, type WalletClient } from 'viem';
import { baseSepolia } from '@/context/para-provider';

export const approveTokens = async ({
  walletClient,
  address,
  amount,
  erc20Address,
  encryptedERC20Address,
}: {
  walletClient: WalletClient;
  address: Address;
  amount: string;
  erc20Address: Address;
  encryptedERC20Address: Address;
}) => {
  const amountInWei = parseEther(amount);

  return walletClient.writeContract({
    address: erc20Address,
    account: address,
    chain: baseSepolia,
    abi: [
      {
        inputs: [
          { internalType: 'address', name: 'spender', type: 'address' },
          { internalType: 'uint256', name: 'value', type: 'uint256' },
        ],
        name: 'approve',
        outputs: [{ internalType: 'bool', name: '', type: 'bool' }],
        stateMutability: 'nonpayable',
        type: 'function',
      },
    ] as const,
    functionName: 'approve',
    args: [encryptedERC20Address, amountInWei],
  });
};

export const wrapTokens = async ({
  walletClient,
  address,
  amount,
  encryptedERC20Address,
}: {
  walletClient: WalletClient;
  address: Address;
  amount: string;
  encryptedERC20Address: Address;
}) => {
  const amountInWei = parseEther(amount);

  return walletClient.writeContract({
    address: encryptedERC20Address,
    account: address,
    chain: baseSepolia,
    abi: [
      {
        inputs: [{ internalType: 'uint256', name: 'amount', type: 'uint256' }],
        name: 'wrap',
        outputs: [],
        stateMutability: 'nonpayable',
        type: 'function',
      },
    ] as const,
    functionName: 'wrap',
    args: [amountInWei],
  });
};
```

### Confidential Transfer Utilities

```tsx theme={null}
// src/lib/confidential-send.ts
import {
    type Address,
    type PublicClient,
    type WalletClient,
    parseEther,
  } from 'viem';
import { encryptValue, getFee } from './inco-lite';
import { baseSepolia } from '@/context/para-provider';

export const confidentialTransfer = async ({
    amount,
    address,
    recipient,
    walletClient,
    publicClient,
    encryptedERC20Address,
    incoEnv,
  }: {
    amount: string;
    address: Address;
    recipient: Address;
    publicClient: PublicClient;
    walletClient: WalletClient;
    encryptedERC20Address: Address;
    incoEnv: 'testnet' | 'devnet';
  }) => {
    try {
      const amountInWei = parseEther(amount);

      const encryptedAmount = await encryptValue({
        value: amountInWei,
        address,
        contractAddress: encryptedERC20Address,
        env: incoEnv,
      });

      const fee = await getFee(incoEnv, publicClient);

      return walletClient.writeContract({
        address: encryptedERC20Address,
        chain: baseSepolia,
        abi: [
          {
            inputs: [
              { internalType: 'address', name: 'to', type: 'address' },
              { internalType: 'bytes', name: 'encryptedAmount', type: 'bytes' },
            ],
            name: 'transfer',
            outputs: [{ internalType: 'bool', name: '', type: 'bool' }],
            stateMutability: 'payable',
            type: 'function',
          },
        ],
        functionName: 'transfer',
        args: [recipient, encryptedAmount],
        account: address,
        value: fee,
      });
    } catch (error) {
      console.error('Error in confidential transfer:', error);
      throw error;
    }
  };
```

### Unwrap Utilities

```tsx theme={null}
// src/lib/unwrap-utils.ts
import {
    type Address,
    type PublicClient,
    type WalletClient,
    parseEther,
  } from 'viem';
import { AttestedComputeSupportedOps } from '@inco/js/lite';
import { attestedCompute } from './inco-lite';
import { baseSepolia } from '@/context/para-provider';

export const unwrapTokens = async ({
    publicClient,
    walletClient,
    address,
    amount,
    encryptedERC20Address,
    incoEnv,
  }: {
    publicClient: PublicClient;
    walletClient: WalletClient;
    address: Address;
    amount: string;
    encryptedERC20Address: Address;
    incoEnv: 'testnet' | 'devnet';
  }) => {
    try {
      const amountInWei = parseEther(amount);

      const balanceHandle = await publicClient.readContract({
        address: encryptedERC20Address,
        abi: [
          {
            inputs: [{ internalType: 'address', name: 'account', type: 'address' }],
            name: 'balanceOf',
            outputs: [{ internalType: 'bytes32', name: '', type: 'bytes32' }],
            stateMutability: 'view',
            type: 'function',
          },
        ],
        functionName: 'balanceOf',
        args: [address],
      });

      const { attestation, signatures } = await attestedCompute({
        walletClient: {...walletClient, chain: baseSepolia},
        lhsHandle: balanceHandle as `0x${string}`,
        op: AttestedComputeSupportedOps.Ge,
        rhsPlaintext: amountInWei,
        env: incoEnv,
      });

      const unwrapTx = await walletClient.writeContract({
        address: encryptedERC20Address,
        chain: baseSepolia,
        abi: [
          {
            inputs: [
              { internalType: 'uint256', name: 'amount', type: 'uint256' },
              {
                internalType: 'tuple',
                name: 'enoughBalanceDecryptionAttestation',
                components: [
                  { internalType: 'bytes32', name: 'handle', type: 'bytes32' },
                  { internalType: 'bytes32', name: 'value', type: 'bytes32' },
                ],
                type: 'tuple',
              },
              { internalType: 'bytes[]', name: 'signature', type: 'bytes[]' },
            ],
            name: 'unwrap',
            outputs: [],
            stateMutability: 'nonpayable',
            type: 'function',
          },
        ],
        functionName: 'unwrap',
        args: [amountInWei, attestation, signatures],
        account: address,
      });

      await publicClient.waitForTransactionReceipt({
        hash: unwrapTx,
        confirmations: 5,
      });

      return unwrapTx;
    } catch (error) {
      console.error('Unwrap error:', error);
      throw error;
    }
  };
```

## Conclusion

With Para, setting up secure, user-friendly access to Inco Confidential Wrapper contracts is fast and easy.

## Related Walkthroughs

<CardGroup cols={3}>
  <Card title="Miden Integration" icon="shield-halved" href="/v2/walkthroughs/miden">
    Intermediate · 25 min · Zero-knowledge virtual machine
  </Card>

  <Card title="Squid Router Integration" icon="arrows-split-up-and-left" href="/v2/walkthroughs/Squid">
    Advanced · 45 min · Cross-chain USDC bridges
  </Card>

  <Card title="Ethereum Transfers" icon="ethereum" href="/v2/walkthroughs/ethereum-transfers">
    Intermediate · 20 min · Send ETH with Ethers and Viem
  </Card>
</CardGroup>
