create-fhevm-example 1.4.3 → 1.4.4

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.
@@ -13,23 +13,11 @@ import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
13
13
  /**
14
14
  * @notice Encrypted Lottery with private ticket numbers - fair and verifiable!
15
15
  *
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!
16
+ * @dev Flow: buyTicket() startDrawing() checkAndClaim() → revealWinner()
17
+ * Gas: Loop in checkAndClaim can be expensive with many tickets!
18
+ * ⚠️ Block randomness is predictable - use VRF in production!
29
19
  */
30
20
  contract EncryptedLottery is ZamaEthereumConfig {
31
- // ==================== TYPES ====================
32
-
33
21
  enum LotteryState {
34
22
  Open, // Accepting tickets
35
23
  Drawing, // Drawing in progress
@@ -41,9 +29,6 @@ contract EncryptedLottery is ZamaEthereumConfig {
41
29
  euint64 number;
42
30
  }
43
31
 
44
- // ==================== STATE ====================
45
-
46
- /// Lottery owner
47
32
  address public owner;
48
33
 
49
34
  /// Current lottery state
@@ -73,9 +58,7 @@ contract EncryptedLottery is ZamaEthereumConfig {
73
58
  /// Lottery round number
74
59
  uint256 public roundNumber;
75
60
 
76
- // ==================== EVENTS ====================
77
-
78
- /// @notice Emitted when a ticket is purchased
61
+ /// Emitted when a ticket is purchased
79
62
  /// @param buyer Address of ticket buyer
80
63
  /// @param ticketIndex Index of the ticket
81
64
  event TicketPurchased(address indexed buyer, uint256 indexed ticketIndex);
@@ -94,18 +77,11 @@ contract EncryptedLottery is ZamaEthereumConfig {
94
77
  /// @param rollover Amount rolled to next round
95
78
  event NoWinner(uint256 indexed roundNumber, uint256 rollover);
96
79
 
97
- // ==================== MODIFIERS ====================
98
-
99
80
  modifier onlyOwner() {
100
81
  require(msg.sender == owner, "Only owner");
101
82
  _;
102
83
  }
103
84
 
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
85
  constructor(uint256 _ticketPrice, uint256 _duration) {
110
86
  require(_ticketPrice > 0, "Ticket price must be > 0");
111
87
  require(_duration > 0, "Duration must be > 0");
@@ -117,9 +93,7 @@ contract EncryptedLottery is ZamaEthereumConfig {
117
93
  roundNumber = 1;
118
94
  }
119
95
 
120
- // ==================== TICKET PURCHASE ====================
121
-
122
- /// @notice Purchase a lottery ticket with an encrypted number
96
+ /// @notice Purchase a lottery ticket with encrypted number
123
97
  /// @param encryptedNumber Your encrypted ticket number
124
98
  /// @param inputProof Proof validating the encrypted input
125
99
  function buyTicket(
@@ -130,7 +104,7 @@ contract EncryptedLottery is ZamaEthereumConfig {
130
104
  require(block.timestamp < endTime, "Lottery ended");
131
105
  require(msg.value >= ticketPrice, "Insufficient payment");
132
106
 
133
- // 🔐 Convert external encrypted input
107
+ // Convert and store encrypted ticket number
134
108
  euint64 ticketNumber = FHE.fromExternal(encryptedNumber, inputProof);
135
109
 
136
110
  // ✅ Grant contract permission
@@ -146,8 +120,6 @@ contract EncryptedLottery is ZamaEthereumConfig {
146
120
  emit TicketPurchased(msg.sender, ticketIndex);
147
121
  }
148
122
 
149
- // ==================== DRAWING ====================
150
-
151
123
  /// @notice Start the drawing process
152
124
  /// @dev Only owner can call after lottery ends
153
125
  function startDrawing() external onlyOwner {
@@ -261,9 +233,6 @@ contract EncryptedLottery is ZamaEthereumConfig {
261
233
  // prizePool carries over if no winner
262
234
  }
263
235
 
264
- // ==================== VIEW FUNCTIONS ====================
265
-
266
- /// @notice Get total number of tickets sold
267
236
  function getTicketCount() external view returns (uint256) {
268
237
  return _tickets.length;
269
238
  }
@@ -13,23 +13,11 @@ import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
13
13
  /**
14
14
  * @notice Encrypted Poker - Texas Hold'em with hidden hole cards!
15
15
  *
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!
16
+ * @dev Flow: joinGame() bet()/fold() → showdown() → revealWinner()
17
+ * Hand strength = card1 + card2 (simplified for demo)
18
+ * ⚠️ Production needs proper hand rankings (flush, straight, etc.)
29
19
  */
30
20
  contract EncryptedPoker is ZamaEthereumConfig {
31
- // ==================== TYPES ====================
32
-
33
21
  enum GameState {
34
22
  WaitingForPlayers, // Waiting for 2 players
35
23
  CardsDealt, // Both players have cards
@@ -47,8 +35,6 @@ contract EncryptedPoker is ZamaEthereumConfig {
47
35
  bool folded;
48
36
  }
49
37
 
50
- // ==================== STATE ====================
51
-
52
38
  /// Game state
53
39
  GameState public state;
54
40
 
@@ -70,8 +56,6 @@ contract EncryptedPoker is ZamaEthereumConfig {
70
56
  /// Encrypted comparison result (true if player 0 wins)
71
57
  ebool private _player0Wins;
72
58
 
73
- // ==================== EVENTS ====================
74
-
75
59
  /// @notice Emitted when a player joins
76
60
  /// @param player Address of joining player
77
61
  /// @param seat Seat number (0 or 1)
@@ -97,8 +81,6 @@ contract EncryptedPoker is ZamaEthereumConfig {
97
81
  /// @param pot Total pot won
98
82
  event GameWon(address indexed winner, uint256 pot);
99
83
 
100
- // ==================== CONSTRUCTOR ====================
101
-
102
84
  /// @notice Creates a new poker game
103
85
  /// @param _minBet Minimum bet amount in wei
104
86
  constructor(uint256 _minBet) {
@@ -108,8 +90,6 @@ contract EncryptedPoker is ZamaEthereumConfig {
108
90
  state = GameState.WaitingForPlayers;
109
91
  }
110
92
 
111
- // ==================== JOIN GAME ====================
112
-
113
93
  /// @notice Join the game with encrypted hole cards
114
94
  /// @param encCard1 First encrypted card (1-13)
115
95
  /// @param encCard2 Second encrypted card (1-13)
@@ -173,8 +153,6 @@ contract EncryptedPoker is ZamaEthereumConfig {
173
153
  emit PlayerJoined(msg.sender, seat);
174
154
  }
175
155
 
176
- // ==================== BETTING ====================
177
-
178
156
  /// @notice Place a bet
179
157
  function bet() external payable {
180
158
  require(
@@ -220,8 +198,6 @@ contract EncryptedPoker is ZamaEthereumConfig {
220
198
  emit GameWon(winner, winnings);
221
199
  }
222
200
 
223
- // ==================== SHOWDOWN ====================
224
-
225
201
  /// @notice Initiate showdown - compare hands using FHE
226
202
  function showdown() external {
227
203
  require(
@@ -276,8 +252,6 @@ contract EncryptedPoker is ZamaEthereumConfig {
276
252
  emit GameWon(winner, winnings);
277
253
  }
278
254
 
279
- // ==================== RESET ====================
280
-
281
255
  /// @notice Reset for a new game
282
256
  function resetGame() external {
283
257
  require(state == GameState.Finished, "Game not finished");
@@ -290,8 +264,6 @@ contract EncryptedPoker is ZamaEthereumConfig {
290
264
  gameId++;
291
265
  }
292
266
 
293
- // ==================== VIEW FUNCTIONS ====================
294
-
295
267
  /// @notice Get game info
296
268
  function getGameInfo()
297
269
  external
@@ -327,8 +299,6 @@ contract EncryptedPoker is ZamaEthereumConfig {
327
299
  return (_players[seat].card1, _players[seat].card2);
328
300
  }
329
301
 
330
- // ==================== INTERNAL ====================
331
-
332
302
  function _getSeat(address player) internal view returns (uint8) {
333
303
  if (_players[0].addr == player) return 0;
334
304
  if (_players[1].addr == player) return 1;
@@ -1,39 +1,23 @@
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 Rock-Paper-Scissors game with encrypted moves - fair play guaranteed using FHE!
9
+
10
+ * @dev Commit-reveal pattern without trusted third party.
11
+ * Move encoding: 0=Rock, 1=Paper, 2=Scissors
12
+ * Gas: FHE.rem() is computationally expensive (~300k gas)
24
13
  */
25
14
  contract RockPaperScissors is ZamaEthereumConfig {
26
- // ==================== TYPES ====================
27
-
28
15
  enum GameState {
29
16
  WaitingForPlayers, // 0 or 1 player joined
30
- BothMoved, // Both players submitted moves
31
- Revealed // Winner determined and revealed
17
+ BothMoved, // Both players submitted moves
18
+ Revealed // Winner determined and revealed
32
19
  }
33
20
 
34
- // ==================== STATE ====================
35
-
36
- /// Player 1 address
37
21
  address public player1;
38
22
 
39
23
  /// Player 2 address
@@ -52,9 +36,7 @@ contract RockPaperScissors is ZamaEthereumConfig {
52
36
  /// Game ID for tracking multiple games
53
37
  uint256 public gameId;
54
38
 
55
- // ==================== EVENTS ====================
56
-
57
- /// @notice Emitted when a player joins
39
+ /// Emitted when a player joins
58
40
  /// @param player Address of joining player
59
41
  /// @param playerNumber 1 or 2
60
42
  event PlayerJoined(address indexed player, uint8 playerNumber);
@@ -67,34 +49,29 @@ contract RockPaperScissors is ZamaEthereumConfig {
67
49
  /// @param gameId Current game ID
68
50
  event GameResult(address indexed winner, uint256 indexed gameId);
69
51
 
70
- // ==================== CONSTRUCTOR ====================
71
-
72
- /// @notice Creates a new Rock-Paper-Scissors game
73
52
  constructor() {
74
53
  gameId = 1;
75
54
  state = GameState.WaitingForPlayers;
76
55
  }
77
56
 
78
- // ==================== PLAY FUNCTIONS ====================
79
-
80
57
  /// @notice Submit an encrypted move to play
81
58
  /// @dev Move must be 0 (Rock), 1 (Paper), or 2 (Scissors)
82
- /// @param encryptedMove Encrypted move value (0-2)
83
59
  /// @param inputProof Proof validating the encrypted input
84
60
  function play(
85
61
  externalEuint8 encryptedMove,
86
62
  bytes calldata inputProof
87
63
  ) external {
88
- require(state == GameState.WaitingForPlayers, "Game not accepting moves");
64
+ require(
65
+ state == GameState.WaitingForPlayers,
66
+ "Game not accepting moves"
67
+ );
89
68
  require(
90
69
  msg.sender != player1 && msg.sender != player2,
91
70
  "Already in this game"
92
71
  );
93
72
 
94
- // 🔐 Convert external encrypted input to internal euint8
73
+ // Convert and validate encrypted move
95
74
  euint8 move = FHE.fromExternal(encryptedMove, inputProof);
96
-
97
- // ✅ Grant contract permission to operate on this value
98
75
  FHE.allowThis(move);
99
76
 
100
77
  if (player1 == address(0)) {
@@ -117,31 +94,25 @@ contract RockPaperScissors is ZamaEthereumConfig {
117
94
  function determineWinner() external {
118
95
  require(state == GameState.BothMoved, "Not ready to determine winner");
119
96
 
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));
97
+ // Winner calculation: (move1 - move2 + 3) % 3
98
+ // 0=tie, 1=player1 wins, 2=player2 wins
99
+ euint8 diff = FHE.add(
100
+ FHE.add(_move1, FHE.asEuint8(3)),
101
+ FHE.neg(_move2)
102
+ );
127
103
 
128
- // Modulo 3 to get result (0, 1, or 2)
104
+ // 🎲 Why rem? Modulo keeps result in 0-2 range (tie/p1 wins/p2 wins)
105
+ // ⚡ rem is expensive (~300k gas) - consider alternatives for production
129
106
  euint8 result = FHE.rem(diff, 3);
130
107
 
131
- // 🔍 Check outcomes
108
+ // Compare results to determine outcome
132
109
  ebool isTie = FHE.eq(result, FHE.asEuint8(0));
133
110
  ebool player1Wins = FHE.eq(result, FHE.asEuint8(1));
134
111
 
135
- // 📊 Encode winner as address bits (simplified for demo)
136
- // In production, use public decryption pattern
137
-
138
- // Make result publicly decryptable
112
+ // 🌐 Why makePubliclyDecryptable? Anyone can decrypt result with KMS proof
139
113
  FHE.allowThis(result);
140
114
  FHE.makePubliclyDecryptable(result);
141
115
 
142
- // Store encrypted result for later reveal
143
- // For this demo, we'll use select to pick winner
144
-
145
116
  state = GameState.Revealed;
146
117
 
147
118
  emit GameResult(address(0), gameId); // Placeholder - real winner after decryption
@@ -157,7 +128,10 @@ contract RockPaperScissors is ZamaEthereumConfig {
157
128
  require(state == GameState.Revealed, "Game not ready for reveal");
158
129
 
159
130
  // Build ciphertext list for verification
160
- euint8 diff = FHE.add(FHE.add(_move1, FHE.asEuint8(3)), FHE.neg(_move2));
131
+ euint8 diff = FHE.add(
132
+ FHE.add(_move1, FHE.asEuint8(3)),
133
+ FHE.neg(_move2)
134
+ );
161
135
  euint8 result = FHE.rem(diff, 3);
162
136
 
163
137
  bytes32[] memory cts = new bytes32[](1);
@@ -193,16 +167,17 @@ contract RockPaperScissors is ZamaEthereumConfig {
193
167
  gameId++;
194
168
  }
195
169
 
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
- ) {
170
+ function getGameState()
171
+ external
172
+ view
173
+ returns (
174
+ address p1,
175
+ address p2,
176
+ GameState currentState,
177
+ address currentWinner,
178
+ uint256 currentGameId
179
+ )
180
+ {
206
181
  return (player1, player2, state, winner, gameId);
207
182
  }
208
183
 
@@ -12,19 +12,17 @@ import {
12
12
  } from "@openzeppelin/confidential-contracts/interfaces/IERC7984.sol";
13
13
 
14
14
  /**
15
- * @notice Linear vesting wallet for ERC7984 tokens - amounts stay encrypted!
16
- *
17
- * @dev Timeline: |--START--|---VESTING---|--END--|
18
- * 0% linear 100%
19
- *
20
- * All vesting calculations are performed on encrypted values using FHE operations.
15
+ * @notice Linear vesting wallet for ERC7984 tokens with fully encrypted amounts and schedules.
16
+
17
+ * @dev Timeline: |--START--|---VESTING---|--END--| (0% → linear → 100%)
18
+ * All vesting calculations performed on encrypted values using FHE.
19
+ * ⚡ Gas: FHE.div/mul operations are expensive (~200k gas each)
21
20
  */
22
21
  contract VestingWalletExample is
23
22
  Ownable,
24
23
  ReentrancyGuardTransient,
25
24
  ZamaEthereumConfig
26
25
  {
27
- // 🔐 Track released amounts per token (encrypted)
28
26
  mapping(address token => euint128) private _tokenReleased;
29
27
  uint64 private _start;
30
28
  uint64 private _duration;
@@ -50,8 +48,6 @@ contract VestingWalletExample is
50
48
  _duration = durationSeconds;
51
49
  }
52
50
 
53
- // ==================== VIEW FUNCTIONS ====================
54
-
55
51
  function start() public view virtual returns (uint64) {
56
52
  return _start;
57
53
  }
@@ -77,9 +73,7 @@ contract VestingWalletExample is
77
73
  euint128 vestedAmount_ = vestedAmount(token, uint48(block.timestamp));
78
74
  euint128 releasedAmount = released(token);
79
75
 
80
- // 🔀 FHE.select: encrypted if-else
81
- // If vested >= released: return (vested - released)
82
- // Else: return 0
76
+ // Encrypted comparison: if vested >= released → return difference, else 0
83
77
  ebool canRelease = FHE.ge(vestedAmount_, releasedAmount);
84
78
  return
85
79
  FHE.select(
@@ -89,11 +83,10 @@ contract VestingWalletExample is
89
83
  );
90
84
  }
91
85
 
92
- /// @notice Release vested tokens to beneficiary
93
86
  function release(address token) public virtual nonReentrant {
94
87
  euint64 amount = releasable(token);
95
88
 
96
- // Transfer encrypted amount to owner
89
+ // Transfer encrypted amount using allowTransient (cheaper!)
97
90
  FHE.allowTransient(amount, token);
98
91
  euint64 amountSent = IERC7984(token).confidentialTransfer(
99
92
  owner(),
@@ -122,8 +115,6 @@ contract VestingWalletExample is
122
115
  return _vestingSchedule(totalAllocation, timestamp);
123
116
  }
124
117
 
125
- // ==================== INTERNAL ====================
126
-
127
118
  /// @dev Linear vesting: (total * elapsed) / duration
128
119
  function _vestingSchedule(
129
120
  euint128 totalAllocation,
@@ -136,7 +127,7 @@ contract VestingWalletExample is
136
127
  // After end: 100% vested
137
128
  return totalAllocation;
138
129
  } else {
139
- // During vesting: linear unlock
130
+ // Gas warning: FHE.mul + FHE.div cost ~400k gas combined!
140
131
  return
141
132
  FHE.div(
142
133
  FHE.mul(totalAllocation, (timestamp - start())),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-fhevm-example",
3
- "version": "1.4.3",
3
+ "version": "1.4.4",
4
4
  "description": "Create FHEVM example projects with a single command - A comprehensive toolkit for building privacy-preserving smart contracts",
5
5
  "bin": {
6
6
  "create-fhevm-example": "./dist/scripts/index.js"