diff --git a/src/main/java/org/arkecosystem/crypto/utils/AbiDecoder.java b/src/main/java/org/arkecosystem/crypto/utils/AbiDecoder.java index 14363ba..6b08495 100644 --- a/src/main/java/org/arkecosystem/crypto/utils/AbiDecoder.java +++ b/src/main/java/org/arkecosystem/crypto/utils/AbiDecoder.java @@ -33,15 +33,44 @@ public Map decodeFunctionData(String data) throws Exception { return result; } + public String decodeError(String data) throws Exception { + data = stripHexPrefix(data); + + String errorSelector = data.substring(0, 8); + + Map abiItem = findErrorBySelector(errorSelector); + if (abiItem == null) { + throw new Exception("Function selector not found in ABI: " + errorSelector); + } + + return (String) abiItem.get("name"); + } + private Map findFunctionBySelector(String selector) { for (Map item : this.abi) { - if ("function".equals(item.get("type"))) { - String functionSignature = getFunctionSignature(item); - String functionSelector = - stripHexPrefix(keccak256(functionSignature)).substring(0, 8); - if (functionSelector.equals(selector)) { - return item; - } + if (!"function".equals(item.get("type"))) { + continue; + } + + String functionSignature = getFunctionSignature(item); + String functionSelector = stripHexPrefix(keccak256(functionSignature)).substring(0, 8); + if (functionSelector.equals(selector)) { + return item; + } + } + return null; + } + + private Map findErrorBySelector(String selector) { + for (Map item : this.abi) { + if (!"error".equals(item.get("type"))) { + continue; + } + + String errorSignature = getFunctionSignature(item); + String errorSelector = stripHexPrefix(keccak256(errorSignature)).substring(0, 8); + if (errorSelector.equals(selector)) { + return item; } } return null; diff --git a/src/test/java/org/arkecosystem/crypto/utils/AbiDecoderTest.java b/src/test/java/org/arkecosystem/crypto/utils/AbiDecoderTest.java index aaf4bae..63fbadc 100644 --- a/src/test/java/org/arkecosystem/crypto/utils/AbiDecoderTest.java +++ b/src/test/java/org/arkecosystem/crypto/utils/AbiDecoderTest.java @@ -191,4 +191,42 @@ void it_should_decode_vote_payload() throws Exception { assertEquals(expectedData, decodedData); } + + @Test + void it_should_decode_error_payload() throws Exception { + assertEquals("CallerIsNotValidator", decoder.decodeError("cd03235e")); + } + + @Test + void it_should_decode_error_payload_with_hex_prefix() throws Exception { + assertEquals("CallerIsNotValidator", decoder.decodeError("0xcd03235e")); + } + + @Test + void it_should_decode_error_with_parameters() throws Exception { + String selector = stripSelector("InvalidRange(uint256,uint256)"); + + assertEquals("InvalidRange", decoder.decodeError(selector)); + } + + @Test + void it_should_throw_when_error_selector_is_not_found() { + Exception thrown = assertThrows(Exception.class, () -> decoder.decodeError("123456ab")); + + assertEquals("Function selector not found in ABI: 123456ab", thrown.getMessage()); + } + + @Test + void it_should_not_decode_function_selectors_as_errors() { + String voteSelector = "6dd7d8ea"; + + Exception thrown = assertThrows(Exception.class, () -> decoder.decodeError(voteSelector)); + + assertEquals("Function selector not found in ABI: " + voteSelector, thrown.getMessage()); + } + + private String stripSelector(String signature) { + String hash = org.web3j.crypto.Hash.sha3String(signature); + return hash.startsWith("0x") ? hash.substring(2, 10) : hash.substring(0, 8); + } }