upgradeable.patch 19 KB

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