Solidity

如何接收過去交易的 revert() 原因?

  • December 21, 2020

我執行一個 geth/parity 節點,用於將使用者的交易廣播到乙太坊區塊鏈。一些合約函式提供了恢復的原因(見下面的例子):

contract Example {
 function test (uint i) {
   require(i == 1, "ERROR_CODE")
 }
}

據我了解,無法使用 eth_getTransactionReceipt 獲取還原原因字元串。但是我仍然需要恢復失敗交易的原因。

如何獲取過去失敗交易的恢復原因(上例中的“ERROR_CODE”)?(至少對於最近 20 個區塊的交易)

在solidity 0.4.22 中添加了requirerevert原因。從這裡可以看出,它們是 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
 ]
}

但是,這不是獲取錯誤程式碼的便捷方式。還有其他方法嗎?

引用自:https://ethereum.stackexchange.com/questions/48383