solid-grinder 1.1.0

A CLI that goes along with building blocks of smart contract. Along with our front-end snippets, this toolbox can reduce L2 gas cost by encoding calldata for dApps development to use as little bytes of calldata as possible.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
deleted file mode 100644
index 2797a088..00000000
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ /dev/null
@@ -1,21 +0,0 @@
----
-name: Bug report
-about: Report a bug in OpenZeppelin Contracts
-
----
-
-<!-- Briefly describe the issue you're experiencing. Tell us what you were trying to do and what happened instead. -->
-
-<!-- Remember, this is not a place to ask for help debugging code. For that, we welcome you in the OpenZeppelin Community Forum: https://forum.openzeppelin.com/. -->
-
-**💻 Environment**
-
-<!-- Tell us what version of OpenZeppelin Contracts you're using, and how you're using it: Truffle, Remix, etc. -->
-
-**📝 Details**
-
-<!-- Describe the problem you have been experiencing in more detail. Include as much information as you think is relevant. Keep in mind that transactions can fail for many reasons; context is key here. -->
-
-**🔢 Code to reproduce bug**
-
-<!-- We will be able to better help if you provide a minimal example that triggers the bug. -->
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index 4018cef2..d343a53d 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -1,4 +1,8 @@
+blank_issues_enabled: false
 contact_links:
