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.From its SKILL.md
npx -y skills add interesting-guy/cofhe-dev-skill --skill cofhe-devAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
8.4 KB, ~2.0k tokens by cl100k_base, as published. Nobody here has run it
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)
- Encrypted values are handles, not data.
euint32etc. are wrappers over ciphertext handles (pointers). The actual ciphertext lives in the coprocessor. - 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.
- 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. ForgettingFHE.allowThis()after writing an encrypted value to storage is the #1 bug in CoFHE development. - You cannot branch on encrypted data. No
if (encrypted), norequire(encrypted). UseFHE.select(cond, a, b)— constant-time, both branches always evaluated. - 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?"
- 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) orFHE.verifyDecryptResult(...)(verifies only) - For UI-only display, skip the chain entirely:
decryptForView(ctHash, FheTypes.UintXX).
- Contract grants permission:
Critical gotchas — check EVERY contract against this list
- Missing
FHE.allowThis()after state updates. Any encrypted value stored for later use needsFHE.allowThis(value)in the same transaction, AFTER the operation that produced it. New handles fromFHE.addetc. 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 todecryptForViewtheir balance, the contract must grant them access on every new handle (e.g., after every transfer). - Arithmetic is unchecked.
euinttypes 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: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.
eboolis 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 asInEuintXXinput 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+selectper 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()returnsbytes32in 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 handle0— 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.
decryptForTxrequires exactly one of.withPermit()or.withoutPermit()before.execute().withoutPermitonly works onallowPublic'd handles.
Workflow for any CoFHE task
- 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
- Writing/reviewing Solidity contracts →
- 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?).
- For clients: default to
@cofhe/sdk(NOT cofhejs — it's deprecated). Builder pattern, explicit permits, viem-first. - 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. - 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; inputsInEuintXX,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.mdto any page URL for raw markdown, and/llms.txtis the full index. When in doubt about an API that may have changed, fetch the docs rather than guessing.
What ships with it: 3 files
21.6 KB alongside SKILL.md
references/
- client-sdk.md5.4 KB
- contract-patterns.md11.0 KB
- setup-testing-errors.md5.2 KB