Add EIP 712 helpers (#2418)
This commit is contained in:
committed by
GitHub
parent
061e7f0da7
commit
5748034cd3
105
contracts/drafts/EIP712.sol
Normal file
105
contracts/drafts/EIP712.sol
Normal file
@ -0,0 +1,105 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity >=0.6.0 <0.8.0;
|
||||
|
||||
/**
|
||||
* @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].
|
||||
*/
|
||||
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;
|
||||
|
||||
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) internal {
|
||||
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 = _getChainId();
|
||||
_CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion);
|
||||
_TYPE_HASH = typeHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the domain separator for the current chain.
|
||||
*/
|
||||
function _domainSeparatorV4() internal view returns (bytes32) {
|
||||
if (_getChainId() == _CACHED_CHAIN_ID) {
|
||||
return _CACHED_DOMAIN_SEPARATOR;
|
||||
} else {
|
||||
return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION);
|
||||
}
|
||||
}
|
||||
|
||||
function _buildDomainSeparator(bytes32 typeHash, bytes32 name, bytes32 version) private view returns (bytes32) {
|
||||
return keccak256(
|
||||
abi.encode(
|
||||
typeHash,
|
||||
name,
|
||||
version,
|
||||
_getChainId(),
|
||||
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 returns (bytes32) {
|
||||
return keccak256(abi.encodePacked("\x19\x01", _domainSeparatorV4(), structHash));
|
||||
}
|
||||
|
||||
function _getChainId() private pure returns (uint256 chainId) {
|
||||
// solhint-disable-next-line no-inline-assembly
|
||||
assembly {
|
||||
chainId := chainid()
|
||||
}
|
||||
}
|
||||
}
|
||||
9
contracts/drafts/README.adoc
Normal file
9
contracts/drafts/README.adoc
Normal file
@ -0,0 +1,9 @@
|
||||
= Draft EIPS
|
||||
|
||||
This directory contains implementations of EIPs that are still in Draft status.
|
||||
|
||||
Due to their nature as drafts, the details of these contracts may change and we cannot guarantee their xref:ROOT:releases-stability.adoc[stability]. Minor releases of OpenZeppelin Contracts may contain breaking changes for the contracts in this directory, which will be duly announced in the https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/CHANGELOG.md[changelog]. The EIPs included here are used by projects in production and this may make them less likely to change significantly.
|
||||
|
||||
== Cryptography
|
||||
|
||||
{{EIP712}}
|
||||
Reference in New Issue
Block a user