@zentity/fhevm-contracts 0.3.0 → 0.4.1

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 (123) hide show
  1. package/abi/ComplianceRules.json +25 -16
  2. package/abi/CompliantERC20.json +30 -16
  3. package/abi/IdentityRegistry.json +496 -119
  4. package/contracts/compliance/ComplianceRules.sol +40 -189
  5. package/contracts/core/IdentityRegistry.sol +280 -232
  6. package/contracts/interfaces/IComplianceRules.sol +30 -0
  7. package/contracts/interfaces/IIdentityRegistry.sol +133 -159
  8. package/contracts/proxy/ERC1967Proxy.sol +6 -0
  9. package/contracts/test/MockFacilitator.sol +40 -0
  10. package/contracts/tokens/CompliantERC20.sol +28 -242
  11. package/deployments/hardhat/addresses.json +3 -8
  12. package/deployments/sepolia/ComplianceRules.json +91 -207
  13. package/deployments/sepolia/CompliantERC20.json +114 -293
  14. package/deployments/sepolia/IdentityRegistry.json +498 -742
  15. package/deployments/sepolia/IdentityRegistry_Implementation.json +1698 -0
  16. package/deployments/sepolia/IdentityRegistry_Proxy.json +192 -0
  17. package/deployments/sepolia/addresses.json +5 -6
  18. package/dist/index.d.ts +175 -9
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +44 -1
  21. package/package.json +1 -1
  22. package/typechain-types/@openzeppelin/contracts/access/Ownable.ts +153 -0
  23. package/typechain-types/@openzeppelin/contracts/access/Ownable2Step.ts +217 -0
  24. package/typechain-types/@openzeppelin/contracts/access/index.ts +5 -0
  25. package/typechain-types/@openzeppelin/contracts/index.ts +11 -0
  26. package/typechain-types/@openzeppelin/contracts/interfaces/IERC1967.ts +168 -0
  27. package/typechain-types/@openzeppelin/contracts/interfaces/IERC5267.ts +151 -0
  28. package/typechain-types/{contracts/tokens/CompliantERC20.sol/IComplianceChecker.ts → @openzeppelin/contracts/interfaces/draft-IERC1822.sol/IERC1822Proxiable.ts} +12 -17
  29. package/typechain-types/@openzeppelin/contracts/interfaces/draft-IERC1822.sol/index.ts +4 -0
  30. package/typechain-types/@openzeppelin/contracts/interfaces/index.ts +7 -0
  31. package/typechain-types/@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.ts +105 -0
  32. package/typechain-types/@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.ts +69 -0
  33. package/typechain-types/@openzeppelin/contracts/proxy/ERC1967/index.ts +5 -0
  34. package/typechain-types/@openzeppelin/contracts/proxy/Proxy.ts +69 -0
  35. package/typechain-types/@openzeppelin/contracts/proxy/beacon/IBeacon.ts +90 -0
  36. package/typechain-types/@openzeppelin/contracts/proxy/beacon/index.ts +4 -0
  37. package/typechain-types/@openzeppelin/contracts/proxy/index.ts +8 -0
  38. package/typechain-types/@openzeppelin/contracts/utils/Address.ts +69 -0
  39. package/typechain-types/@openzeppelin/contracts/utils/Errors.ts +69 -0
  40. package/typechain-types/@openzeppelin/contracts/utils/Strings.ts +69 -0
  41. package/typechain-types/@openzeppelin/contracts/utils/cryptography/ECDSA.ts +69 -0
  42. package/typechain-types/@openzeppelin/contracts/utils/cryptography/index.ts +4 -0
  43. package/typechain-types/@openzeppelin/contracts/utils/index.ts +10 -0
  44. package/typechain-types/@openzeppelin/contracts/utils/math/SafeCast.ts +69 -0
  45. package/typechain-types/@openzeppelin/contracts/utils/math/index.ts +4 -0
  46. package/typechain-types/@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.ts +251 -0
  47. package/typechain-types/@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.ts +186 -0
  48. package/typechain-types/@openzeppelin/contracts-upgradeable/access/index.ts +5 -0
  49. package/typechain-types/@openzeppelin/contracts-upgradeable/index.ts +9 -0
  50. package/typechain-types/@openzeppelin/contracts-upgradeable/proxy/index.ts +5 -0
  51. package/typechain-types/@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.ts +105 -0
  52. package/typechain-types/@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.ts +196 -0
  53. package/typechain-types/@openzeppelin/contracts-upgradeable/proxy/utils/index.ts +5 -0
  54. package/typechain-types/@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.ts +105 -0
  55. package/typechain-types/@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.ts +184 -0
  56. package/typechain-types/@openzeppelin/contracts-upgradeable/utils/cryptography/index.ts +4 -0
  57. package/typechain-types/@openzeppelin/contracts-upgradeable/utils/index.ts +6 -0
  58. package/typechain-types/@openzeppelin/index.ts +7 -0
  59. package/typechain-types/contracts/compliance/ComplianceRules.ts +18 -7
  60. package/typechain-types/contracts/core/IdentityRegistry.ts +493 -233
  61. package/typechain-types/contracts/index.ts +2 -0
  62. package/typechain-types/contracts/interfaces/IComplianceRules.ts +290 -0
  63. package/typechain-types/contracts/interfaces/IIdentityRegistry.ts +269 -341
  64. package/typechain-types/contracts/interfaces/index.ts +1 -0
  65. package/typechain-types/contracts/test/MockFacilitator.ts +187 -0
  66. package/typechain-types/contracts/test/index.ts +4 -0
  67. package/typechain-types/contracts/tokens/{CompliantERC20.sol/CompliantERC20.ts → CompliantERC20.ts} +19 -8
  68. package/typechain-types/contracts/tokens/index.ts +1 -2
  69. package/typechain-types/factories/@openzeppelin/contracts/access/Ownable2Step__factory.ts +138 -0
  70. package/typechain-types/factories/@openzeppelin/contracts/access/Ownable__factory.ts +96 -0
  71. package/typechain-types/factories/@openzeppelin/contracts/access/index.ts +5 -0
  72. package/typechain-types/factories/@openzeppelin/contracts/index.ts +7 -0
  73. package/typechain-types/factories/@openzeppelin/contracts/interfaces/IERC1967__factory.ts +67 -0
  74. package/typechain-types/factories/@openzeppelin/contracts/interfaces/IERC5267__factory.ts +71 -0
  75. package/typechain-types/factories/@openzeppelin/contracts/interfaces/draft-IERC1822.sol/IERC1822Proxiable__factory.ts +38 -0
  76. package/typechain-types/factories/@openzeppelin/contracts/interfaces/draft-IERC1822.sol/index.ts +4 -0
  77. package/typechain-types/factories/@openzeppelin/contracts/interfaces/index.ts +6 -0
  78. package/typechain-types/factories/@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy__factory.ts +144 -0
  79. package/typechain-types/factories/@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils__factory.ts +105 -0
  80. package/typechain-types/factories/@openzeppelin/contracts/proxy/ERC1967/index.ts +5 -0
  81. package/typechain-types/factories/@openzeppelin/contracts/proxy/Proxy__factory.ts +26 -0
  82. package/typechain-types/factories/@openzeppelin/contracts/proxy/beacon/IBeacon__factory.ts +35 -0
  83. package/typechain-types/factories/@openzeppelin/contracts/proxy/beacon/index.ts +4 -0
  84. package/typechain-types/factories/@openzeppelin/contracts/proxy/index.ts +6 -0
  85. package/typechain-types/factories/@openzeppelin/contracts/utils/Address__factory.ts +75 -0
  86. package/typechain-types/factories/@openzeppelin/contracts/utils/Errors__factory.ts +101 -0
  87. package/typechain-types/factories/@openzeppelin/contracts/utils/Strings__factory.ts +90 -0
  88. package/typechain-types/factories/@openzeppelin/contracts/utils/cryptography/ECDSA__factory.ts +91 -0
  89. package/typechain-types/factories/@openzeppelin/contracts/utils/cryptography/index.ts +4 -0
  90. package/typechain-types/factories/@openzeppelin/contracts/utils/index.ts +8 -0
  91. package/typechain-types/factories/@openzeppelin/contracts/utils/math/SafeCast__factory.ts +118 -0
  92. package/typechain-types/factories/@openzeppelin/contracts/utils/math/index.ts +4 -0
  93. package/typechain-types/factories/@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable__factory.ts +165 -0
  94. package/typechain-types/factories/@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable__factory.ts +122 -0
  95. package/typechain-types/factories/@openzeppelin/contracts-upgradeable/access/index.ts +5 -0
  96. package/typechain-types/factories/@openzeppelin/contracts-upgradeable/index.ts +6 -0
  97. package/typechain-types/factories/@openzeppelin/contracts-upgradeable/proxy/index.ts +4 -0
  98. package/typechain-types/factories/@openzeppelin/contracts-upgradeable/proxy/utils/Initializable__factory.ts +48 -0
  99. package/typechain-types/factories/@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable__factory.ts +153 -0
  100. package/typechain-types/factories/@openzeppelin/contracts-upgradeable/proxy/utils/index.ts +5 -0
  101. package/typechain-types/factories/@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable__factory.ts +48 -0
  102. package/typechain-types/factories/@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable__factory.ts +97 -0
  103. package/typechain-types/factories/@openzeppelin/contracts-upgradeable/utils/cryptography/index.ts +4 -0
  104. package/typechain-types/factories/@openzeppelin/contracts-upgradeable/utils/index.ts +5 -0
  105. package/typechain-types/factories/@openzeppelin/index.ts +5 -0
  106. package/typechain-types/factories/contracts/compliance/ComplianceRules__factory.ts +26 -17
  107. package/typechain-types/factories/contracts/core/IdentityRegistry__factory.ts +493 -116
  108. package/typechain-types/factories/contracts/index.ts +1 -0
  109. package/typechain-types/factories/contracts/interfaces/IComplianceRules__factory.ts +193 -0
  110. package/typechain-types/factories/contracts/interfaces/IIdentityRegistry__factory.ts +236 -186
  111. package/typechain-types/factories/contracts/interfaces/index.ts +1 -0
  112. package/typechain-types/factories/contracts/test/MockFacilitator__factory.ts +194 -0
  113. package/typechain-types/factories/contracts/test/index.ts +4 -0
  114. package/typechain-types/factories/contracts/tokens/CompliantERC20__factory.ts +595 -0
  115. package/typechain-types/factories/contracts/tokens/index.ts +1 -1
  116. package/typechain-types/factories/index.ts +1 -0
  117. package/typechain-types/hardhat.d.ts +394 -16
  118. package/typechain-types/index.ts +48 -4
  119. package/deployments/hardhat/.chainId +0 -1
  120. package/typechain-types/contracts/tokens/CompliantERC20.sol/index.ts +0 -5
  121. package/typechain-types/factories/contracts/tokens/CompliantERC20.sol/CompliantERC20__factory.ts +0 -581
  122. package/typechain-types/factories/contracts/tokens/CompliantERC20.sol/IComplianceChecker__factory.ts +0 -44
  123. package/typechain-types/factories/contracts/tokens/CompliantERC20.sol/index.ts +0 -5
