ethereum-attestation-service / eas-sdk

Ethereum Attestation Service - TypeScript/JavaScript SDK
MIT License
97 stars 47 forks source link

signOffchainMessage hitting rpcUrl with eth_signedTypedDataV4 #115

Closed polus-arcticus closed 2 months ago

polus-arcticus commented 2 months ago

Hi im trying to use the signOffchainAttestation API

export const signOffchainBuyMessage = async (
  easAddress: `0x${string}`,
  walletClient: WalletClient,
  buyParams: BuyParams,
  options?: OffchainAttestationOptions
)  => {
  const signer = clientToSigner(walletClient)
  if (!signer) return
  const eas = getEAS(easAddress, signer)
  console.log(eas)
  const offchain = await eas.getOffchain()

  const offchainAttestation =  await offchain.signOffchainAttestation(
    {
      recipient: buyParams.demander,
      expirationTime: 0n,
      time: BigInt(Math.floor(Date.now() / 1000)),
      revocable: true,
      schema: buyParams.schemaUID,
      refUID: ZERO_BYTES32,
      data: createBuyData(buyParams.data),
    },
    signer,
  )
  console.log('offchainAttestation', offchainAttestation)

  return offchainAttestation
}

Its working well on hardhat tests forking mainnet, but i'm trying to do it in node against base sepolia i run into

124 |         case "NUMERIC_FAULT":
125 |         case "BUFFER_OVERRUN":
126 |             error = new RangeError(message);
127 |             break;
128 |         default:
129 |             error = new Error(message);
                          ^
error: could not coalesce error (error={ "message": "HTTP request failed.\n\nStatus: 400\nURL: https://base-sepolia.g.alchemy.com/v2/${---MyalchemyKEy--}\nRequest body: {\"method\":\"eth_signTypedData_v4\",\"params\":[\"0x788ced731764cf1bdbf0da8acedaca7cae4c9997\",\"{\\\"types\\\":{\\\"Attest\\\":[{\\\"name\\\":\\\"version\\\",\\\"type\\\":\\\"uint16\\\"},{\\\"name\\\":\\\"schema\\\",\\\"type\\\":\\\"bytes32\\\"},{\\\"name\\\":\\\"recipient\\\",\\\"type\\\":\\\"address\\\"},{\\\"name\\\":\\\"time\\\",\\\"type\\\":\\\"uint64\\\"},{\\\"name\\\":\\\"expirationTime\\\",\\\"type\\\":\\\"uint64\\\"},{\\\"name\\\":\\\"revocable\\\",\\\"type\\\":\\\"bool\\\"},{\\\"name\\\":\\\"refUID\\\",\\\"type\\\":\\\"bytes32\\\"},{\\\"name\\\":\\\"data\\\",\\\"type\\\":\\\"bytes\\\"},{\\\"name\\\":\\\"salt\\\",\\\"type\\\":\\\"bytes32\\\"}],\\\"EIP712Domain\\\":[{\\\"name\\\":\\\"name\\\",\\\"type\\\":\\\"string\\\"},{\\\"name\\\":\\\"version\\\",\\\"type\\\":\\\"string\\\"},{\\\"name\\\":\\\"chainId\\\",\\\"type\\\":\\\"uint256\\\"},{\\\"name\\\":\\\"verifyingContract\\\",\\\"type\\\":\\\"address\\\"}]},\\\"domain\\\":{\\\"name\\\":\\\"EAS Attestation\\\",\\\"version\\\":\\\"1.2.0\\\",\\\"chainId\\\":\\\"0x14a34\\\",\\\"verifyingContract\\\":\\\"0x4200000000000000000000000000000000000021\\\"},\\\"primaryType\\\":\\\"Attest\\\",\\\"message\\\":{\\\"version\\\":\\\"2\\\",\\\"schema\\\":\\\"0x7674c84acee890ef03bdbe281853efce9a10afe427dbfb203577ff3137bd0349\\\",\\\"recipient\\\":\\\"0x788ced731764cf1bdbf0da8acedaca7cae4c9997\\\",\\\"time\\\":\\\"1724329886\\\",\\\"expirationTime\\\":\\\"0\\\",\\\"revocable\\\":true,\\\"refUID\\\":\\\"0x0000000000000000000000000000000000000000000000000000000000000000\\\",\\\"data\\\":\\\"0x000000000000000000000000788ced731764cf1bdbf0da8acedaca7cae4c999700000000000000000000000000000000000000000000000000000000000000640000000000000000000000003d3c9fcc5c5c751b10db7fa7b13e59a97e8044670000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000d9ef660000000000000000000000000000000000000000000000000000000000d9f66e0000000000000000000000000000000000000000000000000000000000da047e00000000000000000000000000000000000000000000000000000000000000146772796361702f636f777361793a6c6174657374000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e68656c6c6f20636f6f7068697665000000000000000000000000000000000000\\\",\\\"salt\\\":\\\"0xf3e93d423f9ad6576ac13b8b509692259b32e630626dd0bbb7f320c7b24fc33b\\\"}}\"]}\n\nDetails: {\"code\":-32600,\"message\":\"Unsupported method: eth_signTypedData_v4. See available methods at https://docs.alchemy.com/alchemy/documentation/apis\"}\nVersion: viem@2.17.5" }, payload={ "id": 6, "jsonrpc": "2.0", "method": "eth_signTypedData_v4", "params": [ "0x788ced731764cf1bdbf0da8acedaca7cae4c9997", "{\"types\":{\"Attest\":[{\"name\":\"version\",\"type\":\"uint16\"},{\"name\":\"schema\",\"type\":\"bytes32\"},{\"name\":\"recipient\",\"type\":\"address\"},{\"name\":\"time\",\"type\":\"uint64\"},{\"name\":\"expirationTime\",\"type\":\"uint64\"},{\"name\":\"revocable\",\"type\":\"bool\"},{\"name\":\"refUID\",\"type\":\"bytes32\"},{\"name\":\"data\",\"type\":\"bytes\"},{\"name\":\"salt\",\"type\":\"bytes32\"}],\"EIP712Domain\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"version\",\"type\":\"string\"},{\"name\":\"chainId\",\"type\":\"uint256\"},{\"name\":\"verifyingContract\",\"type\":\"address\"}]},\"domain\":{\"name\":\"EAS Attestation\",\"version\":\"1.2.0\",\"chainId\":\"0x14a34\",\"verifyingContract\":\"0x4200000000000000000000000000000000000021\"},\"primaryType\":\"Attest\",\"message\":{\"version\":\"2\",\"schema\":\"0x7674c84acee890ef03bdbe281853efce9a10afe427dbfb203577ff3137bd0349\",\"recipient\":\"0x788ced731764cf1bdbf0da8acedaca7cae4c9997\",\"time\":\"1724329886\",\"expirationTime\":\"0\",\"revocable\":true,\"refUID\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"data\":\"0x000000000000000000000000788ced731764cf1bdbf0da8acedaca7cae4c999700000000000000000000000000000000000000000000000000000000000000640000000000000000000000003d3c9fcc5c5c751b10db7fa7b13e59a97e8044670000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000d9ef660000000000000000000000000000000000000000000000000000000000d9f66e0000000000000000000000000000000000000000000000000000000000da047e00000000000000000000000000000000000000000000000000000000000000146772796361702f636f777361793a6c6174657374000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e68656c6c6f20636f6f7068697665000000000000000000000000000000000000\",\"salt\":\"0xf3e93d423f9ad6576ac13b8b509692259b32e630626dd0bbb7f320c7b24fc33b\"}}" ] }, code=UNKNOWN_ERROR, version=6.13.2)

