brownie
brownie copied to clipboard
Handle new error types for solidity 0.8.4
https://blog.soliditylang.org/2021/04/21/custom-errors/
This post has some example ethers js code that should be a decent guide.
Was this fixed in #1110 ?
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.
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'
+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.
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....
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.
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
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)
.
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.
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]})