create-fhevm-example 1.4.3 → 1.4.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/contracts/advanced/BlindAuction.sol +22 -57
  2. package/contracts/advanced/EncryptedEscrow.sol +7 -31
  3. package/contracts/advanced/HiddenVoting.sol +19 -54
  4. package/contracts/advanced/PrivateKYC.sol +9 -33
  5. package/contracts/advanced/PrivatePayroll.sol +9 -35
  6. package/contracts/basic/decryption/PublicDecryptMultipleValues.sol +19 -21
  7. package/contracts/basic/decryption/PublicDecryptSingleValue.sol +19 -20
  8. package/contracts/basic/decryption/UserDecryptMultipleValues.sol +11 -21
  9. package/contracts/basic/decryption/UserDecryptSingleValue.sol +15 -30
  10. package/contracts/basic/encryption/EncryptMultipleValues.sol +16 -22
  11. package/contracts/basic/encryption/EncryptSingleValue.sol +15 -17
  12. package/contracts/basic/encryption/FHECounter.sol +19 -17
  13. package/contracts/basic/fhe-operations/FHEAdd.sol +14 -16
  14. package/contracts/basic/fhe-operations/FHEArithmetic.sol +19 -31
  15. package/contracts/basic/fhe-operations/FHEComparison.sol +12 -29
  16. package/contracts/basic/fhe-operations/FHEIfThenElse.sol +9 -10
  17. package/contracts/concepts/FHEAccessControl.sol +28 -28
  18. package/contracts/concepts/FHEAntiPatterns.sol +8 -37
  19. package/contracts/concepts/FHEHandles.sol +14 -29
  20. package/contracts/concepts/FHEInputProof.sol +13 -33
  21. package/contracts/gaming/EncryptedLottery.sol +11 -38
  22. package/contracts/gaming/EncryptedPoker.sol +8 -34
  23. package/contracts/gaming/RockPaperScissors.sol +43 -64
  24. package/contracts/openzeppelin/ERC7984.sol +6 -1
  25. package/contracts/openzeppelin/ERC7984ERC20Wrapper.sol +5 -1
  26. package/contracts/openzeppelin/SwapERC7984ToERC20.sol +5 -1
  27. package/contracts/openzeppelin/SwapERC7984ToERC7984.sol +5 -1
  28. package/contracts/openzeppelin/VestingWallet.sol +13 -17
  29. package/dist/scripts/commands/generate-config.js +18 -3
  30. package/dist/scripts/shared/config.d.ts.map +1 -1
  31. package/dist/scripts/shared/config.js +81 -75
  32. package/package.json +1 -1
@@ -5,21 +5,23 @@ import {FHE, euint8, externalEuint8} from "@fhevm/solidity/lib/FHE.sol";
5
5
  import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
6
6
 
7
7
  /**
8
- * @notice Simple example: adding two encrypted values (a + b)
9
- *
10
- * @dev Demonstrates the most basic FHE operation and permission flow.
8
+ * @notice Introduction to homomorphic addition on encrypted values.
9
+ * Demonstrates the most fundamental FHE operation: adding two encrypted
10
+ * numbers without decrypting them. Shows the complete flow from receiving
11
+ * encrypted inputs, performing the addition, and granting permissions
12
+ * for both contract storage and user decryption.
13
+
14
+ * @dev Shows the most basic FHE operation and permission flow.
15
+ * ⚡ Gas: FHE.add() costs ~100k gas (coprocessor call)
11
16
  */
