I am trying to compute the hash to be signed for EIP-712 typed data using the ethers.js(v5.7.2) library in TypeScript. I'm using _TypedDataEncoder.hash()
to hash the typed data, but I am encountering the following error:
Error: ambiguous primary types or unused types: "PermitSingle", "EIP712Domain" (argument="types", value= {... }, code=INVALID_ARGUMENT, version=hash/5.7.0)
Here is the TypeScript code I am using:
import { arrayify, BytesLike, concat, hexlify } from '@ethersproject/bytes'
import { _TypedDataEncoder } from '@ethersproject/hash'
import { keccak256 } from '@ethersproject/keccak256'
import { TypedDataDomain, TypedDataField } from 'ethers'
function hashTypedDataV4(params: any) {
// Destructure the params
const [from, jsonData]: [string, string] = params;
const { types, domain, primaryType, message } = JSON.parse(jsonData);
// Hash the EIP-712 Domain
const domainSeparator = _TypedDataEncoder.hashDomain(domain);
// Hash the message (EIP-712 typed data)
const messageHash = structHash(domain, types, message);
// Compute the final hash to sign (according to EIP-712)
const hashToSign = arrayify(keccak256(concat(['0x1901', domainSeparator, messageHash])));
return hashToSign;
}
function structHash(domain: TypedDataDomain, types: Record<string, TypedDataField[]>, data: Record<string, any>) {
return _TypedDataEncoder.hash(domain, types, data); // ERROR OCCURS HERE!
}
The error seems to occur on the return _TypedDataEncoder.hash(domain, types, data) line in the structHash
method. From the error message, it seems like there is some ambiguity regarding the primary types, but I am not sure how to resolve it.
Below is an example of the params I am passing to hashTypedDataV4 (please note that the function expects the second element of params to be a JSON string, but I'm showing it as a JSON object for readability – you can use JSON.stringify() to convert it back to the expected format):
[
"0xfdb267dc5e340647aab9372fc5a8e406d4e71c8d",
{
"types": {
"PermitSingle": [
{ "name": "details", "type": "PermitDetails" },
{ "name": "spender", "type": "address" },
{ "name": "sigDeadline", "type": "uint256" }
],
"PermitDetails": [
{ "name": "token", "type": "address" },
{ "name": "amount", "type": "uint160" },
{ "name": "expiration", "type": "uint48" },
{ "name": "nonce", "type": "uint48" }
],
"EIP712Domain": [
{ "name": "name", "type": "string" },
{ "name": "chainId", "type": "uint256"
},
{ "name": "verifyingContract", "type": "address" }
]
},
"domain": { "name": "Permit2", "chainId": "1", "verifyingContract": "0x14a5698aeab8aa3f472c6d0a0061730f45467bd9"
},
"primaryType": "PermitSingle",
"message": {
"details": {
"token": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
"amount": "1461501637330902918203684832716283019655932542975",
"expiration": "1690127209",
"nonce": "0"
},
"spender": "0x49c4f688ade58ad80de1ddd932db3b804fef5cef",
"sigDeadline": "1687537009"
}
}
]
Could anyone please help me understand what might be causing this error and how to resolve it?
Best Answer
To resolve the error, remove the
EIP712Domain
fromtypes
object (keep onlyPermitSingle
andPermitDetails
there). The domain type is inbuilt in the EIP712 standard and hence TypedDataEncoder so you do not need to specify it in the types. Hence the declared typeEIP712Domain
was unused and it was causing the error.Here is a working piece of code: