From b393406a94ef69608d8d883a950f0cb76bfca611 Mon Sep 17 00:00:00 2001 From: ronhuafeng Date: Fri, 9 Sep 2022 17:09:17 +0100 Subject: [PATCH] Remove the draft prefix for EIP712 as it is now final (#3621) Co-authored-by: Francisco --- CHANGELOG.md | 10 ++ contracts/governance/Governor.sol | 2 +- contracts/governance/utils/Votes.sol | 2 +- contracts/metatx/MinimalForwarder.sol | 2 +- contracts/mocks/EIP712External.sol | 2 +- .../ERC20/extensions/draft-ERC20Permit.sol | 2 +- contracts/utils/cryptography/EIP712.sol | 103 ++++++++++++++++++ contracts/utils/cryptography/draft-EIP712.sol | 101 +---------------- 8 files changed, 120 insertions(+), 104 deletions(-) create mode 100644 contracts/utils/cryptography/EIP712.sol diff --git a/CHANGELOG.md b/CHANGELOG.md index 04be8cb32ce..3a2e3a3d8f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,16 @@ * `SafeCast`: optimize downcasting of signed integers. ([#3565](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3565)) * `VestingWallet`: remove unused library `Math.sol`. ([#3605](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3605)) * `ECDSA`: Remove redundant check on the `v` value. ([#3591](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3591)) + * `EIP712`: `utils/cryptography/EIP712.sol` + +### Deprecations + + * `EIP712`: Added the file `EIP712.sol` and deprecated `draft-EIP712.sol` since the EIP is no longer a Draft. Developers are encouraged to update their imports. ([#3621](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3621)) + +```diff +-import "@openzeppelin/contracts/utils/cryptography/draft-EIP712.sol"; ++import "@openzeppelin/contracts/utils/cryptography/EIP712.sol"; +``` ### Compatibility Note diff --git a/contracts/governance/Governor.sol b/contracts/governance/Governor.sol index ae34135911c..5083165b17f 100644 --- a/contracts/governance/Governor.sol +++ b/contracts/governance/Governor.sol @@ -6,7 +6,7 @@ pragma solidity ^0.8.0; import "../token/ERC721/IERC721Receiver.sol"; import "../token/ERC1155/IERC1155Receiver.sol"; import "../utils/cryptography/ECDSA.sol"; -import "../utils/cryptography/draft-EIP712.sol"; +import "../utils/cryptography/EIP712.sol"; import "../utils/introspection/ERC165.sol"; import "../utils/math/SafeCast.sol"; import "../utils/structs/DoubleEndedQueue.sol"; diff --git a/contracts/governance/utils/Votes.sol b/contracts/governance/utils/Votes.sol index 52d2c0e1928..b0cdbb42fec 100644 --- a/contracts/governance/utils/Votes.sol +++ b/contracts/governance/utils/Votes.sol @@ -5,7 +5,7 @@ pragma solidity ^0.8.0; import "../../utils/Context.sol"; import "../../utils/Counters.sol"; import "../../utils/Checkpoints.sol"; -import "../../utils/cryptography/draft-EIP712.sol"; +import "../../utils/cryptography/EIP712.sol"; import "./IVotes.sol"; /** diff --git a/contracts/metatx/MinimalForwarder.sol b/contracts/metatx/MinimalForwarder.sol index 30b7c91f816..54474860ef8 100644 --- a/contracts/metatx/MinimalForwarder.sol +++ b/contracts/metatx/MinimalForwarder.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.0; import "../utils/cryptography/ECDSA.sol"; -import "../utils/cryptography/draft-EIP712.sol"; +import "../utils/cryptography/EIP712.sol"; /** * @dev Simple minimal forwarder to be used together with an ERC2771 compatible contract. See {ERC2771Context}. diff --git a/contracts/mocks/EIP712External.sol b/contracts/mocks/EIP712External.sol index 6f244690047..93f77d54946 100644 --- a/contracts/mocks/EIP712External.sol +++ b/contracts/mocks/EIP712External.sol @@ -2,8 +2,8 @@ pragma solidity ^0.8.0; -import "../utils/cryptography/draft-EIP712.sol"; import "../utils/cryptography/ECDSA.sol"; +import "../utils/cryptography/EIP712.sol"; contract EIP712External is EIP712 { constructor(string memory name, string memory version) EIP712(name, version) {} diff --git a/contracts/token/ERC20/extensions/draft-ERC20Permit.sol b/contracts/token/ERC20/extensions/draft-ERC20Permit.sol index 63aeb527378..637b666fc0e 100644 --- a/contracts/token/ERC20/extensions/draft-ERC20Permit.sol +++ b/contracts/token/ERC20/extensions/draft-ERC20Permit.sol @@ -5,8 +5,8 @@ pragma solidity ^0.8.0; import "./draft-IERC20Permit.sol"; import "../ERC20.sol"; -import "../../../utils/cryptography/draft-EIP712.sol"; import "../../../utils/cryptography/ECDSA.sol"; +import "../../../utils/cryptography/EIP712.sol"; import "../../../utils/Counters.sol"; /** diff --git a/contracts/utils/cryptography/EIP712.sol b/contracts/utils/cryptography/EIP712.sol new file mode 100644 index 00000000000..a372b8de888 --- /dev/null +++ b/contracts/utils/cryptography/EIP712.sol @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "./ECDSA.sol"; + +/** + * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data. + * + * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible, + * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding + * they need in their contracts using a combination of `abi.encode` and `keccak256`. + * + * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding + * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA + * ({_hashTypedDataV4}). + * + * The implementation of the domain separator was designed to be as efficient as possible while still properly updating + * the chain id to protect against replay attacks on an eventual fork of the chain. + * + * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method + * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask]. + * + * _Available since v3.4._ + */ +abstract contract EIP712 { + /* solhint-disable var-name-mixedcase */ + // 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 _CACHED_DOMAIN_SEPARATOR; + uint256 private immutable _CACHED_CHAIN_ID; + address private immutable _CACHED_THIS; + + bytes32 private immutable _HASHED_NAME; + bytes32 private immutable _HASHED_VERSION; + bytes32 private immutable _TYPE_HASH; + + /* solhint-enable var-name-mixedcase */ + + /** + * @dev Initializes the domain separator and parameter caches. + * + * The meaning of `name` and `version` is specified in + * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]: + * + * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol. + * - `version`: the current major version of the signing domain. + * + * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart + * contract upgrade]. + */ + constructor(string memory name, string memory version) { + bytes32 hashedName = keccak256(bytes(name)); + bytes32 hashedVersion = keccak256(bytes(version)); + bytes32 typeHash = keccak256( + "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" + ); + _HASHED_NAME = hashedName; + _HASHED_VERSION = hashedVersion; + _CACHED_CHAIN_ID = block.chainid; + _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion); + _CACHED_THIS = address(this); + _TYPE_HASH = typeHash; + } + + /** + * @dev Returns the domain separator for the current chain. + */ + function _domainSeparatorV4() internal view returns (bytes32) { + if (address(this) == _CACHED_THIS && block.chainid == _CACHED_CHAIN_ID) { + return _CACHED_DOMAIN_SEPARATOR; + } else { + return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION); + } + } + + function _buildDomainSeparator( + bytes32 typeHash, + bytes32 nameHash, + bytes32 versionHash + ) private view returns (bytes32) { + return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this))); + } + + /** + * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this + * function returns the hash of the fully encoded EIP712 message for this domain. + * + * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example: + * + * ```solidity + * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode( + * keccak256("Mail(address to,string contents)"), + * mailTo, + * keccak256(bytes(mailContents)) + * ))); + * address signer = ECDSA.recover(digest, signature); + * ``` + */ + function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) { + return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash); + } +} diff --git a/contracts/utils/cryptography/draft-EIP712.sol b/contracts/utils/cryptography/draft-EIP712.sol index a32c25b7f7a..dbe2a92ec91 100644 --- a/contracts/utils/cryptography/draft-EIP712.sol +++ b/contracts/utils/cryptography/draft-EIP712.sol @@ -1,104 +1,7 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/cryptography/draft-EIP712.sol) pragma solidity ^0.8.0; -import "./ECDSA.sol"; +// EIP-712 is Final as of 2022-08-11. This file is deprecated. -/** - * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data. - * - * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible, - * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding - * they need in their contracts using a combination of `abi.encode` and `keccak256`. - * - * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding - * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA - * ({_hashTypedDataV4}). - * - * The implementation of the domain separator was designed to be as efficient as possible while still properly updating - * the chain id to protect against replay attacks on an eventual fork of the chain. - * - * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method - * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask]. - * - * _Available since v3.4._ - */ -abstract contract EIP712 { - /* solhint-disable var-name-mixedcase */ - // 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 _CACHED_DOMAIN_SEPARATOR; - uint256 private immutable _CACHED_CHAIN_ID; - address private immutable _CACHED_THIS; - - bytes32 private immutable _HASHED_NAME; - bytes32 private immutable _HASHED_VERSION; - bytes32 private immutable _TYPE_HASH; - - /* solhint-enable var-name-mixedcase */ - - /** - * @dev Initializes the domain separator and parameter caches. - * - * The meaning of `name` and `version` is specified in - * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]: - * - * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol. - * - `version`: the current major version of the signing domain. - * - * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart - * contract upgrade]. - */ - constructor(string memory name, string memory version) { - bytes32 hashedName = keccak256(bytes(name)); - bytes32 hashedVersion = keccak256(bytes(version)); - bytes32 typeHash = keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" - ); - _HASHED_NAME = hashedName; - _HASHED_VERSION = hashedVersion; - _CACHED_CHAIN_ID = block.chainid; - _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion); - _CACHED_THIS = address(this); - _TYPE_HASH = typeHash; - } - - /** - * @dev Returns the domain separator for the current chain. - */ - function _domainSeparatorV4() internal view returns (bytes32) { - if (address(this) == _CACHED_THIS && block.chainid == _CACHED_CHAIN_ID) { - return _CACHED_DOMAIN_SEPARATOR; - } else { - return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION); - } - } - - function _buildDomainSeparator( - bytes32 typeHash, - bytes32 nameHash, - bytes32 versionHash - ) private view returns (bytes32) { - return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this))); - } - - /** - * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this - * function returns the hash of the fully encoded EIP712 message for this domain. - * - * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example: - * - * ```solidity - * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode( - * keccak256("Mail(address to,string contents)"), - * mailTo, - * keccak256(bytes(mailContents)) - * ))); - * address signer = ECDSA.recover(digest, signature); - * ``` - */ - function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) { - return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash); - } -} +import "./EIP712.sol";