create-fhevm-example 1.3.1 → 1.4.0

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 (122) hide show
  1. package/contracts/advanced/BlindAuction.sol +255 -0
  2. package/contracts/advanced/EncryptedEscrow.sol +315 -0
  3. package/contracts/advanced/HiddenVoting.sol +231 -0
  4. package/contracts/advanced/PrivateKYC.sol +309 -0
  5. package/contracts/advanced/PrivatePayroll.sol +285 -0
  6. package/contracts/basic/decryption/PublicDecryptMultipleValues.sol +160 -0
  7. package/contracts/basic/decryption/PublicDecryptSingleValue.sol +142 -0
  8. package/contracts/basic/decryption/UserDecryptMultipleValues.sol +61 -0
  9. package/contracts/basic/decryption/UserDecryptSingleValue.sol +59 -0
  10. package/contracts/basic/encryption/EncryptMultipleValues.sol +72 -0
  11. package/contracts/basic/encryption/EncryptSingleValue.sol +44 -0
  12. package/contracts/basic/encryption/FHECounter.sol +54 -0
  13. package/contracts/basic/fhe-operations/FHEAdd.sol +51 -0
  14. package/contracts/basic/fhe-operations/FHEArithmetic.sol +99 -0
  15. package/contracts/basic/fhe-operations/FHEComparison.sol +116 -0
  16. package/contracts/basic/fhe-operations/FHEIfThenElse.sol +53 -0
  17. package/contracts/concepts/FHEAccessControl.sol +94 -0
  18. package/contracts/concepts/FHEAntiPatterns.sol +329 -0
  19. package/contracts/concepts/FHEHandles.sol +128 -0
  20. package/contracts/concepts/FHEInputProof.sol +104 -0
  21. package/contracts/gaming/EncryptedLottery.sol +298 -0
  22. package/contracts/gaming/EncryptedPoker.sol +337 -0
  23. package/contracts/gaming/RockPaperScissors.sol +213 -0
  24. package/contracts/openzeppelin/ERC7984.sol +85 -0
  25. package/contracts/openzeppelin/ERC7984ERC20Wrapper.sol +43 -0
  26. package/contracts/openzeppelin/SwapERC7984ToERC20.sol +110 -0
  27. package/contracts/openzeppelin/SwapERC7984ToERC7984.sol +48 -0
  28. package/contracts/openzeppelin/VestingWallet.sol +147 -0
  29. package/contracts/openzeppelin/mocks/ERC20Mock.sol +31 -0
  30. package/dist/scripts/commands/add-mode.d.ts.map +1 -0
  31. package/dist/scripts/{add-mode.js → commands/add-mode.js} +27 -61
  32. package/dist/scripts/commands/doctor.d.ts.map +1 -0
  33. package/dist/scripts/{doctor.js → commands/doctor.js} +2 -2
  34. package/dist/scripts/commands/generate-config.d.ts.map +1 -0
  35. package/dist/scripts/{generate-config.js → commands/generate-config.js} +3 -10
  36. package/dist/scripts/commands/generate-docs.d.ts.map +1 -0
  37. package/dist/scripts/{generate-docs.js → commands/generate-docs.js} +4 -3
  38. package/dist/scripts/commands/maintenance.d.ts.map +1 -0
  39. package/dist/scripts/{maintenance.js → commands/maintenance.js} +11 -10
  40. package/dist/scripts/index.js +63 -59
  41. package/dist/scripts/{builders.d.ts → shared/builders.d.ts} +2 -2
  42. package/dist/scripts/shared/builders.d.ts.map +1 -0
  43. package/dist/scripts/{builders.js → shared/builders.js} +49 -30
  44. package/dist/scripts/{config.d.ts → shared/config.d.ts} +0 -2
  45. package/dist/scripts/shared/config.d.ts.map +1 -0
  46. package/dist/scripts/{config.js → shared/config.js} +48 -59
  47. package/dist/scripts/shared/generators.d.ts +42 -0
  48. package/dist/scripts/shared/generators.d.ts.map +1 -0
  49. package/dist/scripts/{utils.js → shared/generators.js} +34 -271
  50. package/dist/scripts/shared/ui.d.ts.map +1 -0
  51. package/dist/scripts/{ui.js → shared/ui.js} +3 -2
  52. package/dist/scripts/{utils.d.ts → shared/utils.d.ts} +4 -27
  53. package/dist/scripts/shared/utils.d.ts.map +1 -0
  54. package/dist/scripts/shared/utils.js +228 -0
  55. package/fhevm-hardhat-template/.eslintignore +26 -0
  56. package/fhevm-hardhat-template/.eslintrc.yml +21 -0
  57. package/fhevm-hardhat-template/.github/workflows/main.yml +47 -0
  58. package/fhevm-hardhat-template/.github/workflows/manual-windows.yml +28 -0
  59. package/fhevm-hardhat-template/.github/workflows/manual.yml +28 -0
  60. package/fhevm-hardhat-template/.prettierignore +25 -0
  61. package/fhevm-hardhat-template/.prettierrc.yml +15 -0
  62. package/fhevm-hardhat-template/.solcover.js +4 -0
  63. package/fhevm-hardhat-template/.solhint.json +12 -0
  64. package/fhevm-hardhat-template/.solhintignore +3 -0
  65. package/fhevm-hardhat-template/.vscode/extensions.json +3 -0
  66. package/fhevm-hardhat-template/.vscode/settings.json +9 -0
  67. package/fhevm-hardhat-template/LICENSE +33 -0
  68. package/fhevm-hardhat-template/README.md +110 -0
  69. package/fhevm-hardhat-template/contracts/FHECounter.sol +46 -0
  70. package/fhevm-hardhat-template/deploy/deploy.ts +17 -0
  71. package/fhevm-hardhat-template/hardhat.config.ts +90 -0
  72. package/fhevm-hardhat-template/package-lock.json +10405 -0
  73. package/fhevm-hardhat-template/package.json +104 -0
  74. package/fhevm-hardhat-template/tasks/FHECounter.ts +184 -0
  75. package/fhevm-hardhat-template/tasks/accounts.ts +9 -0
  76. package/fhevm-hardhat-template/test/FHECounter.ts +104 -0
  77. package/fhevm-hardhat-template/test/FHECounterSepolia.ts +104 -0
  78. package/fhevm-hardhat-template/tsconfig.json +23 -0
  79. package/package.json +13 -10
  80. package/test/advanced/BlindAuction.ts +246 -0
  81. package/test/advanced/EncryptedEscrow.ts +295 -0
  82. package/test/advanced/HiddenVoting.ts +268 -0
  83. package/test/advanced/PrivateKYC.ts +382 -0
  84. package/test/advanced/PrivatePayroll.ts +253 -0
  85. package/test/basic/decryption/PublicDecryptMultipleValues.ts +254 -0
  86. package/test/basic/decryption/PublicDecryptSingleValue.ts +264 -0
  87. package/test/basic/decryption/UserDecryptMultipleValues.ts +107 -0
  88. package/test/basic/decryption/UserDecryptSingleValue.ts +97 -0
  89. package/test/basic/encryption/EncryptMultipleValues.ts +110 -0
  90. package/test/basic/encryption/EncryptSingleValue.ts +124 -0
  91. package/test/basic/encryption/FHECounter.ts +112 -0
  92. package/test/basic/fhe-operations/FHEAdd.ts +97 -0
  93. package/test/basic/fhe-operations/FHEArithmetic.ts +161 -0
  94. package/test/basic/fhe-operations/FHEComparison.ts +167 -0
  95. package/test/basic/fhe-operations/FHEIfThenElse.ts +97 -0
  96. package/test/concepts/FHEAccessControl.ts +154 -0
  97. package/test/concepts/FHEAntiPatterns.ts +111 -0
  98. package/test/concepts/FHEHandles.ts +156 -0
  99. package/test/concepts/FHEInputProof.ts +151 -0
  100. package/test/gaming/EncryptedLottery.ts +214 -0
  101. package/test/gaming/EncryptedPoker.ts +349 -0
  102. package/test/gaming/RockPaperScissors.ts +205 -0
  103. package/test/openzeppelin/ERC7984.ts +142 -0
  104. package/test/openzeppelin/ERC7984ERC20Wrapper.ts +71 -0
  105. package/test/openzeppelin/SwapERC7984ToERC20.ts +76 -0
  106. package/test/openzeppelin/SwapERC7984ToERC7984.ts +113 -0
  107. package/test/openzeppelin/VestingWallet.ts +89 -0
  108. package/dist/scripts/add-mode.d.ts.map +0 -1
  109. package/dist/scripts/builders.d.ts.map +0 -1
  110. package/dist/scripts/config.d.ts.map +0 -1
  111. package/dist/scripts/doctor.d.ts.map +0 -1
  112. package/dist/scripts/generate-config.d.ts.map +0 -1
  113. package/dist/scripts/generate-docs.d.ts.map +0 -1
  114. package/dist/scripts/maintenance.d.ts.map +0 -1
  115. package/dist/scripts/ui.d.ts.map +0 -1
  116. package/dist/scripts/utils.d.ts.map +0 -1
  117. /package/dist/scripts/{add-mode.d.ts → commands/add-mode.d.ts} +0 -0
  118. /package/dist/scripts/{doctor.d.ts → commands/doctor.d.ts} +0 -0
  119. /package/dist/scripts/{generate-config.d.ts → commands/generate-config.d.ts} +0 -0
  120. /package/dist/scripts/{generate-docs.d.ts → commands/generate-docs.d.ts} +0 -0
  121. /package/dist/scripts/{maintenance.d.ts → commands/maintenance.d.ts} +0 -0
  122. /package/dist/scripts/{ui.d.ts → shared/ui.d.ts} +0 -0
