Getting straight to the point here, check out json file number one (formatted like a legitimate EIP-712 hash):
{
"types": {
"EIP712Domain": [
{
"name": "name",
"type": "string"
},
{
"name": "version",
"type": "string"
},
{
"name": "chainId",
"type": "uint256"
},
{
"name": "verifyingContract",
"type": "address"
}
],
"AuthRequest": [
{
"name": "prompt",
"type": "string"
},
{
"name": "createdAt",
"type": "uint256"
}
]
},
"primaryType": "AuthRequest",
"domain": {
"name": "Example App",
"version": "1",
"chainId": 1,
"verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
},
"message": {
"prompt": "Welcome! In order to authenticate to this website, sign this request and your public address will be sent to the server in a verifiable way.",
"createdAt": 1718570375196
}
}Here is the second JSON address (different from the first):
{
"domain": {
"chainId": 1,
"name": "Example App",
"verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC",
"version": "1"
},
"message": {
"prompt": "Welcome! In order to authenticate to this website, sign this request and your public address will be sent to the server in a verifiable way.",
"createdAt": 1718570375196
},
"primaryType": "AuthRequest",
"types": {
"EIP712Domain": [
{
"name": "name",
"type": "string"
},
{
"name": "version",
"type": "string"
},
{
"name": "chainId",
"type": "uint256"
},
{
"name": "verifyingContract",
"type": "address"
}
],
"AuthRequest": [
{
"name": "prompt",
"type": "string"
},
{
"name": "createdAt",
"type": "uint256"
}
]
}
}If we create two files containing both of these json excerpts in each one (repsectively), then go to sign said files (using the EIP-712 signature standard), we'll get the same sigrnature and that signature will validate against both sets of EIP-712 data.
Once you arrive at Remix IDE’s site, make sure you head to the deploy and run transactions tab and set your environment to Remix VM(Cancun) as shown below:
Once we’ve done that, we’re going to go to the files panel (top left) and then create two files called eip712-compliant-version1.json and eip712-compliant-version2.json, respectively. Once that’s complete, all we have to do is right click on each file and then select the sign Typed Data option (for EIP-712 compatible transaction data).
Doing so, should produce a result that’s easily readable in the console of the application.
As shown above, the signature produced for both files is the exact same (if we’re using the EIP-712 encoding method).
Here's why we got the same signature for both JSON files:
-
EIP-712 Standardization: The EIP-712 standard specifies a precise way to encode and hash structured data. This process involves:
- Defining Types: The
typesobject (e.g.,EIP712Domain,AuthRequest) defines the name and type of each field, and importantly, the order of these fields within their respective type definitions. - Encoding Data: When the data is encoded for hashing:
- The
domaindata is encoded according to the field order specified intypes.EIP712Domain. - The
messagedata (forAuthRequest) is encoded according to the field order specified intypes.AuthRequest.
- The
- Hashing: These encoded parts (domain separator and struct hash) are then combined and hashed.
- Defining Types: The
-
Content Equivalence:
typesObject: The definitions forEIP712DomainandAuthRequest(including the names, types, and order of their members) are identical in bothjsonexcerpts.EIP712Domain: [ { "name": "name", "type": "string" }, { "name": "version", "type": "string" }, ... ]AuthRequest: [ { "name": "prompt", "type": "string" }, { "name": "createdAt", "type": "uint256" } ]
primaryType: Is"AuthRequest"in both filesdomainObject Values: While the order of keys (chainId,name, etc.) within thedomainobject itself differs between your two JSON files, the values associated with these keys are identical:name: "Example App"version: "1"chainId: 1verifyingContract: "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" When the EIP-712 hashing algorithm constructs thedomainSeparator, it will fetch these values based on the field names defined intypes.EIP712Domainand encode them in the order specified there (name, then version, then chainId, then verifyingContract). Since the values are the same, the resulting encodeddomaindata is the same.
messageObject Values: ThepromptandcreatedAtvalues are identical in both files. The hashing will follow the order defined intypes.AuthRequest.
-
Hashing Process is Order-Independent of JSON Key Order (for objects): The EIP-712 signing logic (e.g., in Remix, ethers.js, MetaMask) parses the JSON. When it needs to encode the
domainobject, for example, it looks at thetypes.EIP712Domaindefinition. It sees it needsnamefirst, so it looks updomain.namefrom the JSON. Then it needsversion, so it looks updomain.version, and so on. The order of keys in thedomainJSON object itself doesn't change which value is fetched forname,version, etc. The same applies to the top-level keys (domain,message,primaryType,types). The signing utility knows what each of these top-level keys means and processes them according to the EIP-712 specification, regardless of their order in the JSON file.
In summary:
- The type definitions in
typesare identical (including field order). - The
primaryTypeis identical. - The actual data values within
domainandmessageare identical.
The EIP-712 encoding and hashing algorithm will produce the exact same byte string to be signed. When this identical byte string is signed with the same private key (corresponding with 0x5B38... in this case), the resulting cryptographic signature will also be identical.


