@zentity/fhevm-contracts 0.3.0 → 0.4.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/abi/ComplianceRules.json +25 -16
- package/abi/CompliantERC20.json +30 -16
- package/abi/IdentityRegistry.json +496 -119
- package/contracts/compliance/ComplianceRules.sol +40 -189
- package/contracts/core/IdentityRegistry.sol +280 -232
- package/contracts/interfaces/IComplianceRules.sol +30 -0
- package/contracts/interfaces/IIdentityRegistry.sol +133 -159
- package/contracts/proxy/ERC1967Proxy.sol +6 -0
- package/contracts/test/MockFacilitator.sol +40 -0
- package/contracts/tokens/CompliantERC20.sol +28 -242
- package/deployments/hardhat/addresses.json +3 -8
- package/dist/index.d.ts +175 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +44 -1
- package/package.json +1 -1
- package/typechain-types/@openzeppelin/contracts/access/Ownable.ts +153 -0
- package/typechain-types/@openzeppelin/contracts/access/Ownable2Step.ts +217 -0
- package/typechain-types/@openzeppelin/contracts/access/index.ts +5 -0
- package/typechain-types/@openzeppelin/contracts/index.ts +11 -0
- package/typechain-types/@openzeppelin/contracts/interfaces/IERC1967.ts +168 -0
- package/typechain-types/@openzeppelin/contracts/interfaces/IERC5267.ts +151 -0
- package/typechain-types/{contracts/tokens/CompliantERC20.sol/IComplianceChecker.ts → @openzeppelin/contracts/interfaces/draft-IERC1822.sol/IERC1822Proxiable.ts} +12 -17
- package/typechain-types/@openzeppelin/contracts/interfaces/draft-IERC1822.sol/index.ts +4 -0
- package/typechain-types/@openzeppelin/contracts/interfaces/index.ts +7 -0
- package/typechain-types/@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.ts +105 -0
- package/typechain-types/@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.ts +69 -0
- package/typechain-types/@openzeppelin/contracts/proxy/ERC1967/index.ts +5 -0
- package/typechain-types/@openzeppelin/contracts/proxy/Proxy.ts +69 -0
- package/typechain-types/@openzeppelin/contracts/proxy/beacon/IBeacon.ts +90 -0
- package/typechain-types/@openzeppelin/contracts/proxy/beacon/index.ts +4 -0
- package/typechain-types/@openzeppelin/contracts/proxy/index.ts +8 -0
- package/typechain-types/@openzeppelin/contracts/utils/Address.ts +69 -0
- package/typechain-types/@openzeppelin/contracts/utils/Errors.ts +69 -0
- package/typechain-types/@openzeppelin/contracts/utils/Strings.ts +69 -0
- package/typechain-types/@openzeppelin/contracts/utils/cryptography/ECDSA.ts +69 -0
- package/typechain-types/@openzeppelin/contracts/utils/cryptography/index.ts +4 -0
- package/typechain-types/@openzeppelin/contracts/utils/index.ts +10 -0
- package/typechain-types/@openzeppelin/contracts/utils/math/SafeCast.ts +69 -0
- package/typechain-types/@openzeppelin/contracts/utils/math/index.ts +4 -0
- package/typechain-types/@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.ts +251 -0
- package/typechain-types/@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.ts +186 -0
- package/typechain-types/@openzeppelin/contracts-upgradeable/access/index.ts +5 -0
- package/typechain-types/@openzeppelin/contracts-upgradeable/index.ts +9 -0
- package/typechain-types/@openzeppelin/contracts-upgradeable/proxy/index.ts +5 -0
- package/typechain-types/@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.ts +105 -0
- package/typechain-types/@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.ts +196 -0
- package/typechain-types/@openzeppelin/contracts-upgradeable/proxy/utils/index.ts +5 -0
- package/typechain-types/@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.ts +105 -0
- package/typechain-types/@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.ts +184 -0
- package/typechain-types/@openzeppelin/contracts-upgradeable/utils/cryptography/index.ts +4 -0
- package/typechain-types/@openzeppelin/contracts-upgradeable/utils/index.ts +6 -0
- package/typechain-types/@openzeppelin/index.ts +7 -0
- package/typechain-types/contracts/compliance/ComplianceRules.ts +18 -7
- package/typechain-types/contracts/core/IdentityRegistry.ts +493 -233
- package/typechain-types/contracts/index.ts +2 -0
- package/typechain-types/contracts/interfaces/IComplianceRules.ts +290 -0
- package/typechain-types/contracts/interfaces/IIdentityRegistry.ts +269 -341
- package/typechain-types/contracts/interfaces/index.ts +1 -0
- package/typechain-types/contracts/test/MockFacilitator.ts +187 -0
- package/typechain-types/contracts/test/index.ts +4 -0
- package/typechain-types/contracts/tokens/{CompliantERC20.sol/CompliantERC20.ts → CompliantERC20.ts} +19 -8
- package/typechain-types/contracts/tokens/index.ts +1 -2
- package/typechain-types/factories/@openzeppelin/contracts/access/Ownable2Step__factory.ts +138 -0
- package/typechain-types/factories/@openzeppelin/contracts/access/Ownable__factory.ts +96 -0
- package/typechain-types/factories/@openzeppelin/contracts/access/index.ts +5 -0
- package/typechain-types/factories/@openzeppelin/contracts/index.ts +7 -0
- package/typechain-types/factories/@openzeppelin/contracts/interfaces/IERC1967__factory.ts +67 -0
- package/typechain-types/factories/@openzeppelin/contracts/interfaces/IERC5267__factory.ts +71 -0
- package/typechain-types/factories/@openzeppelin/contracts/interfaces/draft-IERC1822.sol/IERC1822Proxiable__factory.ts +38 -0
- package/typechain-types/factories/@openzeppelin/contracts/interfaces/draft-IERC1822.sol/index.ts +4 -0
- package/typechain-types/factories/@openzeppelin/contracts/interfaces/index.ts +6 -0
- package/typechain-types/factories/@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy__factory.ts +144 -0
- package/typechain-types/factories/@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils__factory.ts +105 -0
- package/typechain-types/factories/@openzeppelin/contracts/proxy/ERC1967/index.ts +5 -0
- package/typechain-types/factories/@openzeppelin/contracts/proxy/Proxy__factory.ts +26 -0
- package/typechain-types/factories/@openzeppelin/contracts/proxy/beacon/IBeacon__factory.ts +35 -0
- package/typechain-types/factories/@openzeppelin/contracts/proxy/beacon/index.ts +4 -0
- package/typechain-types/factories/@openzeppelin/contracts/proxy/index.ts +6 -0
- package/typechain-types/factories/@openzeppelin/contracts/utils/Address__factory.ts +75 -0
- package/typechain-types/factories/@openzeppelin/contracts/utils/Errors__factory.ts +101 -0
- package/typechain-types/factories/@openzeppelin/contracts/utils/Strings__factory.ts +90 -0
- package/typechain-types/factories/@openzeppelin/contracts/utils/cryptography/ECDSA__factory.ts +91 -0
- package/typechain-types/factories/@openzeppelin/contracts/utils/cryptography/index.ts +4 -0
- package/typechain-types/factories/@openzeppelin/contracts/utils/index.ts +8 -0
- package/typechain-types/factories/@openzeppelin/contracts/utils/math/SafeCast__factory.ts +118 -0
- package/typechain-types/factories/@openzeppelin/contracts/utils/math/index.ts +4 -0
- package/typechain-types/factories/@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable__factory.ts +165 -0
- package/typechain-types/factories/@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable__factory.ts +122 -0
- package/typechain-types/factories/@openzeppelin/contracts-upgradeable/access/index.ts +5 -0
- package/typechain-types/factories/@openzeppelin/contracts-upgradeable/index.ts +6 -0
- package/typechain-types/factories/@openzeppelin/contracts-upgradeable/proxy/index.ts +4 -0
- package/typechain-types/factories/@openzeppelin/contracts-upgradeable/proxy/utils/Initializable__factory.ts +48 -0
- package/typechain-types/factories/@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable__factory.ts +153 -0
- package/typechain-types/factories/@openzeppelin/contracts-upgradeable/proxy/utils/index.ts +5 -0
- package/typechain-types/factories/@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable__factory.ts +48 -0
- package/typechain-types/factories/@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable__factory.ts +97 -0
- package/typechain-types/factories/@openzeppelin/contracts-upgradeable/utils/cryptography/index.ts +4 -0
- package/typechain-types/factories/@openzeppelin/contracts-upgradeable/utils/index.ts +5 -0
- package/typechain-types/factories/@openzeppelin/index.ts +5 -0
- package/typechain-types/factories/contracts/compliance/ComplianceRules__factory.ts +26 -17
- package/typechain-types/factories/contracts/core/IdentityRegistry__factory.ts +493 -116
- package/typechain-types/factories/contracts/index.ts +1 -0
- package/typechain-types/factories/contracts/interfaces/IComplianceRules__factory.ts +193 -0
- package/typechain-types/factories/contracts/interfaces/IIdentityRegistry__factory.ts +236 -186
- package/typechain-types/factories/contracts/interfaces/index.ts +1 -0
- package/typechain-types/factories/contracts/test/MockFacilitator__factory.ts +194 -0
- package/typechain-types/factories/contracts/test/index.ts +4 -0
- package/typechain-types/factories/contracts/tokens/CompliantERC20__factory.ts +595 -0
- package/typechain-types/factories/contracts/tokens/index.ts +1 -1
- package/typechain-types/factories/index.ts +1 -0
- package/typechain-types/hardhat.d.ts +394 -16
- package/typechain-types/index.ts +48 -4
- package/deployments/hardhat/.chainId +0 -1
- package/typechain-types/contracts/tokens/CompliantERC20.sol/index.ts +0 -5
- package/typechain-types/factories/contracts/tokens/CompliantERC20.sol/CompliantERC20__factory.ts +0 -581
- package/typechain-types/factories/contracts/tokens/CompliantERC20.sol/IComplianceChecker__factory.ts +0 -44
- 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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
-
|
|
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
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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 =
|
|
43
|
+
complianceChecker = IComplianceRules(checker);
|
|
123
44
|
}
|
|
124
45
|
}
|
|
125
46
|
|
|
126
|
-
// ============ Admin
|
|
47
|
+
// ============ Admin ============
|
|
127
48
|
|
|
128
|
-
|
|
129
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
134
|
+
// ============ Internal ============
|
|
303
135
|
|
|
304
|
-
|
|
305
|
-
|
|
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": "
|
|
9
|
-
"txHash": "0x4c6d0f381e453643c8f6c7c25d9df71d7d348b3cd6b84d7267c2d5bc03833d77"
|
|
6
|
+
"address": "0x5FC8d32690cc91D4c39d9d3abcBD16989F875707"
|
|
10
7
|
},
|
|
11
8
|
"ComplianceRules": {
|
|
12
|
-
"address": "
|
|
13
|
-
"txHash": "0x6250c7b2f2cd290b65c44bb47df75865a1d35ac2feead60d8725e1c31be2965f"
|
|
9
|
+
"address": "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853"
|
|
14
10
|
},
|
|
15
11
|
"CompliantERC20": {
|
|
16
|
-
"address": "
|
|
17
|
-
"txHash": "0xb4891d2c03a25f4cb8b9f7e8bf806d4e80983a91bdda206c87001d300ee720a5"
|
|
12
|
+
"address": "0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6"
|
|
18
13
|
}
|
|
19
14
|
}
|
|
20
15
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -42,7 +42,11 @@ export declare const ABIS: {
|
|
|
42
42
|
anonymous?: undefined;
|
|
43
43
|
outputs?: undefined;
|
|
44
44
|
} | {
|
|
45
|
-
inputs:
|
|
45
|
+
inputs: {
|
|
46
|
+
internalType: string;
|
|
47
|
+
name: string;
|
|
48
|
+
type: string;
|
|
49
|
+
}[];
|
|
46
50
|
name: string;
|
|
47
51
|
type: string;
|
|
48
52
|
stateMutability?: undefined;
|
|
@@ -60,6 +64,27 @@ export declare const ABIS: {
|
|
|
60
64
|
type: string;
|
|
61
65
|
stateMutability?: undefined;
|
|
62
66
|
outputs?: undefined;
|
|
67
|
+
} | {
|
|
68
|
+
inputs: ({
|
|
69
|
+
components: {
|
|
70
|
+
internalType: string;
|
|
71
|
+
name: string;
|
|
72
|
+
type: string;
|
|
73
|
+
}[];
|
|
74
|
+
internalType: string;
|
|
75
|
+
name: string;
|
|
76
|
+
type: string;
|
|
77
|
+
} | {
|
|
78
|
+
internalType: string;
|
|
79
|
+
name: string;
|
|
80
|
+
type: string;
|
|
81
|
+
components?: undefined;
|
|
82
|
+
})[];
|
|
83
|
+
name: string;
|
|
84
|
+
outputs: never[];
|
|
85
|
+
stateMutability: string;
|
|
86
|
+
type: string;
|
|
87
|
+
anonymous?: undefined;
|
|
63
88
|
} | {
|
|
64
89
|
inputs: {
|
|
65
90
|
internalType: string;
|
|
@@ -88,7 +113,11 @@ export declare const ABIS: {
|
|
|
88
113
|
anonymous?: undefined;
|
|
89
114
|
outputs?: undefined;
|
|
90
115
|
} | {
|
|
91
|
-
inputs:
|
|
116
|
+
inputs: {
|
|
117
|
+
internalType: string;
|
|
118
|
+
name: string;
|
|
119
|
+
type: string;
|
|
120
|
+
}[];
|
|
92
121
|
name: string;
|
|
93
122
|
type: string;
|
|
94
123
|
stateMutability?: undefined;
|
|
@@ -134,7 +163,11 @@ export declare const ABIS: {
|
|
|
134
163
|
anonymous?: undefined;
|
|
135
164
|
outputs?: undefined;
|
|
136
165
|
} | {
|
|
137
|
-
inputs:
|
|
166
|
+
inputs: {
|
|
167
|
+
internalType: string;
|
|
168
|
+
name: string;
|
|
169
|
+
type: string;
|
|
170
|
+
}[];
|
|
138
171
|
name: string;
|
|
139
172
|
type: string;
|
|
140
173
|
stateMutability?: undefined;
|
|
@@ -182,7 +215,11 @@ export declare function getAbi(name: ContractName): ({
|
|
|
182
215
|
anonymous?: undefined;
|
|
183
216
|
outputs?: undefined;
|
|
184
217
|
} | {
|
|
185
|
-
inputs:
|
|
218
|
+
inputs: {
|
|
219
|
+
internalType: string;
|
|
220
|
+
name: string;
|
|
221
|
+
type: string;
|
|
222
|
+
}[];
|
|
186
223
|
name: string;
|
|
187
224
|
type: string;
|
|
188
225
|
stateMutability?: undefined;
|
|
@@ -227,7 +264,11 @@ export declare function getAbi(name: ContractName): ({
|
|
|
227
264
|
anonymous?: undefined;
|
|
228
265
|
outputs?: undefined;
|
|
229
266
|
} | {
|
|
230
|
-
inputs:
|
|
267
|
+
inputs: {
|
|
268
|
+
internalType: string;
|
|
269
|
+
name: string;
|
|
270
|
+
type: string;
|
|
271
|
+
}[];
|
|
231
272
|
name: string;
|
|
232
273
|
type: string;
|
|
233
274
|
stateMutability?: undefined;
|
|
@@ -268,7 +309,11 @@ export declare function getAbi(name: ContractName): ({
|
|
|
268
309
|
anonymous?: undefined;
|
|
269
310
|
outputs?: undefined;
|
|
270
311
|
} | {
|
|
271
|
-
inputs:
|
|
312
|
+
inputs: {
|
|
313
|
+
internalType: string;
|
|
314
|
+
name: string;
|
|
315
|
+
type: string;
|
|
316
|
+
}[];
|
|
272
317
|
name: string;
|
|
273
318
|
type: string;
|
|
274
319
|
stateMutability?: undefined;
|
|
@@ -286,6 +331,27 @@ export declare function getAbi(name: ContractName): ({
|
|
|
286
331
|
type: string;
|
|
287
332
|
stateMutability?: undefined;
|
|
288
333
|
outputs?: undefined;
|
|
334
|
+
} | {
|
|
335
|
+
inputs: ({
|
|
336
|
+
components: {
|
|
337
|
+
internalType: string;
|
|
338
|
+
name: string;
|
|
339
|
+
type: string;
|
|
340
|
+
}[];
|
|
341
|
+
internalType: string;
|
|
342
|
+
name: string;
|
|
343
|
+
type: string;
|
|
344
|
+
} | {
|
|
345
|
+
internalType: string;
|
|
346
|
+
name: string;
|
|
347
|
+
type: string;
|
|
348
|
+
components?: undefined;
|
|
349
|
+
})[];
|
|
350
|
+
name: string;
|
|
351
|
+
outputs: never[];
|
|
352
|
+
stateMutability: string;
|
|
353
|
+
type: string;
|
|
354
|
+
anonymous?: undefined;
|
|
289
355
|
} | {
|
|
290
356
|
inputs: {
|
|
291
357
|
internalType: string;
|
|
@@ -310,7 +376,11 @@ export declare const IdentityRegistryABI: ({
|
|
|
310
376
|
anonymous?: undefined;
|
|
311
377
|
outputs?: undefined;
|
|
312
378
|
} | {
|
|
313
|
-
inputs:
|
|
379
|
+
inputs: {
|
|
380
|
+
internalType: string;
|
|
381
|
+
name: string;
|
|
382
|
+
type: string;
|
|
383
|
+
}[];
|
|
314
384
|
name: string;
|
|
315
385
|
type: string;
|
|
316
386
|
stateMutability?: undefined;
|
|
@@ -328,6 +398,27 @@ export declare const IdentityRegistryABI: ({
|
|
|
328
398
|
type: string;
|
|
329
399
|
stateMutability?: undefined;
|
|
330
400
|
outputs?: undefined;
|
|
401
|
+
} | {
|
|
402
|
+
inputs: ({
|
|
403
|
+
components: {
|
|
404
|
+
internalType: string;
|
|
405
|
+
name: string;
|
|
406
|
+
type: string;
|
|
407
|
+
}[];
|
|
408
|
+
internalType: string;
|
|
409
|
+
name: string;
|
|
410
|
+
type: string;
|
|
411
|
+
} | {
|
|
412
|
+
internalType: string;
|
|
413
|
+
name: string;
|
|
414
|
+
type: string;
|
|
415
|
+
components?: undefined;
|
|
416
|
+
})[];
|
|
417
|
+
name: string;
|
|
418
|
+
outputs: never[];
|
|
419
|
+
stateMutability: string;
|
|
420
|
+
type: string;
|
|
421
|
+
anonymous?: undefined;
|
|
331
422
|
} | {
|
|
332
423
|
inputs: {
|
|
333
424
|
internalType: string;
|
|
@@ -356,7 +447,11 @@ export declare const ComplianceRulesABI: ({
|
|
|
356
447
|
anonymous?: undefined;
|
|
357
448
|
outputs?: undefined;
|
|
358
449
|
} | {
|
|
359
|
-
inputs:
|
|
450
|
+
inputs: {
|
|
451
|
+
internalType: string;
|
|
452
|
+
name: string;
|
|
453
|
+
type: string;
|
|
454
|
+
}[];
|
|
360
455
|
name: string;
|
|
361
456
|
type: string;
|
|
362
457
|
stateMutability?: undefined;
|
|
@@ -402,7 +497,11 @@ export declare const CompliantERC20ABI: ({
|
|
|
402
497
|
anonymous?: undefined;
|
|
403
498
|
outputs?: undefined;
|
|
404
499
|
} | {
|
|
405
|
-
inputs:
|
|
500
|
+
inputs: {
|
|
501
|
+
internalType: string;
|
|
502
|
+
name: string;
|
|
503
|
+
type: string;
|
|
504
|
+
}[];
|
|
406
505
|
name: string;
|
|
407
506
|
type: string;
|
|
408
507
|
stateMutability?: undefined;
|
|
@@ -445,6 +544,73 @@ export declare function getContractAddresses(networkOrChainId: NetworkName | num
|
|
|
445
544
|
prefer?: NetworkName;
|
|
446
545
|
overrides?: Partial<ContractAddresses>;
|
|
447
546
|
}): ContractAddresses;
|
|
547
|
+
/** Attribute bitmask constants for grantAttributeAccess */
|
|
548
|
+
export declare const ATTR: {
|
|
549
|
+
readonly BIRTH_YEAR: 1;
|
|
550
|
+
readonly COUNTRY: 2;
|
|
551
|
+
readonly COMPLIANCE: 4;
|
|
552
|
+
readonly BLACKLIST: 8;
|
|
553
|
+
readonly ALL: 15;
|
|
554
|
+
};
|
|
555
|
+
/** Purpose enum matching the Solidity `IIdentityRegistry.Purpose` */
|
|
556
|
+
export declare enum Purpose {
|
|
557
|
+
COMPLIANCE_CHECK = 0,
|
|
558
|
+
AGE_VERIFICATION = 1,
|
|
559
|
+
NATIONALITY_CHECK = 2,
|
|
560
|
+
TRANSFER_GATING = 3,
|
|
561
|
+
AUDIT = 4
|
|
562
|
+
}
|
|
563
|
+
/** EIP-712 type definition for the attestation permit (compatible with ethers/viem signTypedData) */
|
|
564
|
+
export declare const ATTEST_PERMIT_TYPES: {
|
|
565
|
+
readonly AttestPermit: readonly [{
|
|
566
|
+
readonly name: "user";
|
|
567
|
+
readonly type: "address";
|
|
568
|
+
}, {
|
|
569
|
+
readonly name: "birthYearOffset";
|
|
570
|
+
readonly type: "uint8";
|
|
571
|
+
}, {
|
|
572
|
+
readonly name: "countryCode";
|
|
573
|
+
readonly type: "uint16";
|
|
574
|
+
}, {
|
|
575
|
+
readonly name: "complianceLevel";
|
|
576
|
+
readonly type: "uint8";
|
|
577
|
+
}, {
|
|
578
|
+
readonly name: "isBlacklisted";
|
|
579
|
+
readonly type: "bool";
|
|
580
|
+
}, {
|
|
581
|
+
readonly name: "proofSetHash";
|
|
582
|
+
readonly type: "bytes32";
|
|
583
|
+
}, {
|
|
584
|
+
readonly name: "policyVersion";
|
|
585
|
+
readonly type: "uint32";
|
|
586
|
+
}, {
|
|
587
|
+
readonly name: "nonce";
|
|
588
|
+
readonly type: "uint256";
|
|
589
|
+
}, {
|
|
590
|
+
readonly name: "deadline";
|
|
591
|
+
readonly type: "uint256";
|
|
592
|
+
}];
|
|
593
|
+
};
|
|
594
|
+
/** Build the EIP-712 domain for attestation permit signing */
|
|
595
|
+
export declare function getAttestPermitDomain(chainId: number, registryAddress: string): {
|
|
596
|
+
name: string;
|
|
597
|
+
version: string;
|
|
598
|
+
chainId: number;
|
|
599
|
+
verifyingContract: string;
|
|
600
|
+
};
|
|
601
|
+
/** TypeScript type for the permit struct (matches Solidity AttestPermitData) */
|
|
602
|
+
export interface AttestPermitData {
|
|
603
|
+
birthYearOffset: number;
|
|
604
|
+
countryCode: number;
|
|
605
|
+
complianceLevel: number;
|
|
606
|
+
isBlacklisted: boolean;
|
|
607
|
+
proofSetHash: string;
|
|
608
|
+
policyVersion: number;
|
|
609
|
+
deadline: number;
|
|
610
|
+
v: number;
|
|
611
|
+
r: string;
|
|
612
|
+
s: string;
|
|
613
|
+
}
|
|
448
614
|
export declare function resolveContractAddresses(networkOrChainId: NetworkName | number, options?: {
|
|
449
615
|
prefer?: NetworkName;
|
|
450
616
|
overrides?: Partial<ContractAddresses>;
|