create-fhevm-example 1.4.4 → 1.4.6

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 (48) hide show
  1. package/README.md +3 -4
  2. package/contracts/advanced/BlindAuction.sol +5 -1
  3. package/contracts/advanced/EncryptedEscrow.sol +5 -1
  4. package/contracts/advanced/HiddenVoting.sol +5 -1
  5. package/contracts/advanced/PrivateKYC.sol +6 -1
  6. package/contracts/advanced/PrivatePayroll.sol +5 -1
  7. package/contracts/basic/decryption/PublicDecryptMultipleValues.sol +11 -5
  8. package/contracts/basic/decryption/PublicDecryptSingleValue.sol +11 -5
  9. package/contracts/basic/decryption/UserDecryptMultipleValues.sol +5 -1
  10. package/contracts/basic/decryption/UserDecryptSingleValue.sol +5 -1
  11. package/contracts/basic/encryption/EncryptMultipleValues.sol +4 -1
  12. package/contracts/basic/encryption/EncryptSingleValue.sol +6 -2
  13. package/contracts/basic/encryption/FHECounter.sol +4 -1
  14. package/contracts/basic/fhe-operations/FHEAdd.sol +5 -1
  15. package/contracts/basic/fhe-operations/FHEArithmetic.sol +5 -1
  16. package/contracts/basic/fhe-operations/FHEComparison.sol +5 -1
  17. package/contracts/basic/fhe-operations/FHEIfThenElse.sol +5 -1
  18. package/contracts/concepts/FHEAccessControl.sol +8 -3
  19. package/contracts/concepts/FHEHandles.sol +5 -1
  20. package/contracts/concepts/FHEInputProof.sol +5 -1
  21. package/contracts/concepts/antipatterns/ControlFlow.sol +160 -0
  22. package/contracts/concepts/antipatterns/OperationsGasNoise.sol +190 -0
  23. package/contracts/concepts/antipatterns/Permissions.sol +254 -0
  24. package/contracts/gaming/EncryptedLottery.sol +5 -1
  25. package/contracts/gaming/EncryptedPoker.sol +5 -1
  26. package/contracts/gaming/RockPaperScissors.sol +5 -1
  27. package/contracts/openzeppelin/ERC7984.sol +6 -1
  28. package/contracts/openzeppelin/ERC7984ERC20Wrapper.sol +5 -1
  29. package/contracts/openzeppelin/SwapERC7984ToERC20.sol +5 -1
  30. package/contracts/openzeppelin/SwapERC7984ToERC7984.sol +5 -1
  31. package/contracts/openzeppelin/VestingWallet.sol +6 -1
  32. package/dist/scripts/commands/add-mode.d.ts.map +1 -1
  33. package/dist/scripts/commands/add-mode.js +10 -21
  34. package/dist/scripts/commands/doctor.js +4 -1
  35. package/dist/scripts/commands/generate-config.js +18 -3
  36. package/dist/scripts/commands/generate-docs.js +4 -1
  37. package/dist/scripts/index.js +43 -24
  38. package/dist/scripts/shared/builders.d.ts.map +1 -1
  39. package/dist/scripts/shared/builders.js +8 -14
  40. package/dist/scripts/shared/config.d.ts.map +1 -1
  41. package/dist/scripts/shared/config.js +119 -84
  42. package/dist/scripts/shared/utils.js +2 -2
  43. package/package.json +1 -1
  44. package/test/concepts/FHEAntiPatterns.ts +2 -1
  45. package/test/concepts/antipatterns/ControlFlow.ts +125 -0
  46. package/test/concepts/antipatterns/OperationsGasNoise.ts +187 -0
  47. package/test/concepts/antipatterns/Permissions.ts +327 -0
  48. package/contracts/concepts/FHEAntiPatterns.sol +0 -296
