EIP712.sol 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. // SPDX-License-Identifier: MIT
  2. // OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/EIP712.sol)
  3. pragma solidity ^0.8.8;
  4. import "./ECDSA.sol";
  5. import "../ShortStrings.sol";
  6. import "../../interfaces/IERC5267.sol";
  7. /**
  8. * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
  9. *
  10. * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
  11. * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
  12. * they need in their contracts using a combination of `abi.encode` and `keccak256`.
  13. *
  14. * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
  15. * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
  16. * ({_hashTypedDataV4}).
  17. *
  18. * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
  19. * the chain id to protect against replay attacks on an eventual fork of the chain.
  20. *
  21. * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
  22. * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
  23. *
  24. * NOTE: In the upgradeable version of this contract, the cached values will correspond to the address, and the domain
  25. * separator of the implementation contract. This will cause the `_domainSeparatorV4` function to always rebuild the
  26. * separator from the immutable values, which is cheaper than accessing a cached version in cold storage.
  27. *
  28. * _Available since v3.4._
  29. *
  30. * @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment
  31. */
  32. abstract contract EIP712 is IERC5267 {
  33. using ShortStrings for *;
  34. bytes32 private constant _TYPE_HASH =
  35. keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
  36. // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
  37. // invalidate the cached domain separator if the chain id changes.
  38. bytes32 private immutable _cachedDomainSeparator;
  39. uint256 private immutable _cachedChainId;
  40. address private immutable _cachedThis;
  41. bytes32 private immutable _hashedName;
  42. bytes32 private immutable _hashedVersion;
  43. ShortString private immutable _name;
  44. ShortString private immutable _version;
  45. string private _nameFallback;
  46. string private _versionFallback;
  47. /**
  48. * @dev Initializes the domain separator and parameter caches.
  49. *
  50. * The meaning of `name` and `version` is specified in
  51. * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
  52. *
  53. * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
  54. * - `version`: the current major version of the signing domain.
  55. *
  56. * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
  57. * contract upgrade].
  58. */
  59. constructor(string memory name, string memory version) {
  60. _name = name.toShortStringWithFallback(_nameFallback);
  61. _version = version.toShortStringWithFallback(_versionFallback);
  62. _hashedName = keccak256(bytes(name));
  63. _hashedVersion = keccak256(bytes(version));
  64. _cachedChainId = block.chainid;
  65. _cachedDomainSeparator = _buildDomainSeparator();
  66. _cachedThis = address(this);
  67. }
  68. /**
  69. * @dev Returns the domain separator for the current chain.
  70. */
  71. function _domainSeparatorV4() internal view returns (bytes32) {
  72. if (address(this) == _cachedThis && block.chainid == _cachedChainId) {
  73. return _cachedDomainSeparator;
  74. } else {
  75. return _buildDomainSeparator();
  76. }
  77. }
  78. function _buildDomainSeparator() private view returns (bytes32) {
  79. return keccak256(abi.encode(_TYPE_HASH, _hashedName, _hashedVersion, block.chainid, address(this)));
  80. }
  81. /**
  82. * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
  83. * function returns the hash of the fully encoded EIP712 message for this domain.
  84. *
  85. * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
  86. *
  87. * ```solidity
  88. * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
  89. * keccak256("Mail(address to,string contents)"),
  90. * mailTo,
  91. * keccak256(bytes(mailContents))
  92. * )));
  93. * address signer = ECDSA.recover(digest, signature);
  94. * ```
  95. */
  96. function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
  97. return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);
  98. }
  99. /**
  100. * @dev See {EIP-5267}.
  101. *
  102. * _Available since v4.9._
  103. */
  104. function eip712Domain()
  105. public
  106. view
  107. virtual
  108. override
  109. returns (
  110. bytes1 fields,
  111. string memory name,
  112. string memory version,
  113. uint256 chainId,
  114. address verifyingContract,
  115. bytes32 salt,
  116. uint256[] memory extensions
  117. )
  118. {
  119. return (
  120. hex"0f", // 01111
  121. _name.toStringWithFallback(_nameFallback),
  122. _version.toStringWithFallback(_versionFallback),
  123. block.chainid,
  124. address(this),
  125. bytes32(0),
  126. new uint256[](0)
  127. );
  128. }
  129. }