+  - name: Bug Reports & Feature Requests
+    url: https://github.com/OpenZeppelin/openzeppelin-contracts/issues/new/choose
+    about: Visit the OpenZeppelin Contracts repository
   - name: Questions & Support Requests
     url: https://forum.openzeppelin.com/c/support/contracts/18
     about: Ask in the OpenZeppelin Forum
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
deleted file mode 100644
index ff596b0c..00000000
--- a/.github/ISSUE_TEMPLATE/feature_request.md
+++ /dev/null
@@ -1,14 +0,0 @@
----
-name: Feature request
-about: Suggest an idea for OpenZeppelin Contracts
-
----
-
-**🧐 Motivation**
-<!-- Is your feature request related to a specific problem? Is it just a crazy idea? Tell us about it! -->
-
-**📝 Details**
-<!-- Please describe your feature request in detail. -->
-
-<!-- Make sure that you have reviewed the OpenZeppelin Contracts Contributor Guidelines. -->
-<!-- https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/CONTRIBUTING.md -->
diff --git a/README.md b/README.md
index 9fc95518..53130e3c 100644
--- a/README.md
+++ b/README.md
@@ -16,17 +16,20 @@
 
 :building_construction: **Want to scale your decentralized application?** Check out [OpenZeppelin Defender](https://openzeppelin.com/defender) — a secure platform for automating and monitoring your operations.
 
+> **Note**
+> You are looking at the upgradeable variant of OpenZeppelin Contracts. Be sure to review the documentation on [Using OpenZeppelin Contracts with Upgrades](https://docs.openzeppelin.com/contracts/4.x/upgradeable).
+
 ## Overview
 
 ### Installation
 
 ```
-$ npm install @openzeppelin/contracts
+$ npm install @openzeppelin/contracts-upgradeable
 ```
 
 OpenZeppelin Contracts features a [stable API](https://docs.openzeppelin.com/contracts/releases-stability#api-stability), which means that your contracts won't break unexpectedly when upgrading to a newer minor version.
 
-An alternative to npm is to use the GitHub repository (`openzeppelin/openzeppelin-contracts`) to retrieve the contracts. When doing this, make sure to specify the tag for a release such as `v4.5.0`, instead of using the `master` branch.
+An alternative to npm is to use the GitHub repository (`openzeppelin/openzeppelin-contracts-upgradeable`) to retrieve the contracts. When doing this, make sure to specify the tag for a release such as `v4.5.0`, instead of using the `master` branch.
 
 ### Usage
 
@@ -35,10 +38,11 @@ Once installed, you can use the contracts in the library by importing them:
 ```solidity
 pragma solidity ^0.8.0;
 
-import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
+import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";
 
-contract MyCollectible is ERC721 {
-    constructor() ERC721("MyCollectible", "MCO") {
+contract MyCollectible is ERC721Upgradeable {
+    function initialize() initializer public {
+        __ERC721_init("MyCollectible", "MCO");
     }
 }
 ```
diff --git a/contracts/finance/VestingWallet.sol b/contracts/finance/VestingWallet.sol
index fe67eb54..d26ea4e1 100644
--- a/contracts/finance/VestingWallet.sol
+++ b/contracts/finance/VestingWallet.sol
@@ -15,6 +15,8 @@ import "../utils/Context.sol";
  * Any token transferred to this contract will follow the vesting schedule as if they were locked from the beginning.
  * Consequently, if the vesting has already started, any amount of tokens sent to this contract will (at least partly)
  * be immediately releasable.
+ *
+ * @custom:storage-size 52
  */
 contract VestingWallet is Context {
     event EtherReleased(uint256 amount);
diff --git a/contracts/governance/TimelockControllerWith46Migration.sol b/contracts/governance/TimelockControllerWith46Migration.sol
new file mode 100644
index 00000000..3315e7bd
--- /dev/null
+++ b/contracts/governance/TimelockControllerWith46Migration.sol
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: MIT
+// OpenZeppelin Contracts v4.6.0 (governance/TimelockControllerWith46Migration.sol)
+
+pragma solidity ^0.8.0;
+
+import "./TimelockController.sol";
+
+/**
+ * @dev Extension of the TimelockController that includes an additional
+ * function to migrate from OpenZeppelin Upgradeable Contracts <4.6 to >=4.6.
+ *
+ * This migration is necessary to setup administration rights over the new
+ * `CANCELLER_ROLE`.
+ *
+ * The migration is trustless and can be performed by anyone.
+ *
+ * _Available since v4.6._
+ */
+contract TimelockControllerWith46Migration is TimelockController {
+    constructor(
+        uint256 minDelay,
+        address[] memory proposers,
+        address[] memory executors,
+        address admin
+    ) TimelockController(minDelay, proposers, executors, admin) {}
+
+    /**
+     * @dev Migration function. Running it is necessary for upgradeable
+     * instances that were initially setup with code <4.6 and that upgraded
+     * to >=4.6.
+     */
+    function migrateTo46() public virtual {
+        require(
+            getRoleAdmin(PROPOSER_ROLE) == TIMELOCK_ADMIN_ROLE && getRoleAdmin(CANCELLER_ROLE) == DEFAULT_ADMIN_ROLE,
+            "TimelockController: already migrated"
+        );
+        _setRoleAdmin(CANCELLER_ROLE, TIMELOCK_ADMIN_ROLE);
+    }
+}
diff --git a/contracts/governance/extensions/GovernorVotes.sol b/contracts/governance/extensions/GovernorVotes.sol
index 64431711..885f0e42 100644
--- a/contracts/governance/extensions/GovernorVotes.sol
+++ b/contracts/governance/extensions/GovernorVotes.sol
@@ -10,6 +10,8 @@ import "../../interfaces/IERC5805.sol";
  * @dev Extension of {Governor} for voting weight extraction from an {ERC20Votes} token, or since v4.5 an {ERC721Votes} token.
  *
  * _Available since v4.3._
+ *
+ * @custom:storage-size 51
  */
 abstract contract GovernorVotes is Governor {
     IERC5805 public immutable token;
diff --git a/contracts/governance/extensions/GovernorVotesComp.sol b/contracts/governance/extensions/GovernorVotesComp.sol
index 17250ad7..1d26b72e 100644
--- a/contracts/governance/extensions/GovernorVotesComp.sol
+++ b/contracts/governance/extensions/GovernorVotesComp.sol
@@ -10,6 +10,8 @@ import "../../token/ERC20/extensions/ERC20VotesComp.sol";
  * @dev Extension of {Governor} for voting weight extraction from a Comp token.
  *
  * _Available since v4.3._
+ *
+ * @custom:storage-size 51
  */
 abstract contract GovernorVotesComp is Governor {
     ERC20VotesComp public immutable token;
diff --git a/contracts/package.json b/contracts/package.json
index 55e70b17..ceefb984 100644
--- a/contracts/package.json
+++ b/contracts/package.json
@@ -1,5 +1,5 @@
 {
-  "name": "@openzeppelin/contracts",
+  "name": "@openzeppelin/contracts-upgradeable",
   "description": "Secure Smart Contract library for Solidity",
   "version": "4.8.2",
   "files": [
@@ -13,7 +13,7 @@
   },
   "repository": {
     "type": "git",
-    "url": "https://github.com/OpenZeppelin/openzeppelin-contracts.git"
+    "url": "https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable.git"
   },
   "keywords": [
     "solidity",
diff --git a/contracts/security/PullPayment.sol b/contracts/security/PullPayment.sol
index 65b4980f..f336592e 100644
--- a/contracts/security/PullPayment.sol
+++ b/contracts/security/PullPayment.sol
@@ -22,6 +22,8 @@ import "../utils/escrow/Escrow.sol";
  * To use, derive from the `PullPayment` contract, and use {_asyncTransfer}
  * instead of Solidity's `transfer` function. Payees can query their due
  * payments with {payments}, and retrieve them with {withdrawPayments}.
+ *
+ * @custom:storage-size 51
  */
 abstract contract PullPayment {
     Escrow private immutable _escrow;
diff --git a/contracts/token/ERC20/extensions/ERC20Capped.sol b/contracts/token/ERC20/extensions/ERC20Capped.sol
index 16f830d1..9ef98148 100644
--- a/contracts/token/ERC20/extensions/ERC20Capped.sol
+++ b/contracts/token/ERC20/extensions/ERC20Capped.sol
@@ -7,6 +7,8 @@ import "../ERC20.sol";
 
 /**
  * @dev Extension of {ERC20} that adds a cap to the supply of tokens.
+ *
+ * @custom:storage-size 51
  */
 abstract contract ERC20Capped is ERC20 {
     uint256 private immutable _cap;
diff --git a/contracts/token/ERC20/extensions/ERC20Permit.sol b/contracts/token/ERC20/extensions/ERC20Permit.sol
index a357199b..9dc8e894 100644
--- a/contracts/token/ERC20/extensions/ERC20Permit.sol
+++ b/contracts/token/ERC20/extensions/ERC20Permit.sol
@@ -18,6 +18,8 @@ import "../../../utils/Counters.sol";
  * need to send a transaction, and thus is not required to hold Ether at all.
  *
  * _Available since v3.4._
+ *
+ * @custom:storage-size 51
  */
 abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 {
     using Counters for Counters.Counter;
diff --git a/contracts/token/ERC20/extensions/ERC20Wrapper.sol b/contracts/token/ERC20/extensions/ERC20Wrapper.sol
index bfe782e4..7264fe32 100644
--- a/contracts/token/ERC20/extensions/ERC20Wrapper.sol
+++ b/contracts/token/ERC20/extensions/ERC20Wrapper.sol
@@ -14,6 +14,8 @@ import "../utils/SafeERC20.sol";
  * wrapping of an existing "basic" ERC20 into a governance token.
  *
  * _Available since v4.2._
+ *
+ * @custom:storage-size 51
  */
 abstract contract ERC20Wrapper is ERC20 {
     IERC20 private immutable _underlying;
diff --git a/contracts/token/ERC20/utils/TokenTimelock.sol b/contracts/token/ERC20/utils/TokenTimelock.sol
index ed855b7b..3d30f59d 100644
--- a/contracts/token/ERC20/utils/TokenTimelock.sol
+++ b/contracts/token/ERC20/utils/TokenTimelock.sol
@@ -11,6 +11,8 @@ import "./SafeERC20.sol";
  *
  * Useful for simple vesting schedules like "advisors get all of their tokens
  * after 1 year".
+ *
+ * @custom:storage-size 53
  */
 contract TokenTimelock {
     using SafeERC20 for IERC20;
diff --git a/contracts/utils/cryptography/EIP712.sol b/contracts/utils/cryptography/EIP712.sol
index 6a4e1cad..55d8eced 100644
--- a/contracts/utils/cryptography/EIP712.sol
+++ b/contracts/utils/cryptography/EIP712.sol
@@ -4,7 +4,6 @@
 pragma solidity ^0.8.8;
 
 import "./ECDSA.sol";
-import "../ShortStrings.sol";
 import "../../interfaces/IERC5267.sol";
 
 /**
@@ -30,27 +29,19 @@ import "../../interfaces/IERC5267.sol";
  *
  * _Available since v3.4._
  *
- * @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment
+ * @custom:storage-size 52
  */
 abstract contract EIP712 is IERC5267 {
-    using ShortStrings for *;
-
     bytes32 private constant _TYPE_HASH =
         keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
 
-    // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
-    // invalidate the cached domain separator if the chain id changes.
-    bytes32 private immutable _cachedDomainSeparator;
-    uint256 private immutable _cachedChainId;
-    address private immutable _cachedThis;
-
+    /// @custom:oz-renamed-from _HASHED_NAME
     bytes32 private immutable _hashedName;
+    /// @custom:oz-renamed-from _HASHED_VERSION
     bytes32 private immutable _hashedVersion;
 
-    ShortString private immutable _name;
-    ShortString private immutable _version;
-    string private _nameFallback;
-    string private _versionFallback;
+    string private _name;
+    string private _version;
 
     /**
      * @dev Initializes the domain separator and parameter caches.
@@ -65,29 +56,23 @@ abstract contract EIP712 is IERC5267 {
      * contract upgrade].
      */
     constructor(string memory name, string memory version) {
-        _name = name.toShortStringWithFallback(_nameFallback);
-        _version = version.toShortStringWithFallback(_versionFallback);
-        _hashedName = keccak256(bytes(name));
-        _hashedVersion = keccak256(bytes(version));
-
-        _cachedChainId = block.chainid;
-        _cachedDomainSeparator = _buildDomainSeparator();
-        _cachedThis = address(this);
+        _name = name;
+        _version = version;
+
+        // Reset prior values in storage if upgrading
+        _hashedName = 0;
+        _hashedVersion = 0;
     }
 
     /**
      * @dev Returns the domain separator for the current chain.
      */
     function _domainSeparatorV4() internal view returns (bytes32) {
-        if (address(this) == _cachedThis && block.chainid == _cachedChainId) {
-            return _cachedDomainSeparator;
-        } else {
-            return _buildDomainSeparator();
-        }
+        return _buildDomainSeparator();
     }
 
     function _buildDomainSeparator() private view returns (bytes32) {
-        return keccak256(abi.encode(_TYPE_HASH, _hashedName, _hashedVersion, block.chainid, address(this)));
+        return keccak256(abi.encode(_TYPE_HASH, _EIP712NameHash(), _EIP712VersionHash(), block.chainid, address(this)));
     }
 
     /**
@@ -129,14 +114,80 @@ abstract contract EIP712 is IERC5267 {
             uint256[] memory extensions
         )
     {
+        // If the hashed name and version in storage are non-zero, the contract hasn't been properly initialized
+        // and the EIP712 domain is not reliable, as it will be missing name and version.
+        require(_hashedName == 0 && _hashedVersion == 0, "EIP712: Uninitialized");
+
         return (
             hex"0f", // 01111
-            _name.toStringWithFallback(_nameFallback),
-            _version.toStringWithFallback(_versionFallback),
+            _EIP712Name(),
+            _EIP712Version(),
             block.chainid,
             address(this),
             bytes32(0),
             new uint256[](0)
         );
     }
+
+    /**
+     * @dev The name parameter for the EIP712 domain.
+     *
+     * NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
+     * are a concern.
+     */
+    function _EIP712Name() internal virtual view returns (string memory) {
+        return _name;
+    }
+
+    /**
+     * @dev The version parameter for the EIP712 domain.
+     *
+     * NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
+     * are a concern.
+     */
+    function _EIP712Version() internal virtual view returns (string memory) {
+        return _version;
+    }
+
+    /**
+     * @dev The hash of the name parameter for the EIP712 domain.
+     *
+     * NOTE: In previous versions this function was virtual. In this version you should override `_EIP712Name` instead.
+     */
+    function _EIP712NameHash() internal view returns (bytes32) {
+        string memory name = _EIP712Name();
+        if (bytes(name).length > 0) {
+            return keccak256(bytes(name));
+        } else {
+            // If the name is empty, the contract may have been upgraded without initializing the new storage.
+            // We return the name hash in storage if non-zero, otherwise we assume the name is empty by design.
+            bytes32 hashedName = _hashedName;
+            if (hashedName != 0) {
+                return hashedName;
+            } else {
+                return keccak256("");
+            }
+        }
+    }
+
+    /**
+     * @dev The hash of the version parameter for the EIP712 domain.
+     *
+     * NOTE: In previous versions this function was virtual. In this version you should override `_EIP712Version` instead.
+     */
+    function _EIP712VersionHash() internal view returns (bytes32) {
+        string memory version = _EIP712Version();
+        if (bytes(version).length > 0) {
+            return keccak256(bytes(version));
+        } else {
+            // If the version is empty, the contract may have been upgraded without initializing the new storage.
+            // We return the version hash in storage if non-zero, otherwise we assume the version is empty by design.
+            bytes32 hashedVersion = _hashedVersion;
+            if (hashedVersion != 0) {
+                return hashedVersion;
+            } else {
+                return keccak256("");
+            }
+        }
+    }
 }
diff --git a/package.json b/package.json
index 8458dd61..b4672240 100644
--- a/package.json
+++ b/package.json
@@ -36,7 +36,7 @@
   },
   "repository": {
     "type": "git",
-    "url": "https://github.com/OpenZeppelin/openzeppelin-contracts.git"
+    "url": "https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable.git"
   },
   "keywords": [
     "solidity",
diff --git a/test/utils/cryptography/EIP712.test.js b/test/utils/cryptography/EIP712.test.js
index 54a4e772..ba4602ed 100644
--- a/test/utils/cryptography/EIP712.test.js
+++ b/test/utils/cryptography/EIP712.test.js
@@ -47,26 +47,6 @@ contract('EIP712', function (accounts) {
           const rebuildDomain = await getDomain(this.eip712);
           expect(mapValues(rebuildDomain, String)).to.be.deep.equal(mapValues(this.domain, String));
         });
-
-        if (shortOrLong === 'short') {
-          // Long strings are in storage, and the proxy will not be properly initialized unless
-          // the upgradeable contract variant is used and the initializer is invoked.
-
-          it('adjusts when behind proxy', async function () {
-            const factory = await Clones.new();
-            const cloneReceipt = await factory.$clone(this.eip712.address);
-            const cloneAddress = cloneReceipt.logs.find(({ event }) => event === 'return$clone').args.instance;
-            const clone = new EIP712Verifier(cloneAddress);
-
-            const cloneDomain = { ...this.domain, verifyingContract: clone.address };
-
-            const reportedDomain = await getDomain(clone);
-            expect(mapValues(reportedDomain, String)).to.be.deep.equal(mapValues(cloneDomain, String));
-
-            const expectedSeparator = await domainSeparator(cloneDomain);
-            expect(await clone.$_domainSeparatorV4()).to.equal(expectedSeparator);
-          });
-        }
       });
 
       it('hash digest', async function () {