where we see

Details: {\"code\":-32600,\"message\":\"Unsupported method: eth_signTypedData_v4. See available methods at https://docs.alchemy.com/alchemy/documentation/apis\"}\nVersion: viem@2.17.5" }, payload={ "id": 6, "jsonrpc": "2.0", "method": "eth_signTypedData_v4"

i'm not sure where in the code its hitting the rpc url,

perhaps i need to write my own EIP-1193 compliant provider ?

thanks!

lbeder commented 2 months ago

This is happening because you are trying to sign the message using Alchemy's provider, and they never supported this flow properly (no idea why if to be honest)

polus-arcticus commented 2 months ago

This is happening because you are trying to sign the message using Alchemy's provider, and they never supported this flow properly (no idea why if to be honest)

Thanks for the prompt response, ill give it a try with moralis or infura.

polus-arcticus commented 2 months ago

https://forum.openzeppelin.com/t/providererror-the-method-eth-signtypeddata-v4-does-not-exist-is-not-available/16929

image

https://community.infura.io/t/the-method-eth-signtypeddata-does-not-exist-is-not-available/3122/3 image

I may be running into this issue getting out of hardhat because it is no longer intercepting the eth_signedtypeddata_v4 eth rpc call. and since im in node and not the browser i dont have a wallet doing it for me either. For others crossing this issue, i'm seeing that if creating my own EIP-1193 provider might help me get around this, as for some reason the ethers JsonRpcSigner is not intercepting this rpc call like hardhat or metamask

https://stackoverflow.com/questions/71744849/the-method-eth-signtypeddata-v4-does-not-exist-is-not-available

@lbeder if interested

polus-arcticus commented 2 months ago

Writing my suggested change here https://github.com/polus-arcticus/eas-sdk/tree/feature/sign-without-special-provider . I dont think EAS should rely on specially made providers such as those created by metamask or hardhat to intercept eth_signtypeddata_v4 before it hits the rpc directly. In this branch it uses @metamask/eth-sig-util to sign the messages instead of asking the provider to do it

lbeder commented 2 months ago

Writing my suggested change here https://github.com/polus-arcticus/eas-sdk/tree/feature/sign-without-special-provider . I dont think EAS should rely on specially made providers such as those created by metamask or hardhat to intercept eth_signtypeddata_v4 before it hits the rpc directly. In this branch it uses @metamask/eth-sig-util to sign the messages instead of asking the provider to do it

Some providers are indeed don't handle this great, but in general, it's still recommended to delegate this functionality to a signer, since it's ultimately the controlling the required keys. Other than Alchemy's, we never encountered any issues.

polus-arcticus commented 2 months ago

Some providers are indeed don't handle this great, but in general, it's still recommended to delegate this functionality to a signer, since it's ultimately the controlling the required keys. Other than Alchemy's, we never encountered any issues.

@lbeder Indeed, for base sepolia, i couldnt get any one i tried to work, Ankr, Moralis, Alchemy, Infura, after a while i gave up. Digging deeper, it was because i was not in the browser or in hardhat. Indeed, metamask and hardhat intercept requests to a provider for eth_signtypedata_v4. I expect 99% of users test and exist in these environments. But if one is looking to do this server side with autonomous agents they are out of luck. Perhaps extending the signer prototype with @metamask/eth-sig-util to keep the private key management on the web3.js/ethers/viem instance is the way to go

lbeder commented 2 months ago

Yes, currently the SDK expects a signer that implements the signTypedData function (https://github.com/ethereum-attestation-service/eas-sdk/blob/017ee207cf5b8dfbbfa4cd7f9988fba33a7891f0/src/offchain/typed-data-handler.ts#L17). You can overload or even provide a custom implementation for this function.