Help us improve
Share bugs, ideas, or general feedback.
From bn-fhevm
Used when writing, refactoring, or reviewing FHEVM Solidity contracts against the @fhevm/solidity@0.11.x API. Covers encrypted types (ebool, euint8..256, eaddress), FHE operations (arithmetic, comparison, FHE.select), the ACL lifecycle (FHE.allow, allowThis, allowTransient, isSenderAllowed, makePubliclyDecryptable), and the input-proof flow (externalEuint* + FHE.fromExternal). Triggers on FHEVM contract patterns, encrypted types, FHE.* library calls, ACL permissions, input proofs, ciphertext handles, ZamaEthereumConfig, or writing a confidential counter / voting / auction / vault contract.
npx claudepluginhub bootnodedev/zama-s2-bounty-skillsHow this skill is triggered — by the user, by Claude, or both
Slash command
/bn-fhevm:fhevm-contractsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Write confidential Solidity contracts with the `@fhevm/solidity@0.11.x` API. This skill routes to three references: [`api-reference.md`](references/api-reference.md) (types, ops, casts), [`control-flow-patterns.md`](references/control-flow-patterns.md) (`FHE.select`, `require` replacements, capping), and [`acl-and-permissions.md`](references/acl-and-permissions.md) (the ACL lifecycle end-to-end...
Measures whether skills, rules, and agent definitions are actually followed by auto-generating test scenarios at 3 strictness levels and reporting compliance rates with full tool call timelines.
Share bugs, ideas, or general feedback.
Write confidential Solidity contracts with the @fhevm/solidity@0.11.x API. This skill routes to three references: api-reference.md (types, ops, casts), control-flow-patterns.md (FHE.select, require replacements, capping), and acl-and-permissions.md (the ACL lifecycle end-to-end). Protocol-level context is in fhevm-overview; the footgun catalogue is in fhevm-antipatterns; ERC-7984 tokens are in fhevm-erc7984.
ZamaEthereumConfig on every contract under contracts/Every .sol file that defines a contract (including mocks, helpers, test stubs) must inherit ZamaEthereumConfig from @fhevm/solidity/config/ZamaConfig.sol. Without it, FHE.* calls revert at runtime because the coprocessor addresses are never wired. This includes mocks that make no FHE calls — the constructor call is harmless, and the grader / lint rule flags every missing inheritance.
import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
contract MyToken is ZamaEthereumConfig, ERC7984 { /* ... */ }
if (encryptedBid > price) does not compile — the contract sees only handles, never plaintext. Use FHE.select(cond, ifTrue, ifFalse). Full pattern catalogue: control-flow-patterns.md.
require(balance >= amount) cannot work on ciphertexts either. Compute both outcomes and select the valid one — the "transfer-zero" pattern. Callers detect failure by decrypting a status code, not by catching reverts.
FHE.allowThis + FHE.allowNew handles from FHE.* ops carry no ACL grants. After any op whose result lands in state:
_result = FHE.add(a, b);
FHE.allowThis(_result); // contract can read next tx
FHE.allow(_result, msg.sender); // user can decrypt
Missing FHE.allowThis is the #1 production failure mode. See acl-and-permissions.md and fhevm-antipatterns entry 9.
FHE.fromExternal is the only entry for user-supplied ciphertextUser-supplied encrypted inputs arrive as (externalEuint*, bytes proof) tuples. Convert them with FHE.fromExternal(encrypted, proof) — this triggers the on-chain InputVerifier ZKPoK check. The proof is bound to (contract_address, user_address); encrypting for the wrong contract invalidates it.
function increment(externalEuint64 enc, bytes calldata proof) external {
euint64 delta = FHE.fromExternal(enc, proof);
/* ... */
}
delegatecall on FHE statedelegatecall exposes the caller's ciphertext handles — ACL is address-bound. Factories use new Contract(...); no minimal proxies, no OZ Clones. Higher deployment gas, but the only safe approach.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;
import {FHE, euint64, ebool, externalEuint64} from "@fhevm/solidity/lib/FHE.sol";
import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
contract ConfidentialCounter is ZamaEthereumConfig {
euint64 private _count;
error OnlyOwner();
address private immutable _owner;
modifier onlyOwner() { if (msg.sender != _owner) revert OnlyOwner(); _; }
constructor() { _owner = msg.sender; }
// 1. Take encrypted input, convert with FHE.fromExternal (input proof).
function increment(externalEuint64 enc, bytes calldata proof) external {
euint64 delta = FHE.fromExternal(enc, proof);
_count = FHE.add(_count, delta);
FHE.allowThis(_count); // contract can reuse next tx
FHE.allow(_count, msg.sender); // caller can decrypt
}
// 2. Expose handle for off-chain userDecrypt (no `view returns (euint*)`).
function countHandle() external view returns (bytes32) {
return euint64.unwrap(_count);
}
// 3. Encrypted conditional (no `if`); reset to zero if the owner calls.
function resetIf(ebool condition) external onlyOwner {
_count = FHE.select(condition, FHE.asEuint64(0), _count);
FHE.allowThis(_count);
FHE.allow(_count, _owner);
}
}
All five Essential Principles exercised in one file: config inheritance, no-branch conditional (FHE.select), derived-handle ACL, input proof, handle exposure via bytes32 (not view returns (euint*)).
For full production-grade reference contracts, see Zama's official examples.
| Question | Go to |
|---|---|
| What types exist, what ops are supported, what are the gas tradeoffs? | api-reference.md |
| How do I write if/else on encrypted values? How do I cap / clamp / saturate? | control-flow-patterns.md |
| What encrypted ops do NOT exist (encrypted divisor, sqrt, fractional power)? | control-flow-patterns.md § What Encrypted Ops Do NOT Exist |
When do I call FHE.allow / allowThis / allowTransient? Why am I getting ACLNotAllowed? | acl-and-permissions.md |
How do two contracts hand off an encrypted handle (caller-side allowTransient + user-side setOperator)? | acl-and-permissions.md § End-to-end cross-contract handover |
| Why are my encrypted values not decryptable off-chain? | fhevm-decryption |
| Why does my contract compile but revert at runtime? | fhevm-antipatterns |
| How do I write tests? How do I build input proofs in Mocha? | fhevm-testing |
| How do I write an ERC-7984 confidential token? | fhevm-erc7984 |
| How does the protocol work end-to-end (coprocessor, KMS, relayer)? | fhevm-overview |
For every contract you write (e.g. contracts/Foo.sol), also produce:
test/foo.test.ts — Hardhat mock test exercising the happy path and at least one userDecrypt assertion.FHE.allowThis + FHE.allow(handle, msg.sender) in the same function.view that exposes state → return bytes32 (the unwrapped handle), never euint*.error OnlyX(); modifier onlyX() { if (...) revert OnlyX(); _; }), not OZ Ownable unless explicitly required.If test/foo.test.ts is missing, or any derived-handle store lacks ACL grants, the contract is not done.
@fhevm/solidity/lib/FHE.sol.euint64 vs euint128 vs euint256 (see api-reference.md § type widths).if/require logic with FHE.select + transfer-zero patterns.fhevm-overview.hardhat vars → fhevm-setup.@fhevm/mock-utils → fhevm-testing.publicDecrypt / userDecrypt client flows → fhevm-decryption.fhevm-frontend.fhevm-erc7984.Canonical pinned versions and Zama documentation links live in fhevm-overview. This skill targets the same @fhevm/solidity@0.11.1 + @openzeppelin/confidential-contracts@0.4.x baseline; Solidity is 0.8.27 (evmVersion: cancun).