12
17
  contract FHEAdd is ZamaEthereumConfig {
13
18
  euint8 private _a;
14
19
  euint8 private _b;
15
20
  euint8 private _result;
16
21
 
17
- constructor() {}
18
-
19
22
  /// @notice Set the first operand (encrypted)
20
23
  function setA(externalEuint8 inputA, bytes calldata inputProof) external {
21
24
  _a = FHE.fromExternal(inputA, inputProof);
22
- // Only contract needs permission to use this for computation
23
25
  FHE.allowThis(_a);
24
26
  }
25
27
 
@@ -30,21 +32,17 @@ contract FHEAdd is ZamaEthereumConfig {
30
32
  }
31
33
 
32
34
  /// @notice Compute a + b on encrypted values
33
- /// @dev The contract computes on ciphertexts - it never sees actual values!
35
+ /// @dev Contract operates on ciphertexts - never sees actual values!
34
36
  function computeAPlusB() external {
35
- // 🔐 Addition on encrypted values
36
- // Neither the contract nor anyone else knows what a, b, or result are
37
+ // 🧮 Homomorphic: operates on encrypted data without decrypting
37
38
  _result = FHE.add(_a, _b);
38
39
 
39
- // 📋 PERMISSION FLOW:
40
- // During this function, contract has "ephemeral" permission on _result
41
- // When function ends, ephemeral permission is revoked
42
- // We need PERMANENT permissions for future access:
43
- FHE.allowThis(_result); // Contract can use result later
44
- FHE.allow(_result, msg.sender); // Caller can decrypt result
40
+ // 🔑 Why both? allowThis = contract can use it, allow = user can decrypt
41
+ FHE.allowThis(_result);
42
+ FHE.allow(_result, msg.sender);
45
43
  }
46
44
 
47
- /// @notice Returns the encrypted result
45
+ /// @notice Returns the encrypted result handle
48
46
  function result() public view returns (euint8) {
49
47
  return _result;
50
48
  }
@@ -5,93 +5,81 @@ import {FHE, euint32, externalEuint32} from "@fhevm/solidity/lib/FHE.sol";
5
5
  import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
6
6
 
7
7
  /**
8
- * @notice Demonstrates all FHE arithmetic operations on encrypted integers
8
+ * @notice Complete suite of FHE arithmetic operations on encrypted values.
9
+ * Covers all basic math: addition, subtraction, multiplication, division,
10
+ * remainder (modulo), minimum, and maximum. Includes gas cost comparisons
11
+ * and important limitations (e.g., division/remainder only work with
12
+ * plaintext divisors, not encrypted divisors).
9
13
  *
10
- * @dev This contract shows how to perform mathematical operations on encrypted values
11
- * without ever revealing the underlying data.
12
- *
13
- * Available operations:
14
- * - FHE.add(a, b) : Addition
15
- * - FHE.sub(a, b) : Subtraction
16
- * - FHE.mul(a, b) : Multiplication
17
- * - FHE.div(a, b) : Division (integer)
18
- * - FHE.rem(a, b) : Remainder (modulo)
19
- * - FHE.min(a, b) : Minimum of two values
20
- * - FHE.max(a, b) : Maximum of two values
14
+ * @dev Gas costs vary: add/sub (~100k) < mul (~150k) < div/rem (~300k)
15
+ * ⚠️ div/rem only work with plaintext divisor (not encrypted!)
21
16
  */
22
17
  contract FHEArithmetic is ZamaEthereumConfig {
23
18
  euint32 private _a;
24
19
  euint32 private _b;
25
20
  euint32 private _result;
26
21
 
27
- // solhint-disable-next-line no-empty-blocks
28
- constructor() {}
29
-
30
- /// @notice Sets the first operand (encrypted)
31
22
  function setA(externalEuint32 inputA, bytes calldata inputProof) external {
32
23
  _a = FHE.fromExternal(inputA, inputProof);
33
24
  FHE.allowThis(_a);
34
25
  }
35
26
 
36
- /// @notice Sets the second operand (encrypted)
37
27
  function setB(externalEuint32 inputB, bytes calldata inputProof) external {
38
28
  _b = FHE.fromExternal(inputB, inputProof);
39
29
  FHE.allowThis(_b);
40
30
  }
41
31
 
42
- /// @notice Computes encrypted addition: result = a + b
32
+ /// @notice Encrypted addition: result = a + b
43
33
  function computeAdd() external {
44
34
  _result = FHE.add(_a, _b);
45
35
  _grantPermissions();
46
36
  }
47
37
 
48
- /// @notice Computes encrypted subtraction: result = a - b
49
- /// @dev No underflow protection - in production, add range checks
38
+ /// @notice Encrypted subtraction: result = a - b
39
+ /// @dev ⚠️ No underflow protection! Wraps around at 0.
50
40
  function computeSub() external {
51
41
  _result = FHE.sub(_a, _b);
52
42
  _grantPermissions();
53
43
  }
54
44
 
55
- /// @notice Computes encrypted multiplication: result = a * b
56
- /// @dev No overflow protection - in production, add range checks
45
+ /// @notice Encrypted multiplication: result = a * b
46
+ /// @dev ⚠️ No overflow protection! May wrap at type max.
57
47
  function computeMul() external {
58
48
  _result = FHE.mul(_a, _b);
59
49
  _grantPermissions();
60
50
  }
61
51
 
62
- /// @notice Computes encrypted division: result = a / b (scalar)
63
- /// @dev Divisor must be a scalar (plaintext) because FHE division by encrypted value is not supported.
52
+ /// @notice Encrypted division: result = a / divisor
53
+ /// @dev WRONG: FHE.div(encryptedA, encryptedB) - not supported!
54
+ /// ✅ CORRECT: FHE.div(encryptedA, plaintextB)
64
55
  function computeDiv(uint32 divisor) external {
65
56
  _result = FHE.div(_a, divisor);
66
57
  _grantPermissions();
67
58
  }
68
59
 
69
- /// @notice Computes encrypted remainder: result = a % b (scalar)
70
- /// @dev Divisor must be a scalar (plaintext).
60
+ /// @notice Encrypted remainder: result = a % modulus
61
+ /// @dev Modulus must be plaintext (same limitation as div)
71
62
  function computeRem(uint32 modulus) external {
72
63
  _result = FHE.rem(_a, modulus);
73
64
  _grantPermissions();
74
65
  }
75
66
 
76
- /// @notice Computes encrypted minimum: result = min(a, b)
67
+ /// @notice Encrypted minimum: result = min(a, b)
77
68
  function computeMin() external {
78
69
  _result = FHE.min(_a, _b);
79
70
  _grantPermissions();
80
71
  }
81
72
 
82
- /// @notice Computes encrypted maximum: result = max(a, b)
73
+ /// @notice Encrypted maximum: result = max(a, b)
83
74
  function computeMax() external {
84
75
  _result = FHE.max(_a, _b);
85
76
  _grantPermissions();
86
77
  }
87
78
 
88
- /// @notice Returns the encrypted result
89
- /// @dev Caller must have FHE permissions to decrypt
90
79
  function getResult() public view returns (euint32) {
91
80
  return _result;
92
81
  }
93
82
 
94
- /// @dev Grants FHE permissions to contract and caller for decryption
95
83
  function _grantPermissions() internal {
96
84
  FHE.allowThis(_result);
97
85
  FHE.allow(_result, msg.sender);
@@ -10,19 +10,15 @@ import {
10
10
  import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
11
11
 
12
12
  /**
13
- * @notice Demonstrates all FHE comparison operations on encrypted integers
13
+ * @notice Complete guide to encrypted comparisons and conditional selection.
14
+ * Covers all comparison operators (eq, ne, gt, lt, ge, le) that return
15
+ * encrypted booleans (ebool), and demonstrates FHE.select for branching
16
+ * without information leakage. Critical for implementing logic like
17
+ * "find maximum" or "check threshold" without revealing values.
14
18
  *
15
- * @dev This contract shows how to compare encrypted values without decrypting them.
16
- * Comparison results are returned as encrypted booleans (ebool).
17
- *
18
- * Available operations:
19
- * - FHE.eq(a, b) : Equal (a == b)
20
- * - FHE.ne(a, b) : Not equal (a != b)
21
- * - FHE.gt(a, b) : Greater than (a > b)
22
- * - FHE.lt(a, b) : Less than (a < b)
23
- * - FHE.ge(a, b) : Greater or equal (a >= b)
24
- * - FHE.le(a, b) : Less or equal (a <= b)
25
- * - FHE.select(cond, a, b) : Conditional selection (cond ? a : b)
19
+ * @dev Results are encrypted booleans (ebool) - comparisons reveal nothing!
20
+ * Gas: Comparisons ~100k, select ~120k
21
+ * ❌ WRONG: if (FHE.gt(a,b)) → decrypts! ✅ CORRECT: FHE.select()
26
22
  */
27
23
  contract FHEComparison is ZamaEthereumConfig {
28
24
  euint32 private _a;
@@ -30,67 +26,57 @@ contract FHEComparison is ZamaEthereumConfig {
30
26
  ebool private _boolResult;
31
27
  euint32 private _selectedResult;
32
28
 
33
- // solhint-disable-next-line no-empty-blocks
34
- constructor() {}
35
-
36
- /// @notice Sets the first operand (encrypted)
37
29
  function setA(externalEuint32 inputA, bytes calldata inputProof) external {
38
30
  _a = FHE.fromExternal(inputA, inputProof);
39
31
  FHE.allowThis(_a);
40
32
  }
41
33
 
42
- /// @notice Sets the second operand (encrypted)
43
34
  function setB(externalEuint32 inputB, bytes calldata inputProof) external {
44
35
  _b = FHE.fromExternal(inputB, inputProof);
45
36
  FHE.allowThis(_b);
46
37
  }
47
38
 
48
- /// @notice Computes encrypted equality: result = (a == b)
49
39
  function computeEq() external {
50
40
  _boolResult = FHE.eq(_a, _b);
51
41
  _grantBoolPermissions();
52
42
  }
53
43
 
54
- /// @notice Computes encrypted inequality: result = (a != b)
55
44
  function computeNe() external {
56
45
  _boolResult = FHE.ne(_a, _b);
57
46
  _grantBoolPermissions();
58
47
  }
59
48
 
60
- /// @notice Computes encrypted greater than: result = (a > b)
61
49
  function computeGt() external {
62
50
  _boolResult = FHE.gt(_a, _b);
63
51
  _grantBoolPermissions();
64
52
  }
65
53
 
66
- /// @notice Computes encrypted less than: result = (a < b)
67
54
  function computeLt() external {
68
55
  _boolResult = FHE.lt(_a, _b);
69
56
  _grantBoolPermissions();
70
57
  }
71
58
 
72
- /// @notice Computes encrypted greater or equal: result = (a >= b)
73
59
  function computeGe() external {
74
60
  _boolResult = FHE.ge(_a, _b);
75
61
  _grantBoolPermissions();
76
62
  }
77
63
 
78
- /// @notice Computes encrypted less or equal: result = (a <= b)
79
64
  function computeLe() external {
80
65
  _boolResult = FHE.le(_a, _b);
81
66
  _grantBoolPermissions();
82
67
  }
83
68
 
84
- /// @notice Computes encrypted maximum using select: result = (a > b) ? a : b
85
- /// @dev Demonstrates FHE.select for conditional logic on encrypted values
69
+ /// @notice Encrypted maximum using select: (a > b) ? a : b
70
+ /// @dev 🔀 Why select? Using if(aGtB) would decrypt and leak the comparison!
86
71
  function computeMaxViaSelect() external {
87
72
  ebool aGtB = FHE.gt(_a, _b);
73
+ // select evaluates both branches, picks one without revealing which
88
74
  _selectedResult = FHE.select(aGtB, _a, _b);
89
75
  FHE.allowThis(_selectedResult);
90
76
  FHE.allow(_selectedResult, msg.sender);
91
77
  }
92
78
 
93
- /// @notice Computes encrypted minimum using select: result = (a < b) ? a : b
79
+ /// @notice Encrypted minimum using select: (a < b) ? a : b
94
80
  function computeMinViaSelect() external {
95
81
  ebool aLtB = FHE.lt(_a, _b);
96
82
  _selectedResult = FHE.select(aLtB, _a, _b);
@@ -98,17 +84,14 @@ contract FHEComparison is ZamaEthereumConfig {
98
84
  FHE.allow(_selectedResult, msg.sender);
99
85
  }
100
86
 
101
- /// @notice Returns the encrypted boolean result
102
87
  function getBoolResult() public view returns (ebool) {
103
88
  return _boolResult;
104
89
  }
105
90
 
106
- /// @notice Returns the encrypted selected result
107
91
  function getSelectedResult() public view returns (euint32) {
108
92
  return _selectedResult;
109
93
  }
110
94
 
111
- /// @dev Grants FHE permissions for boolean result
112
95
  function _grantBoolPermissions() internal {
113
96
  FHE.allowThis(_boolResult);
114
97
  FHE.allow(_boolResult, msg.sender);
@@ -5,17 +5,20 @@ import {FHE, ebool, euint8, externalEuint8} from "@fhevm/solidity/lib/FHE.sol";
5
5
  import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
6
6
 
7
7
  /**
8
- * @notice Demonstrates conditional logic: max(a, b) using encrypted comparison
8
+ * @notice Conditional logic without information leakage using FHE.select.
9
+ * Demonstrates how to implement if-then-else logic on encrypted values
10
+ * (computing max of two numbers). Using regular if/else would decrypt
11
+ * and leak which branch was taken. FHE.select evaluates BOTH branches
12
+ * and picks one based on the encrypted condition, preserving privacy.
9
13
  *
10
- * @dev Shows how to use FHE.select() for encrypted if-then-else logic.
14
+ * @dev Can't use if/else (leaks info!) Use FHE.select instead
15
+ * ⚡ Gas: ~120k for select operation
11
16
  */
12
17
  contract FHEIfThenElse is ZamaEthereumConfig {
13
18
  euint8 private _a;
14
19
  euint8 private _b;
15
20
  euint8 private _max;
16
21
 
17
- constructor() {}
18
-
19
22
  /// @notice Sets the first operand (encrypted)
20
23
  function setA(externalEuint8 inputA, bytes calldata inputProof) external {
21
24
  _a = FHE.fromExternal(inputA, inputProof);
@@ -31,14 +34,10 @@ contract FHEIfThenElse is ZamaEthereumConfig {
31
34
  /// @notice Compute max(a, b) without revealing which is larger
32
35
  /// @dev Uses FHE.select() - the encrypted "if-then-else"
33
36
  function computeMax() external {
34
- // 🔍 Compare encrypted values - result is encrypted boolean!
35
- // We don't know if a >= b, only the encrypted result
36
37
  ebool aIsGreaterOrEqual = FHE.ge(_a, _b);
37
38
 
38
- // 🔀 FHE.select(condition, ifTrue, ifFalse)
39
- // - BOTH branches are evaluated (no short-circuit)
40
- // - Result is encrypted - no one knows which was selected
41
- // - This is how you do "if-else" on encrypted values!
39
+ // 🔀 Why select? if/else would leak which branch was taken!
40
+ // select evaluates BOTH branches, picks one based on encrypted condition
42
41
  _max = FHE.select(aIsGreaterOrEqual, _a, _b);
43
42
 
44
43
  // Grant permissions for decryption
@@ -5,21 +5,20 @@ import {FHE, euint32, externalEuint32} from "@fhevm/solidity/lib/FHE.sol";
5
5
  import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
6
6
 
7
7
  /**
8
- * @notice Critical access control patterns in FHEVM: FHE.allow, FHE.allowThis, FHE.allowTransient. Includes common mistakes and correct implementations.
9
- *
10
- * @dev Key functions:
11
- * FHE.allow(handle, address) - permanent permission
12
- * FHE.allowThis(handle) - permission for contract itself
13
- * FHE.allowTransient(handle, address) - temporary, expires at tx end
8
+ * @notice Master class for FHE permission patterns and access control.
9
+ * Explains the three permission types: allow() for permanent access,
10
+ * allowThis() for contract operations, and allowTransient() for
11
+ * temporary cross-contract calls. Includes correct and incorrect
12
+ * usage examples to prevent common decryption failures.
13
+
14
+ * @dev allow() = permanent, allowThis() = contract permission,
15
+ * allowTransient() = expires at TX end
16
+ * Both allowThis + allow required for user decryption!
14
17
  */
15
18
  contract FHEAccessControl is ZamaEthereumConfig {
16
19
  euint32 private _secretValue;
17
20
  mapping(address => bool) public hasAccess;
18
21
 
19
- constructor() {}
20
-
21
- // ==================== CORRECT PATTERN ====================
22
-
23
22
  /// @notice ✅ CORRECT: Full access pattern for user decryption
24
23
  function storeWithFullAccess(
25
24
  externalEuint32 input,
@@ -27,13 +26,12 @@ contract FHEAccessControl is ZamaEthereumConfig {
27
26
  ) external {
28
27
  _secretValue = FHE.fromExternal(input, inputProof);
29
28
 
30
- // ⚠️ CRITICAL: BOTH are required for user decryption!
31
- FHE.allowThis(_secretValue); // Contract can operate on it
32
- FHE.allow(_secretValue, msg.sender); // User can decrypt it
33
-
34
- // ❓ Why allowThis is needed for user decryption?
35
- // User decryption = re-encryption for user's key
36
- // This requires contract's permission to "release" the value
29
+ // Why BOTH allowThis + allow?
30
+ // - allowThis: Contract authorizes "releasing" the encrypted value
31
+ // - allow(user): User can request decryption for their key
32
+ // Missing either = decryption fails!
33
+ FHE.allowThis(_secretValue);
34
+ FHE.allow(_secretValue, msg.sender);
37
35
 
38
36
  hasAccess[msg.sender] = true;
39
37
  }
@@ -42,7 +40,8 @@ contract FHEAccessControl is ZamaEthereumConfig {
42
40
  function grantAccess(address user) external {
43
41
  require(hasAccess[msg.sender], "Caller has no access to grant");
44
42
 
45
- // Works because contract has allowThis permission
43
+ // Why this works: Contract already has allowThis from storeWithFullAccess
44
+ // We only need to grant allow(user) for the new user
46
45
  FHE.allow(_secretValue, user);
47
46
  hasAccess[user] = true;
48
47
  }
@@ -51,8 +50,6 @@ contract FHEAccessControl is ZamaEthereumConfig {
51
50
  return _secretValue;
52
51
  }
53
52
 
54
- // ==================== WRONG PATTERNS (EDUCATIONAL) ====================
55
-
56
53
  /// @notice ❌ WRONG: Missing allowThis → user decryption FAILS
57
54
  function storeWithoutAllowThis(
58
55
  externalEuint32 input,
@@ -60,8 +57,9 @@ contract FHEAccessControl is ZamaEthereumConfig {
60
57
  ) external {
61
58
  _secretValue = FHE.fromExternal(input, inputProof);
62
59
 
63
- // ❌ Missing: FHE.allowThis(_secretValue)
64
- // User has permission, but decryption will FAIL!
60
+ // ❌ Common mistake: Only allow(user) without allowThis
61
+ // Result: User has permission but can't decrypt!
62
+ // Why? Decryption needs contract to authorize the release
65
63
  FHE.allow(_secretValue, msg.sender);
66
64
  }
67
65
 
@@ -72,21 +70,23 @@ contract FHEAccessControl is ZamaEthereumConfig {
72
70
  ) external {
73
71
  _secretValue = FHE.fromExternal(input, inputProof);
74
72
 
73
+ // ❌ Another mistake: Only allowThis without allow(user)
74
+ // Result: Contract can compute but no one can decrypt!
75
75
  FHE.allowThis(_secretValue);
76
- // ❌ Missing: FHE.allow(_secretValue, msg.sender)
77
- // Contract can operate, but no one can decrypt!
78
76
  }
79
77
 
80
- // ==================== TRANSIENT ACCESS ====================
81
-
82
78
  /// @notice Temporary access - expires at end of transaction
83
- /// @dev Use for: passing values between contracts in same tx
79
+ /// @dev Gas: allowTransient ~50% cheaper than allow!
80
+ /// Use for passing values between contracts in same TX
84
81
  function computeAndShareTransient(
85
82
  address recipient
86
83
  ) external returns (euint32) {
87
84
  euint32 computed = FHE.add(_secretValue, FHE.asEuint32(1));
88
85
 
89
- // 💨 Transient = cheaper than permanent, auto-expires
86
+ // Why allowTransient instead of allow?
87
+ // - Cheaper: ~50% less gas than permanent allow
88
+ // - Auto-cleanup: Expires at TX end (no storage pollution)
89
+ // - Use case: Passing values between contracts in same transaction
90
90
  FHE.allowTransient(computed, recipient);
91
91
 
92
92
  return computed;
@@ -10,28 +10,19 @@ import {
10
10
  import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
11
11
 
12
12
  /**
13
- * @notice Common FHE mistakes and their correct alternatives. Covers: branching, permissions, require/revert, re-encryption, loops, noise, and deprecated APIs.
13
+ * @notice Comprehensive guide to FHE anti-patterns and their solutions.
14
+ * Covers 9 critical mistakes: using if/else on encrypted values,
15
+ * incorrect permission patterns, require() statements that leak info,
16
+ * unbounded loops, noise accumulation, and deprecated APIs.
17
+ * Each pattern shows both ❌ WRONG and ✅ CORRECT implementations.
14
18
  *
15
- * @dev This contract demonstrates 9 critical anti-patterns in FHE development:
16
- * 1. Branching on encrypted values (causes decryption!)
17
- * 2. Returning encrypted values without permissions
18
- * 3. Using require/revert with encrypted conditions
19
- * 4. Encrypted computation without permission grants
20
- * 5. Leaking information through gas/timing
21
- * 6. Unauthenticated re-encryption (security critical!)
22
- * 7. Encrypted loop iterations (gas/timing leak)
23
- * 8. Too many chained operations (noise accumulation)
24
- * 9. Using deprecated FHEVM APIs
25
- *
26
- * Each anti-pattern shows both ❌ WRONG and ✅ CORRECT implementations.
19
+ * @dev Covers 9 critical anti-patterns with WRONG and ✅ CORRECT examples.
20
+ * This is an educational contract - study each pattern before building production code!
27
21
  */
28
22
  contract FHEAntiPatterns is ZamaEthereumConfig {
29
23
  euint32 private _secretBalance;
30
24
  euint32 private _threshold;
31
25
 
32
- // solhint-disable-next-line no-empty-blocks
33
- constructor() {}
34
-
35
26
  /// @notice Initialize the contract with encrypted balance and threshold values
36
27
  /// @dev Both values are encrypted and permissions are granted to the caller
37
28
  function initialize(
@@ -48,9 +39,7 @@ contract FHEAntiPatterns is ZamaEthereumConfig {
48
39
  FHE.allow(_threshold, msg.sender);
49
40
  }
50
41
 
51
- // ========================================================================
52
42
  // ANTI-PATTERN 1: Branching on encrypted values
53
- // ========================================================================
54
43
 
55
44
  /**
56
45
  * ❌ WRONG: if/else on encrypted value causes decryption
@@ -90,9 +79,7 @@ contract FHEAntiPatterns is ZamaEthereumConfig {
90
79
  FHE.allow(_secretBalance, msg.sender);
91
80
  }
92
81
 
93
- // ========================================================================
94
82
  // ANTI-PATTERN 2: View function returning encrypted without permissions
95
- // ========================================================================
96
83
 
97
84
  /**
98
85
  * @notice ❌ WRONG: Returns encrypted value but caller can't decrypt
@@ -113,9 +100,7 @@ contract FHEAntiPatterns is ZamaEthereumConfig {
113
100
  return _secretBalance;
114
101
  }
115
102
 
116
- // ========================================================================
117
103
  // ANTI-PATTERN 3: Using require/revert with encrypted conditions
118
- // ========================================================================
119
104
 
120
105
  /**
121
106
  * ❌ WRONG: Cannot use require with encrypted values
@@ -141,9 +126,7 @@ contract FHEAntiPatterns is ZamaEthereumConfig {
141
126
  return hasEnough;
142
127
  }
143
128
 
144
- // ========================================================================
145
129
  // ANTI-PATTERN 4: Encrypted computation without permission grants
146
- // ========================================================================
147
130
 
148
131
  /**
149
132
  * @notice ❌ WRONG: Compute but forget to grant permissions
@@ -166,9 +149,7 @@ contract FHEAntiPatterns is ZamaEthereumConfig {
166
149
  FHE.allow(_secretBalance, msg.sender);
167
150
  }
168
151
 
169
- // ========================================================================
170
152
  // ANTI-PATTERN 5: Leaking information through gas/timing
171
- // ========================================================================
172
153
 
173
154
  /**
174
155
  * @notice ⚠️ CAUTION: Be aware of side-channel attacks
@@ -184,9 +165,7 @@ contract FHEAntiPatterns is ZamaEthereumConfig {
184
165
  return "Be aware of gas/timing side channels";
185
166
  }
186
167
 
187
- // ========================================================================
188
168
  // ANTI-PATTERN 6: Unauthenticated Re-encryption (SECURITY CRITICAL)
189
- // ========================================================================
190
169
 
191
170
  /**
192
171
  * @notice ❌ WRONG: Re-encrypt for any provided public key
@@ -221,9 +200,7 @@ contract FHEAntiPatterns is ZamaEthereumConfig {
221
200
  "Use fhevm.js userDecrypt which handles this automatically.";
222
201
  }
223
202
 
224
- // ========================================================================
225
203
  // ANTI-PATTERN 7: Encrypted Loop Iterations (GAS/TIMING LEAK)
226
- // ========================================================================
227
204
 
228
205
  /// ❌ WRONG: Using encrypted value as loop count
229
206
  /// @dev Loop count is visible through gas consumption and timing
@@ -264,9 +241,7 @@ contract FHEAntiPatterns is ZamaEthereumConfig {
264
241
  FHE.allow(accumulator, msg.sender);
265
242
  }
266
243
 
267
- // ========================================================================
268
244
  // ANTI-PATTERN 8: Too Many Chained Operations (Noise Accumulation)
269
- // ========================================================================
270
245
 
271
246
  /// @notice ⚠️ CAUTION: FHE operations accumulate "noise"
272
247
  /// @dev Each FHE operation adds noise to the ciphertext. After too many
@@ -285,9 +260,7 @@ contract FHEAntiPatterns is ZamaEthereumConfig {
285
260
  "If you need many operations, consider batching or restructuring logic.";
286
261
  }
287
262
 
288
- // ========================================================================
289
263
  // ANTI-PATTERN 9: Using Deprecated FHEVM APIs
290
- // ========================================================================
291
264
 
292
265
  /**
293
266
  * @notice Caution about deprecated FHEVM APIs
@@ -306,9 +279,7 @@ contract FHEAntiPatterns is ZamaEthereumConfig {
306
279
  "or userDecrypt pattern via fhevm.js for user decryption.";
307
280
  }
308
281
 
309
- // ========================================================================
310
- // SUMMARY: Key Rules (Always check before deploying!)
311
- // ========================================================================
282
+ // SUMMARY: Key Rules
312
283
 
313
284
  /**
314
285
  * @notice Quick reference for FHE best practices