# Cofhe Dev

> Build confidential smart contracts and dApps with Fhenix CoFHE (Fully Homomorphic Encryption coprocessor for EVM chains). Use this skill whenever the user mentions CoFHE, Fhenix, FHE.sol, cofhejs, @cofhe/sdk, FHERC20, ERC-7984, encrypted types (euint/ebool/eaddress), confidential tokens, private balances, encrypted bids/votes, or wants to add privacy/confidentiality to any Solidity contract or EVM dApp. Also use it when debugging "execution reverted" errors, ACLNotAllowed errors, or stuck decryptions in an FHE project — even if the user doesn't name Fhenix explicitly but their code imports cofhe-contracts or @cofhe/sdk.

- Skill: `interesting-guy/cofhe-dev` (Agent Skill, multi-file: 4 files)
- Install (CLI): `npx skillmds@latest add interesting-guy/cofhe-dev`
- Raw SKILL.md: https://api.skillmd.com/api/skills/interesting-guy/cofhe-dev/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: interesting-guy (https://skillmd.com/u/interesting-guy)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/interesting-guy/cofhe-dev

---


# CoFHE Development (Fhenix)

CoFHE is Fhenix's FHE coprocessor: contracts stay on Ethereum/Arbitrum/Base, heavy FHE computation runs off-chain, data stays encrypted end-to-end. Writing CoFHE code is NOT like writing normal Solidity — naive ports leak information or silently break. This skill encodes the rules that make FHE contracts correct on the first try.

## The mental model (read this before writing any code)

1. **Encrypted values are handles, not data.** `euint32` etc. are wrappers over ciphertext handles (pointers). The actual ciphertext lives in the coprocessor.
2. **Everything is asynchronous.** FHE operations are queued and computed off-chain. Results are not available in the same transaction. Design two-phase flows: submit → settle.
3. **Access control is per-handle.** Every handle has an ACL. A handle created in a transaction is accessible to the creating contract *only for that transaction*. Persisting access requires explicit `FHE.allow*` calls. Forgetting `FHE.allowThis()` after writing an encrypted value to storage is the #1 bug in CoFHE development.
4. **You cannot branch on encrypted data.** No `if (encrypted)`, no `require(encrypted)`. Use `FHE.select(cond, a, b)` — constant-time, both branches always evaluated.
5. **Ciphertexts don't hide metadata.** FHE protects *values*, not *behavior*. msg.value, deposit sizes, calldata shape, which function was called, when, and by whom are all public — and any of them correlated with a secret leaks it (e.g., a deposit proportional to a sealed bid reveals the bid). Design rule: every public-facing quantity must be fixed or uniform across users (exact-amount deposits, fixed-size inputs, constant call patterns). When reviewing, ask: "what does a block explorer observer learn from this transaction, ignoring the ciphertexts entirely?"
6. **Decryption is a three-step dance** (new flow — `FHE.decrypt()` is deprecated):
   - Contract grants permission: `FHE.allowPublic(ct)` / `FHE.allow(ct, addr)` / `FHE.allowSender(ct)`
   - Client decrypts off-chain: `decryptForTx(ctHash)` → returns `{ ctHash, decryptedValue, signature }` (signature from the Threshold Network)
   - Anyone submits on-chain: `FHE.publishDecryptResult(ctHash, plaintext, signature)` (stores publicly) or `FHE.verifyDecryptResult(...)` (verifies only)
   - For UI-only display, skip the chain entirely: `decryptForView(ctHash, FheTypes.UintXX)`.

## Critical gotchas — check EVERY contract against this list

- **Missing `FHE.allowThis()` after state updates.** Any encrypted value stored for later use needs `FHE.allowThis(value)` in the same transaction, AFTER the operation that produced it. New handles from `FHE.add` etc. are new handles — the old permission does not carry over.
- **Missing `FHE.allow(value, user)` for user-readable values.** If a user should be able to `decryptForView` their balance, the contract must grant them access on every new handle (e.g., after every transfer).
- **Arithmetic is unchecked.** `euint` types wrap around on overflow/underflow — revert-on-overflow would leak information. Guard with comparisons + select, e.g. clamp a transfer amount to the available balance:
  ```solidity
  ebool canTransfer = FHE.lte(amount, balance);
  euint64 actualAmount = FHE.select(canTransfer, amount, FHE.asEuint64(0));
  ```
- **Division/remainder by encrypted zero returns the type's max value** (e.g. encrypted 255 for euint8), not a revert. Handle it.
- **`ebool` is a euint8 under the hood**, not a real boolean.
- **No revert paths that depend on encrypted values.** Reverting based on plaintext-adjacent conditions derived from ciphertexts leaks information. Constant execution paths only.
- **Use the smallest sufficient bit width** (euint32 over euint64 when possible) — FHE ops are expensive and cost scales with width.
- **Reuse encrypted constants.** Encrypt `0`/`1`/common thresholds once at init, `FHE.allowThis()` them, reuse.
- **Trivially encrypted values (`FHE.asEuint32(5)` from a plaintext literal) are NOT secret** — the plaintext was visible on-chain. Real secrets must come in as `InEuintXX` input structs encrypted client-side with ZK proofs.
- **Do FHE work incrementally per-action, never in settlement loops.** Maintain running aggregates (max, sum, winner index) with one `gt`+`select` per user action; a settlement function that iterates participants doing FHE comparisons is unbounded gas, a fat async batch, and a griefing vector. Settlement should only flip permissions (`allowPublic`) and emit.
- **Verify the bit width fits the value RANGE, not just the type.** Arithmetic is unchecked: euint64 holding wei caps at ~18.4 ETH, and a larger value silently wraps to a tiny one — no revert, no warning. Re-denominate (e.g., gwei instead of wei) or widen the type.
- **`euintXX.unwrap()` returns `bytes32`** in cofhe-contracts 0.1.x, not uint256 — type events/params accordingly.
- **Uninitialized encrypted storage is a landmine.** A never-written `mapping(address => euint64)` entry is handle `0` — any FHE op on it reverts. Initialize encrypted state in the constructor, or guard reads with a fallback: `euint64.unwrap(bal) == 0 ? ZERO : bal` (where ZERO is a pre-allowed encrypted constant).
- **Cross-contract calls need `FHE.allowTransient(ct, otherContract)`** before passing handles to another contract within a transaction.
- **Permits are scoped to chainId + account.** Wrong-chain or wrong-account is the usual cause of mysterious ACL/permit errors client-side.
- **`decryptForTx` requires exactly one of `.withPermit()` or `.withoutPermit()`** before `.execute()`. `withoutPermit` only works on `allowPublic`'d handles.

## Workflow for any CoFHE task

1. **Identify the task type**, then read the matching reference file:
   - Writing/reviewing Solidity contracts → `references/contract-patterns.md`
   - Frontend/client work, encryption, decryption, permits, or migrating from old `cofhejs` → `references/client-sdk.md`
   - Project setup, testing, debugging errors, network/version questions → `references/setup-testing-errors.md`
2. **For contracts:** write the logic, then run the gotcha checklist above line by line. Pay special attention to every storage write of an encrypted type (allowThis?) and every user-facing value (allow user?).
3. **For clients:** default to `@cofhe/sdk` (NOT cofhejs — it's deprecated). Builder pattern, explicit permits, viem-first.
4. **For two-phase flows** (auctions, unshields, reveals): make the async boundary explicit in the design — what is submitted encrypted, what triggers `allowPublic`, who publishes the decrypt result, and what the UI shows while pending.
5. **Local dev:** always start in the mock environment (Hardhat or Foundry plugin) — instant results, plaintext visible for debugging. Move to testnet (Sepolia / Arbitrum Sepolia / Base Sepolia) only after mock tests pass.

## Quick facts (current as of June 2026 — verify before relying on versions)

- Packages: `@fhenixprotocol/cofhe-contracts` (Solidity, v0.1.x), `@cofhe/sdk` (TS client, v0.5.x), `@cofhe/hardhat-plugin`, `@cofhe/hardhat-3-plugin`, `@cofhe/foundry-plugin`, `@cofhe/mock-contracts`, `@fhenixprotocol/cofhe-errors` (error decoder), `@fhenixprotocol/fhenix-confidential-contracts` (FHERC20/ERC-7984 primitives).
- Networks: Sepolia, Arbitrum Sepolia, Base Sepolia (testnet only, API v1).
- Types: `euint8/16/32/64/128`, `ebool`, `eaddress`; inputs `InEuintXX`, `InEbool`, `InEaddress`. Encrypted randomness: `FHE.randomEuintXX()` (front-running-proof — see contract-patterns.md for range/coin-flip recipes).
- Decode revert selectors instantly: `npx cofhe-errors 0x<selector>`.
- Docs live at `https://cofhe-docs.fhenix.zone` — append `.md` to any page URL for raw markdown, and `/llms.txt` is the full index. When in doubt about an API that may have changed, fetch the docs rather than guessing.

