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.
Files changed (32) hide show
  1. package/contracts/advanced/BlindAuction.sol +22 -57
  2. package/contracts/advanced/EncryptedEscrow.sol +7 -31
  3. package/contracts/advanced/HiddenVoting.sol +19 -54
  4. package/contracts/advanced/PrivateKYC.sol +9 -33
  5. package/contracts/advanced/PrivatePayroll.sol +9 -35
  6. package/contracts/basic/decryption/PublicDecryptMultipleValues.sol +19 -21
  7. package/contracts/basic/decryption/PublicDecryptSingleValue.sol +19 -20
  8. package/contracts/basic/decryption/UserDecryptMultipleValues.sol +11 -21
  9. package/contracts/basic/decryption/UserDecryptSingleValue.sol +15 -30
  10. package/contracts/basic/encryption/EncryptMultipleValues.sol +16 -22
  11. package/contracts/basic/encryption/EncryptSingleValue.sol +15 -17
  12. package/contracts/basic/encryption/FHECounter.sol +19 -17
  13. package/contracts/basic/fhe-operations/FHEAdd.sol +14 -16
  14. package/contracts/basic/fhe-operations/FHEArithmetic.sol +19 -31
  15. package/contracts/basic/fhe-operations/FHEComparison.sol +12 -29
  16. package/contracts/basic/fhe-operations/FHEIfThenElse.sol +9 -10
  17. package/contracts/concepts/FHEAccessControl.sol +28 -28
  18. package/contracts/concepts/FHEAntiPatterns.sol +8 -37
  19. package/contracts/concepts/FHEHandles.sol +14 -29
  20. package/contracts/concepts/FHEInputProof.sol +13 -33
  21. package/contracts/gaming/EncryptedLottery.sol +11 -38
  22. package/contracts/gaming/EncryptedPoker.sol +8 -34
  23. package/contracts/gaming/RockPaperScissors.sol +43 -64
  24. package/contracts/openzeppelin/ERC7984.sol +6 -1
  25. package/contracts/openzeppelin/ERC7984ERC20Wrapper.sol +5 -1
  26. package/contracts/openzeppelin/SwapERC7984ToERC20.sol +5 -1
  27. package/contracts/openzeppelin/SwapERC7984ToERC7984.sol +5 -1
  28. package/contracts/openzeppelin/VestingWallet.sol +13 -17
  29. package/dist/scripts/commands/generate-config.js +18 -3
  30. package/dist/scripts/shared/config.d.ts.map +1 -1
  31. package/dist/scripts/shared/config.js +81 -75
  32. package/package.json +1 -1
@@ -5,24 +5,22 @@ 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 Understanding FHE handles: creation, computation, immutability, and symbolic execution in mock mode.
8
+ * @notice Deep dive into FHE handles: what they are and how they work.
9
+ * Explains that handles are uint256 pointers to encrypted data,
10
+ * demonstrates three creation methods (fromExternal, asEuint, operations),
11
+ * and emphasizes immutability - every operation creates a NEW handle.
12
+ * Includes gas cost comparisons for different operations.
9
13
  *
10
- * @dev Handle = uint256 pointer to encrypted data stored by FHE coprocessor.
11
- * Types: euint8/16/32/64/128/256, ebool, eaddress, ebytes64/128/256
14
+ * @dev Handle = uint256 pointer to encrypted data. Operations create NEW handles (immutable).
15
+ * ⚡ Gas: asEuint32 ~20k, fromExternal ~50k, add/sub ~100k
12
16
  */
