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
|
@@ -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
|
|
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
|
|
11
|
-
*
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
|
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
|
|
17
|
-
*
|
|
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
|
-
//
|
|
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
|
-
|
|
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 //
|
|
45
|
+
bytes calldata inputProof // Single proof covers both!
|
|
62
46
|
) external {
|
|
63
|
-
//
|
|
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
|
-
//
|
|
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
|
|
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
|
|
17
|
-
*
|
|
18
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
|
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
|
|
17
|
-
*
|
|
18
|
-
*
|
|
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
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
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,
|
|
31
|
-
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
|
-
|
|
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(
|
|
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
|
-
//
|
|
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
|
-
//
|
|
121
|
-
//
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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(
|
|
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
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
*/
|