From zama-protocol
Write confidential Solidity smart contracts on Zama's FHEVM. Use when the user is writing, reviewing, or debugging encrypted Solidity — FHE types (euint64, ebool), ACL (FHE.allowThis/allow), FHE.select, ERC-7984 confidential tokens, ZamaEthereumConfig, `@fhevm/solidity`, `@openzeppelin/confidential-contracts`, Foundry or Hardhat setup for FHEVM, or any Solidity code that uses the FHE library. For protocol concepts and architecture, load the zama-protocol skill. For TypeScript SDK integration, load the zama-typescript skill.
How this skill is triggered — by the user, by Claude, or both
Slash command
/zama-protocol:zama-solidityThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Encrypted Solidity on FHEVM. This skill covers contract patterns, FHE operations, ACL, ERC-7984 tokens, and project setup.
Encrypted Solidity on FHEVM. This skill covers contract patterns, FHE operations, ACL, ERC-7984 tokens, and project setup.
Before writing any code: load the zama-protocol skill and read the universal gotchas — they cover the protocol-level bugs that apply to all FHEVM work (branching on encrypted values, ACL semantics, trivial encryption, HCU costs, decryption flow). What follows here is Solidity-specific.
Load on demand — don't read them all up front.
| Task | Read |
|---|---|
| Config, encrypted types, FHE gotchas for contracts | references/overview.md |
| Confidential token (encrypted ERC-20, hidden balances) | references/erc7984.md |
| Hand-written FHE ops, manual ACL, production decryption | references/fhe-advanced.md |
| Foundry project setup | references/setups/foundry.md (default) |
| Hardhat project setup | references/setups/hardhat.md |
| Verified contract addresses | references/addresses.md — never guess addresses |
These supplement the universal gotchas in the zama-protocol skill.
Inherit ZamaEthereumConfig first. It sets the FHEVM coprocessor addresses (ACL, FHEVMExecutor, KMSVerifier) per block.chainid. Without it, those addresses default to address(0) and every FHE op reverts the moment it tries to call them — with no revert reason, because the EVM is calling an empty account. Confusing to debug if you don't know to look for it.
Use ERC-7984 for any confidential token. Inherit from @openzeppelin/confidential-contracts/token/ERC7984/ERC7984.sol — never reimplement encrypted balances, allowances, or transfers. The audited version handles ACL, transient allowance handles, and the silent-zero-transfer semantics that hand-rolled versions routinely get wrong. See references/erc7984.md.
Confidential-transfer / ERC-7984 check (review-time). Whenever a contract accepts encrypted amounts (externalEuint64/euint64) and is named or described in value-moving terms — fund, vault, pool, escrow, payroll, subscription, auction settlement, payment — grep the repo for IERC7984, confidentialTransfer, and @openzeppelin/confidential-contracts. Three failure modes to catch:
externalEuint64 "amount." Either confirm the project explicitly documents this (e.g. README says "no token movement") and flag the disclosure, or raise it as a finding. It is easy to sign off on FHE plumbing while missing that the value leg is absent.mapping(address => euint64) balances plus hand-rolled transfer/allowance: reinvented ERC-7984. Direct the author to inherit ERC-7984 from OpenZeppelin instead — same audited fix for ACL, transient allowances, and silent-zero semantics. See references/erc7984.md.ERC7984ERC20Wrapper (or hand-rolled equivalent) for USDC, USDT, WETH, ZAMA, etc., point the author at the deployed wrappers (cUSDC, cUSDT, cWETH, cZAMA) in references/addresses.md. Redeploying a private wrapper fragments the encrypted balance pool, breaks composability with other dApps using the official wrapper, and skips the audited Wrappers Registry — always check references/addresses.md before writing a wrapper.ACL after every state update. FHE.allowThis(handle) for the contract, FHE.allow(handle, user) for decrypt access. ERC-7984 handles this internally; only hand-written FHE code needs it. For single-tx cross-contract handles, prefer FHE.allowTransient over persistent FHE.allow.
FHE.select branches must be encrypted values. Both arms must be the same encrypted type — materialise zeros with FHE.asEuint64(0), not a literal 0. Both branches always execute.
Silent failures on insufficient balance. FHE contracts transfer 0 via FHE.select — no revert. Test the zero-transfer case and call every function twice to prove ACL persistence.
via_ir is a solc setting, not a build-tool requirement. Both Foundry and Hardhat call the same compiler. The official templates (fhevm-foundry-template, fhevm-hardhat-template) compile FHE contracts without IR — start from them. If solc reports "stack too deep" on your own contracts, enable IR (via_ir = true in Foundry, viaIR: true in Hardhat). Don't enable it preemptively as a cargo-cult based on older Zama docs.
FHE.makePubliclyDecryptable(handle) after every update to a publicly-decryptable state variable.
Decryption helpers are test-only. decrypt, userDecrypt, etc. don't exist on production FHE.sol. Make decryption the last step — no conditional actions after.
Provides CDSS development patterns for drug interaction checking, dose validation, clinical scoring (NEWS2, qSOFA), and alert classification integrated into EMR workflows.
npx claudepluginhub zama-ai/skills --plugin zama-protocol