Ethers.js – How to Fix ‘Ambiguous Primary Types or Unused Types’ Error with _TypedDataEncoder.hash

eip712ethers.jshashsignature

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 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
Related Topic