@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.
- package/README.md +160 -0
- package/abi/ComplianceRules.json +352 -0
- package/abi/CompliantERC20.json +493 -0
- package/abi/IdentityRegistry.json +712 -0
- package/abi/index.d.ts +3 -0
- package/abi/index.js +9 -0
- package/contracts/ARCHITECTURE.md +66 -0
- package/contracts/ARCHITECTURE_EXPLAINER.md +77 -0
- package/contracts/compliance/ComplianceRules.sol +255 -0
- package/contracts/core/IdentityRegistry.sol +352 -0
- package/contracts/interfaces/IIdentityRegistry.sol +226 -0
- package/contracts/mocks/.gitkeep +0 -0
- package/contracts/tokens/CompliantERC20.sol +379 -0
- package/deployments/hardhat/addresses.json +20 -0
- package/deployments/localhost/.chainId +1 -0
- package/deployments/localhost/ComplianceRules.json +662 -0
- package/deployments/localhost/CompliantERC20.json +888 -0
- package/deployments/localhost/IdentityRegistry.json +1093 -0
- package/deployments/localhost/solcInputs/e36969353329df673b4fae03d39e01c4.json +60 -0
- package/deployments/sepolia/.chainId +1 -0
- package/deployments/sepolia/.gitkeep +0 -0
- package/deployments/sepolia/ComplianceRules.json +662 -0
- package/deployments/sepolia/CompliantERC20.json +888 -0
- package/deployments/sepolia/IdentityRegistry.json +1093 -0
- package/deployments/sepolia/solcInputs/93d280ff0d4e798a18947a9ed6015031.json +60 -0
- package/dist/index.d.ts +459 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +135 -0
- package/package.json +110 -0
- package/typechain-types/@fhevm/index.ts +5 -0
- package/typechain-types/@fhevm/solidity/config/ZamaConfig.sol/ZamaConfig.ts +69 -0
- package/typechain-types/@fhevm/solidity/config/ZamaConfig.sol/ZamaEthereumConfig.ts +90 -0
- package/typechain-types/@fhevm/solidity/config/ZamaConfig.sol/index.ts +5 -0
- package/typechain-types/@fhevm/solidity/config/index.ts +5 -0
- package/typechain-types/@fhevm/solidity/index.ts +7 -0
- package/typechain-types/@fhevm/solidity/lib/FHE.sol/FHE.ts +112 -0
- package/typechain-types/@fhevm/solidity/lib/FHE.sol/IKMSVerifier.ts +108 -0
- package/typechain-types/@fhevm/solidity/lib/FHE.sol/index.ts +5 -0
- package/typechain-types/@fhevm/solidity/lib/Impl.sol/IACL.ts +190 -0
- package/typechain-types/@fhevm/solidity/lib/Impl.sol/IFHEVMExecutor.ts +623 -0
- package/typechain-types/@fhevm/solidity/lib/Impl.sol/IInputVerifier.ts +90 -0
- package/typechain-types/@fhevm/solidity/lib/Impl.sol/index.ts +6 -0
- package/typechain-types/@fhevm/solidity/lib/index.ts +7 -0
- package/typechain-types/common.ts +131 -0
- package/typechain-types/contracts/compliance/ComplianceRules.ts +479 -0
- package/typechain-types/contracts/compliance/index.ts +4 -0
- package/typechain-types/contracts/core/IdentityRegistry.ts +874 -0
- package/typechain-types/contracts/core/index.ts +4 -0
- package/typechain-types/contracts/index.ts +11 -0
- package/typechain-types/contracts/interfaces/IIdentityRegistry.ts +798 -0
- package/typechain-types/contracts/interfaces/index.ts +4 -0
- package/typechain-types/contracts/tokens/CompliantERC20.sol/CompliantERC20.ts +572 -0
- package/typechain-types/contracts/tokens/CompliantERC20.sol/IComplianceChecker.ts +95 -0
- package/typechain-types/contracts/tokens/CompliantERC20.sol/index.ts +5 -0
- package/typechain-types/contracts/tokens/index.ts +5 -0
- package/typechain-types/factories/@fhevm/index.ts +4 -0
- package/typechain-types/factories/@fhevm/solidity/config/ZamaConfig.sol/ZamaConfig__factory.ts +69 -0
- package/typechain-types/factories/@fhevm/solidity/config/ZamaConfig.sol/ZamaEthereumConfig__factory.ts +43 -0
- package/typechain-types/factories/@fhevm/solidity/config/ZamaConfig.sol/index.ts +5 -0
- package/typechain-types/factories/@fhevm/solidity/config/index.ts +4 -0
- package/typechain-types/factories/@fhevm/solidity/index.ts +5 -0
- package/typechain-types/factories/@fhevm/solidity/lib/FHE.sol/FHE__factory.ts +88 -0
- package/typechain-types/factories/@fhevm/solidity/lib/FHE.sol/IKMSVerifier__factory.ts +54 -0
- package/typechain-types/factories/@fhevm/solidity/lib/FHE.sol/index.ts +5 -0
- package/typechain-types/factories/@fhevm/solidity/lib/Impl.sol/IACL__factory.ts +121 -0
- package/typechain-types/factories/@fhevm/solidity/lib/Impl.sol/IFHEVMExecutor__factory.ts +810 -0
- package/typechain-types/factories/@fhevm/solidity/lib/Impl.sol/IInputVerifier__factory.ts +32 -0
- package/typechain-types/factories/@fhevm/solidity/lib/Impl.sol/index.ts +6 -0
- package/typechain-types/factories/@fhevm/solidity/lib/index.ts +5 -0
- package/typechain-types/factories/contracts/compliance/ComplianceRules__factory.ts +437 -0
- package/typechain-types/factories/contracts/compliance/index.ts +4 -0
- package/typechain-types/factories/contracts/core/IdentityRegistry__factory.ts +777 -0
- package/typechain-types/factories/contracts/core/index.ts +4 -0
- package/typechain-types/factories/contracts/index.ts +7 -0
- package/typechain-types/factories/contracts/interfaces/IIdentityRegistry__factory.ts +640 -0
- package/typechain-types/factories/contracts/interfaces/index.ts +4 -0
- package/typechain-types/factories/contracts/tokens/CompliantERC20.sol/CompliantERC20__factory.ts +581 -0
- package/typechain-types/factories/contracts/tokens/CompliantERC20.sol/IComplianceChecker__factory.ts +44 -0
- package/typechain-types/factories/contracts/tokens/CompliantERC20.sol/index.ts +5 -0
- package/typechain-types/factories/contracts/tokens/index.ts +4 -0
- package/typechain-types/factories/index.ts +5 -0
- package/typechain-types/hardhat.d.ts +261 -0
- package/typechain-types/index.ts +32 -0
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// solhint-disable func-name-mixedcase
|
|
3
|
+
pragma solidity ^0.8.27;
|
|
4
|
+
|
|
5
|
+
import {FHE, euint64, ebool, externalEuint64} from "@fhevm/solidity/lib/FHE.sol";
|
|
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
|
|
33
|
+
string public name;
|
|
34
|
+
|
|
35
|
+
/// @notice Token symbol
|
|
36
|
+
string public symbol;
|
|
37
|
+
|
|
38
|
+
/// @notice Token decimals
|
|
39
|
+
uint8 public constant DECIMALS = 18;
|
|
40
|
+
|
|
41
|
+
/// @notice Total supply (public for transparency)
|
|
42
|
+
uint256 public totalSupply;
|
|
43
|
+
|
|
44
|
+
// ============ Token State ============
|
|
45
|
+
|
|
46
|
+
/// @notice Encrypted balances
|
|
47
|
+
mapping(address account => euint64 balance) private balances;
|
|
48
|
+
|
|
49
|
+
/// @notice Encrypted allowances
|
|
50
|
+
mapping(address owner => mapping(address spender => euint64 allowance)) private allowances;
|
|
51
|
+
|
|
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;
|
|
61
|
+
|
|
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
|
+
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
|
+
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
|
+
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
|
+
event ComplianceCheckerUpdated(address indexed newChecker);
|
|
82
|
+
|
|
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
|
+
error ComplianceCheckerNotSet();
|
|
104
|
+
/// @notice Thrown when caller supplies an unauthorized ciphertext handle
|
|
105
|
+
error UnauthorizedCiphertext();
|
|
106
|
+
/// @notice Thrown when mint amount would exceed uint64 accounting bounds
|
|
107
|
+
error TotalSupplyOverflow();
|
|
108
|
+
|
|
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) {
|
|
118
|
+
name = tokenName;
|
|
119
|
+
symbol = tokenSymbol;
|
|
120
|
+
owner = msg.sender;
|
|
121
|
+
if (checker != address(0)) {
|
|
122
|
+
complianceChecker = IComplianceChecker(checker);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// ============ Admin Functions ============
|
|
127
|
+
|
|
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);
|
|
135
|
+
emit ComplianceCheckerUpdated(checker);
|
|
136
|
+
}
|
|
137
|
+
|
|
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();
|
|
168
|
+
if (amount > type(uint64).max) revert TotalSupplyOverflow();
|
|
169
|
+
if (totalSupply + amount > type(uint64).max) revert TotalSupplyOverflow();
|
|
170
|
+
|
|
171
|
+
euint64 mintAmount = FHE.asEuint64(uint64(amount));
|
|
172
|
+
balances[to] = FHE.add(balances[to], mintAmount);
|
|
173
|
+
FHE.allowThis(balances[to]);
|
|
174
|
+
FHE.allow(balances[to], to);
|
|
175
|
+
|
|
176
|
+
totalSupply += amount;
|
|
177
|
+
|
|
178
|
+
emit Mint(to, amount);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// ============ Token Functions ============
|
|
182
|
+
|
|
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
|
+
function transfer(
|
|
196
|
+
address to,
|
|
197
|
+
externalEuint64 encryptedAmount,
|
|
198
|
+
bytes calldata inputProof
|
|
199
|
+
) external returns (bool success) {
|
|
200
|
+
euint64 amount = FHE.fromExternal(encryptedAmount, inputProof);
|
|
201
|
+
return _transfer(msg.sender, to, amount);
|
|
202
|
+
}
|
|
203
|
+
|
|
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) {
|
|
211
|
+
if (!FHE.isSenderAllowed(amount)) revert UnauthorizedCiphertext();
|
|
212
|
+
return _transfer(msg.sender, to, amount);
|
|
213
|
+
}
|
|
214
|
+
|
|
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
|
+
function approve(
|
|
223
|
+
address spender,
|
|
224
|
+
externalEuint64 encryptedAmount,
|
|
225
|
+
bytes calldata inputProof
|
|
226
|
+
) external returns (bool success) {
|
|
227
|
+
euint64 amount = FHE.fromExternal(encryptedAmount, inputProof);
|
|
228
|
+
allowances[msg.sender][spender] = amount;
|
|
229
|
+
FHE.allowThis(amount);
|
|
230
|
+
FHE.allow(amount, msg.sender);
|
|
231
|
+
FHE.allow(amount, spender);
|
|
232
|
+
|
|
233
|
+
emit Approval(msg.sender, spender);
|
|
234
|
+
return true;
|
|
235
|
+
}
|
|
236
|
+
|
|
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
|
+
function transferFrom(
|
|
246
|
+
address from,
|
|
247
|
+
address to,
|
|
248
|
+
externalEuint64 encryptedAmount,
|
|
249
|
+
bytes calldata inputProof
|
|
250
|
+
) external returns (bool success) {
|
|
251
|
+
euint64 amount = FHE.fromExternal(encryptedAmount, inputProof);
|
|
252
|
+
|
|
253
|
+
// Check allowance
|
|
254
|
+
ebool hasAllowance = FHE.le(amount, allowances[from][msg.sender]);
|
|
255
|
+
|
|
256
|
+
// Reduce allowance (branch-free)
|
|
257
|
+
euint64 newAllowance = FHE.select(
|
|
258
|
+
hasAllowance,
|
|
259
|
+
FHE.sub(allowances[from][msg.sender], amount),
|
|
260
|
+
allowances[from][msg.sender]
|
|
261
|
+
);
|
|
262
|
+
allowances[from][msg.sender] = newAllowance;
|
|
263
|
+
FHE.allowThis(newAllowance);
|
|
264
|
+
FHE.allow(newAllowance, from);
|
|
265
|
+
FHE.allow(newAllowance, msg.sender);
|
|
266
|
+
|
|
267
|
+
// Only transfer if allowance was sufficient
|
|
268
|
+
euint64 actualAmount = FHE.select(hasAllowance, amount, FHE.asEuint64(0));
|
|
269
|
+
|
|
270
|
+
return _transfer(from, to, actualAmount);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// ============ View Functions ============
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* @notice Get encrypted balance
|
|
277
|
+
* @param account Address to query
|
|
278
|
+
* @return Encrypted balance
|
|
279
|
+
*/
|
|
280
|
+
function balanceOf(address account) external view returns (euint64) {
|
|
281
|
+
return balances[account];
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* @notice Get encrypted allowance
|
|
286
|
+
* @param account Owner address
|
|
287
|
+
* @param spender Spender address
|
|
288
|
+
* @return Encrypted allowance
|
|
289
|
+
*/
|
|
290
|
+
function allowance(address account, address spender) external view returns (euint64) {
|
|
291
|
+
return allowances[account][spender];
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* @notice Get decimals
|
|
296
|
+
* @return Token decimals
|
|
297
|
+
*/
|
|
298
|
+
function decimals() external pure returns (uint8) {
|
|
299
|
+
return DECIMALS;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// ============ Internal Functions ============
|
|
303
|
+
|
|
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) {
|
|
328
|
+
ebool canTransfer;
|
|
329
|
+
|
|
330
|
+
// Check compliance if checker is set
|
|
331
|
+
if (address(complianceChecker) != address(0)) {
|
|
332
|
+
ebool senderCompliant = complianceChecker.checkCompliance(from);
|
|
333
|
+
ebool recipientCompliant = complianceChecker.checkCompliance(to);
|
|
334
|
+
ebool bothCompliant = FHE.and(senderCompliant, recipientCompliant);
|
|
335
|
+
|
|
336
|
+
// Check sufficient balance
|
|
337
|
+
ebool hasSufficientBalance = FHE.le(amount, balances[from]);
|
|
338
|
+
|
|
339
|
+
// Combine all conditions
|
|
340
|
+
canTransfer = FHE.and(bothCompliant, hasSufficientBalance);
|
|
341
|
+
} else {
|
|
342
|
+
// No compliance checker, only check balance
|
|
343
|
+
canTransfer = FHE.le(amount, balances[from]);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// Branch-free: select actual amount or 0
|
|
347
|
+
euint64 actualAmount = FHE.select(canTransfer, amount, FHE.asEuint64(0));
|
|
348
|
+
|
|
349
|
+
// Update balances
|
|
350
|
+
euint64 newFromBalance = FHE.sub(balances[from], actualAmount);
|
|
351
|
+
euint64 newToBalance = FHE.add(balances[to], actualAmount);
|
|
352
|
+
|
|
353
|
+
balances[from] = newFromBalance;
|
|
354
|
+
balances[to] = newToBalance;
|
|
355
|
+
|
|
356
|
+
// Set permissions
|
|
357
|
+
FHE.allowThis(newFromBalance);
|
|
358
|
+
FHE.allowThis(newToBalance);
|
|
359
|
+
FHE.allow(newFromBalance, from);
|
|
360
|
+
FHE.allow(newToBalance, to);
|
|
361
|
+
|
|
362
|
+
// Always emit (hides success/failure)
|
|
363
|
+
emit Transfer(from, to);
|
|
364
|
+
|
|
365
|
+
return true;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
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
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"network": "hardhat",
|
|
3
|
+
"chainId": 31337,
|
|
4
|
+
"deployedAt": "2025-12-18T22:52:19.870Z",
|
|
5
|
+
"deployer": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
|
|
6
|
+
"contracts": {
|
|
7
|
+
"IdentityRegistry": {
|
|
8
|
+
"address": "0x5FbDB2315678afecb367f032d93F642f64180aa3",
|
|
9
|
+
"txHash": "0x4c6d0f381e453643c8f6c7c25d9df71d7d348b3cd6b84d7267c2d5bc03833d77"
|
|
10
|
+
},
|
|
11
|
+
"ComplianceRules": {
|
|
12
|
+
"address": "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0",
|
|
13
|
+
"txHash": "0x6250c7b2f2cd290b65c44bb47df75865a1d35ac2feead60d8725e1c31be2965f"
|
|
14
|
+
},
|
|
15
|
+
"CompliantERC20": {
|
|
16
|
+
"address": "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9",
|
|
17
|
+
"txHash": "0xb4891d2c03a25f4cb8b9f7e8bf806d4e80983a91bdda206c87001d300ee720a5"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
31337
|