create-fhevm-example 1.4.2 → 1.4.4

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 (28) hide show
  1. package/contracts/advanced/BlindAuction.sol +18 -57
  2. package/contracts/advanced/EncryptedEscrow.sol +2 -30
  3. package/contracts/advanced/HiddenVoting.sol +15 -54
  4. package/contracts/advanced/PrivateKYC.sol +3 -32
  5. package/contracts/advanced/PrivatePayroll.sol +4 -34
  6. package/contracts/basic/decryption/PublicDecryptMultipleValues.sol +9 -17
  7. package/contracts/basic/decryption/PublicDecryptSingleValue.sol +9 -16
  8. package/contracts/basic/decryption/UserDecryptMultipleValues.sol +7 -21
  9. package/contracts/basic/decryption/UserDecryptSingleValue.sol +11 -30
  10. package/contracts/basic/encryption/EncryptMultipleValues.sol +12 -21
  11. package/contracts/basic/encryption/EncryptSingleValue.sol +11 -17
  12. package/contracts/basic/encryption/FHECounter.sol +15 -16
  13. package/contracts/basic/fhe-operations/FHEAdd.sol +10 -16
  14. package/contracts/basic/fhe-operations/FHEArithmetic.sol +15 -31
  15. package/contracts/basic/fhe-operations/FHEComparison.sol +8 -29
  16. package/contracts/basic/fhe-operations/FHEIfThenElse.sol +4 -9
  17. package/contracts/concepts/FHEAccessControl.sol +23 -28
  18. package/contracts/concepts/FHEAntiPatterns.sol +4 -37
  19. package/contracts/concepts/FHEHandles.sol +10 -29
  20. package/contracts/concepts/FHEInputProof.sol +9 -33
  21. package/contracts/gaming/EncryptedLottery.sol +6 -37
  22. package/contracts/gaming/EncryptedPoker.sol +3 -33
  23. package/contracts/gaming/RockPaperScissors.sol +39 -64
  24. package/contracts/openzeppelin/VestingWallet.sol +8 -17
  25. package/dist/scripts/shared/generators.d.ts +12 -0
  26. package/dist/scripts/shared/generators.d.ts.map +1 -1
  27. package/dist/scripts/shared/generators.js +126 -2
  28. package/package.json +1 -1
@@ -5,14 +5,12 @@ import {FHE, euint8} from "@fhevm/solidity/lib/FHE.sol";
5
5
  import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
6
6
 
7
7
  /**
8
- * @notice Implements a simple 8-sided Die Roll game demonstrating public, permissionless decryption
8
+ * @notice 8-sided die roll game demonstrating public decryption of multiple encrypted values.
9
9
  *
10
- * @dev Uses FHE.makePubliclyDecryptable feature to allow anyone to decrypt the game results.
11
- * Inherits from ZamaEthereumConfig to access FHE functions like FHE.randEuint8() and FHE.checkSignatures().
10
+ * @dev Uses FHE.randEuint8() + FHE.makePubliclyDecryptable() for both dice rolls.
11
+ * ⚠️ Order matters in cts[] array for checkSignatures!
12
12
  */
13
13
  contract HighestDieRoll is ZamaEthereumConfig {
14
- constructor() {}
15
-
16
14
  // Simple counter to assign a unique ID to each new game.
17
15
  uint256 private counter = 0;
18
16
 
@@ -68,7 +66,8 @@ contract HighestDieRoll is ZamaEthereumConfig {
68
66
  revealed: false
69
67
  });
70
68
 
71
- // We make the results publicly decryptable.
69
+ // 🌐 Both values marked for public decryption
70
+ // Anyone can decrypt with valid KMS proof
72
71
  FHE.makePubliclyDecryptable(playerAEncryptedDieRoll);
73
72
  FHE.makePubliclyDecryptable(playerBEncryptedDieRoll);
74
73
 
