## Problem
When writing a function that logically feels read-only — such as checking whether a submitted encrypted value meets a threshold — developers will naturally reach for the `view` modifier:
```solidity
function meetsThreshold(
uint256 id,
externalEuint32 req
) external view returns (ebool) {
euint32 val = FHE.fromExternal(req, permission);
return FHE.ge(val, thresholds[id]);
}
This fails to compile with:
TypeError: Function declared as view, but this expression (potentially) modifies the state.
The error is technically correct but non-obvious and currently undocumented.
Root Cause
FHE.fromExternal internally calls Impl.verify(), which validates the external ciphertext proof against the FHE coprocessor. This writes state at the EVM level, making it incompatible with view — even though it doesn't mutate any application-level contract state.
The same applies to other FHE precompile operations including FHE.add, FHE.sub, FHE.mul, FHE.ge, FHE.le, and FHE.eq in certain contexts.
Proposed Fix
No code change needed. This is purely a documentation improvement.
The Getting Started guide and FHE.fromExternal API reference should explicitly state:
Functions using FHE.fromExternal or any FHE precompile operation cannot be marked as view. Although these operations do not modify your contract's application state, they perform internal proof validation that modifies EVM state under the hood. Declare such functions without view and invoke them as regular transactions or via eth_call.
Additionally, a short note on the recommended pattern for developers wanting off-chain query-like behavior would be helpful. For example, separating proof submission and threshold evaluation into two distinct steps.
Environment
- fhEVM version: latest
- Toolchain: Hardhat / Foundry (standard
solc setups)
- Network: SepoliaETH
This fails to compile with:
The error is technically correct but non-obvious and currently undocumented.
Root Cause
FHE.fromExternalinternally callsImpl.verify(), which validates the external ciphertext proof against the FHE coprocessor. This writes state at the EVM level, making it incompatible withview— even though it doesn't mutate any application-level contract state.The same applies to other FHE precompile operations including
FHE.add,FHE.sub,FHE.mul,FHE.ge,FHE.le, andFHE.eqin certain contexts.Proposed Fix
No code change needed. This is purely a documentation improvement.
The Getting Started guide and
FHE.fromExternalAPI reference should explicitly state:Additionally, a short note on the recommended pattern for developers wanting off-chain query-like behavior would be helpful. For example, separating proof submission and threshold evaluation into two distinct steps.
Environment
solcsetups)