@@ -0,0 +1,190 @@
1
+ // SPDX-License-Identifier: BSD-3-Clause-Clear
2
+ pragma solidity ^0.8.24;
3
+
4
+ import {
5
+ FHE,
6
+ euint32,
7
+ euint256,
8
+ externalEuint32
9
+ } from "@fhevm/solidity/lib/FHE.sol";
10
+ import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
11
+
12
+ /**
13
+ * @notice Operations, gas, and noise anti-patterns in FHE development.
14
+ * Demonstrates performance issues, side-channel leaks, and
15
+ * inefficient encrypted computation patterns.
16
+ *
17
+ * @dev Covers 4 patterns: gas/timing side channels, noise accumulation,
18
+ * deprecated APIs, and type mismatches.
19
+ */
20
+ contract FHEOperationsGasNoiseAntiPatterns is ZamaEthereumConfig {
21
+ euint32 private _secretValue;
22
+
23
+ // ═══════════════════════════════════════════════════════════════════════
24
+ // ANTI-PATTERN 1: Gas/Timing Side Channel Attacks
25
+ // ═══════════════════════════════════════════════════════════════════════
26
+
27
+ /**
28
+ * ❌ WRONG: Different code paths have different gas costs
29
+ * @dev Gas consumption reveals which branch was taken
30
+ */
31
+ function wrongGasLeak(
32
+ externalEuint32 input,
33
+ bytes calldata inputProof
34
+ ) external {
35
+ euint32 value = FHE.fromExternal(input, inputProof);
36
+
37
+ // ❌ These operations have different gas costs!
38
+ // If value > 100: expensive operation
39
+ // If value <= 100: cheap operation
40
+ // Gas cost reveals the comparison result!
41
+
42
+ // Simulating different paths (commented to avoid actual leak)
43
+ // if (decrypt(value > 100)) {
44
+ // // Expensive: 10 multiplications
45
+ // } else {
46
+ // // Cheap: 1 addition
47
+ // }
48
+
49
+ _secretValue = value;
50
+ FHE.allowThis(_secretValue);
51
+ }
52
+
53
+ /**
54
+ * ✅ CORRECT: Constant-time operations
55
+ * @dev All paths consume same gas
56
+ */
57
+ function correctConstantTime(
58
+ externalEuint32 input,
59
+ bytes calldata inputProof
60
+ ) external {
61
+ euint32 value = FHE.fromExternal(input, inputProof);
62
+
63
+ // ✅ Always perform same operations regardless of value
64
+ euint32 result = FHE.mul(value, FHE.asEuint32(2));
65
+
66
+ _secretValue = result;
67
+ FHE.allowThis(_secretValue);
68
+ FHE.allow(_secretValue, msg.sender);
69
+ }
70
+
71
+ // ═══════════════════════════════════════════════════════════════════════
72
+ // ANTI-PATTERN 2: Noise Accumulation (Too Many Operations)
73
+ // ═══════════════════════════════════════════════════════════════════════
74
+
75
+ /**
76
+ * ❌ WRONG: Long chain of FHE operations
77
+ * @dev Each operation adds noise, eventually corrupting ciphertext
78
+ */
79
+ function wrongNoiseAccumulation(
80
+ externalEuint32 input,
81
+ bytes calldata inputProof
82
+ ) external {
83
+ euint32 value = FHE.fromExternal(input, inputProof);
84
+
85
+ // ❌ Too many chained operations!
86
+ // Each mul adds significant noise
87
+ euint32 result = value;
88
+ for (uint i = 0; i < 5; i++) {
89
+ result = FHE.mul(result, FHE.asEuint32(2)); // High noise per operation
90
+ }
91
+
92
+ _secretValue = result;
93
+ FHE.allowThis(_secretValue);
94
+ }
95
+
96
+ /**
97
+ * ✅ CORRECT: Minimize operation chains
98
+ * @dev Use mathematical optimization to reduce operations
99
+ */
100
+ function correctMinimizeOperations(
101
+ externalEuint32 input,
102
+ bytes calldata inputProof
103
+ ) external {
104
+ euint32 value = FHE.fromExternal(input, inputProof);
105
+
106
+ // ✅ Instead of 5 multiplications by 2, use single multiplication
107
+ // 2^5 = 32
108
+ euint32 result = FHE.mul(value, FHE.asEuint32(32));
109
+
110
+ _secretValue = result;
111
+ FHE.allowThis(_secretValue);
112
+ FHE.allow(_secretValue, msg.sender);
113
+ }
114
+
115
+ // ═══════════════════════════════════════════════════════════════════════
116
+ // ANTI-PATTERN 3: Using Deprecated FHEVM APIs
117
+ // ═══════════════════════════════════════════════════════════════════════
118
+
119
+ /**
120
+ * ❌ WRONG: Using old TFHE.decrypt() pattern
121
+ * @dev Deprecated in FHEVM v0.9+
122
+ */
123
+ function wrongDeprecatedAPI() external pure returns (string memory) {
124
+ // ❌ OLD (v0.8 and earlier):
125
+ // TFHE.decrypt() - went through Zama Oracle
126
+ //
127
+ // This pattern is deprecated and no longer supported
128
+
129
+ return "Don't use TFHE.decrypt() - it's deprecated";
130
+ }
131
+
132
+ /**
133
+ * ✅ CORRECT: Use modern FHEVM v0.9+ APIs
134
+ * @dev Use FHE.makePubliclyDecryptable() or userDecrypt pattern
135
+ */
136
+ function correctModernAPI() external pure returns (string memory) {
137
+ // ✅ NEW (v0.9+):
138
+ // - FHE.makePubliclyDecryptable() for public decryption
139
+ // - userDecrypt pattern via fhevm.js for user decryption
140
+
141
+ return "Use FHE.makePubliclyDecryptable() or fhevm.js userDecrypt()";
142
+ }
143
+
144
+ // ═══════════════════════════════════════════════════════════════════════
145
+ // ANTI-PATTERN 4: Unnecessary Large Data Types
146
+ // ═══════════════════════════════════════════════════════════════════════
147
+
148
+ /**
149
+ * ❌ WRONG: Using euint256 when euint32 is sufficient
150
+ * @dev Larger types = more gas + more noise
151
+ */
152
+ function wrongOversizedType(
153
+ externalEuint32 input,
154
+ bytes calldata inputProof
155
+ ) external {
156
+ euint32 value = FHE.fromExternal(input, inputProof);
157
+
158
+ // ❌ Unnecessarily converting to euint256!
159
+ // euint256 operations are limited and more expensive
160
+ // This conversion is wasteful when euint32 would suffice
161
+ euint256 largeValue = FHE.asEuint256(value);
162
+
163
+ // Converting back to euint32 (double conversion waste!)
164
+ _secretValue = FHE.asEuint32(largeValue);
165
+ FHE.allowThis(_secretValue);
166
+ }
167
+
168
+ /**
169
+ * ✅ CORRECT: Use smallest sufficient data type
170
+ * @dev euint32 is cheaper than euint256
171
+ */
172
+ function correctRightSizedType(
173
+ externalEuint32 input,
174
+ bytes calldata inputProof
175
+ ) external {
176
+ euint32 value = FHE.fromExternal(input, inputProof);
177
+
178
+ // ✅ Keep using euint32 if values fit
179
+ euint32 result = FHE.mul(value, FHE.asEuint32(2));
180
+
181
+ _secretValue = result;
182
+ FHE.allowThis(_secretValue);
183
+ FHE.allow(_secretValue, msg.sender);
184
+ }
185
+
186
+ /// @notice Helper to get value for testing
187
+ function getValue() external view returns (euint32) {
188
+ return _secretValue;
189
+ }
190
+ }
@@ -0,0 +1,254 @@
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 Permission management anti-patterns in FHE development.
9
+ * Demonstrates common mistakes with allowThis, allow, and
10
+ * permission propagation across transfers and contracts.
11
+ *
12
+ * @dev Covers 6 critical patterns: missing allowThis, missing allow(user),
13
+ * view functions without permissions, unauthenticated re-encryption,
14
+ * transfer permission issues, and cross-contract delegation.
15
+ */
16
+ contract FHEPermissionsAntiPatterns is ZamaEthereumConfig {
17
+ euint32 private _secretValue;
18
+ mapping(address => euint32) private _balances;
19
+
20
+ // ═══════════════════════════════════════════════════════════════════════
21
+ // ANTI-PATTERN 1: Missing allowThis After Computation
22
+ // ═══════════════════════════════════════════════════════════════════════
23
+
24
+ /**
25
+ * ❌ WRONG: Compute but forget allowThis
26
+ * @dev Result exists but contract can't use it in future operations
27
+ */
28
+ function wrongMissingAllowThis(
29
+ externalEuint32 input,
30
+ bytes calldata inputProof
31
+ ) external {
32
+ _secretValue = FHE.fromExternal(input, inputProof);
33
+ euint32 doubled = FHE.mul(_secretValue, FHE.asEuint32(2));
34
+ _secretValue = doubled;
35
+
36
+ // ❌ Missing FHE.allowThis! Contract can't use this value later
37
+ FHE.allow(_secretValue, msg.sender);
38
+ }
39
+
40
+ /**
41
+ * ✅ CORRECT: Always grant allowThis after computation
42
+ * @dev Contract needs permission to use encrypted values
43
+ */
44
+ function correctWithAllowThis(
45
+ externalEuint32 input,
46
+ bytes calldata inputProof
47
+ ) external {
48
+ _secretValue = FHE.fromExternal(input, inputProof);
49
+ euint32 doubled = FHE.mul(_secretValue, FHE.asEuint32(2));
50
+ _secretValue = doubled;
51
+
52
+ FHE.allowThis(_secretValue);
53
+ FHE.allow(_secretValue, msg.sender);
54
+ }
55
+
56
+ // ═══════════════════════════════════════════════════════════════════════
57
+ // ANTI-PATTERN 2: Missing allow(user)
58
+ // ═══════════════════════════════════════════════════════════════════════
59
+
60
+ /**
61
+ * ❌ WRONG: Only allowThis without user permission
62
+ * @dev No one can decrypt the value
63
+ */
64
+ function wrongMissingUserAllow(
65
+ externalEuint32 input,
66
+ bytes calldata inputProof
67
+ ) external {
68
+ _secretValue = FHE.fromExternal(input, inputProof);
69
+
70
+ // ❌ Contract can compute but no one can decrypt!
71
+ FHE.allowThis(_secretValue);
72
+ }
73
+
74
+ /**
75
+ * ✅ CORRECT: Grant both allowThis and allow(user)
76
+ * @dev User can decrypt after contract operations
77
+ */
78
+ function correctWithUserAllow(
79
+ externalEuint32 input,
80
+ bytes calldata inputProof
81
+ ) external {
82
+ _secretValue = FHE.fromExternal(input, inputProof);
83
+
84
+ FHE.allowThis(_secretValue);
85
+ FHE.allow(_secretValue, msg.sender);
86
+ }
87
+
88
+ // ═══════════════════════════════════════════════════════════════════════
89
+ // ANTI-PATTERN 3: View Function Without Permissions
90
+ // ═══════════════════════════════════════════════════════════════════════
91
+
92
+ /**
93
+ * ❌ WRONG: Store value without granting permission to caller
94
+ * @dev When caller tries to get value via view, they can't decrypt it
95
+ */
96
+ function wrongStoreWithoutPermission(
97
+ externalEuint32 input,
98
+ bytes calldata inputProof
99
+ ) external {
100
+ _secretValue = FHE.fromExternal(input, inputProof);
101
+
102
+ // ❌ Only allowThis, caller has no permission!
103
+ FHE.allowThis(_secretValue);
104
+ }
105
+
106
+ /**
107
+ * ✅ CORRECT: Grant permission to caller when storing
108
+ * @dev Caller can now decrypt value returned from view function
109
+ */
110
+ function correctStoreWithPermission(
111
+ externalEuint32 input,
112
+ bytes calldata inputProof
113
+ ) external {
114
+ _secretValue = FHE.fromExternal(input, inputProof);
115
+
116
+ FHE.allowThis(_secretValue);
117
+ FHE.allow(_secretValue, msg.sender); // ✅ Grant permission!
118
+ }
119
+
120
+ /// @notice View function to get stored value
121
+ function getValue() external view returns (euint32) {
122
+ return _secretValue;
123
+ }
124
+
125
+ // ═══════════════════════════════════════════════════════════════════════
126
+ // ANTI-PATTERN 4: Unauthenticated Re-encryption
127
+ // ═══════════════════════════════════════════════════════════════════════
128
+
129
+ /**
130
+ * ❌ WRONG: Re-encrypt without verifying public key ownership
131
+ * @dev Anyone can provide any public key and steal encrypted data
132
+ */
133
+ function wrongReencryptWithoutAuth(
134
+ bytes32 publicKey
135
+ ) external view returns (bytes memory) {
136
+ // ❌ SECURITY RISK: No verification that caller owns this public key!
137
+ // Attacker can provide victim's public key and get their data
138
+
139
+ // This would allow impersonation attacks:
140
+ // return Gateway.reencrypt(_secretValue, publicKey);
141
+
142
+ return ""; // Placeholder
143
+ }
144
+
145
+ /**
146
+ * ✅ CORRECT: Use FHEVM's built-in authentication
147
+ * @dev fhevm.js SDK verifies EIP-712 signature automatically
148
+ * Only the owner of the public key can decrypt
149
+ */
150
+ function correctReencryptWithAuth() external view returns (euint32) {
151
+ // ✅ Return handle directly
152
+ // Client uses fhevm.instance.reencrypt() which:
153
+ // 1. Signs request with their private key (EIP-712)
154
+ // 2. Gateway verifies signature matches public key
155
+ // 3. Only then re-encrypts for that public key
156
+
157
+ return _secretValue;
158
+ }
159
+
160
+ // ═══════════════════════════════════════════════════════════════════════
161
+ // ANTI-PATTERN 5: Transfer Without Permission Propagation
162
+ // ═══════════════════════════════════════════════════════════════════════
163
+
164
+ /**
165
+ * @notice Initialize balance for msg.sender
166
+ * @dev Required before using transfer functions
167
+ */
168
+ function initializeBalance(
169
+ externalEuint32 initialBalance,
170
+ bytes calldata inputProof
171
+ ) external {
172
+ _balances[msg.sender] = FHE.fromExternal(initialBalance, inputProof);
173
+ FHE.allowThis(_balances[msg.sender]);
174
+ FHE.allow(_balances[msg.sender], msg.sender);
175
+ }
176
+
177
+ /**
178
+ * ❌ WRONG: Transfer without granting permissions
179
+ * @dev Recipient gets balance but can't use or decrypt it
180
+ */
181
+ function wrongTransferWithoutPermission(
182
+ address recipient,
183
+ externalEuint32 amount,
184
+ bytes calldata inputProof
185
+ ) external {
186
+ euint32 transferAmount = FHE.fromExternal(amount, inputProof);
187
+
188
+ _balances[msg.sender] = FHE.sub(_balances[msg.sender], transferAmount);
189
+ _balances[recipient] = FHE.add(_balances[recipient], transferAmount);
190
+
191
+ // ❌ Recipient has no permission to use their new balance!
192
+ }
193
+
194
+ /**
195
+ * ✅ CORRECT: Grant permissions after transfer
196
+ * @dev Both parties can use and decrypt their updated balances
197
+ */
198
+ function correctTransferWithPermission(
199
+ address recipient,
200
+ externalEuint32 amount,
201
+ bytes calldata inputProof
202
+ ) external {
203
+ euint32 transferAmount = FHE.fromExternal(amount, inputProof);
204
+
205
+ _balances[msg.sender] = FHE.sub(_balances[msg.sender], transferAmount);
206
+ _balances[recipient] = FHE.add(_balances[recipient], transferAmount);
207
+
208
+ // ✅ Grant permissions to both parties
209
+ FHE.allowThis(_balances[msg.sender]);
210
+ FHE.allow(_balances[msg.sender], msg.sender);
211
+ FHE.allowThis(_balances[recipient]);
212
+ FHE.allow(_balances[recipient], recipient);
213
+ }
214
+
215
+ // ═══════════════════════════════════════════════════════════════════════
216
+ // ANTI-PATTERN 6: Cross-Contract Permission Delegation
217
+ // ═══════════════════════════════════════════════════════════════════════
218
+
219
+ /**
220
+ * ❌ WRONG: Call another contract without granting permission
221
+ * @dev Other contract can't use the encrypted value
222
+ */
223
+ function wrongCrossContractCall(address processor) external returns (bool) {
224
+ // ❌ processor contract has no permission to use _secretValue!
225
+ // This call will fail or return garbage
226
+ (bool success, ) = processor.call(
227
+ abi.encodeWithSignature("process(uint256)", _secretValue)
228
+ );
229
+ return success;
230
+ }
231
+
232
+ /**
233
+ * ✅ CORRECT: Grant temporary permission before cross-contract call
234
+ * @dev Use allowTransient for gas-efficient temporary access
235
+ */
236
+ function correctCrossContractCall(
237
+ address processor
238
+ ) external returns (bool) {
239
+ // ✅ Grant temporary permission (expires at end of transaction)
240
+ FHE.allowTransient(_secretValue, processor);
241
+
242
+ // Now processor can use _secretValue in this transaction
243
+ (bool success, ) = processor.call(
244
+ abi.encodeWithSignature("process(uint256)", _secretValue)
245
+ );
246
+
247
+ return success;
248
+ }
249
+
250
+ /// @notice Helper to get balance for testing
251
+ function getBalance(address user) external view returns (euint32) {
252
+ return _balances[user];
253
+ }
254
+ }
@@ -11,7 +11,11 @@ import {
11
11
  import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
12
12
 
13
13
  /**
14
- * @notice Encrypted Lottery with private ticket numbers - fair and verifiable!
14
+ * @notice Provably fair lottery with encrypted ticket numbers.
15
+ * Players buy tickets with encrypted numbers. Winning number is generated
16
+ * using FHE randomness. Winner is determined by comparing encrypted values
17
+ * without revealing losing tickets. Ensures fairness and privacy - no one
18
+ * can see ticket numbers before the draw.
15
19
  *
16
20
  * @dev Flow: buyTicket() → startDrawing() → checkAndClaim() → revealWinner()
17
21
  * ⚡ Gas: Loop in checkAndClaim can be expensive with many tickets!
@@ -11,7 +11,11 @@ import {
11
11
  import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
12
12
 
13
13
  /**
14
- * @notice Encrypted Poker - Texas Hold'em with hidden hole cards!
14
+ * @notice On-chain Texas Hold'em poker with encrypted hole cards.
15
+ * Two players receive encrypted hole cards that remain hidden throughout
16
+ * the game. Hand strength is computed using FHE operations. Winner is
17
+ * determined by comparing encrypted hand strengths. Demonstrates complex
18
+ * game logic with multiple encrypted states and conditional operations.
15
19
  *
16
20
  * @dev Flow: joinGame() → bet()/fold() → showdown() → revealWinner()
17
21
  * Hand strength = card1 + card2 (simplified for demo)
@@ -5,7 +5,11 @@ import {FHE, euint8, ebool, externalEuint8} from "@fhevm/solidity/lib/FHE.sol";
5
5
  import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
6
6
 
7
7
  /**
8
- * @notice Rock-Paper-Scissors game with encrypted moves - fair play guaranteed using FHE!
8
+ * @notice Fair Rock-Paper-Scissors game with encrypted moves.
9
+ * Players submit encrypted moves (0=Rock, 1=Paper, 2=Scissors) ensuring
10
+ * neither player can see the other's choice before committing. Winner is
11
+ * determined using FHE operations and revealed publicly. No trusted third
12
+ * party needed - cryptography guarantees fairness.
9
13
 
10
14
  * @dev Commit-reveal pattern without trusted third party.
11
15
  * Move encoding: 0=Rock, 1=Paper, 2=Scissors
@@ -12,7 +12,12 @@ import {
12
12
  } from "@openzeppelin/confidential-contracts/token/ERC7984/ERC7984.sol";
13
13
 
14
14
  /**
15
- * @notice Confidential token using OpenZeppelin's ERC7984 standard
15
+ * @notice Confidential ERC20-compatible token using OpenZeppelin's ERC7984 standard.
16
+ * Implements a fully private token where balances and transfer amounts
17
+ * are encrypted. Compatible with standard ERC20 interfaces but with FHE
18
+ * under the hood. Supports both visible minting (owner knows amount) and
19
+ * confidential minting (fully private). Foundation for building private
20
+ * DeFi applications.
16
21
  *
17
22
  * @dev Demonstrates minting and burning with both visible and encrypted amounts.
18
23
  * Shows how to integrate FHE with standard token operations.
@@ -9,7 +9,11 @@ import {
9
9
  } from "@openzeppelin/confidential-contracts/token/ERC7984/extensions/ERC7984ERC20Wrapper.sol";
10
10
 
11
11
  /**
12
- * @notice Wraps ERC20 tokens into confidential ERC7984 tokens
12
+ * @notice Bridge between public ERC20 and confidential ERC7984 tokens.
13
+ * Allows users to wrap regular ERC20 tokens into privacy-preserving
14
+ * ERC7984 tokens (public → private) and unwrap them back (private → public).
15
+ * Wrapping is instant, unwrapping requires decryption proof from KMS.
16
+ * Essential for bringing existing tokens into the confidential ecosystem.
13
17
  *
14
18
  * @dev WRAP: ERC20 → ERC7984 (public → private)
15
19
  * UNWRAP: ERC7984 → ERC20 (private → public, requires decryption)
@@ -12,7 +12,11 @@ import {
12
12
  import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
13
13
 
14
14
  /**
15
- * @notice Swap confidential ERC7984 tokens to regular ERC20 tokens
15
+ * @notice Atomic swap from confidential ERC7984 to public ERC20 tokens.
16
+ * Two-step process: (1) Initiate swap with encrypted amount, request
17
+ * decryption from KMS. (2) Finalize swap with decryption proof, receive
18
+ * ERC20 tokens. Demonstrates the FHEVM v0.9 public decryption flow with
19
+ * makePubliclyDecryptable() and checkSignatures() for trustless swaps.
16
20
  *
17
21
  * @dev Uses FHEVM v0.9 decryption flow:
18
22
  * FHE.makePubliclyDecryptable() + FHE.checkSignatures()
@@ -8,7 +8,11 @@ import {
8
8
  import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
9
9
 
10
10
  /**
11
- * @notice Fully confidential swap between two ERC7984 tokens
11
+ * @notice Fully private atomic swap between two confidential ERC7984 tokens.
12
+ * Both input and output amounts remain encrypted throughout the entire
13
+ * swap process. No decryption needed - amounts stay private from start
14
+ * to finish. Perfect for confidential DEX operations where trade sizes
15
+ * must remain hidden. The ultimate privacy-preserving token exchange.
12
16
  *
13
17
  * @dev Both input and output amounts remain encrypted throughout the swap.
14
18
  */
@@ -12,7 +12,12 @@ import {
12
12
  } from "@openzeppelin/confidential-contracts/interfaces/IERC7984.sol";
13
13
 
14
14
  /**
15
- * @notice Linear vesting wallet for ERC7984 tokens with fully encrypted amounts and schedules.
15
+ * @notice Time-locked vesting wallet with fully encrypted token amounts.
16
+ * Implements linear vesting for ERC7984 confidential tokens. Vesting
17
+ * schedule, amounts, and release calculations all happen on encrypted
18
+ * values using FHE operations. Beneficiary can release vested tokens
19
+ * over time without revealing the total allocation or vesting progress
20
+ * to observers.
16
21
 
17
22
  * @dev Timeline: |--START--|---VESTING---|--END--| (0% → linear → 100%)
18
23
  * All vesting calculations performed on encrypted values using FHE.
@@ -1 +1 @@
1
- {"version":3,"file":"add-mode.d.ts","sourceRoot":"","sources":["../../../scripts/commands/add-mode.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA2OH;;GAEG;AACH,wBAAsB,UAAU,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA8ElE"}
1
+ {"version":3,"file":"add-mode.d.ts","sourceRoot":"","sources":["../../../scripts/commands/add-mode.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAuOH;;GAEG;AACH,wBAAsB,UAAU,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA8ElE"}
@@ -58,7 +58,7 @@ const utils_1 = require("../shared/utils");
58
58
  function detectHardhatProject(targetDir) {
59
59
  const packageJsonPath = path.join(targetDir, "package.json");
60
60
  const hardhatConfigTs = path.join(targetDir, "hardhat.config.ts");
61
- const hardhatConfigJs = path.join(targetDir, "hardhat.config");
61
+ const hardhatConfigJs = path.join(targetDir, "hardhat.config.js");
62
62
  if (!fs.existsSync(packageJsonPath)) {
63
63
  return false;
64
64
  }
@@ -106,7 +106,7 @@ function updateHardhatConfig(targetDir) {
106
106
  ? configPathJs
107
107
  : null;
108
108
  if (!actualPath) {
109
- throw new Error("hardhat.config.ts or hardhat.config.js not found");
109
+ throw new Error(utils_1.ERROR_MESSAGES.CONFIG_NOT_FOUND);
110
110
  }
111
111
  let content = fs.readFileSync(actualPath, "utf-8");
112
112
  if (content.includes("@fhevm/hardhat-plugin")) {
@@ -139,12 +139,12 @@ function updateHardhatConfig(targetDir) {
139
139
  function addExampleFiles(exampleName, targetDir) {
140
140
  const example = config_1.EXAMPLES[exampleName];
141
141
  if (!example) {
142
- throw new Error(`Unknown example: ${exampleName}`);
142
+ throw new Error(utils_1.ERROR_MESSAGES.UNKNOWN_EXAMPLE(exampleName));
143
143
  }
144
144
  const rootDir = (0, utils_1.getRootDir)();
145
145
  const contractName = (0, utils_1.getContractName)(example.contract);
146
146
  if (!contractName) {
147
- throw new Error("Could not extract contract name");
147
+ throw new Error(utils_1.ERROR_MESSAGES.CONTRACT_NAME_FAILED);
148
148
  }
149
149
  const contractSource = path.join(rootDir, example.contract);
150
150
  const testSource = path.join(rootDir, example.test);
@@ -154,15 +154,9 @@ function addExampleFiles(exampleName, targetDir) {
154
154
  if (!fs.existsSync(contractsDir)) {
155
155
  fs.mkdirSync(contractsDir, { recursive: true });
156
156
  }
157
- if (fs.existsSync(contractDest)) {
158
- // File exists - will be handled by caller with prompts
159
- fs.copyFileSync(contractSource, contractDest);
160
- p.log.success(`Overwritten: ${contractName}.sol`);
161
- }
162
- else {
163
- fs.copyFileSync(contractSource, contractDest);
164
- p.log.success(`Added: ${contractName}.sol`);
165
- }
157
+ const isContractOverwrite = fs.existsSync(contractDest);
158
+ fs.copyFileSync(contractSource, contractDest);
159
+ p.log.success(`${isContractOverwrite ? "Overwritten" : "Added"}: ${contractName}.sol`);
166
160
  // Handle test file
167
161
  const testFileName = path.basename(example.test);
168
162
  const testDest = path.join(targetDir, "test", testFileName);
@@ -170,14 +164,9 @@ function addExampleFiles(exampleName, targetDir) {
170
164
  if (!fs.existsSync(testDir)) {
171
165
  fs.mkdirSync(testDir, { recursive: true });
172
166
  }
173
- if (fs.existsSync(testDest)) {
174
- fs.copyFileSync(testSource, testDest);
175
- p.log.success(`Overwritten: ${testFileName}`);
176
- }
177
- else {
178
- fs.copyFileSync(testSource, testDest);
179
- p.log.success(`Added: ${testFileName}`);
180
- }
167
+ const isTestOverwrite = fs.existsSync(testDest);
168
+ fs.copyFileSync(testSource, testDest);
169
+ p.log.success(`${isTestOverwrite ? "Overwritten" : "Added"}: ${testFileName}`);
181
170
  // Handle contract dependencies
182
171
  if (example.dependencies) {
183
172
  p.log.message("");
@@ -162,4 +162,7 @@ async function main() {
162
162
  p.outro(picocolors_1.default.green("✅ All checks passed! You are ready to develop. 🚀"));
163
163
  }
164
164
  }
165
- main().catch(console.error);
165
+ main().catch((err) => {
166
+ console.error(err);
167
+ process.exit(1);
168
+ });