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.
- package/contracts/advanced/BlindAuction.sol +18 -57
- package/contracts/advanced/EncryptedEscrow.sol +2 -30
- package/contracts/advanced/HiddenVoting.sol +15 -54
- package/contracts/advanced/PrivateKYC.sol +3 -32
- package/contracts/advanced/PrivatePayroll.sol +4 -34
- package/contracts/basic/decryption/PublicDecryptMultipleValues.sol +9 -17
- package/contracts/basic/decryption/PublicDecryptSingleValue.sol +9 -16
- package/contracts/basic/decryption/UserDecryptMultipleValues.sol +7 -21
- package/contracts/basic/decryption/UserDecryptSingleValue.sol +11 -30
- package/contracts/basic/encryption/EncryptMultipleValues.sol +12 -21
- package/contracts/basic/encryption/EncryptSingleValue.sol +11 -17
- package/contracts/basic/encryption/FHECounter.sol +15 -16
- package/contracts/basic/fhe-operations/FHEAdd.sol +10 -16
- package/contracts/basic/fhe-operations/FHEArithmetic.sol +15 -31
- package/contracts/basic/fhe-operations/FHEComparison.sol +8 -29
- package/contracts/basic/fhe-operations/FHEIfThenElse.sol +4 -9
- package/contracts/concepts/FHEAccessControl.sol +23 -28
- package/contracts/concepts/FHEAntiPatterns.sol +4 -37
- package/contracts/concepts/FHEHandles.sol +10 -29
- package/contracts/concepts/FHEInputProof.sol +9 -33
- package/contracts/gaming/EncryptedLottery.sol +6 -37
- package/contracts/gaming/EncryptedPoker.sol +3 -33
- package/contracts/gaming/RockPaperScissors.sol +39 -64
- package/contracts/openzeppelin/VestingWallet.sol +8 -17
- package/package.json +1 -1
|
@@ -10,33 +10,19 @@ import {
|
|
|
10
10
|
import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
-
* @notice Blind Auction with encrypted bids - only the winning price is revealed
|
|
14
|
-
|
|
15
|
-
* @dev
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* - FHE.makePubliclyDecryptable() for revealing results
|
|
19
|
-
* - FHE.checkSignatures() for proof verification
|
|
20
|
-
*
|
|
21
|
-
* Flow:
|
|
22
|
-
* 1. Owner creates auction with end time and minimum bid
|
|
23
|
-
* 2. Bidders submit encrypted bids (one per address)
|
|
24
|
-
* 3. Owner ends auction → winner computed via FHE.gt/select
|
|
25
|
-
* 4. Anyone can reveal winner after decryption proof is ready
|
|
26
|
-
*
|
|
27
|
-
* ⚠️ IMPORTANT: Losing bids remain encrypted forever!
|
|
13
|
+
* @notice Blind Auction with encrypted bids - only the winning price is revealed after bidding ends
|
|
14
|
+
|
|
15
|
+
* @dev Flow: bid() → endAuction() → revealWinner()
|
|
16
|
+
* Uses FHE.gt/select to find winner without revealing losing bids.
|
|
17
|
+
* Losing bids remain encrypted forever!
|
|
28
18
|
*/
|
|
29
19
|
contract BlindAuction is ZamaEthereumConfig {
|
|
30
|
-
// ==================== TYPES ====================
|
|
31
|
-
|
|
32
20
|
enum AuctionState {
|
|
33
21
|
Open, // Accepting bids
|
|
34
22
|
Closed, // Bidding ended, pending reveal
|
|
35
23
|
Revealed // Winner revealed on-chain
|
|
36
24
|
}
|
|
37
25
|
|
|
38
|
-
// ==================== STATE ====================
|
|
39
|
-
|
|
40
26
|
/// Auction owner (deployer)
|
|
41
27
|
address public owner;
|
|
42
28
|
|
|
@@ -70,8 +56,6 @@ contract BlindAuction is ZamaEthereumConfig {
|
|
|
70
56
|
/// Revealed winning amount (set after reveal)
|
|
71
57
|
uint64 public winningAmount;
|
|
72
58
|
|
|
73
|
-
// ==================== EVENTS ====================
|
|
74
|
-
|
|
75
59
|
/// @notice Emitted when a new bid is placed
|
|
76
60
|
/// @param bidder Address of the bidder
|
|
77
61
|
event BidPlaced(address indexed bidder);
|
|
@@ -89,16 +73,12 @@ contract BlindAuction is ZamaEthereumConfig {
|
|
|
89
73
|
/// @param amount Winning bid amount
|
|
90
74
|
event WinnerRevealed(address indexed winner, uint64 amount);
|
|
91
75
|
|
|
92
|
-
// ==================== MODIFIERS ====================
|
|
93
|
-
|
|
94
76
|
/// @dev Restricts function to owner only
|
|
95
77
|
modifier onlyOwner() {
|
|
96
78
|
require(msg.sender == owner, "Only owner can call");
|
|
97
79
|
_;
|
|
98
80
|
}
|
|
99
81
|
|
|
100
|
-
// ==================== CONSTRUCTOR ====================
|
|
101
|
-
|
|
102
82
|
/// @notice Creates a new blind auction
|
|
103
83
|
/// @param _endTime Unix timestamp when bidding ends
|
|
104
84
|
/// @param _minimumBid Minimum bid amount (plaintext)
|
|
@@ -110,11 +90,8 @@ contract BlindAuction is ZamaEthereumConfig {
|
|
|
110
90
|
auctionState = AuctionState.Open;
|
|
111
91
|
}
|
|
112
92
|
|
|
113
|
-
// ==================== BIDDING ====================
|
|
114
|
-
|
|
115
93
|
/// @notice Submit an encrypted bid to the auction
|
|
116
94
|
/// @dev Each address can only bid once
|
|
117
|
-
/// @param encryptedBid The encrypted bid amount
|
|
118
95
|
/// @param inputProof Proof validating the encrypted input
|
|
119
96
|
function bid(
|
|
120
97
|
externalEuint64 encryptedBid,
|
|
@@ -124,13 +101,11 @@ contract BlindAuction is ZamaEthereumConfig {
|
|
|
124
101
|
require(block.timestamp < endTime, "Auction has ended");
|
|
125
102
|
require(!hasBid[msg.sender], "Already placed a bid");
|
|
126
103
|
|
|
127
|
-
//
|
|
104
|
+
// Convert external encrypted input to internal handle (proof verified)
|
|
128
105
|
euint64 bidAmount = FHE.fromExternal(encryptedBid, inputProof);
|
|
129
|
-
|
|
130
|
-
// ✅ Grant contract permission to operate on this value
|
|
131
106
|
FHE.allowThis(bidAmount);
|
|
132
107
|
|
|
133
|
-
//
|
|
108
|
+
// Store bid - amount stays encrypted
|
|
134
109
|
_bids[msg.sender] = bidAmount;
|
|
135
110
|
hasBid[msg.sender] = true;
|
|
136
111
|
bidders.push(msg.sender);
|
|
@@ -138,30 +113,24 @@ contract BlindAuction is ZamaEthereumConfig {
|
|
|
138
113
|
emit BidPlaced(msg.sender);
|
|
139
114
|
}
|
|
140
115
|
|
|
141
|
-
// ==================== END AUCTION ====================
|
|
142
|
-
|
|
143
116
|
/// @notice End the auction and compute the winner
|
|
117
|
+
/// @dev ⚡ Gas: O(n) loop with FHE.gt/select. ~200k gas per bidder!
|
|
144
118
|
/// @dev Only owner can call after end time
|
|
145
119
|
function endAuction() external onlyOwner {
|
|
146
120
|
require(auctionState == AuctionState.Open, "Auction not open");
|
|
147
121
|
require(block.timestamp >= endTime, "Auction not yet ended");
|
|
148
122
|
require(bidders.length > 0, "No bids placed");
|
|
149
123
|
|
|
150
|
-
//
|
|
151
|
-
|
|
152
|
-
// Initialize with first bidder
|
|
124
|
+
// Find winner using encrypted comparisons (no bids revealed!)
|
|
153
125
|
euint64 currentMax = _bids[bidders[0]];
|
|
154
126
|
euint64 currentWinnerIdx = FHE.asEuint64(0);
|
|
155
127
|
|
|
156
|
-
// 🔄 Iterate through remaining bidders
|
|
157
128
|
for (uint256 i = 1; i < bidders.length; i++) {
|
|
158
129
|
euint64 candidateBid = _bids[bidders[i]];
|
|
159
130
|
|
|
160
|
-
//
|
|
161
|
-
//
|
|
131
|
+
// 🔀 Why select? if/else would leak which bid is higher!
|
|
132
|
+
// Losing bids remain encrypted forever
|
|
162
133
|
ebool isGreater = FHE.gt(candidateBid, currentMax);
|
|
163
|
-
|
|
164
|
-
// 🔀 Select the higher bid and its index
|
|
165
134
|
currentMax = FHE.select(isGreater, candidateBid, currentMax);
|
|
166
135
|
currentWinnerIdx = FHE.select(
|
|
167
136
|
isGreater,
|
|
@@ -170,14 +139,12 @@ contract BlindAuction is ZamaEthereumConfig {
|
|
|
170
139
|
);
|
|
171
140
|
}
|
|
172
141
|
|
|
173
|
-
//
|
|
142
|
+
// Check minimum bid (comparison stays encrypted)
|
|
174
143
|
ebool meetsMinimum = FHE.ge(currentMax, FHE.asEuint64(minimumBid));
|
|
175
|
-
|
|
176
|
-
// If no one meets minimum, winner stays at index 0 but amount becomes 0
|
|
177
144
|
_winningBid = FHE.select(meetsMinimum, currentMax, FHE.asEuint64(0));
|
|
178
145
|
_winnerIndex = currentWinnerIdx;
|
|
179
146
|
|
|
180
|
-
//
|
|
147
|
+
// Mark for public decryption via KMS relayer
|
|
181
148
|
FHE.allowThis(_winningBid);
|
|
182
149
|
FHE.allowThis(_winnerIndex);
|
|
183
150
|
FHE.makePubliclyDecryptable(_winningBid);
|
|
@@ -189,36 +156,32 @@ contract BlindAuction is ZamaEthereumConfig {
|
|
|
189
156
|
}
|
|
190
157
|
|
|
191
158
|
/// @notice Reveal the winner with KMS decryption proof
|
|
192
|
-
/// @dev Anyone can call
|
|
193
|
-
///
|
|
194
|
-
/// @param decryptionProof KMS signature proving decryption
|
|
159
|
+
/// @dev Anyone can call once relayer provides proof.
|
|
160
|
+
/// ⚠️ Order matters! cts[] must match abi.decode() order.
|
|
195
161
|
function revealWinner(
|
|
196
162
|
bytes memory abiEncodedResults,
|
|
197
163
|
bytes memory decryptionProof
|
|
198
164
|
) external {
|
|
199
165
|
require(auctionState == AuctionState.Closed, "Auction not closed");
|
|
200
166
|
|
|
201
|
-
//
|
|
202
|
-
// Order MUST match abiEncodedResults encoding order!
|
|
167
|
+
// Build handle array - order must match abi.decode() below!
|
|
203
168
|
bytes32[] memory cts = new bytes32[](2);
|
|
204
169
|
cts[0] = FHE.toBytes32(_winningBid);
|
|
205
170
|
cts[1] = FHE.toBytes32(_winnerIndex);
|
|
206
171
|
|
|
207
|
-
//
|
|
172
|
+
// Verify KMS signatures (reverts if proof invalid)
|
|
208
173
|
FHE.checkSignatures(cts, abiEncodedResults, decryptionProof);
|
|
209
174
|
|
|
210
|
-
//
|
|
175
|
+
// Decode verified plaintext results
|
|
211
176
|
(uint64 revealedAmount, uint64 winnerIdx) = abi.decode(
|
|
212
177
|
abiEncodedResults,
|
|
213
178
|
(uint64, uint64)
|
|
214
179
|
);
|
|
215
180
|
|
|
216
|
-
// 🏆 Look up winner address
|
|
217
181
|
if (revealedAmount >= minimumBid && winnerIdx < bidders.length) {
|
|
218
182
|
winner = bidders[winnerIdx];
|
|
219
183
|
winningAmount = revealedAmount;
|
|
220
184
|
} else {
|
|
221
|
-
// No valid winner (all bids below minimum)
|
|
222
185
|
winner = address(0);
|
|
223
186
|
winningAmount = 0;
|
|
224
187
|
}
|
|
@@ -228,8 +191,6 @@ contract BlindAuction is ZamaEthereumConfig {
|
|
|
228
191
|
emit WinnerRevealed(winner, winningAmount);
|
|
229
192
|
}
|
|
230
193
|
|
|
231
|
-
// ==================== VIEW FUNCTIONS ====================
|
|
232
|
-
|
|
233
194
|
/// @notice Get the number of bidders
|
|
234
195
|
function getBidderCount() external view returns (uint256) {
|
|
235
196
|
return bidders.length;
|
|
@@ -12,24 +12,10 @@ import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
|
|
|
12
12
|
/**
|
|
13
13
|
* @notice Encrypted Escrow service - amounts hidden until release!
|
|
14
14
|
*
|
|
15
|
-
* @dev
|
|
16
|
-
* -
|
|
17
|
-
* - Conditions verified without revealing values
|
|
18
|
-
* - Multi-party agreement pattern
|
|
19
|
-
* - Dispute resolution with arbiter
|
|
20
|
-
*
|
|
21
|
-
* Flow:
|
|
22
|
-
* 1. Buyer creates escrow with encrypted amount
|
|
23
|
-
* 2. Buyer deposits funds
|
|
24
|
-
* 3. Seller delivers goods/services
|
|
25
|
-
* 4. Buyer releases funds OR disputes
|
|
26
|
-
* 5. Arbiter resolves disputes if needed
|
|
27
|
-
*
|
|
28
|
-
* ⚠️ IMPORTANT: Amount revealed only on release/refund
|
|
15
|
+
* @dev Flow: createEscrow() → fundEscrow() → release()/requestRefund()/raiseDispute()
|
|
16
|
+
* Multi-party agreement with arbiter for disputes.
|
|
29
17
|
*/
|
|
30
18
|
contract EncryptedEscrow is ZamaEthereumConfig {
|
|
31
|
-
// ==================== TYPES ====================
|
|
32
|
-
|
|
33
19
|
enum EscrowState {
|
|
34
20
|
Created, // Escrow created, awaiting deposit
|
|
35
21
|
Funded, // Funds deposited
|
|
@@ -49,8 +35,6 @@ contract EncryptedEscrow is ZamaEthereumConfig {
|
|
|
49
35
|
uint256 deadline;
|
|
50
36
|
}
|
|
51
37
|
|
|
52
|
-
// ==================== STATE ====================
|
|
53
|
-
|
|
54
38
|
/// Contract owner
|
|
55
39
|
address public owner;
|
|
56
40
|
|
|
@@ -63,8 +47,6 @@ contract EncryptedEscrow is ZamaEthereumConfig {
|
|
|
63
47
|
/// Arbiter fee percentage (default 1%)
|
|
64
48
|
uint256 public arbiterFeePercent;
|
|
65
49
|
|
|
66
|
-
// ==================== EVENTS ====================
|
|
67
|
-
|
|
68
50
|
/// @notice Emitted when escrow is created
|
|
69
51
|
/// @param escrowId ID of the escrow
|
|
70
52
|
/// @param buyer Address of buyer
|
|
@@ -100,8 +82,6 @@ contract EncryptedEscrow is ZamaEthereumConfig {
|
|
|
100
82
|
/// @param winner Address favored in resolution
|
|
101
83
|
event DisputeResolved(uint256 indexed escrowId, address indexed winner);
|
|
102
84
|
|
|
103
|
-
// ==================== CONSTRUCTOR ====================
|
|
104
|
-
|
|
105
85
|
/// @notice Creates the escrow contract
|
|
106
86
|
/// @param _arbiterFeePercent Fee percentage for arbiter (0-10)
|
|
107
87
|
constructor(uint256 _arbiterFeePercent) {
|
|
@@ -110,8 +90,6 @@ contract EncryptedEscrow is ZamaEthereumConfig {
|
|
|
110
90
|
arbiterFeePercent = _arbiterFeePercent;
|
|
111
91
|
}
|
|
112
92
|
|
|
113
|
-
// ==================== ESCROW CREATION ====================
|
|
114
|
-
|
|
115
93
|
/// @notice Create a new escrow with encrypted amount
|
|
116
94
|
/// @param seller Address of the seller
|
|
117
95
|
/// @param arbiter Address of dispute arbiter
|
|
@@ -172,8 +150,6 @@ contract EncryptedEscrow is ZamaEthereumConfig {
|
|
|
172
150
|
emit EscrowFunded(escrowId, msg.value);
|
|
173
151
|
}
|
|
174
152
|
|
|
175
|
-
// ==================== RELEASE / REFUND ====================
|
|
176
|
-
|
|
177
153
|
/// @notice Release funds to seller
|
|
178
154
|
/// @dev Only buyer can release after delivery
|
|
179
155
|
/// @param escrowId ID of escrow to release
|
|
@@ -211,8 +187,6 @@ contract EncryptedEscrow is ZamaEthereumConfig {
|
|
|
211
187
|
emit FundsRefunded(escrowId, escrow.buyer);
|
|
212
188
|
}
|
|
213
189
|
|
|
214
|
-
// ==================== DISPUTE RESOLUTION ====================
|
|
215
|
-
|
|
216
190
|
/// @notice Raise a dispute
|
|
217
191
|
/// @param escrowId ID of escrow
|
|
218
192
|
function raiseDispute(uint256 escrowId) external {
|
|
@@ -264,8 +238,6 @@ contract EncryptedEscrow is ZamaEthereumConfig {
|
|
|
264
238
|
emit DisputeResolved(escrowId, winner);
|
|
265
239
|
}
|
|
266
240
|
|
|
267
|
-
// ==================== VIEW FUNCTIONS ====================
|
|
268
|
-
|
|
269
241
|
/// @notice Get escrow details
|
|
270
242
|
function getEscrow(
|
|
271
243
|
uint256 escrowId
|
|
@@ -5,33 +5,18 @@ import {FHE, euint64, ebool, externalEuint8} from "@fhevm/solidity/lib/FHE.sol";
|
|
|
5
5
|
import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* @notice Hidden Voting with encrypted ballots and homomorphic tallying
|
|
8
|
+
* @notice Hidden Voting with encrypted ballots and homomorphic tallying - individual votes stay private forever!
|
|
9
9
|
*
|
|
10
|
-
* @dev
|
|
11
|
-
*
|
|
12
|
-
* - Homomorphic addition for vote tallying
|
|
13
|
-
* - FHE.makePubliclyDecryptable() for revealing final results
|
|
14
|
-
* - Privacy: individual votes remain encrypted forever
|
|
15
|
-
*
|
|
16
|
-
* Flow:
|
|
17
|
-
* 1. Owner creates a proposal with voting duration
|
|
18
|
-
* 2. Eligible voters submit encrypted votes (0 or 1)
|
|
19
|
-
* 3. Votes are homomorphically added to running totals
|
|
20
|
-
* 4. After voting ends, owner closes and results become decryptable
|
|
21
|
-
* 5. Anyone can reveal final Yes/No counts with valid proof
|
|
10
|
+
* @dev Flow: vote() → closeVoting() → revealResults()
|
|
11
|
+
* ⚡ Gas: Each vote costs ~200k gas (FHE.add + FHE.select operations)
|
|
22
12
|
*/
|
|
23
13
|
contract HiddenVoting is ZamaEthereumConfig {
|
|
24
|
-
// ==================== TYPES ====================
|
|
25
|
-
|
|
26
14
|
enum VotingState {
|
|
27
15
|
Active, // Accepting votes
|
|
28
16
|
Closed, // Voting ended, pending reveal
|
|
29
17
|
Revealed // Results revealed on-chain
|
|
30
18
|
}
|
|
31
19
|
|
|
32
|
-
// ==================== STATE ====================
|
|
33
|
-
|
|
34
|
-
/// Voting owner (deployer)
|
|
35
20
|
address public owner;
|
|
36
21
|
|
|
37
22
|
/// Current voting state
|
|
@@ -59,9 +44,7 @@ contract HiddenVoting is ZamaEthereumConfig {
|
|
|
59
44
|
uint64 public revealedYesVotes;
|
|
60
45
|
uint64 public revealedNoVotes;
|
|
61
46
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
/// @notice Emitted when a vote is cast
|
|
47
|
+
/// Emitted when a vote is cast
|
|
65
48
|
/// @param voter Address of the voter
|
|
66
49
|
event VoteCast(address indexed voter);
|
|
67
50
|
|
|
@@ -75,19 +58,11 @@ contract HiddenVoting is ZamaEthereumConfig {
|
|
|
75
58
|
/// @param noVotes Final No vote count
|
|
76
59
|
event ResultsRevealed(uint64 yesVotes, uint64 noVotes);
|
|
77
60
|
|
|
78
|
-
// ==================== MODIFIERS ====================
|
|
79
|
-
|
|
80
|
-
/// @dev Restricts function to owner only
|
|
81
61
|
modifier onlyOwner() {
|
|
82
62
|
require(msg.sender == owner, "Only owner can call");
|
|
83
63
|
_;
|
|
84
64
|
}
|
|
85
65
|
|
|
86
|
-
// ==================== CONSTRUCTOR ====================
|
|
87
|
-
|
|
88
|
-
/// @notice Creates a new voting proposal
|
|
89
|
-
/// @param _proposal Description of what is being voted on
|
|
90
|
-
/// @param _durationSeconds How long voting is open
|
|
91
66
|
constructor(string memory _proposal, uint256 _durationSeconds) {
|
|
92
67
|
require(bytes(_proposal).length > 0, "Empty proposal");
|
|
93
68
|
require(_durationSeconds > 0, "Duration must be positive");
|
|
@@ -97,18 +72,15 @@ contract HiddenVoting is ZamaEthereumConfig {
|
|
|
97
72
|
endTime = block.timestamp + _durationSeconds;
|
|
98
73
|
votingState = VotingState.Active;
|
|
99
74
|
|
|
100
|
-
//
|
|
75
|
+
// Initialize encrypted counters
|
|
101
76
|
_yesVotes = FHE.asEuint64(0);
|
|
102
77
|
_noVotes = FHE.asEuint64(0);
|
|
103
78
|
FHE.allowThis(_yesVotes);
|
|
104
79
|
FHE.allowThis(_noVotes);
|
|
105
80
|
}
|
|
106
81
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
/// @notice Cast an encrypted vote
|
|
110
|
-
/// @dev Vote must be 0 (No) or 1 (Yes). Values > 1 are treated as Yes.
|
|
111
|
-
/// @param encryptedVote Encrypted vote (0 or 1)
|
|
82
|
+
/// @notice Cast an encrypted vote (0=No, 1=Yes)
|
|
83
|
+
/// @dev Homomorphic tallying: votes added without decryption!
|
|
112
84
|
/// @param inputProof Proof validating the encrypted input
|
|
113
85
|
function vote(
|
|
114
86
|
externalEuint8 encryptedVote,
|
|
@@ -118,14 +90,13 @@ contract HiddenVoting is ZamaEthereumConfig {
|
|
|
118
90
|
require(block.timestamp < endTime, "Voting has ended");
|
|
119
91
|
require(!hasVoted[msg.sender], "Already voted");
|
|
120
92
|
|
|
121
|
-
//
|
|
122
|
-
// Note: Using euint8 for vote, but storing as euint64 for addition
|
|
93
|
+
// Convert vote and normalize to 0 or 1
|
|
123
94
|
euint64 voteValue = FHE.asEuint64(
|
|
124
95
|
FHE.fromExternal(encryptedVote, inputProof)
|
|
125
96
|
);
|
|
126
97
|
|
|
127
|
-
//
|
|
128
|
-
//
|
|
98
|
+
// 🧮 Why homomorphic? Votes are tallied WITHOUT decrypting individual ballots!
|
|
99
|
+
// Individual votes remain private forever
|
|
129
100
|
ebool isYes = FHE.gt(voteValue, FHE.asEuint64(0));
|
|
130
101
|
|
|
131
102
|
// ➕ Add to Yes counter if vote is Yes (1)
|
|
@@ -136,7 +107,6 @@ contract HiddenVoting is ZamaEthereumConfig {
|
|
|
136
107
|
);
|
|
137
108
|
_yesVotes = FHE.add(_yesVotes, yesIncrement);
|
|
138
109
|
|
|
139
|
-
// ➕ Add to No counter if vote is No (0)
|
|
140
110
|
euint64 noIncrement = FHE.select(
|
|
141
111
|
isYes,
|
|
142
112
|
FHE.asEuint64(0),
|
|
@@ -144,26 +114,21 @@ contract HiddenVoting is ZamaEthereumConfig {
|
|
|
144
114
|
);
|
|
145
115
|
_noVotes = FHE.add(_noVotes, noIncrement);
|
|
146
116
|
|
|
147
|
-
// ✅ Update permissions for new values
|
|
148
|
-
FHE.allowThis(_yesVotes);
|
|
149
117
|
FHE.allowThis(_noVotes);
|
|
150
118
|
|
|
151
|
-
// 📋 Record that this address has voted
|
|
152
119
|
hasVoted[msg.sender] = true;
|
|
153
120
|
voterCount++;
|
|
154
121
|
|
|
155
122
|
emit VoteCast(msg.sender);
|
|
156
123
|
}
|
|
157
124
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
/// @notice Close voting and prepare results for decryption
|
|
125
|
+
/// @notice Close voting and mark results for decryption
|
|
161
126
|
/// @dev Only owner can call after voting period ends
|
|
162
127
|
function closeVoting() external onlyOwner {
|
|
163
128
|
require(votingState == VotingState.Active, "Voting not active");
|
|
164
129
|
require(block.timestamp >= endTime, "Voting not yet ended");
|
|
165
130
|
|
|
166
|
-
//
|
|
131
|
+
// Mark totals for public decryption via relayer
|
|
167
132
|
FHE.makePubliclyDecryptable(_yesVotes);
|
|
168
133
|
FHE.makePubliclyDecryptable(_noVotes);
|
|
169
134
|
|
|
@@ -182,15 +147,15 @@ contract HiddenVoting is ZamaEthereumConfig {
|
|
|
182
147
|
) external {
|
|
183
148
|
require(votingState == VotingState.Closed, "Voting not closed");
|
|
184
149
|
|
|
185
|
-
//
|
|
150
|
+
// Build handle array for signature verification
|
|
186
151
|
bytes32[] memory cts = new bytes32[](2);
|
|
187
152
|
cts[0] = FHE.toBytes32(_yesVotes);
|
|
188
153
|
cts[1] = FHE.toBytes32(_noVotes);
|
|
189
154
|
|
|
190
|
-
//
|
|
155
|
+
// Verify KMS proof (reverts if invalid)
|
|
191
156
|
FHE.checkSignatures(cts, abiEncodedResults, decryptionProof);
|
|
192
157
|
|
|
193
|
-
//
|
|
158
|
+
// Decode verified plaintext results
|
|
194
159
|
(uint64 yesCount, uint64 noCount) = abi.decode(
|
|
195
160
|
abiEncodedResults,
|
|
196
161
|
(uint64, uint64)
|
|
@@ -203,15 +168,11 @@ contract HiddenVoting is ZamaEthereumConfig {
|
|
|
203
168
|
emit ResultsRevealed(yesCount, noCount);
|
|
204
169
|
}
|
|
205
170
|
|
|
206
|
-
// ==================== VIEW FUNCTIONS ====================
|
|
207
|
-
|
|
208
|
-
/// @notice Get encrypted Yes votes handle (after voting closed)
|
|
209
171
|
function getEncryptedYesVotes() external view returns (euint64) {
|
|
210
172
|
require(votingState != VotingState.Active, "Voting still active");
|
|
211
173
|
return _yesVotes;
|
|
212
174
|
}
|
|
213
175
|
|
|
214
|
-
/// @notice Get encrypted No votes handle (after voting closed)
|
|
215
176
|
function getEncryptedNoVotes() external view returns (euint64) {
|
|
216
177
|
require(votingState != VotingState.Active, "Voting still active");
|
|
217
178
|
return _noVotes;
|
|
@@ -14,22 +14,11 @@ import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
|
|
|
14
14
|
/**
|
|
15
15
|
* @notice Private KYC - verify identity without revealing personal data!
|
|
16
16
|
*
|
|
17
|
-
* @dev
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* - Only boolean results revealed: "Is 18+?", "Is from allowed country?"
|
|
21
|
-
* - Personal data never leaves encrypted form
|
|
22
|
-
*
|
|
23
|
-
* Verification types:
|
|
24
|
-
* 1. Age verification: isAbove18(), isAbove21()
|
|
25
|
-
* 2. Country check: isFromAllowedCountry()
|
|
26
|
-
* 3. Credit score: hasCreditScoreAbove(threshold)
|
|
27
|
-
*
|
|
28
|
-
* ⚠️ IMPORTANT: This is a demo - production KYC needs trusted attesters!
|
|
17
|
+
* @dev Flow: submitKYC() → verifyAge18()/verifyGoodCredit()/etc.
|
|
18
|
+
* Returns encrypted booleans: "Is 18+?", "Good credit?" without revealing actual values.
|
|
19
|
+
* ⚠️ Production KYC needs trusted attesters!
|
|
29
20
|
*/
|
|
30
21
|
contract PrivateKYC is ZamaEthereumConfig {
|
|
31
|
-
// ==================== TYPES ====================
|
|
32
|
-
|
|
33
22
|
struct Identity {
|
|
34
23
|
euint8 age; // 0-255 years
|
|
35
24
|
euint8 countryCode; // ISO 3166-1 numeric (1-255)
|
|
@@ -38,8 +27,6 @@ contract PrivateKYC is ZamaEthereumConfig {
|
|
|
38
27
|
uint256 verifiedAt; // Timestamp of verification
|
|
39
28
|
}
|
|
40
29
|
|
|
41
|
-
// ==================== STATE ====================
|
|
42
|
-
|
|
43
30
|
/// Contract owner (KYC admin)
|
|
44
31
|
address public owner;
|
|
45
32
|
|
|
@@ -56,8 +43,6 @@ contract PrivateKYC is ZamaEthereumConfig {
|
|
|
56
43
|
/// Minimum credit score for "good" rating
|
|
57
44
|
uint16 public constant MIN_CREDIT_GOOD = 700;
|
|
58
45
|
|
|
59
|
-
// ==================== EVENTS ====================
|
|
60
|
-
|
|
61
46
|
/// @notice Emitted when user submits KYC
|
|
62
47
|
/// @param user Address of user
|
|
63
48
|
event KYCSubmitted(address indexed user);
|
|
@@ -76,8 +61,6 @@ contract PrivateKYC is ZamaEthereumConfig {
|
|
|
76
61
|
/// @param minAge Minimum age required
|
|
77
62
|
event AgeVerificationRequested(address indexed user, uint8 minAge);
|
|
78
63
|
|
|
79
|
-
// ==================== MODIFIERS ====================
|
|
80
|
-
|
|
81
64
|
modifier onlyOwner() {
|
|
82
65
|
require(msg.sender == owner, "Only owner");
|
|
83
66
|
_;
|
|
@@ -88,8 +71,6 @@ contract PrivateKYC is ZamaEthereumConfig {
|
|
|
88
71
|
_;
|
|
89
72
|
}
|
|
90
73
|
|
|
91
|
-
// ==================== CONSTRUCTOR ====================
|
|
92
|
-
|
|
93
74
|
/// @notice Creates the KYC contract
|
|
94
75
|
/// @param _allowedCountryCodes Initial list of allowed country codes
|
|
95
76
|
constructor(uint8[] memory _allowedCountryCodes) {
|
|
@@ -101,8 +82,6 @@ contract PrivateKYC is ZamaEthereumConfig {
|
|
|
101
82
|
}
|
|
102
83
|
}
|
|
103
84
|
|
|
104
|
-
// ==================== KYC SUBMISSION ====================
|
|
105
|
-
|
|
106
85
|
/// @notice Submit encrypted KYC data
|
|
107
86
|
/// @param encAge Encrypted age
|
|
108
87
|
/// @param encCountry Encrypted country code
|
|
@@ -147,8 +126,6 @@ contract PrivateKYC is ZamaEthereumConfig {
|
|
|
147
126
|
emit KYCRevoked(msg.sender);
|
|
148
127
|
}
|
|
149
128
|
|
|
150
|
-
// ==================== VERIFICATION FUNCTIONS ====================
|
|
151
|
-
|
|
152
129
|
/// @notice Check if user is 18 or older
|
|
153
130
|
/// @param user Address to verify
|
|
154
131
|
/// @return result Encrypted boolean result
|
|
@@ -232,8 +209,6 @@ contract PrivateKYC is ZamaEthereumConfig {
|
|
|
232
209
|
return result;
|
|
233
210
|
}
|
|
234
211
|
|
|
235
|
-
// ==================== DECRYPTABLE VERIFICATION ====================
|
|
236
|
-
|
|
237
212
|
/// @notice Request age verification with public result
|
|
238
213
|
/// @dev Makes the result publicly decryptable
|
|
239
214
|
/// @param user Address to verify
|
|
@@ -252,8 +227,6 @@ contract PrivateKYC is ZamaEthereumConfig {
|
|
|
252
227
|
return result;
|
|
253
228
|
}
|
|
254
229
|
|
|
255
|
-
// ==================== ADMIN FUNCTIONS ====================
|
|
256
|
-
|
|
257
230
|
/// @notice Update country allowlist
|
|
258
231
|
/// @param countryCode Country code to update
|
|
259
232
|
/// @param allowed Whether to allow or deny
|
|
@@ -278,8 +251,6 @@ contract PrivateKYC is ZamaEthereumConfig {
|
|
|
278
251
|
}
|
|
279
252
|
}
|
|
280
253
|
|
|
281
|
-
// ==================== VIEW FUNCTIONS ====================
|
|
282
|
-
|
|
283
254
|
/// @notice Check if user has submitted KYC
|
|
284
255
|
function hasSubmittedKYC(address user) external view returns (bool) {
|
|
285
256
|
return _identities[user].isVerified;
|
|
@@ -12,24 +12,10 @@ import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
|
|
|
12
12
|
/**
|
|
13
13
|
* @notice Private Payroll system - salaries stay encrypted, only employees see their own!
|
|
14
14
|
*
|
|
15
|
-
* @dev
|
|
16
|
-
*
|
|
17
|
-
* - Bulk payments without revealing individual amounts
|
|
18
|
-
* - Each employee can decrypt only their own salary
|
|
19
|
-
* - Total payroll visible to employer for budgeting
|
|
20
|
-
*
|
|
21
|
-
* Flow:
|
|
22
|
-
* 1. Employer adds employees with encrypted salaries
|
|
23
|
-
* 2. Employer funds the contract
|
|
24
|
-
* 3. Employer calls payAll() to process all salaries
|
|
25
|
-
* 4. Employees can view (decrypt) their own salary
|
|
26
|
-
*
|
|
27
|
-
* ⚠️ IMPORTANT: Uses cumulative encrypted sum for total payroll tracking
|
|
15
|
+
* @dev Flow: addEmployee() → fund() → processPayment()
|
|
16
|
+
* Each employee can decrypt only their own salary.
|
|
28
17
|
*/
|
|
29
18
|
contract PrivatePayroll is ZamaEthereumConfig {
|
|
30
|
-
// ==================== STATE ====================
|
|
31
|
-
|
|
32
|
-
/// Contract owner (employer)
|
|
33
19
|
address public employer;
|
|
34
20
|
|
|
35
21
|
/// List of employee addresses
|
|
@@ -50,9 +36,7 @@ contract PrivatePayroll is ZamaEthereumConfig {
|
|
|
50
36
|
/// Payment period in seconds (default: 30 days)
|
|
51
37
|
uint256 public paymentPeriod;
|
|
52
38
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
/// @notice Emitted when a new employee is added
|
|
39
|
+
/// Emitted when a new employee is added
|
|
56
40
|
/// @param employee Address of the employee
|
|
57
41
|
event EmployeeAdded(address indexed employee);
|
|
58
42
|
|
|
@@ -73,8 +57,6 @@ contract PrivatePayroll is ZamaEthereumConfig {
|
|
|
73
57
|
/// @param amount Amount funded
|
|
74
58
|
event ContractFunded(uint256 amount);
|
|
75
59
|
|
|
76
|
-
// ==================== MODIFIERS ====================
|
|
77
|
-
|
|
78
60
|
modifier onlyEmployer() {
|
|
79
61
|
require(msg.sender == employer, "Only employer");
|
|
80
62
|
_;
|
|
@@ -85,10 +67,6 @@ contract PrivatePayroll is ZamaEthereumConfig {
|
|
|
85
67
|
_;
|
|
86
68
|
}
|
|
87
69
|
|
|
88
|
-
// ==================== CONSTRUCTOR ====================
|
|
89
|
-
|
|
90
|
-
/// @notice Creates a new private payroll contract
|
|
91
|
-
/// @param _paymentPeriod Time between payments in seconds
|
|
92
70
|
constructor(uint256 _paymentPeriod) {
|
|
93
71
|
employer = msg.sender;
|
|
94
72
|
paymentPeriod = _paymentPeriod > 0 ? _paymentPeriod : 30 days;
|
|
@@ -96,8 +74,6 @@ contract PrivatePayroll is ZamaEthereumConfig {
|
|
|
96
74
|
FHE.allowThis(_totalSalaries);
|
|
97
75
|
}
|
|
98
76
|
|
|
99
|
-
// ==================== EMPLOYEE MANAGEMENT ====================
|
|
100
|
-
|
|
101
77
|
/// @notice Add a new employee with encrypted salary
|
|
102
78
|
/// @param employee Address of the employee
|
|
103
79
|
/// @param encryptedSalary Encrypted salary amount
|
|
@@ -190,8 +166,6 @@ contract PrivatePayroll is ZamaEthereumConfig {
|
|
|
190
166
|
emit EmployeeRemoved(employee);
|
|
191
167
|
}
|
|
192
168
|
|
|
193
|
-
// ==================== PAYMENTS ====================
|
|
194
|
-
|
|
195
169
|
/// @notice Fund the contract for payroll
|
|
196
170
|
function fund() external payable onlyEmployer {
|
|
197
171
|
require(msg.value > 0, "Must send funds");
|
|
@@ -231,9 +205,7 @@ contract PrivatePayroll is ZamaEthereumConfig {
|
|
|
231
205
|
emit PaymentProcessed(employee, block.timestamp);
|
|
232
206
|
}
|
|
233
207
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
/// @notice Get encrypted salary handle for an employee
|
|
208
|
+
/// @notice Get encrypted salary handle for employee
|
|
237
209
|
/// @dev Only callable by the employee themselves
|
|
238
210
|
function getMySalary() external view onlyEmployee returns (euint64) {
|
|
239
211
|
return _salaries[msg.sender];
|
|
@@ -276,8 +248,6 @@ contract PrivatePayroll is ZamaEthereumConfig {
|
|
|
276
248
|
return (employees.length, address(this).balance, paymentPeriod);
|
|
277
249
|
}
|
|
278
250
|
|
|
279
|
-
// ==================== RECEIVE ====================
|
|
280
|
-
|
|
281
251
|
/// @notice Accept ETH deposits
|
|
282
252
|
receive() external payable {
|
|
283
253
|
emit ContractFunded(msg.value);
|