In standard Solidity, you write if (balance >= amount) { ... } to branch on a condition. With encrypted values, this is impossible — you cannot branch on an encrypted boolean because the EVM does not know whether it is true or false.FHEVM solves this with FHE.select(), an encrypted ternary operator that evaluates both branches and picks the correct result — all while keeping the condition encrypted.
// ❌ THIS DOES NOT WORK with encrypted valuesebool hasEnough = FHE.ge(balance, amount);if (hasEnough) { // ERROR: Cannot convert ebool to bool balance = FHE.sub(balance, amount);}
The EVM needs a plaintext bool for if statements. An ebool is an encrypted value — the EVM cannot read it without decryption.
Critical design pattern: Instead of reverting on failure, operations always succeed but transfer 0 (or perform no change) when conditions aren’t met.
Why? If a transaction reverts, an observer learns the condition failed (e.g., “balance was insufficient”). This leaks private information. With FHE.select(), both outcomes execute identically — the observer cannot distinguish success from failure.This pattern appears throughout FHEVM development:
Confidential ERC-20: Transfers send 0 on insufficient balance
Voting: Duplicate votes are silently ignored
Auctions: Invalid bids have no effect
DeFi: Operations fail silently on insufficient collateral
All encrypted comparison operators return ebool:| Operator | Description | Example |
|----------|-------------|---------||
| FHE.eq(a, b) | Equal | FHE.eq(encAge, FHE.asEuint32(18)) |
| FHE.ne(a, b) | Not equal | FHE.ne(encStatus, FHE.asEuint8(0)) |
| FHE.lt(a, b) | Less than | FHE.lt(encBid, encReserve) |
| FHE.le(a, b) | Less than or equal | FHE.le(encAge, FHE.asEuint32(65)) |
| FHE.gt(a, b) | Greater than | FHE.gt(encBalance, encCost) |
| FHE.ge(a, b) | Greater than or equal | FHE.ge(encBalance, encPrice) |
Type restrictions:
euint256 only supports FHE.eq() and FHE.ne() — NO ordering comparisons
function clampBuiltin(uint32 value, uint32 minVal, uint32 maxVal) external { euint32 encVal = FHE.asEuint32(value); euint32 encMin = FHE.asEuint32(minVal); euint32 encMax = FHE.asEuint32(maxVal); euint32 raised = FHE.max(encVal, encMin); // if value < min, use min _result = FHE.min(raised, encMax); // if value > max, use max FHE.allowThis(_result);}
FHE.min() and FHE.max() are built-in and internally use FHE.select(). You can also write them manually: