@openzeppelin/confidential-contracts 0.2.0-rc.2 → 0.3.0-rc.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/build/contracts/Checkpoints.json +2 -2
- package/build/contracts/CheckpointsConfidential.json +3 -9
- package/build/contracts/{ConfidentialFungibleToken.json → ERC7984.json} +28 -33
- package/build/contracts/{ConfidentialFungibleTokenERC20Wrapper.json → ERC7984ERC20Wrapper.json} +34 -39
- package/build/contracts/ERC7984Freezable.json +666 -0
- package/build/contracts/ERC7984ObserverAccess.json +676 -0
- package/build/contracts/ERC7984Omnibus.json +994 -0
- package/build/contracts/ERC7984Restricted.json +677 -0
- package/build/contracts/ERC7984Rwa.json +1370 -0
- package/build/contracts/{ConfidentialFungibleTokenUtils.json → ERC7984Utils.json} +4 -4
- package/build/contracts/{ConfidentialFungibleTokenVotes.json → ERC7984Votes.json} +99 -81
- package/build/contracts/{TFHESafeMath.json → FHESafeMath.json} +4 -4
- package/build/contracts/HandleAccessManager.json +34 -0
- package/build/contracts/{IConfidentialFungibleToken.json → IERC7984.json} +15 -15
- package/build/contracts/{IConfidentialFungibleTokenReceiver.json → IERC7984Receiver.json} +2 -2
- package/build/contracts/IERC7984Rwa.json +797 -0
- package/build/contracts/VestingWalletCliffConfidential.json +3 -3
- package/build/contracts/VestingWalletConfidential.json +3 -3
- package/build/contracts/VestingWalletConfidentialFactory.json +18 -153
- package/build/contracts/VotesConfidential.json +23 -0
- package/finance/ERC7821WithExecutor.sol +2 -2
- package/finance/VestingWalletCliffConfidential.sol +15 -4
- package/finance/VestingWalletConfidential.sol +32 -20
- package/finance/VestingWalletConfidentialFactory.sol +34 -123
- package/governance/utils/VotesConfidential.sol +18 -4
- package/interfaces/{IConfidentialFungibleToken.sol → IERC7984.sol} +6 -6
- package/interfaces/{IConfidentialFungibleTokenReceiver.sol → IERC7984Receiver.sol} +3 -3
- package/interfaces/IERC7984Rwa.sol +64 -0
- package/package.json +4 -4
- package/token/{ConfidentialFungibleToken.sol → ERC7984/ERC7984.sol} +73 -84
- package/token/{extensions/ConfidentialFungibleTokenERC20Wrapper.sol → ERC7984/extensions/ERC7984ERC20Wrapper.sol} +36 -27
- package/token/ERC7984/extensions/ERC7984Freezable.sol +66 -0
- package/token/ERC7984/extensions/ERC7984ObserverAccess.sol +63 -0
- package/token/ERC7984/extensions/ERC7984Omnibus.sol +209 -0
- package/token/ERC7984/extensions/ERC7984Restricted.sol +118 -0
- package/token/ERC7984/extensions/ERC7984Rwa.sol +236 -0
- package/token/ERC7984/extensions/ERC7984Votes.sol +31 -0
- package/token/{utils/ConfidentialFungibleTokenUtils.sol → ERC7984/utils/ERC7984Utils.sol} +12 -12
- package/utils/FHESafeMath.sol +72 -0
- package/utils/HandleAccessManager.sol +29 -0
- package/utils/structs/CheckpointsConfidential.sol +1 -6
- package/utils/structs/temporary-Checkpoints.sol +2 -2
- package/token/extensions/ConfidentialFungibleTokenVotes.sol +0 -29
- package/utils/TFHESafeMath.sol +0 -37
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
// OpenZeppelin Confidential Contracts (last updated v0.
|
|
2
|
+
// OpenZeppelin Confidential Contracts (last updated v0.3.0-rc.0) (token/ERC7984/ERC7984.sol)
|
|
3
3
|
pragma solidity ^0.8.27;
|
|
4
4
|
|
|
5
5
|
import {FHE, externalEuint64, ebool, euint64} from "@fhevm/solidity/lib/FHE.sol";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
6
|
+
import {IERC7984} from "./../../interfaces/IERC7984.sol";
|
|
7
|
+
import {FHESafeMath} from "./../../utils/FHESafeMath.sol";
|
|
8
|
+
import {ERC7984Utils} from "./utils/ERC7984Utils.sol";
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
* @dev Reference implementation for {
|
|
11
|
+
* @dev Reference implementation for {IERC7984}.
|
|
12
12
|
*
|
|
13
13
|
* This contract implements a fungible token where balances and transfers are encrypted using the Zama fhEVM,
|
|
14
14
|
* providing confidentiality to users. Token amounts are stored as encrypted, unsigned integers (`euint64`)
|
|
@@ -22,87 +22,86 @@ import {ConfidentialFungibleTokenUtils} from "./utils/ConfidentialFungibleTokenU
|
|
|
22
22
|
* - Transfer and call pattern
|
|
23
23
|
* - Safe overflow/underflow handling for FHE operations
|
|
24
24
|
*/
|
|
25
|
-
abstract contract
|
|
25
|
+
abstract contract ERC7984 is IERC7984 {
|
|
26
26
|
mapping(address holder => euint64) private _balances;
|
|
27
27
|
mapping(address holder => mapping(address spender => uint48)) private _operators;
|
|
28
|
-
mapping(uint256 requestId => euint64 encryptedAmount) private _requestHandles;
|
|
29
28
|
euint64 private _totalSupply;
|
|
30
29
|
string private _name;
|
|
31
30
|
string private _symbol;
|
|
32
|
-
string private
|
|
31
|
+
string private _contractURI;
|
|
33
32
|
|
|
34
33
|
/// @dev The given receiver `receiver` is invalid for transfers.
|
|
35
|
-
error
|
|
34
|
+
error ERC7984InvalidReceiver(address receiver);
|
|
36
35
|
|
|
37
36
|
/// @dev The given sender `sender` is invalid for transfers.
|
|
38
|
-
error
|
|
37
|
+
error ERC7984InvalidSender(address sender);
|
|
39
38
|
|
|
40
39
|
/// @dev The given holder `holder` is not authorized to spend on behalf of `spender`.
|
|
41
|
-
error
|
|
40
|
+
error ERC7984UnauthorizedSpender(address holder, address spender);
|
|
42
41
|
|
|
43
42
|
/// @dev The holder `holder` is trying to send tokens but has a balance of 0.
|
|
44
|
-
error
|
|
43
|
+
error ERC7984ZeroBalance(address holder);
|
|
45
44
|
|
|
46
45
|
/**
|
|
47
46
|
* @dev The caller `user` does not have access to the encrypted amount `amount`.
|
|
48
47
|
*
|
|
49
48
|
* NOTE: Try using the equivalent transfer function with an input proof.
|
|
50
49
|
*/
|
|
51
|
-
error
|
|
50
|
+
error ERC7984UnauthorizedUseOfEncryptedAmount(euint64 amount, address user);
|
|
52
51
|
|
|
53
52
|
/// @dev The given caller `caller` is not authorized for the current operation.
|
|
54
|
-
error
|
|
53
|
+
error ERC7984UnauthorizedCaller(address caller);
|
|
55
54
|
|
|
56
55
|
/// @dev The given gateway request ID `requestId` is invalid.
|
|
57
|
-
error
|
|
56
|
+
error ERC7984InvalidGatewayRequest(uint256 requestId);
|
|
58
57
|
|
|
59
|
-
constructor(string memory name_, string memory symbol_, string memory
|
|
58
|
+
constructor(string memory name_, string memory symbol_, string memory contractURI_) {
|
|
60
59
|
_name = name_;
|
|
61
60
|
_symbol = symbol_;
|
|
62
|
-
|
|
61
|
+
_contractURI = contractURI_;
|
|
63
62
|
}
|
|
64
63
|
|
|
65
|
-
/// @inheritdoc
|
|
64
|
+
/// @inheritdoc IERC7984
|
|
66
65
|
function name() public view virtual returns (string memory) {
|
|
67
66
|
return _name;
|
|
68
67
|
}
|
|
69
68
|
|
|
70
|
-
/// @inheritdoc
|
|
69
|
+
/// @inheritdoc IERC7984
|
|
71
70
|
function symbol() public view virtual returns (string memory) {
|
|
72
71
|
return _symbol;
|
|
73
72
|
}
|
|
74
73
|
|
|
75
|
-
/// @inheritdoc
|
|
74
|
+
/// @inheritdoc IERC7984
|
|
76
75
|
function decimals() public view virtual returns (uint8) {
|
|
77
76
|
return 6;
|
|
78
77
|
}
|
|
79
78
|
|
|
80
|
-
/// @inheritdoc
|
|
81
|
-
function
|
|
82
|
-
return
|
|
79
|
+
/// @inheritdoc IERC7984
|
|
80
|
+
function contractURI() public view virtual returns (string memory) {
|
|
81
|
+
return _contractURI;
|
|
83
82
|
}
|
|
84
83
|
|
|
85
|
-
/// @inheritdoc
|
|
84
|
+
/// @inheritdoc IERC7984
|
|
86
85
|
function confidentialTotalSupply() public view virtual returns (euint64) {
|
|
87
86
|
return _totalSupply;
|
|
88
87
|
}
|
|
89
88
|
|
|
90
|
-
/// @inheritdoc
|
|
89
|
+
/// @inheritdoc IERC7984
|
|
91
90
|
function confidentialBalanceOf(address account) public view virtual returns (euint64) {
|
|
92
91
|
return _balances[account];
|
|
93
92
|
}
|
|
94
93
|
|
|
95
|
-
/// @inheritdoc
|
|
94
|
+
/// @inheritdoc IERC7984
|
|
96
95
|
function isOperator(address holder, address spender) public view virtual returns (bool) {
|
|
97
96
|
return holder == spender || block.timestamp <= _operators[holder][spender];
|
|
98
97
|
}
|
|
99
98
|
|
|
100
|
-
/// @inheritdoc
|
|
99
|
+
/// @inheritdoc IERC7984
|
|
101
100
|
function setOperator(address operator, uint48 until) public virtual {
|
|
102
101
|
_setOperator(msg.sender, operator, until);
|
|
103
102
|
}
|
|
104
103
|
|
|
105
|
-
/// @inheritdoc
|
|
104
|
+
/// @inheritdoc IERC7984
|
|
106
105
|
function confidentialTransfer(
|
|
107
106
|
address to,
|
|
108
107
|
externalEuint64 encryptedAmount,
|
|
@@ -111,43 +110,37 @@ abstract contract ConfidentialFungibleToken is IConfidentialFungibleToken {
|
|
|
111
110
|
return _transfer(msg.sender, to, FHE.fromExternal(encryptedAmount, inputProof));
|
|
112
111
|
}
|
|
113
112
|
|
|
114
|
-
/// @inheritdoc
|
|
113
|
+
/// @inheritdoc IERC7984
|
|
115
114
|
function confidentialTransfer(address to, euint64 amount) public virtual returns (euint64) {
|
|
116
|
-
require(
|
|
117
|
-
FHE.isAllowed(amount, msg.sender),
|
|
118
|
-
ConfidentialFungibleTokenUnauthorizedUseOfEncryptedAmount(amount, msg.sender)
|
|
119
|
-
);
|
|
115
|
+
require(FHE.isAllowed(amount, msg.sender), ERC7984UnauthorizedUseOfEncryptedAmount(amount, msg.sender));
|
|
120
116
|
return _transfer(msg.sender, to, amount);
|
|
121
117
|
}
|
|
122
118
|
|
|
123
|
-
/// @inheritdoc
|
|
119
|
+
/// @inheritdoc IERC7984
|
|
124
120
|
function confidentialTransferFrom(
|
|
125
121
|
address from,
|
|
126
122
|
address to,
|
|
127
123
|
externalEuint64 encryptedAmount,
|
|
128
124
|
bytes calldata inputProof
|
|
129
125
|
) public virtual returns (euint64 transferred) {
|
|
130
|
-
require(isOperator(from, msg.sender),
|
|
126
|
+
require(isOperator(from, msg.sender), ERC7984UnauthorizedSpender(from, msg.sender));
|
|
131
127
|
transferred = _transfer(from, to, FHE.fromExternal(encryptedAmount, inputProof));
|
|
132
128
|
FHE.allowTransient(transferred, msg.sender);
|
|
133
129
|
}
|
|
134
130
|
|
|
135
|
-
/// @inheritdoc
|
|
131
|
+
/// @inheritdoc IERC7984
|
|
136
132
|
function confidentialTransferFrom(
|
|
137
133
|
address from,
|
|
138
134
|
address to,
|
|
139
135
|
euint64 amount
|
|
140
136
|
) public virtual returns (euint64 transferred) {
|
|
141
|
-
require(
|
|
142
|
-
|
|
143
|
-
ConfidentialFungibleTokenUnauthorizedUseOfEncryptedAmount(amount, msg.sender)
|
|
144
|
-
);
|
|
145
|
-
require(isOperator(from, msg.sender), ConfidentialFungibleTokenUnauthorizedSpender(from, msg.sender));
|
|
137
|
+
require(FHE.isAllowed(amount, msg.sender), ERC7984UnauthorizedUseOfEncryptedAmount(amount, msg.sender));
|
|
138
|
+
require(isOperator(from, msg.sender), ERC7984UnauthorizedSpender(from, msg.sender));
|
|
146
139
|
transferred = _transfer(from, to, amount);
|
|
147
140
|
FHE.allowTransient(transferred, msg.sender);
|
|
148
141
|
}
|
|
149
142
|
|
|
150
|
-
/// @inheritdoc
|
|
143
|
+
/// @inheritdoc IERC7984
|
|
151
144
|
function confidentialTransferAndCall(
|
|
152
145
|
address to,
|
|
153
146
|
externalEuint64 encryptedAmount,
|
|
@@ -158,21 +151,18 @@ abstract contract ConfidentialFungibleToken is IConfidentialFungibleToken {
|
|
|
158
151
|
FHE.allowTransient(transferred, msg.sender);
|
|
159
152
|
}
|
|
160
153
|
|
|
161
|
-
/// @inheritdoc
|
|
154
|
+
/// @inheritdoc IERC7984
|
|
162
155
|
function confidentialTransferAndCall(
|
|
163
156
|
address to,
|
|
164
157
|
euint64 amount,
|
|
165
158
|
bytes calldata data
|
|
166
159
|
) public virtual returns (euint64 transferred) {
|
|
167
|
-
require(
|
|
168
|
-
FHE.isAllowed(amount, msg.sender),
|
|
169
|
-
ConfidentialFungibleTokenUnauthorizedUseOfEncryptedAmount(amount, msg.sender)
|
|
170
|
-
);
|
|
160
|
+
require(FHE.isAllowed(amount, msg.sender), ERC7984UnauthorizedUseOfEncryptedAmount(amount, msg.sender));
|
|
171
161
|
transferred = _transferAndCall(msg.sender, to, amount, data);
|
|
172
162
|
FHE.allowTransient(transferred, msg.sender);
|
|
173
163
|
}
|
|
174
164
|
|
|
175
|
-
/// @inheritdoc
|
|
165
|
+
/// @inheritdoc IERC7984
|
|
176
166
|
function confidentialTransferFromAndCall(
|
|
177
167
|
address from,
|
|
178
168
|
address to,
|
|
@@ -180,29 +170,26 @@ abstract contract ConfidentialFungibleToken is IConfidentialFungibleToken {
|
|
|
180
170
|
bytes calldata inputProof,
|
|
181
171
|
bytes calldata data
|
|
182
172
|
) public virtual returns (euint64 transferred) {
|
|
183
|
-
require(isOperator(from, msg.sender),
|
|
173
|
+
require(isOperator(from, msg.sender), ERC7984UnauthorizedSpender(from, msg.sender));
|
|
184
174
|
transferred = _transferAndCall(from, to, FHE.fromExternal(encryptedAmount, inputProof), data);
|
|
185
175
|
FHE.allowTransient(transferred, msg.sender);
|
|
186
176
|
}
|
|
187
177
|
|
|
188
|
-
/// @inheritdoc
|
|
178
|
+
/// @inheritdoc IERC7984
|
|
189
179
|
function confidentialTransferFromAndCall(
|
|
190
180
|
address from,
|
|
191
181
|
address to,
|
|
192
182
|
euint64 amount,
|
|
193
183
|
bytes calldata data
|
|
194
184
|
) public virtual returns (euint64 transferred) {
|
|
195
|
-
require(
|
|
196
|
-
|
|
197
|
-
ConfidentialFungibleTokenUnauthorizedUseOfEncryptedAmount(amount, msg.sender)
|
|
198
|
-
);
|
|
199
|
-
require(isOperator(from, msg.sender), ConfidentialFungibleTokenUnauthorizedSpender(from, msg.sender));
|
|
185
|
+
require(FHE.isAllowed(amount, msg.sender), ERC7984UnauthorizedUseOfEncryptedAmount(amount, msg.sender));
|
|
186
|
+
require(isOperator(from, msg.sender), ERC7984UnauthorizedSpender(from, msg.sender));
|
|
200
187
|
transferred = _transferAndCall(from, to, amount, data);
|
|
201
188
|
FHE.allowTransient(transferred, msg.sender);
|
|
202
189
|
}
|
|
203
190
|
|
|
204
191
|
/**
|
|
205
|
-
* @dev Discloses an encrypted amount `encryptedAmount` publicly via an {
|
|
192
|
+
* @dev Discloses an encrypted amount `encryptedAmount` publicly via an {IERC7984-AmountDisclosed}
|
|
206
193
|
* event. The caller and this contract must be authorized to use the encrypted amount on the ACL.
|
|
207
194
|
*
|
|
208
195
|
* NOTE: This is an asynchronous operation where the actual decryption happens off-chain and
|
|
@@ -210,29 +197,34 @@ abstract contract ConfidentialFungibleToken is IConfidentialFungibleToken {
|
|
|
210
197
|
*/
|
|
211
198
|
function discloseEncryptedAmount(euint64 encryptedAmount) public virtual {
|
|
212
199
|
require(
|
|
213
|
-
FHE.isAllowed(encryptedAmount, msg.sender)
|
|
214
|
-
|
|
200
|
+
FHE.isAllowed(encryptedAmount, msg.sender),
|
|
201
|
+
ERC7984UnauthorizedUseOfEncryptedAmount(encryptedAmount, msg.sender)
|
|
215
202
|
);
|
|
216
203
|
|
|
217
204
|
bytes32[] memory cts = new bytes32[](1);
|
|
218
205
|
cts[0] = euint64.unwrap(encryptedAmount);
|
|
219
|
-
|
|
220
|
-
_requestHandles[requestID] = encryptedAmount;
|
|
206
|
+
FHE.requestDecryption(cts, this.finalizeDiscloseEncryptedAmount.selector);
|
|
221
207
|
}
|
|
222
208
|
|
|
223
|
-
|
|
209
|
+
/**
|
|
210
|
+
* @dev Finalizes a disclose encrypted amount request.
|
|
211
|
+
* For gas saving purposes, the `requestId` might not be related to a
|
|
212
|
+
* {discloseEncryptedAmount} request. As a result, the current {finalizeDiscloseEncryptedAmount}
|
|
213
|
+
* function might emit a disclosed amount related to another decryption request context.
|
|
214
|
+
* In this case it would only display public information
|
|
215
|
+
* since the handle would have already been allowed for public decryption through a previous
|
|
216
|
+
* `FHE.requestDecryption` call.
|
|
217
|
+
* The downside of this behavior is that a {finalizeDiscloseEncryptedAmount} watcher might observe
|
|
218
|
+
* unexpected `AmountDisclosed` events.
|
|
219
|
+
*/
|
|
224
220
|
function finalizeDiscloseEncryptedAmount(
|
|
225
221
|
uint256 requestId,
|
|
226
|
-
|
|
227
|
-
bytes
|
|
222
|
+
bytes calldata cleartexts,
|
|
223
|
+
bytes calldata decryptionProof
|
|
228
224
|
) public virtual {
|
|
229
|
-
FHE.checkSignatures(requestId,
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
require(FHE.isInitialized(requestHandle), ConfidentialFungibleTokenInvalidGatewayRequest(requestId));
|
|
233
|
-
emit AmountDisclosed(requestHandle, amount);
|
|
234
|
-
|
|
235
|
-
_requestHandles[requestId] = euint64.wrap(0);
|
|
225
|
+
FHE.checkSignatures(requestId, cleartexts, decryptionProof);
|
|
226
|
+
euint64 requestHandle = euint64.wrap(FHE.loadRequestedHandles(requestId)[0]);
|
|
227
|
+
emit AmountDisclosed(requestHandle, abi.decode(cleartexts, (uint64)));
|
|
236
228
|
}
|
|
237
229
|
|
|
238
230
|
function _setOperator(address holder, address operator, uint48 until) internal virtual {
|
|
@@ -241,18 +233,18 @@ abstract contract ConfidentialFungibleToken is IConfidentialFungibleToken {
|
|
|
241
233
|
}
|
|
242
234
|
|
|
243
235
|
function _mint(address to, euint64 amount) internal returns (euint64 transferred) {
|
|
244
|
-
require(to != address(0),
|
|
236
|
+
require(to != address(0), ERC7984InvalidReceiver(address(0)));
|
|
245
237
|
return _update(address(0), to, amount);
|
|
246
238
|
}
|
|
247
239
|
|
|
248
240
|
function _burn(address from, euint64 amount) internal returns (euint64 transferred) {
|
|
249
|
-
require(from != address(0),
|
|
241
|
+
require(from != address(0), ERC7984InvalidSender(address(0)));
|
|
250
242
|
return _update(from, address(0), amount);
|
|
251
243
|
}
|
|
252
244
|
|
|
253
245
|
function _transfer(address from, address to, euint64 amount) internal returns (euint64 transferred) {
|
|
254
|
-
require(from != address(0),
|
|
255
|
-
require(to != address(0),
|
|
246
|
+
require(from != address(0), ERC7984InvalidSender(address(0)));
|
|
247
|
+
require(to != address(0), ERC7984InvalidReceiver(address(0)));
|
|
256
248
|
return _update(from, to, amount);
|
|
257
249
|
}
|
|
258
250
|
|
|
@@ -266,14 +258,11 @@ abstract contract ConfidentialFungibleToken is IConfidentialFungibleToken {
|
|
|
266
258
|
euint64 sent = _transfer(from, to, amount);
|
|
267
259
|
|
|
268
260
|
// Perform callback
|
|
269
|
-
|
|
270
|
-
ConfidentialFungibleTokenUtils.checkOnTransferReceived(msg.sender, from, to, sent, data),
|
|
271
|
-
sent,
|
|
272
|
-
FHE.asEuint64(0)
|
|
273
|
-
);
|
|
261
|
+
ebool success = ERC7984Utils.checkOnTransferReceived(msg.sender, from, to, sent, data);
|
|
274
262
|
|
|
275
|
-
//
|
|
276
|
-
_update(to, from, FHE.
|
|
263
|
+
// Try to refund if callback fails
|
|
264
|
+
euint64 refund = _update(to, from, FHE.select(success, FHE.asEuint64(0), sent));
|
|
265
|
+
transferred = FHE.sub(sent, refund);
|
|
277
266
|
}
|
|
278
267
|
|
|
279
268
|
function _update(address from, address to, euint64 amount) internal virtual returns (euint64 transferred) {
|
|
@@ -281,13 +270,13 @@ abstract contract ConfidentialFungibleToken is IConfidentialFungibleToken {
|
|
|
281
270
|
euint64 ptr;
|
|
282
271
|
|
|
283
272
|
if (from == address(0)) {
|
|
284
|
-
(success, ptr) =
|
|
273
|
+
(success, ptr) = FHESafeMath.tryIncrease(_totalSupply, amount);
|
|
285
274
|
FHE.allowThis(ptr);
|
|
286
275
|
_totalSupply = ptr;
|
|
287
276
|
} else {
|
|
288
277
|
euint64 fromBalance = _balances[from];
|
|
289
|
-
require(FHE.isInitialized(fromBalance),
|
|
290
|
-
(success, ptr) =
|
|
278
|
+
require(FHE.isInitialized(fromBalance), ERC7984ZeroBalance(from));
|
|
279
|
+
(success, ptr) = FHESafeMath.tryDecrease(fromBalance, amount);
|
|
291
280
|
FHE.allowThis(ptr);
|
|
292
281
|
FHE.allow(ptr, from);
|
|
293
282
|
_balances[from] = ptr;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
// OpenZeppelin Confidential Contracts (last updated v0.
|
|
2
|
+
// OpenZeppelin Confidential Contracts (last updated v0.3.0-rc.0) (token/ERC7984/extensions/ERC7984ERC20Wrapper.sol)
|
|
3
3
|
|
|
4
4
|
pragma solidity ^0.8.27;
|
|
5
5
|
|
|
@@ -9,14 +9,17 @@ import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol";
|
|
|
9
9
|
import {IERC20Metadata} from "@openzeppelin/contracts/interfaces/IERC20Metadata.sol";
|
|
10
10
|
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
|
11
11
|
import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
|
|
12
|
-
import {
|
|
12
|
+
import {ERC7984} from "./../ERC7984.sol";
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
-
* @dev A wrapper contract built on top of {
|
|
16
|
-
* into
|
|
15
|
+
* @dev A wrapper contract built on top of {ERC7984} that allows wrapping an `ERC20` token
|
|
16
|
+
* into an `ERC7984` token. The wrapper contract implements the `IERC1363Receiver` interface
|
|
17
17
|
* which allows users to transfer `ERC1363` tokens directly to the wrapper with a callback to wrap the tokens.
|
|
18
|
+
*
|
|
19
|
+
* WARNING: Minting assumes the full amount of the underlying token transfer has been received, hence some non-standard
|
|
20
|
+
* tokens such as fee-on-transfer or other deflationary-type tokens are not supported by this wrapper.
|
|
18
21
|
*/
|
|
19
|
-
abstract contract
|
|
22
|
+
abstract contract ERC7984ERC20Wrapper is ERC7984, IERC1363Receiver {
|
|
20
23
|
IERC20 private immutable _underlying;
|
|
21
24
|
uint8 private immutable _decimals;
|
|
22
25
|
uint256 private immutable _rate;
|
|
@@ -38,7 +41,7 @@ abstract contract ConfidentialFungibleTokenERC20Wrapper is ConfidentialFungibleT
|
|
|
38
41
|
}
|
|
39
42
|
}
|
|
40
43
|
|
|
41
|
-
/// @inheritdoc
|
|
44
|
+
/// @inheritdoc ERC7984
|
|
42
45
|
function decimals() public view virtual override returns (uint8) {
|
|
43
46
|
return _decimals;
|
|
44
47
|
}
|
|
@@ -68,16 +71,16 @@ abstract contract ConfidentialFungibleTokenERC20Wrapper is ConfidentialFungibleT
|
|
|
68
71
|
bytes calldata data
|
|
69
72
|
) public virtual returns (bytes4) {
|
|
70
73
|
// check caller is the token contract
|
|
71
|
-
require(address(underlying()) == msg.sender,
|
|
72
|
-
|
|
73
|
-
// transfer excess back to the sender
|
|
74
|
-
uint256 excess = amount % rate();
|
|
75
|
-
if (excess > 0) SafeERC20.safeTransfer(underlying(), from, excess);
|
|
74
|
+
require(address(underlying()) == msg.sender, ERC7984UnauthorizedCaller(msg.sender));
|
|
76
75
|
|
|
77
76
|
// mint confidential token
|
|
78
77
|
address to = data.length < 20 ? from : address(bytes20(data));
|
|
79
78
|
_mint(to, FHE.asEuint64(SafeCast.toUint64(amount / rate())));
|
|
80
79
|
|
|
80
|
+
// transfer excess back to the sender
|
|
81
|
+
uint256 excess = amount % rate();
|
|
82
|
+
if (excess > 0) SafeERC20.safeTransfer(underlying(), from, excess);
|
|
83
|
+
|
|
81
84
|
// return magic value
|
|
82
85
|
return IERC1363Receiver.onTransferReceived.selector;
|
|
83
86
|
}
|
|
@@ -104,10 +107,7 @@ abstract contract ConfidentialFungibleTokenERC20Wrapper is ConfidentialFungibleT
|
|
|
104
107
|
* NOTE: The caller *must* already be approved by ACL for the given `amount`.
|
|
105
108
|
*/
|
|
106
109
|
function unwrap(address from, address to, euint64 amount) public virtual {
|
|
107
|
-
require(
|
|
108
|
-
FHE.isAllowed(amount, msg.sender),
|
|
109
|
-
ConfidentialFungibleTokenUnauthorizedUseOfEncryptedAmount(amount, msg.sender)
|
|
110
|
-
);
|
|
110
|
+
require(FHE.isAllowed(amount, msg.sender), ERC7984UnauthorizedUseOfEncryptedAmount(amount, msg.sender));
|
|
111
111
|
_unwrap(from, to, amount);
|
|
112
112
|
}
|
|
113
113
|
|
|
@@ -125,24 +125,24 @@ abstract contract ConfidentialFungibleTokenERC20Wrapper is ConfidentialFungibleT
|
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
/**
|
|
128
|
-
* @dev
|
|
129
|
-
* Fills unwrap requests.
|
|
128
|
+
* @dev Fills an unwrap request for a given request id related to a decrypted unwrap amount.
|
|
130
129
|
*/
|
|
131
|
-
function finalizeUnwrap(
|
|
132
|
-
|
|
130
|
+
function finalizeUnwrap(
|
|
131
|
+
uint256 requestID,
|
|
132
|
+
bytes calldata cleartexts,
|
|
133
|
+
bytes calldata decryptionProof
|
|
134
|
+
) public virtual {
|
|
135
|
+
FHE.checkSignatures(requestID, cleartexts, decryptionProof);
|
|
133
136
|
address to = _receivers[requestID];
|
|
134
|
-
require(to != address(0),
|
|
137
|
+
require(to != address(0), ERC7984InvalidGatewayRequest(requestID));
|
|
135
138
|
delete _receivers[requestID];
|
|
136
139
|
|
|
137
|
-
SafeERC20.safeTransfer(underlying(), to,
|
|
140
|
+
SafeERC20.safeTransfer(underlying(), to, abi.decode(cleartexts, (uint64)) * rate());
|
|
138
141
|
}
|
|
139
142
|
|
|
140
143
|
function _unwrap(address from, address to, euint64 amount) internal virtual {
|
|
141
|
-
require(to != address(0),
|
|
142
|
-
require(
|
|
143
|
-
from == msg.sender || isOperator(from, msg.sender),
|
|
144
|
-
ConfidentialFungibleTokenUnauthorizedSpender(from, msg.sender)
|
|
145
|
-
);
|
|
144
|
+
require(to != address(0), ERC7984InvalidReceiver(to));
|
|
145
|
+
require(from == msg.sender || isOperator(from, msg.sender), ERC7984UnauthorizedSpender(from, msg.sender));
|
|
146
146
|
|
|
147
147
|
// try to burn, see how much we actually got
|
|
148
148
|
euint64 burntAmount = _burn(from, amount);
|
|
@@ -156,6 +156,15 @@ abstract contract ConfidentialFungibleTokenERC20Wrapper is ConfidentialFungibleT
|
|
|
156
156
|
_receivers[requestID] = to;
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
+
/**
|
|
160
|
+
* @dev Returns the default number of decimals of the underlying ERC-20 token that is being wrapped.
|
|
161
|
+
* Used as a default fallback when {_tryGetAssetDecimals} fails to fetch decimals of the underlying
|
|
162
|
+
* ERC-20 token.
|
|
163
|
+
*/
|
|
164
|
+
function _fallbackUnderlyingDecimals() internal pure virtual returns (uint8) {
|
|
165
|
+
return 18;
|
|
166
|
+
}
|
|
167
|
+
|
|
159
168
|
/**
|
|
160
169
|
* @dev Returns the maximum number that will be used for {decimals} by the wrapper.
|
|
161
170
|
*/
|
|
@@ -170,6 +179,6 @@ abstract contract ConfidentialFungibleTokenERC20Wrapper is ConfidentialFungibleT
|
|
|
170
179
|
if (success && encodedDecimals.length == 32) {
|
|
171
180
|
return abi.decode(encodedDecimals, (uint8));
|
|
172
181
|
}
|
|
173
|
-
return
|
|
182
|
+
return _fallbackUnderlyingDecimals();
|
|
174
183
|
}
|
|
175
184
|
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// OpenZeppelin Confidential Contracts (last updated v0.3.0-rc.0) (token/ERC7984/extensions/ERC7984Freezable.sol)
|
|
3
|
+
|
|
4
|
+
pragma solidity ^0.8.27;
|
|
5
|
+
|
|
6
|
+
import {FHE, ebool, euint64, externalEuint64} from "@fhevm/solidity/lib/FHE.sol";
|
|
7
|
+
import {FHESafeMath} from "../../../utils/FHESafeMath.sol";
|
|
8
|
+
import {ERC7984} from "../ERC7984.sol";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @dev Extension of {ERC7984} that implements a confidential
|
|
12
|
+
* freezing mechanism that can be managed by an authorized account with
|
|
13
|
+
* {setConfidentialFrozen} functions.
|
|
14
|
+
*
|
|
15
|
+
* The freezing mechanism provides the guarantee to the contract owner
|
|
16
|
+
* (e.g. a DAO or a well-configured multisig) that a specific confidential
|
|
17
|
+
* amount of tokens held by an account won't be transferable until those
|
|
18
|
+
* tokens are unfrozen.
|
|
19
|
+
*
|
|
20
|
+
* Inspired by https://github.com/OpenZeppelin/openzeppelin-community-contracts/blob/master/contracts/token/ERC20/extensions/ERC20Freezable.sol
|
|
21
|
+
*/
|
|
22
|
+
abstract contract ERC7984Freezable is ERC7984 {
|
|
23
|
+
/// @dev Confidential frozen amount of tokens per address.
|
|
24
|
+
mapping(address account => euint64 encryptedAmount) private _frozenBalances;
|
|
25
|
+
|
|
26
|
+
/// @dev Emitted when a confidential amount of token is frozen for an account
|
|
27
|
+
event TokensFrozen(address indexed account, euint64 encryptedAmount);
|
|
28
|
+
|
|
29
|
+
/// @dev Returns the confidential frozen balance of an account.
|
|
30
|
+
function confidentialFrozen(address account) public view virtual returns (euint64) {
|
|
31
|
+
return _frozenBalances[account];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/// @dev Returns the confidential available (unfrozen) balance of an account. Up to {confidentialBalanceOf}.
|
|
35
|
+
function confidentialAvailable(address account) public virtual returns (euint64) {
|
|
36
|
+
(ebool success, euint64 unfrozen) = FHESafeMath.tryDecrease(
|
|
37
|
+
confidentialBalanceOf(account),
|
|
38
|
+
confidentialFrozen(account)
|
|
39
|
+
);
|
|
40
|
+
return FHE.select(success, unfrozen, FHE.asEuint64(0));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/// @dev Internal function to freeze a confidential amount of tokens for an account.
|
|
44
|
+
function _setConfidentialFrozen(address account, euint64 encryptedAmount) internal virtual {
|
|
45
|
+
FHE.allowThis(encryptedAmount);
|
|
46
|
+
FHE.allow(encryptedAmount, account);
|
|
47
|
+
_frozenBalances[account] = encryptedAmount;
|
|
48
|
+
emit TokensFrozen(account, encryptedAmount);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @dev See {ERC7984-_update}.
|
|
53
|
+
*
|
|
54
|
+
* The `from` account must have sufficient unfrozen balance,
|
|
55
|
+
* otherwise 0 tokens are transferred.
|
|
56
|
+
* The default freezing behavior can be changed (for a pass-through for instance) by overriding
|
|
57
|
+
* {confidentialAvailable}.
|
|
58
|
+
*/
|
|
59
|
+
function _update(address from, address to, euint64 encryptedAmount) internal virtual override returns (euint64) {
|
|
60
|
+
if (from != address(0)) {
|
|
61
|
+
euint64 unfrozen = confidentialAvailable(from);
|
|
62
|
+
encryptedAmount = FHE.select(FHE.le(encryptedAmount, unfrozen), encryptedAmount, FHE.asEuint64(0));
|
|
63
|
+
}
|
|
64
|
+
return super._update(from, to, encryptedAmount);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// OpenZeppelin Confidential Contracts (last updated v0.3.0-rc.0) (token/ERC7984/extensions/ERC7984ObserverAccess.sol)
|
|
3
|
+
|
|
4
|
+
pragma solidity ^0.8.27;
|
|
5
|
+
|
|
6
|
+
import {FHE, euint64} from "@fhevm/solidity/lib/FHE.sol";
|
|
7
|
+
import {ERC7984} from "../ERC7984.sol";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @dev Extension of {ERC7984} that allows each account to add a observer who is given
|
|
11
|
+
* permanent ACL access to its transfer and balance amounts. A observer can be added or removed at any point in time.
|
|
12
|
+
*/
|
|
13
|
+
abstract contract ERC7984ObserverAccess is ERC7984 {
|
|
14
|
+
mapping(address => address) private _observers;
|
|
15
|
+
|
|
16
|
+
/// @dev Emitted when the observer is changed for the given account `account`.
|
|
17
|
+
event ERC7984ObserverAccessObserverSet(address account, address oldObserver, address newObserver);
|
|
18
|
+
|
|
19
|
+
/// @dev Thrown when an account tries to set a `newObserver` for a given `account` without proper authority.
|
|
20
|
+
error Unauthorized();
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @dev Sets the observer for the given account `account` to `newObserver`. Can be called by the
|
|
24
|
+
* account or the existing observer to abdicate the observer role (may only set to `address(0)`).
|
|
25
|
+
*/
|
|
26
|
+
function setObserver(address account, address newObserver) public virtual {
|
|
27
|
+
address oldObserver = observer(account);
|
|
28
|
+
require(msg.sender == account || (msg.sender == oldObserver && newObserver == address(0)), Unauthorized());
|
|
29
|
+
if (oldObserver != newObserver) {
|
|
30
|
+
if (newObserver != address(0)) {
|
|
31
|
+
euint64 balanceHandle = confidentialBalanceOf(account);
|
|
32
|
+
if (FHE.isInitialized(balanceHandle)) {
|
|
33
|
+
FHE.allow(balanceHandle, newObserver);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
emit ERC7984ObserverAccessObserverSet(account, oldObserver, _observers[account] = newObserver);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/// @dev Returns the observer for the given account `account`.
|
|
42
|
+
function observer(address account) public view virtual returns (address) {
|
|
43
|
+
return _observers[account];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function _update(address from, address to, euint64 amount) internal virtual override returns (euint64 transferred) {
|
|
47
|
+
transferred = super._update(from, to, amount);
|
|
48
|
+
|
|
49
|
+
address fromObserver = observer(from);
|
|
50
|
+
address toObserver = observer(to);
|
|
51
|
+
|
|
52
|
+
if (fromObserver != address(0)) {
|
|
53
|
+
FHE.allow(confidentialBalanceOf(from), fromObserver);
|
|
54
|
+
FHE.allow(transferred, fromObserver);
|
|
55
|
+
}
|
|
56
|
+
if (toObserver != address(0)) {
|
|
57
|
+
FHE.allow(confidentialBalanceOf(to), toObserver);
|
|
58
|
+
if (toObserver != fromObserver) {
|
|
59
|
+
FHE.allow(transferred, toObserver);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|