ethereum / go-ethereum

Go implementation of the Ethereum protocol
https://geth.ethereum.org
GNU Lesser General Public License v3.0
47.54k stars 20.13k forks source link

solidity custom error support #26823

Closed ARR552 closed 2 hours ago

ARR552 commented 1 year ago

Rationale

Formerly, the only way to return a revert reason was using require(condition, ‘Something bad happened’);. The code generated by the abigen tool for a smartcontract, returned the revert reason if something was wrong. This error was triggered during the gas estimation. Now, the new way of adding custom errors (revert messages) is revert NotAllowed();. Using the code generated by the abigen, the error received is just execution reverted. I could decode the error by my own if i had access to the output data returned in the tx but this is inside the autogenerated code and not exported. I want to keep receiving the same revert reason even using the new solidity custom errors.

If this feature is already implemented, if so how can i use it??

Implementation

It should be something like this: https://blog.soliditylang.org/2021/04/21/custom-errors/

MariusVanDerWijden commented 1 year ago

I added custom error handling a while ago: https://github.com/ethereum/go-ethereum/pull/23161 However I think it only added the types and not the error unmarshaling part

smartcontracts commented 1 year ago

@MariusVanDerWijden is it correct that Geth currently doesn't surface custom revert messages in call/gas estimation?

MariusVanDerWijden commented 1 year ago

yes

smartcontracts commented 1 year ago

Got it, thank you!

n00b21337 commented 1 year ago

@MariusVanDerWijden is it correct that Geth currently doesn't surface custom revert messages in call/gas estimation?

Could you please elaborate a bit on that want to be sure. Does this mean there isn't any trace of a custom error data in hex value when we use call or gasEstimate in the response we get?

edited:

I did make a test calls over Alchemy to see what is happening and I see that in Data there is info that is signature of error so maybe you are saying that there isn't "translation" of error to human readable string? But this isnt only for call/estimates but all custom errors outputs in trx, right?

image

n00b21337 commented 1 year ago

But just plain eth_call will output nothing

image

samlaf commented 8 months ago

Are there plans to support this anytime soon? Took me a while to figure out what was happening, and seems like our only option is to revert to more gas-expensive string errors. Probably not that hard to take the 4byte error code from the data and show it as part of the error string, unless I'm missing something?

RonTuretzky commented 8 months ago

bumping this for attention

n00b21337 commented 8 months ago

Are there plans to support this anytime soon? Took me a while to figure out what was happening, and seems like our only option is to revert to more gas-expensive string errors. Probably not that hard to take the 4byte error code from the data and show it as part of the error string, unless I'm missing something?

I suppose its intended to be solved on app level, like Tenderly solves that perfectly most of the time, etherscan sadly still doesn't for some reason.

samlaf commented 8 months ago

Are there plans to support this anytime soon? Took me a while to figure out what was happening, and seems like our only option is to revert to more gas-expensive string errors. Probably not that hard to take the 4byte error code from the data and show it as part of the error string, unless I'm missing something?

I suppose its intended to be solved on app level, like Tenderly solves that perfectly most of the time, etherscan sadly still doesn't for some reason.

Are those projects even using geth abigen? We're using abigen directly and this is making us unable to use custom errors in our contracts. Unless I'm missing something when simulating transactions to estimate gas limit, there's no way right now to retrieve the error string, as its not exposed by geth.

samlaf commented 6 months ago

https://github.com/ethereum/solidity/issues/14442 just got merged. The Solidity change is specifically enabling custom errors to use with the common require(bool, err) syntax. It's a widely used syntax because it's quite concise, but until now you couldn't use error types with it so basically no one used error types. More and more teams will probably start migrating to use it, so would be really nice to have abigen support for it.

RonTuretzky commented 6 months ago

https://github.com/ethereum/solidity/issues/14442 just got merged. The Solidity change is specifically enabling custom errors to use with the common require(bool, err) syntax. It's a widely used syntax because it's quite concise, but until now you couldn't use error types with it so basically no one used error types. More and more teams will probably start migrating to use it, so would be really nice to have abigen support for it.

Bump

ypatil12 commented 2 months ago

Bump on this. Solidity 0.8.27 now supports custom errors in the legacy pipeline. Any timeline on support here?

cc @MariusVanDerWijden

9OP commented 1 month ago

Hello, any update on this one ? In comparison ether.js return the full data byte slice on custom error revert. It would be nice to have the same behaviour here and allow the end user to unpack the byte slice on his side.

Confucian-e commented 2 weeks ago

I opened a PR at https://github.com/ethereum/go-ethereum/pull/30653 to fix this. Could you please review it?

islishude commented 2 weeks ago

I just tested with etherjs, it can decode custom errors correctly from geth rpc.

pragma solidity ^0.8.20;

error MyError();

contract Example {
    function customError() public pure {
        revert MyError();
    }

    function panicError(uint256 _x) public pure returns (uint256){
        return _x / 0;
    }
}
import { ethers } from "ethers"

const rpc = new ethers.JsonRpcProvider("http://localhost:8545")

const abi = `[
    {
        "inputs": [],
        "name": "MyError",
        "type": "error"
    },
    {
        "inputs": [
            {
                "internalType": "uint256",
                "name": "_n",
                "type": "uint256"
            }
        ],
        "name": "push0",
        "outputs": [],
        "stateMutability": "nonpayable",
        "type": "function"
    },
    {
        "inputs": [],
        "name": "customError",
        "outputs": [],
        "stateMutability": "pure",
        "type": "function"
    },
    {
        "inputs": [],
        "name": "num",
        "outputs": [
            {
                "internalType": "uint256",
                "name": "",
                "type": "uint256"
            }
        ],
        "stateMutability": "view",
        "type": "function"
    },
    {
        "inputs": [
            {
                "internalType": "uint256",
                "name": "_x",
                "type": "uint256"
            }
        ],
        "name": "panicError",
        "outputs": [
            {
                "internalType": "uint256",
                "name": "",
                "type": "uint256"
            }
        ],
        "stateMutability": "pure",
        "type": "function"
    }
]`

const contract = new ethers.Contract("0x3Ba34f18F2b56f2cf49B738468D513014a2542eB", abi, rpc)

try {
    await contract.customError()
} catch (err) {
    console.log((err as any).message)
}

try {
    await contract.panicError(1)
} catch (err) {
    console.log((err as any).message)
}
execution reverted: MyError()
execution reverted: Panic due to DIVIDE_BY_ZERO(18) (action="call", data="0x4e487b710000000000000000000000000000000000000000000000000000000000000012", reason="Panic due to DIVIDE_BY_ZERO(18)", transaction={ "data": "0x67c9d7930000000000000000000000000000000000000000000000000000000000000001", "to": "0x3Ba34f18F2b56f2cf49B738468D513014a2542eB" }, invocation=null, revert={ "args": [ 18 ], "name": "Panic", "signature": "Panic(uint256)" }, code=CALL_EXCEPTION, version=6.13.4)