diff --git a/client-sdk/quick-start/react.mdx b/client-sdk/quick-start/react.mdx index e8b52e9..27427db 100644 --- a/client-sdk/quick-start/react.mdx +++ b/client-sdk/quick-start/react.mdx @@ -1,10 +1,68 @@ --- title: "Quick Start: React" -description: "Get started with @cofhe/sdk in a React application" +description: "Wrap your React app with CofheProvider and use the @cofhe/react hooks to encrypt, decrypt, and read FHE state" --- - -`@cofhe/react` is currently in development. This page will be updated with a full quick start guide once the React library is available. - +`@cofhe/react` is the React layer on top of [`@cofhe/sdk`](/client-sdk/introduction/overview). It ships a context provider, hooks for connection / permits / encryption / decryption / contract reads / writes, and an optional styled UI kit including the `CofheEncryptInput` field and the `CofheFloatingButton` portal. -In the meantime, you can use `@cofhe/sdk/web` directly in your React app. See the [Client Setup](/client-sdk/guides/client-setup) guide for how to configure and connect the SDK with viem or wagmi. +## Install + + + +```bash npm +npm install @cofhe/react @cofhe/sdk @fhenixprotocol/cofhe-contracts viem +``` + +```bash pnpm +pnpm add @cofhe/react @cofhe/sdk @fhenixprotocol/cofhe-contracts viem +``` + +```bash yarn +yarn add @cofhe/react @cofhe/sdk @fhenixprotocol/cofhe-contracts viem +``` + + + +## Wrap your app + +```tsx providers.tsx +'use client'; + +import { CofheProvider } from '@cofhe/react'; +import { createCofheConfig } from '@cofhe/sdk/web'; +import { chains } from '@cofhe/sdk/chains'; + +const config = createCofheConfig({ + environment: 'react', + supportedChains: [chains.sepolia], + react: { projectName: 'My dApp' }, +}); + +export function Providers({ children }: { children: React.ReactNode }) { + return {children}; +} +``` + +## Encrypt and decrypt + +```tsx ConfidentialBalance.tsx +'use client'; + +import { useCofheTokenDecryptedBalance, useCofheEncrypt } from '@cofhe/react'; +import { Encryptable } from '@cofhe/sdk'; + +export function ConfidentialBalance({ tokenAddress }: { tokenAddress: `0x${string}` }) { + const { balance, disabledDueToMissingValidPermit } = useCofheTokenDecryptedBalance(tokenAddress); + const encrypt = useCofheEncrypt(); + + if (disabledDueToMissingValidPermit) return ; + + return
Balance: {balance?.toString() ?? '…'}
; +} +``` + +## Where to next + +- [Getting Started](/client-sdk/react/getting-started) — install, provider, SSR notes, `react.*` config options. +- [Hooks](/client-sdk/react/hooks) — full hook surface (connection, permits, encryption, decryption, reads / writes, token helpers). +- [Components & Portal](/client-sdk/react/components-and-portal) — `CofheEncryptInput`, `CofheFloatingButton`, custom-token import. diff --git a/client-sdk/react/components-and-portal.mdx b/client-sdk/react/components-and-portal.mdx new file mode 100644 index 0000000..9aaf832 --- /dev/null +++ b/client-sdk/react/components-and-portal.mdx @@ -0,0 +1,120 @@ +--- +title: Components & Portal +description: "Drop-in encrypted-input field, the CoFHE floating button portal, and the custom-token import flow" +--- + +The components on this page live behind the `@cofhe/react/ui` entrypoint. Importing from this entry automatically loads the package styles — the headless root `@cofhe/react` import does not. See [Getting Started → Hooks-only vs styled UI](/client-sdk/react/getting-started#hooks-only-vs-styled-ui) for the full picture. + +## `CofheEncryptInput` + +Drop-in input field with type selector, real-time validation against the chosen FHE type, encryption progress reporting, and one-click copy of the encrypted result. + +```tsx +import { CofheEncryptInput } from '@cofhe/react/ui'; +import { FheTypesList } from '@cofhe/react'; + +export function EncryptDemo() { + return ( + console.log('Encrypted:', data)} + onEncryptError={(err) => console.error('Encryption failed:', err)} + /> + ); +} +``` + +### Props + +| Prop | Type | Default | Description | +| --- | --- | --- | --- | +| `placeholder` | `string` | `''` | Input placeholder text. | +| `initialValue` | `string` | `''` | Initial input string. | +| `options` | `FheTypeOption[]` | none | Type-selector dropdown options. Pass `FheTypesList` from `@cofhe/react` for the full set, or a subset to restrict choices. | +| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Component size. | +| `hasError` | `boolean` | `false` | Force the error state (e.g. driven by external validation). | +| `errorMessage` | `string` | `''` | Error string rendered below the input. | +| `disabled` | `boolean` | `false` | Disable input + encrypt actions. | +| `showProgressBar` | `boolean` | `false` | Render the encryption progress bar inline. | +| `debounceMs` | `number` | `300` | Debounce on `onTextChange`. | +| `onTextChange` | `(value: string) => void` | — | Fires on every input change (post-debounce). | +| `onTypeChange` | `(value: string) => void` | — | Fires when the type selector changes. | +| `onEncryptStart` | `(data) => void` | — | Fires when encryption begins. | +| `onEncryptProgress` | `(data) => void` | — | Per-step progress callback (`fetchKeys` → `pack` → `prove` → `verify` → `done`). | +| `onEncryptComplete` | `(data) => void` | — | Fires once the encrypted result is available. | +| `onEncryptError` | `(error: string) => void` | — | Fires on encryption failure. | +| `className` | `string` | — | Extra class on the root element. | +| `testId` | `string` | — | `data-testid` for the root element. | + +Internally `CofheEncryptInput` is driven by [`useEncryptInput`](/client-sdk/react/hooks#useencryptinput-ui-helper) — if you want the same logic without the bundled visuals, drop down to that hook. + +## `CofheFloatingButton` portal + +The portal is the user-facing UI for managing permits, browsing CoFHE tokens, importing custom tokens, shielding / unshielding, and watching pending decryptions. It mounts as a floating button anywhere in your app: + +```tsx +import { CofheProvider } from '@cofhe/react'; +import { CofheFloatingButtonWithProvider } from '@cofhe/react/ui'; + +export function App() { + return ( + + + + + ); +} +``` + +### Wallet-disconnected state + +Since `@cofhe/react@0.5.0`, when no wallet is connected the portal hides the wallet header and primary navigation and shows a "please connect" message instead. To make that message say *your* app name, set `react.projectName` on the config: + +```ts +createCofheConfig({ + environment: 'react', + supportedChains: [chains.sepolia], + react: { projectName: 'My dApp' }, +}); +``` + +See [Getting Started → `react.*` config options](/client-sdk/react/getting-started#react-config-options) for the full table. + +### Position and theme + +Set the initial position and theme through the config: + +```ts +createCofheConfig({ + environment: 'react', + supportedChains: [chains.sepolia], + react: { + position: 'bottom-right', + initialTheme: 'dark', + }, +}); +``` + +Both can also be flipped at runtime via context-bound setters that `CofheProvider` exposes on its inner store (used by the portal's own settings UI). + +## Custom-token import + +Added in `@cofhe/react@0.5.0`. Lets users import a CoFHE-aware token by its contract address straight from the portal's token list or portfolio flow: + +- The portal resolves the token's metadata, on-chain CoFHE compatibility, and (when relevant) its wrapped-token pair before the user confirms the import. +- Imported tokens are persisted per chain in `localStorage` and merged into `useCofheTokens()` results, so other hooks (`useCofheTokenDecryptedBalance`, `useCofheTokenShield`, etc.) treat them as first-class tokens immediately. +- Hooks like `useCofheReadContract`, `useCofheTokenDecryptedBalance`, and `useCofheReadContractAndDecrypt` automatically gate their reads on the imported token's permit / ACL state — `disabledDueToMissingValidPermit` reflects custom-token state the same way it does for known tokens. + +No extra setup is needed beyond mounting `CofheFloatingButton` — the import UI is already wired in. + +## Bundling notes + +- `@cofhe/react/ui` ships its own CSS via the bundle; you don't need to import a separate stylesheet. +- Internal icons are bundled — `@mui/icons-material` and `@mui/material` are **no longer** required peers as of `0.5.0`. If you previously installed them just for `@cofhe/react`, you can remove them. + +## Related + +- [Getting Started](/client-sdk/react/getting-started) — install, provider, SSR notes. +- [Hooks](/client-sdk/react/hooks) — read / write / decrypt / token hooks that back these components. diff --git a/client-sdk/react/getting-started.mdx b/client-sdk/react/getting-started.mdx new file mode 100644 index 0000000..dd85545 --- /dev/null +++ b/client-sdk/react/getting-started.mdx @@ -0,0 +1,143 @@ +--- +title: Getting Started +description: "Set up @cofhe/react in a Next.js, Vite, or any React app" +--- + +`@cofhe/react` is the React layer on top of [`@cofhe/sdk`](/client-sdk/introduction/overview). It ships a context provider, a set of hooks for reading/writing encrypted state, and an optional styled UI kit (`@cofhe/react/ui`) including the `CofheEncryptInput` input and the `CofheFloatingButton` portal. + + +The hooks ship from the root `@cofhe/react` import and are **headless** — no styles, no peer deps on `@mui/material`. The styled UI components live behind a separate `@cofhe/react/ui` entry that loads the package CSS for you. + + +## Installation + + + +```bash npm +npm install @cofhe/react @cofhe/sdk @fhenixprotocol/cofhe-contracts +``` + +```bash pnpm +pnpm add @cofhe/react @cofhe/sdk @fhenixprotocol/cofhe-contracts +``` + +```bash yarn +yarn add @cofhe/react @cofhe/sdk @fhenixprotocol/cofhe-contracts +``` + + + +### Peer dependencies + +| Package | Range | +| --- | --- | +| `react` | `^16.8.0 \|\| ^17 \|\| ^18 \|\| ^19` | +| `react-dom` | same range as `react` | +| `viem` | `^2.38.6` | + + +As of `0.5.0`, `@cofhe/react` **no longer requires** `@mui/material` / `@mui/icons-material` as peers — the internal icons are bundled inside the package. + + +## Wrap your app with `CofheProvider` + +There are two equivalent ways to set up the provider — pass a config and let the provider create the SDK client, or pass an existing client you created yourself. + + + +```tsx Config-only (most apps) +import { CofheProvider } from '@cofhe/react'; +import { createCofheConfig } from '@cofhe/sdk/web'; +import { chains } from '@cofhe/sdk/chains'; + +const config = createCofheConfig({ + environment: 'react', + supportedChains: [chains.sepolia], + react: { + projectName: 'My dApp', + // logger: (level, ...args) => console.log(level, ...args), + }, +}); + +export function Providers({ children }: { children: React.ReactNode }) { + return {children}; +} +``` + +```tsx Pass an existing client +import { CofheProvider } from '@cofhe/react'; +import { createCofheConfig, createCofheClient } from '@cofhe/sdk/web'; +import { chains } from '@cofhe/sdk/chains'; + +const config = createCofheConfig({ + environment: 'react', + supportedChains: [chains.sepolia], +}); +const client = createCofheClient(config); + +export function Providers({ children }: { children: React.ReactNode }) { + return {children}; +} +``` + + + +`CofheProvider` props: + +| Prop | Type | Required | Description | +| --- | --- | --- | --- | +| `config` | `CofheConfig` | one of `config` / `cofheClient` | Provider creates and owns the SDK client. | +| `cofheClient` | `CofheClient` | one of `config` / `cofheClient` | You own the client. The provider asserts `config.environment === 'react'`. | +| `children` | `ReactNode` | yes | Your app tree. | + +### `react.*` config options + +`createCofheConfig({ react: { … } })` accepts a few React-specific knobs: + +| Option | Type | Default | Description | +| --- | --- | --- | --- | +| `projectName` | `string` | `undefined` | Shown in the wallet-disconnected portal state so users see *your* app name instead of a generic message. | +| `logger` | `(level, ...args) => void` | `undefined` | Internal logger for `@cofhe/react`. Pipe to your own observability stack. | +| `position` | `FloatingButtonPosition` | `'bottom-right'` | Default position of the `CofheFloatingButton`. | +| `initialTheme` | `'light' \| 'dark'` | follows system | Initial theme for the styled UI. | + +The `projectName` and `logger` options were added in [`@cofhe/react@0.5.0`](https://github.com/FhenixProtocol/cofhesdk/blob/master/packages/react/CHANGELOG.md#050). + +## Hooks-only vs styled UI + +The root `@cofhe/react` entrypoint is **headless** — it ships only the provider and hooks, with **no package CSS** loaded: + +```tsx +import { CofheProvider, useCofheClient } from '@cofhe/react'; +``` + +The styled UI components live behind `@cofhe/react/ui`. Importing from this entry automatically loads the package styles: + +```tsx +import { CofheProvider } from '@cofhe/react'; +import { CofheFloatingButtonWithProvider, CofheEncryptInput } from '@cofhe/react/ui'; + +export function App() { + return ( + + + + + ); +} +``` + + +If you're styling everything from scratch (Tailwind, CSS-in-JS, design system), stick with the hooks-only entrypoint and skip `@cofhe/react/ui` entirely — you'll save bundle size and own the visuals end-to-end. + + +## SSR support + +Since [`@cofhe/sdk@0.5.1`](https://github.com/FhenixProtocol/cofhesdk/blob/master/packages/sdk/CHANGELOG.md#051), `@cofhe/sdk/web` no longer crashes Next.js / Remix / Astro server builds with `self is not defined`. The TFHE WASM module is lazy-loaded on the first `encryptInputs(...)` call instead of at import time, so importing the SDK from a Server Component (or any code that runs at build time) is safe. + +The provider itself is a Client Component — keep it under a `"use client"` boundary as usual. + +## Next steps + +- [Hooks](/client-sdk/react/hooks) — `useCofheClient`, `useCofheConnection`, `useCofheEnabled`, `useCofheActivePermit`, read / write / decrypt hooks. +- [Components & Portal](/client-sdk/react/components-and-portal) — `CofheEncryptInput`, the `CofheFloatingButton` portal, and the custom-token import flow. diff --git a/client-sdk/react/hooks.mdx b/client-sdk/react/hooks.mdx new file mode 100644 index 0000000..a2f177a --- /dev/null +++ b/client-sdk/react/hooks.mdx @@ -0,0 +1,229 @@ +--- +title: Hooks +description: "React hooks for connection state, permits, encryption, decryption, and contract reads/writes" +--- + +Every hook below is exported from the root `@cofhe/react` entrypoint and works inside any tree wrapped in [`CofheProvider`](/client-sdk/react/getting-started). The hooks compose on top of the SDK client — when you need direct client access, use [`useCofheClient`](#usecofheclient). + +## Client and connection + +### `useCofheClient` + +Returns the SDK `CofheClient` owned by the provider. Use it when you need a one-off, imperative SDK call from inside a component (most apps don't — see the higher-level hooks below). + +```tsx +import { useCofheClient } from '@cofhe/react'; + +const client = useCofheClient(); +``` + +### `useCofheConnection` + +Reactive snapshot of the connection state. Re-renders when the user connects/disconnects a wallet or switches chains. + +```tsx +import { useCofheConnection } from '@cofhe/react'; + +const { connected, account, chainId, publicClient, walletClient } = useCofheConnection(); +``` + +Companion helpers shadow the most common fields so you can destructure narrowly: + +- `useCofheAccount()` → `address | undefined` +- `useCofheChainId()` → `number | undefined` +- `useCofhePublicClient()` → `PublicClient | undefined` +- `useCofheWalletClient()` → `WalletClient | undefined` + +### `useCofheConnect` / `useCofheAutoConnect` + +Imperative hooks for driving the connection lifecycle from app UI (e.g. a custom "Connect" button or a wallet-restore-on-mount effect). + +```tsx +import { useCofheConnect, useCofheAutoConnect } from '@cofhe/react'; + +const { connect, disconnect } = useCofheConnect(); +useCofheAutoConnect(); // restore last connection on mount +``` + +## Coprocessor status + +### `useCofheEnabled` + +Added in `@cofhe/react@0.5.0`. Reads `TaskManager.isEnabled()` from the connected chain and re-evaluates as block state changes. Useful for showing a "CoFHE temporarily unavailable" banner without firing a doomed transaction. + +```tsx +import { useCofheEnabled } from '@cofhe/react'; + +const { enabled, isLoading, error } = useCofheEnabled(); + +if (!isLoading && enabled === false) { + return CoFHE is paused — try again shortly.; +} +``` + +The hook is gated by the TaskManager's `onlyIfEnabled` modifier — see [TaskManager → Enable / Disable Switch](/deep-dive/cofhe-components/task-manager#enable--disable-switch). + +## Permits + +### `useCofheActivePermit` + +Returns the active permit for the current `chainId + account`, plus a typed validity flag (`isValid`) that already accounts for schema, signature, and expiry. + +```tsx +import { useCofheActivePermit } from '@cofhe/react'; + +const { permit, isValid, error } = useCofheActivePermit(); +``` + +`error` is the same typed union as [`ValidationResult.error`](/client-sdk/guides/permits#validating-permits): `'invalid-schema' | 'expired' | 'not-signed' | null`. + +### `useCofheAllPermits` / `useCofheSelectPermit` / `useCofheRemovePermit` + +Bulk management of the permit store. + +```tsx +const allPermits = useCofheAllPermits(); +const select = useCofheSelectPermit(); +const remove = useCofheRemovePermit(); +``` + +### Mutations: create / navigate + +For permit creation flows (e.g. a "Sign permit" button on a settings page), there are higher-level mutations under `@cofhe/react/permits` — `useCofheCreatePermitMutation`, `useCofheNavigateToCreatePermit`, `usePermitForm`, `usePermitsList`. These wrap the underlying SDK calls so you can drop them straight into a UI without manually managing loading/error state. + +## Encryption + +### `useCofheEncrypt` + +Encrypt one or more values from inside a component. Returns the mutation surface so you can drive a button or a form without juggling promises manually. + +```tsx +import { useCofheEncrypt } from '@cofhe/react'; +import { Encryptable } from '@cofhe/sdk'; + +const encrypt = useCofheEncrypt(); + +const onSubmit = async () => { + const [encryptedAmount] = await encrypt.mutateAsync([Encryptable.uint64(amount)]); + // pass encryptedAmount to your contract call +}; +``` + +### `useEncryptInput` (UI helper) + +Powers the styled `CofheEncryptInput` component (see [Components & Portal](/client-sdk/react/components-and-portal)). Use it directly to build a custom encrypted-input UI with progress reporting: + +```tsx +import { useEncryptInput } from '@cofhe/react'; + +const { + onEncryptInput, // (type, value) => Promise + isEncryptingInput, // boolean + encryptionStep, // 'fetchKeys' | 'pack' | 'prove' | 'verify' | 'done' + encryptionProgress, // 0..100 + encryptionProgressLabel, // human-readable label + inputEncryptionDisabled, +} = useEncryptInput(); +``` + +## Reading encrypted state + +### `useCofheReadContract` + +Mirrors wagmi's `useReadContract` for an FHE-aware read. Returns the encrypted ciphertext handle from the contract along with a `disabledDueToMissingValidPermit` flag so your UI can render a "sign a permit" prompt rather than firing a request that would fail anyway. + +```tsx +import { useCofheReadContract } from '@cofhe/react'; + +const { + data, + disabledDueToMissingValidPermit, + isLoading, +} = useCofheReadContract({ + address: token.address, + abi: token.abi, + functionName: 'encryptedBalanceOf', + args: [account], +}); +``` + + +**Renamed in `@cofhe/react@0.5.0`.** This flag used to be `disabledDueToMissingPermit`. The new name (`...ValidPermit`) reflects that it goes to `true` when the active permit is missing **or** invalid (expired / unsigned / malformed schema). Update any consumer code that destructured the old name. + + +### `useCofheReadContracts` + +Multi-call variant — same shape, takes an array of contracts. + +### `useCofheReadContractAndDecrypt` + +Reads an encrypted handle from a contract **and** decrypts it via the SDK in one hook. Useful for "display a confidential balance" UIs: + +```tsx +import { useCofheReadContractAndDecrypt } from '@cofhe/react'; +import { FheTypes } from '@cofhe/sdk'; + +const { data, disabledDueToMissingValidPermit, isLoading } = useCofheReadContractAndDecrypt({ + address: token.address, + abi: token.abi, + functionName: 'encryptedBalanceOf', + args: [account], + fheType: FheTypes.Uint64, +}); +``` + +### `useCofheTokenDecryptedBalance` + +Convenience wrapper around the read-and-decrypt flow specifically for CoFHE tokens. Re-renders when the active permit becomes valid. + +```tsx +const { balance, disabledDueToMissingValidPermit } = useCofheTokenDecryptedBalance(tokenAddress); +``` + +## Writing encrypted state + +### `useCofheWriteContract` / `useCofheSimulateWriteContract` + +FHE-aware analogues of wagmi's `useWriteContract` / `useSimulateContract`. They encrypt any `Encryptable.*` values in the `args` array before submitting: + +```tsx +const { writeContractAsync } = useCofheWriteContract(); + +await writeContractAsync({ + address: token.address, + abi: token.abi, + functionName: 'transfer', + args: [recipient, Encryptable.uint64(amount)], +}); +``` + +The simulate variant returns the same data shape wagmi does, plus encryption awaits. + +## CoFHE token helpers + +A set of higher-level hooks for the official CoFHE token flow (shield / unshield / transfer / claim): + +| Hook | Purpose | +| --- | --- | +| `useCofheTokens` | List of CoFHE-aware tokens for the current chain. Includes **custom tokens** the user imported (added in `@cofhe/react@0.5.0`). | +| `useCofheTokenLists` | Underlying token-list resolver. | +| `useCofheTokenPublicBalance` | Public ERC-20 balance for a token. | +| `useCofheTokenTransfer` | `transfer` mutation. | +| `useCofheTokenApprove` | `approve` mutation. | +| `useCofheTokenShield` | Shield a public balance into the encrypted side. | +| `useCofheTokenUnshield` | Request unshield (returns an `UnshieldClaim` handle). | +| `useCofheTokenClaimUnshielded` | Finalize an unshield claim once the decryption result lands on-chain. | +| `useCofheTokenClaimable` / `useCofheTokensClaimable` | Discover claimable unshield positions for one or many tokens. | +| `useCofheTokenDecryptedBalance` | See [Reading encrypted state](#reading-encrypted-state) above. | +| `useCofheTokensWithExistingEncryptedBalances` | Filter the token list down to those the connected account already holds. | +| `useCofhePinnedToken` / `useCofhePinnedTokenAddress` | Persisted "primary token" selection. | + +Each shield/unshield/claim hook also exposes a `getCofhe…CallArgs(...)` helper for cases where you want to compose the transaction call manually instead of using the mutation. + +## Wagmi parity + +Hooks that mirror a wagmi API keep the same prop shape on purpose — `address`, `abi`, `functionName`, `args` flow through identically. The CoFHE-specific surface (the `disabledDueToMissingValidPermit` flag, the `fheType` arg on `useCofheReadContractAndDecrypt`, the auto-encryption of `Encryptable.*` values) is additive. + +## Next steps + +- [Components & Portal](/client-sdk/react/components-and-portal) — `CofheEncryptInput`, `CofheFloatingButton`, the custom-token import flow. diff --git a/docs.json b/docs.json index 5c8b34d..1121671 100644 --- a/docs.json +++ b/docs.json @@ -112,6 +112,14 @@ "client-sdk/foundry-plugin/testing" ] }, + { + "group": "React", + "pages": [ + "client-sdk/react/getting-started", + "client-sdk/react/hooks", + "client-sdk/react/components-and-portal" + ] + }, { "group": "Examples", "pages": [