@openzeppelin/confidential-contracts 0.3.0-rc.0 → 0.3.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.
- package/build/contracts/Checkpoints.json +2 -2
- package/build/contracts/CheckpointsConfidential.json +2 -2
- package/build/contracts/ERC7984.json +72 -38
- package/build/contracts/ERC7984ERC20Wrapper.json +159 -39
- package/build/contracts/ERC7984Freezable.json +72 -38
- package/build/contracts/ERC7984ObserverAccess.json +73 -39
- package/build/contracts/ERC7984Omnibus.json +72 -38
- package/build/contracts/ERC7984Restricted.json +74 -40
- package/build/contracts/ERC7984Rwa.json +55 -40
- package/build/contracts/ERC7984Utils.json +2 -2
- package/build/contracts/ERC7984Votes.json +80 -46
- package/build/contracts/FHESafeMath.json +2 -2
- package/build/contracts/IERC7984.json +19 -0
- package/finance/ERC7821WithExecutor.sol +3 -4
- package/finance/VestingWalletCliffConfidential.sol +3 -4
- package/finance/VestingWalletConfidential.sol +3 -4
- package/finance/VestingWalletConfidentialFactory.sol +1 -1
- package/interfaces/IERC7984.sol +4 -3
- package/interfaces/IERC7984Receiver.sol +1 -1
- package/interfaces/IERC7984Rwa.sol +2 -3
- package/package.json +2 -2
- package/token/ERC7984/ERC7984.sol +32 -25
- package/token/ERC7984/extensions/ERC7984ERC20Wrapper.sol +87 -40
- package/token/ERC7984/extensions/ERC7984Freezable.sol +13 -4
- package/token/ERC7984/extensions/ERC7984ObserverAccess.sol +2 -2
- package/token/ERC7984/extensions/ERC7984Omnibus.sol +3 -3
- package/token/ERC7984/extensions/ERC7984Restricted.sol +3 -11
- package/token/ERC7984/extensions/ERC7984Rwa.sol +38 -26
- package/token/ERC7984/extensions/ERC7984Votes.sol +1 -1
- package/token/ERC7984/utils/ERC7984Utils.sol +3 -2
- package/utils/FHESafeMath.sol +5 -2
- package/utils/structs/temporary-Checkpoints.sol +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
// OpenZeppelin Confidential Contracts (last updated v0.3.0
|
|
2
|
+
// OpenZeppelin Confidential Contracts (last updated v0.3.0) (token/ERC7984/extensions/ERC7984Freezable.sol)
|
|
3
3
|
|
|
4
4
|
pragma solidity ^0.8.27;
|
|
5
5
|
|
|
@@ -31,8 +31,16 @@ abstract contract ERC7984Freezable is ERC7984 {
|
|
|
31
31
|
return _frozenBalances[account];
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
/// @dev Returns the confidential available (unfrozen) balance of an account.
|
|
34
|
+
/// @dev Returns the confidential available (unfrozen) balance of an account. Gives ACL allowance to `account`.
|
|
35
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) {
|
|
36
44
|
(ebool success, euint64 unfrozen) = FHESafeMath.tryDecrease(
|
|
37
45
|
confidentialBalanceOf(account),
|
|
38
46
|
confidentialFrozen(account)
|
|
@@ -54,11 +62,12 @@ abstract contract ERC7984Freezable is ERC7984 {
|
|
|
54
62
|
* The `from` account must have sufficient unfrozen balance,
|
|
55
63
|
* otherwise 0 tokens are transferred.
|
|
56
64
|
* The default freezing behavior can be changed (for a pass-through for instance) by overriding
|
|
57
|
-
* {
|
|
65
|
+
* {_confidentialAvailable}. The internal function is used for actual gating (not the public function)
|
|
66
|
+
* to avoid unnecessarily granting ACL allowances.
|
|
58
67
|
*/
|
|
59
68
|
function _update(address from, address to, euint64 encryptedAmount) internal virtual override returns (euint64) {
|
|
60
69
|
if (from != address(0)) {
|
|
61
|
-
euint64 unfrozen =
|
|
70
|
+
euint64 unfrozen = _confidentialAvailable(from);
|
|
62
71
|
encryptedAmount = FHE.select(FHE.le(encryptedAmount, unfrozen), encryptedAmount, FHE.asEuint64(0));
|
|
63
72
|
}
|
|
64
73
|
return super._update(from, to, encryptedAmount);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
// OpenZeppelin Confidential Contracts (last updated v0.3.0
|
|
2
|
+
// OpenZeppelin Confidential Contracts (last updated v0.3.0) (token/ERC7984/extensions/ERC7984ObserverAccess.sol)
|
|
3
3
|
|
|
4
4
|
pragma solidity ^0.8.27;
|
|
5
5
|
|
|
@@ -11,7 +11,7 @@ import {ERC7984} from "../ERC7984.sol";
|
|
|
11
11
|
* permanent ACL access to its transfer and balance amounts. A observer can be added or removed at any point in time.
|
|
12
12
|
*/
|
|
13
13
|
abstract contract ERC7984ObserverAccess is ERC7984 {
|
|
14
|
-
mapping(address => address) private _observers;
|
|
14
|
+
mapping(address account => address) private _observers;
|
|
15
15
|
|
|
16
16
|
/// @dev Emitted when the observer is changed for the given account `account`.
|
|
17
17
|
event ERC7984ObserverAccessObserverSet(address account, address oldObserver, address newObserver);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
// OpenZeppelin Confidential Contracts (last updated v0.3.0
|
|
2
|
+
// OpenZeppelin Confidential Contracts (last updated v0.3.0) (token/ERC7984/extensions/ERC7984Omnibus.sol)
|
|
3
3
|
|
|
4
4
|
pragma solidity ^0.8.27;
|
|
5
5
|
|
|
@@ -189,8 +189,6 @@ abstract contract ERC7984Omnibus is ERC7984 {
|
|
|
189
189
|
euint64 amount,
|
|
190
190
|
bytes calldata data
|
|
191
191
|
) internal virtual returns (euint64) {
|
|
192
|
-
euint64 transferred = confidentialTransferFromAndCall(omnibusFrom, omnibusTo, amount, data);
|
|
193
|
-
|
|
194
192
|
FHE.allowThis(sender);
|
|
195
193
|
FHE.allow(sender, omnibusFrom);
|
|
196
194
|
FHE.allow(sender, omnibusTo);
|
|
@@ -199,6 +197,8 @@ abstract contract ERC7984Omnibus is ERC7984 {
|
|
|
199
197
|
FHE.allow(recipient, omnibusFrom);
|
|
200
198
|
FHE.allow(recipient, omnibusTo);
|
|
201
199
|
|
|
200
|
+
euint64 transferred = confidentialTransferFromAndCall(omnibusFrom, omnibusTo, amount, data);
|
|
201
|
+
|
|
202
202
|
FHE.allowThis(transferred);
|
|
203
203
|
FHE.allow(transferred, omnibusFrom);
|
|
204
204
|
FHE.allow(transferred, omnibusTo);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
// OpenZeppelin Confidential Contracts (last updated v0.3.0
|
|
2
|
+
// OpenZeppelin Confidential Contracts (last updated v0.3.0) (token/ERC7984/extensions/ERC7984Restricted.sol)
|
|
3
3
|
|
|
4
4
|
pragma solidity ^0.8.27;
|
|
5
5
|
|
|
@@ -38,14 +38,6 @@ abstract contract ERC7984Restricted is ERC7984 {
|
|
|
38
38
|
* @dev Returns whether a user account is allowed to interact with the token.
|
|
39
39
|
*
|
|
40
40
|
* Default implementation only disallows explicitly BLOCKED accounts (i.e. a blocklist).
|
|
41
|
-
*
|
|
42
|
-
* To convert into an allowlist, override as:
|
|
43
|
-
*
|
|
44
|
-
* ```solidity
|
|
45
|
-
* function isUserAllowed(address account) public view virtual override returns (bool) {
|
|
46
|
-
* return getRestriction(account) == Restriction.ALLOWED;
|
|
47
|
-
* }
|
|
48
|
-
* ```
|
|
49
41
|
*/
|
|
50
42
|
function isUserAllowed(address account) public view virtual returns (bool) {
|
|
51
43
|
return getRestriction(account) != Restriction.BLOCKED; // i.e. DEFAULT && ALLOWED
|
|
@@ -59,7 +51,7 @@ abstract contract ERC7984Restricted is ERC7984 {
|
|
|
59
51
|
* * `from` must be allowed to transfer tokens (see {isUserAllowed}).
|
|
60
52
|
* * `to` must be allowed to receive tokens (see {isUserAllowed}).
|
|
61
53
|
*
|
|
62
|
-
* The default restriction
|
|
54
|
+
* The default restriction behavior can be changed (for a pass-through for instance) by overriding
|
|
63
55
|
* {_checkSenderRestriction} and/or {_checkRecipientRestriction}.
|
|
64
56
|
*/
|
|
65
57
|
function _update(address from, address to, euint64 value) internal virtual override returns (euint64) {
|
|
@@ -91,7 +83,7 @@ abstract contract ERC7984Restricted is ERC7984 {
|
|
|
91
83
|
_setRestriction(account, Restriction.DEFAULT);
|
|
92
84
|
}
|
|
93
85
|
|
|
94
|
-
/// @dev Checks if a user account is restricted. Reverts with {
|
|
86
|
+
/// @dev Checks if a user account is restricted. Reverts with {UserRestricted} if so.
|
|
95
87
|
function _checkRestriction(address account) internal view virtual {
|
|
96
88
|
require(isUserAllowed(account), UserRestricted(account));
|
|
97
89
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
// OpenZeppelin Confidential Contracts (last updated v0.3.0
|
|
2
|
+
// OpenZeppelin Confidential Contracts (last updated v0.3.0) (token/ERC7984/extensions/ERC7984Rwa.sol)
|
|
3
3
|
|
|
4
4
|
pragma solidity ^0.8.27;
|
|
5
5
|
|
|
@@ -11,7 +11,6 @@ import {Multicall} from "@openzeppelin/contracts/utils/Multicall.sol";
|
|
|
11
11
|
import {Pausable} from "@openzeppelin/contracts/utils/Pausable.sol";
|
|
12
12
|
import {IERC7984} from "./../../../interfaces/IERC7984.sol";
|
|
13
13
|
import {IERC7984Rwa} from "./../../../interfaces/IERC7984Rwa.sol";
|
|
14
|
-
import {FHESafeMath} from "./../../../utils/FHESafeMath.sol";
|
|
15
14
|
import {ERC7984} from "./../ERC7984.sol";
|
|
16
15
|
import {ERC7984Freezable} from "./ERC7984Freezable.sol";
|
|
17
16
|
import {ERC7984Restricted} from "./ERC7984Restricted.sol";
|
|
@@ -20,15 +19,17 @@ import {ERC7984Restricted} from "./ERC7984Restricted.sol";
|
|
|
20
19
|
* @dev Extension of {ERC7984} that supports confidential Real World Assets (RWAs).
|
|
21
20
|
* This interface provides compliance checks, transfer controls and enforcement actions.
|
|
22
21
|
*/
|
|
23
|
-
abstract contract ERC7984Rwa is
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
22
|
+
abstract contract ERC7984Rwa is IERC7984Rwa, ERC7984Freezable, ERC7984Restricted, Pausable, Multicall, AccessControl {
|
|
23
|
+
/**
|
|
24
|
+
* @dev Accounts granted the agent role have the following permissioned abilities:
|
|
25
|
+
*
|
|
26
|
+
* - Mint/Burn to/from a given address (does not require permission)
|
|
27
|
+
* - Force transfer from a given address (does not require permission)
|
|
28
|
+
* - Bypasses pause and restriction checks (not frozen)
|
|
29
|
+
* - Pause/Unpause the contract
|
|
30
|
+
* - Block/Unblock a given account
|
|
31
|
+
* - Set frozen amount of tokens for a given account.
|
|
32
|
+
*/
|
|
32
33
|
bytes32 public constant AGENT_ROLE = keccak256("AGENT_ROLE");
|
|
33
34
|
|
|
34
35
|
/// @dev Checks if the sender is an admin.
|
|
@@ -50,11 +51,8 @@ abstract contract ERC7984Rwa is
|
|
|
50
51
|
/// @inheritdoc ERC165
|
|
51
52
|
function supportsInterface(
|
|
52
53
|
bytes4 interfaceId
|
|
53
|
-
) public view virtual override(IERC165,
|
|
54
|
-
return
|
|
55
|
-
interfaceId == type(IERC7984Rwa).interfaceId ||
|
|
56
|
-
interfaceId == type(IERC7984).interfaceId ||
|
|
57
|
-
super.supportsInterface(interfaceId);
|
|
54
|
+
) public view virtual override(IERC165, ERC7984, AccessControl) returns (bool) {
|
|
55
|
+
return interfaceId == type(IERC7984Rwa).interfaceId || super.supportsInterface(interfaceId);
|
|
58
56
|
}
|
|
59
57
|
|
|
60
58
|
/// @dev Returns true if has admin role, false otherwise.
|
|
@@ -94,10 +92,10 @@ abstract contract ERC7984Rwa is
|
|
|
94
92
|
|
|
95
93
|
/// @dev Unblocks a user account.
|
|
96
94
|
function unblockUser(address account) public virtual onlyAgent {
|
|
97
|
-
|
|
95
|
+
_resetUser(account);
|
|
98
96
|
}
|
|
99
97
|
|
|
100
|
-
/// @dev Sets confidential frozen for an account.
|
|
98
|
+
/// @dev Sets confidential frozen for an account with proof.
|
|
101
99
|
function setConfidentialFrozen(
|
|
102
100
|
address account,
|
|
103
101
|
externalEuint64 encryptedAmount,
|
|
@@ -106,7 +104,7 @@ abstract contract ERC7984Rwa is
|
|
|
106
104
|
_setConfidentialFrozen(account, FHE.fromExternal(encryptedAmount, inputProof));
|
|
107
105
|
}
|
|
108
106
|
|
|
109
|
-
/// @dev Sets confidential frozen for an account
|
|
107
|
+
/// @dev Sets confidential frozen for an account.
|
|
110
108
|
function setConfidentialFrozen(address account, euint64 encryptedAmount) public virtual onlyAgent {
|
|
111
109
|
require(
|
|
112
110
|
FHE.isAllowed(encryptedAmount, msg.sender),
|
|
@@ -121,7 +119,9 @@ abstract contract ERC7984Rwa is
|
|
|
121
119
|
externalEuint64 encryptedAmount,
|
|
122
120
|
bytes calldata inputProof
|
|
123
121
|
) public virtual onlyAgent returns (euint64) {
|
|
124
|
-
|
|
122
|
+
euint64 mintedAmount = _mint(to, FHE.fromExternal(encryptedAmount, inputProof));
|
|
123
|
+
FHE.allow(mintedAmount, msg.sender);
|
|
124
|
+
return mintedAmount;
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
/// @dev Mints confidential amount of tokens to account.
|
|
@@ -130,7 +130,9 @@ abstract contract ERC7984Rwa is
|
|
|
130
130
|
FHE.isAllowed(encryptedAmount, msg.sender),
|
|
131
131
|
ERC7984UnauthorizedUseOfEncryptedAmount(encryptedAmount, msg.sender)
|
|
132
132
|
);
|
|
133
|
-
|
|
133
|
+
euint64 mintedAmount = _mint(to, encryptedAmount);
|
|
134
|
+
FHE.allow(mintedAmount, msg.sender);
|
|
135
|
+
return mintedAmount;
|
|
134
136
|
}
|
|
135
137
|
|
|
136
138
|
/// @dev Burns confidential amount of tokens from account with proof.
|
|
@@ -139,7 +141,9 @@ abstract contract ERC7984Rwa is
|
|
|
139
141
|
externalEuint64 encryptedAmount,
|
|
140
142
|
bytes calldata inputProof
|
|
141
143
|
) public virtual onlyAgent returns (euint64) {
|
|
142
|
-
|
|
144
|
+
euint64 burntAmount = _burn(account, FHE.fromExternal(encryptedAmount, inputProof));
|
|
145
|
+
FHE.allow(burntAmount, msg.sender);
|
|
146
|
+
return burntAmount;
|
|
143
147
|
}
|
|
144
148
|
|
|
145
149
|
/// @dev Burns confidential amount of tokens from account.
|
|
@@ -148,10 +152,12 @@ abstract contract ERC7984Rwa is
|
|
|
148
152
|
FHE.isAllowed(encryptedAmount, msg.sender),
|
|
149
153
|
ERC7984UnauthorizedUseOfEncryptedAmount(encryptedAmount, msg.sender)
|
|
150
154
|
);
|
|
151
|
-
|
|
155
|
+
euint64 burntAmount = _burn(account, encryptedAmount);
|
|
156
|
+
FHE.allow(burntAmount, msg.sender);
|
|
157
|
+
return burntAmount;
|
|
152
158
|
}
|
|
153
159
|
|
|
154
|
-
/// @dev
|
|
160
|
+
/// @dev Variant of {forceConfidentialTransferFrom-address-address-euint64} with an input proof.
|
|
155
161
|
function forceConfidentialTransferFrom(
|
|
156
162
|
address from,
|
|
157
163
|
address to,
|
|
@@ -161,7 +167,11 @@ abstract contract ERC7984Rwa is
|
|
|
161
167
|
return _forceUpdate(from, to, FHE.fromExternal(encryptedAmount, inputProof));
|
|
162
168
|
}
|
|
163
169
|
|
|
164
|
-
|
|
170
|
+
/**
|
|
171
|
+
* @dev Force transfer callable by the role {AGENT_ROLE} which transfers tokens from `from` to `to` and
|
|
172
|
+
* bypasses the {ERC7984Restricted} (only on from) and https://docs.openzeppelin.com/contracts/api/utils#pausable[`++Pausable++`]
|
|
173
|
+
* checks. Frozen tokens are not transferred and must be unfrozen first.
|
|
174
|
+
*/
|
|
165
175
|
function forceConfidentialTransferFrom(
|
|
166
176
|
address from,
|
|
167
177
|
address to,
|
|
@@ -214,7 +224,9 @@ abstract contract ERC7984Rwa is
|
|
|
214
224
|
function _forceUpdate(address from, address to, euint64 encryptedAmount) internal virtual returns (euint64) {
|
|
215
225
|
// bypassing `from` restriction check with {_checkSenderRestriction}. Still performing `to` restriction check.
|
|
216
226
|
// bypassing paused state by directly calling `super._update`
|
|
217
|
-
|
|
227
|
+
euint64 transferred = super._update(from, to, encryptedAmount);
|
|
228
|
+
FHE.allow(transferred, msg.sender);
|
|
229
|
+
return transferred;
|
|
218
230
|
}
|
|
219
231
|
|
|
220
232
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
// OpenZeppelin Confidential Contracts (last updated v0.3.0
|
|
2
|
+
// OpenZeppelin Confidential Contracts (last updated v0.3.0) (token/ERC7984/extensions/ERC7984Votes.sol)
|
|
3
3
|
pragma solidity ^0.8.27;
|
|
4
4
|
|
|
5
5
|
import {euint64} from "@fhevm/solidity/lib/FHE.sol";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
// OpenZeppelin Confidential Contracts (last updated v0.3.0
|
|
2
|
+
// OpenZeppelin Confidential Contracts (last updated v0.3.0) (token/ERC7984/utils/ERC7984Utils.sol)
|
|
3
3
|
pragma solidity ^0.8.27;
|
|
4
4
|
|
|
5
5
|
import {FHE, ebool, euint64} from "@fhevm/solidity/lib/FHE.sol";
|
|
@@ -16,7 +16,8 @@ library ERC7984Utils {
|
|
|
16
16
|
* The transfer callback is not invoked on the recipient if the recipient has no code (i.e. is an EOA). If the
|
|
17
17
|
* recipient has non-zero code, it must implement
|
|
18
18
|
* {IERC7984Receiver-onConfidentialTransferReceived} and return an `ebool` indicating
|
|
19
|
-
* whether the transfer was accepted or not. If the `ebool` is `false`, the transfer
|
|
19
|
+
* whether the transfer was accepted or not. If the `ebool` is `false`, the transfer function
|
|
20
|
+
* should try to refund the `from` address.
|
|
20
21
|
*/
|
|
21
22
|
function checkOnTransferReceived(
|
|
22
23
|
address operator,
|
package/utils/FHESafeMath.sol
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
// OpenZeppelin Confidential Contracts (last updated v0.3.0
|
|
2
|
+
// OpenZeppelin Confidential Contracts (last updated v0.3.0) (utils/FHESafeMath.sol)
|
|
3
3
|
pragma solidity ^0.8.24;
|
|
4
4
|
|
|
5
5
|
import {FHE, ebool, euint64} from "@fhevm/solidity/lib/FHE.sol";
|
|
@@ -7,6 +7,9 @@ import {FHE, ebool, euint64} from "@fhevm/solidity/lib/FHE.sol";
|
|
|
7
7
|
/**
|
|
8
8
|
* @dev Library providing safe arithmetic operations for encrypted values
|
|
9
9
|
* to handle potential overflows in FHE operations.
|
|
10
|
+
*
|
|
11
|
+
* NOTE: An uninitialized `euint64` value (equivalent to euint64.wrap(bytes32(0))) is evaluated as 0.
|
|
12
|
+
* This library will may return an uninitialized value if all inputs are uninitialized.
|
|
10
13
|
*/
|
|
11
14
|
library FHESafeMath {
|
|
12
15
|
/**
|
|
@@ -33,7 +36,7 @@ library FHESafeMath {
|
|
|
33
36
|
if (!FHE.isInitialized(delta)) {
|
|
34
37
|
return (FHE.asEbool(true), oldValue);
|
|
35
38
|
}
|
|
36
|
-
return (FHE.eq(
|
|
39
|
+
return (FHE.eq(delta, 0), FHE.asEuint64(0));
|
|
37
40
|
}
|
|
38
41
|
success = FHE.ge(oldValue, delta);
|
|
39
42
|
updated = FHE.select(success, FHE.sub(oldValue, delta), oldValue);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
// OpenZeppelin Confidential Contracts (last updated v0.3.0
|
|
2
|
+
// OpenZeppelin Confidential Contracts (last updated v0.3.0) (utils/structs/temporary-Checkpoints.sol)
|
|
3
3
|
// OpenZeppelin Contracts (last updated v5.4.0) (utils/structs/Checkpoints.sol)
|
|
4
4
|
// This file was procedurally generated from scripts/generate/templates/Checkpoints.js.
|
|
5
5
|
// WARNING: This file is temporary and will be deleted once the latest version of the file is released in v5.5.0 of @openzeppelin/contracts.
|