upgradeable.patch 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  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 38197f3a..bc934d1c 100644
  63. --- a/README.md
  64. +++ b/README.md
  65. @@ -19,6 +19,9 @@
  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. @@ -26,7 +29,7 @@
  73. #### Hardhat, Truffle (npm)
  74. ```
  75. -$ npm install @openzeppelin/contracts
  76. +$ npm install @openzeppelin/contracts-upgradeable
  77. ```
  78. 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.
  79. @@ -38,10 +41,10 @@ OpenZeppelin Contracts features a [stable API](https://docs.openzeppelin.com/con
  80. > **Warning** Foundry installs the latest version initially, but subsequent `forge update` commands will use the `master` branch.
  81. ```
  82. -$ forge install OpenZeppelin/openzeppelin-contracts
  83. +$ forge install OpenZeppelin/openzeppelin-contracts-upgradeable
  84. ```
  85. -Add `@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/` in `remappings.txt.`
  86. +Add `@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/` in `remappings.txt.`
  87. ### Usage
  88. @@ -50,10 +53,11 @@ Once installed, you can use the contracts in the library by importing them:
  89. ```solidity
  90. pragma solidity ^0.8.20;
  91. -import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
  92. +import {ERC721Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";
  93. -contract MyCollectible is ERC721 {
  94. - constructor() ERC721("MyCollectible", "MCO") {
  95. +contract MyCollectible is ERC721Upgradeable {
  96. + function initialize() initializer public {
  97. + __ERC721_init("MyCollectible", "MCO");
  98. }
  99. }
  100. ```
  101. diff --git a/contracts/package.json b/contracts/package.json
  102. index df141192..1cf90ad1 100644
  103. --- a/contracts/package.json
  104. +++ b/contracts/package.json
  105. @@ -1,5 +1,5 @@
  106. {
  107. - "name": "@openzeppelin/contracts",
  108. + "name": "@openzeppelin/contracts-upgradeable",
  109. "description": "Secure Smart Contract library for Solidity",
  110. "version": "4.9.2",
  111. "files": [
  112. @@ -13,7 +13,7 @@
  113. },
  114. "repository": {
  115. "type": "git",
  116. - "url": "https://github.com/OpenZeppelin/openzeppelin-contracts.git"
  117. + "url": "https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable.git"
  118. },
  119. "keywords": [
  120. "solidity",
  121. diff --git a/contracts/utils/cryptography/EIP712.sol b/contracts/utils/cryptography/EIP712.sol
  122. index 3800804a..90c1db78 100644
  123. --- a/contracts/utils/cryptography/EIP712.sol
  124. +++ b/contracts/utils/cryptography/EIP712.sol
  125. @@ -4,7 +4,6 @@
  126. pragma solidity ^0.8.20;
  127. import {MessageHashUtils} from "./MessageHashUtils.sol";
  128. -import {ShortStrings, ShortString} from "../ShortStrings.sol";
  129. import {IERC5267} from "../../interfaces/IERC5267.sol";
  130. /**
  131. @@ -28,28 +27,18 @@ import {IERC5267} from "../../interfaces/IERC5267.sol";
  132. * NOTE: In the upgradeable version of this contract, the cached values will correspond to the address, and the domain
  133. * separator of the implementation contract. This will cause the {_domainSeparatorV4} function to always rebuild the
  134. * separator from the immutable values, which is cheaper than accessing a cached version in cold storage.
  135. - *
  136. - * @custom:oz-upgrades-unsafe-allow state-variable-immutable
  137. */
  138. abstract contract EIP712 is IERC5267 {
  139. - using ShortStrings for *;
  140. -
  141. bytes32 private constant TYPE_HASH =
  142. keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
  143. - // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
  144. - // invalidate the cached domain separator if the chain id changes.
  145. - bytes32 private immutable _cachedDomainSeparator;
  146. - uint256 private immutable _cachedChainId;
  147. - address private immutable _cachedThis;
  148. -
  149. + /// @custom:oz-renamed-from _HASHED_NAME
  150. bytes32 private immutable _hashedName;
  151. + /// @custom:oz-renamed-from _HASHED_VERSION
  152. bytes32 private immutable _hashedVersion;
  153. - ShortString private immutable _name;
  154. - ShortString private immutable _version;
  155. - string private _nameFallback;
  156. - string private _versionFallback;
  157. + string private _name;
  158. + string private _version;
  159. /**
  160. * @dev Initializes the domain separator and parameter caches.
  161. @@ -64,29 +53,23 @@ abstract contract EIP712 is IERC5267 {
  162. * contract upgrade].
  163. */
  164. constructor(string memory name, string memory version) {
  165. - _name = name.toShortStringWithFallback(_nameFallback);
  166. - _version = version.toShortStringWithFallback(_versionFallback);
  167. - _hashedName = keccak256(bytes(name));
  168. - _hashedVersion = keccak256(bytes(version));
  169. -
  170. - _cachedChainId = block.chainid;
  171. - _cachedDomainSeparator = _buildDomainSeparator();
  172. - _cachedThis = address(this);
  173. + _name = name;
  174. + _version = version;
  175. +
  176. + // Reset prior values in storage if upgrading
  177. + _hashedName = 0;
  178. + _hashedVersion = 0;
  179. }
  180. /**
  181. * @dev Returns the domain separator for the current chain.
  182. */
  183. function _domainSeparatorV4() internal view returns (bytes32) {
  184. - if (address(this) == _cachedThis && block.chainid == _cachedChainId) {
  185. - return _cachedDomainSeparator;
  186. - } else {
  187. - return _buildDomainSeparator();
  188. - }
  189. + return _buildDomainSeparator();
  190. }
  191. function _buildDomainSeparator() private view returns (bytes32) {
  192. - return keccak256(abi.encode(TYPE_HASH, _hashedName, _hashedVersion, block.chainid, address(this)));
  193. + return keccak256(abi.encode(TYPE_HASH, _EIP712NameHash(), _EIP712VersionHash(), block.chainid, address(this)));
  194. }
  195. /**
  196. @@ -125,6 +108,10 @@ abstract contract EIP712 is IERC5267 {
  197. uint256[] memory extensions
  198. )
  199. {
  200. + // If the hashed name and version in storage are non-zero, the contract hasn't been properly initialized
  201. + // and the EIP712 domain is not reliable, as it will be missing name and version.
  202. + require(_hashedName == 0 && _hashedVersion == 0, "EIP712: Uninitialized");
  203. +
  204. return (
  205. hex"0f", // 01111
  206. _EIP712Name(),
  207. @@ -139,22 +126,62 @@ abstract contract EIP712 is IERC5267 {
  208. /**
  209. * @dev The name parameter for the EIP712 domain.
  210. *
  211. - * NOTE: By default this function reads _name which is an immutable value.
  212. - * It only reads from storage if necessary (in case the value is too large to fit in a ShortString).
  213. + * NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
  214. + * are a concern.
  215. */
  216. - // solhint-disable-next-line func-name-mixedcase
  217. - function _EIP712Name() internal view returns (string memory) {
  218. - return _name.toStringWithFallback(_nameFallback);
  219. + function _EIP712Name() internal view virtual returns (string memory) {
  220. + return _name;
  221. }
  222. /**
  223. * @dev The version parameter for the EIP712 domain.
  224. *
  225. - * NOTE: By default this function reads _version which is an immutable value.
  226. - * It only reads from storage if necessary (in case the value is too large to fit in a ShortString).
  227. + * NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
  228. + * are a concern.
  229. */
  230. - // solhint-disable-next-line func-name-mixedcase
  231. - function _EIP712Version() internal view returns (string memory) {
  232. - return _version.toStringWithFallback(_versionFallback);
  233. + function _EIP712Version() internal view virtual returns (string memory) {
  234. + return _version;
  235. + }
  236. +
  237. + /**
  238. + * @dev The hash of the name parameter for the EIP712 domain.
  239. + *
  240. + * NOTE: In previous versions this function was virtual. In this version you should override `_EIP712Name` instead.
  241. + */
  242. + function _EIP712NameHash() internal view returns (bytes32) {
  243. + string memory name = _EIP712Name();
  244. + if (bytes(name).length > 0) {
  245. + return keccak256(bytes(name));
  246. + } else {
  247. + // If the name is empty, the contract may have been upgraded without initializing the new storage.
  248. + // We return the name hash in storage if non-zero, otherwise we assume the name is empty by design.
  249. + bytes32 hashedName = _hashedName;
  250. + if (hashedName != 0) {
  251. + return hashedName;
  252. + } else {
  253. + return keccak256("");
  254. + }
  255. + }
  256. + }
  257. +
  258. + /**
  259. + * @dev The hash of the version parameter for the EIP712 domain.
  260. + *
  261. + * NOTE: In previous versions this function was virtual. In this version you should override `_EIP712Version` instead.
  262. + */
  263. + function _EIP712VersionHash() internal view returns (bytes32) {
  264. + string memory version = _EIP712Version();
  265. + if (bytes(version).length > 0) {
  266. + return keccak256(bytes(version));
  267. + } else {
  268. + // If the version is empty, the contract may have been upgraded without initializing the new storage.
  269. + // We return the version hash in storage if non-zero, otherwise we assume the version is empty by design.
  270. + bytes32 hashedVersion = _hashedVersion;
  271. + if (hashedVersion != 0) {
  272. + return hashedVersion;
  273. + } else {
  274. + return keccak256("");
  275. + }
  276. + }
  277. }
  278. }
  279. diff --git a/package.json b/package.json
  280. index ffa868ac..e254d962 100644
  281. --- a/package.json
  282. +++ b/package.json
  283. @@ -33,7 +33,7 @@
  284. },
  285. "repository": {
  286. "type": "git",
  287. - "url": "https://github.com/OpenZeppelin/openzeppelin-contracts.git"
  288. + "url": "https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable.git"
  289. },
  290. "keywords": [
  291. "solidity",
  292. diff --git a/test/utils/cryptography/EIP712.test.js b/test/utils/cryptography/EIP712.test.js
  293. index 7ea535b7..32e3a370 100644
  294. --- a/test/utils/cryptography/EIP712.test.js
  295. +++ b/test/utils/cryptography/EIP712.test.js
  296. @@ -47,26 +47,6 @@ contract('EIP712', function (accounts) {
  297. const rebuildDomain = await getDomain(this.eip712);
  298. expect(mapValues(rebuildDomain, String)).to.be.deep.equal(mapValues(this.domain, String));
  299. });
  300. -
  301. - if (shortOrLong === 'short') {
  302. - // Long strings are in storage, and the proxy will not be properly initialized unless
  303. - // the upgradeable contract variant is used and the initializer is invoked.
  304. -
  305. - it('adjusts when behind proxy', async function () {
  306. - const factory = await Clones.new();
  307. - const cloneReceipt = await factory.$clone(this.eip712.address);
  308. - const cloneAddress = cloneReceipt.logs.find(({ event }) => event === 'return$clone').args.instance;
  309. - const clone = new EIP712Verifier(cloneAddress);
  310. -
  311. - const cloneDomain = { ...this.domain, verifyingContract: clone.address };
  312. -
  313. - const reportedDomain = await getDomain(clone);
  314. - expect(mapValues(reportedDomain, String)).to.be.deep.equal(mapValues(cloneDomain, String));
  315. -
  316. - const expectedSeparator = await domainSeparator(cloneDomain);
  317. - expect(await clone.$_domainSeparatorV4()).to.equal(expectedSeparator);
  318. - });
  319. - }
  320. });
  321. it('hash digest', async function () {