EIP712.sol 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. // SPDX-License-Identifier: MIT
  2. // OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/EIP712.sol)
  3. pragma solidity ^0.8.0;
  4. import "./ECDSA.sol";
  5. /**
  6. * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
  7. *
  8. * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
  9. * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
  10. * they need in their contracts using a combination of `abi.encode` and `keccak256`.
  11. *
  12. * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
  13. * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
  14. * ({_hashTypedDataV4}).
  15. *
  16. * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
  17. * the chain id to protect against replay attacks on an eventual fork of the chain.
  18. *
  19. * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
  20. * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
  21. *
  22. * _Available since v3.4._
  23. */
  24. abstract contract EIP712 {
  25. /* solhint-disable var-name-mixedcase */
  26. // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
  27. // invalidate the cached domain separator if the chain id changes.
  28. bytes32 private immutable _CACHED_DOMAIN_SEPARATOR;
  29. uint256 private immutable _CACHED_CHAIN_ID;
  30. address private immutable _CACHED_THIS;
  31. bytes32 private immutable _HASHED_NAME;
  32. bytes32 private immutable _HASHED_VERSION;
  33. bytes32 private immutable _TYPE_HASH;
  34. /* solhint-enable var-name-mixedcase */
  35. /**
  36. * @dev Initializes the domain separator and parameter caches.
  37. *
  38. * The meaning of `name` and `version` is specified in
  39. * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
  40. *
  41. * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
  42. * - `version`: the current major version of the signing domain.
  43. *
  44. * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
  45. * contract upgrade].
  46. */
  47. constructor(string memory name, string memory version) {
  48. bytes32 hashedName = keccak256(bytes(name));
  49. bytes32 hashedVersion = keccak256(bytes(version));
  50. bytes32 typeHash = keccak256(
  51. "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
  52. );
  53. _HASHED_NAME = hashedName;
  54. _HASHED_VERSION = hashedVersion;
  55. _CACHED_CHAIN_ID = block.chainid;
  56. _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion);
  57. _CACHED_THIS = address(this);
  58. _TYPE_HASH = typeHash;
  59. }
  60. /**
  61. * @dev Returns the domain separator for the current chain.
  62. */
  63. function _domainSeparatorV4() internal view returns (bytes32) {
  64. if (address(this) == _CACHED_THIS && block.chainid == _CACHED_CHAIN_ID) {
  65. return _CACHED_DOMAIN_SEPARATOR;
  66. } else {
  67. return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION);
  68. }
  69. }
  70. function _buildDomainSeparator(
  71. bytes32 typeHash,
  72. bytes32 nameHash,
  73. bytes32 versionHash
  74. ) private view returns (bytes32) {
  75. return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this)));
  76. }
  77. /**
  78. * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
  79. * function returns the hash of the fully encoded EIP712 message for this domain.
  80. *
  81. * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
  82. *
  83. * ```solidity
  84. * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
  85. * keccak256("Mail(address to,string contents)"),
  86. * mailTo,
  87. * keccak256(bytes(mailContents))
  88. * )));
  89. * address signer = ECDSA.recover(digest, signature);
  90. * ```
  91. */
  92. function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
  93. return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);
  94. }
  95. }