13
17
  contract FHEHandles is ZamaEthereumConfig {
14
- // 🔐 Storage handles - persist across transactions
15
- // These are just uint256 pointers, actual ciphertext is off-chain
16
18
  euint32 private _storedValue;
17
19
  euint32 private _computedValue;
18
20
 
19
21
  event HandleCreated(string operation, uint256 gasUsed);
20
22
  event HandleStored(string description);
21
23
 
22
- constructor() {}
23
-
24
- // ==================== HANDLE CREATION ====================
25
-
26
24
  /// @notice Pattern 1: Create handle from user's encrypted input
27
25
  function createFromExternal(
28
26
  externalEuint32 input,
@@ -30,8 +28,7 @@ contract FHEHandles is ZamaEthereumConfig {
30
28
  ) external {
31
29
  uint256 gasBefore = gasleft();
32
30
 
33
- // 📥 FHE.fromExternal: converts external handle to internal handle
34
- // The proof is verified automatically
31
+ // fromExternal: validates proof and creates internal handle
35
32
  _storedValue = FHE.fromExternal(input, inputProof);
36
33
 
37
34
  emit HandleCreated("fromExternal", gasBefore - gasleft());
@@ -45,8 +42,7 @@ contract FHEHandles is ZamaEthereumConfig {
45
42
  function createFromPlaintext(uint32 plaintextValue) external {
46
43
  uint256 gasBefore = gasleft();
47
44
 
48
- // 📥 FHE.asEuint32: encrypts a public constant
49
- // Use for: thresholds, comparison values, zero-initialization
45
+ // asEuint32: encrypts a public constant (visible on-chain!)
50
46
  _storedValue = FHE.asEuint32(plaintextValue);
51
47
 
52
48
  emit HandleCreated("asEuint32", gasBefore - gasleft());
@@ -55,23 +51,18 @@ contract FHEHandles is ZamaEthereumConfig {
55
51
  FHE.allow(_storedValue, msg.sender);
56
52
  }
57
53
 
58
- // ==================== HANDLE COMPUTATION ====================
59
-
60
- /// @notice Key insight: FHE operations create NEW handles
61
- /// @dev Original handles are IMMUTABLE - they never change
54
+ /// @notice ⚠️ Key insight: FHE operations create NEW handles (immutable!)
62
55
  function computeNewHandle() external {
63
56
  uint256 gasBefore = gasleft();
64
57
 
65
58
  euint32 constant10 = FHE.asEuint32(10);
66
59
 
67
- // 🔄 FHE.add creates a BRAND NEW handle
68
- // _storedValue handle is UNCHANGED
69
- // _computedValue gets the NEW handle
60
+ // 🔄 Why NEW handle? FHE values are immutable - operations always create new ones
70
61
  _computedValue = FHE.add(_storedValue, constant10);
71
62
 
72
63
  emit HandleCreated("add (new handle)", gasBefore - gasleft());
73
64
 
74
- // ⚠️ Must grant permissions for the NEW handle!
65
+ // Must grant permissions for NEW handle
75
66
  FHE.allowThis(_computedValue);
76
67
  FHE.allow(_computedValue, msg.sender);
77
68
 
@@ -94,18 +85,14 @@ contract FHEHandles is ZamaEthereumConfig {
94
85
  FHE.allow(_computedValue, msg.sender);
95
86
  }
96
87
 
97
- // ==================== HANDLE IMMUTABILITY ====================
98
-
99
- /// @notice Demonstrates: updating a variable creates NEW handle
88
+ /// @notice Demonstrates: updating variable creates NEW handle
100
89
  function demonstrateImmutability()
101
90
  external
102
91
  returns (euint32 original, euint32 updated)
103
92
  {
104
- // 📌 Save reference to current handle
105
93
  euint32 originalHandle = _storedValue;
106
94
 
107
- // 🔄 This creates NEW handle, assigns to _storedValue
108
- // originalHandle still points to OLD value!
95
+ // This creates NEW handle! originalHandle still points to OLD value
109
96
  _storedValue = FHE.add(_storedValue, FHE.asEuint32(100));
110
97
 
111
98
  FHE.allowThis(_storedValue);
@@ -116,8 +103,6 @@ contract FHEHandles is ZamaEthereumConfig {
116
103
  return (originalHandle, _storedValue);
117
104
  }
118
105
 
119
- // ==================== GETTERS ====================
120
-
121
106
  function getStoredValue() external view returns (euint32) {
122
107
  return _storedValue;
123
108
  }
@@ -11,68 +11,50 @@ import {
11
11
  import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
12
12
 
13
13
  /**
14
- * @notice Explains input proof validation in FHEVM: what proofs are, why they are needed, and how to use them correctly with single and batched inputs.
14
+ * @notice Input proof validation and batching strategies in FHEVM.
15
+ * Explains why proofs are essential (prevent garbage data, wrong types,
16
+ * and replay attacks) and demonstrates the gas-efficient batching pattern
17
+ * where one proof validates multiple encrypted inputs, saving ~50k gas
18
+ * per additional value.
15
19
  *
16
- * @dev Why proofs? They ensure:
17
- * 1. Ciphertext is valid (not garbage data)
18
- * 2. Value is in valid range for the type
19
- * 3. Sender knows the plaintext (proof of knowledge)
20
+ * @dev Proofs ensure: valid ciphertext + correct range + proof of knowledge.
21
+ * Gas: Batching multiple values in ONE proof saves ~50k gas vs separate proofs!
20
22
  */
21
23
  contract FHEInputProof is ZamaEthereumConfig {
22
24
  euint32 private _singleValue;
23
25
  euint32 private _valueA;
24
26
  euint64 private _valueB;
25
27
 
26
- constructor() {}
27
-
28
- // ==================== SINGLE INPUT ====================
29
-
30
28
  /// @notice Receive single encrypted value with proof
31
29
  function setSingleValue(
32
30
  externalEuint32 encryptedInput,
33
31
  bytes calldata inputProof
34
32
  ) external {
35
- // 📥 FHE.fromExternal validates proof automatically
36
- // If proof is invalid → transaction reverts
37
- // Proof ensures:
38
- // ✓ Valid euint32 ciphertext
39
- // ✓ Encrypted for THIS contract
40
- // ✓ Sender knows the plaintext
33
+ // 🔐 Why proof needed? Prevents: garbage data, wrong type, replay attacks
41
34
  _singleValue = FHE.fromExternal(encryptedInput, inputProof);
42
35
 
43
36
  FHE.allowThis(_singleValue);
44
37
  FHE.allow(_singleValue, msg.sender);
45
38
  }
46
39
 
47
- // ==================== MULTIPLE INPUTS (BATCHED) ====================
48
-
49
- /// @notice Receive multiple encrypted values with SINGLE proof
50
- /// @dev Client-side batching is more gas-efficient!
51
- ///
52
- /// Client code:
53
- /// const input = fhevm.createEncryptedInput(contractAddr, userAddr);
54
- /// input.add32(valueA); // → handles[0]
55
- /// input.add64(valueB); // → handles[1]
56
- /// const enc = await input.encrypt();
57
- /// // enc.inputProof covers BOTH values!
40
+ /// @notice Receive multiple values with SINGLE proof (gas efficient!)
41
+ /// @dev Client batches: input.add32(a).add64(b).encrypt() → one proof for both!
58
42
  function setMultipleValues(
59
43
  externalEuint32 inputA,
60
44
  externalEuint64 inputB,
61
- bytes calldata inputProof // Single proof covers both!
45
+ bytes calldata inputProof // Single proof covers both!
62
46
  ) external {
63
- // Both validated by same proof
47
+ // 💡 Same proof validates both - saves ~50k gas!
64
48
  _valueA = FHE.fromExternal(inputA, inputProof);
65
49
  _valueB = FHE.fromExternal(inputB, inputProof);
66
50
 
67
- // ⚠️ Each value needs its own permission grants
51
+ // Each value needs own permissions
68
52
  FHE.allowThis(_valueA);
69
53
  FHE.allowThis(_valueB);
70
54
  FHE.allow(_valueA, msg.sender);
71
55
  FHE.allow(_valueB, msg.sender);
72
56
  }
73
57
 
74
- // ==================== COMPUTATION WITH NEW INPUT ====================
75
-
76
58
  /// @notice Add new encrypted input to existing stored value
77
59
  function addToValue(
78
60
  externalEuint32 addend,
@@ -88,8 +70,6 @@ contract FHEInputProof is ZamaEthereumConfig {
88
70
  FHE.allow(_singleValue, msg.sender);
89
71
  }
90
72
 
91
- // ==================== GETTERS ====================
92
-
93
73
  function getSingleValue() external view returns (euint32) {
94
74
  return _singleValue;
95
75
  }
@@ -11,25 +11,17 @@ 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
- * @dev Demonstrates FHE random selection and encrypted matching:
17
- * - Players purchase tickets with encrypted numbers
18
- * - Winning number generated using block randomness + FHE
19
- * - Winners identified via FHE.eq without revealing ticket numbers
20
- * - Only winner's ticket revealed at end
21
- *
22
- * Flow:
23
- * 1. Lottery created with ticket price and duration
24
- * 2. Players buy tickets with encrypted numbers
25
- * 3. Owner draws winning number at end
26
- * 4. Winners can claim prizes
27
- *
28
- * ⚠️ IMPORTANT: Block randomness is predictable - use VRF in production!
20
+ * @dev Flow: buyTicket() startDrawing() checkAndClaim() → revealWinner()
21
+ * Gas: Loop in checkAndClaim can be expensive with many tickets!
22
+ * ⚠️ Block randomness is predictable - use VRF in production!
29
23
  */
30
24
  contract EncryptedLottery is ZamaEthereumConfig {
31
- // ==================== TYPES ====================
32
-
33
25
  enum LotteryState {
34
26
  Open, // Accepting tickets
35
27
  Drawing, // Drawing in progress
@@ -41,9 +33,6 @@ contract EncryptedLottery is ZamaEthereumConfig {
41
33
  euint64 number;
42
34
  }
43
35
 
44
- // ==================== STATE ====================
45
-
46
- /// Lottery owner
47
36
  address public owner;
48
37
 
49
38
  /// Current lottery state
@@ -73,9 +62,7 @@ contract EncryptedLottery is ZamaEthereumConfig {
73
62
  /// Lottery round number
74
63
  uint256 public roundNumber;
75
64
 
76
- // ==================== EVENTS ====================
77
-
78
- /// @notice Emitted when a ticket is purchased
65
+ /// Emitted when a ticket is purchased
79
66
  /// @param buyer Address of ticket buyer
80
67
  /// @param ticketIndex Index of the ticket
81
68
  event TicketPurchased(address indexed buyer, uint256 indexed ticketIndex);
@@ -94,18 +81,11 @@ contract EncryptedLottery is ZamaEthereumConfig {
94
81
  /// @param rollover Amount rolled to next round
95
82
  event NoWinner(uint256 indexed roundNumber, uint256 rollover);
96
83
 
97
- // ==================== MODIFIERS ====================
98
-
99
84
  modifier onlyOwner() {
100
85
  require(msg.sender == owner, "Only owner");
101
86
  _;
102
87
  }
103
88
 
104
- // ==================== CONSTRUCTOR ====================
105
-
106
- /// @notice Creates a new encrypted lottery
107
- /// @param _ticketPrice Price per ticket in wei
108
- /// @param _duration Duration in seconds
109
89
  constructor(uint256 _ticketPrice, uint256 _duration) {
110
90
  require(_ticketPrice > 0, "Ticket price must be > 0");
111
91
  require(_duration > 0, "Duration must be > 0");
@@ -117,9 +97,7 @@ contract EncryptedLottery is ZamaEthereumConfig {
117
97
  roundNumber = 1;
118
98
  }
119
99
 
120
- // ==================== TICKET PURCHASE ====================
121
-
122
- /// @notice Purchase a lottery ticket with an encrypted number
100
+ /// @notice Purchase a lottery ticket with encrypted number
123
101
  /// @param encryptedNumber Your encrypted ticket number
124
102
  /// @param inputProof Proof validating the encrypted input
125
103
  function buyTicket(
@@ -130,7 +108,7 @@ contract EncryptedLottery is ZamaEthereumConfig {
130
108
  require(block.timestamp < endTime, "Lottery ended");
131
109
  require(msg.value >= ticketPrice, "Insufficient payment");
132
110
 
133
- // 🔐 Convert external encrypted input
111
+ // Convert and store encrypted ticket number
134
112
  euint64 ticketNumber = FHE.fromExternal(encryptedNumber, inputProof);
135
113
 
136
114
  // ✅ Grant contract permission
@@ -146,8 +124,6 @@ contract EncryptedLottery is ZamaEthereumConfig {
146
124
  emit TicketPurchased(msg.sender, ticketIndex);
147
125
  }
148
126
 
149
- // ==================== DRAWING ====================
150
-
151
127
  /// @notice Start the drawing process
152
128
  /// @dev Only owner can call after lottery ends
153
129
  function startDrawing() external onlyOwner {
@@ -261,9 +237,6 @@ contract EncryptedLottery is ZamaEthereumConfig {
261
237
  // prizePool carries over if no winner
262
238
  }
263
239
 
264
- // ==================== VIEW FUNCTIONS ====================
265
-
266
- /// @notice Get total number of tickets sold
267
240
  function getTicketCount() external view returns (uint256) {
268
241
  return _tickets.length;
269
242
  }
@@ -11,25 +11,17 @@ 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
- * @dev Demonstrates multi-player FHE game mechanics:
17
- * - Each player's hole cards remain encrypted until showdown
18
- * - Hand strength computed via FHE without revealing cards
19
- * - Winner determined by encrypted comparison
20
- * - Only winning hand revealed, loser's cards stay private
21
- *
22
- * Simplified rules for demo:
23
- * - 2 players (heads-up)
24
- * - Cards encoded as 1-13 (Ace=1, King=13)
25
- * - Hand strength = card1 + card2 (simplified for demo)
26
- * - Higher total wins
27
- *
28
- * ⚠️ IMPORTANT: Production poker would need proper hand rankings!
20
+ * @dev Flow: joinGame() bet()/fold() → showdown() → revealWinner()
21
+ * Hand strength = card1 + card2 (simplified for demo)
22
+ * ⚠️ Production needs proper hand rankings (flush, straight, etc.)
29
23
  */
30
24
  contract EncryptedPoker is ZamaEthereumConfig {
31
- // ==================== TYPES ====================
32
-
33
25
  enum GameState {
34
26
  WaitingForPlayers, // Waiting for 2 players
35
27
  CardsDealt, // Both players have cards
@@ -47,8 +39,6 @@ contract EncryptedPoker is ZamaEthereumConfig {
47
39
  bool folded;
48
40
  }
49
41
 
50
- // ==================== STATE ====================
51
-
52
42
  /// Game state
53
43
  GameState public state;
54
44
 
@@ -70,8 +60,6 @@ contract EncryptedPoker is ZamaEthereumConfig {
70
60
  /// Encrypted comparison result (true if player 0 wins)
71
61
  ebool private _player0Wins;
72
62
 
73
- // ==================== EVENTS ====================
74
-
75
63
  /// @notice Emitted when a player joins
76
64
  /// @param player Address of joining player
77
65
  /// @param seat Seat number (0 or 1)
@@ -97,8 +85,6 @@ contract EncryptedPoker is ZamaEthereumConfig {
97
85
  /// @param pot Total pot won
98
86
  event GameWon(address indexed winner, uint256 pot);
99
87
 
100
- // ==================== CONSTRUCTOR ====================
101
-
102
88
  /// @notice Creates a new poker game
103
89
  /// @param _minBet Minimum bet amount in wei
104
90
  constructor(uint256 _minBet) {
@@ -108,8 +94,6 @@ contract EncryptedPoker is ZamaEthereumConfig {
108
94
  state = GameState.WaitingForPlayers;
109
95
  }
110
96
 
111
- // ==================== JOIN GAME ====================
112
-
113
97
  /// @notice Join the game with encrypted hole cards
114
98
  /// @param encCard1 First encrypted card (1-13)
115
99
  /// @param encCard2 Second encrypted card (1-13)
@@ -173,8 +157,6 @@ contract EncryptedPoker is ZamaEthereumConfig {
173
157
  emit PlayerJoined(msg.sender, seat);
174
158
  }
175
159
 
176
- // ==================== BETTING ====================
177
-
178
160
  /// @notice Place a bet
179
161
  function bet() external payable {
180
162
  require(
@@ -220,8 +202,6 @@ contract EncryptedPoker is ZamaEthereumConfig {
220
202
  emit GameWon(winner, winnings);
221
203
  }
222
204
 
223
- // ==================== SHOWDOWN ====================
224
-
225
205
  /// @notice Initiate showdown - compare hands using FHE
226
206
  function showdown() external {
227
207
  require(
@@ -276,8 +256,6 @@ contract EncryptedPoker is ZamaEthereumConfig {
276
256
  emit GameWon(winner, winnings);
277
257
  }
278
258
 
279
- // ==================== RESET ====================
280
-
281
259
  /// @notice Reset for a new game
282
260
  function resetGame() external {
283
261
  require(state == GameState.Finished, "Game not finished");
@@ -290,8 +268,6 @@ contract EncryptedPoker is ZamaEthereumConfig {
290
268
  gameId++;
291
269
  }
292
270
 
293
- // ==================== VIEW FUNCTIONS ====================
294
-
295
271
  /// @notice Get game info
296
272
  function getGameInfo()
297
273
  external
@@ -327,8 +303,6 @@ contract EncryptedPoker is ZamaEthereumConfig {
327
303
  return (_players[seat].card1, _players[seat].card2);
328
304
  }
329
305
 
330
- // ==================== INTERNAL ====================
331
-
332
306
  function _getSeat(address player) internal view returns (uint8) {
333
307
  if (_players[0].addr == player) return 0;
334
308
  if (_players[1].addr == player) return 1;
@@ -1,39 +1,27 @@
1
1
  // SPDX-License-Identifier: BSD-3-Clause-Clear
2
2
  pragma solidity ^0.8.24;
3
3
 
4
- import {
5
- FHE,
6
- euint8,
7
- ebool,
8
- externalEuint8
9
- } from "@fhevm/solidity/lib/FHE.sol";
4
+ import {FHE, euint8, ebool, externalEuint8} from "@fhevm/solidity/lib/FHE.sol";
10
5
  import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
11
6
 
12
7
  /**
13
- * @notice Rock-Paper-Scissors game with encrypted moves - fair play guaranteed!
14
- *
15
- * @dev Demonstrates FHE commit-reveal pattern without trusted third party:
16
- * - Players submit encrypted moves (0=Rock, 1=Paper, 2=Scissors)
17
- * - FHE comparison determines winner without revealing moves
18
- * - Loser's move revealed only after game concludes
19
- *
20
- * Move encoding: 0 = Rock, 1 = Paper, 2 = Scissors
21
- * Win logic: (move1 - move2 + 3) % 3 1 = player1 wins, 2 = player2 wins
22
- *
23
- * ⚠️ IMPORTANT: Uses FHE.rem for modulo - computationally expensive but secure
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.
13
+
14
+ * @dev Commit-reveal pattern without trusted third party.
15
+ * Move encoding: 0=Rock, 1=Paper, 2=Scissors
16
+ * Gas: FHE.rem() is computationally expensive (~300k gas)
24
17
  */
25
18
  contract RockPaperScissors is ZamaEthereumConfig {
26
- // ==================== TYPES ====================
27
-
28
19
  enum GameState {
29
20
  WaitingForPlayers, // 0 or 1 player joined
30
- BothMoved, // Both players submitted moves
31
- Revealed // Winner determined and revealed
21
+ BothMoved, // Both players submitted moves
22
+ Revealed // Winner determined and revealed
32
23
  }
33
24
 
34
- // ==================== STATE ====================
35
-
36
- /// Player 1 address
37
25
  address public player1;
38
26
 
39
27
  /// Player 2 address
@@ -52,9 +40,7 @@ contract RockPaperScissors is ZamaEthereumConfig {
52
40
  /// Game ID for tracking multiple games
53
41
  uint256 public gameId;
54
42
 
55
- // ==================== EVENTS ====================
56
-
57
- /// @notice Emitted when a player joins
43
+ /// Emitted when a player joins
58
44
  /// @param player Address of joining player
59
45
  /// @param playerNumber 1 or 2
60
46
  event PlayerJoined(address indexed player, uint8 playerNumber);
@@ -67,34 +53,29 @@ contract RockPaperScissors is ZamaEthereumConfig {
67
53
  /// @param gameId Current game ID
68
54
  event GameResult(address indexed winner, uint256 indexed gameId);
69
55
 
70
- // ==================== CONSTRUCTOR ====================
71
-
72
- /// @notice Creates a new Rock-Paper-Scissors game
73
56
  constructor() {
74
57
  gameId = 1;
75
58
  state = GameState.WaitingForPlayers;
76
59
  }
77
60
 
78
- // ==================== PLAY FUNCTIONS ====================
79
-
80
61
  /// @notice Submit an encrypted move to play
81
62
  /// @dev Move must be 0 (Rock), 1 (Paper), or 2 (Scissors)
82
- /// @param encryptedMove Encrypted move value (0-2)
83
63
  /// @param inputProof Proof validating the encrypted input
84
64
  function play(
85
65
  externalEuint8 encryptedMove,
86
66
  bytes calldata inputProof
87
67
  ) external {
88
- require(state == GameState.WaitingForPlayers, "Game not accepting moves");
68
+ require(
69
+ state == GameState.WaitingForPlayers,
70
+ "Game not accepting moves"
71
+ );
89
72
  require(
90
73
  msg.sender != player1 && msg.sender != player2,
91
74
  "Already in this game"
92
75
  );
93
76
 
94
- // 🔐 Convert external encrypted input to internal euint8
77
+ // Convert and validate encrypted move
95
78
  euint8 move = FHE.fromExternal(encryptedMove, inputProof);
96
-
97
- // ✅ Grant contract permission to operate on this value
98
79
  FHE.allowThis(move);
99
80
 
100
81
  if (player1 == address(0)) {
@@ -117,31 +98,25 @@ contract RockPaperScissors is ZamaEthereumConfig {
117
98
  function determineWinner() external {
118
99
  require(state == GameState.BothMoved, "Not ready to determine winner");
119
100
 
120
- // 🎯 Winner calculation using FHE
121
- // Formula: (move1 - move2 + 3) % 3
122
- // Result: 0 = tie, 1 = player1 wins, 2 = player2 wins
123
-
124
- // Convert moves to computation: add 3 to avoid negative numbers
125
- // move1 + 3 - move2 = (move1 - move2 + 3)
126
- euint8 diff = FHE.add(FHE.add(_move1, FHE.asEuint8(3)), FHE.neg(_move2));
101
+ // Winner calculation: (move1 - move2 + 3) % 3
102
+ // 0=tie, 1=player1 wins, 2=player2 wins
103
+ euint8 diff = FHE.add(
104
+ FHE.add(_move1, FHE.asEuint8(3)),
105
+ FHE.neg(_move2)
106
+ );
127
107
 
128
- // Modulo 3 to get result (0, 1, or 2)
108
+ // 🎲 Why rem? Modulo keeps result in 0-2 range (tie/p1 wins/p2 wins)
109
+ // ⚡ rem is expensive (~300k gas) - consider alternatives for production
129
110
  euint8 result = FHE.rem(diff, 3);
130
111
 
131
- // 🔍 Check outcomes
112
+ // Compare results to determine outcome
132
113
  ebool isTie = FHE.eq(result, FHE.asEuint8(0));
133
114
  ebool player1Wins = FHE.eq(result, FHE.asEuint8(1));
134
115
 
135
- // 📊 Encode winner as address bits (simplified for demo)
136
- // In production, use public decryption pattern
137
-
138
- // Make result publicly decryptable
116
+ // 🌐 Why makePubliclyDecryptable? Anyone can decrypt result with KMS proof
139
117
  FHE.allowThis(result);
140
118
  FHE.makePubliclyDecryptable(result);
141
119
 
142
- // Store encrypted result for later reveal
143
- // For this demo, we'll use select to pick winner
144
-
145
120
  state = GameState.Revealed;
146
121
 
147
122
  emit GameResult(address(0), gameId); // Placeholder - real winner after decryption
@@ -157,7 +132,10 @@ contract RockPaperScissors is ZamaEthereumConfig {
157
132
  require(state == GameState.Revealed, "Game not ready for reveal");
158
133
 
159
134
  // Build ciphertext list for verification
160
- euint8 diff = FHE.add(FHE.add(_move1, FHE.asEuint8(3)), FHE.neg(_move2));
135
+ euint8 diff = FHE.add(
136
+ FHE.add(_move1, FHE.asEuint8(3)),
137
+ FHE.neg(_move2)
138
+ );
161
139
  euint8 result = FHE.rem(diff, 3);
162
140
 
163
141
  bytes32[] memory cts = new bytes32[](1);
@@ -193,16 +171,17 @@ contract RockPaperScissors is ZamaEthereumConfig {
193
171
  gameId++;
194
172
  }
195
173
 
196
- // ==================== VIEW FUNCTIONS ====================
197
-
198
- /// @notice Get current game state
199
- function getGameState() external view returns (
200
- address p1,
201
- address p2,
202
- GameState currentState,
203
- address currentWinner,
204
- uint256 currentGameId
205
- ) {
174
+ function getGameState()
175
+ external
176
+ view
177
+ returns (
178
+ address p1,
179
+ address p2,
180
+ GameState currentState,
181
+ address currentWinner,
182
+ uint256 currentGameId
183
+ )
184
+ {
206
185
  return (player1, player2, state, winner, gameId);
207
186
  }
208
187
 
@@ -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
  */