Solidity
如何接收過去交易的 revert() 原因?
我執行一個 geth/parity 節點,用於將使用者的交易廣播到乙太坊區塊鏈。一些合約函式提供了恢復的原因(見下面的例子):
contract Example { function test (uint i) { require(i == 1, "ERROR_CODE") } }
據我了解,無法使用 eth_getTransactionReceipt 獲取還原原因字元串。但是我仍然需要恢復失敗交易的原因。
如何獲取過去失敗交易的恢復原因(上例中的“ERROR_CODE”)?(至少對於最近 20 個區塊的交易)
在solidity 0.4.22 中添加了
require
和revert
原因。從這裡可以看出,它們是 abi 編碼的,就好像它是對函式“Error(string)”的呼叫。這篇博文給出了一個例子:
eth_call
一個函式function myFunction(uint256 input) public view returns (uint256) { require(input >= 5, "myFunction only accepts arguments which are greather than or equal to 5"); return input * input - 25; }
輸入參數無效(本例中小於 5),將返回
0x08c379a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000476d7946756e6374696f6e206f6e6c79206163636570747320617267756d656e747320776869636820617265206772656174686572207468616e206f7220657175616c20746f203500000000000000000000000000000000000000000000000000
這是
0x08c379a0 // Function selector 0000000000000000000000000000000000000000000000000000000000000020 // Offset of string return value 0000000000000000000000000000000000000000000000000000000000000047 // Length of string return value (the revert reason) 6d7946756e6374696f6e206f6e6c79206163636570747320617267756d656e74 // first 32 bytes of the revert reason 7320776869636820617265206772656174686572207468616e206f7220657175 // next 32 bytes of the revert reason 616c20746f203500000000000000000000000000000000000000000000000000 // last 7 bytes of the revert reason
因此,解碼返回的字元串將為您提供還原原因。
使用 Web3j,這可以像這樣完成:
public Optional<String> getRevertReason(EthCall ethCall) { String errorMethodId = "0x08c379a0"; // Numeric.toHexString(Hash.sha3("Error(string)".getBytes())).substring(0, 10) List<TypeReference<Type>> revertReasonTypes = Collections.singletonList(TypeReference.create((Class<Type>) AbiTypes.getType("string"))); if (!ethCall.hasError() && ethCall.getValue() != null && ethCall.getValue().startsWith(errorMethodId)) { String encodedRevertReason = ethCall.getValue().substring(errorMethodId.length()); List<Type> decoded = FunctionReturnDecoder.decode(encodedRevertReason, revertReasonTypes); Utf8String decodedRevertReason = (Utf8String) decoded.get(0); return Optional.of(decodedRevertReason.getValue()); } return Optional.empty(); }
使用 eth_Call 執行函式:
Example.test(0x0000000000000000000000000000000000000000000000000000000000000002)
在範例合約中,我們將收到來自 geth 節點的以下回复:
0x08c379a000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000a4552524f525f434f4445000000000000000000000000000000000000000000
這給出了以下輸出參數:
arg0: 0x08c379a arg1: 0x20 arg2: 0x0a arg3: 0x4552524f525f434f444500000000000000000000000000000000000000000000
其中 arg3 == ‘ERROR_CODE’(utf8 + 十六進制編碼)
所以,我想,對於完全同步的節點,我們可以使用 eth_Call 並明確指示失敗的事務塊號:
{ method: "eth_Call", params: [ { from: '0x...address', to: '0x...contract', value: '0x0', data: "0x29e99f070000000000000000000000000000000000000000000000000000000000000002" }, FALED_TRANSACTION_BLOCK_NR ] }
但是,這不是獲取錯誤程式碼的便捷方式。還有其他方法嗎?