@zentity/fhevm-contracts 0.1.1 → 0.2.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.
package/README.md CHANGED
@@ -6,7 +6,7 @@ fhEVM smart contracts for privacy-preserving identity attestations.
6
6
 
7
7
  This package provides Solidity contracts using Zama's fhEVM:
8
8
 
9
- - **IdentityRegistry** — encrypted identity attributes (birth year offset, country, KYC, blacklist)
9
+ - **IdentityRegistry** — encrypted identity attributes (birth year offset, country, compliance (KYC) level, blacklist)
10
10
  - **ComplianceRules** — encrypted compliance checks with cached results
11
11
  - **CompliantERC20** — demo token enforcing compliance on transfers
12
12
 
@@ -142,6 +142,7 @@ See `docs/guide.md` for details and best practices.
142
142
 
143
143
  - `docs/guide.md` (deployment, testing, faucets, ownership)
144
144
  - `docs/architecture.md` (contract flow overview)
145
+ - `CONTRIBUTING.md` (pre-commit checklist, changesets, deployments)
145
146
 
146
147
  ## Development
147
148
 
@@ -8,7 +8,7 @@
8
8
  },
9
9
  {
10
10
  "internalType": "uint8",
11
- "name": "initialMinKycLevel",
11
+ "name": "initialMinComplianceLevel",
12
12
  "type": "uint8"
13
13
  }
14
14
  ],
@@ -92,7 +92,7 @@
92
92
  "type": "uint8"
93
93
  }
94
94
  ],
95
- "name": "MinKycLevelUpdated",
95
+ "name": "MinComplianceLevelUpdated",
96
96
  "type": "event"
97
97
  },
98
98
  {
@@ -268,7 +268,7 @@
268
268
  },
269
269
  {
270
270
  "inputs": [],
271
- "name": "minKycLevel",
271
+ "name": "minComplianceLevel",
272
272
  "outputs": [
273
273
  {
274
274
  "internalType": "uint8",
@@ -331,7 +331,7 @@
331
331
  "type": "uint8"
332
332
  }
333
333
  ],
334
- "name": "setMinKycLevel",
334
+ "name": "setMinComplianceLevel",
335
335
  "outputs": [],
336
336
  "stateMutability": "nonpayable",
337
337
  "type": "function"
@@ -260,7 +260,7 @@
260
260
  },
261
261
  {
262
262
  "internalType": "externalEuint8",
263
- "name": "encKycLevel",
263
+ "name": "encComplianceLevel",
264
264
  "type": "bytes32"
265
265
  },
266
266
  {
@@ -429,10 +429,10 @@
429
429
  "type": "address"
430
430
  }
431
431
  ],
432
- "name": "getCountryCode",
432
+ "name": "getComplianceLevel",
433
433
  "outputs": [
434
434
  {
435
- "internalType": "euint16",
435
+ "internalType": "euint8",
436
436
  "name": "",
437
437
  "type": "bytes32"
438
438
  }
@@ -448,12 +448,12 @@
448
448
  "type": "address"
449
449
  },
450
450
  {
451
- "internalType": "uint16",
452
- "name": "country",
453
- "type": "uint16"
451
+ "internalType": "uint8",
452
+ "name": "minLevel",
453
+ "type": "uint8"
454
454
  }
455
455
  ],
456
- "name": "getCountryResult",
456
+ "name": "getComplianceLevelResult",
457
457
  "outputs": [
458
458
  {
459
459
  "internalType": "ebool",
@@ -472,10 +472,10 @@
472
472
  "type": "address"
473
473
  }
474
474
  ],
475
- "name": "getKycLevel",
475
+ "name": "getCountryCode",
476
476
  "outputs": [
477
477
  {
478
- "internalType": "euint8",
478
+ "internalType": "euint16",
479
479
  "name": "",
480
480
  "type": "bytes32"
481
481
  }
@@ -491,12 +491,12 @@
491
491
  "type": "address"
492
492
  },
493
493
  {
494
- "internalType": "uint8",
495
- "name": "minLevel",
496
- "type": "uint8"
494
+ "internalType": "uint16",
495
+ "name": "country",
496
+ "type": "uint16"
497
497
  }
498
498
  ],