@@ -4,167 +4,54 @@ pragma solidity ^0.8.27;
4
4
 
5
5
  import {FHE, euint64, ebool, externalEuint64} from "@fhevm/solidity/lib/FHE.sol";
6
6
  import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
7
-
8
- /**
9
- * @title CompliantERC20
10
- * @author Gustavo Valverde
11
- * @notice ERC20-like token with encrypted balances and compliance checks
12
- * @dev Part of zentity-fhevm-contracts - Builder Track
13
- *
14
- * @custom:category token
15
- * @custom:concept FHE.select() for branch-free compliant transfers
16
- * @custom:difficulty advanced
17
- *
18
- * This contract implements a compliant token with encrypted balances.
19
- * Transfers only succeed if both parties pass compliance checks, but
20
- * failures are handled silently (transfer of 0) to prevent information leakage.
21
- *
22
- * Key patterns demonstrated:
23
- * 1. FHE.select() for branch-free conditional logic
24
- * 2. Combining multiple encrypted conditions with FHE.and()
25
- * 3. Encrypted balance management
26
- * 4. No-revert compliance (privacy-preserving failure handling)
27
- * 5. Integration with external compliance checker
28
- */
29
- contract CompliantERC20 is ZamaEthereumConfig {
30
- // ============ Token Metadata ============
31
-
32
- /// @notice Token name
7
+ import {Ownable2Step, Ownable} from "@openzeppelin/contracts/access/Ownable2Step.sol";
8
+ import {IComplianceRules} from "../interfaces/IComplianceRules.sol";
9
+
10
+ /// @title CompliantERC20
11
+ /// @author Gustavo Valverde
12
+ /// @notice ERC20-like token with encrypted balances and branch-free compliance
13
+ /// @dev Transfers use FHE.select() — failed compliance silently transfers 0,
14
+ /// preventing information leakage about compliance status.
15
+ contract CompliantERC20 is Ownable2Step, ZamaEthereumConfig {
33
16
  string public name;
34
-
35
- /// @notice Token symbol
36
17
  string public symbol;
37
-
38
- /// @notice Token decimals
39
18
  uint8 public constant DECIMALS = 18;
40
-
41
- /// @notice Total supply (public for transparency)
42
19
  uint256 public totalSupply;
43
20
 
44
- // ============ Token State ============
45
-
46
- /// @notice Encrypted balances
47
21
  mapping(address account => euint64 balance) private balances;
48
-
49
- /// @notice Encrypted allowances
50
22
  mapping(address owner => mapping(address spender => euint64 allowance)) private allowances;
51
23
 
52
- // ============ Compliance State ============
53
-
54
- /// @notice Compliance checker interface (can be ComplianceRules or custom)
55
- IComplianceChecker public complianceChecker;
56
-
57
- /// @notice Owner/admin
58
- address public owner;
59
- /// @notice Pending owner for two-step ownership transfer
60
- address public pendingOwner;
24
+ IComplianceRules public complianceChecker;
61
25
 
62
- // ============ Events ============
63
-
64
- /// @notice Emitted on token transfers (indexed for efficient filtering)
65
- /// @param from Address tokens are transferred from
66
- /// @param to Address tokens are transferred to
67
26
  event Transfer(address indexed from, address indexed to);
68
-
69
- /// @notice Emitted when spending allowance is set
70
- /// @param owner Address of the token owner
71
- /// @param spender Address authorized to spend
72
27
  event Approval(address indexed owner, address indexed spender);
73
-
74
- /// @notice Emitted when new tokens are minted
75
- /// @param to Address receiving the minted tokens
76
- /// @param amount Number of tokens minted
77
28
  event Mint(address indexed to, uint256 indexed amount);
78
-
79
- /// @notice Emitted when the compliance checker contract is updated
80
- /// @param newChecker Address of the new compliance checker
81
29
  event ComplianceCheckerUpdated(address indexed newChecker);
82
30
 
83
- /// @notice Emitted when ownership transfer is initiated
84
- /// @param currentOwner Current owner address
85
- /// @param pendingOwner Address that can accept ownership
86
- event OwnershipTransferStarted(address indexed currentOwner, address indexed pendingOwner);
87
-
88
- /// @notice Emitted when ownership transfer is completed
89
- /// @param previousOwner Previous owner address
90
- /// @param newOwner New owner address
91
- event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
92
-
93
- // ============ Errors ============
94
-
95
- /// @notice Thrown when caller is not the contract owner
96
- error OnlyOwner();
97
- /// @notice Thrown when caller is not the pending owner
98
- error OnlyPendingOwner();
99
- /// @notice Thrown when new owner is the zero address
100
- error InvalidOwner();
101
-
102
- /// @notice Thrown when compliance checker is required but not set
103
31
  error ComplianceCheckerNotSet();
104
- /// @notice Thrown when caller supplies an unauthorized ciphertext handle
105
32
  error UnauthorizedCiphertext();
106
- /// @notice Thrown when mint amount would exceed uint64 accounting bounds
107
33
  error TotalSupplyOverflow();
108
34
 
109
- // ============ Constructor ============
110
-
111
- /**
112
- * @notice Initialize the token
113
- * @param tokenName Token name
114
- * @param tokenSymbol Token symbol
115
- * @param checker Address of the compliance checker contract
116
- */
117
- constructor(string memory tokenName, string memory tokenSymbol, address checker) {
35
+ constructor(
36
+ string memory tokenName,
37
+ string memory tokenSymbol,
38
+ address checker
39
+ ) Ownable(msg.sender) {
118
40
  name = tokenName;
119
41
  symbol = tokenSymbol;
120
- owner = msg.sender;
121
42
  if (checker != address(0)) {
122
- complianceChecker = IComplianceChecker(checker);
43
+ complianceChecker = IComplianceRules(checker);
123
44
  }
124
45
  }
125
46
 
126
- // ============ Admin Functions ============
47
+ // ============ Admin ============
127
48
 
128
- /**
129
- * @notice Set the compliance checker contract
130
- * @param checker Address of the compliance checker
131
- */
132
- function setComplianceChecker(address checker) external {
133
- if (msg.sender != owner) revert OnlyOwner();
134
- complianceChecker = IComplianceChecker(checker);
49
+ function setComplianceChecker(address checker) external onlyOwner {
50
+ complianceChecker = IComplianceRules(checker);
135
51
  emit ComplianceCheckerUpdated(checker);
136
52
  }
137
53
 
138
- /**
139
- * @notice Initiate transfer of contract ownership
140
- * @param newOwner Address that can accept ownership
141
- */
142
- function transferOwnership(address newOwner) external {
143
- if (msg.sender != owner) revert OnlyOwner();
144
- if (newOwner == address(0)) revert InvalidOwner();
145
- pendingOwner = newOwner;
146
- emit OwnershipTransferStarted(owner, newOwner);
147
- }
148
-
149
- /**
150
- * @notice Accept ownership transfer
151
- */
152
- function acceptOwnership() external {
153
- if (msg.sender != pendingOwner) revert OnlyPendingOwner();
154
- address previousOwner = owner;
155
- owner = pendingOwner;
156
- pendingOwner = address(0);
157
- emit OwnershipTransferred(previousOwner, owner);
158
- }
159
-
160
- /**
161
- * @notice Mint tokens to an address
162
- * @dev Only owner can mint. Compliance is NOT checked on mint.
163
- * @param to Recipient address
164
- * @param amount Amount to mint (plaintext)
165
- */
166
- function mint(address to, uint256 amount) external {
167
- if (msg.sender != owner) revert OnlyOwner();
54
+ function mint(address to, uint256 amount) external onlyOwner {
168
55
  if (amount > type(uint64).max) revert TotalSupplyOverflow();
169
56
  if (totalSupply + amount > type(uint64).max) revert TotalSupplyOverflow();
170
57
 
@@ -174,86 +61,48 @@ contract CompliantERC20 is ZamaEthereumConfig {
174
61
  FHE.allow(balances[to], to);
175
62
 
176
63
  totalSupply += amount;
177
-
178
64
  emit Mint(to, amount);
179
65
  }
180
66
 
181
67
  // ============ Token Functions ============
182
68
 
183
- /**
184
- * @notice Transfer tokens with encrypted amount
185
- * @dev Branch-free transfer with compliance checks
186
- * @param to Recipient address
187
- * @param encryptedAmount Encrypted amount to transfer
188
- * @param inputProof Proof for encrypted input
189
- * @return success Always returns true (actual transfer amount may be 0)
190
- *
191
- * Key insight: We never revert on failed compliance. Instead:
192
- * - If compliant: transfer the requested amount
193
- * - If not compliant: transfer 0 (no state change, no info leak)
194
- */
195
69
  function transfer(
196
70
  address to,
197
71
  externalEuint64 encryptedAmount,
198
72
  bytes calldata inputProof
199
- ) external returns (bool success) {
73
+ ) external returns (bool) {
200
74
  euint64 amount = FHE.fromExternal(encryptedAmount, inputProof);
201
75
  return _transfer(msg.sender, to, amount);
202
76
  }
203
77
 
204
- /**
205
- * @notice Transfer with euint64 amount (for approved callers)
206
- * @param to Recipient
207
- * @param amount Encrypted amount
208
- * @return success Always true
209
- */
210
- function transfer(address to, euint64 amount) external returns (bool success) {
78
+ function transfer(address to, euint64 amount) external returns (bool) {
211
79
  if (!FHE.isSenderAllowed(amount)) revert UnauthorizedCiphertext();
212
80
  return _transfer(msg.sender, to, amount);
213
81
  }
214
82
 
215
- /**
216
- * @notice Approve spender to transfer tokens
217
- * @param spender Address to approve
218
- * @param encryptedAmount Encrypted allowance amount
219
- * @param inputProof Proof for encrypted input
220
- * @return success Always true
221
- */
222
83
  function approve(
223
84
  address spender,
224
85
  externalEuint64 encryptedAmount,
225
86
  bytes calldata inputProof
226
- ) external returns (bool success) {
87
+ ) external returns (bool) {
227
88
  euint64 amount = FHE.fromExternal(encryptedAmount, inputProof);
228
89
  allowances[msg.sender][spender] = amount;
229
90
  FHE.allowThis(amount);
230
91
  FHE.allow(amount, msg.sender);
231
92
  FHE.allow(amount, spender);
232
-
233
93
  emit Approval(msg.sender, spender);
234
94
  return true;
235
95
  }
236
96
 
237
- /**
238
- * @notice Transfer from another account (requires approval)
239
- * @param from Source address
240
- * @param to Destination address
241
- * @param encryptedAmount Encrypted amount
242
- * @param inputProof Proof for encrypted input
243
- * @return success Always true
244
- */
245
97
  function transferFrom(
246
98
  address from,
247
99
  address to,
248
100
  externalEuint64 encryptedAmount,
249
101
  bytes calldata inputProof
250
- ) external returns (bool success) {
102
+ ) external returns (bool) {
251
103
  euint64 amount = FHE.fromExternal(encryptedAmount, inputProof);
252
104
 
253
- // Check allowance
254
105
  ebool hasAllowance = FHE.le(amount, allowances[from][msg.sender]);
255
-
256
- // Reduce allowance (branch-free)
257
106
  euint64 newAllowance = FHE.select(
258
107
  hasAllowance,
259
108
  FHE.sub(allowances[from][msg.sender], amount),
@@ -264,116 +113,53 @@ contract CompliantERC20 is ZamaEthereumConfig {
264
113
  FHE.allow(newAllowance, from);
265
114
  FHE.allow(newAllowance, msg.sender);
266
115
 
267
- // Only transfer if allowance was sufficient
268
116
  euint64 actualAmount = FHE.select(hasAllowance, amount, FHE.asEuint64(0));
269
-
270
117
  return _transfer(from, to, actualAmount);
271
118
  }
272
119
 
273
- // ============ View Functions ============
120
+ // ============ View ============
274
121
 
275
- /**
276
- * @notice Get encrypted balance
277
- * @param account Address to query
278
- * @return Encrypted balance
279
- */
280
122
  function balanceOf(address account) external view returns (euint64) {
281
123
  return balances[account];
282
124
  }
283
125
 
284
- /**
285
- * @notice Get encrypted allowance
286
- * @param account Owner address
287
- * @param spender Spender address
288
- * @return Encrypted allowance
289
- */
290
126
  function allowance(address account, address spender) external view returns (euint64) {
291
127
  return allowances[account][spender];
292
128
  }
293
129
 
294
- /**
295
- * @notice Get decimals
296
- * @return Token decimals
297
- */
298
130
  function decimals() external pure returns (uint8) {
299
131
  return DECIMALS;
300
132
  }
301
133
 
302
- // ============ Internal Functions ============
134
+ // ============ Internal ============
303
135
 
304
- /**
305
- * @notice Internal transfer implementation
306
- * @dev The heart of branch-free compliance
307
- *
308
- * Logic flow:
309
- * 1. Check sender compliance (if checker is set)
310
- * 2. Check recipient compliance (if checker is set)
311
- * 3. Check sender has sufficient balance
312
- * 4. Combine all checks with FHE.and()
313
- * 5. Use FHE.select() to set transfer amount:
314
- * - If all checks pass: transfer requested amount
315
- * - If any check fails: transfer 0
316
- * 6. Update balances (even if amount is 0)
317
- *
318
- * @param from Source address
319
- * @param to Destination address
320
- * @param amount Encrypted amount to transfer
321
- * @return success Always returns true (actual transfer may be 0)
322
- */
323
- function _transfer(
324
- address from,
325
- address to,
326
- euint64 amount
327
- ) internal returns (bool success) {
136
+ /// @dev Branch-free transfer: FHE.select(canTransfer, amount, 0)
137
+ function _transfer(address from, address to, euint64 amount) internal returns (bool) {
328
138
  ebool canTransfer;
329
139
 
330
- // Check compliance if checker is set
331
140
  if (address(complianceChecker) != address(0)) {
332
141
  ebool senderCompliant = complianceChecker.checkCompliance(from);
333
142
  ebool recipientCompliant = complianceChecker.checkCompliance(to);
334
143
  ebool bothCompliant = FHE.and(senderCompliant, recipientCompliant);
335
-
336
- // Check sufficient balance
337
144
  ebool hasSufficientBalance = FHE.le(amount, balances[from]);
338
-
339
- // Combine all conditions
340
145
  canTransfer = FHE.and(bothCompliant, hasSufficientBalance);
341
146
  } else {
342
- // No compliance checker, only check balance
343
147
  canTransfer = FHE.le(amount, balances[from]);
344
148
  }
345
149
 
346
- // Branch-free: select actual amount or 0
347
150
  euint64 actualAmount = FHE.select(canTransfer, amount, FHE.asEuint64(0));
348
-
349
- // Update balances
350
151
  euint64 newFromBalance = FHE.sub(balances[from], actualAmount);
351
152
  euint64 newToBalance = FHE.add(balances[to], actualAmount);
352
153
 
353
154
  balances[from] = newFromBalance;
354
155
  balances[to] = newToBalance;
355
156
 
356
- // Set permissions
357
157
  FHE.allowThis(newFromBalance);
358
158
  FHE.allowThis(newToBalance);
359
159
  FHE.allow(newFromBalance, from);
360
160
  FHE.allow(newToBalance, to);
361
161
 
362
- // Always emit (hides success/failure)
363
162
  emit Transfer(from, to);
364
-
365
163
  return true;
366
164
  }
367
165
  }
368
-
369
- /**
370
- * @title IComplianceChecker
371
- * @author Gustavo Valverde
372
- * @notice Interface for compliance checking contracts
373
- */
374
- interface IComplianceChecker {
375
- /// @notice Check if a user passes compliance requirements
376
- /// @param user Address to check compliance for
377
- /// @return Encrypted boolean indicating compliance status
378
- function checkCompliance(address user) external returns (ebool);
379
- }
@@ -1,20 +1,15 @@
1
1
  {
2
2
  "network": "hardhat",
3
3
  "chainId": 31337,
4
- "deployedAt": "2025-12-18T22:52:19.870Z",
5
- "deployer": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
6
4
  "contracts": {
7
5
  "IdentityRegistry": {
8
- "address": "0x5FbDB2315678afecb367f032d93F642f64180aa3",
9
- "txHash": "0x4c6d0f381e453643c8f6c7c25d9df71d7d348b3cd6b84d7267c2d5bc03833d77"
6
+ "address": "0x5FC8d32690cc91D4c39d9d3abcBD16989F875707"
10
7
  },
11
8
  "ComplianceRules": {
12
- "address": "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0",
13
- "txHash": "0x6250c7b2f2cd290b65c44bb47df75865a1d35ac2feead60d8725e1c31be2965f"
9
+ "address": "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853"
14
10
  },
15
11
  "CompliantERC20": {
16
- "address": "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9",
17
- "txHash": "0xb4891d2c03a25f4cb8b9f7e8bf806d4e80983a91bdda206c87001d300ee720a5"
12
+ "address": "0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6"
18
13
  }
19
14
  }
20
15
  }