Skip to content

Commit

Permalink
Merge pull request #906 from eth-brownie/feat-solc-error-codes
Browse files Browse the repository at this point in the history
Solidity error codes
  • Loading branch information
iamdefinitelyahuman authored Dec 25, 2020
2 parents d5e2dd5 + fe46bac commit a1bc5bc
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 5 deletions.
12 changes: 10 additions & 2 deletions brownie/network/transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from brownie.exceptions import RPCRequestError
from brownie.project import build
from brownie.project import main as project_main
from brownie.project.compiler.solidity import SOLIDITY_ERROR_CODES
from brownie.project.sources import highlight_source
from brownie.test import coverage
from brownie.utils import color
Expand Down Expand Up @@ -618,8 +619,15 @@ def _reverted_trace(self, trace: Sequence,) -> None:
for step in (i for i in trace[::-1] if i["op"] in ("REVERT", "INVALID")):
if step["op"] == "REVERT" and int(step["stack"][-2], 16):
# get returned error string from stack
data = _get_memory(step, -1)[4:]
self._revert_msg = decode_abi(["string"], data)[0]
data = _get_memory(step, -1)
if data[:4].hex() == "0x4e487b71": # keccak of Panic(uint256)
error_code = int(data[4:].hex(), 16)
if error_code in SOLIDITY_ERROR_CODES:
self._revert_msg = SOLIDITY_ERROR_CODES[error_code]
else:
self._revert_msg = f"Panic (error code: {error_code})"
else:
self._revert_msg = decode_abi(["string"], data[4:])[0]

elif self.contract_address:
self._revert_msg = "invalid opcode" if step["op"] == "INVALID" else ""
Expand Down
14 changes: 14 additions & 0 deletions brownie/project/compiler/solidity.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,20 @@

AVAILABLE_SOLC_VERSIONS = None

# error codes used in Solidity >=0.8.0
# docs.soliditylang.org/en/v0.8.0/control-structures.html#panic-via-assert-and-error-via-require
SOLIDITY_ERROR_CODES = {
1: "Failed assertion",
17: "Integer overflow",
18: "Division or modulo by zero",
33: "Conversion to enum out of bounds",
24: "Access to storage byte array that is incorrectly encoded",
49: "Pop from empty array",
50: "Index out of range",
65: "Attempted to allocate too much memory",
81: "Call to zero-initialized variable of internal function type",
}


def get_version() -> Version:
return solcx.get_solc_version().truncate()
Expand Down
6 changes: 3 additions & 3 deletions tests/network/transaction/test_revert_msg.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ def test_nonpayable(tester, evmtester, console_mode):
def test_solidity_invalid_opcodes(evmtester):
with pytest.raises(VirtualMachineError) as exc:
evmtester.invalidOpcodes(0, 0)
assert exc.value.revert_msg == "invalid opcode"
assert exc.value.revert_msg in ("Failed assertion", "invalid opcode")
with pytest.raises(VirtualMachineError) as exc:
evmtester.invalidOpcodes(1, 0)
assert exc.value.revert_msg == "dev: foobar"
Expand All @@ -172,10 +172,10 @@ def test_solidity_invalid_opcodes(evmtester):
assert exc.value.revert_msg == "Index out of range"
with pytest.raises(VirtualMachineError) as exc:
evmtester.invalidOpcodes(2, 0)
assert exc.value.revert_msg == "Division by zero"
assert exc.value.revert_msg in ("Division or modulo by zero", "Division by zero")
with pytest.raises(VirtualMachineError) as exc:
evmtester.modulusByZero(2, 0)
assert exc.value.revert_msg == "Modulus by zero"
assert exc.value.revert_msg in ("Division or modulo by zero", "Modulus by zero")


def test_vyper_revert_reasons(vypertester, console_mode):
Expand Down

0 comments on commit a1bc5bc

Please sign in to comment.