enzoferey / ethers-error-parser

Parse Ethers.js errors with ease πŸ’…πŸ»
MIT License
66 stars 8 forks source link

[Question] Error format depending on the EVM backend ? #42

Open challet opened 1 year ago

challet commented 1 year ago

Hello, while using Ganache as a development backend, some errors are not handled as expected. For instance with the following one (the context is a call to ethers estimateGas method which makes a dry run of the transaction and so can throw similar errors as the actual transaction):

{
    "code": -32603,
    "data": {
        "code": -32000,
        "data": {
            "hash": null,
            "message": "revert",
            "programCounter": 1420,
            "reason": "[revert message]",
            "result": "0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c34303920436f6e666c6963740000000000000000000000000000000000000000"
        },
        "message": "VM Exception while processing transaction: revert [revert message]",
        "name": "RuntimeError",
        "stack": "RuntimeError: VM Exception while processing transaction: revert [revert message]\n    at exactimate ([path]/node_modules/ganache/dist/node/1.js:2:182333)"
    },
    "message": "Internal JSON-RPC error."
}

It is parsed as { errorCode: 'UNKNOWN_ERROR', context: '-32603' } while imho it should return the EXECUTION_REVERTED one : https://github.com/enzoferey/ethers-error-parser/blob/d4e452b411738e9b97a5e9e8978ce875a46bd967/lib/constants.ts#L6

The text message looks really specific to Ganache and I'm wondering whether the whole error structure is also specific to it. How could it be then handled by the error parser ?

enzoferey commented 1 year ago

Hey @challet ! Indeed Ganache is probably changing the format a bit, but maybe there is a way to make it compatible. Are you using https://github.com/trufflesuite/ganache#as-an-ethersjs-provider ? Could you share some code around how you are populating and executing transactions ?

challet commented 1 year ago

Are you using https://github.com/trufflesuite/ganache#as-an-ethersjs-provider ?

Actually no, because I'm using ganache CLI and then connecting my dapp through @wagmi/core publicProvider : it wraps an Ethers StaticJsonRpcProvider and doesn't involve any error handling.

This is the code I'm using (several parts pasted together, hopefully without omission):

import { configureChains, createClient, getContract, getProvider, prepareWriteContract, writeContract, InjectedConnector } from '@wagmi/core'
import { publicProvider } from '@wagmi/core/providers/public'
import { localhost } from '@wagmi/core/chains'
import { getParsedEthersError } from '@enzoferey/ethers-error-parser'
import type { Contract } from 'ethers'
import type { EthersError } from '@enzoferey/ethers-error-parser'

// configure wagmi
const { provider, chains } = configureChains(
  [localhost],
  [publicProvider()]
)
// it creates a singleton that will be used internally by other wagmi functions
createClient({
  autoConnect: true,
  connectors: [new InjectedConnector({ chains })],
  provider
})
// […]
const contract: Contract = await getContract({
  address, abi, signerOrProvider: getProvider()
})
try {
  const config = await prepareWriteContract({ // this function calls `estimateGas`
    address: contract.address,
    abi: contract.interface.fragments,
    functionName: 'myFunction' // method of the contract that is reverting the transaction
  })
  trx = await writeContract(config)
} catch (e) {
  console.log(getParsedEthersError(e as EthersError))
}

I'm trying to dig into ethers to see where it can differ from the expected format (maybe in the providers, maybe in the backend).

challet commented 1 year ago

Tracing it back into Metamask, that is the raw error received from the backend being re-thrown without modification by ethers or a provider. Or maybe it's always (Ganache and other EVM engines) like that when the error is thrown from an estimateGas operation ?

challet commented 1 year ago

In fact, I see two scenarios here for the same error but with a different format (with or without Ganache, even though there might still be an other layer of difference in using it).

call to estimateGas (for later use its result as an argument of populateTransaction)

That's the one I'm reporting. The backend error is not caught (or re-thrown without change) by estimateGas and then bubbles up to the stack. See here

call to populateTransaction without prior estimate

It is caught by populateTransaction (which calls estimateGas) here and rethrown as UNPREDICTABLE_GAS_LIMIT. That's the one already handled here

enzoferey commented 1 year ago

Thanks for all the details @challet ! πŸ™πŸ»

Maybe indeed we do not support the estimateGas call indeed, I would need to test it out. I'm a bit busy these days, but I will get to it as soon as possible and let you know πŸ‘πŸ»

challet commented 1 year ago

Imho, the problem is on the ethers side where it should throw the same error for both cases. There is no hurry and please consider it a discussion in progress as I'm a bit confused in how the various providers and signers work, especially if they all behave the same. I opened a discussion on the ethers repo about it : https://github.com/ethers-io/ethers.js/discussions/3618