499
- "name": "getKycLevelResult",
499
+ "name": "getCountryResult",
500
500
  "outputs": [
501
501
  {
502
502
  "internalType": "ebool",
@@ -533,7 +533,7 @@
533
533
  "type": "uint8"
534
534
  }
535
535
  ],
536
- "name": "hasMinKycLevel",
536
+ "name": "hasMinComplianceLevel",
537
537
  "outputs": [
538
538
  {
539
539
  "internalType": "ebool",
@@ -12,7 +12,7 @@ flowchart TD
12
12
  EncIn -- "2) tx call" --> ERC["CompliantERC20"]
13
13
 
14
14
  subgraph OnChain["On-chain fhEVM"]
15
- IR -- "3) Stores encrypted attrs" --> IRState["Encrypted attributes: birthYear/country/kyc/blacklist"]
15
+ IR -- "3) Stores encrypted attrs" --> IRState["Encrypted attributes: birthYear/country/compliance/blacklist"]
16
16
  IR -- "4) ACL grants" --> ACL["ACL per ciphertext handle"]
17
17
  ERC -- "5) calls" --> CR["ComplianceRules"]
18
18
  CR -- "6) queries" --> IR
@@ -11,7 +11,7 @@ sequenceDiagram
11
11
  actor User
12
12
  participant UI as Zentity Web App (Web2)
13
13
  participant BE as Zentity Backend (Web2)
14
- participant KYC as KYC/Liveness Services (Web2)
14
+ participant Verifier as KYC/Liveness Services (Web2)
15
15
  participant Registrar as Registrar Wallet (Web3)
16
16
  participant IR as IdentityRegistry (fhEVM)
17
17
  participant CR as ComplianceRules (fhEVM)
@@ -19,8 +19,8 @@ sequenceDiagram
19
19
 
20
20
  User->>UI: Complete verification flow (docs + liveness)
21
21
  UI->>BE: Submit verification data
22
- BE->>KYC: Verify identity and liveness
23
- KYC-->>BE: KYC result + attributes
22
+ BE->>Verifier: Verify identity and liveness
23
+ Verifier-->>BE: Compliance result + attributes
24
24
 
25
25
  BE->>UI: Verification complete (Web2)
26
26
  UI->>Registrar: Request on-chain attestation
@@ -44,7 +44,7 @@ sequenceDiagram
44
44
 
45
45
  ### Web2 (Zentity Backend)
46
46
  - **Collects and verifies identity data** (documents, liveness, etc.).
47
- - **Produces the final KYC result** and attribute set.
47
+ - **Produces the final compliance result** and attribute set.
48
48
  - **Never stores plaintext on-chain**. Only encrypted attributes are sent to
49
49
  Web3 via the registrar flow.
50
50
 
@@ -36,8 +36,8 @@ contract ComplianceRules is ZamaEthereumConfig {
36
36
  /// @notice Pending owner for two-step ownership transfer
37
37
  address public pendingOwner;
38
38
 
39
- /// @notice Minimum KYC level required for compliance
40
- uint8 public minKycLevel;
39
+ /// @notice Minimum compliance level required for compliance
40
+ uint8 public minComplianceLevel;
41
41
 
42
42
  /// @notice Store last compliance check result for each user
43
43
  mapping(address user => ebool result) private complianceResults;
@@ -47,9 +47,9 @@ contract ComplianceRules is ZamaEthereumConfig {
47
47
 
48
48
  // ============ Events ============
49
49
 
50
- /// @notice Emitted when the minimum KYC level requirement is updated
51
- /// @param newLevel The new minimum KYC level required for compliance
52
- event MinKycLevelUpdated(uint8 indexed newLevel);
50
+ /// @notice Emitted when the minimum compliance level requirement is updated
51
+ /// @param newLevel The new minimum compliance level required for compliance
52
+ event MinComplianceLevelUpdated(uint8 indexed newLevel);
53
53
 
54
54
  /// @notice Emitted when a compliance check is performed for a user
55
55
  /// @param user Address of the user whose compliance was checked
@@ -107,24 +107,24 @@ contract ComplianceRules is ZamaEthereumConfig {
107
107
  /**
108
108
  * @notice Initialize with identity registry reference
109
109
  * @param registry Address of the IdentityRegistry contract
110
- * @param initialMinKycLevel Initial minimum KYC level (default: 1)
110
+ * @param initialMinComplianceLevel Initial minimum compliance level (default: 1)
111
111
  */
112
- constructor(address registry, uint8 initialMinKycLevel) {
112
+ constructor(address registry, uint8 initialMinComplianceLevel) {
113
113
  if (registry == address(0)) revert RegistryNotSet();
114
114
  identityRegistry = IIdentityRegistry(registry);
115
115
  owner = msg.sender;
116
- minKycLevel = initialMinKycLevel;
116
+ minComplianceLevel = initialMinComplianceLevel;
117
117
  }
118
118
 
119
119
  // ============ Admin Functions ============
120
120
 
121
121
  /**
122
- * @notice Update minimum KYC level
122
+ * @notice Update minimum compliance level
123
123
  * @param newLevel New minimum level
124
124
  */
125
- function setMinKycLevel(uint8 newLevel) external onlyOwner {
126
- minKycLevel = newLevel;
127
- emit MinKycLevelUpdated(newLevel);
125
+ function setMinComplianceLevel(uint8 newLevel) external onlyOwner {
126
+ minComplianceLevel = newLevel;
127
+ emit MinComplianceLevelUpdated(newLevel);
128
128
  }
129
129
 
130
130
  /**
@@ -162,7 +162,7 @@ contract ComplianceRules is ZamaEthereumConfig {
162
162
 
163
163
  /**
164
164
  * @notice Check if user passes all compliance requirements
165
- * @dev Combines: hasMinKycLevel AND isNotBlacklisted
165
+ * @dev Combines: hasMinComplianceLevel AND isNotBlacklisted
166
166
  * @param user Address to check
167
167
  * @return Encrypted boolean indicating compliance status
168
168
  *
@@ -181,11 +181,11 @@ contract ComplianceRules is ZamaEthereumConfig {
181
181
  }
182
182
 
183
183
  // Get individual compliance checks
184
- ebool hasKyc = identityRegistry.hasMinKycLevel(user, minKycLevel);
184
+ ebool hasCompliance = identityRegistry.hasMinComplianceLevel(user, minComplianceLevel);
185
185
  ebool notBlacklisted = identityRegistry.isNotBlacklisted(user);
186
186
 
187
187
  // Combine all conditions
188
- ebool result = FHE.and(hasKyc, notBlacklisted);
188
+ ebool result = FHE.and(hasCompliance, notBlacklisted);
189
189
 
190
190
  // Store and grant permissions
191
191
  complianceResults[user] = result;
@@ -216,12 +216,12 @@ contract ComplianceRules is ZamaEthereumConfig {
216
216
  }
217
217
 
218
218
  // Get individual compliance checks
219
- ebool hasKyc = identityRegistry.hasMinKycLevel(user, minKycLevel);
219
+ ebool hasCompliance = identityRegistry.hasMinComplianceLevel(user, minComplianceLevel);
220
220
  ebool notBlacklisted = identityRegistry.isNotBlacklisted(user);
221
221
  ebool isFromAllowedCountry = identityRegistry.isFromCountry(user, allowedCountry);
222
222
 
223
223
  // Combine all conditions
224
- ebool result = FHE.and(FHE.and(hasKyc, notBlacklisted), isFromAllowedCountry);
224
+ ebool result = FHE.and(FHE.and(hasCompliance, notBlacklisted), isFromAllowedCountry);
225
225
 
226
226
  // Grant permissions
227
227
  FHE.allowThis(result);
@@ -9,7 +9,7 @@ import {IIdentityRegistry} from "../interfaces/IIdentityRegistry.sol";
9
9
  /**
10
10
  * @title IdentityRegistry
11
11
  * @author Gustavo Valverde
12
- * @notice On-chain encrypted identity registry for KYC or compliance platforms
12
+ * @notice On-chain encrypted identity registry for compliance platforms
13
13
  * @dev Part of zentity-fhevm-contracts - Builder Track
14
14
  *
15
15
  * @custom:category identity
@@ -35,8 +35,8 @@ contract IdentityRegistry is IIdentityRegistry, ZamaEthereumConfig {
35
35
  /// @notice Encrypted country code (ISO 3166-1 numeric)
36
36
  mapping(address user => euint16 countryCode) private countryCodes;
37
37
 
38
- /// @notice Encrypted KYC verification level (0-5)
39
- mapping(address user => euint8 kycLevel) private kycLevels;
38
+ /// @notice Encrypted compliance verification level (0-5)
39
+ mapping(address user => euint8 complianceLevel) private complianceLevels;
40
40
 
41
41
  /// @notice Encrypted blacklist status
42
42
  mapping(address user => ebool blacklisted) private isBlacklisted;
@@ -118,7 +118,7 @@ contract IdentityRegistry is IIdentityRegistry, ZamaEthereumConfig {
118
118
  address user,
119
119
  externalEuint8 encBirthYearOffset,
120
120
  externalEuint16 encCountryCode,
121
- externalEuint8 encKycLevel,
121
+ externalEuint8 encComplianceLevel,
122
122
  externalEbool encIsBlacklisted,
123
123
  bytes calldata inputProof
124
124
  ) external onlyRegistrar {
@@ -127,24 +127,24 @@ contract IdentityRegistry is IIdentityRegistry, ZamaEthereumConfig {
127
127
  // Convert and store encrypted values
128
128
  euint8 birthYear = FHE.fromExternal(encBirthYearOffset, inputProof);
129
129
  euint16 country = FHE.fromExternal(encCountryCode, inputProof);
130
- euint8 kyc = FHE.fromExternal(encKycLevel, inputProof);
130
+ euint8 compliance = FHE.fromExternal(encComplianceLevel, inputProof);
131
131
  ebool blacklisted = FHE.fromExternal(encIsBlacklisted, inputProof);
132
132
 
133
133
  birthYearOffsets[user] = birthYear;
134
134
  countryCodes[user] = country;
135
- kycLevels[user] = kyc;
135
+ complianceLevels[user] = compliance;
136
136
  isBlacklisted[user] = blacklisted;
137
137
 
138
138
  // Grant contract permission to all values
139
139
  FHE.allowThis(birthYear);
140
140
  FHE.allowThis(country);
141
- FHE.allowThis(kyc);
141
+ FHE.allowThis(compliance);
142
142
  FHE.allowThis(blacklisted);
143
143
 
144
144
  // Grant user permission to their own data
145
145
  FHE.allow(birthYear, user);
146
146
  FHE.allow(country, user);
147
- FHE.allow(kyc, user);
147
+ FHE.allow(compliance, user);
148
148
  FHE.allow(blacklisted, user);
149
149
 
150
150
  uint256 newAttestationId = latestAttestationId[user] + 1;
@@ -169,7 +169,7 @@ contract IdentityRegistry is IIdentityRegistry, ZamaEthereumConfig {
169
169
  // Set encrypted values to encrypted zeros
170
170
  birthYearOffsets[user] = FHE.asEuint8(0);
171
171
  countryCodes[user] = FHE.asEuint16(0);
172
- kycLevels[user] = FHE.asEuint8(0);
172
+ complianceLevels[user] = FHE.asEuint8(0);
173
173
  isBlacklisted[user] = FHE.asEbool(false);
174
174
  attestationTimestamp[user] = 0;
175
175
  currentAttestationId[user] = 0;
@@ -200,10 +200,10 @@ contract IdentityRegistry is IIdentityRegistry, ZamaEthereumConfig {
200
200
  }
201
201
 
202
202
  /// @inheritdoc IIdentityRegistry
203
- function getKycLevel(address user) external view returns (euint8) {
203
+ function getComplianceLevel(address user) external view returns (euint8) {
204
204
  if (attestationTimestamp[user] == 0) revert NotAttested();
205
- if (!FHE.isSenderAllowed(kycLevels[user])) revert AccessProhibited();
206
- return kycLevels[user];
205
+ if (!FHE.isSenderAllowed(complianceLevels[user])) revert AccessProhibited();
206
+ return complianceLevels[user];
207
207
  }
208
208
 
209
209
  /// @inheritdoc IIdentityRegistry
@@ -216,10 +216,10 @@ contract IdentityRegistry is IIdentityRegistry, ZamaEthereumConfig {
216
216
  // ============ Verification Helpers ============
217
217
 
218
218
  /// @inheritdoc IIdentityRegistry
219
- function hasMinKycLevel(address user, uint8 minLevel) external returns (ebool) {
219
+ function hasMinComplianceLevel(address user, uint8 minLevel) external returns (ebool) {
220
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));
221
+ if (!FHE.isSenderAllowed(complianceLevels[user])) revert AccessProhibited();
222
+ ebool result = FHE.ge(complianceLevels[user], FHE.asEuint8(minLevel));
223
223
 
224
224
  // Store result for later retrieval
225
225
  bytes32 key = keccak256(abi.encodePacked(user, uint8(0), uint256(minLevel)));
@@ -269,12 +269,12 @@ contract IdentityRegistry is IIdentityRegistry, ZamaEthereumConfig {
269
269
  // ============ Result Getters ============
270
270
 
271
271
  /**
272
- * @notice Get the last KYC level verification result
272
+ * @notice Get the last compliance level verification result
273
273
  * @param user Address that was checked
274
274
  * @param minLevel Level that was checked
275
275
  * @return Encrypted boolean result
276
276
  */
277
- function getKycLevelResult(address user, uint8 minLevel) external view returns (ebool) {
277
+ function getComplianceLevelResult(address user, uint8 minLevel) external view returns (ebool) {
278
278
  bytes32 key = keccak256(abi.encodePacked(user, uint8(0), uint256(minLevel)));
279
279
  ebool result = verificationResults[key];
280
280
  if (!FHE.isSenderAllowed(result)) revert AccessProhibited();
@@ -330,7 +330,7 @@ contract IdentityRegistry is IIdentityRegistry, ZamaEthereumConfig {
330
330
 
331
331
  FHE.allow(birthYearOffsets[msg.sender], grantee);
332
332
  FHE.allow(countryCodes[msg.sender], grantee);
333
- FHE.allow(kycLevels[msg.sender], grantee);
333
+ FHE.allow(complianceLevels[msg.sender], grantee);
334
334
  FHE.allow(isBlacklisted[msg.sender], grantee);
335
335
 
336
336
  emit AccessGranted(msg.sender, grantee);
@@ -108,14 +108,14 @@ interface IIdentityRegistry {
108
108
  /// @param user Address of the user being attested
109
109
  /// @param encBirthYearOffset Encrypted birth year offset (years since 1900)
110
110
  /// @param encCountryCode Encrypted ISO 3166-1 numeric country code
111
- /// @param encKycLevel Encrypted KYC verification level (0-3)
111
+ /// @param encComplianceLevel Encrypted compliance verification level (0-3)
112
112
  /// @param encIsBlacklisted Encrypted blacklist status
113
113
  /// @param inputProof FHE proof for encrypted inputs
114
114
  function attestIdentity(
115
115
  address user,
116
116
  externalEuint8 encBirthYearOffset,
117
117
  externalEuint16 encCountryCode,
118
- externalEuint8 encKycLevel,
118
+ externalEuint8 encComplianceLevel,
119
119
  externalEbool encIsBlacklisted,
120
120
  bytes calldata inputProof
121
121
  ) external;
@@ -136,10 +136,10 @@ interface IIdentityRegistry {
136
136
  /// @return Encrypted ISO 3166-1 numeric country code
137
137
  function getCountryCode(address user) external view returns (euint16);
138
138
 
139
- /// @notice Get user's encrypted KYC level
139
+ /// @notice Get user's encrypted compliance level
140
140
  /// @param user Address of the user
141
- /// @return Encrypted KYC verification level (0-3)
142
- function getKycLevel(address user) external view returns (euint8);
141
+ /// @return Encrypted compliance (KYC) verification level (0-3)
142
+ function getComplianceLevel(address user) external view returns (euint8);
143
143
 
144
144
  /// @notice Get user's encrypted blacklist status
145
145
  /// @param user Address of the user
@@ -148,11 +148,11 @@ interface IIdentityRegistry {
148
148
 
149
149
  // ============ Verification Helpers ============
150
150
 
151
- /// @notice Check if user has minimum KYC level (encrypted comparison)
151
+ /// @notice Check if user has minimum compliance level (encrypted comparison)
152
152
  /// @param user Address of the user
153
- /// @param minLevel Minimum KYC level required
153
+ /// @param minLevel Minimum compliance level required
154
154
  /// @return Encrypted boolean result of comparison
155
- function hasMinKycLevel(address user, uint8 minLevel) external returns (ebool);
155
+ function hasMinComplianceLevel(address user, uint8 minLevel) external returns (ebool);
156
156
 
157
157
  /// @notice Check if user is from a specific country (encrypted comparison)
158
158
  /// @param user Address of the user