@openzeppelin/confidential-contracts 0.2.0-rc.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 (31) hide show
  1. package/README.md +24 -0
  2. package/build/contracts/Checkpoints.json +16 -0
  3. package/build/contracts/CheckpointsConfidential.json +16 -0
  4. package/build/contracts/ConfidentialFungibleToken.json +614 -0
  5. package/build/contracts/ConfidentialFungibleTokenERC20Wrapper.json +793 -0
  6. package/build/contracts/ConfidentialFungibleTokenUtils.json +10 -0
  7. package/build/contracts/ConfidentialFungibleTokenVotes.json +1002 -0
  8. package/build/contracts/ERC7821WithExecutor.json +145 -0
  9. package/build/contracts/IConfidentialFungibleToken.json +458 -0
  10. package/build/contracts/IConfidentialFungibleTokenReceiver.json +45 -0
  11. package/build/contracts/TFHESafeMath.json +10 -0
  12. package/build/contracts/VestingWalletCliffConfidential.json +275 -0
  13. package/build/contracts/VestingWalletCliffExecutorConfidential.json +424 -0
  14. package/build/contracts/VestingWalletCliffExecutorConfidentialFactory.json +290 -0
  15. package/build/contracts/VestingWalletConfidential.json +246 -0
  16. package/build/contracts/VotesConfidential.json +412 -0
  17. package/finance/ERC7821WithExecutor.sol +46 -0
  18. package/finance/VestingWalletCliffConfidential.sol +62 -0
  19. package/finance/VestingWalletCliffExecutorConfidentialFactory.sol +203 -0
  20. package/finance/VestingWalletConfidential.sol +130 -0
  21. package/governance/utils/VotesConfidential.sol +202 -0
  22. package/interfaces/IConfidentialFungibleToken.sol +135 -0
  23. package/interfaces/IConfidentialFungibleTokenReceiver.sol +19 -0
  24. package/package.json +39 -0
  25. package/token/ConfidentialFungibleToken.sol +314 -0
  26. package/token/extensions/ConfidentialFungibleTokenERC20Wrapper.sol +175 -0
  27. package/token/extensions/ConfidentialFungibleTokenVotes.sol +29 -0
  28. package/token/utils/ConfidentialFungibleTokenUtils.sol +46 -0
  29. package/utils/TFHESafeMath.sol +37 -0
  30. package/utils/structs/CheckpointsConfidential.sol +193 -0
  31. package/utils/structs/temporary-Checkpoints.sol +835 -0
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@openzeppelin/confidential-contracts",
3
+ "description": "Smart Contract library for use with confidential coprocessors",
4
+ "version": "0.2.0-rc.1",
5
+ "files": [
6
+ "**/*.sol",
7
+ "/build/contracts/*.json",
8
+ "!/mocks/**/*"
9
+ ],
10
+ "scripts": {
11
+ "prepack": "bash ../scripts/prepack.sh",
12
+ "prepare-docs": "cd ..; npm run prepare-docs"
13
+ },
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "https://github.com/OpenZeppelin/openzeppelin-confidential-contracts.git"
17
+ },
18
+ "keywords": [
19
+ "solidity",
20
+ "ethereum",
21
+ "smart",
22
+ "contracts",
23
+ "security",
24
+ "zeppelin",
25
+ "confidential",
26
+ "fhe"
27
+ ],
28
+ "author": "OpenZeppelin Community <maintainers@openzeppelin.org>",
29
+ "license": "MIT",
30
+ "bugs": {
31
+ "url": "https://github.com/OpenZeppelin/openzeppelin-confidential-contracts/issues"
32
+ },
33
+ "homepage": "https://openzeppelin.com/contracts/",
34
+ "peerDependencies": {
35
+ "@fhevm/solidity": "0.7.0",
36
+ "@openzeppelin/contracts": "^5.3.0",
37
+ "@openzeppelin/contracts-upgradeable": "^5.3.0"
38
+ }
39
+ }
@@ -0,0 +1,314 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // OpenZeppelin Confidential Contracts (last updated v0.2.0-rc.1) (token/ConfidentialFungibleToken.sol)
3
+ pragma solidity ^0.8.27;
4
+
5
+ import {FHE, externalEuint64, ebool, euint64} from "@fhevm/solidity/lib/FHE.sol";
6
+ import {IConfidentialFungibleToken} from "./../interfaces/IConfidentialFungibleToken.sol";
7
+ import {TFHESafeMath} from "./../utils/TFHESafeMath.sol";
8
+ import {ConfidentialFungibleTokenUtils} from "./utils/ConfidentialFungibleTokenUtils.sol";
9
+
10
+ /**
11
+ * @dev Reference implementation for {IConfidentialFungibleToken}.
12
+ *
13
+ * This contract implements a fungible token where balances and transfers are encrypted using the Zama fhEVM,
14
+ * providing confidentiality to users. Token amounts are stored as encrypted, unsigned integers (`euint64`)
15
+ * that can only be decrypted by authorized parties.
16
+ *
17
+ * Key features:
18
+ *
19
+ * - All balances are encrypted
20
+ * - Transfers happen without revealing amounts
21
+ * - Support for operators (delegated transfer capabilities with time bounds)
22
+ * - Transfer and call pattern
23
+ * - Safe overflow/underflow handling for FHE operations
24
+ */
25
+ abstract contract ConfidentialFungibleToken is IConfidentialFungibleToken {
26
+ mapping(address holder => euint64) private _balances;
27
+ mapping(address holder => mapping(address spender => uint48)) private _operators;
28
+ mapping(uint256 requestId => euint64 encryptedAmount) private _requestHandles;
29
+ euint64 private _totalSupply;
30
+ string private _name;
31
+ string private _symbol;
32
+ string private _tokenURI;
33
+
34
+ /// @dev The given receiver `receiver` is invalid for transfers.
35
+ error ConfidentialFungibleTokenInvalidReceiver(address receiver);
36
+
37
+ /// @dev The given sender `sender` is invalid for transfers.
38
+ error ConfidentialFungibleTokenInvalidSender(address sender);
39
+
40
+ /// @dev The given holder `holder` is not authorized to spend on behalf of `spender`.
41
+ error ConfidentialFungibleTokenUnauthorizedSpender(address holder, address spender);
42
+
43
+ /// @dev The holder `holder` is trying to send tokens but has a balance of 0.
44
+ error ConfidentialFungibleTokenZeroBalance(address holder);
45
+
46
+ /**
47
+ * @dev The caller `user` does not have access to the encrypted amount `amount`.
48
+ *
49
+ * NOTE: Try using the equivalent transfer function with an input proof.
50
+ */
51
+ error ConfidentialFungibleTokenUnauthorizedUseOfEncryptedAmount(euint64 amount, address user);
52
+
53
+ /// @dev The given caller `caller` is not authorized for the current operation.
54
+ error ConfidentialFungibleTokenUnauthorizedCaller(address caller);
55
+
56
+ /// @dev The given gateway request ID `requestId` is invalid.
57
+ error ConfidentialFungibleTokenInvalidGatewayRequest(uint256 requestId);
58
+
59
+ constructor(string memory name_, string memory symbol_, string memory tokenURI_) {
60
+ _name = name_;
61
+ _symbol = symbol_;
62
+ _tokenURI = tokenURI_;
63
+ }
64
+
65
+ /// @inheritdoc IConfidentialFungibleToken
66
+ function name() public view virtual returns (string memory) {
67
+ return _name;
68
+ }
69
+
70
+ /// @inheritdoc IConfidentialFungibleToken
71
+ function symbol() public view virtual returns (string memory) {
72
+ return _symbol;
73
+ }
74
+
75
+ /// @inheritdoc IConfidentialFungibleToken
76
+ function decimals() public view virtual returns (uint8) {
77
+ return 6;
78
+ }
79
+
80
+ /// @inheritdoc IConfidentialFungibleToken
81
+ function tokenURI() public view virtual returns (string memory) {
82
+ return _tokenURI;
83
+ }
84
+
85
+ /// @inheritdoc IConfidentialFungibleToken
86
+ function confidentialTotalSupply() public view virtual returns (euint64) {
87
+ return _totalSupply;
88
+ }
89
+
90
+ /// @inheritdoc IConfidentialFungibleToken
91
+ function confidentialBalanceOf(address account) public view virtual returns (euint64) {
92
+ return _balances[account];
93
+ }
94
+
95
+ /// @inheritdoc IConfidentialFungibleToken
96
+ function isOperator(address holder, address spender) public view virtual returns (bool) {
97
+ return holder == spender || block.timestamp <= _operators[holder][spender];
98
+ }
99
+
100
+ /// @inheritdoc IConfidentialFungibleToken
101
+ function setOperator(address operator, uint48 until) public virtual {
102
+ _setOperator(msg.sender, operator, until);
103
+ }
104
+
105
+ /// @inheritdoc IConfidentialFungibleToken
106
+ function confidentialTransfer(
107
+ address to,
108
+ externalEuint64 encryptedAmount,
109
+ bytes calldata inputProof
110
+ ) public virtual returns (euint64) {
111
+ return _transfer(msg.sender, to, FHE.fromExternal(encryptedAmount, inputProof));
112
+ }
113
+
114
+ /// @inheritdoc IConfidentialFungibleToken
115
+ function confidentialTransfer(address to, euint64 amount) public virtual returns (euint64) {
116
+ require(
117
+ FHE.isAllowed(amount, msg.sender),
118
+ ConfidentialFungibleTokenUnauthorizedUseOfEncryptedAmount(amount, msg.sender)
119
+ );
120
+ return _transfer(msg.sender, to, amount);
121
+ }
122
+
123
+ /// @inheritdoc IConfidentialFungibleToken
124
+ function confidentialTransferFrom(
125
+ address from,
126
+ address to,
127
+ externalEuint64 encryptedAmount,
128
+ bytes calldata inputProof
129
+ ) public virtual returns (euint64 transferred) {
130
+ require(isOperator(from, msg.sender), ConfidentialFungibleTokenUnauthorizedSpender(from, msg.sender));
131
+ transferred = _transfer(from, to, FHE.fromExternal(encryptedAmount, inputProof));
132
+ FHE.allowTransient(transferred, msg.sender);
133
+ }
134
+
135
+ /// @inheritdoc IConfidentialFungibleToken
136
+ function confidentialTransferFrom(
137
+ address from,
138
+ address to,
139
+ euint64 amount
140
+ ) public virtual returns (euint64 transferred) {
141
+ require(
142
+ FHE.isAllowed(amount, msg.sender),
143
+ ConfidentialFungibleTokenUnauthorizedUseOfEncryptedAmount(amount, msg.sender)
144
+ );
145
+ require(isOperator(from, msg.sender), ConfidentialFungibleTokenUnauthorizedSpender(from, msg.sender));
146
+ transferred = _transfer(from, to, amount);
147
+ FHE.allowTransient(transferred, msg.sender);
148
+ }
149
+
150
+ /// @inheritdoc IConfidentialFungibleToken
151
+ function confidentialTransferAndCall(
152
+ address to,
153
+ externalEuint64 encryptedAmount,
154
+ bytes calldata inputProof,
155
+ bytes calldata data
156
+ ) public virtual returns (euint64 transferred) {
157
+ transferred = _transferAndCall(msg.sender, to, FHE.fromExternal(encryptedAmount, inputProof), data);
158
+ FHE.allowTransient(transferred, msg.sender);
159
+ }
160
+
161
+ /// @inheritdoc IConfidentialFungibleToken
162
+ function confidentialTransferAndCall(
163
+ address to,
164
+ euint64 amount,
165
+ bytes calldata data
166
+ ) public virtual returns (euint64 transferred) {
167
+ require(
168
+ FHE.isAllowed(amount, msg.sender),
169
+ ConfidentialFungibleTokenUnauthorizedUseOfEncryptedAmount(amount, msg.sender)
170
+ );
171
+ transferred = _transferAndCall(msg.sender, to, amount, data);
172
+ FHE.allowTransient(transferred, msg.sender);
173
+ }
174
+
175
+ /// @inheritdoc IConfidentialFungibleToken
176
+ function confidentialTransferFromAndCall(
177
+ address from,
178
+ address to,
179
+ externalEuint64 encryptedAmount,
180
+ bytes calldata inputProof,
181
+ bytes calldata data
182
+ ) public virtual returns (euint64 transferred) {
183
+ require(isOperator(from, msg.sender), ConfidentialFungibleTokenUnauthorizedSpender(from, msg.sender));
184
+ transferred = _transferAndCall(from, to, FHE.fromExternal(encryptedAmount, inputProof), data);
185
+ FHE.allowTransient(transferred, msg.sender);
186
+ }
187
+
188
+ /// @inheritdoc IConfidentialFungibleToken
189
+ function confidentialTransferFromAndCall(
190
+ address from,
191
+ address to,
192
+ euint64 amount,
193
+ bytes calldata data
194
+ ) public virtual returns (euint64 transferred) {
195
+ require(
196
+ FHE.isAllowed(amount, msg.sender),
197
+ ConfidentialFungibleTokenUnauthorizedUseOfEncryptedAmount(amount, msg.sender)
198
+ );
199
+ require(isOperator(from, msg.sender), ConfidentialFungibleTokenUnauthorizedSpender(from, msg.sender));
200
+ transferred = _transferAndCall(from, to, amount, data);
201
+ FHE.allowTransient(transferred, msg.sender);
202
+ }
203
+
204
+ /**
205
+ * @dev Discloses an encrypted amount `encryptedAmount` publicly via an {IConfidentialFungibleToken-AmountDisclosed}
206
+ * event. The caller and this contract must be authorized to use the encrypted amount on the ACL.
207
+ *
208
+ * NOTE: This is an asynchronous operation where the actual decryption happens off-chain and
209
+ * {finalizeDiscloseEncryptedAmount} is called with the result.
210
+ */
211
+ function discloseEncryptedAmount(euint64 encryptedAmount) public virtual {
212
+ require(
213
+ FHE.isAllowed(encryptedAmount, msg.sender) && FHE.isAllowed(encryptedAmount, address(this)),
214
+ ConfidentialFungibleTokenUnauthorizedUseOfEncryptedAmount(encryptedAmount, msg.sender)
215
+ );
216
+
217
+ bytes32[] memory cts = new bytes32[](1);
218
+ cts[0] = euint64.unwrap(encryptedAmount);
219
+ uint256 requestID = FHE.requestDecryption(cts, this.finalizeDiscloseEncryptedAmount.selector);
220
+ _requestHandles[requestID] = encryptedAmount;
221
+ }
222
+
223
+ /// @dev May only be called by the gateway contract. Finalizes a disclose encrypted amount request.
224
+ function finalizeDiscloseEncryptedAmount(
225
+ uint256 requestId,
226
+ uint64 amount,
227
+ bytes[] memory signatures
228
+ ) public virtual {
229
+ FHE.checkSignatures(requestId, signatures);
230
+
231
+ euint64 requestHandle = _requestHandles[requestId];
232
+ require(FHE.isInitialized(requestHandle), ConfidentialFungibleTokenInvalidGatewayRequest(requestId));
233
+ emit AmountDisclosed(requestHandle, amount);
234
+
235
+ _requestHandles[requestId] = euint64.wrap(0);
236
+ }
237
+
238
+ function _setOperator(address holder, address operator, uint48 until) internal virtual {
239
+ _operators[holder][operator] = until;
240
+ emit OperatorSet(holder, operator, until);
241
+ }
242
+
243
+ function _mint(address to, euint64 amount) internal returns (euint64 transferred) {
244
+ require(to != address(0), ConfidentialFungibleTokenInvalidReceiver(address(0)));
245
+ return _update(address(0), to, amount);
246
+ }
247
+
248
+ function _burn(address from, euint64 amount) internal returns (euint64 transferred) {
249
+ require(from != address(0), ConfidentialFungibleTokenInvalidSender(address(0)));
250
+ return _update(from, address(0), amount);
251
+ }
252
+
253
+ function _transfer(address from, address to, euint64 amount) internal returns (euint64 transferred) {
254
+ require(from != address(0), ConfidentialFungibleTokenInvalidSender(address(0)));
255
+ require(to != address(0), ConfidentialFungibleTokenInvalidReceiver(address(0)));
256
+ return _update(from, to, amount);
257
+ }
258
+
259
+ function _transferAndCall(
260
+ address from,
261
+ address to,
262
+ euint64 amount,
263
+ bytes calldata data
264
+ ) internal returns (euint64 transferred) {
265
+ // Try to transfer amount + replace input with actually transferred amount.
266
+ euint64 sent = _transfer(from, to, amount);
267
+
268
+ // Perform callback
269
+ transferred = FHE.select(
270
+ ConfidentialFungibleTokenUtils.checkOnTransferReceived(msg.sender, from, to, sent, data),
271
+ sent,
272
+ FHE.asEuint64(0)
273
+ );
274
+
275
+ // Refund if success fails. refund should never fail
276
+ _update(to, from, FHE.sub(sent, transferred));
277
+ }
278
+
279
+ function _update(address from, address to, euint64 amount) internal virtual returns (euint64 transferred) {
280
+ ebool success;
281
+ euint64 ptr;
282
+
283
+ if (from == address(0)) {
284
+ (success, ptr) = TFHESafeMath.tryIncrease(_totalSupply, amount);
285
+ FHE.allowThis(ptr);
286
+ _totalSupply = ptr;
287
+ } else {
288
+ euint64 fromBalance = _balances[from];
289
+ require(FHE.isInitialized(fromBalance), ConfidentialFungibleTokenZeroBalance(from));
290
+ (success, ptr) = TFHESafeMath.tryDecrease(fromBalance, amount);
291
+ FHE.allowThis(ptr);
292
+ FHE.allow(ptr, from);
293
+ _balances[from] = ptr;
294
+ }
295
+
296
+ transferred = FHE.select(success, amount, FHE.asEuint64(0));
297
+
298
+ if (to == address(0)) {
299
+ ptr = FHE.sub(_totalSupply, transferred);
300
+ FHE.allowThis(ptr);
301
+ _totalSupply = ptr;
302
+ } else {
303
+ ptr = FHE.add(_balances[to], transferred);
304
+ FHE.allowThis(ptr);
305
+ FHE.allow(ptr, to);
306
+ _balances[to] = ptr;
307
+ }
308
+
309
+ if (from != address(0)) FHE.allow(transferred, from);
310
+ if (to != address(0)) FHE.allow(transferred, to);
311
+ FHE.allowThis(transferred);
312
+ emit ConfidentialTransfer(from, to, transferred);
313
+ }
314
+ }
@@ -0,0 +1,175 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // OpenZeppelin Confidential Contracts (last updated v0.2.0-rc.1) (token/extensions/ConfidentialFungibleTokenERC20Wrapper.sol)
3
+
4
+ pragma solidity ^0.8.27;
5
+
6
+ import {FHE, externalEuint64, euint64} from "@fhevm/solidity/lib/FHE.sol";
7
+ import {IERC1363Receiver} from "@openzeppelin/contracts/interfaces/IERC1363Receiver.sol";
8
+ import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol";
9
+ import {IERC20Metadata} from "@openzeppelin/contracts/interfaces/IERC20Metadata.sol";
10
+ import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
11
+ import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
12
+ import {ConfidentialFungibleToken} from "./../ConfidentialFungibleToken.sol";
13
+
14
+ /**
15
+ * @dev A wrapper contract built on top of {ConfidentialFungibleToken} that allows wrapping an `ERC20` token
16
+ * into a confidential fungible token. The wrapper contract implements the `IERC1363Receiver` interface
17
+ * which allows users to transfer `ERC1363` tokens directly to the wrapper with a callback to wrap the tokens.
18
+ */
19
+ abstract contract ConfidentialFungibleTokenERC20Wrapper is ConfidentialFungibleToken, IERC1363Receiver {
20
+ IERC20 private immutable _underlying;
21
+ uint8 private immutable _decimals;
22
+ uint256 private immutable _rate;
23
+
24
+ /// @dev Mapping from gateway decryption request ID to the address that will receive the tokens
25
+ mapping(uint256 decryptionRequest => address) private _receivers;
26
+
27
+ constructor(IERC20 underlying_) {
28
+ _underlying = underlying_;
29
+
30
+ uint8 tokenDecimals = _tryGetAssetDecimals(underlying_);
31
+ uint8 maxDecimals = _maxDecimals();
32
+ if (tokenDecimals > maxDecimals) {
33
+ _decimals = maxDecimals;
34
+ _rate = 10 ** (tokenDecimals - maxDecimals);
35
+ } else {
36
+ _decimals = tokenDecimals;
37
+ _rate = 1;
38
+ }
39
+ }
40
+
41
+ /// @inheritdoc ConfidentialFungibleToken
42
+ function decimals() public view virtual override returns (uint8) {
43
+ return _decimals;
44
+ }
45
+
46
+ /**
47
+ * @dev Returns the rate at which the underlying token is converted to the wrapped token.
48
+ * For example, if the `rate` is 1000, then 1000 units of the underlying token equal 1 unit of the wrapped token.
49
+ */
50
+ function rate() public view virtual returns (uint256) {
51
+ return _rate;
52
+ }
53
+
54
+ /// @dev Returns the address of the underlying ERC-20 token that is being wrapped.
55
+ function underlying() public view returns (IERC20) {
56
+ return _underlying;
57
+ }
58
+
59
+ /**
60
+ * @dev `ERC1363` callback function which wraps tokens to the address specified in `data` or
61
+ * the address `from` (if no address is specified in `data`). This function refunds any excess tokens
62
+ * sent beyond the nearest multiple of {rate}. See {wrap} from more details on wrapping tokens.
63
+ */
64
+ function onTransferReceived(
65
+ address /*operator*/,
66
+ address from,
67
+ uint256 amount,
68
+ bytes calldata data
69
+ ) public virtual returns (bytes4) {
70
+ // check caller is the token contract
71
+ require(address(underlying()) == msg.sender, ConfidentialFungibleTokenUnauthorizedCaller(msg.sender));
72
+
73
+ // transfer excess back to the sender
74
+ uint256 excess = amount % rate();
75
+ if (excess > 0) SafeERC20.safeTransfer(underlying(), from, excess);
76
+
77
+ // mint confidential token
78
+ address to = data.length < 20 ? from : address(bytes20(data));
79
+ _mint(to, FHE.asEuint64(SafeCast.toUint64(amount / rate())));
80
+
81
+ // return magic value
82
+ return IERC1363Receiver.onTransferReceived.selector;
83
+ }
84
+
85
+ /**
86
+ * @dev Wraps amount `amount` of the underlying token into a confidential token and sends it to
87
+ * `to`. Tokens are exchanged at a fixed rate specified by {rate} such that `amount / rate()` confidential
88
+ * tokens are sent. Amount transferred in is rounded down to the nearest multiple of {rate}.
89
+ */
90
+ function wrap(address to, uint256 amount) public virtual {
91
+ // take ownership of the tokens
92
+ SafeERC20.safeTransferFrom(underlying(), msg.sender, address(this), amount - (amount % rate()));
93
+
94
+ // mint confidential token
95
+ _mint(to, FHE.asEuint64(SafeCast.toUint64(amount / rate())));
96
+ }
97
+
98
+ /**
99
+ * @dev Unwraps tokens from `from` and sends the underlying tokens to `to`. The caller must be `from`
100
+ * or be an approved operator for `from`. `amount * rate()` underlying tokens are sent to `to`.
101
+ *
102
+ * NOTE: This is an asynchronous function and waits for decryption to be completed off-chain before disbursing
103
+ * tokens.
104
+ * NOTE: The caller *must* already be approved by ACL for the given `amount`.
105
+ */
106
+ function unwrap(address from, address to, euint64 amount) public virtual {
107
+ require(
108
+ FHE.isAllowed(amount, msg.sender),
109
+ ConfidentialFungibleTokenUnauthorizedUseOfEncryptedAmount(amount, msg.sender)
110
+ );
111
+ _unwrap(from, to, amount);
112
+ }
113
+
114
+ /**
115
+ * @dev Variant of {unwrap} that passes an `inputProof` which approves the caller for the `encryptedAmount`
116
+ * in the ACL.
117
+ */
118
+ function unwrap(
119
+ address from,
120
+ address to,
121
+ externalEuint64 encryptedAmount,
122
+ bytes calldata inputProof
123
+ ) public virtual {
124
+ _unwrap(from, to, FHE.fromExternal(encryptedAmount, inputProof));
125
+ }
126
+
127
+ /**
128
+ * @dev Called by the fhEVM gateway with the decrypted amount `amount` for a request id `requestId`.
129
+ * Fills unwrap requests.
130
+ */
131
+ function finalizeUnwrap(uint256 requestID, uint64 amount, bytes[] memory signatures) public virtual {
132
+ FHE.checkSignatures(requestID, signatures);
133
+ address to = _receivers[requestID];
134
+ require(to != address(0), ConfidentialFungibleTokenInvalidGatewayRequest(requestID));
135
+ delete _receivers[requestID];
136
+
137
+ SafeERC20.safeTransfer(underlying(), to, amount * rate());
138
+ }
139
+
140
+ function _unwrap(address from, address to, euint64 amount) internal virtual {
141
+ require(to != address(0), ConfidentialFungibleTokenInvalidReceiver(to));
142
+ require(
143
+ from == msg.sender || isOperator(from, msg.sender),
144
+ ConfidentialFungibleTokenUnauthorizedSpender(from, msg.sender)
145
+ );
146
+
147
+ // try to burn, see how much we actually got
148
+ euint64 burntAmount = _burn(from, amount);
149
+
150
+ // decrypt that burntAmount
151
+ bytes32[] memory cts = new bytes32[](1);
152
+ cts[0] = euint64.unwrap(burntAmount);
153
+ uint256 requestID = FHE.requestDecryption(cts, this.finalizeUnwrap.selector);
154
+
155
+ // register who is getting the tokens
156
+ _receivers[requestID] = to;
157
+ }
158
+
159
+ /**
160
+ * @dev Returns the maximum number that will be used for {decimals} by the wrapper.
161
+ */
162
+ function _maxDecimals() internal pure virtual returns (uint8) {
163
+ return 6;
164
+ }
165
+
166
+ function _tryGetAssetDecimals(IERC20 asset_) private view returns (uint8 assetDecimals) {
167
+ (bool success, bytes memory encodedDecimals) = address(asset_).staticcall(
168
+ abi.encodeCall(IERC20Metadata.decimals, ())
169
+ );
170
+ if (success && encodedDecimals.length == 32) {
171
+ return abi.decode(encodedDecimals, (uint8));
172
+ }
173
+ return 18;
174
+ }
175
+ }
@@ -0,0 +1,29 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // OpenZeppelin Confidential Contracts (last updated v0.2.0-rc.1) (token/extensions/ConfidentialFungibleTokenVotes.sol)
3
+ pragma solidity ^0.8.27;
4
+
5
+ import {euint64} from "@fhevm/solidity/lib/FHE.sol";
6
+ import {VotesConfidential} from "./../../governance/utils/VotesConfidential.sol";
7
+ import {ConfidentialFungibleToken} from "./../ConfidentialFungibleToken.sol";
8
+
9
+ abstract contract ConfidentialFungibleTokenVotes is ConfidentialFungibleToken, VotesConfidential {
10
+ function confidentialTotalSupply()
11
+ public
12
+ view
13
+ virtual
14
+ override(VotesConfidential, ConfidentialFungibleToken)
15
+ returns (euint64)
16
+ {
17
+ return super.confidentialTotalSupply();
18
+ }
19
+
20
+ function _update(address from, address to, euint64 amount) internal virtual override returns (euint64 transferred) {
21
+ transferred = super._update(from, to, amount);
22
+
23
+ _transferVotingUnits(from, to, transferred);
24
+ }
25
+
26
+ function _getVotingUnits(address account) internal view virtual override returns (euint64) {
27
+ return confidentialBalanceOf(account);
28
+ }
29
+ }
@@ -0,0 +1,46 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // OpenZeppelin Confidential Contracts (last updated v0.2.0-rc.1) (token/utils/ConfidentialFungibleTokenUtils.sol)
3
+ pragma solidity ^0.8.24;
4
+
5
+ import {FHE, ebool, euint64} from "@fhevm/solidity/lib/FHE.sol";
6
+
7
+ import {IConfidentialFungibleTokenReceiver} from "../../interfaces/IConfidentialFungibleTokenReceiver.sol";
8
+ import {ConfidentialFungibleToken} from "../ConfidentialFungibleToken.sol";
9
+
10
+ /// @dev Library that provides common {ConfidentialFungibleToken} utility functions.
11
+ library ConfidentialFungibleTokenUtils {
12
+ /**
13
+ * @dev Performs a transfer callback to the recipient of the transfer `to`. Should be invoked
14
+ * after all transfers "withCallback" on a {ConfidentialFungibleToken}.
15
+ *
16
+ * The transfer callback is not invoked on the recipient if the recipient has no code (i.e. is an EOA). If the
17
+ * recipient has non-zero code, it must implement
18
+ * {IConfidentialFungibleTokenReceiver-onConfidentialTransferReceived} and return an `ebool` indicating
19
+ * whether the transfer was accepted or not. If the `ebool` is `false`, the transfer will be reversed.
20
+ */
21
+ function checkOnTransferReceived(
22
+ address operator,
23
+ address from,
24
+ address to,
25
+ euint64 amount,
26
+ bytes calldata data
27
+ ) internal returns (ebool) {
28
+ if (to.code.length > 0) {
29
+ try
30
+ IConfidentialFungibleTokenReceiver(to).onConfidentialTransferReceived(operator, from, amount, data)
31
+ returns (ebool retval) {
32
+ return retval;
33
+ } catch (bytes memory reason) {
34
+ if (reason.length == 0) {
35
+ revert ConfidentialFungibleToken.ConfidentialFungibleTokenInvalidReceiver(to);
36
+ } else {
37
+ assembly ("memory-safe") {
38
+ revert(add(32, reason), mload(reason))
39
+ }
40
+ }
41
+ }
42
+ } else {
43
+ return FHE.asEbool(true);
44
+ }
45
+ }
46
+ }
@@ -0,0 +1,37 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // OpenZeppelin Confidential Contracts (last updated v0.2.0-rc.1) (utils/TFHESafeMath.sol)
3
+ pragma solidity ^0.8.24;
4
+
5
+ import {FHE, ebool, euint64} from "@fhevm/solidity/lib/FHE.sol";
6
+
7
+ /**
8
+ * @dev Library providing safe arithmetic operations for encrypted values
9
+ * to handle potential overflows in FHE operations.
10
+ */
11
+ library TFHESafeMath {
12
+ /**
13
+ * @dev Try to increase the encrypted value `oldValue` by `delta`. If the operation is successful,
14
+ * `success` will be true and `updated` will be the new value. Otherwise, `success` will be false
15
+ * and `updated` will be the original value.
16
+ */
17
+ function tryIncrease(euint64 oldValue, euint64 delta) internal returns (ebool success, euint64 updated) {
18
+ if (!FHE.isInitialized(oldValue)) {
19
+ success = FHE.asEbool(true);
20
+ updated = delta;
21
+ } else {
22
+ euint64 newValue = FHE.add(oldValue, delta);
23
+ success = FHE.ge(newValue, oldValue);
24
+ updated = FHE.select(success, newValue, oldValue);
25
+ }
26
+ }
27
+
28
+ /**
29
+ * @dev Try to decrease the encrypted value `oldValue` by `delta`. If the operation is successful,
30
+ * `success` will be true and `updated` will be the new value. Otherwise, `success` will be false
31
+ * and `updated` will be the original value.
32
+ */
33
+ function tryDecrease(euint64 oldValue, euint64 delta) internal returns (ebool success, euint64 updated) {
34
+ success = FHE.ge(oldValue, delta);
35
+ updated = FHE.select(success, FHE.sub(oldValue, delta), oldValue);
36
+ }
37
+ }