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.
- package/contracts/advanced/BlindAuction.sol +255 -0
- package/contracts/advanced/EncryptedEscrow.sol +315 -0
- package/contracts/advanced/HiddenVoting.sol +231 -0
- package/contracts/advanced/PrivateKYC.sol +309 -0
- package/contracts/advanced/PrivatePayroll.sol +285 -0
- package/contracts/basic/decryption/PublicDecryptMultipleValues.sol +160 -0
- package/contracts/basic/decryption/PublicDecryptSingleValue.sol +142 -0
- package/contracts/basic/decryption/UserDecryptMultipleValues.sol +61 -0
- package/contracts/basic/decryption/UserDecryptSingleValue.sol +59 -0
- package/contracts/basic/encryption/EncryptMultipleValues.sol +72 -0
- package/contracts/basic/encryption/EncryptSingleValue.sol +44 -0
- package/contracts/basic/encryption/FHECounter.sol +54 -0
- package/contracts/basic/fhe-operations/FHEAdd.sol +51 -0
- package/contracts/basic/fhe-operations/FHEArithmetic.sol +99 -0
- package/contracts/basic/fhe-operations/FHEComparison.sol +116 -0
- package/contracts/basic/fhe-operations/FHEIfThenElse.sol +53 -0
- package/contracts/concepts/FHEAccessControl.sol +94 -0
- package/contracts/concepts/FHEAntiPatterns.sol +329 -0
- package/contracts/concepts/FHEHandles.sol +128 -0
- package/contracts/concepts/FHEInputProof.sol +104 -0
- package/contracts/gaming/EncryptedLottery.sol +298 -0
- package/contracts/gaming/EncryptedPoker.sol +337 -0
- package/contracts/gaming/RockPaperScissors.sol +213 -0
- package/contracts/openzeppelin/ERC7984.sol +85 -0
- package/contracts/openzeppelin/ERC7984ERC20Wrapper.sol +43 -0
- package/contracts/openzeppelin/SwapERC7984ToERC20.sol +110 -0
- package/contracts/openzeppelin/SwapERC7984ToERC7984.sol +48 -0
- package/contracts/openzeppelin/VestingWallet.sol +147 -0
- package/contracts/openzeppelin/mocks/ERC20Mock.sol +31 -0
- package/dist/scripts/commands/add-mode.d.ts.map +1 -0
- package/dist/scripts/{add-mode.js → commands/add-mode.js} +27 -61
- package/dist/scripts/commands/doctor.d.ts.map +1 -0
- package/dist/scripts/{doctor.js → commands/doctor.js} +2 -2
- package/dist/scripts/commands/generate-config.d.ts.map +1 -0
- package/dist/scripts/{generate-config.js → commands/generate-config.js} +3 -10
- package/dist/scripts/commands/generate-docs.d.ts.map +1 -0
- package/dist/scripts/{generate-docs.js → commands/generate-docs.js} +4 -3
- package/dist/scripts/commands/maintenance.d.ts.map +1 -0
- package/dist/scripts/{maintenance.js → commands/maintenance.js} +11 -10
- package/dist/scripts/index.js +63 -59
- package/dist/scripts/{builders.d.ts → shared/builders.d.ts} +2 -2
- package/dist/scripts/shared/builders.d.ts.map +1 -0
- package/dist/scripts/{builders.js → shared/builders.js} +49 -30
- package/dist/scripts/{config.d.ts → shared/config.d.ts} +0 -2
- package/dist/scripts/shared/config.d.ts.map +1 -0
- package/dist/scripts/{config.js → shared/config.js} +48 -59
- package/dist/scripts/shared/generators.d.ts +42 -0
- package/dist/scripts/shared/generators.d.ts.map +1 -0
- package/dist/scripts/{utils.js → shared/generators.js} +34 -271
- package/dist/scripts/shared/ui.d.ts.map +1 -0
- package/dist/scripts/{ui.js → shared/ui.js} +3 -2
- package/dist/scripts/{utils.d.ts → shared/utils.d.ts} +4 -27
- package/dist/scripts/shared/utils.d.ts.map +1 -0
- package/dist/scripts/shared/utils.js +228 -0
- package/fhevm-hardhat-template/.eslintignore +26 -0
- package/fhevm-hardhat-template/.eslintrc.yml +21 -0
- package/fhevm-hardhat-template/.github/workflows/main.yml +47 -0
- package/fhevm-hardhat-template/.github/workflows/manual-windows.yml +28 -0
- package/fhevm-hardhat-template/.github/workflows/manual.yml +28 -0
- package/fhevm-hardhat-template/.prettierignore +25 -0
- package/fhevm-hardhat-template/.prettierrc.yml +15 -0
- package/fhevm-hardhat-template/.solcover.js +4 -0
- package/fhevm-hardhat-template/.solhint.json +12 -0
- package/fhevm-hardhat-template/.solhintignore +3 -0
- package/fhevm-hardhat-template/.vscode/extensions.json +3 -0
- package/fhevm-hardhat-template/.vscode/settings.json +9 -0
- package/fhevm-hardhat-template/LICENSE +33 -0
- package/fhevm-hardhat-template/README.md +110 -0
- package/fhevm-hardhat-template/contracts/FHECounter.sol +46 -0
- package/fhevm-hardhat-template/deploy/deploy.ts +17 -0
- package/fhevm-hardhat-template/hardhat.config.ts +90 -0
- package/fhevm-hardhat-template/package-lock.json +10405 -0
- package/fhevm-hardhat-template/package.json +104 -0
- package/fhevm-hardhat-template/tasks/FHECounter.ts +184 -0
- package/fhevm-hardhat-template/tasks/accounts.ts +9 -0
- package/fhevm-hardhat-template/test/FHECounter.ts +104 -0
- package/fhevm-hardhat-template/test/FHECounterSepolia.ts +104 -0
- package/fhevm-hardhat-template/tsconfig.json +23 -0
- package/package.json +13 -10
- package/test/advanced/BlindAuction.ts +246 -0
- package/test/advanced/EncryptedEscrow.ts +295 -0
- package/test/advanced/HiddenVoting.ts +268 -0
- package/test/advanced/PrivateKYC.ts +382 -0
- package/test/advanced/PrivatePayroll.ts +253 -0
- package/test/basic/decryption/PublicDecryptMultipleValues.ts +254 -0
- package/test/basic/decryption/PublicDecryptSingleValue.ts +264 -0
- package/test/basic/decryption/UserDecryptMultipleValues.ts +107 -0
- package/test/basic/decryption/UserDecryptSingleValue.ts +97 -0
- package/test/basic/encryption/EncryptMultipleValues.ts +110 -0
- package/test/basic/encryption/EncryptSingleValue.ts +124 -0
- package/test/basic/encryption/FHECounter.ts +112 -0
- package/test/basic/fhe-operations/FHEAdd.ts +97 -0
- package/test/basic/fhe-operations/FHEArithmetic.ts +161 -0
- package/test/basic/fhe-operations/FHEComparison.ts +167 -0
- package/test/basic/fhe-operations/FHEIfThenElse.ts +97 -0
- package/test/concepts/FHEAccessControl.ts +154 -0
- package/test/concepts/FHEAntiPatterns.ts +111 -0
- package/test/concepts/FHEHandles.ts +156 -0
- package/test/concepts/FHEInputProof.ts +151 -0
- package/test/gaming/EncryptedLottery.ts +214 -0
- package/test/gaming/EncryptedPoker.ts +349 -0
- package/test/gaming/RockPaperScissors.ts +205 -0
- package/test/openzeppelin/ERC7984.ts +142 -0
- package/test/openzeppelin/ERC7984ERC20Wrapper.ts +71 -0
- package/test/openzeppelin/SwapERC7984ToERC20.ts +76 -0
- package/test/openzeppelin/SwapERC7984ToERC7984.ts +113 -0
- package/test/openzeppelin/VestingWallet.ts +89 -0
- package/dist/scripts/add-mode.d.ts.map +0 -1
- package/dist/scripts/builders.d.ts.map +0 -1
- package/dist/scripts/config.d.ts.map +0 -1
- package/dist/scripts/doctor.d.ts.map +0 -1
- package/dist/scripts/generate-config.d.ts.map +0 -1
- package/dist/scripts/generate-docs.d.ts.map +0 -1
- package/dist/scripts/maintenance.d.ts.map +0 -1
- package/dist/scripts/ui.d.ts.map +0 -1
- package/dist/scripts/utils.d.ts.map +0 -1
- /package/dist/scripts/{add-mode.d.ts → commands/add-mode.d.ts} +0 -0
- /package/dist/scripts/{doctor.d.ts → commands/doctor.d.ts} +0 -0
- /package/dist/scripts/{generate-config.d.ts → commands/generate-config.d.ts} +0 -0
- /package/dist/scripts/{generate-docs.d.ts → commands/generate-docs.d.ts} +0 -0
- /package/dist/scripts/{maintenance.d.ts → commands/maintenance.d.ts} +0 -0
- /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
|
+
}
|