@@ -0,0 +1,59 @@
1
+ // SPDX-License-Identifier: BSD-3-Clause-Clear
2
+ pragma solidity ^0.8.24;
3
+
4
+ import {FHE, euint32} from "@fhevm/solidity/lib/FHE.sol";
5
+ import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
6
+
7
+ /**
8
+ * @notice Demonstrates the FHE decryption mechanism and highlights common pitfalls
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.
12
+ */
13
+ contract UserDecryptSingleValue is ZamaEthereumConfig {
14
+ euint32 private _trivialEuint32;
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
21
+ function initializeUint32(uint32 value) external {
22
+ // Compute a trivial FHE formula _trivialEuint32 = value + 1
23
+ _trivialEuint32 = FHE.add(FHE.asEuint32(value), FHE.asEuint32(1));
24
+
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.
33
+ FHE.allowThis(_trivialEuint32);
34
+ FHE.allow(_trivialEuint32, msg.sender);
35
+ }
36
+
37
+ /// @notice Demonstrate INCORRECT permission granting (Anti-pattern)
38
+ /// @dev Missing FHE.allowThis() causes user decryption to fail
39
+ function initializeUint32Wrong(uint32 value) external {
40
+ // Compute a trivial FHE formula _trivialEuint32 = value + 1
41
+ _trivialEuint32 = FHE.add(FHE.asEuint32(value), FHE.asEuint32(1));
42
+
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.
52
+ FHE.allow(_trivialEuint32, msg.sender);
53
+ }
54
+
55
+ /// @notice Returns the encrypted uint32 value
56
+ function encryptedUint32() public view returns (euint32) {
57
+ return _trivialEuint32;
58
+ }
59
+ }
@@ -0,0 +1,72 @@
1
+ // SPDX-License-Identifier: BSD-3-Clause-Clear
2
+ pragma solidity ^0.8.24;
3
+
4
+ import {
5
+ FHE,
6
+ externalEbool,
7
+ externalEuint32,
8
+ externalEaddress,
9
+ ebool,
10
+ euint32,
11
+ eaddress
12
+ } from "@fhevm/solidity/lib/FHE.sol";
13
+ import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
14
+
15
+ /**
16
+ * @notice Encrypting and handling multiple values in a single transaction efficiently.
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
23
+ */
24
+ 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
29
+
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!
37
+ function initialize(
38
+ externalEbool inputEbool,
39
+ externalEuint32 inputEuint32,
40
+ externalEaddress inputEaddress,
41
+ bytes calldata inputProof // Single proof covers all values
42
+ ) external {
43
+ // Convert each external input to internal handle
44
+ _encryptedEbool = FHE.fromExternal(inputEbool, inputProof);
45
+ _encryptedEuint32 = FHE.fromExternal(inputEuint32, inputProof);
46
+ _encryptedEaddress = FHE.fromExternal(inputEaddress, inputProof);
47
+
48
+ // ⚠️ IMPORTANT: Each value needs its own permission grants!
49
+ // You cannot batch permission grants
50
+
51
+ FHE.allowThis(_encryptedEbool);
52
+ FHE.allow(_encryptedEbool, msg.sender);
53
+
54
+ FHE.allowThis(_encryptedEuint32);
55
+ FHE.allow(_encryptedEuint32, msg.sender);
56
+
57
+ FHE.allowThis(_encryptedEaddress);
58
+ FHE.allow(_encryptedEaddress, msg.sender);
59
+ }
60
+
61
+ function encryptedBool() public view returns (ebool) {
62
+ return _encryptedEbool;
63
+ }
64
+
65
+ function encryptedUint32() public view returns (euint32) {
66
+ return _encryptedEuint32;
67
+ }
68
+
69
+ function encryptedAddress() public view returns (eaddress) {
70
+ return _encryptedEaddress;
71
+ }
72
+ }
@@ -0,0 +1,44 @@
1
+ // SPDX-License-Identifier: BSD-3-Clause-Clear
2
+ pragma solidity ^0.8.24;
3
+
4
+ import {FHE, externalEuint32, euint32} from "@fhevm/solidity/lib/FHE.sol";
5
+ import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
6
+
7
+ /**
8
+ * @notice FHE encryption mechanism with single values, including common pitfalls and best practices for developers.
9
+ *
10
+ * @dev Shows the complete flow: receiving encrypted input from user, validating proof,
11
+ * storing the encrypted value, and granting permissions for decryption.
12
+ */
13
+ contract EncryptSingleValue is ZamaEthereumConfig {
14
+ // 🔐 Stored encrypted - only authorized users can decrypt
15
+ euint32 private _encryptedEuint32;
16
+
17
+ constructor() {}
18
+
19
+ /// @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
22
+ function initialize(
23
+ externalEuint32 inputEuint32,
24
+ bytes calldata inputProof
25
+ ) 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
31
+ _encryptedEuint32 = FHE.fromExternal(inputEuint32, inputProof);
32
+
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
37
+ }
38
+
39
+ /// @notice Returns the encrypted handle (not the actual value!)
40
+ /// @dev To decrypt, use fhevm.userDecryptEuint() on the client side
41
+ function encryptedUint32() public view returns (euint32) {
42
+ return _encryptedEuint32;
43
+ }
44
+ }
@@ -0,0 +1,54 @@
1
+ // SPDX-License-Identifier: BSD-3-Clause-Clear
2
+ pragma solidity ^0.8.24;
3
+
4
+ import {FHE, euint32, externalEuint32} from "@fhevm/solidity/lib/FHE.sol";
5
+ import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
6
+
7
+ /**
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.
12
+ */
13
+ contract FHECounter is ZamaEthereumConfig {
14
+ // 🔐 The count is always encrypted - no one can see the actual value
15
+ euint32 private _count;
16
+
17
+ /// @notice Returns the encrypted count handle (not the actual value!)
18
+ function getCount() external view returns (euint32) {
19
+ return _count;
20
+ }
21
+
22
+ /// @notice Increments the counter by an encrypted value
23
+ function increment(
24
+ externalEuint32 inputEuint32,
25
+ bytes calldata inputProof
26
+ ) external {
27
+ // Convert external encrypted input to internal handle
28
+ // The proof is verified automatically
29
+ euint32 encryptedValue = FHE.fromExternal(inputEuint32, inputProof);
30
+
31
+ // Add encrypted values - computation happens on ciphertexts
32
+ // Neither the contract nor anyone else sees the actual numbers
33
+ _count = FHE.add(_count, encryptedValue);
34
+
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
38
+ }
39
+
40
+ /// @notice Decrements the counter by an encrypted value
41
+ function decrement(
42
+ externalEuint32 inputEuint32,
43
+ bytes calldata inputProof
44
+ ) external {
45
+ euint32 encryptedValue = FHE.fromExternal(inputEuint32, inputProof);
46
+
47
+ // Subtract encrypted values
48
+ // ⚠️ No underflow protection here - add checks in production!
49
+ _count = FHE.sub(_count, encryptedValue);
50
+
51
+ FHE.allowThis(_count);
52
+ FHE.allow(_count, msg.sender);
53
+ }
54
+ }
@@ -0,0 +1,51 @@
1
+ // SPDX-License-Identifier: BSD-3-Clause-Clear
2
+ pragma solidity ^0.8.24;
3
+
4
+ import {FHE, euint8, externalEuint8} from "@fhevm/solidity/lib/FHE.sol";
5
+ import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
6
+
7
+ /**
8
+ * @notice Simple example: adding two encrypted values (a + b)
9
+ *
10
+ * @dev Demonstrates the most basic FHE operation and permission flow.
11
+ */
12
+ contract FHEAdd is ZamaEthereumConfig {
13
+ euint8 private _a;
14
+ euint8 private _b;
15
+ euint8 private _result;
16
+
17
+ constructor() {}
18
+
19
+ /// @notice Set the first operand (encrypted)
20
+ function setA(externalEuint8 inputA, bytes calldata inputProof) external {
21
+ _a = FHE.fromExternal(inputA, inputProof);
22
+ // Only contract needs permission to use this for computation
23
+ FHE.allowThis(_a);
24
+ }
25
+
26
+ /// @notice Set the second operand (encrypted)
27
+ function setB(externalEuint8 inputB, bytes calldata inputProof) external {
28
+ _b = FHE.fromExternal(inputB, inputProof);
29
+ FHE.allowThis(_b);
30
+ }
31
+
32
+ /// @notice Compute a + b on encrypted values
33
+ /// @dev The contract computes on ciphertexts - it never sees actual values!
34
+ function computeAPlusB() external {
35
+ // 🔐 Addition on encrypted values
36
+ // Neither the contract nor anyone else knows what a, b, or result are
37
+ _result = FHE.add(_a, _b);
38
+
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
45
+ }
46
+
47
+ /// @notice Returns the encrypted result
48
+ function result() public view returns (euint8) {
49
+ return _result;
50
+ }
51
+ }
@@ -0,0 +1,99 @@
1
+ // SPDX-License-Identifier: BSD-3-Clause-Clear
2
+ pragma solidity ^0.8.24;
3
+
4
+ import {FHE, euint32, externalEuint32} from "@fhevm/solidity/lib/FHE.sol";
5
+ import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
6
+
7
+ /**
8
+ * @notice Demonstrates all FHE arithmetic operations on encrypted integers
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
21
+ */
22
+ contract FHEArithmetic is ZamaEthereumConfig {
23
+ euint32 private _a;
24
+ euint32 private _b;
25
+ euint32 private _result;
26
+
27
+ // solhint-disable-next-line no-empty-blocks
28
+ constructor() {}
29
+
30
+ /// @notice Sets the first operand (encrypted)
31
+ function setA(externalEuint32 inputA, bytes calldata inputProof) external {
32
+ _a = FHE.fromExternal(inputA, inputProof);
33
+ FHE.allowThis(_a);
34
+ }
35
+
36
+ /// @notice Sets the second operand (encrypted)
37
+ function setB(externalEuint32 inputB, bytes calldata inputProof) external {
38
+ _b = FHE.fromExternal(inputB, inputProof);
39
+ FHE.allowThis(_b);
40
+ }
41
+
42
+ /// @notice Computes encrypted addition: result = a + b
43
+ function computeAdd() external {
44
+ _result = FHE.add(_a, _b);
45
+ _grantPermissions();
46
+ }
47
+
48
+ /// @notice Computes encrypted subtraction: result = a - b
49
+ /// @dev No underflow protection - in production, add range checks
50
+ function computeSub() external {
51
+ _result = FHE.sub(_a, _b);
52
+ _grantPermissions();
53
+ }
54
+
55
+ /// @notice Computes encrypted multiplication: result = a * b
56
+ /// @dev No overflow protection - in production, add range checks
57
+ function computeMul() external {
58
+ _result = FHE.mul(_a, _b);
59
+ _grantPermissions();
60
+ }
61
+
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.
64
+ function computeDiv(uint32 divisor) external {
65
+ _result = FHE.div(_a, divisor);
66
+ _grantPermissions();
67
+ }
68
+
69
+ /// @notice Computes encrypted remainder: result = a % b (scalar)
70
+ /// @dev Divisor must be a scalar (plaintext).
71
+ function computeRem(uint32 modulus) external {
72
+ _result = FHE.rem(_a, modulus);
73
+ _grantPermissions();
74
+ }
75
+
76
+ /// @notice Computes encrypted minimum: result = min(a, b)
77
+ function computeMin() external {
78
+ _result = FHE.min(_a, _b);
79
+ _grantPermissions();
80
+ }
81
+
82
+ /// @notice Computes encrypted maximum: result = max(a, b)
83
+ function computeMax() external {
84
+ _result = FHE.max(_a, _b);
85
+ _grantPermissions();
86
+ }
87
+
88
+ /// @notice Returns the encrypted result
89
+ /// @dev Caller must have FHE permissions to decrypt
90
+ function getResult() public view returns (euint32) {
91
+ return _result;
92
+ }
93
+
94
+ /// @dev Grants FHE permissions to contract and caller for decryption
95
+ function _grantPermissions() internal {
96
+ FHE.allowThis(_result);
97
+ FHE.allow(_result, msg.sender);
98
+ }
99
+ }
@@ -0,0 +1,116 @@
1
+ // SPDX-License-Identifier: BSD-3-Clause-Clear
2
+ pragma solidity ^0.8.24;
3
+
4
+ import {
5
+ FHE,
6
+ ebool,
7
+ euint32,
8
+ externalEuint32
9
+ } from "@fhevm/solidity/lib/FHE.sol";
10
+ import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
11
+
12
+ /**
13
+ * @notice Demonstrates all FHE comparison operations on encrypted integers
14
+ *
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)
26
+ */
27
+ contract FHEComparison is ZamaEthereumConfig {
28
+ euint32 private _a;
29
+ euint32 private _b;
30
+ ebool private _boolResult;
31
+ euint32 private _selectedResult;
32
+
33
+ // solhint-disable-next-line no-empty-blocks
34
+ constructor() {}
35
+
36
+ /// @notice Sets the first operand (encrypted)
37
+ function setA(externalEuint32 inputA, bytes calldata inputProof) external {
38
+ _a = FHE.fromExternal(inputA, inputProof);
39
+ FHE.allowThis(_a);
40
+ }
41
+
42
+ /// @notice Sets the second operand (encrypted)
43
+ function setB(externalEuint32 inputB, bytes calldata inputProof) external {
44
+ _b = FHE.fromExternal(inputB, inputProof);
45
+ FHE.allowThis(_b);
46
+ }
47
+
48
+ /// @notice Computes encrypted equality: result = (a == b)
49
+ function computeEq() external {
50
+ _boolResult = FHE.eq(_a, _b);
51
+ _grantBoolPermissions();
52
+ }
53
+
54
+ /// @notice Computes encrypted inequality: result = (a != b)
55
+ function computeNe() external {
56
+ _boolResult = FHE.ne(_a, _b);
57
+ _grantBoolPermissions();
58
+ }
59
+
60
+ /// @notice Computes encrypted greater than: result = (a > b)
61
+ function computeGt() external {
62
+ _boolResult = FHE.gt(_a, _b);
63
+ _grantBoolPermissions();
64
+ }
65
+
66
+ /// @notice Computes encrypted less than: result = (a < b)
67
+ function computeLt() external {
68
+ _boolResult = FHE.lt(_a, _b);
69
+ _grantBoolPermissions();
70
+ }
71
+
72
+ /// @notice Computes encrypted greater or equal: result = (a >= b)
73
+ function computeGe() external {
74
+ _boolResult = FHE.ge(_a, _b);
75
+ _grantBoolPermissions();
76
+ }
77
+
78
+ /// @notice Computes encrypted less or equal: result = (a <= b)
79
+ function computeLe() external {
80
+ _boolResult = FHE.le(_a, _b);
81
+ _grantBoolPermissions();
82
+ }
83
+
84
+ /// @notice Computes encrypted maximum using select: result = (a > b) ? a : b
85
+ /// @dev Demonstrates FHE.select for conditional logic on encrypted values
86
+ function computeMaxViaSelect() external {
87
+ ebool aGtB = FHE.gt(_a, _b);
88
+ _selectedResult = FHE.select(aGtB, _a, _b);
89
+ FHE.allowThis(_selectedResult);
90
+ FHE.allow(_selectedResult, msg.sender);
91
+ }
92
+
93
+ /// @notice Computes encrypted minimum using select: result = (a < b) ? a : b
94
+ function computeMinViaSelect() external {
95
+ ebool aLtB = FHE.lt(_a, _b);
96
+ _selectedResult = FHE.select(aLtB, _a, _b);
97
+ FHE.allowThis(_selectedResult);
98
+ FHE.allow(_selectedResult, msg.sender);
99
+ }
100
+
101
+ /// @notice Returns the encrypted boolean result
102
+ function getBoolResult() public view returns (ebool) {
103
+ return _boolResult;
104
+ }
105
+
106
+ /// @notice Returns the encrypted selected result
107
+ function getSelectedResult() public view returns (euint32) {
108
+ return _selectedResult;
109
+ }
110
+
111
+ /// @dev Grants FHE permissions for boolean result
112
+ function _grantBoolPermissions() internal {
113
+ FHE.allowThis(_boolResult);
114
+ FHE.allow(_boolResult, msg.sender);
115
+ }
116
+ }
@@ -0,0 +1,53 @@
1
+ // SPDX-License-Identifier: BSD-3-Clause-Clear
2
+ pragma solidity ^0.8.24;
3
+
4
+ import {FHE, ebool, euint8, externalEuint8} from "@fhevm/solidity/lib/FHE.sol";
5
+ import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
6
+
7
+ /**
8
+ * @notice Demonstrates conditional logic: max(a, b) using encrypted comparison
9
+ *
10
+ * @dev Shows how to use FHE.select() for encrypted if-then-else logic.
11
+ */
12
+ contract FHEIfThenElse is ZamaEthereumConfig {
13
+ euint8 private _a;
14
+ euint8 private _b;
15
+ euint8 private _max;
16
+
17
+ constructor() {}
18
+
19
+ /// @notice Sets the first operand (encrypted)
20
+ function setA(externalEuint8 inputA, bytes calldata inputProof) external {
21
+ _a = FHE.fromExternal(inputA, inputProof);
22
+ FHE.allowThis(_a);
23
+ }
24
+
25
+ /// @notice Sets the second operand (encrypted)
26
+ function setB(externalEuint8 inputB, bytes calldata inputProof) external {
27
+ _b = FHE.fromExternal(inputB, inputProof);
28
+ FHE.allowThis(_b);
29
+ }
30
+
31
+ /// @notice Compute max(a, b) without revealing which is larger
32
+ /// @dev Uses FHE.select() - the encrypted "if-then-else"
33
+ function computeMax() external {
34
+ // 🔍 Compare encrypted values - result is encrypted boolean!
35
+ // We don't know if a >= b, only the encrypted result
36
+ ebool aIsGreaterOrEqual = FHE.ge(_a, _b);
37
+
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!
42
+ _max = FHE.select(aIsGreaterOrEqual, _a, _b);
43
+
44
+ // Grant permissions for decryption
45
+ FHE.allowThis(_max);
46
+ FHE.allow(_max, msg.sender);
47
+ }
48
+
49
+ /// @notice Returns the encrypted result
50
+ function result() public view returns (euint8) {
51
+ return _max;
52
+ }
53
+ }
@@ -0,0 +1,94 @@
1
+ // SPDX-License-Identifier: BSD-3-Clause-Clear
2
+ pragma solidity ^0.8.24;
3
+
4
+ import {FHE, euint32, externalEuint32} from "@fhevm/solidity/lib/FHE.sol";
5
+ import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
6
+
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
14
+ */
15
+ contract FHEAccessControl is ZamaEthereumConfig {
16
+ euint32 private _secretValue;
17
+ mapping(address => bool) public hasAccess;
18
+
19
+ constructor() {}
20
+
21
+ // ==================== CORRECT PATTERN ====================
22
+
23
+ /// @notice ✅ CORRECT: Full access pattern for user decryption
24
+ function storeWithFullAccess(
25
+ externalEuint32 input,
26
+ bytes calldata inputProof
27
+ ) external {
28
+ _secretValue = FHE.fromExternal(input, inputProof);
29
+
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
37
+
38
+ hasAccess[msg.sender] = true;
39
+ }
40
+
41
+ /// @notice Grant access to additional users
42
+ function grantAccess(address user) external {
43
+ require(hasAccess[msg.sender], "Caller has no access to grant");
44
+
45
+ // ✅ Works because contract has allowThis permission
46
+ FHE.allow(_secretValue, user);
47
+ hasAccess[user] = true;
48
+ }
49
+
50
+ function getSecretValue() external view returns (euint32) {
51
+ return _secretValue;
52
+ }
53
+
54
+ // ==================== WRONG PATTERNS (EDUCATIONAL) ====================
55
+
56
+ /// @notice ❌ WRONG: Missing allowThis → user decryption FAILS
57
+ function storeWithoutAllowThis(
58
+ externalEuint32 input,
59
+ bytes calldata inputProof
60
+ ) external {
61
+ _secretValue = FHE.fromExternal(input, inputProof);
62
+
63
+ // ❌ Missing: FHE.allowThis(_secretValue)
64
+ // User has permission, but decryption will FAIL!
65
+ FHE.allow(_secretValue, msg.sender);
66
+ }
67
+
68
+ /// @notice ❌ WRONG: Missing allow(user) → no one can decrypt
69
+ function storeWithoutUserAllow(
70
+ externalEuint32 input,
71
+ bytes calldata inputProof
72
+ ) external {
73
+ _secretValue = FHE.fromExternal(input, inputProof);
74
+
75
+ FHE.allowThis(_secretValue);
76
+ // ❌ Missing: FHE.allow(_secretValue, msg.sender)
77
+ // Contract can operate, but no one can decrypt!
78
+ }
79
+
80
+ // ==================== TRANSIENT ACCESS ====================
81
+
82
+ /// @notice Temporary access - expires at end of transaction
83
+ /// @dev Use for: passing values between contracts in same tx
84
+ function computeAndShareTransient(
85
+ address recipient
86
+ ) external returns (euint32) {
87
+ euint32 computed = FHE.add(_secretValue, FHE.asEuint32(1));
88
+
89
+ // 💨 Transient = cheaper than permanent, auto-expires
90
+ FHE.allowTransient(computed, recipient);
91
+
92
+ return computed;
93
+ }
94
+ }