@@ -119,13 +118,7 @@ contract HighestDieRoll is ZamaEthereumConfig {
119
118
  ) public {
120
119
  require(!games[gameId].revealed, "Game already revealed");
121
120
 
122
- // 1. FHE Verification: Build the list of ciphertexts (handles) and verify the proof.
123
- // The verification checks that 'abiEncodedClearGameResult' is the true decryption
124
- // of the '(playerAEncryptedDieRoll, playerBEncryptedDieRoll)' handle pair using
125
- // the provided 'decryptionProof'.
126
-
127
- // Creating the list of handles in the right order! In this case the order does not matter since the proof
128
- // only involves 1 single handle.
121
+ // Verify KMS proof - ORDER MATTERS!
129
122
  bytes32[] memory cts = new bytes32[](2);
130
123
  cts[0] = FHE.toBytes32(games[gameId].playerAEncryptedDieRoll);
131
124
  cts[1] = FHE.toBytes32(games[gameId].playerBEncryptedDieRoll);
@@ -133,9 +126,8 @@ contract HighestDieRoll is ZamaEthereumConfig {
133
126
  // This FHE call reverts the transaction if the decryption proof is invalid.
134
127
  FHE.checkSignatures(cts, abiEncodedClearGameResult, decryptionProof);
135
128
 
136
- // 2. Decode the clear result and determine the winner's address.
137
- // In this very specific case, the function argument `abiEncodedClearGameResult` could have been replaced by two
138
- // `uint8` instead of an abi-encoded uint8 pair. In this case, we should have to compute abi.encode on-chain
129
+ // Decode both decrypted die rolls
130
+ // Note: Using abi.decode here, but could also accept two uint8 parameters
139
131
  (
140
132
  uint8 decodedClearPlayerADieRoll,
141
133
  uint8 decodedClearPlayerBDieRoll
@@ -153,7 +145,7 @@ contract HighestDieRoll is ZamaEthereumConfig {
153
145
  : address(0)
154
146
  );
155
147
 
156
- // 3. Store the revealed flag
148
+ // Store game result
157
149
  games[gameId].revealed = true;
158
150
  games[gameId].winner = winner;
159
151
  }
@@ -5,14 +5,12 @@ import {FHE, ebool} from "@fhevm/solidity/lib/FHE.sol";
5
5
  import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
6
6
 
7
7
  /**
8
- * @notice Implements a simple Heads or Tails game demonstrating public, permissionless decryption
8
+ * @notice Simple Heads or Tails game demonstrating public, permissionless decryption with FHE.
9
9
  *
10
- * @dev Uses FHE.makePubliclyDecryptable feature to allow anyone to decrypt the game results.
11
- * Inherits from ZamaEthereumConfig to access FHE functions like FHE.randEbool() and FHE.checkSignatures().
10
+ * @dev Uses FHE.randEbool() for random result + FHE.makePubliclyDecryptable() for revealing.
11
+ * Anyone can decrypt results with valid KMS proof.
12
12
  */
13
13
  contract HeadsOrTails is ZamaEthereumConfig {
14
- constructor() {}
15
-
16
14
  /// Simple counter to assign a unique ID to each new game.
17
15
  uint256 private counter = 0;
18
16
 
@@ -66,7 +64,8 @@ contract HeadsOrTails is ZamaEthereumConfig {
66
64
  winner: address(0)
67
65
  });
68
66
 
69
- // We make the result publicly decryptable.
67
+ // 🌐 Why makePubliclyDecryptable? Allows ANYONE to decrypt (not just allowed users)
68
+ // Use case: Public game results, lottery winners, voting tallies
70
69
  FHE.makePubliclyDecryptable(headsOrTailsResult);
71
70
 
72
71
  // You can catch the event to get the gameId and the encryptedHasHeadsWon handle
@@ -113,21 +112,15 @@ contract HeadsOrTails is ZamaEthereumConfig {
113
112
  "Game winner already revealed"
114
113
  );
115
114
 
116
- // 1. FHE Verification: Build the list of ciphertexts (handles) and verify the proof.
117
- // The verification checks that 'abiEncodedClearGameResult' is the true decryption
118
- // of the 'encryptedHasHeadsWon' handle using the provided 'decryptionProof'.
119
-
120
- // Creating the list of handles in the right order! In this case the order does not matter since the proof
121
- // only involves 1 single handle.
115
+ // Verify KMS decryption proof
122
116
  bytes32[] memory cts = new bytes32[](1);
123
117
  cts[0] = FHE.toBytes32(games[gameId].encryptedHasHeadsWon);
124
118
 
125
119
  // This FHE call reverts the transaction if the decryption proof is invalid.
126
120
  FHE.checkSignatures(cts, abiEncodedClearGameResult, decryptionProof);
127
121
 
128
- // 2. Decode the clear result and determine the winner's address.
129
- // In this very specific case, the function argument `abiEncodedClearGameResult` could have been a simple
130
- // `bool` instead of an abi-encoded bool. In this case, we should have compute abi.encode on-chain
122
+ // Decode the decrypted result to determine winner
123
+ // Note: Using abi.decode here, but could also accept plain bool parameter
131
124
  bool decodedClearGameResult = abi.decode(
132
125
  abiEncodedClearGameResult,
133
126
  (bool)
@@ -136,7 +129,7 @@ contract HeadsOrTails is ZamaEthereumConfig {
136
129
  ? games[gameId].headsPlayer
137
130
  : games[gameId].tailsPlayer;
138
131
 
139
- // 3. Store the winner
132
+ // Store the winner
140
133
  games[gameId].winner = winner;
141
134
  }
142
135
  }
@@ -5,33 +5,25 @@ import {FHE, ebool, euint32, euint64} from "@fhevm/solidity/lib/FHE.sol";
5
5
  import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
6
6
 
7
7
  /**
8
- * @notice Demonstrates user decryption of multiple encrypted values
8
+ * @notice Demonstrates user decryption of multiple encrypted values with different types.
9
9
  *
10
- * @dev Shows how to create encrypted values from plaintext and grant permissions
11
- * for multiple values of different types (ebool, euint32, euint64).
10
+ * @dev Each value needs separate permission grants (no batching).
11
+ * ⚠️ Cannot batch permission grants - must call allow() for each value!
12
12
  */
13
13
  contract UserDecryptMultipleValues is ZamaEthereumConfig {
14
- // 🔐 Multiple encrypted values of different types
15
14
  ebool private _encryptedBool;
16
15
  euint32 private _encryptedUint32;
17
16
  euint64 private _encryptedUint64;
18
17
 
19
- constructor() {}
20
-
21
- /// @notice Initialize multiple encrypted values from plaintext
22
- /// @dev Uses FHE.asEuintX() to create encrypted constants from plaintext
23
- /// (The plaintext IS visible on-chain, but result is encrypted)
18
+ /// @notice Initialize multiple values from plaintext
19
+ /// @dev FHE.asEuintX() creates encrypted constants from plaintext
24
20
  function initialize(bool a, uint32 b, uint64 c) external {
25
- // Create encrypted values from plaintext constants
26
- // FHE.asEbool(a) encrypts a boolean value
21
+ // Create encrypted values from plaintext
27
22
  _encryptedBool = FHE.xor(FHE.asEbool(a), FHE.asEbool(false));
28
-
29
- // FHE.asEuint32(b) + 1 creates an encrypted (b + 1)
30
23
  _encryptedUint32 = FHE.add(FHE.asEuint32(b), FHE.asEuint32(1));
31
24
  _encryptedUint64 = FHE.add(FHE.asEuint64(c), FHE.asEuint64(1));
32
25
 
33
- // ⚠️ CRITICAL: Grant permissions for EACH value separately
34
- // You cannot batch permission grants!
26
+ // ⚠️ Why separate? No batching for permissions - each needs individual allow()
35
27
  FHE.allowThis(_encryptedBool);
36
28
  FHE.allowThis(_encryptedUint32);
37
29
  FHE.allowThis(_encryptedUint64);
@@ -41,20 +33,14 @@ contract UserDecryptMultipleValues is ZamaEthereumConfig {
41
33
  FHE.allow(_encryptedUint64, msg.sender);
42
34
  }
43
35
 
44
- /// @notice Returns the encrypted boolean value
45
- /// @dev Client decrypts with: fhevm.userDecrypt(FhevmType.ebool, handle, ...)
46
36
  function encryptedBool() public view returns (ebool) {
47
37
  return _encryptedBool;
48
38
  }
49
39
 
50
- /// @notice Returns the encrypted uint32 value
51
- /// @dev Client decrypts with: fhevm.userDecrypt(FhevmType.euint32, handle, ...)
52
40
  function encryptedUint32() public view returns (euint32) {
53
41
  return _encryptedUint32;
54
42
  }
55
43
 
56
- /// @notice Returns the encrypted uint64 value
57
- /// @dev Client decrypts with: fhevm.userDecrypt(FhevmType.euint64, handle, ...)
58
44
  function encryptedUint64() public view returns (euint64) {
59
45
  return _encryptedUint64;
60
46
  }
@@ -5,54 +5,35 @@ import {FHE, euint32} from "@fhevm/solidity/lib/FHE.sol";
5
5
  import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
6
6
 
7
7
  /**
8
- * @notice Demonstrates the FHE decryption mechanism and highlights common pitfalls
8
+ * @notice Demonstrates FHE decryption mechanism and highlights the critical permission pattern.
9
9
  *
10
- * @dev This trivial example shows correct vs incorrect permission granting patterns.
11
- * Emphasizes that BOTH FHE.allowThis() and FHE.allow(user) are required for user decryption.
10
+ * @dev Shows CORRECT vs INCORRECT permission granting.
11
+ * ⚠️ Both allowThis + allow required for user decryption!
12
12
  */
13
13
  contract UserDecryptSingleValue is ZamaEthereumConfig {
14
14
  euint32 private _trivialEuint32;
15
15
 
16
- // solhint-disable-next-line no-empty-blocks
17
- constructor() {}
18
-
19
- /// @notice Initialize with a trivial formula (value + 1)
20
- /// @dev Demonstrates correct permission granting
16
+ /// @notice ✅ CORRECT: Proper permission pattern
21
17
  function initializeUint32(uint32 value) external {
22
- // Compute a trivial FHE formula _trivialEuint32 = value + 1
23
18
  _trivialEuint32 = FHE.add(FHE.asEuint32(value), FHE.asEuint32(1));
24
19
 
25
- // Grant FHE permissions to:
26
- // The contract caller (`msg.sender`): allows them to decrypt `_trivialEuint32`.
27
- // The contract itself (`address(this)`): allows it to operate on `_trivialEuint32` and
28
- // also enables the caller to perform user decryption.
29
- //
30
- // Note: If you forget to call `FHE.allowThis(_trivialEuint32)`, the user will NOT be able
31
- // to user decrypt the value! Both the contract and the caller must have FHE permissions
32
- // for user decryption to succeed.
20
+ // 🔑 Why both needed?
21
+ // - allowThis: Contract authorizes releasing the value
22
+ // - allow: User can request decryption
33
23
  FHE.allowThis(_trivialEuint32);
34
24
  FHE.allow(_trivialEuint32, msg.sender);
35
25
  }
36
26
 
37
- /// @notice Demonstrate INCORRECT permission granting (Anti-pattern)
38
- /// @dev Missing FHE.allowThis() causes user decryption to fail
27
+ /// @notice WRONG: Missing allowThis causes decryption to FAIL!
28
+ /// @dev Common mistake - user gets permission but decryption still fails
39
29
  function initializeUint32Wrong(uint32 value) external {
40
- // Compute a trivial FHE formula _trivialEuint32 = value + 1
41
30
  _trivialEuint32 = FHE.add(FHE.asEuint32(value), FHE.asEuint32(1));
42
31
 
43
- // ❌ Common FHE permission mistake:
44
- // ================================================================
45
- // We grant FHE permissions to the contract caller (`msg.sender`),
46
- // expecting they will be able to user decrypt the encrypted value later.
47
- //
48
- // However, this will fail! 💥
49
- // The contract itself (`address(this)`) also needs FHE permissions to allow user decryption.
50
- // Without granting the contract access using `FHE.allowThis(...)`,
51
- // the user decryption attempt by the user will not succeed.
32
+ // ❌ Missing allowThis user can't decrypt!
33
+ // Why? Decryption needs contract authorization to release
52
34
  FHE.allow(_trivialEuint32, msg.sender);
53
35
  }
54
36
 
55
- /// @notice Returns the encrypted uint32 value
56
37
  function encryptedUint32() public view returns (euint32) {
57
38
  return _trivialEuint32;
58
39
  }
@@ -15,39 +15,30 @@ import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
15
15
  /**
16
16
  * @notice Encrypting and handling multiple values in a single transaction efficiently.
17
17
  *
18
- * @dev Supported encrypted types:
19
- * - euint8/16/32/64/128/256: encrypted integers
20
- * - ebool: encrypted boolean
21
- * - eaddress: encrypted address
22
- * - ebytes64/128/256: encrypted bytes
18
+ * @dev Demonstrates batched input (ONE proof for multiple values).
19
+ * Gas: Batching saves ~50k gas vs separate proofs!
23
20
  */
24
21
  contract EncryptMultipleValues is ZamaEthereumConfig {
25
- // 🔐 Three different encrypted types stored together
26
- ebool private _encryptedEbool; // e.g., vote, permission flag
27
- euint32 private _encryptedEuint32; // e.g., amount, balance
28
- eaddress private _encryptedEaddress; // e.g., hidden recipient
22
+ ebool private _encryptedEbool;
23
+ euint32 private _encryptedEuint32;
24
+ eaddress private _encryptedEaddress;
29
25
 
30
- constructor() {}
31
-
32
- /// @notice Store multiple encrypted values from a single batched input
33
- /// @dev Client-side batching example:
34
- /// const input = await fhevm.createEncryptedInput(contractAddr, userAddr)
35
- /// .addBool(true).add32(123).addAddress(addr).encrypt();
36
- /// // This creates ONE proof for ALL values - more gas efficient!
26
+ /// @notice Store multiple encrypted values from single batched input
27
+ /// @dev Client creates ONE proof for ALL values using createEncryptedInput().
28
+ /// Much cheaper than separate encrypt() calls!
37
29
  function initialize(
38
30
  externalEbool inputEbool,
39
31
  externalEuint32 inputEuint32,
40
32
  externalEaddress inputEaddress,
41
- bytes calldata inputProof // Single proof covers all values
33
+ bytes calldata inputProof // Single proof covers all!
42
34
  ) external {
43
- // Convert each external input to internal handle
35
+ // 💡 Why one proof? Client batches all values before encrypt()
36
+ // Saves ~50k gas per additional value!
44
37
  _encryptedEbool = FHE.fromExternal(inputEbool, inputProof);
45
38
  _encryptedEuint32 = FHE.fromExternal(inputEuint32, inputProof);
46
39
  _encryptedEaddress = FHE.fromExternal(inputEaddress, inputProof);
47
40
 
48
- // ⚠️ IMPORTANT: Each value needs its own permission grants!
49
- // You cannot batch permission grants
50
-
41
+ // 🔐 Each value needs own permissions (no batching here)
51
42
  FHE.allowThis(_encryptedEbool);
52
43
  FHE.allow(_encryptedEbool, msg.sender);
53
44
 
@@ -5,39 +5,33 @@ import {FHE, externalEuint32, euint32} from "@fhevm/solidity/lib/FHE.sol";
5
5
  import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
6
6
 
7
7
  /**
8
- * @notice FHE encryption mechanism with single values, including common pitfalls and best practices for developers.
9
- *
8
+ * @notice FHE encryption mechanism with single values, including common pitfalls and best practices.
9
+
10
10
  * @dev Shows the complete flow: receiving encrypted input from user, validating proof,
11
11
  * storing the encrypted value, and granting permissions for decryption.
12
12
  */
13
13
  contract EncryptSingleValue is ZamaEthereumConfig {
14
- // 🔐 Stored encrypted - only authorized users can decrypt
15
14
  euint32 private _encryptedEuint32;
16
15
 
17
- constructor() {}
18
-
19
16
  /// @notice Store an encrypted value submitted by the user
20
- /// @dev inputEuint32: Encrypted value (created client-side with fhevm.createEncryptedInput())
21
- /// inputProof: Zero-knowledge proof that the encryption is valid
17
+ /// @dev inputProof ensures: value encrypted for THIS contract + THIS user.
18
+ /// Prevents replay attacks from other contracts/users.
22
19
  function initialize(
23
20
  externalEuint32 inputEuint32,
24
21
  bytes calldata inputProof
25
22
  ) external {
26
- // Convert external input to internal handle
27
- // 📋 The proof ensures:
28
- // - Value was encrypted for THIS contract address
29
- // - Value was encrypted by THIS user (msg.sender)
30
- // - Prevents replay attacks from other contracts/users
23
+ // 🔐 Why proof? Prevents: replay attacks, wrong contract, invalid ciphertext
31
24
  _encryptedEuint32 = FHE.fromExternal(inputEuint32, inputProof);
32
25
 
33
- // ⚠️ CRITICAL: Grant permissions for future decryption
34
- // Without BOTH of these, user decryption will fail!
35
- FHE.allowThis(_encryptedEuint32); // Contract can operate on it
36
- FHE.allow(_encryptedEuint32, msg.sender); // User can decrypt it
26
+ // 🔑 Why both?
27
+ // - allowThis: Contract can store/compute with it
28
+ // - allow(user): User can decrypt it
29
+ FHE.allowThis(_encryptedEuint32);
30
+ FHE.allow(_encryptedEuint32, msg.sender);
37
31
  }
38
32
 
39
33
  /// @notice Returns the encrypted handle (not the actual value!)
40
- /// @dev To decrypt, use fhevm.userDecryptEuint() on the client side
34
+ /// @dev To decrypt, use fhevm.userDecryptEuint32() on client side
41
35
  function encryptedUint32() public view returns (euint32) {
42
36
  return _encryptedEuint32;
43
37
  }
@@ -6,46 +6,45 @@ import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
6
6
 
7
7
  /**
8
8
  * @notice Confidential counter implementation using FHEVM, compared with a standard counter to highlight encryption benefits.
9
- *
10
- * @dev Demonstrates basic FHE operations: encryption, computation, and permission management.
11
- * Shows how to work with encrypted values without ever revealing the underlying data.
9
+
10
+ * @dev Demonstrates basic FHE workflow: fromExternal() compute allow permissions.
11
+ * All arithmetic happens on encrypted values without revealing the count.
12
12
  */
13
13
  contract FHECounter is ZamaEthereumConfig {
14
- // 🔐 The count is always encrypted - no one can see the actual value
15
14
  euint32 private _count;
16
15
 
17
- /// @notice Returns the encrypted count handle (not the actual value!)
18
16
  function getCount() external view returns (euint32) {
19
17
  return _count;
20
18
  }
21
19
 
22
- /// @notice Increments the counter by an encrypted value
20
+ /// @notice Increments counter by encrypted amount
21
+ /// @dev Why allowThis + allow? Contract needs permission to store,
22
+ /// user needs permission to decrypt. Both required!
23
23
  function increment(
24
24
  externalEuint32 inputEuint32,
25
25
  bytes calldata inputProof
26
26
  ) external {
27
- // Convert external encrypted input to internal handle
28
- // The proof is verified automatically
27
+ // 🔐 Why proof? Ensures valid ciphertext encrypted for THIS contract
29
28
  euint32 encryptedValue = FHE.fromExternal(inputEuint32, inputProof);
30
29
 
31
- // Add encrypted values - computation happens on ciphertexts
32
- // Neither the contract nor anyone else sees the actual numbers
30
+ // 🧮 Homomorphic add: works on encrypted data
33
31
  _count = FHE.add(_count, encryptedValue);
34
32
 
35
- // ⚠️ CRITICAL: Both permissions required for user decryption!
36
- FHE.allowThis(_count); // Contract can continue using this value
37
- FHE.allow(_count, msg.sender); // Caller can decrypt it
33
+ // 🔑 Both needed: allowThis = contract stores, allow = user decrypts
34
+ FHE.allowThis(_count);
35
+ FHE.allow(_count, msg.sender);
38
36
  }
39
37
 
40
- /// @notice Decrements the counter by an encrypted value
38
+ /// @notice Decrements counter by encrypted amount
39
+ /// @dev ⚠️ No underflow protection! FHE.sub wraps around at 0.
40
+ /// ❌ WRONG: Checking result < 0 reveals information
41
+ /// ✅ CORRECT: Use application-level balance tracking or FHE.select()
41
42
  function decrement(
42
43
  externalEuint32 inputEuint32,
43
44
  bytes calldata inputProof
44
45
  ) external {
45
46
  euint32 encryptedValue = FHE.fromExternal(inputEuint32, inputProof);
46
47
 
47
- // Subtract encrypted values
48
- // ⚠️ No underflow protection here - add checks in production!
49
48
  _count = FHE.sub(_count, encryptedValue);
50
49
 
51
50
  FHE.allowThis(_count);
@@ -5,21 +5,19 @@ 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 Simple example demonstrating addition of two encrypted values (a + b)
9
+
10
+ * @dev Shows the most basic FHE operation and permission flow.
11
+ * ⚡ Gas: FHE.add() costs ~100k gas (coprocessor call)
11
12
  */
12
13
  contract FHEAdd is ZamaEthereumConfig {
13
14
  euint8 private _a;
14
15
  euint8 private _b;
15
16
  euint8 private _result;
16
17
 
17
- constructor() {}
18
-
19
18
  /// @notice Set the first operand (encrypted)
20
19
  function setA(externalEuint8 inputA, bytes calldata inputProof) external {
21
20
  _a = FHE.fromExternal(inputA, inputProof);
22
- // Only contract needs permission to use this for computation
23
21
  FHE.allowThis(_a);
24
22
  }
25
23
 
@@ -30,21 +28,17 @@ contract FHEAdd is ZamaEthereumConfig {
30
28
  }
31
29
 
32
30
  /// @notice Compute a + b on encrypted values
33
- /// @dev The contract computes on ciphertexts - it never sees actual values!
31
+ /// @dev Contract operates on ciphertexts - never sees actual values!
34
32
  function computeAPlusB() external {
35
- // 🔐 Addition on encrypted values
36
- // Neither the contract nor anyone else knows what a, b, or result are
33
+ // 🧮 Homomorphic: operates on encrypted data without decrypting
37
34
  _result = FHE.add(_a, _b);
38
35
 
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
36
+ // 🔑 Why both? allowThis = contract can use it, allow = user can decrypt
37
+ FHE.allowThis(_result);
38
+ FHE.allow(_result, msg.sender);
45
39
  }
46
40
 
47
- /// @notice Returns the encrypted result
41
+ /// @notice Returns the encrypted result handle
48
42
  function result() public view returns (euint8) {
49
43
  return _result;
50
44
  }
@@ -5,93 +5,77 @@ 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 Demonstrates all FHE arithmetic operations: add, sub, mul, div, rem, min, max
9
9
  *
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
10
+ * @dev Gas costs vary: add/sub (~100k) < mul (~150k) < div/rem (~300k)
11
+ * ⚠️ div/rem only work with plaintext divisor (not encrypted!)
21
12
  */
22
13
  contract FHEArithmetic is ZamaEthereumConfig {
23
14
  euint32 private _a;
24
15
  euint32 private _b;
25
16
  euint32 private _result;
26
17
 
27
- // solhint-disable-next-line no-empty-blocks
28
- constructor() {}
29
-
30
- /// @notice Sets the first operand (encrypted)
31
18
  function setA(externalEuint32 inputA, bytes calldata inputProof) external {
32
19
  _a = FHE.fromExternal(inputA, inputProof);
33
20
  FHE.allowThis(_a);
34
21
  }
35
22
 
36
- /// @notice Sets the second operand (encrypted)
37
23
  function setB(externalEuint32 inputB, bytes calldata inputProof) external {
38
24
  _b = FHE.fromExternal(inputB, inputProof);
39
25
  FHE.allowThis(_b);
40
26
  }
41
27
 
42
- /// @notice Computes encrypted addition: result = a + b
28
+ /// @notice Encrypted addition: result = a + b
43
29
  function computeAdd() external {
44
30
  _result = FHE.add(_a, _b);
45
31
  _grantPermissions();
46
32
  }
47
33
 
48
- /// @notice Computes encrypted subtraction: result = a - b
49
- /// @dev No underflow protection - in production, add range checks
34
+ /// @notice Encrypted subtraction: result = a - b
35
+ /// @dev ⚠️ No underflow protection! Wraps around at 0.
50
36
  function computeSub() external {
51
37
  _result = FHE.sub(_a, _b);
52
38
  _grantPermissions();
53
39
  }
54
40
 
55
- /// @notice Computes encrypted multiplication: result = a * b
56
- /// @dev No overflow protection - in production, add range checks
41
+ /// @notice Encrypted multiplication: result = a * b
42
+ /// @dev ⚠️ No overflow protection! May wrap at type max.
57
43
  function computeMul() external {
58
44
  _result = FHE.mul(_a, _b);
59
45
  _grantPermissions();
60
46
  }
61
47
 
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.
48
+ /// @notice Encrypted division: result = a / divisor
49
+ /// @dev WRONG: FHE.div(encryptedA, encryptedB) - not supported!
50
+ /// ✅ CORRECT: FHE.div(encryptedA, plaintextB)
64
51
  function computeDiv(uint32 divisor) external {
65
52
  _result = FHE.div(_a, divisor);
66
53
  _grantPermissions();
67
54
  }
68
55
 
69
- /// @notice Computes encrypted remainder: result = a % b (scalar)
70
- /// @dev Divisor must be a scalar (plaintext).
56
+ /// @notice Encrypted remainder: result = a % modulus
57
+ /// @dev Modulus must be plaintext (same limitation as div)
71
58
  function computeRem(uint32 modulus) external {
72
59
  _result = FHE.rem(_a, modulus);
73
60
  _grantPermissions();
74
61
  }
75
62
 
76
- /// @notice Computes encrypted minimum: result = min(a, b)
63
+ /// @notice Encrypted minimum: result = min(a, b)
77
64
  function computeMin() external {
78
65
  _result = FHE.min(_a, _b);
79
66
  _grantPermissions();
80
67
  }
81
68
 
82
- /// @notice Computes encrypted maximum: result = max(a, b)
69
+ /// @notice Encrypted maximum: result = max(a, b)
83
70
  function computeMax() external {
84
71
  _result = FHE.max(_a, _b);
85
72
  _grantPermissions();
86
73
  }
87
74
 
88
- /// @notice Returns the encrypted result
89
- /// @dev Caller must have FHE permissions to decrypt
90
75
  function getResult() public view returns (euint32) {
91
76
  return _result;
92
77
  }
93
78
 
94
- /// @dev Grants FHE permissions to contract and caller for decryption
95
79
  function _grantPermissions() internal {
96
80
  FHE.allowThis(_result);
97
81
  FHE.allow(_result, msg.sender);