Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to sign typed data using Ethers.js and Magic Connect? #374

Closed
2 tasks done
0xFloop opened this issue Oct 17, 2022 · 12 comments
Closed
2 tasks done

How to sign typed data using Ethers.js and Magic Connect? #374

0xFloop opened this issue Oct 17, 2022 · 12 comments

Comments

@0xFloop
Copy link

0xFloop commented Oct 17, 2022

✅ Prerequisites

  • Did you perform a cursory search of open issues? Is this question already asked elsewhere?
  • Are you reporting to the correct repository (magic-sdk)?

❓ Question

Signing typed data using Magic Connect as wallet provider in ethers is throwing "TypeError: Cannot read properties of undefined (reading 'EIP712Domain')" after clicking the sign button. The ui is then stuck in perpetual loading sign. Same sig using metamask as provider works as intended. Code pulled directly from Ethers docs.

  const signTypedMessage = async () => {
    const domain = {
      name: "Ether Mail",
      version: "1",
      chainId: 5,
      verifyingContract: "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC",
    };

    // The named list of all type definitions
    const types = {
      Person: [
        { name: "name", type: "string" },
        { name: "wallet", type: "address" },
      ],
      Mail: [
        { name: "from", type: "Person" },
        { name: "to", type: "Person" },
        { name: "contents", type: "string" },
      ],
    };

    // The data to sign
    const value = {
      from: {
        name: "Cow",
        wallet: "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826",
      },
      to: {
        name: "Bob",
        wallet: "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB",
      },
      contents: "Hello, Bob!",
    };
    let signer = await provider.getSigner();

    const signedMessage = await signer
      ._signTypedData(domain, types, value)
      .catch((e) => console.log(e));
    console.log(signedMessage);
  };

🌎 Environment

Software Version(s)
magic-sdk 10.0.0
@magic-ext/connect 3.0.0
Browser Brave/Arc
npm 6.14.17
Operating System MacOS Monterey
@am-hernandez
Copy link
Member

Hi @0xFloop , thank you for reporting this issue. We will be taking a look at this and following up here!

@0xFloop
Copy link
Author

0xFloop commented Oct 19, 2022

Update on the issue, using ethers method _signTypedData(domain, types, value) does not work for some reason. But the same data manually signed via signer.provider.send("eth_signTypedData_v4", params) does work. This is confusing because the ethers method is calling the .send method within it. All it does is format the data a little different. This discrepancy between methods of signing data does not pose any problem with metamask as provider, only when using magic connect. Hope that helps solve the issue

@0xFloop
Copy link
Author

0xFloop commented Nov 1, 2022

@am-hernandez any update?

@am-hernandez
Copy link
Member

@0xFloop We will need more time to dig into this. No updates yet!

@JacobInCode
Copy link

Any word on this - I'm experiencing this issue signing a transaction with the ThirdWebSdk and magic

@JacobInCode
Copy link

@am-hernandez any update on this?

@am-hernandez
Copy link
Member

am-hernandez commented Dec 16, 2022

@0xFloop @0xJacobean

Magic Connect includes EIP712Domain in types even when you exclude it. This causes a conflict because EIP712Domain should not be passed into ethers since ethers adds it automatically.

You should not pass the EIP712Domain into ethers. It will compute it for you. It is seeing that as an alternate possible root.
You can’t have more than one root parent since that would mean you are passing in a bunch of unused types. You can pass in nested structures, just not cyclic ones...
-- Richard Moore, ethers.js, source

This is why you will see the error: TypeError: Cannot read properties of undefined (reading 'EIP712Domain').

Changes are required from our end to make it work with the example shared by @0xFloop from the ethers docs. For now, if possible for your use-case, you can use signer.provider.send("eth_signTypedData_v4", params) as a workaround.

@JacobInCode
Copy link

thanks

@garyng2000
Copy link

"TypeError: Cannot read properties of undefined (reading 'EIP712Domain')". This is because etherjs stringify the object before sending it out and the popup becomes a string, not typed string. I have to bypass ethers js and send it directly.

However, it fails in the encoding. I am trying to sign a ERC2612 Permit request(which works both in ether js private wallet and also work in metamask direct, so the object is perfectly ok) but failed in magic.link. The dialog popup also see everything correctly but the signing failed in the encoding process. Unfortunately, this is a packed app so no way I can trace it. coming from here (https://assets.auth.magic.link/static/app.chunk~vendor~crypto.891afc247160367a9f08.js). Has anyone really get it working for the permit request ? I thought it is very standard.

app.chunkvendorcrypto.891afc247160367a9f08.js:2

   Uncaught (in promise) Error: Argument is not a number
at s (app.chunk~vendor~crypto.891afc247160367a9f08.js:2:438775)
at u (app.chunk~vendor~crypto.891afc247160367a9f08.js:2:440040)
at n.rawEncode (app.chunk~vendor~crypto.891afc247160367a9f08.js:2:443303)
at Object.encodeData (app.chunk~vendor~crypto.891afc247160367a9f08.js:2:2250991)
at Object.hashStruct (app.chunk~vendor~crypto.891afc247160367a9f08.js:2:2251536)
at Object.sign (app.chunk~vendor~crypto.891afc247160367a9f08.js:2:2251935)
at Object.signTypedData_v4 (app.chunk~vendor~crypto.891afc247160367a9f08.js:2:2255633)
at Object.d [as signTypedDataV4] (app.chunk~services.dd0e545cb7889e3216ba.js:1:20919)
at Object.<anonymous> (app.chunk~store.eb792c92dab84cbd9c45.js:1:23904)
at Generator.next (<anonymous>)

@0xFloop
Copy link
Author

0xFloop commented Jan 24, 2023

This is not possible in my case as I am using seaport.js to sign typed seaport orders. Because of this I do not have the luxury of changing the method by which the orders are being signed within seaport.js

@0xClouds
Copy link

0xClouds commented Feb 2, 2023

Was able to achieve what I needed using 0xFloop's suggestions of manually signing with ("eth_signTypedData_v4", params).
This ended up working with both email login or using a metamask wallet.
If you need more assistance I would use this as reference: https://docs.metamask.io/guide/signing-data.html#signtypeddata-v4

@am-hernandez
Copy link
Member

@0xFloop
@0xClouds @0xJacobean

We just pushed changes to resolve this issue. I just tested it using the example from ethers.js. Here's a code sample:

const signTypedDataWithMagic = async () => {
  const domain = {
    name: "Ether Mail",
    version: "1",
    chainId: 5,
    verifyingContract: "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
  };

  // The named list of all type definitions
  const types = {
    Person: [
      { name: "name", type: "string" },
      { name: "wallet", type: "address" }
    ],
    Contact: [
      { name: "from", type: "Person" },
      { name: "to", type: "Person" },
      { name: "contents", type: "string" }
    ]
  };

  // The data to sign
  const value = {
    from: {
      name: "Cow",
      wallet: "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"
    },
    to: {
      name: "Bob",
      wallet: "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"
    },
    contents: "Hello, Bob!"
  };

  try {
    const signer = await web3.getSigner();

    const signature = await signer._signTypedData(domain, types, value);
    console.log(signature);
  } catch (err) {
    console.log(err);
  }
};

Please let us know if you are still experiencing this issue and we'll reopen!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants