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

Handle new error types for solidity 0.8.4 #1108

Closed
BlinkyStitt opened this issue Jun 6, 2021 · 10 comments · Fixed by #1728
Closed

Handle new error types for solidity 0.8.4 #1108

BlinkyStitt opened this issue Jun 6, 2021 · 10 comments · Fixed by #1728

Comments

@BlinkyStitt
Copy link
Collaborator

https://blog.soliditylang.org/2021/04/21/custom-errors/

This post has some example ethers js code that should be a decent guide.

@iamdefinitelyahuman iamdefinitelyahuman added this to the Brownie 1.15.0 milestone Jun 19, 2021
@iamdefinitelyahuman
Copy link
Member

Was this fixed in #1110 ?

@cameel
Copy link

cameel commented Aug 14, 2021

I think that #1110 made Brownie not crash on them but did not add any way to parse the info out of them and display it to the user.

@julianmrodri
Copy link
Contributor

HI, im having trouble parsing custom errors in my tests. Im using solc 0.8.4 and I get the following error when trying to parse the revert reason. Is there a plan to support this in the short term? Should I be using the brownie.reverts in a different way? Thanks!

  with brownie.reverts("CustomErrorMessage()"):
        token.issue(mintAmount, {'from': owner})

I get this error:

 - AssertionError: Unexpected revert string 'typed error: 0xd9a6bff9'

@tfalencar
Copy link
Contributor

+1 for this feature. It's been almost 2 years that solidity announced Catch for custom error types: https://blog.ethereum.org/2020/01/29/solidity-0.6-try-catch/
so who knows when this will actually land. Might as well bite the bullet and do the parsing on brownie itself.
The existing helper with // dev: in brownie has been one of the decisive factors to choose brownie, so extending this for having support for custom errors is highly valuable IMO.

@julianmrodri
Copy link
Contributor

Worth to mention I found this workaround to check for custom errors in tests. Replace YourCustomError() with the actual error you expect to throw.

  errMsg = web3.keccak(text='YourCustomError()')[:4].hex()  
  
  with brownie.reverts('typed error: ' + errMsg):
        ... your contract call....
        

@EdNoepel
Copy link

In case it is useful, I created some sample contracts and tests surrounding this:
https://github.com/EdNoepel/brownie-test-issues/blob/master/tests/test_errors.py

Note that when a custom error is raised by an imported/library contract, the exception and callstack does not reveal details about the error.

@damn1
Copy link

damn1 commented Jan 11, 2023

Hey guys @julianmrodri workaround works perfect but does anybody knows how to do same thing when errors have parameters? If I have an error for example like this error AddressNotAdmin(address addr);, the revert message is like typed error: 0x0888870300000000000000000000000033a4622b82d4c04a53e170c638b944ce27cffce3. Do you how that hash is built? Thanks

@cameel
Copy link

cameel commented Jan 11, 2023

The selector is built the same way as for functions, i.e. includes parameter types. I.e. for your AddressNotAdmin error the selector is created by hashing AddressNotAdmin(address).

@michprev
Copy link

michprev commented Feb 10, 2023

This is not exactly a solution for this issue but you should give a try to the new Python-based testing framework that was inspired by Brownie - Woke testing framework (spoiler alert - I am the lead developer). It handles user-defined errors in a form of dataclass exceptions. To do this, pytypes , Python equivalents of Solidity types with type hints, are generated which also provide autocompletions while writing tests in Python.

JoaoMorais96 added a commit to JoaoMorais96/brownie that referenced this issue Mar 21, 2023
Attempt to fix issue eth-brownie#1108 (eth-brownie#1108).
Can be used in testing as follows:
with brownie.reverts(<DeployedContract>.encode_custom_error(<nameOfCustomError>, [params_of_custom_error]))
@JoaoMorais96
Copy link

JoaoMorais96 commented Mar 21, 2023

To anybody still struggling with this, I submitted a pull request.
In the meanwhile (or if it is not approved) you can use this function in your testing:

from eth_utils.abi import function_abi_to_4byte_selector, collapse_if_tuple
def encode_custom_error(contract_, err_name, params):

    contract_abi = contract_.abi

    for error in [abi for abi in contract_abi if abi["type"] == "error"]:
        # Get error signature components
        name = error["name"]
        data_types = [collapse_if_tuple(abi_input) for abi_input in error.get("inputs", [])]
        error_signature_hex = function_abi_to_4byte_selector(error).hex()
 
        if err_name == name:
            encoded_params = ''
            for param in params:
                if(type(param)==str):
                    return('typed error: 0x'+error_signature_hex+param.zfill(66)[2:])
                elif(type(param)==int):
                    val = "{0:#0{1}x}".format(param,66)
                    val = val[2:]
                else:
                    return 'Unsupported type'
                encoded_params = encoded_params + val
            return('typed error: 0x'+error_signature_hex+encoded_params)
        
    return 'error not found'

For example if you want to test if a function checkIfPar(uint256 num) in your contract NumberGetter throws the error numberNotPa(num), for num = 10, you would do:

with brownie.reverts(encode_custom_error('NumberGetter', 'numberNotPar ', [10])):
        NumberGetterdeploy.checkIfPar(10
                                        {"from":accounts[0]})

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

Successfully merging a pull request may close this issue.

9 participants