create-fhevm-example 1.4.3 → 1.4.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/contracts/advanced/BlindAuction.sol +22 -57
- package/contracts/advanced/EncryptedEscrow.sol +7 -31
- package/contracts/advanced/HiddenVoting.sol +19 -54
- package/contracts/advanced/PrivateKYC.sol +9 -33
- package/contracts/advanced/PrivatePayroll.sol +9 -35
- package/contracts/basic/decryption/PublicDecryptMultipleValues.sol +19 -21
- package/contracts/basic/decryption/PublicDecryptSingleValue.sol +19 -20
- package/contracts/basic/decryption/UserDecryptMultipleValues.sol +11 -21
- package/contracts/basic/decryption/UserDecryptSingleValue.sol +15 -30
- package/contracts/basic/encryption/EncryptMultipleValues.sol +16 -22
- package/contracts/basic/encryption/EncryptSingleValue.sol +15 -17
- package/contracts/basic/encryption/FHECounter.sol +19 -17
- package/contracts/basic/fhe-operations/FHEAdd.sol +14 -16
- package/contracts/basic/fhe-operations/FHEArithmetic.sol +19 -31
- package/contracts/basic/fhe-operations/FHEComparison.sol +12 -29
- package/contracts/basic/fhe-operations/FHEIfThenElse.sol +9 -10
- package/contracts/concepts/FHEAccessControl.sol +28 -28
- package/contracts/concepts/FHEAntiPatterns.sol +8 -37
- package/contracts/concepts/FHEHandles.sol +14 -29
- package/contracts/concepts/FHEInputProof.sol +13 -33
- package/contracts/gaming/EncryptedLottery.sol +11 -38
- package/contracts/gaming/EncryptedPoker.sol +8 -34
- package/contracts/gaming/RockPaperScissors.sol +43 -64
- package/contracts/openzeppelin/ERC7984.sol +6 -1
- package/contracts/openzeppelin/ERC7984ERC20Wrapper.sol +5 -1
- package/contracts/openzeppelin/SwapERC7984ToERC20.sol +5 -1
- package/contracts/openzeppelin/SwapERC7984ToERC7984.sol +5 -1
- package/contracts/openzeppelin/VestingWallet.sol +13 -17
- package/dist/scripts/commands/generate-config.js +18 -3
- package/dist/scripts/shared/config.d.ts.map +1 -1
- package/dist/scripts/shared/config.js +81 -75
- package/package.json +1 -1
|
@@ -10,26 +10,16 @@ import {
|
|
|
10
10
|
import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
-
* @notice
|
|
13
|
+
* @notice Confidential payroll system with encrypted salaries.
|
|
14
|
+
* Employers can add employees with encrypted salary amounts. Each employee
|
|
15
|
+
* can decrypt only their own salary - other salaries remain hidden.
|
|
16
|
+
* Demonstrates selective decryption permissions where different users
|
|
17
|
+
* see different encrypted values. Perfect for privacy-preserving HR systems.
|
|
14
18
|
*
|
|
15
|
-
* @dev
|
|
16
|
-
*
|
|
17
|
-
* - Bulk payments without revealing individual amounts
|
|
18
|
-
* - Each employee can decrypt only their own salary
|
|
19
|
-
* - Total payroll visible to employer for budgeting
|
|
20
|
-
*
|
|
21
|
-
* Flow:
|
|
22
|
-
* 1. Employer adds employees with encrypted salaries
|
|
23
|
-
* 2. Employer funds the contract
|
|
24
|
-
* 3. Employer calls payAll() to process all salaries
|
|
25
|
-
* 4. Employees can view (decrypt) their own salary
|
|
26
|
-
*
|
|
27
|
-
* ⚠️ IMPORTANT: Uses cumulative encrypted sum for total payroll tracking
|
|
19
|
+
* @dev Flow: addEmployee() → fund() → processPayment()
|
|
20
|
+
* Each employee can decrypt only their own salary.
|
|
28
21
|
*/
|
|
29
22
|
contract PrivatePayroll is ZamaEthereumConfig {
|
|
30
|
-
// ==================== STATE ====================
|
|
31
|
-
|
|
32
|
-
/// Contract owner (employer)
|
|
33
23
|
address public employer;
|
|
34
24
|
|
|
35
25
|
/// List of employee addresses
|
|
@@ -50,9 +40,7 @@ contract PrivatePayroll is ZamaEthereumConfig {
|
|
|
50
40
|
/// Payment period in seconds (default: 30 days)
|
|
51
41
|
uint256 public paymentPeriod;
|
|
52
42
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
/// @notice Emitted when a new employee is added
|
|
43
|
+
/// Emitted when a new employee is added
|
|
56
44
|
/// @param employee Address of the employee
|
|
57
45
|
event EmployeeAdded(address indexed employee);
|
|
58
46
|
|
|
@@ -73,8 +61,6 @@ contract PrivatePayroll is ZamaEthereumConfig {
|
|
|
73
61
|
/// @param amount Amount funded
|
|
74
62
|
event ContractFunded(uint256 amount);
|
|
75
63
|
|
|
76
|
-
// ==================== MODIFIERS ====================
|
|
77
|
-
|
|
78
64
|
modifier onlyEmployer() {
|
|
79
65
|
require(msg.sender == employer, "Only employer");
|
|
80
66
|
_;
|
|
@@ -85,10 +71,6 @@ contract PrivatePayroll is ZamaEthereumConfig {
|
|
|
85
71
|
_;
|
|
86
72
|
}
|
|
87
73
|
|
|
88
|
-
// ==================== CONSTRUCTOR ====================
|
|
89
|
-
|
|
90
|
-
/// @notice Creates a new private payroll contract
|
|
91
|
-
/// @param _paymentPeriod Time between payments in seconds
|
|
92
74
|
constructor(uint256 _paymentPeriod) {
|
|
93
75
|
employer = msg.sender;
|
|
94
76
|
paymentPeriod = _paymentPeriod > 0 ? _paymentPeriod : 30 days;
|
|
@@ -96,8 +78,6 @@ contract PrivatePayroll is ZamaEthereumConfig {
|
|
|
96
78
|
FHE.allowThis(_totalSalaries);
|
|
97
79
|
}
|
|
98
80
|
|
|
99
|
-
// ==================== EMPLOYEE MANAGEMENT ====================
|
|
100
|
-
|
|
101
81
|
/// @notice Add a new employee with encrypted salary
|
|
102
82
|
/// @param employee Address of the employee
|
|
103
83
|
/// @param encryptedSalary Encrypted salary amount
|
|
@@ -190,8 +170,6 @@ contract PrivatePayroll is ZamaEthereumConfig {
|
|
|
190
170
|
emit EmployeeRemoved(employee);
|
|
191
171
|
}
|
|
192
172
|
|
|
193
|
-
// ==================== PAYMENTS ====================
|
|
194
|
-
|
|
195
173
|
/// @notice Fund the contract for payroll
|
|
196
174
|
function fund() external payable onlyEmployer {
|
|
197
175
|
require(msg.value > 0, "Must send funds");
|
|
@@ -231,9 +209,7 @@ contract PrivatePayroll is ZamaEthereumConfig {
|
|
|
231
209
|
emit PaymentProcessed(employee, block.timestamp);
|
|
232
210
|
}
|
|
233
211
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
/// @notice Get encrypted salary handle for an employee
|
|
212
|
+
/// @notice Get encrypted salary handle for employee
|
|
237
213
|
/// @dev Only callable by the employee themselves
|
|
238
214
|
function getMySalary() external view onlyEmployee returns (euint64) {
|
|
239
215
|
return _salaries[msg.sender];
|
|
@@ -276,8 +252,6 @@ contract PrivatePayroll is ZamaEthereumConfig {
|
|
|
276
252
|
return (employees.length, address(this).balance, paymentPeriod);
|
|
277
253
|
}
|
|
278
254
|
|
|
279
|
-
// ==================== RECEIVE ====================
|
|
280
|
-
|
|
281
255
|
/// @notice Accept ETH deposits
|
|
282
256
|
receive() external payable {
|
|
283
257
|
emit ContractFunded(msg.value);
|
|
@@ -5,14 +5,16 @@ 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
|
|
8
|
+
* @notice Highest die roll game with public decryption of multiple values.
|
|
9
|
+
* Two players roll encrypted 8-sided dice, results are made publicly
|
|
10
|
+
* decryptable. Demonstrates handling multiple encrypted values in
|
|
11
|
+
* checkSignatures() where ORDER MATTERS - the cts[] array must match
|
|
12
|
+
* the order of values in the ABI-encoded result.
|
|
9
13
|
*
|
|
10
|
-
* @dev Uses FHE.
|
|
11
|
-
*
|
|
14
|
+
* @dev Uses FHE.randEuint8() + FHE.makePubliclyDecryptable() for both dice rolls.
|
|
15
|
+
* ⚠️ Order matters in cts[] array for checkSignatures!
|
|
12
16
|
*/
|
|
13
17
|
contract HighestDieRoll is ZamaEthereumConfig {
|
|
14
|
-
constructor() {}
|
|
15
|
-
|
|
16
18
|
// Simple counter to assign a unique ID to each new game.
|
|
17
19
|
uint256 private counter = 0;
|
|
18
20
|
|
|
@@ -31,7 +33,8 @@ contract HighestDieRoll is ZamaEthereumConfig {
|
|
|
31
33
|
// Mapping to store all game states, accessible by a unique game ID.
|
|
32
34
|
mapping(uint256 gameId => Game game) public games;
|
|
33
35
|
|
|
34
|
-
/// @notice Emitted when a new game is started,
|
|
36
|
+
/// @notice Emitted when a new game is started,
|
|
37
|
+
/// providing the encrypted handle required for decryption
|
|
35
38
|
/// @param gameId The unique identifier for the game
|
|
36
39
|
/// @param playerA The address of playerA
|
|
37
40
|
/// @param playerB The address of playerB
|
|
@@ -68,7 +71,8 @@ contract HighestDieRoll is ZamaEthereumConfig {
|
|
|
68
71
|
revealed: false
|
|
69
72
|
});
|
|
70
73
|
|
|
71
|
-
//
|
|
74
|
+
// 🌐 Both values marked for public decryption
|
|
75
|
+
// Anyone can decrypt with valid KMS proof
|
|
72
76
|
FHE.makePubliclyDecryptable(playerAEncryptedDieRoll);
|
|
73
77
|
FHE.makePubliclyDecryptable(playerBEncryptedDieRoll);
|
|
74
78
|
|
|
@@ -98,7 +102,8 @@ contract HighestDieRoll is ZamaEthereumConfig {
|
|
|
98
102
|
return games[gameId].playerBEncryptedDieRoll;
|
|
99
103
|
}
|
|
100
104
|
|
|
101
|
-
/// @notice Returns the address of the game winner.
|
|
105
|
+
/// @notice Returns the address of the game winner.
|
|
106
|
+
/// If the game is finalized, the function returns `address(0)`
|
|
102
107
|
/// @notice if the game is a draw.
|
|
103
108
|
function getWinner(uint256 gameId) public view returns (address) {
|
|
104
109
|
require(games[gameId].revealed, "Game winner not yet revealed");
|
|
@@ -110,8 +115,8 @@ contract HighestDieRoll is ZamaEthereumConfig {
|
|
|
110
115
|
return games[gameId].revealed;
|
|
111
116
|
}
|
|
112
117
|
|
|
113
|
-
/// @notice Verifies the provided (decryption proof, ABI-encoded clear values)
|
|
114
|
-
///
|
|
118
|
+
/// @notice Verifies the provided (decryption proof, ABI-encoded clear values)
|
|
119
|
+
/// pair against the stored ciphertext, and then stores the winner of the game.
|
|
115
120
|
function recordAndVerifyWinner(
|
|
116
121
|
uint256 gameId,
|
|
117
122
|
bytes memory abiEncodedClearGameResult,
|
|
@@ -119,13 +124,7 @@ contract HighestDieRoll is ZamaEthereumConfig {
|
|
|
119
124
|
) public {
|
|
120
125
|
require(!games[gameId].revealed, "Game already revealed");
|
|
121
126
|
|
|
122
|
-
//
|
|
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.
|
|
127
|
+
// Verify KMS proof - ORDER MATTERS!
|
|
129
128
|
bytes32[] memory cts = new bytes32[](2);
|
|
130
129
|
cts[0] = FHE.toBytes32(games[gameId].playerAEncryptedDieRoll);
|
|
131
130
|
cts[1] = FHE.toBytes32(games[gameId].playerBEncryptedDieRoll);
|
|
@@ -133,9 +132,8 @@ contract HighestDieRoll is ZamaEthereumConfig {
|
|
|
133
132
|
// This FHE call reverts the transaction if the decryption proof is invalid.
|
|
134
133
|
FHE.checkSignatures(cts, abiEncodedClearGameResult, decryptionProof);
|
|
135
134
|
|
|
136
|
-
//
|
|
137
|
-
//
|
|
138
|
-
// `uint8` instead of an abi-encoded uint8 pair. In this case, we should have to compute abi.encode on-chain
|
|
135
|
+
// Decode both decrypted die rolls
|
|
136
|
+
// Note: Using abi.decode here, but could also accept two uint8 parameters
|
|
139
137
|
(
|
|
140
138
|
uint8 decodedClearPlayerADieRoll,
|
|
141
139
|
uint8 decodedClearPlayerBDieRoll
|
|
@@ -153,7 +151,7 @@ contract HighestDieRoll is ZamaEthereumConfig {
|
|
|
153
151
|
: address(0)
|
|
154
152
|
);
|
|
155
153
|
|
|
156
|
-
//
|
|
154
|
+
// Store game result
|
|
157
155
|
games[gameId].revealed = true;
|
|
158
156
|
games[gameId].winner = winner;
|
|
159
157
|
}
|
|
@@ -5,14 +5,16 @@ 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
|
|
8
|
+
* @notice Heads or Tails game with public, permissionless decryption.
|
|
9
|
+
* Demonstrates makePubliclyDecryptable() which allows ANYONE to decrypt
|
|
10
|
+
* the result (not just allowed users). Perfect for public game results,
|
|
11
|
+
* lottery winners, or voting tallies. Uses FHE.randEbool() for fair
|
|
12
|
+
* randomness and KMS-verified decryption proofs.
|
|
9
13
|
*
|
|
10
|
-
* @dev Uses FHE.
|
|
11
|
-
*
|
|
14
|
+
* @dev Uses FHE.randEbool() for random result + FHE.makePubliclyDecryptable() for revealing.
|
|
15
|
+
* Anyone can decrypt results with valid KMS proof.
|
|
12
16
|
*/
|
|
13
17
|
contract HeadsOrTails is ZamaEthereumConfig {
|
|
14
|
-
constructor() {}
|
|
15
|
-
|
|
16
18
|
/// Simple counter to assign a unique ID to each new game.
|
|
17
19
|
uint256 private counter = 0;
|
|
18
20
|
|
|
@@ -29,7 +31,8 @@ contract HeadsOrTails is ZamaEthereumConfig {
|
|
|
29
31
|
// Mapping to store all game states, accessible by a unique game ID.
|
|
30
32
|
mapping(uint256 gameId => Game game) public games;
|
|
31
33
|
|
|
32
|
-
/// @notice Emitted when a new game is started,
|
|
34
|
+
/// @notice Emitted when a new game is started,
|
|
35
|
+
/// providing the encrypted handle required for decryption
|
|
33
36
|
/// @param gameId The unique identifier for the game
|
|
34
37
|
/// @param headsPlayer The address choosing Heads
|
|
35
38
|
/// @param tailsPlayer The address choosing Tails
|
|
@@ -66,7 +69,8 @@ contract HeadsOrTails is ZamaEthereumConfig {
|
|
|
66
69
|
winner: address(0)
|
|
67
70
|
});
|
|
68
71
|
|
|
69
|
-
//
|
|
72
|
+
// 🌐 Why makePubliclyDecryptable? Allows ANYONE to decrypt (not just allowed users)
|
|
73
|
+
// Use case: Public game results, lottery winners, voting tallies
|
|
70
74
|
FHE.makePubliclyDecryptable(headsOrTailsResult);
|
|
71
75
|
|
|
72
76
|
// You can catch the event to get the gameId and the encryptedHasHeadsWon handle
|
|
@@ -98,10 +102,11 @@ contract HeadsOrTails is ZamaEthereumConfig {
|
|
|
98
102
|
return games[gameId].winner;
|
|
99
103
|
}
|
|
100
104
|
|
|
101
|
-
/// @notice Verifies the provided (decryption proof, ABI-encoded clear value)
|
|
102
|
-
///
|
|
105
|
+
/// @notice Verifies the provided (decryption proof, ABI-encoded clear value)
|
|
106
|
+
/// pair against the stored ciphertext, and then stores the winner of the game.
|
|
103
107
|
/// @dev gameId: The ID of the game to settle.
|
|
104
|
-
/// abiEncodedClearGameResult: The ABI-encoded clear value (bool)
|
|
108
|
+
/// abiEncodedClearGameResult: The ABI-encoded clear value (bool)
|
|
109
|
+
/// associated to the `decryptionProof`.
|
|
105
110
|
/// decryptionProof: The proof that validates the decryption.
|
|
106
111
|
function recordAndVerifyWinner(
|
|
107
112
|
uint256 gameId,
|
|
@@ -113,21 +118,15 @@ contract HeadsOrTails is ZamaEthereumConfig {
|
|
|
113
118
|
"Game winner already revealed"
|
|
114
119
|
);
|
|
115
120
|
|
|
116
|
-
//
|
|
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.
|
|
121
|
+
// Verify KMS decryption proof
|
|
122
122
|
bytes32[] memory cts = new bytes32[](1);
|
|
123
123
|
cts[0] = FHE.toBytes32(games[gameId].encryptedHasHeadsWon);
|
|
124
124
|
|
|
125
125
|
// This FHE call reverts the transaction if the decryption proof is invalid.
|
|
126
126
|
FHE.checkSignatures(cts, abiEncodedClearGameResult, decryptionProof);
|
|
127
127
|
|
|
128
|
-
//
|
|
129
|
-
//
|
|
130
|
-
// `bool` instead of an abi-encoded bool. In this case, we should have compute abi.encode on-chain
|
|
128
|
+
// Decode the decrypted result to determine winner
|
|
129
|
+
// Note: Using abi.decode here, but could also accept plain bool parameter
|
|
131
130
|
bool decodedClearGameResult = abi.decode(
|
|
132
131
|
abiEncodedClearGameResult,
|
|
133
132
|
(bool)
|
|
@@ -136,7 +135,7 @@ contract HeadsOrTails is ZamaEthereumConfig {
|
|
|
136
135
|
? games[gameId].headsPlayer
|
|
137
136
|
: games[gameId].tailsPlayer;
|
|
138
137
|
|
|
139
|
-
//
|
|
138
|
+
// Store the winner
|
|
140
139
|
games[gameId].winner = winner;
|
|
141
140
|
}
|
|
142
141
|
}
|
|
@@ -5,33 +5,29 @@ 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
|
|
8
|
+
* @notice Decrypting multiple encrypted values of different types for a user.
|
|
9
|
+
* Shows how to handle ebool, euint32, and euint64 in one contract.
|
|
10
|
+
* Each value requires individual permission grants - there's no batching
|
|
11
|
+
* for permissions (unlike input proofs). Demonstrates the pattern of
|
|
12
|
+
* granting allowThis() for each value separately.
|
|
9
13
|
*
|
|
10
|
-
* @dev
|
|
11
|
-
*
|
|
14
|
+
* @dev Each value needs separate permission grants (no batching).
|
|
15
|
+
* ⚠️ Cannot batch permission grants - must call allow() for each value!
|
|
12
16
|
*/
|
|
13
17
|
contract UserDecryptMultipleValues is ZamaEthereumConfig {
|
|
14
|
-
// 🔐 Multiple encrypted values of different types
|
|
15
18
|
ebool private _encryptedBool;
|
|
16
19
|
euint32 private _encryptedUint32;
|
|
17
20
|
euint64 private _encryptedUint64;
|
|
18
21
|
|
|
19
|
-
|
|
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)
|
|
22
|
+
/// @notice Initialize multiple values from plaintext
|
|
23
|
+
/// @dev FHE.asEuintX() creates encrypted constants from plaintext
|
|
24
24
|
function initialize(bool a, uint32 b, uint64 c) external {
|
|
25
|
-
// Create encrypted values from plaintext
|
|
26
|
-
// FHE.asEbool(a) encrypts a boolean value
|
|
25
|
+
// Create encrypted values from plaintext
|
|
27
26
|
_encryptedBool = FHE.xor(FHE.asEbool(a), FHE.asEbool(false));
|
|
28
|
-
|
|
29
|
-
// FHE.asEuint32(b) + 1 creates an encrypted (b + 1)
|
|
30
27
|
_encryptedUint32 = FHE.add(FHE.asEuint32(b), FHE.asEuint32(1));
|
|
31
28
|
_encryptedUint64 = FHE.add(FHE.asEuint64(c), FHE.asEuint64(1));
|
|
32
29
|
|
|
33
|
-
// ⚠️
|
|
34
|
-
// You cannot batch permission grants!
|
|
30
|
+
// ⚠️ Why separate? No batching for permissions - each needs individual allow()
|
|
35
31
|
FHE.allowThis(_encryptedBool);
|
|
36
32
|
FHE.allowThis(_encryptedUint32);
|
|
37
33
|
FHE.allowThis(_encryptedUint64);
|
|
@@ -41,20 +37,14 @@ contract UserDecryptMultipleValues is ZamaEthereumConfig {
|
|
|
41
37
|
FHE.allow(_encryptedUint64, msg.sender);
|
|
42
38
|
}
|
|
43
39
|
|
|
44
|
-
/// @notice Returns the encrypted boolean value
|
|
45
|
-
/// @dev Client decrypts with: fhevm.userDecrypt(FhevmType.ebool, handle, ...)
|
|
46
40
|
function encryptedBool() public view returns (ebool) {
|
|
47
41
|
return _encryptedBool;
|
|
48
42
|
}
|
|
49
43
|
|
|
50
|
-
/// @notice Returns the encrypted uint32 value
|
|
51
|
-
/// @dev Client decrypts with: fhevm.userDecrypt(FhevmType.euint32, handle, ...)
|
|
52
44
|
function encryptedUint32() public view returns (euint32) {
|
|
53
45
|
return _encryptedUint32;
|
|
54
46
|
}
|
|
55
47
|
|
|
56
|
-
/// @notice Returns the encrypted uint64 value
|
|
57
|
-
/// @dev Client decrypts with: fhevm.userDecrypt(FhevmType.euint64, handle, ...)
|
|
58
48
|
function encryptedUint64() public view returns (euint64) {
|
|
59
49
|
return _encryptedUint64;
|
|
60
50
|
}
|
|
@@ -5,54 +5,39 @@ 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
|
|
8
|
+
* @notice User-controlled decryption with proper permission management.
|
|
9
|
+
* Demonstrates the critical two-step permission pattern: allowThis()
|
|
10
|
+
* grants the contract permission to store/compute, while allow() grants
|
|
11
|
+
* the user permission to decrypt. Missing either step causes decryption
|
|
12
|
+
* to fail. Includes examples of both correct and incorrect patterns.
|
|
9
13
|
*
|
|
10
|
-
* @dev
|
|
11
|
-
*
|
|
14
|
+
* @dev Shows CORRECT vs INCORRECT permission granting.
|
|
15
|
+
* ⚠️ Both allowThis + allow required for user decryption!
|
|
12
16
|
*/
|
|
13
17
|
contract UserDecryptSingleValue is ZamaEthereumConfig {
|
|
14
18
|
euint32 private _trivialEuint32;
|
|
15
19
|
|
|
16
|
-
|
|
17
|
-
constructor() {}
|
|
18
|
-
|
|
19
|
-
/// @notice Initialize with a trivial formula (value + 1)
|
|
20
|
-
/// @dev Demonstrates correct permission granting
|
|
20
|
+
/// @notice ✅ CORRECT: Proper permission pattern
|
|
21
21
|
function initializeUint32(uint32 value) external {
|
|
22
|
-
// Compute a trivial FHE formula _trivialEuint32 = value + 1
|
|
23
22
|
_trivialEuint32 = FHE.add(FHE.asEuint32(value), FHE.asEuint32(1));
|
|
24
23
|
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
//
|
|
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.
|
|
24
|
+
// 🔑 Why both needed?
|
|
25
|
+
// - allowThis: Contract authorizes releasing the value
|
|
26
|
+
// - allow: User can request decryption
|
|
33
27
|
FHE.allowThis(_trivialEuint32);
|
|
34
28
|
FHE.allow(_trivialEuint32, msg.sender);
|
|
35
29
|
}
|
|
36
30
|
|
|
37
|
-
/// @notice
|
|
38
|
-
/// @dev
|
|
31
|
+
/// @notice ❌ WRONG: Missing allowThis causes decryption to FAIL!
|
|
32
|
+
/// @dev Common mistake - user gets permission but decryption still fails
|
|
39
33
|
function initializeUint32Wrong(uint32 value) external {
|
|
40
|
-
// Compute a trivial FHE formula _trivialEuint32 = value + 1
|
|
41
34
|
_trivialEuint32 = FHE.add(FHE.asEuint32(value), FHE.asEuint32(1));
|
|
42
35
|
|
|
43
|
-
// ❌
|
|
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.
|
|
36
|
+
// ❌ Missing allowThis → user can't decrypt!
|
|
37
|
+
// Why? Decryption needs contract authorization to release
|
|
52
38
|
FHE.allow(_trivialEuint32, msg.sender);
|
|
53
39
|
}
|
|
54
40
|
|
|
55
|
-
/// @notice Returns the encrypted uint32 value
|
|
56
41
|
function encryptedUint32() public view returns (euint32) {
|
|
57
42
|
return _trivialEuint32;
|
|
58
43
|
}
|
|
@@ -13,41 +13,35 @@ import {
|
|
|
13
13
|
import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
|
-
* @notice
|
|
16
|
+
* @notice Efficient handling of multiple encrypted values in one transaction.
|
|
17
|
+
* Demonstrates batched input validation where a single proof covers
|
|
18
|
+
* multiple encrypted values (ebool, euint32, eaddress), saving ~50k gas
|
|
19
|
+
* per additional value compared to separate proofs.
|
|
17
20
|
*
|
|
18
|
-
* @dev
|
|
19
|
-
*
|
|
20
|
-
* - ebool: encrypted boolean
|
|
21
|
-
* - eaddress: encrypted address
|
|
22
|
-
* - ebytes64/128/256: encrypted bytes
|
|
21
|
+
* @dev Demonstrates batched input (ONE proof for multiple values).
|
|
22
|
+
* ⚡ Gas: Batching saves ~50k gas vs separate proofs!
|
|
23
23
|
*/
|
|
24
24
|
contract EncryptMultipleValues is ZamaEthereumConfig {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
eaddress private _encryptedEaddress; // e.g., hidden recipient
|
|
25
|
+
ebool private _encryptedEbool;
|
|
26
|
+
euint32 private _encryptedEuint32;
|
|
27
|
+
eaddress private _encryptedEaddress;
|
|
29
28
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
///
|
|
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!
|
|
29
|
+
/// @notice Store multiple encrypted values from single batched input
|
|
30
|
+
/// @dev Client creates ONE proof for ALL values using createEncryptedInput().
|
|
31
|
+
/// Much cheaper than separate encrypt() calls!
|
|
37
32
|
function initialize(
|
|
38
33
|
externalEbool inputEbool,
|
|
39
34
|
externalEuint32 inputEuint32,
|
|
40
35
|
externalEaddress inputEaddress,
|
|
41
|
-
bytes calldata inputProof // Single proof covers all
|
|
36
|
+
bytes calldata inputProof // Single proof covers all!
|
|
42
37
|
) external {
|
|
43
|
-
//
|
|
38
|
+
// 💡 Why one proof? Client batches all values before encrypt()
|
|
39
|
+
// Saves ~50k gas per additional value!
|
|
44
40
|
_encryptedEbool = FHE.fromExternal(inputEbool, inputProof);
|
|
45
41
|
_encryptedEuint32 = FHE.fromExternal(inputEuint32, inputProof);
|
|
46
42
|
_encryptedEaddress = FHE.fromExternal(inputEaddress, inputProof);
|
|
47
43
|
|
|
48
|
-
//
|
|
49
|
-
// You cannot batch permission grants
|
|
50
|
-
|
|
44
|
+
// 🔐 Each value needs own permissions (no batching here)
|
|
51
45
|
FHE.allowThis(_encryptedEbool);
|
|
52
46
|
FHE.allow(_encryptedEbool, msg.sender);
|
|
53
47
|
|
|
@@ -5,39 +5,37 @@ 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
|
|
9
|
-
*
|
|
8
|
+
* @notice Single value encryption with proof validation.
|
|
9
|
+
* Shows how to receive encrypted data from users, validate proofs,
|
|
10
|
+
* and grant proper permissions. Includes examples of common mistakes
|
|
11
|
+
* and the correct permission pattern (allowThis + allow).
|
|
12
|
+
|
|
10
13
|
* @dev Shows the complete flow: receiving encrypted input from user, validating proof,
|
|
11
14
|
* storing the encrypted value, and granting permissions for decryption.
|
|
12
15
|
*/
|
|
13
16
|
contract EncryptSingleValue is ZamaEthereumConfig {
|
|
14
|
-
// 🔐 Stored encrypted - only authorized users can decrypt
|
|
15
17
|
euint32 private _encryptedEuint32;
|
|
16
18
|
|
|
17
|
-
constructor() {}
|
|
18
|
-
|
|
19
19
|
/// @notice Store an encrypted value submitted by the user
|
|
20
|
-
/// @dev
|
|
21
|
-
///
|
|
20
|
+
/// @dev inputProof ensures: value encrypted for THIS contract + THIS user.
|
|
21
|
+
/// Prevents replay attacks from other contracts/users.
|
|
22
22
|
function initialize(
|
|
23
23
|
externalEuint32 inputEuint32,
|
|
24
24
|
bytes calldata inputProof
|
|
25
25
|
) external {
|
|
26
|
-
//
|
|
27
|
-
//
|
|
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
|
|
26
|
+
// 🔐 Why proof?
|
|
27
|
+
// Prevents: replay attacks, wrong contract, invalid ciphertext
|
|
31
28
|
_encryptedEuint32 = FHE.fromExternal(inputEuint32, inputProof);
|
|
32
29
|
|
|
33
|
-
//
|
|
34
|
-
//
|
|
35
|
-
|
|
36
|
-
FHE.
|
|
30
|
+
// 🔑 Why both?
|
|
31
|
+
// - allowThis: Contract can store/compute with it
|
|
32
|
+
// - allow(user): User can decrypt it
|
|
33
|
+
FHE.allowThis(_encryptedEuint32);
|
|
34
|
+
FHE.allow(_encryptedEuint32, msg.sender);
|
|
37
35
|
}
|
|
38
36
|
|
|
39
37
|
/// @notice Returns the encrypted handle (not the actual value!)
|
|
40
|
-
/// @dev To decrypt, use fhevm.
|
|
38
|
+
/// @dev To decrypt, use fhevm.userDecryptEuint32() on client side
|
|
41
39
|
function encryptedUint32() public view returns (euint32) {
|
|
42
40
|
return _encryptedEuint32;
|
|
43
41
|
}
|
|
@@ -5,47 +5,49 @@ 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 Confidential counter
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
8
|
+
* @notice Confidential counter with encrypted increment/decrement operations.
|
|
9
|
+
* Demonstrates the complete FHE workflow: encryption, computation,
|
|
10
|
+
* and permission management. The counter value remains private,
|
|
11
|
+
* only accessible through decryption by authorized users.
|
|
12
|
+
|
|
13
|
+
* @dev Demonstrates basic FHE workflow: fromExternal() → compute → allow permissions.
|
|
14
|
+
* All arithmetic happens on encrypted values without revealing the count.
|
|
12
15
|
*/
|
|
13
16
|
contract FHECounter is ZamaEthereumConfig {
|
|
14
|
-
// 🔐 The count is always encrypted - no one can see the actual value
|
|
15
17
|
euint32 private _count;
|
|
16
18
|
|
|
17
|
-
/// @notice Returns the encrypted count handle (not the actual value!)
|
|
18
19
|
function getCount() external view returns (euint32) {
|
|
19
20
|
return _count;
|
|
20
21
|
}
|
|
21
22
|
|
|
22
|
-
/// @notice Increments
|
|
23
|
+
/// @notice Increments counter by encrypted amount
|
|
24
|
+
/// @dev Why allowThis + allow? Contract needs permission to store,
|
|
25
|
+
/// user needs permission to decrypt. Both required!
|
|
23
26
|
function increment(
|
|
24
27
|
externalEuint32 inputEuint32,
|
|
25
28
|
bytes calldata inputProof
|
|
26
29
|
) external {
|
|
27
|
-
//
|
|
28
|
-
// The proof is verified automatically
|
|
30
|
+
// 🔐 Why proof? Ensures valid ciphertext encrypted for THIS contract
|
|
29
31
|
euint32 encryptedValue = FHE.fromExternal(inputEuint32, inputProof);
|
|
30
32
|
|
|
31
|
-
//
|
|
32
|
-
// Neither the contract nor anyone else sees the actual numbers
|
|
33
|
+
// 🧮 Homomorphic add: works on encrypted data
|
|
33
34
|
_count = FHE.add(_count, encryptedValue);
|
|
34
35
|
|
|
35
|
-
//
|
|
36
|
-
FHE.allowThis(_count);
|
|
37
|
-
FHE.allow(_count, msg.sender);
|
|
36
|
+
// 🔑 Both needed: allowThis = contract stores, allow = user decrypts
|
|
37
|
+
FHE.allowThis(_count);
|
|
38
|
+
FHE.allow(_count, msg.sender);
|
|
38
39
|
}
|
|
39
40
|
|
|
40
|
-
/// @notice Decrements
|
|
41
|
+
/// @notice Decrements counter by encrypted amount
|
|
42
|
+
/// @dev ⚠️ No underflow protection! FHE.sub wraps around at 0.
|
|
43
|
+
/// ❌ WRONG: Checking result < 0 reveals information
|
|
44
|
+
/// ✅ CORRECT: Use application-level balance tracking or FHE.select()
|
|
41
45
|
function decrement(
|
|
42
46
|
externalEuint32 inputEuint32,
|
|
43
47
|
bytes calldata inputProof
|
|
44
48
|
) external {
|
|
45
49
|
euint32 encryptedValue = FHE.fromExternal(inputEuint32, inputProof);
|
|
46
50
|
|
|
47
|
-
// Subtract encrypted values
|
|
48
|
-
// ⚠️ No underflow protection here - add checks in production!
|
|
49
51
|
_count = FHE.sub(_count, encryptedValue);
|
|
50
52
|
|
|
51
53
|
FHE.allowThis(_count);
|