@openzeppelin/confidential-contracts 0.2.0 → 0.3.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 +2 -2
- package/build/contracts/{ConfidentialFungibleToken.json → ERC7984.json} +87 -58
- package/build/contracts/{ConfidentialFungibleTokenERC20Wrapper.json → ERC7984ERC20Wrapper.json} +143 -59
- package/build/contracts/ERC7984Freezable.json +700 -0
- package/build/contracts/ERC7984ObserverAccess.json +710 -0
- package/build/contracts/ERC7984Omnibus.json +1028 -0
- package/build/contracts/ERC7984Restricted.json +711 -0
- package/build/contracts/ERC7984Rwa.json +1385 -0
- package/build/contracts/{ConfidentialFungibleTokenUtils.json → ERC7984Utils.json} +4 -4
- package/build/contracts/{ConfidentialFungibleTokenVotes.json → ERC7984Votes.json} +142 -113
- package/build/contracts/FHESafeMath.json +2 -2
- package/build/contracts/{IConfidentialFungibleToken.json → IERC7984.json} +26 -7
- package/build/contracts/{IConfidentialFungibleTokenReceiver.json → IERC7984Receiver.json} +2 -2
- package/build/contracts/IERC7984Rwa.json +797 -0
- package/build/contracts/VestingWalletConfidentialFactory.json +2 -2
- package/finance/ERC7821WithExecutor.sol +3 -4
- package/finance/VestingWalletCliffConfidential.sol +3 -4
- package/finance/VestingWalletConfidential.sol +8 -12
- package/finance/VestingWalletConfidentialFactory.sol +7 -12
- package/interfaces/{IConfidentialFungibleToken.sol → IERC7984.sol} +6 -5
- package/interfaces/{IConfidentialFungibleTokenReceiver.sol → IERC7984Receiver.sol} +3 -3
- package/interfaces/IERC7984Rwa.sol +63 -0
- package/package.json +4 -4
- package/token/{ConfidentialFungibleToken.sol → ERC7984/ERC7984.sol} +81 -82
- package/token/{extensions/ConfidentialFungibleTokenERC20Wrapper.sol → ERC7984/extensions/ERC7984ERC20Wrapper.sol} +40 -35
- package/token/ERC7984/extensions/ERC7984Freezable.sol +75 -0
- package/token/ERC7984/extensions/ERC7984ObserverAccess.sol +63 -0
- package/token/ERC7984/extensions/ERC7984Omnibus.sol +209 -0
- package/token/ERC7984/extensions/ERC7984Restricted.sol +110 -0
- package/token/ERC7984/extensions/ERC7984Rwa.sol +248 -0
- package/token/{extensions/ConfidentialFungibleTokenVotes.sol → ERC7984/extensions/ERC7984Votes.sol} +8 -14
- package/token/{utils/ConfidentialFungibleTokenUtils.sol → ERC7984/utils/ERC7984Utils.sol} +14 -13
- package/utils/FHESafeMath.sol +45 -7
- package/utils/structs/temporary-Checkpoints.sol +2 -2
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
// OpenZeppelin Confidential Contracts (last updated v0.
|
|
2
|
+
// OpenZeppelin Confidential Contracts (last updated v0.3.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 {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol";
|
|
7
|
+
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
|
|
8
|
+
import {IERC7984} from "./../../interfaces/IERC7984.sol";
|
|
9
|
+
import {FHESafeMath} from "./../../utils/FHESafeMath.sol";
|
|
10
|
+
import {ERC7984Utils} from "./utils/ERC7984Utils.sol";
|
|
9
11
|
|
|
10
12
|
/**
|
|
11
|
-
* @dev Reference implementation for {
|
|
13
|
+
* @dev Reference implementation for {IERC7984}.
|
|
12
14
|
*
|
|
13
15
|
* This contract implements a fungible token where balances and transfers are encrypted using the Zama fhEVM,
|
|
14
16
|
* providing confidentiality to users. Token amounts are stored as encrypted, unsigned integers (`euint64`)
|
|
@@ -22,87 +24,94 @@ import {ConfidentialFungibleTokenUtils} from "./utils/ConfidentialFungibleTokenU
|
|
|
22
24
|
* - Transfer and call pattern
|
|
23
25
|
* - Safe overflow/underflow handling for FHE operations
|
|
24
26
|
*/
|
|
25
|
-
abstract contract
|
|
27
|
+
abstract contract ERC7984 is IERC7984, ERC165 {
|
|
26
28
|
mapping(address holder => euint64) private _balances;
|
|
27
29
|
mapping(address holder => mapping(address spender => uint48)) private _operators;
|
|
28
|
-
mapping(uint256 requestId => euint64 encryptedAmount) private _requestHandles;
|
|
29
30
|
euint64 private _totalSupply;
|
|
30
31
|
string private _name;
|
|
31
32
|
string private _symbol;
|
|
32
|
-
string private
|
|
33
|
+
string private _contractURI;
|
|
34
|
+
|
|
35
|
+
/// @dev Emitted when an encrypted amount `encryptedAmount` is requested for disclosure by `requester`.
|
|
36
|
+
event AmountDiscloseRequested(euint64 indexed encryptedAmount, address indexed requester);
|
|
33
37
|
|
|
34
38
|
/// @dev The given receiver `receiver` is invalid for transfers.
|
|
35
|
-
error
|
|
39
|
+
error ERC7984InvalidReceiver(address receiver);
|
|
36
40
|
|
|
37
41
|
/// @dev The given sender `sender` is invalid for transfers.
|
|
38
|
-
error
|
|
42
|
+
error ERC7984InvalidSender(address sender);
|
|
39
43
|
|
|
40
44
|
/// @dev The given holder `holder` is not authorized to spend on behalf of `spender`.
|
|
41
|
-
error
|
|
45
|
+
error ERC7984UnauthorizedSpender(address holder, address spender);
|
|
42
46
|
|
|
43
47
|
/// @dev The holder `holder` is trying to send tokens but has a balance of 0.
|
|
44
|
-
error
|
|
48
|
+
error ERC7984ZeroBalance(address holder);
|
|
45
49
|
|
|
46
50
|
/**
|
|
47
51
|
* @dev The caller `user` does not have access to the encrypted amount `amount`.
|
|
48
52
|
*
|
|
49
53
|
* NOTE: Try using the equivalent transfer function with an input proof.
|
|
50
54
|
*/
|
|
51
|
-
error
|
|
55
|
+
error ERC7984UnauthorizedUseOfEncryptedAmount(euint64 amount, address user);
|
|
52
56
|
|
|
53
57
|
/// @dev The given caller `caller` is not authorized for the current operation.
|
|
54
|
-
error
|
|
58
|
+
error ERC7984UnauthorizedCaller(address caller);
|
|
55
59
|
|
|
56
60
|
/// @dev The given gateway request ID `requestId` is invalid.
|
|
57
|
-
error
|
|
61
|
+
error ERC7984InvalidGatewayRequest(uint256 requestId);
|
|
58
62
|
|
|
59
|
-
constructor(string memory name_, string memory symbol_, string memory
|
|
63
|
+
constructor(string memory name_, string memory symbol_, string memory contractURI_) {
|
|
60
64
|
_name = name_;
|
|
61
65
|
_symbol = symbol_;
|
|
62
|
-
|
|
66
|
+
_contractURI = contractURI_;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/// @inheritdoc ERC165
|
|
70
|
+
function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC165) returns (bool) {
|
|
71
|
+
return interfaceId == type(IERC7984).interfaceId || super.supportsInterface(interfaceId);
|
|
63
72
|
}
|
|
64
73
|
|
|
65
|
-
/// @inheritdoc
|
|
74
|
+
/// @inheritdoc IERC7984
|
|
66
75
|
function name() public view virtual returns (string memory) {
|
|
67
76
|
return _name;
|
|
68
77
|
}
|
|
69
78
|
|
|
70
|
-
/// @inheritdoc
|
|
79
|
+
/// @inheritdoc IERC7984
|
|
71
80
|
function symbol() public view virtual returns (string memory) {
|
|
72
81
|
return _symbol;
|
|
73
82
|
}
|
|
74
83
|
|
|
75
|
-
/// @inheritdoc
|
|
84
|
+
/// @inheritdoc IERC7984
|
|
76
85
|
function decimals() public view virtual returns (uint8) {
|
|
77
86
|
return 6;
|
|
78
87
|
}
|
|
79
88
|
|
|
80
|
-
/// @inheritdoc
|
|
81
|
-
function
|
|
82
|
-
return
|
|
89
|
+
/// @inheritdoc IERC7984
|
|
90
|
+
function contractURI() public view virtual returns (string memory) {
|
|
91
|
+
return _contractURI;
|
|
83
92
|
}
|
|
84
93
|
|
|
85
|
-
/// @inheritdoc
|
|
94
|
+
/// @inheritdoc IERC7984
|
|
86
95
|
function confidentialTotalSupply() public view virtual returns (euint64) {
|
|
87
96
|
return _totalSupply;
|
|
88
97
|
}
|
|
89
98
|
|
|
90
|
-
/// @inheritdoc
|
|
99
|
+
/// @inheritdoc IERC7984
|
|
91
100
|
function confidentialBalanceOf(address account) public view virtual returns (euint64) {
|
|
92
101
|
return _balances[account];
|
|
93
102
|
}
|
|
94
103
|
|
|
95
|
-
/// @inheritdoc
|
|
104
|
+
/// @inheritdoc IERC7984
|
|
96
105
|
function isOperator(address holder, address spender) public view virtual returns (bool) {
|
|
97
106
|
return holder == spender || block.timestamp <= _operators[holder][spender];
|
|
98
107
|
}
|
|
99
108
|
|
|
100
|
-
/// @inheritdoc
|
|
109
|
+
/// @inheritdoc IERC7984
|
|
101
110
|
function setOperator(address operator, uint48 until) public virtual {
|
|
102
111
|
_setOperator(msg.sender, operator, until);
|
|
103
112
|
}
|
|
104
113
|
|
|
105
|
-
/// @inheritdoc
|
|
114
|
+
/// @inheritdoc IERC7984
|
|
106
115
|
function confidentialTransfer(
|
|
107
116
|
address to,
|
|
108
117
|
externalEuint64 encryptedAmount,
|
|
@@ -111,43 +120,37 @@ abstract contract ConfidentialFungibleToken is IConfidentialFungibleToken {
|
|
|
111
120
|
return _transfer(msg.sender, to, FHE.fromExternal(encryptedAmount, inputProof));
|
|
112
121
|
}
|
|
113
122
|
|
|
114
|
-
/// @inheritdoc
|
|
123
|
+
/// @inheritdoc IERC7984
|
|
115
124
|
function confidentialTransfer(address to, euint64 amount) public virtual returns (euint64) {
|
|
116
|
-
require(
|
|
117
|
-
FHE.isAllowed(amount, msg.sender),
|
|
118
|
-
ConfidentialFungibleTokenUnauthorizedUseOfEncryptedAmount(amount, msg.sender)
|
|
119
|
-
);
|
|
125
|
+
require(FHE.isAllowed(amount, msg.sender), ERC7984UnauthorizedUseOfEncryptedAmount(amount, msg.sender));
|
|
120
126
|
return _transfer(msg.sender, to, amount);
|
|
121
127
|
}
|
|
122
128
|
|
|
123
|
-
/// @inheritdoc
|
|
129
|
+
/// @inheritdoc IERC7984
|
|
124
130
|
function confidentialTransferFrom(
|
|
125
131
|
address from,
|
|
126
132
|
address to,
|
|
127
133
|
externalEuint64 encryptedAmount,
|
|
128
134
|
bytes calldata inputProof
|
|
129
135
|
) public virtual returns (euint64 transferred) {
|
|
130
|
-
require(isOperator(from, msg.sender),
|
|
136
|
+
require(isOperator(from, msg.sender), ERC7984UnauthorizedSpender(from, msg.sender));
|
|
131
137
|
transferred = _transfer(from, to, FHE.fromExternal(encryptedAmount, inputProof));
|
|
132
138
|
FHE.allowTransient(transferred, msg.sender);
|
|
133
139
|
}
|
|
134
140
|
|
|
135
|
-
/// @inheritdoc
|
|
141
|
+
/// @inheritdoc IERC7984
|
|
136
142
|
function confidentialTransferFrom(
|
|
137
143
|
address from,
|
|
138
144
|
address to,
|
|
139
145
|
euint64 amount
|
|
140
146
|
) public virtual returns (euint64 transferred) {
|
|
141
|
-
require(
|
|
142
|
-
|
|
143
|
-
ConfidentialFungibleTokenUnauthorizedUseOfEncryptedAmount(amount, msg.sender)
|
|
144
|
-
);
|
|
145
|
-
require(isOperator(from, msg.sender), ConfidentialFungibleTokenUnauthorizedSpender(from, msg.sender));
|
|
147
|
+
require(FHE.isAllowed(amount, msg.sender), ERC7984UnauthorizedUseOfEncryptedAmount(amount, msg.sender));
|
|
148
|
+
require(isOperator(from, msg.sender), ERC7984UnauthorizedSpender(from, msg.sender));
|
|
146
149
|
transferred = _transfer(from, to, amount);
|
|
147
150
|
FHE.allowTransient(transferred, msg.sender);
|
|
148
151
|
}
|
|
149
152
|
|
|
150
|
-
/// @inheritdoc
|
|
153
|
+
/// @inheritdoc IERC7984
|
|
151
154
|
function confidentialTransferAndCall(
|
|
152
155
|
address to,
|
|
153
156
|
externalEuint64 encryptedAmount,
|
|
@@ -158,21 +161,18 @@ abstract contract ConfidentialFungibleToken is IConfidentialFungibleToken {
|
|
|
158
161
|
FHE.allowTransient(transferred, msg.sender);
|
|
159
162
|
}
|
|
160
163
|
|
|
161
|
-
/// @inheritdoc
|
|
164
|
+
/// @inheritdoc IERC7984
|
|
162
165
|
function confidentialTransferAndCall(
|
|
163
166
|
address to,
|
|
164
167
|
euint64 amount,
|
|
165
168
|
bytes calldata data
|
|
166
169
|
) public virtual returns (euint64 transferred) {
|
|
167
|
-
require(
|
|
168
|
-
FHE.isAllowed(amount, msg.sender),
|
|
169
|
-
ConfidentialFungibleTokenUnauthorizedUseOfEncryptedAmount(amount, msg.sender)
|
|
170
|
-
);
|
|
170
|
+
require(FHE.isAllowed(amount, msg.sender), ERC7984UnauthorizedUseOfEncryptedAmount(amount, msg.sender));
|
|
171
171
|
transferred = _transferAndCall(msg.sender, to, amount, data);
|
|
172
172
|
FHE.allowTransient(transferred, msg.sender);
|
|
173
173
|
}
|
|
174
174
|
|
|
175
|
-
/// @inheritdoc
|
|
175
|
+
/// @inheritdoc IERC7984
|
|
176
176
|
function confidentialTransferFromAndCall(
|
|
177
177
|
address from,
|
|
178
178
|
address to,
|
|
@@ -180,59 +180,58 @@ abstract contract ConfidentialFungibleToken is IConfidentialFungibleToken {
|
|
|
180
180
|
bytes calldata inputProof,
|
|
181
181
|
bytes calldata data
|
|
182
182
|
) public virtual returns (euint64 transferred) {
|
|
183
|
-
require(isOperator(from, msg.sender),
|
|
183
|
+
require(isOperator(from, msg.sender), ERC7984UnauthorizedSpender(from, msg.sender));
|
|
184
184
|
transferred = _transferAndCall(from, to, FHE.fromExternal(encryptedAmount, inputProof), data);
|
|
185
185
|
FHE.allowTransient(transferred, msg.sender);
|
|
186
186
|
}
|
|
187
187
|
|
|
188
|
-
/// @inheritdoc
|
|
188
|
+
/// @inheritdoc IERC7984
|
|
189
189
|
function confidentialTransferFromAndCall(
|
|
190
190
|
address from,
|
|
191
191
|
address to,
|
|
192
192
|
euint64 amount,
|
|
193
193
|
bytes calldata data
|
|
194
194
|
) public virtual returns (euint64 transferred) {
|
|
195
|
-
require(
|
|
196
|
-
|
|
197
|
-
ConfidentialFungibleTokenUnauthorizedUseOfEncryptedAmount(amount, msg.sender)
|
|
198
|
-
);
|
|
199
|
-
require(isOperator(from, msg.sender), ConfidentialFungibleTokenUnauthorizedSpender(from, msg.sender));
|
|
195
|
+
require(FHE.isAllowed(amount, msg.sender), ERC7984UnauthorizedUseOfEncryptedAmount(amount, msg.sender));
|
|
196
|
+
require(isOperator(from, msg.sender), ERC7984UnauthorizedSpender(from, msg.sender));
|
|
200
197
|
transferred = _transferAndCall(from, to, amount, data);
|
|
201
198
|
FHE.allowTransient(transferred, msg.sender);
|
|
202
199
|
}
|
|
203
200
|
|
|
204
201
|
/**
|
|
205
|
-
* @dev
|
|
206
|
-
*
|
|
202
|
+
* @dev Starts the process to disclose an encrypted amount `encryptedAmount` publicly by making it
|
|
203
|
+
* publicly decryptable. Emits the {AmountDiscloseRequested} event.
|
|
207
204
|
*
|
|
208
|
-
* NOTE:
|
|
209
|
-
*
|
|
205
|
+
* NOTE: Both `msg.sender` and `address(this)` must have permission to access the encrypted amount
|
|
206
|
+
* `encryptedAmount` to request disclosure of the encrypted amount `encryptedAmount`.
|
|
210
207
|
*/
|
|
211
|
-
function
|
|
208
|
+
function requestDiscloseEncryptedAmount(euint64 encryptedAmount) public virtual {
|
|
212
209
|
require(
|
|
213
|
-
FHE.isAllowed(encryptedAmount, msg.sender)
|
|
214
|
-
|
|
210
|
+
FHE.isAllowed(encryptedAmount, msg.sender),
|
|
211
|
+
ERC7984UnauthorizedUseOfEncryptedAmount(encryptedAmount, msg.sender)
|
|
215
212
|
);
|
|
216
213
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
uint256 requestID = FHE.requestDecryption(cts, this.finalizeDiscloseEncryptedAmount.selector);
|
|
220
|
-
_requestHandles[requestID] = encryptedAmount;
|
|
214
|
+
FHE.makePubliclyDecryptable(encryptedAmount);
|
|
215
|
+
emit AmountDiscloseRequested(encryptedAmount, msg.sender);
|
|
221
216
|
}
|
|
222
217
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
218
|
+
/**
|
|
219
|
+
* @dev Publicly discloses an encrypted value with a given decryption proof. Emits the {AmountDisclosed} event.
|
|
220
|
+
*
|
|
221
|
+
* NOTE: May not be tied to a prior request via {requestDiscloseEncryptedAmount}.
|
|
222
|
+
*/
|
|
223
|
+
function discloseEncryptedAmount(
|
|
224
|
+
euint64 encryptedAmount,
|
|
225
|
+
uint64 cleartextAmount,
|
|
226
|
+
bytes calldata decryptionProof
|
|
228
227
|
) public virtual {
|
|
229
|
-
|
|
228
|
+
bytes32[] memory handles = new bytes32[](1);
|
|
229
|
+
handles[0] = euint64.unwrap(encryptedAmount);
|
|
230
230
|
|
|
231
|
-
|
|
232
|
-
require(FHE.isInitialized(requestHandle), ConfidentialFungibleTokenInvalidGatewayRequest(requestId));
|
|
233
|
-
emit AmountDisclosed(requestHandle, amount);
|
|
231
|
+
bytes memory cleartextMemory = abi.encode(cleartextAmount);
|
|
234
232
|
|
|
235
|
-
|
|
233
|
+
FHE.checkSignatures(handles, cleartextMemory, decryptionProof);
|
|
234
|
+
emit AmountDisclosed(encryptedAmount, cleartextAmount);
|
|
236
235
|
}
|
|
237
236
|
|
|
238
237
|
function _setOperator(address holder, address operator, uint48 until) internal virtual {
|
|
@@ -241,18 +240,18 @@ abstract contract ConfidentialFungibleToken is IConfidentialFungibleToken {
|
|
|
241
240
|
}
|
|
242
241
|
|
|
243
242
|
function _mint(address to, euint64 amount) internal returns (euint64 transferred) {
|
|
244
|
-
require(to != address(0),
|
|
243
|
+
require(to != address(0), ERC7984InvalidReceiver(address(0)));
|
|
245
244
|
return _update(address(0), to, amount);
|
|
246
245
|
}
|
|
247
246
|
|
|
248
247
|
function _burn(address from, euint64 amount) internal returns (euint64 transferred) {
|
|
249
|
-
require(from != address(0),
|
|
248
|
+
require(from != address(0), ERC7984InvalidSender(address(0)));
|
|
250
249
|
return _update(from, address(0), amount);
|
|
251
250
|
}
|
|
252
251
|
|
|
253
252
|
function _transfer(address from, address to, euint64 amount) internal returns (euint64 transferred) {
|
|
254
|
-
require(from != address(0),
|
|
255
|
-
require(to != address(0),
|
|
253
|
+
require(from != address(0), ERC7984InvalidSender(address(0)));
|
|
254
|
+
require(to != address(0), ERC7984InvalidReceiver(address(0)));
|
|
256
255
|
return _update(from, to, amount);
|
|
257
256
|
}
|
|
258
257
|
|
|
@@ -266,7 +265,7 @@ abstract contract ConfidentialFungibleToken is IConfidentialFungibleToken {
|
|
|
266
265
|
euint64 sent = _transfer(from, to, amount);
|
|
267
266
|
|
|
268
267
|
// Perform callback
|
|
269
|
-
ebool success =
|
|
268
|
+
ebool success = ERC7984Utils.checkOnTransferReceived(msg.sender, from, to, sent, data);
|
|
270
269
|
|
|
271
270
|
// Try to refund if callback fails
|
|
272
271
|
euint64 refund = _update(to, from, FHE.select(success, FHE.asEuint64(0), sent));
|
|
@@ -283,7 +282,7 @@ abstract contract ConfidentialFungibleToken is IConfidentialFungibleToken {
|
|
|
283
282
|
_totalSupply = ptr;
|
|
284
283
|
} else {
|
|
285
284
|
euint64 fromBalance = _balances[from];
|
|
286
|
-
require(FHE.isInitialized(fromBalance),
|
|
285
|
+
require(FHE.isInitialized(fromBalance), ERC7984ZeroBalance(from));
|
|
287
286
|
(success, ptr) = FHESafeMath.tryDecrease(fromBalance, amount);
|
|
288
287
|
FHE.allowThis(ptr);
|
|
289
288
|
FHE.allow(ptr, from);
|
|
@@ -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) (token/ERC7984/extensions/ERC7984ERC20Wrapper.sol)
|
|
3
3
|
|
|
4
4
|
pragma solidity ^0.8.27;
|
|
5
5
|
|
|
@@ -9,23 +9,27 @@ 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
18
|
*
|
|
19
19
|
* WARNING: Minting assumes the full amount of the underlying token transfer has been received, hence some non-standard
|
|
20
20
|
* tokens such as fee-on-transfer or other deflationary-type tokens are not supported by this wrapper.
|
|
21
21
|
*/
|
|
22
|
-
abstract contract
|
|
22
|
+
abstract contract ERC7984ERC20Wrapper is ERC7984, IERC1363Receiver {
|
|
23
23
|
IERC20 private immutable _underlying;
|
|
24
24
|
uint8 private immutable _decimals;
|
|
25
25
|
uint256 private immutable _rate;
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
mapping(euint64 unwrapAmount => address recipient) private _unwrapRequests;
|
|
28
|
+
|
|
29
|
+
event UnwrapRequested(address indexed receiver, euint64 amount);
|
|
30
|
+
event UnwrapFinalized(address indexed receiver, euint64 encryptedAmount, uint64 cleartextAmount);
|
|
31
|
+
|
|
32
|
+
error InvalidUnwrapRequest(euint64 amount);
|
|
29
33
|
|
|
30
34
|
constructor(IERC20 underlying_) {
|
|
31
35
|
_underlying = underlying_;
|
|
@@ -41,7 +45,7 @@ abstract contract ConfidentialFungibleTokenERC20Wrapper is ConfidentialFungibleT
|
|
|
41
45
|
}
|
|
42
46
|
}
|
|
43
47
|
|
|
44
|
-
/// @inheritdoc
|
|
48
|
+
/// @inheritdoc ERC7984
|
|
45
49
|
function decimals() public view virtual override returns (uint8) {
|
|
46
50
|
return _decimals;
|
|
47
51
|
}
|
|
@@ -71,7 +75,7 @@ abstract contract ConfidentialFungibleTokenERC20Wrapper is ConfidentialFungibleT
|
|
|
71
75
|
bytes calldata data
|
|
72
76
|
) public virtual returns (bytes4) {
|
|
73
77
|
// check caller is the token contract
|
|
74
|
-
require(address(underlying()) == msg.sender,
|
|
78
|
+
require(address(underlying()) == msg.sender, ERC7984UnauthorizedCaller(msg.sender));
|
|
75
79
|
|
|
76
80
|
// mint confidential token
|
|
77
81
|
address to = data.length < 20 ? from : address(bytes20(data));
|
|
@@ -102,15 +106,11 @@ abstract contract ConfidentialFungibleTokenERC20Wrapper is ConfidentialFungibleT
|
|
|
102
106
|
* @dev Unwraps tokens from `from` and sends the underlying tokens to `to`. The caller must be `from`
|
|
103
107
|
* or be an approved operator for `from`. `amount * rate()` underlying tokens are sent to `to`.
|
|
104
108
|
*
|
|
105
|
-
* NOTE:
|
|
106
|
-
* tokens.
|
|
109
|
+
* NOTE: The unwrap request created by this function must be finalized by calling {finalizeUnwrap}.
|
|
107
110
|
* NOTE: The caller *must* already be approved by ACL for the given `amount`.
|
|
108
111
|
*/
|
|
109
112
|
function unwrap(address from, address to, euint64 amount) public virtual {
|
|
110
|
-
require(
|
|
111
|
-
FHE.isAllowed(amount, msg.sender),
|
|
112
|
-
ConfidentialFungibleTokenUnauthorizedUseOfEncryptedAmount(amount, msg.sender)
|
|
113
|
-
);
|
|
113
|
+
require(FHE.isAllowed(amount, msg.sender), ERC7984UnauthorizedUseOfEncryptedAmount(amount, msg.sender));
|
|
114
114
|
_unwrap(from, to, amount);
|
|
115
115
|
}
|
|
116
116
|
|
|
@@ -127,35 +127,40 @@ abstract contract ConfidentialFungibleTokenERC20Wrapper is ConfidentialFungibleT
|
|
|
127
127
|
_unwrap(from, to, FHE.fromExternal(encryptedAmount, inputProof));
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
130
|
+
/// @dev Fills an unwrap request for a given cipher-text `burntAmount` with the `cleartextAmount` and `decryptionProof`.
|
|
131
|
+
function finalizeUnwrap(
|
|
132
|
+
euint64 burntAmount,
|
|
133
|
+
uint64 burntAmountCleartext,
|
|
134
|
+
bytes calldata decryptionProof
|
|
135
|
+
) public virtual {
|
|
136
|
+
address to = _unwrapRequests[burntAmount];
|
|
137
|
+
require(to != address(0), InvalidUnwrapRequest(burntAmount));
|
|
138
|
+
delete _unwrapRequests[burntAmount];
|
|
138
139
|
|
|
139
|
-
|
|
140
|
+
bytes32[] memory handles = new bytes32[](1);
|
|
141
|
+
handles[0] = euint64.unwrap(burntAmount);
|
|
142
|
+
|
|
143
|
+
bytes memory cleartexts = abi.encode(burntAmountCleartext);
|
|
144
|
+
|
|
145
|
+
FHE.checkSignatures(handles, cleartexts, decryptionProof);
|
|
146
|
+
|
|
147
|
+
SafeERC20.safeTransfer(underlying(), to, burntAmountCleartext * rate());
|
|
148
|
+
|
|
149
|
+
emit UnwrapFinalized(to, burntAmount, burntAmountCleartext);
|
|
140
150
|
}
|
|
141
151
|
|
|
142
152
|
function _unwrap(address from, address to, euint64 amount) internal virtual {
|
|
143
|
-
require(to != address(0),
|
|
144
|
-
require(
|
|
145
|
-
from == msg.sender || isOperator(from, msg.sender),
|
|
146
|
-
ConfidentialFungibleTokenUnauthorizedSpender(from, msg.sender)
|
|
147
|
-
);
|
|
153
|
+
require(to != address(0), ERC7984InvalidReceiver(to));
|
|
154
|
+
require(from == msg.sender || isOperator(from, msg.sender), ERC7984UnauthorizedSpender(from, msg.sender));
|
|
148
155
|
|
|
149
156
|
// try to burn, see how much we actually got
|
|
150
157
|
euint64 burntAmount = _burn(from, amount);
|
|
158
|
+
FHE.makePubliclyDecryptable(burntAmount);
|
|
151
159
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
cts[0] = euint64.unwrap(burntAmount);
|
|
155
|
-
uint256 requestID = FHE.requestDecryption(cts, this.finalizeUnwrap.selector);
|
|
160
|
+
assert(_unwrapRequests[burntAmount] == address(0));
|
|
161
|
+
_unwrapRequests[burntAmount] = to;
|
|
156
162
|
|
|
157
|
-
|
|
158
|
-
_receivers[requestID] = to;
|
|
163
|
+
emit UnwrapRequested(to, burntAmount);
|
|
159
164
|
}
|
|
160
165
|
|
|
161
166
|
/**
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// OpenZeppelin Confidential Contracts (last updated v0.3.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. Gives ACL allowance to `account`.
|
|
35
|
+
function confidentialAvailable(address account) public virtual returns (euint64) {
|
|
36
|
+
euint64 amount = _confidentialAvailable(account);
|
|
37
|
+
FHE.allowThis(amount);
|
|
38
|
+
FHE.allow(amount, account);
|
|
39
|
+
return amount;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/// @dev Internal function to calculate the available balance of an account. Does not give any allowances.
|
|
43
|
+
function _confidentialAvailable(address account) internal virtual returns (euint64) {
|
|
44
|
+
(ebool success, euint64 unfrozen) = FHESafeMath.tryDecrease(
|
|
45
|
+
confidentialBalanceOf(account),
|
|
46
|
+
confidentialFrozen(account)
|
|
47
|
+
);
|
|
48
|
+
return FHE.select(success, unfrozen, FHE.asEuint64(0));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/// @dev Internal function to freeze a confidential amount of tokens for an account.
|
|
52
|
+
function _setConfidentialFrozen(address account, euint64 encryptedAmount) internal virtual {
|
|
53
|
+
FHE.allowThis(encryptedAmount);
|
|
54
|
+
FHE.allow(encryptedAmount, account);
|
|
55
|
+
_frozenBalances[account] = encryptedAmount;
|
|
56
|
+
emit TokensFrozen(account, encryptedAmount);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* @dev See {ERC7984-_update}.
|
|
61
|
+
*
|
|
62
|
+
* The `from` account must have sufficient unfrozen balance,
|
|
63
|
+
* otherwise 0 tokens are transferred.
|
|
64
|
+
* The default freezing behavior can be changed (for a pass-through for instance) by overriding
|
|
65
|
+
* {_confidentialAvailable}. The internal function is used for actual gating (not the public function)
|
|
66
|
+
* to avoid unnecessarily granting ACL allowances.
|
|
67
|
+
*/
|
|
68
|
+
function _update(address from, address to, euint64 encryptedAmount) internal virtual override returns (euint64) {
|
|
69
|
+
if (from != address(0)) {
|
|
70
|
+
euint64 unfrozen = _confidentialAvailable(from);
|
|
71
|
+
encryptedAmount = FHE.select(FHE.le(encryptedAmount, unfrozen), encryptedAmount, FHE.asEuint64(0));
|
|
72
|
+
}
|
|
73
|
+
return super._update(from, to, encryptedAmount);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// OpenZeppelin Confidential Contracts (last updated v0.3.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 account => 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
|
+
}
|