@openzeppelin/confidential-contracts 0.4.0 → 0.5.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.
Files changed (39) hide show
  1. package/build/contracts/BatcherConfidential.json +5 -0
  2. package/build/contracts/CheckpointsConfidential.json +2 -2
  3. package/build/contracts/ERC7984.json +6 -28
  4. package/build/contracts/ERC7984BalanceCapHookModule.json +277 -0
  5. package/build/contracts/ERC7984ERC20Wrapper.json +6 -28
  6. package/build/contracts/ERC7984Freezable.json +6 -28
  7. package/build/contracts/ERC7984HolderCapHookModule.json +291 -0
  8. package/build/contracts/ERC7984HookModule.json +200 -0
  9. package/build/contracts/ERC7984Hooked.json +832 -0
  10. package/build/contracts/ERC7984IdentityCheck.json +691 -0
  11. package/build/contracts/ERC7984ObserverAccess.json +6 -28
  12. package/build/contracts/ERC7984Omnibus.json +6 -28
  13. package/build/contracts/ERC7984Restricted.json +6 -28
  14. package/build/contracts/ERC7984Rwa.json +61 -29
  15. package/build/contracts/ERC7984Utils.json +2 -2
  16. package/build/contracts/ERC7984Votes.json +6 -28
  17. package/build/contracts/FHESafeMath.json +2 -2
  18. package/build/contracts/IERC7984HookModule.json +151 -0
  19. package/build/contracts/IERC7984Rwa.json +87 -0
  20. package/build/contracts/IIdentityRegistry.json +30 -0
  21. package/finance/BatcherConfidential.sol +7 -3
  22. package/governance/utils/VotesConfidential.sol +2 -2
  23. package/interfaces/IERC7984HookModule.sol +39 -0
  24. package/interfaces/IERC7984Receiver.sol +3 -1
  25. package/interfaces/IERC7984Rwa.sol +28 -1
  26. package/package.json +1 -1
  27. package/token/ERC7984/ERC7984.sol +39 -28
  28. package/token/ERC7984/extensions/ERC7984ERC20Wrapper.sol +3 -3
  29. package/token/ERC7984/extensions/ERC7984Freezable.sol +3 -7
  30. package/token/ERC7984/extensions/ERC7984Hooked.sol +158 -0
  31. package/token/ERC7984/extensions/ERC7984IdentityCheck.sol +58 -0
  32. package/token/ERC7984/extensions/ERC7984Restricted.sol +3 -3
  33. package/token/ERC7984/extensions/ERC7984Rwa.sol +65 -28
  34. package/token/ERC7984/utils/ERC7984BalanceCapHookModule.sol +92 -0
  35. package/token/ERC7984/utils/ERC7984HolderCapHookModule.sol +145 -0
  36. package/token/ERC7984/utils/ERC7984HookModule.sol +170 -0
  37. package/utils/FHESafeMath.sol +26 -1
  38. package/utils/HandleAccessManager.sol +5 -3
  39. package/utils/structs/CheckpointsConfidential.sol +1 -2
@@ -72,6 +72,31 @@
72
72
  "name": "OperatorSet",
73
73
  "type": "event"
74
74
  },
