forked from ethereum/ERCs
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add EIP-5267: Retrieval of EIP-712 domain (#5267)
* Add EIP-?: Retrieval of EIP-712 domain * grammar * add links for first occurence of eips * add reference solidity implementation * remove ERC20 text * use inline links * add note about opaqueness of hash * clarify reference implementations * typo * add rationale for extensions * add note about extensions in motivation section * clarify method to obtain extension field values * use type name consistently * grammar * refer to applications instead of user agents * use relative links * clarify extensions * remove mathjax * assign eip number * add discussion link * improve reference implementation section * add note about changing domain * use more descriptive name for ref implementation * Update EIPS/eip-5267.md Co-authored-by: Pandapip1 <[email protected]> Co-authored-by: Pandapip1 <[email protected]>
- Loading branch information
Showing
1 changed file
with
134 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
--- | ||
eip: 5267 | ||
title: Retrieval of EIP-712 domain | ||
description: A way to describe and retrieve an EIP-712 domain to securely integrate EIP-712 signatures. | ||
author: Francisco Giordano (@frangio) | ||
discussions-to: https://ethereum-magicians.org/t/eip-5267-retrieval-of-eip-712-domain/9951 | ||
status: Draft | ||
type: Standards Track | ||
category: ERC | ||
created: 2022-07-14 | ||
requires: 155, 712, 2612 | ||
--- | ||
|
||
## Abstract | ||
|
||
This EIP complements [EIP-712](./eip-712.md) by standardizing how contracts should publish the fields and values that describe their domain. This enables applications to retrieve this description and generate appropriate domain separators in a general way, and thus integrate EIP-712 signatures securely and scalably. | ||
|
||
## Motivation | ||
|
||
EIP-712 is a signature scheme for complex structured messages. In order to avoid replay attacks and mitigate phishing, the scheme includes a "domain separator" that makes the resulting signature unique to a specific domain (e.g., a specific contract) and allows user-agents to inform end users the details of what is being signed and how it may be used. A domain is defined by a data structure with fields from a predefined set, all of which are optional, or from extensions. Notably, EIP-712 does not specify any way for contracts to publish which of these fields they use or with what values. This has likely limited adoption of EIP-712, as it is not possible to develop general integrations, and instead applications find that they need to build custom support for each EIP-712 domain. A prime example of this is [EIP-2612](./eip-2612.md) (permit), which has not been widely adopted by applications even though it is understood to be a valuable improvement to the user experience. The present EIP defines an interface that can be used by applications to retrieve a definition of the domain that a contract uses to verify EIP-712 signatures. | ||
|
||
## Specification | ||
|
||
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119. | ||
|
||
Compliant contracts MUST define `eip712Domain` exactly as declared below. All specified values MUST be returned even if they are not used, to ensure proper decoding on the client side. | ||
|
||
```solidity | ||
function eip712Domain() external view returns ( | ||
bytes1 fields, | ||
string name, | ||
string version, | ||
uint256 chainId, | ||
address verifyingContract, | ||
bytes32 salt, | ||
uint256[] extensions | ||
); | ||
``` | ||
|
||
The return values of this function MUST describe the domain separator that is used for verification of EIP-712 signatures in the contract. They describe both the form of the `EIP712Domain` struct (i.e., which of the optional fields and extensions are present) and the value of each field, as follows. | ||
|
||
- `fields`: A bit map where bit `i` is set to 1 if and only if field `i` is present (`0 ≤ i ≤ 4`). Bits are read from least significant to most significant, and fields are indexed in the order that is specified by EIP-712, identical to the order in which they are listed in the function type. | ||
- `name`, `version`, `chainId`, `verifyingContract`, `salt`: The value of the corresponding field in `EIP712Domain`, if present according to `fields`. If the field is not present, the value is unspecified. The semantics of each field is defined in EIP-712. | ||
- `extensions`: A list of EIP numbers that specify additional fields in the domain. The method to obtain the value for each of these additional fields and any conditions for inclusion are expected to be specified in the respective EIP. The value of `fields` does not affect their inclusion. | ||
|
||
The return values of this function (equivalently, its EIP-712 domain) MAY change throughout the lifetime of a contract, but changes SHOULD NOT be frequent. The `chainId` field, if used, SHOULD change to mirror the [EIP-155](./eip-155.md) id of the underlying chain. | ||
|
||
## Rationale | ||
|
||
A notable application of EIP-712 signatures is found in EIP-2612 (permit), which specifies a `DOMAIN_SEPARATOR` function that returns a `bytes32` value (the actual domain separator, i.e., the result of `hashStruct(eip712Domain)`). This value does not suffice for the purposes of integrating with EIP-712, as the RPC methods defined there receive an object describing the domain and not just the separator in hash form. Note that this is not a flaw of the RPC methods, it is indeed part of the security proposition that the domain should be validated and informed to the user as part of the signing process. On its own, a hash does not allow this to be implemented, given it is opaque. The present EIP fills this gap in both EIP-712 and EIP-2612. | ||
|
||
Extensions are described by their EIP numbers because EIP-712 states: "Future extensions to this standard can add new fields [...] new fields should be proposed through the EIP process." | ||
|
||
## Backwards Compatibility | ||
|
||
This is an optional extension to EIP-712 that does not introduce backwards compatibility issues. | ||
|
||
Upgradeable contracts that make use of EIP-712 signatures MAY be upgraded to implement this EIP. | ||
|
||
User-agents or applications that implement this EIP SHOULD additionally support those contracts that due to their immutability cannot be upgraded to implement this EIP, by hardcoding their domain based on contract address and chain id. | ||
|
||
## Reference Implementation | ||
|
||
### Solidity Example | ||
|
||
```solidity | ||
pragma solidity 0.8.0; | ||
contract EIP712VerifyingContract { | ||
function eip712Domain() external view returns ( | ||
bytes1 fields, | ||
string memory name, | ||
string memory version, | ||
uint256 chainId, | ||
address verifyingContract, | ||
bytes32 salt, | ||
uint256[] memory extensions | ||
) { | ||
return ( | ||
hex"0d", // 01101 | ||
"Example", | ||
"", | ||
block.chainid, | ||
address(this), | ||
bytes32(0), | ||
new uint256[](0) | ||
); | ||
} | ||
} | ||
``` | ||
|
||
This contract's domain only uses the fields `name`, `chainId`, and `verifyingContract`, therefore the `fields` value is `01101`, or `0d` in hexadecimal. | ||
|
||
Assuming this contract is on Ethereum mainnet and its address is 0x0000000000000000000000000000000000000001, the domain it describes is: | ||
|
||
```json5 | ||
{ | ||
name: "Example", | ||
chainId: 1, | ||
verifyingContract: "0x0000000000000000000000000000000000000001" | ||
} | ||
``` | ||
|
||
### JavaScript | ||
|
||
A domain object can be constructed based on the return values of an `eip712Domain()` invocation. | ||
|
||
```javascript | ||
const fieldNames = ['name', 'version', 'chainId', 'verifyingContract', 'salt']; | ||
|
||
/** Builds a domain object based on the values obtained by calling `eip712Domain()` in a contract. */ | ||
function buildDomain(fields, name, version, chainId, verifyingContract, salt, extensions) { | ||
if (extensions.length > 0) { | ||
throw Error("extensions not implemented"); | ||
} | ||
|
||
const domain = { name, version, chainId, verifyingContract, salt }; | ||
|
||
for (const [i, field] of fieldNames.entries()) { | ||
if (!(fields & (1 << i))) { | ||
delete domain[field]; | ||
} | ||
} | ||
|
||
return domain; | ||
} | ||
``` | ||
|
||
## Security Considerations | ||
|
||
While this EIP allows a contract to specify a `verifyingContract` other than itself, as well as a `chainId` other than that of the current chain, user-agents and applications should in general validate that these do match the contract and chain before requesting any user signatures for the domain. This may not always be a valid assumption. | ||
|
||
## Copyright | ||
Copyright and related rights waived via [CC0](../LICENSE.md). |