2

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?

1 Answer 1

3
+50

To resolve the error, remove the EIP712Domain from types object (keep only PermitSingle and PermitDetails 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 type EIP712Domain was unused and it was causing the error.

Here is a working piece of code:

let result = _TypedDataEncoder.hash(
  {
    name: "Permit2",
    chainId: "1",
    verifyingContract: "0x14a5698aeab8aa3f472c6d0a0061730f45467bd9",
  },
  {
    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" },
    ],
  },
  {
    details: {
      token: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
      amount: "1461501637330902918203684832716283019655932542975",
      expiration: "1690127209",
      nonce: "0",
    },
    spender: "0x49c4f688ade58ad80de1ddd932db3b804fef5cef",
    sigDeadline: "1687537009",
  }
);
console.log(result); 
// 0x3c636bc8d0efd7d4a25e31a001ea1a4743cd000a1148facaf1f60c4c5b327d40
1
  • Thanks, delete types.EIP712Domain works, I'll award the bounty closer to the end, in case there are other answers.
    – MShakeG
    Commented Jun 30, 2023 at 11:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.