75
+ {
76
+ "anonymous": false,
77
+ "inputs": [
78
+ {
79
+ "indexed": true,
80
+ "internalType": "address",
81
+ "name": "lostAccount",
82
+ "type": "address"
83
+ },
84
+ {
85
+ "indexed": true,
86
+ "internalType": "address",
87
+ "name": "newAccount",
88
+ "type": "address"
89
+ },
90
+ {
91
+ "indexed": false,
92
+ "internalType": "euint64",
93
+ "name": "amount",
94
+ "type": "bytes32"
95
+ }
96
+ ],
97
+ "name": "TokensRecovered",
98
+ "type": "event"
99
+ },
75
100
  {
76
101
  "inputs": [
77
102
  {
@@ -621,6 +646,44 @@
621
646
  "stateMutability": "nonpayable",
622
647
  "type": "function"
623
648
  },
649
+ {
650
+ "inputs": [
651
+ {
652
+ "internalType": "address",
653
+ "name": "account",
654
+ "type": "address"
655
+ }
656
+ ],
657
+ "name": "isAdmin",
658
+ "outputs": [
659
+ {
660
+ "internalType": "bool",
661
+ "name": "",
662
+ "type": "bool"
663
+ }
664
+ ],
665
+ "stateMutability": "view",
666
+ "type": "function"
667
+ },
668
+ {
669
+ "inputs": [
670
+ {
671
+ "internalType": "address",
672
+ "name": "account",
673
+ "type": "address"
674
+ }
675
+ ],
676
+ "name": "isAgent",
677
+ "outputs": [
678
+ {
679
+ "internalType": "bool",
680
+ "name": "",
681
+ "type": "bool"
682
+ }
683
+ ],
684
+ "stateMutability": "view",
685
+ "type": "function"
686
+ },
624
687
  {
625
688
  "inputs": [
626
689
  {
@@ -678,6 +741,30 @@
678
741
  "stateMutability": "view",
679
742
  "type": "function"
680
743
  },
744
+ {
745
+ "inputs": [
746
+ {
747
+ "internalType": "address",
748
+ "name": "lostAccount",
749
+ "type": "address"
750
+ },
751
+ {
752
+ "internalType": "address",
753
+ "name": "newAccount",
754
+ "type": "address"
755
+ }
756
+ ],
757
+ "name": "recoverAddress",
758
+ "outputs": [
759
+ {
760
+ "internalType": "euint64",
761
+ "name": "",
762
+ "type": "bytes32"
763
+ }
764
+ ],
765
+ "stateMutability": "nonpayable",
766
+ "type": "function"
767
+ },
681
768
  {
682
769
  "inputs": [
683
770
  {
@@ -0,0 +1,30 @@
1
+ {
2
+ "_format": "hh-sol-artifact-1",
3
+ "contractName": "IIdentityRegistry",
4
+ "sourceName": "contracts/token/ERC7984/extensions/ERC7984IdentityCheck.sol",
5
+ "abi": [
6
+ {
7
+ "inputs": [
8
+ {
9
+ "internalType": "address",
10
+ "name": "account",
11
+ "type": "address"
12
+ }
13
+ ],
14
+ "name": "isVerified",
15
+ "outputs": [
16
+ {
17
+ "internalType": "bool",
18
+ "name": "",
19
+ "type": "bool"
20
+ }
21
+ ],
22
+ "stateMutability": "view",
23
+ "type": "function"
24
+ }
25
+ ],
26
+ "bytecode": "0x",
27
+ "deployedBytecode": "0x",
28
+ "linkReferences": {},
29
+ "deployedLinkReferences": {}
30
+ }
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- // OpenZeppelin Confidential Contracts (last updated v0.4.0) (finance/BatcherConfidential.sol)
2
+ // OpenZeppelin Confidential Contracts (last updated v0.5.0-rc.0) (finance/BatcherConfidential.sol)
3
3
 
4
4
  pragma solidity ^0.8.27;
5
5
 
@@ -16,8 +16,8 @@ import {FHESafeMath} from "./../utils/FHESafeMath.sol";
16
16
 
17
17
  /**
18
18
  * @dev `BatcherConfidential` is a batching primitive that enables routing between two {ERC7984ERC20Wrapper} contracts
19
- * via a non-confidential route. Users deposit {fromToken} into the batcher and receive {toToken} in exchange. Deposits are
20
- * made by using `ERC7984` transfer and call functions such as {ERC7984-confidentialTransferAndCall}.
19
+ * (with distinct underlying tokens) via a non-confidential route. Users deposit {fromToken} into the batcher and receive
20
+ * {toToken} in exchange. Deposits are made by using `ERC7984` transfer and call functions such as {ERC7984-confidentialTransferAndCall}.
21
21
  *
22
22
  * Developers must implement the virtual function {_executeRoute} to perform the batch's route. This function is called
23
23
  * once the batch deposits are unwrapped into the underlying tokens. The function should swap the underlying {fromToken} for
@@ -110,6 +110,9 @@ abstract contract BatcherConfidential is ReentrancyGuardTransient, IERC7984Recei
110
110
  /// @dev The given `token` does not support `IERC7984ERC20Wrapper` via `ERC165`.
111
111
  error InvalidWrapperToken(address token);
112
112
 
113
+ /// @dev The underlying wrapper tokens are the same.
114
+ error DuplicateUnderlyingTokens();
115
+
113
116
  constructor(IERC7984ERC20Wrapper fromToken_, IERC7984ERC20Wrapper toToken_) {
114
117
  require(
115
118
  ERC165Checker.supportsInterface(address(fromToken_), type(IERC7984ERC20Wrapper).interfaceId),
@@ -119,6 +122,7 @@ abstract contract BatcherConfidential is ReentrancyGuardTransient, IERC7984Recei
119
122
  ERC165Checker.supportsInterface(address(toToken_), type(IERC7984ERC20Wrapper).interfaceId),
120
123
  InvalidWrapperToken(address(toToken_))
121
124
  );
125
+ require(fromToken_.underlying() != toToken_.underlying(), DuplicateUnderlyingTokens());
122
126
 
123
127
  _fromToken = fromToken_;
124
128
  _toToken = toToken_;
@@ -1,9 +1,9 @@
1
1
  // SPDX-License-Identifier: MIT
2
- // OpenZeppelin Confidential Contracts (last updated v0.4.0) (governance/utils/VotesConfidential.sol)
2
+ // OpenZeppelin Confidential Contracts (last updated v0.5.0-rc.0) (governance/utils/VotesConfidential.sol)
3
3
 
4
4
  pragma solidity ^0.8.26;
5
5
 
6
- import {FHE, ebool, euint64} from "@fhevm/solidity/lib/FHE.sol";
6
+ import {FHE, euint64} from "@fhevm/solidity/lib/FHE.sol";
7
7
  import {IERC6372} from "@openzeppelin/contracts/interfaces/IERC6372.sol";
8
8
  import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
9
9
  import {EIP712} from "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
@@ -0,0 +1,39 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // OpenZeppelin Confidential Contracts (last updated v0.5.0-rc.0) (interfaces/IERC7984HookModule.sol)
3
+
4
+ pragma solidity ^0.8.24;
5
+
6
+ import {euint64, ebool} from "@fhevm/solidity/lib/FHE.sol";
7
+ import {IERC165} from "@openzeppelin/contracts/interfaces/IERC165.sol";
8
+
9
+ /// @dev Interface for an ERC-7984 hook module.
10
+ interface IERC7984HookModule is IERC165 {
11
+ /// @dev Optionally emitted by a module to indicate the result of its validation (pre-transfer) hook.
12
+ event ERC7984HookModuleResult(
13
+ address indexed token,
14
+ address indexed from,
15
+ address indexed to,
16
+ euint64 encryptedAmount,
17
+ ebool result,
18
+ bytes32 context
19
+ );
20
+
21
+ /**
22
+ * @dev Hook that runs before a transfer. Should not mutate token state. Module is already
23
+ * granted transient access to `encryptedAmount`.
24
+ */
25
+ function preTransfer(address from, address to, euint64 encryptedAmount) external returns (ebool);
26
+
27
+ /// @dev Performs operation after transfer.
28
+ function postTransfer(address from, address to, euint64 encryptedAmount) external;
29
+
30
+ /// @dev Performs operations after installation.
31
+ function onInstall(bytes calldata initData) external;
32
+
33
+ /**
34
+ * @dev Performs operations after uninstallation.
35
+ *
36
+ * NOTE: The module uninstallation will succeed even if this function reverts.
37
+ */
38
+ function onUninstall(bytes calldata deinitData) external;
39
+ }
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- // OpenZeppelin Confidential Contracts (last updated v0.4.0) (interfaces/IERC7984Receiver.sol)
2
+ // OpenZeppelin Confidential Contracts (last updated v0.5.0-rc.0) (interfaces/IERC7984Receiver.sol)
3
3
  pragma solidity ^0.8.24;
4
4
 
5
5
  import {ebool, euint64} from "@fhevm/solidity/lib/FHE.sol";
@@ -10,6 +10,8 @@ interface IERC7984Receiver {
10
10
  * @dev Called upon receiving a confidential token transfer. Returns an encrypted boolean indicating success
11
11
  * of the callback. If false is returned, the token contract will attempt to refund the transfer.
12
12
  *
13
+ * NOTE: The calling contract (token) must be granted ACL allowance to read the confidential return value.
14
+ *
13
15
  * WARNING: Do not manually refund the transfer AND return false, as this can lead to double refunds.
14
16
  */
15
17
  function onConfidentialTransferReceived(
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- // OpenZeppelin Confidential Contracts (last updated v0.4.0) (interfaces/IERC7984Rwa.sol)
2
+ // OpenZeppelin Confidential Contracts (last updated v0.5.0-rc.0) (interfaces/IERC7984Rwa.sol)
3
3
  pragma solidity ^0.8.24;
4
4
 
5
5
  import {externalEuint64, euint64} from "@fhevm/solidity/lib/FHE.sol";
@@ -7,46 +7,69 @@ import {IERC7984} from "./IERC7984.sol";
7
7
 
8
8
  /// @dev Interface for confidential RWA contracts.
9
9
  interface IERC7984Rwa is IERC7984 {
10
+ /// @dev Emitted when the balance `amount` of `lostAccount` is recovered to `newAccount`.
11
+ event TokensRecovered(address indexed lostAccount, address indexed newAccount, euint64 amount);
12
+
10
13
  /// @dev Returns true if the contract is paused, false otherwise.
11
14
  function paused() external view returns (bool);
15
+
16
+ /// @dev Returns true if has admin role, false otherwise.
17
+ function isAdmin(address account) external view returns (bool);
18
+
19
+ /// @dev Returns true if agent, false otherwise.
20
+ function isAgent(address account) external view returns (bool);
21
+
12
22
  /// @dev Returns whether an account is allowed to interact with the token.
13
23
  function canTransact(address account) external view returns (bool);
24
+
14
25
  /// @dev Returns the confidential frozen balance of an account.
15
26
  function confidentialFrozen(address account) external view returns (euint64);
27
+
16
28
  /// @dev Returns the confidential available (unfrozen) balance of an account. Up to {IERC7984-confidentialBalanceOf}.
17
29
  function confidentialAvailable(address account) external returns (euint64);
30
+
18
31
  /// @dev Pauses contract.
19
32
  function pause() external;
33
+
20
34
  /// @dev Unpauses contract.
21
35
  function unpause() external;
36
+
22
37
  /// @dev Blocks a user account.
23
38
  function blockUser(address account) external;
39
+
24
40
  /// @dev Unblocks a user account.
25
41
  function unblockUser(address account) external;
42
+
26
43
  /// @dev Sets confidential amount of token for an account as frozen with proof.
27
44
  function setConfidentialFrozen(
28
45
  address account,
29
46
  externalEuint64 encryptedAmount,
30
47
  bytes calldata inputProof
31
48
  ) external;
49
+
32
50
  /// @dev Sets confidential amount of token for an account as frozen.
33
51
  function setConfidentialFrozen(address account, euint64 encryptedAmount) external;
52
+
34
53
  /// @dev Mints confidential amount of tokens to account with proof.
35
54
  function confidentialMint(
36
55
  address to,
37
56
  externalEuint64 encryptedAmount,
38
57
  bytes calldata inputProof
39
58
  ) external returns (euint64);
59
+
40
60
  /// @dev Mints confidential amount of tokens to account.
41
61
  function confidentialMint(address to, euint64 encryptedAmount) external returns (euint64);
62
+
42
63
  /// @dev Burns confidential amount of tokens from account with proof.
43
64
  function confidentialBurn(
44
65
  address account,
45
66
  externalEuint64 encryptedAmount,
46
67
  bytes calldata inputProof
47
68
  ) external returns (euint64);
69
+
48
70
  /// @dev Burns confidential amount of tokens from account.
49
71
  function confidentialBurn(address account, euint64 encryptedAmount) external returns (euint64);
72
+
50
73
  /// @dev Forces transfer of confidential amount of tokens from account to account with proof by skipping compliance checks.
51
74
  function forceConfidentialTransferFrom(
52
75
  address from,
@@ -54,10 +77,14 @@ interface IERC7984Rwa is IERC7984 {
54
77
  externalEuint64 encryptedAmount,
55
78
  bytes calldata inputProof
56
79
  ) external returns (euint64);
80
+
57
81
  /// @dev Forces transfer of confidential amount of tokens from account to account by skipping compliance checks.
58
82
  function forceConfidentialTransferFrom(
59
83
  address from,
60
84
  address to,
61
85
  euint64 encryptedAmount
62
86
  ) external returns (euint64);
87
+
88
+ /// @dev Recovers the address of a lost account to a new account.
89
+ function recoverAddress(address lostAccount, address newAccount) external returns (euint64);
63
90
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@openzeppelin/confidential-contracts",
3
3
  "description": "Smart Contract library for use with confidential coprocessors",
4
- "version": "0.4.0",
4
+ "version": "0.5.0-rc.0",
5
5
  "files": [
6
6
  "**/*.sol",
7
7
  "/build/contracts/*.json",
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- // OpenZeppelin Confidential Contracts (last updated v0.3.0) (token/ERC7984/ERC7984.sol)
2
+ // OpenZeppelin Confidential Contracts (last updated v0.5.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";
@@ -44,9 +44,6 @@ abstract contract ERC7984 is IERC7984, ERC165 {
44
44
  /// @dev The given holder `holder` is not authorized to spend on behalf of `spender`.
45
45
  error ERC7984UnauthorizedSpender(address holder, address spender);
46
46
 
47
- /// @dev The holder `holder` is trying to send tokens but has a balance of 0.
48
- error ERC7984ZeroBalance(address holder);
49
-
50
47
  /**
51
48
  * @dev The caller `user` does not have access to the encrypted amount `amount`.
52
49
  *
@@ -57,9 +54,6 @@ abstract contract ERC7984 is IERC7984, ERC165 {
57
54
  /// @dev The given caller `caller` is not authorized for the current operation.
58
55
  error ERC7984UnauthorizedCaller(address caller);
59
56
 
60
- /// @dev The given gateway request ID `requestId` is invalid.
61
- error ERC7984InvalidGatewayRequest(uint256 requestId);
62
-
63
57
  constructor(string memory name_, string memory symbol_, string memory contractURI_) {
64
58
  _name = name_;
65
59
  _symbol = symbol_;
@@ -132,22 +126,20 @@ abstract contract ERC7984 is IERC7984, ERC165 {
132
126
  address to,
133
127
  externalEuint64 encryptedAmount,
134
128
  bytes calldata inputProof
135
- ) public virtual returns (euint64 transferred) {
129
+ ) public virtual returns (euint64) {
136
130
  require(isOperator(from, msg.sender), ERC7984UnauthorizedSpender(from, msg.sender));
137
- transferred = _transfer(from, to, FHE.fromExternal(encryptedAmount, inputProof));
131
+ euint64 transferred = _transfer(from, to, FHE.fromExternal(encryptedAmount, inputProof));
138
132
  FHE.allowTransient(transferred, msg.sender);
133
+ return transferred;
139
134
  }
140
135
 
141
136
  /// @inheritdoc IERC7984
142
- function confidentialTransferFrom(
143
- address from,
144
- address to,
145
- euint64 amount
146
- ) public virtual returns (euint64 transferred) {
137
+ function confidentialTransferFrom(address from, address to, euint64 amount) public virtual returns (euint64) {
147
138
  require(FHE.isAllowed(amount, msg.sender), ERC7984UnauthorizedUseOfEncryptedAmount(amount, msg.sender));
148
139
  require(isOperator(from, msg.sender), ERC7984UnauthorizedSpender(from, msg.sender));
149
- transferred = _transfer(from, to, amount);
140
+ euint64 transferred = _transfer(from, to, amount);
150
141
  FHE.allowTransient(transferred, msg.sender);
142
+ return transferred;
151
143
  }
152
144
 
153
145
  /// @inheritdoc IERC7984
@@ -156,9 +148,8 @@ abstract contract ERC7984 is IERC7984, ERC165 {
156
148
  externalEuint64 encryptedAmount,
157
149
  bytes calldata inputProof,
158
150
  bytes calldata data
159
- ) public virtual returns (euint64 transferred) {
160
- transferred = _transferAndCall(msg.sender, to, FHE.fromExternal(encryptedAmount, inputProof), data);
161
- FHE.allowTransient(transferred, msg.sender);
151
+ ) public virtual returns (euint64) {
152
+ return _transferAndCall(msg.sender, to, FHE.fromExternal(encryptedAmount, inputProof), data);
162
153
  }
163
154
 
164
155
  /// @inheritdoc IERC7984
@@ -166,10 +157,9 @@ abstract contract ERC7984 is IERC7984, ERC165 {
166
157
  address to,
167
158
  euint64 amount,
168
159
  bytes calldata data
169
- ) public virtual returns (euint64 transferred) {
160
+ ) public virtual returns (euint64) {
170
161
  require(FHE.isAllowed(amount, msg.sender), ERC7984UnauthorizedUseOfEncryptedAmount(amount, msg.sender));
171
- transferred = _transferAndCall(msg.sender, to, amount, data);
172
- FHE.allowTransient(transferred, msg.sender);
162
+ return _transferAndCall(msg.sender, to, amount, data);
173
163
  }
174
164
 
175
165
  /// @inheritdoc IERC7984
@@ -179,10 +169,9 @@ abstract contract ERC7984 is IERC7984, ERC165 {
179
169
  externalEuint64 encryptedAmount,
180
170
  bytes calldata inputProof,
181
171
  bytes calldata data
182
- ) public virtual returns (euint64 transferred) {
172
+ ) public virtual returns (euint64) {
183
173
  require(isOperator(from, msg.sender), ERC7984UnauthorizedSpender(from, msg.sender));
184
- transferred = _transferAndCall(from, to, FHE.fromExternal(encryptedAmount, inputProof), data);
185
- FHE.allowTransient(transferred, msg.sender);
174
+ return _transferAndCall(from, to, FHE.fromExternal(encryptedAmount, inputProof), data);
186
175
  }
187
176
 
188
177
  /// @inheritdoc IERC7984
@@ -191,11 +180,10 @@ abstract contract ERC7984 is IERC7984, ERC165 {
191
180
  address to,
192
181
  euint64 amount,
193
182
  bytes calldata data
194
- ) public virtual returns (euint64 transferred) {
183
+ ) public virtual returns (euint64) {
195
184
  require(FHE.isAllowed(amount, msg.sender), ERC7984UnauthorizedUseOfEncryptedAmount(amount, msg.sender));
196
185
  require(isOperator(from, msg.sender), ERC7984UnauthorizedSpender(from, msg.sender));
197
- transferred = _transferAndCall(from, to, amount, data);
198
- FHE.allowTransient(transferred, msg.sender);
186
+ return _transferAndCall(from, to, amount, data);
199
187
  }
200
188
 
201
189
  /**
@@ -255,6 +243,25 @@ abstract contract ERC7984 is IERC7984, ERC165 {
255
243
  return _update(from, to, amount);
256
244
  }
257
245
 
246
+ /**
247
+ * @dev Transfers the given amount of tokens from `from` to `to` and calls the `onConfidentialTransferReceived`
248
+ * function on the recipient.
249
+ *
250
+ * The token contract initiates a second transfer refunding the tokens from the recipient to the sender--the
251
+ * amount is 0 if the callback succeeds, otherwise the amount is the amount that was transferred.
252
+ *
253
+ * The returned `transferred` amount is a fresh ciphertext computed as `sent - refund`
254
+ * and `msg.sender` only receives a transient FHE allowance for it. This value is generally
255
+ * intended to be processed only in the same transaction. Event observers see `sent` and `refund` individually.
256
+ *
257
+ * WARNING: The refund triggered when {IERC7984Receiver-onConfidentialTransferReceived} returns an encrypted
258
+ * false is best-effort only. A receiver that transfers, burns, or otherwise reduces its balance during
259
+ * the hook can still return false, in which case the refund transfers zero tokens. The sender's tokens
260
+ * end up with the recipient rather than being refunded.
261
+ *
262
+ * WARNING: Refunds are subject to the same validation flow as a normal transfer--they may fail for a variety of
263
+ * reasons (such as failed hook validation in {ERC7984Hooked}). In these cases, the tokens do not return to the sender.
264
+ */
258
265
  function _transferAndCall(
259
266
  address from,
260
267
  address to,
@@ -270,8 +277,13 @@ abstract contract ERC7984 is IERC7984, ERC165 {
270
277
  // Try to refund if callback fails
271
278
  euint64 refund = _update(to, from, FHE.select(success, FHE.asEuint64(0), sent));
272
279
  transferred = FHE.sub(sent, refund);
280
+ FHE.allowTransient(transferred, msg.sender);
273
281
  }
274
282
 
283
+ /**
284
+ * @dev Safely moves up to `amount` from `from` to `to`, or mints/burns if `from`/`to` is the zero address.
285
+ * Emits a {ConfidentialTransfer} event with the successfully transferred amount.
286
+ */
275
287
  function _update(address from, address to, euint64 amount) internal virtual returns (euint64 transferred) {
276
288
  ebool success;
277
289
  euint64 ptr;
@@ -282,7 +294,6 @@ abstract contract ERC7984 is IERC7984, ERC165 {
282
294
  _totalSupply = ptr;
283
295
  } else {
284
296
  euint64 fromBalance = _balances[from];
285
- require(FHE.isInitialized(fromBalance), ERC7984ZeroBalance(from));
286
297
  (success, ptr) = FHESafeMath.tryDecrease(fromBalance, amount);
287
298
  FHE.allowThis(ptr);
288
299
  FHE.allow(ptr, from);
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- // OpenZeppelin Confidential Contracts (last updated v0.4.0) (token/ERC7984/extensions/ERC7984ERC20Wrapper.sol)
2
+ // OpenZeppelin Confidential Contracts (last updated v0.5.0-rc.0) (token/ERC7984/extensions/ERC7984ERC20Wrapper.sol)
3
3
 
4
4
  pragma solidity ^0.8.27;
5
5
 
@@ -180,8 +180,8 @@ abstract contract ERC7984ERC20Wrapper is ERC7984, IERC7984ERC20Wrapper, IERC1363
180
180
  }
181
181
 
182
182
  /**
183
- * @dev Get the address that has a pending unwrap request for the given `unwrapAmount`. Returns `address(0)` if no pending
184
- * unwrap request for the amount `unwrapAmount` exists.
183
+ * @dev Gets the address that will receive the ERC-20 tokens associated with a pending unwrap request identified by
184
+ * `unwrapRequestId`. Returns `address(0)` if there is no pending unwrap request with id `unwrapRequestId`.
185
185
  */
186
186
  function unwrapRequester(bytes32 unwrapRequestId) public view virtual returns (address) {
187
187
  return _unwrapRequests[unwrapRequestId];
@@ -1,9 +1,9 @@
1
1
  // SPDX-License-Identifier: MIT
2
- // OpenZeppelin Confidential Contracts (last updated v0.4.0) (token/ERC7984/extensions/ERC7984Freezable.sol)
2
+ // OpenZeppelin Confidential Contracts (last updated v0.5.0-rc.0) (token/ERC7984/extensions/ERC7984Freezable.sol)
3
3
 
4
4
  pragma solidity ^0.8.27;
5
5
 
6
- import {FHE, ebool, euint64, externalEuint64} from "@fhevm/solidity/lib/FHE.sol";
6
+ import {FHE, ebool, euint64} from "@fhevm/solidity/lib/FHE.sol";
7
7
  import {FHESafeMath} from "../../../utils/FHESafeMath.sol";
8
8
  import {ERC7984} from "../ERC7984.sol";
9
9
 
@@ -40,11 +40,7 @@ abstract contract ERC7984Freezable is ERC7984 {
40
40
 
41
41
  /// @dev Internal function to calculate the available balance of an account. Does not give any allowances.
42
42
  function _confidentialAvailable(address account) internal virtual returns (euint64) {
43
- (ebool success, euint64 unfrozen) = FHESafeMath.tryDecrease(
44
- confidentialBalanceOf(account),
45
- confidentialFrozen(account)
46
- );
47
- return FHE.select(success, unfrozen, FHE.asEuint64(0));
43
+ return FHESafeMath.saturatingSub(confidentialBalanceOf(account), confidentialFrozen(account));
48
44
  }
49
45
 
50
46
  /// @dev Internal function to freeze a confidential amount of tokens for an account.