@zentity/fhevm-contracts 0.1.0

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 (83) hide show
  1. package/README.md +160 -0
  2. package/abi/ComplianceRules.json +352 -0
  3. package/abi/CompliantERC20.json +493 -0
  4. package/abi/IdentityRegistry.json +712 -0
  5. package/abi/index.d.ts +3 -0
  6. package/abi/index.js +9 -0
  7. package/contracts/ARCHITECTURE.md +66 -0
  8. package/contracts/ARCHITECTURE_EXPLAINER.md +77 -0
  9. package/contracts/compliance/ComplianceRules.sol +255 -0
  10. package/contracts/core/IdentityRegistry.sol +352 -0
  11. package/contracts/interfaces/IIdentityRegistry.sol +226 -0
  12. package/contracts/mocks/.gitkeep +0 -0
  13. package/contracts/tokens/CompliantERC20.sol +379 -0
  14. package/deployments/hardhat/addresses.json +20 -0
  15. package/deployments/localhost/.chainId +1 -0
  16. package/deployments/localhost/ComplianceRules.json +662 -0
  17. package/deployments/localhost/CompliantERC20.json +888 -0
  18. package/deployments/localhost/IdentityRegistry.json +1093 -0
  19. package/deployments/localhost/solcInputs/e36969353329df673b4fae03d39e01c4.json +60 -0
  20. package/deployments/sepolia/.chainId +1 -0
  21. package/deployments/sepolia/.gitkeep +0 -0
  22. package/deployments/sepolia/ComplianceRules.json +662 -0
  23. package/deployments/sepolia/CompliantERC20.json +888 -0
  24. package/deployments/sepolia/IdentityRegistry.json +1093 -0
  25. package/deployments/sepolia/solcInputs/93d280ff0d4e798a18947a9ed6015031.json +60 -0
  26. package/dist/index.d.ts +459 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +135 -0
  29. package/package.json +110 -0
  30. package/typechain-types/@fhevm/index.ts +5 -0
  31. package/typechain-types/@fhevm/solidity/config/ZamaConfig.sol/ZamaConfig.ts +69 -0
  32. package/typechain-types/@fhevm/solidity/config/ZamaConfig.sol/ZamaEthereumConfig.ts +90 -0
  33. package/typechain-types/@fhevm/solidity/config/ZamaConfig.sol/index.ts +5 -0
  34. package/typechain-types/@fhevm/solidity/config/index.ts +5 -0
  35. package/typechain-types/@fhevm/solidity/index.ts +7 -0
  36. package/typechain-types/@fhevm/solidity/lib/FHE.sol/FHE.ts +112 -0
  37. package/typechain-types/@fhevm/solidity/lib/FHE.sol/IKMSVerifier.ts +108 -0
  38. package/typechain-types/@fhevm/solidity/lib/FHE.sol/index.ts +5 -0
  39. package/typechain-types/@fhevm/solidity/lib/Impl.sol/IACL.ts +190 -0
  40. package/typechain-types/@fhevm/solidity/lib/Impl.sol/IFHEVMExecutor.ts +623 -0
  41. package/typechain-types/@fhevm/solidity/lib/Impl.sol/IInputVerifier.ts +90 -0
  42. package/typechain-types/@fhevm/solidity/lib/Impl.sol/index.ts +6 -0
  43. package/typechain-types/@fhevm/solidity/lib/index.ts +7 -0
  44. package/typechain-types/common.ts +131 -0
  45. package/typechain-types/contracts/compliance/ComplianceRules.ts +479 -0
  46. package/typechain-types/contracts/compliance/index.ts +4 -0
  47. package/typechain-types/contracts/core/IdentityRegistry.ts +874 -0
  48. package/typechain-types/contracts/core/index.ts +4 -0
  49. package/typechain-types/contracts/index.ts +11 -0
  50. package/typechain-types/contracts/interfaces/IIdentityRegistry.ts +798 -0
  51. package/typechain-types/contracts/interfaces/index.ts +4 -0
  52. package/typechain-types/contracts/tokens/CompliantERC20.sol/CompliantERC20.ts +572 -0
  53. package/typechain-types/contracts/tokens/CompliantERC20.sol/IComplianceChecker.ts +95 -0
  54. package/typechain-types/contracts/tokens/CompliantERC20.sol/index.ts +5 -0
  55. package/typechain-types/contracts/tokens/index.ts +5 -0
  56. package/typechain-types/factories/@fhevm/index.ts +4 -0
  57. package/typechain-types/factories/@fhevm/solidity/config/ZamaConfig.sol/ZamaConfig__factory.ts +69 -0
  58. package/typechain-types/factories/@fhevm/solidity/config/ZamaConfig.sol/ZamaEthereumConfig__factory.ts +43 -0
  59. package/typechain-types/factories/@fhevm/solidity/config/ZamaConfig.sol/index.ts +5 -0
  60. package/typechain-types/factories/@fhevm/solidity/config/index.ts +4 -0
  61. package/typechain-types/factories/@fhevm/solidity/index.ts +5 -0
  62. package/typechain-types/factories/@fhevm/solidity/lib/FHE.sol/FHE__factory.ts +88 -0
  63. package/typechain-types/factories/@fhevm/solidity/lib/FHE.sol/IKMSVerifier__factory.ts +54 -0
  64. package/typechain-types/factories/@fhevm/solidity/lib/FHE.sol/index.ts +5 -0
  65. package/typechain-types/factories/@fhevm/solidity/lib/Impl.sol/IACL__factory.ts +121 -0
  66. package/typechain-types/factories/@fhevm/solidity/lib/Impl.sol/IFHEVMExecutor__factory.ts +810 -0
  67. package/typechain-types/factories/@fhevm/solidity/lib/Impl.sol/IInputVerifier__factory.ts +32 -0
  68. package/typechain-types/factories/@fhevm/solidity/lib/Impl.sol/index.ts +6 -0
  69. package/typechain-types/factories/@fhevm/solidity/lib/index.ts +5 -0
  70. package/typechain-types/factories/contracts/compliance/ComplianceRules__factory.ts +437 -0
  71. package/typechain-types/factories/contracts/compliance/index.ts +4 -0
  72. package/typechain-types/factories/contracts/core/IdentityRegistry__factory.ts +777 -0
  73. package/typechain-types/factories/contracts/core/index.ts +4 -0
  74. package/typechain-types/factories/contracts/index.ts +7 -0
  75. package/typechain-types/factories/contracts/interfaces/IIdentityRegistry__factory.ts +640 -0
  76. package/typechain-types/factories/contracts/interfaces/index.ts +4 -0
  77. package/typechain-types/factories/contracts/tokens/CompliantERC20.sol/CompliantERC20__factory.ts +581 -0
  78. package/typechain-types/factories/contracts/tokens/CompliantERC20.sol/IComplianceChecker__factory.ts +44 -0
  79. package/typechain-types/factories/contracts/tokens/CompliantERC20.sol/index.ts +5 -0
  80. package/typechain-types/factories/contracts/tokens/index.ts +4 -0
  81. package/typechain-types/factories/index.ts +5 -0
  82. package/typechain-types/hardhat.d.ts +261 -0
  83. package/typechain-types/index.ts +32 -0
@@ -0,0 +1,352 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // solhint-disable not-rely-on-time
3
+ pragma solidity ^0.8.27;
4
+
5
+ import {FHE, euint8, euint16, ebool, externalEuint8, externalEuint16, externalEbool} from "@fhevm/solidity/lib/FHE.sol";
6
+ import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
7
+ import {IIdentityRegistry} from "../interfaces/IIdentityRegistry.sol";
8
+
9
+ /**
10
+ * @title IdentityRegistry
11
+ * @author Gustavo Valverde
12
+ * @notice On-chain encrypted identity registry for KYC or compliance platforms
13
+ * @dev Part of zentity-fhevm-contracts - Builder Track
14
+ *
15
+ * @custom:category identity
16
+ * @custom:concept Storing encrypted identity attributes (euint8, euint16, ebool)
17
+ * @custom:difficulty intermediate
18
+ *
19
+ * This contract maintains an encrypted identity registry where authorized registrars
20
+ * (typically a backend service) can attest to user identity attributes. All sensitive data
21
+ * remains encrypted on-chain.
22
+ *
23
+ * Key patterns demonstrated:
24
+ * 1. Multiple encrypted types (euint8, euint16, ebool)
25
+ * 2. Role-based access control (registrars)
26
+ * 3. Struct-like data storage with mappings
27
+ * 4. FHE permission management (allowThis, allow)
28
+ */
29
+ contract IdentityRegistry is IIdentityRegistry, ZamaEthereumConfig {
30
+ // ============ Encrypted Identity Attributes ============
31
+
32
+ /// @notice Encrypted birth year offset from 1900
33
+ mapping(address user => euint8 birthYearOffset) private birthYearOffsets;
34
+
35
+ /// @notice Encrypted country code (ISO 3166-1 numeric)
36
+ mapping(address user => euint16 countryCode) private countryCodes;
37
+
38
+ /// @notice Encrypted KYC verification level (0-5)
39
+ mapping(address user => euint8 kycLevel) private kycLevels;
40
+
41
+ /// @notice Encrypted blacklist status
42
+ mapping(address user => ebool blacklisted) private isBlacklisted;
43
+
44
+ /// @notice Timestamp of last attestation
45
+ mapping(address user => uint256 timestamp) public attestationTimestamp;
46
+
47
+ /// @notice Current attestation id for a user (0 if not attested)
48
+ mapping(address user => uint256 attestationId) public currentAttestationId;
49
+
50
+ /// @notice Latest attestation id ever issued for a user
51
+ mapping(address user => uint256 attestationId) public latestAttestationId;
52
+
53
+ /// @notice Attestation metadata for auditability
54
+ struct AttestationMetadata {
55
+ uint256 timestamp;
56
+ uint256 revokedAt;
57
+ address registrar;
58
+ }
59
+
60
+ /// @notice Attestation history by user and attestation id
61
+ mapping(address user => mapping(uint256 attestationId => AttestationMetadata)) private attestationHistory;
62
+
63
+ /// @notice Store verification results for external queries
64
+ mapping(bytes32 key => ebool result) private verificationResults;
65
+
66
+ // ============ Access Control ============
67
+
68
+ /// @notice Owner of the registry
69
+ address public owner;
70
+ /// @notice Pending owner for two-step ownership transfer
71
+ address public pendingOwner;
72
+
73
+ /// @notice Authorized registrars who can attest identities
74
+ mapping(address registrar => bool authorized) public registrars;
75
+
76
+ /// @notice Thrown when caller lacks permission for encrypted data
77
+ error AccessProhibited();
78
+
79
+ // ============ Modifiers ============
80
+
81
+ modifier onlyOwner() {
82
+ if (msg.sender != owner) revert OnlyOwner();
83
+ _;
84
+ }
85
+
86
+ modifier onlyRegistrar() {
87
+ if (!registrars[msg.sender]) revert OnlyRegistrar();
88
+ _;
89
+ }
90
+
91
+ // ============ Constructor ============
92
+
93
+ /// @notice Initializes the registry with the deployer as owner and initial registrar
94
+ constructor() {
95
+ owner = msg.sender;
96
+ registrars[msg.sender] = true;
97
+ emit RegistrarAdded(msg.sender);
98
+ }
99
+
100
+ // ============ Registrar Management ============
101
+
102
+ /// @inheritdoc IIdentityRegistry
103
+ function addRegistrar(address registrar) external onlyOwner {
104
+ registrars[registrar] = true;
105
+ emit RegistrarAdded(registrar);
106
+ }
107
+
108
+ /// @inheritdoc IIdentityRegistry
109
+ function removeRegistrar(address registrar) external onlyOwner {
110
+ registrars[registrar] = false;
111
+ emit RegistrarRemoved(registrar);
112
+ }
113
+
114
+ // ============ Identity Attestation ============
115
+
116
+ /// @inheritdoc IIdentityRegistry
117
+ function attestIdentity(
118
+ address user,
119
+ externalEuint8 encBirthYearOffset,
120
+ externalEuint16 encCountryCode,
121
+ externalEuint8 encKycLevel,
122
+ externalEbool encIsBlacklisted,
123
+ bytes calldata inputProof
124
+ ) external onlyRegistrar {
125
+ if (currentAttestationId[user] != 0) revert AlreadyAttested();
126
+
127
+ // Convert and store encrypted values
128
+ euint8 birthYear = FHE.fromExternal(encBirthYearOffset, inputProof);
129
+ euint16 country = FHE.fromExternal(encCountryCode, inputProof);
130
+ euint8 kyc = FHE.fromExternal(encKycLevel, inputProof);
131
+ ebool blacklisted = FHE.fromExternal(encIsBlacklisted, inputProof);
132
+
133
+ birthYearOffsets[user] = birthYear;
134
+ countryCodes[user] = country;
135
+ kycLevels[user] = kyc;
136
+ isBlacklisted[user] = blacklisted;
137
+
138
+ // Grant contract permission to all values
139
+ FHE.allowThis(birthYear);
140
+ FHE.allowThis(country);
141
+ FHE.allowThis(kyc);
142
+ FHE.allowThis(blacklisted);
143
+
144
+ // Grant user permission to their own data
145
+ FHE.allow(birthYear, user);
146
+ FHE.allow(country, user);
147
+ FHE.allow(kyc, user);
148
+ FHE.allow(blacklisted, user);
149
+
150
+ uint256 newAttestationId = latestAttestationId[user] + 1;
151
+ latestAttestationId[user] = newAttestationId;
152
+ currentAttestationId[user] = newAttestationId;
153
+ attestationHistory[user][newAttestationId] = AttestationMetadata({
154
+ timestamp: block.timestamp,
155
+ revokedAt: 0,
156
+ registrar: msg.sender
157
+ });
158
+ attestationTimestamp[user] = block.timestamp;
159
+
160
+ emit IdentityAttested(user, msg.sender);
161
+ emit IdentityAttestedDetailed(user, msg.sender, newAttestationId, block.timestamp);
162
+ }
163
+
164
+ /// @inheritdoc IIdentityRegistry
165
+ function revokeIdentity(address user) external onlyRegistrar {
166
+ uint256 attestationId = currentAttestationId[user];
167
+ if (attestationId == 0) revert NotAttested();
168
+
169
+ // Set encrypted values to encrypted zeros
170
+ birthYearOffsets[user] = FHE.asEuint8(0);
171
+ countryCodes[user] = FHE.asEuint16(0);
172
+ kycLevels[user] = FHE.asEuint8(0);
173
+ isBlacklisted[user] = FHE.asEbool(false);
174
+ attestationTimestamp[user] = 0;
175
+ currentAttestationId[user] = 0;
176
+
177
+ AttestationMetadata storage metadata = attestationHistory[user][attestationId];
178
+ if (metadata.revokedAt == 0) {
179
+ metadata.revokedAt = block.timestamp;
180
+ }
181
+
182
+ emit IdentityRevoked(user);
183
+ emit IdentityRevokedDetailed(user, msg.sender, attestationId, block.timestamp);
184
+ }
185
+
186
+ // ============ Encrypted Queries ============
187
+
188
+ /// @inheritdoc IIdentityRegistry
189
+ function getBirthYearOffset(address user) external view returns (euint8) {
190
+ if (attestationTimestamp[user] == 0) revert NotAttested();
191
+ if (!FHE.isSenderAllowed(birthYearOffsets[user])) revert AccessProhibited();
192
+ return birthYearOffsets[user];
193
+ }
194
+
195
+ /// @inheritdoc IIdentityRegistry
196
+ function getCountryCode(address user) external view returns (euint16) {
197
+ if (attestationTimestamp[user] == 0) revert NotAttested();
198
+ if (!FHE.isSenderAllowed(countryCodes[user])) revert AccessProhibited();
199
+ return countryCodes[user];
200
+ }
201
+
202
+ /// @inheritdoc IIdentityRegistry
203
+ function getKycLevel(address user) external view returns (euint8) {
204
+ if (attestationTimestamp[user] == 0) revert NotAttested();
205
+ if (!FHE.isSenderAllowed(kycLevels[user])) revert AccessProhibited();
206
+ return kycLevels[user];
207
+ }
208
+
209
+ /// @inheritdoc IIdentityRegistry
210
+ function getBlacklistStatus(address user) external view returns (ebool) {
211
+ if (attestationTimestamp[user] == 0) revert NotAttested();
212
+ if (!FHE.isSenderAllowed(isBlacklisted[user])) revert AccessProhibited();
213
+ return isBlacklisted[user];
214
+ }
215
+
216
+ // ============ Verification Helpers ============
217
+
218
+ /// @inheritdoc IIdentityRegistry
219
+ function hasMinKycLevel(address user, uint8 minLevel) external returns (ebool) {
220
+ if (attestationTimestamp[user] == 0) revert NotAttested();
221
+ if (!FHE.isSenderAllowed(kycLevels[user])) revert AccessProhibited();
222
+ ebool result = FHE.ge(kycLevels[user], FHE.asEuint8(minLevel));
223
+
224
+ // Store result for later retrieval
225
+ bytes32 key = keccak256(abi.encodePacked(user, uint8(0), uint256(minLevel)));
226
+ verificationResults[key] = result;
227
+
228
+ // Grant caller permission to decrypt the result
229
+ FHE.allowThis(result);
230
+ FHE.allow(result, msg.sender);
231
+
232
+ return result;
233
+ }
234
+
235
+ /// @inheritdoc IIdentityRegistry
236
+ function isFromCountry(address user, uint16 country) external returns (ebool) {
237
+ if (attestationTimestamp[user] == 0) revert NotAttested();
238
+ if (!FHE.isSenderAllowed(countryCodes[user])) revert AccessProhibited();
239
+ ebool result = FHE.eq(countryCodes[user], FHE.asEuint16(country));
240
+
241
+ // Store result for later retrieval
242
+ bytes32 key = keccak256(abi.encodePacked(user, uint8(1), uint256(country)));
243
+ verificationResults[key] = result;
244
+
245
+ // Grant caller permission to decrypt the result
246
+ FHE.allowThis(result);
247
+ FHE.allow(result, msg.sender);
248
+
249
+ return result;
250
+ }
251
+
252
+ /// @inheritdoc IIdentityRegistry
253
+ function isNotBlacklisted(address user) external returns (ebool) {
254
+ if (attestationTimestamp[user] == 0) revert NotAttested();
255
+ if (!FHE.isSenderAllowed(isBlacklisted[user])) revert AccessProhibited();
256
+ ebool result = FHE.not(isBlacklisted[user]);
257
+
258
+ // Store result for later retrieval
259
+ bytes32 key = keccak256(abi.encodePacked(user, uint8(2), uint256(0)));
260
+ verificationResults[key] = result;
261
+
262
+ // Grant caller permission to decrypt the result
263
+ FHE.allowThis(result);
264
+ FHE.allow(result, msg.sender);
265
+
266
+ return result;
267
+ }
268
+
269
+ // ============ Result Getters ============
270
+
271
+ /**
272
+ * @notice Get the last KYC level verification result
273
+ * @param user Address that was checked
274
+ * @param minLevel Level that was checked
275
+ * @return Encrypted boolean result
276
+ */
277
+ function getKycLevelResult(address user, uint8 minLevel) external view returns (ebool) {
278
+ bytes32 key = keccak256(abi.encodePacked(user, uint8(0), uint256(minLevel)));
279
+ ebool result = verificationResults[key];
280
+ if (!FHE.isSenderAllowed(result)) revert AccessProhibited();
281
+ return result;
282
+ }
283
+
284
+ /**
285
+ * @notice Get the last country verification result
286
+ * @param user Address that was checked
287
+ * @param country Country code that was checked
288
+ * @return Encrypted boolean result
289
+ */
290
+ function getCountryResult(address user, uint16 country) external view returns (ebool) {
291
+ bytes32 key = keccak256(abi.encodePacked(user, uint8(1), uint256(country)));
292
+ ebool result = verificationResults[key];
293
+ if (!FHE.isSenderAllowed(result)) revert AccessProhibited();
294
+ return result;
295
+ }
296
+
297
+ /**
298
+ * @notice Get the last blacklist verification result
299
+ * @param user Address that was checked
300
+ * @return Encrypted boolean result
301
+ */
302
+ function getBlacklistResult(address user) external view returns (ebool) {
303
+ bytes32 key = keccak256(abi.encodePacked(user, uint8(2), uint256(0)));
304
+ ebool result = verificationResults[key];
305
+ if (!FHE.isSenderAllowed(result)) revert AccessProhibited();
306
+ return result;
307
+ }
308
+
309
+ // ============ Access Control ============
310
+
311
+ /// @inheritdoc IIdentityRegistry
312
+ function transferOwnership(address newOwner) external onlyOwner {
313
+ if (newOwner == address(0)) revert InvalidOwner();
314
+ pendingOwner = newOwner;
315
+ emit OwnershipTransferStarted(owner, newOwner);
316
+ }
317
+
318
+ /// @inheritdoc IIdentityRegistry
319
+ function acceptOwnership() external {
320
+ if (msg.sender != pendingOwner) revert OnlyPendingOwner();
321
+ address previousOwner = owner;
322
+ owner = pendingOwner;
323
+ pendingOwner = address(0);
324
+ emit OwnershipTransferred(previousOwner, owner);
325
+ }
326
+
327
+ /// @inheritdoc IIdentityRegistry
328
+ function grantAccessTo(address grantee) external {
329
+ if (attestationTimestamp[msg.sender] == 0) revert NotAttested();
330
+
331
+ FHE.allow(birthYearOffsets[msg.sender], grantee);
332
+ FHE.allow(countryCodes[msg.sender], grantee);
333
+ FHE.allow(kycLevels[msg.sender], grantee);
334
+ FHE.allow(isBlacklisted[msg.sender], grantee);
335
+
336
+ emit AccessGranted(msg.sender, grantee);
337
+ }
338
+
339
+ /// @inheritdoc IIdentityRegistry
340
+ function isAttested(address user) external view returns (bool) {
341
+ return currentAttestationId[user] > 0;
342
+ }
343
+
344
+ /// @inheritdoc IIdentityRegistry
345
+ function getAttestationMetadata(
346
+ address user,
347
+ uint256 attestationId
348
+ ) external view returns (uint256 timestamp, uint256 revokedAt, address registrar) {
349
+ AttestationMetadata storage metadata = attestationHistory[user][attestationId];
350
+ return (metadata.timestamp, metadata.revokedAt, metadata.registrar);
351
+ }
352
+ }
@@ -0,0 +1,226 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.27;
3
+
4
+ import {euint8, euint16, ebool, externalEuint8, externalEuint16, externalEbool} from "@fhevm/solidity/lib/FHE.sol";
5
+
6
+ /**
7
+ * @title IIdentityRegistry
8
+ * @author Gustavo Valverde
9
+ * @notice Interface for the IdentityRegistry contract
10
+ * @dev Part of zentity-fhevm-contracts - Builder Track
11
+ *
12
+ * @custom:category identity
13
+ * @custom:concept Interface for encrypted identity storage and verification
14
+ * @custom:difficulty intermediate
15
+ */
16
+ interface IIdentityRegistry {
17
+ // ============ Events ============
18
+
19
+ /// @notice Emitted when a new registrar is authorized
20
+ /// @param registrar Address of the authorized registrar
21
+ event RegistrarAdded(address indexed registrar);
22
+
23
+ /// @notice Emitted when a registrar's authorization is revoked
24
+ /// @param registrar Address of the removed registrar
25
+ event RegistrarRemoved(address indexed registrar);
26
+
27
+ /// @notice Emitted when an identity is attested on-chain
28
+ /// @param user Address of the attested user
29
+ /// @param registrar Address of the registrar who performed attestation
30
+ event IdentityAttested(address indexed user, address indexed registrar);
31
+
32
+ /// @notice Emitted when an identity is attested on-chain with metadata
33
+ /// @param user Address of the attested user
34
+ /// @param registrar Address of the registrar who performed attestation
35
+ /// @param attestationId Monotonic attestation identifier for the user
36
+ /// @param timestamp Unix timestamp of attestation
37
+ event IdentityAttestedDetailed(
38
+ address indexed user,
39
+ address indexed registrar,
40
+ uint256 indexed attestationId,
41
+ uint256 timestamp
42
+ );
43
+
44
+ /// @notice Emitted when an identity attestation is revoked
45
+ /// @param user Address whose attestation was revoked
46
+ event IdentityRevoked(address indexed user);
47
+
48
+ /// @notice Emitted when an identity attestation is revoked with metadata
49
+ /// @param user Address whose attestation was revoked
50
+ /// @param registrar Address of the registrar who performed the revocation
51
+ /// @param attestationId Attestation identifier that was revoked
52
+ /// @param timestamp Unix timestamp of revocation
53
+ event IdentityRevokedDetailed(
54
+ address indexed user,
55
+ address indexed registrar,
56
+ uint256 indexed attestationId,
57
+ uint256 timestamp
58
+ );
59
+
60
+ /// @notice Emitted when a user grants access to their encrypted data
61
+ /// @param user Address of the user granting access
62
+ /// @param grantee Address receiving access permission
63
+ event AccessGranted(address indexed user, address indexed grantee);
64
+
65
+ /// @notice Emitted when ownership transfer is initiated
66
+ /// @param currentOwner Current owner address
67
+ /// @param pendingOwner Address that can accept ownership
68
+ event OwnershipTransferStarted(address indexed currentOwner, address indexed pendingOwner);
69
+
70
+ /// @notice Emitted when ownership transfer is completed
71
+ /// @param previousOwner Previous owner address
72
+ /// @param newOwner New owner address
73
+ event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
74
+
75
+ // ============ Errors ============
76
+
77
+ /// @notice Thrown when caller is not the contract owner
78
+ error OnlyOwner();
79
+
80
+ /// @notice Thrown when caller is not the pending owner
81
+ error OnlyPendingOwner();
82
+
83
+ /// @notice Thrown when new owner is the zero address
84
+ error InvalidOwner();
85
+
86
+ /// @notice Thrown when caller is not an authorized registrar
87
+ error OnlyRegistrar();
88
+
89
+ /// @notice Thrown when querying a user without attestation
90
+ error NotAttested();
91
+
92
+ /// @notice Thrown when attempting to attest an already-attested user
93
+ error AlreadyAttested();
94
+
95
+ // ============ Registrar Management ============
96
+
97
+ /// @notice Add a new authorized registrar
98
+ /// @param registrar Address to authorize as registrar
99
+ function addRegistrar(address registrar) external;
100
+
101
+ /// @notice Remove an authorized registrar
102
+ /// @param registrar Address to remove from registrars
103
+ function removeRegistrar(address registrar) external;
104
+
105
+ // ============ Identity Attestation ============
106
+
107
+ /// @notice Attest a user's encrypted identity data on-chain
108
+ /// @param user Address of the user being attested
109
+ /// @param encBirthYearOffset Encrypted birth year offset (years since 1900)
110
+ /// @param encCountryCode Encrypted ISO 3166-1 numeric country code
111
+ /// @param encKycLevel Encrypted KYC verification level (0-3)
112
+ /// @param encIsBlacklisted Encrypted blacklist status
113
+ /// @param inputProof FHE proof for encrypted inputs
114
+ function attestIdentity(
115
+ address user,
116
+ externalEuint8 encBirthYearOffset,
117
+ externalEuint16 encCountryCode,
118
+ externalEuint8 encKycLevel,
119
+ externalEbool encIsBlacklisted,
120
+ bytes calldata inputProof
121
+ ) external;
122
+
123
+ /// @notice Revoke a user's identity attestation
124
+ /// @param user Address of the user to revoke
125
+ function revokeIdentity(address user) external;
126
+
127
+ // ============ Encrypted Queries ============
128
+
129
+ /// @notice Get user's encrypted birth year offset
130
+ /// @param user Address of the user
131
+ /// @return Encrypted birth year offset (years since 1900)
132
+ function getBirthYearOffset(address user) external view returns (euint8);
133
+
134
+ /// @notice Get user's encrypted country code
135
+ /// @param user Address of the user
136
+ /// @return Encrypted ISO 3166-1 numeric country code
137
+ function getCountryCode(address user) external view returns (euint16);
138
+
139
+ /// @notice Get user's encrypted KYC level
140
+ /// @param user Address of the user
141
+ /// @return Encrypted KYC verification level (0-3)
142
+ function getKycLevel(address user) external view returns (euint8);
143
+
144
+ /// @notice Get user's encrypted blacklist status
145
+ /// @param user Address of the user
146
+ /// @return Encrypted blacklist status (true if blacklisted)
147
+ function getBlacklistStatus(address user) external view returns (ebool);
148
+
149
+ // ============ Verification Helpers ============
150
+
151
+ /// @notice Check if user has minimum KYC level (encrypted comparison)
152
+ /// @param user Address of the user
153
+ /// @param minLevel Minimum KYC level required
154
+ /// @return Encrypted boolean result of comparison
155
+ function hasMinKycLevel(address user, uint8 minLevel) external returns (ebool);
156
+
157
+ /// @notice Check if user is from a specific country (encrypted comparison)
158
+ /// @param user Address of the user
159
+ /// @param country ISO 3166-1 numeric country code to check
160
+ /// @return Encrypted boolean result of comparison
161
+ function isFromCountry(address user, uint16 country) external returns (ebool);
162
+
163
+ /// @notice Check if user is not blacklisted (encrypted)
164
+ /// @param user Address of the user
165
+ /// @return Encrypted boolean (true if NOT blacklisted)
166
+ function isNotBlacklisted(address user) external returns (ebool);
167
+
168
+ // ============ Access Control ============
169
+
170
+ /// @notice Grant a contract access to caller's encrypted identity data
171
+ /// @param grantee Address to grant access to
172
+ function grantAccessTo(address grantee) external;
173
+
174
+ /// @notice Check if a user has been attested
175
+ /// @param user Address of the user
176
+ /// @return True if user has valid attestation
177
+ function isAttested(address user) external view returns (bool);
178
+
179
+ // ============ Public State ============
180
+
181
+ /// @notice Get the contract owner address
182
+ /// @return Owner address
183
+ function owner() external view returns (address);
184
+
185
+ /// @notice Get the pending owner address
186
+ /// @return Pending owner address
187
+ function pendingOwner() external view returns (address);
188
+
189
+ /// @notice Initiate transfer of contract ownership
190
+ /// @param newOwner Address that can accept ownership
191
+ function transferOwnership(address newOwner) external;
192
+
193
+ /// @notice Accept ownership transfer
194
+ function acceptOwnership() external;
195
+
196
+ /// @notice Check if an address is an authorized registrar
197
+ /// @param registrar Address to check
198
+ /// @return True if address is authorized registrar
199
+ function registrars(address registrar) external view returns (bool);
200
+
201
+ /// @notice Get the timestamp when a user was attested
202
+ /// @param user Address of the user
203
+ /// @return Unix timestamp of attestation (0 if not attested)
204
+ function attestationTimestamp(address user) external view returns (uint256);
205
+
206
+ /// @notice Get the current attestation id for a user (0 if not attested)
207
+ /// @param user Address of the user
208
+ /// @return Current attestation id
209
+ function currentAttestationId(address user) external view returns (uint256);
210
+
211
+ /// @notice Get the latest attestation id ever issued for a user
212
+ /// @param user Address of the user
213
+ /// @return Latest attestation id
214
+ function latestAttestationId(address user) external view returns (uint256);
215
+
216
+ /// @notice Get attestation metadata for a user and attestation id
217
+ /// @param user Address of the user
218
+ /// @param attestationId Attestation identifier to query
219
+ /// @return timestamp Unix timestamp of attestation
220
+ /// @return revokedAt Unix timestamp of revocation (0 if not revoked)
221
+ /// @return registrar Registrar who performed the attestation
222
+ function getAttestationMetadata(
223
+ address user,
224
+ uint256 attestationId
225
+ ) external view returns (uint256 timestamp, uint256 revokedAt, address registrar);
226
+ }
File without changes