Solidity использует функции revert, require и assert в качестве исключений возврата состояния для обработки ошибок. revert является общим, assert следует использовать только для проверки внутренних ошибок и инвариантов, а require следует использовать для проверки допустимых условий, таких как входные данные или переменные состояния контракта или возвращаемые значения из вызовов внешних контрактов.

Начиная с версии 0.4.22 Solidity можно указать необязательный аргумент причины с помощью функций revert и require. Это означает, что такие функции, как

function myFunction(uint256 input) public view returns (uint256) {
    require(input >= 5); return input * input - 25;
}

теперь можно обогатить, как

function myFunction(uint256 input) public view returns (uint256) {
    require(input >= 5, "myFunction only accepts arguments which are greater than or equal to 5");
    return input * input - 25;
}

Это выглядит как приятное улучшение, но как я могу использовать Web3j, чтобы фактически проверить, был ли отменен переход с eth_call на myFunction и какова была сопутствующая причина?

Как поясняется в запросе на вытягивание, в котором добавлена ​​функция Вернуть с указанием причины, строка причины закодирована как вызов функции Error(string), включая селектор функций. Это означает, что когда вышеуказанная функция возвращается из-за функции require, результатом будет:

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

Если мы используем EthCall Web3j при вызове функции myFunction, эта причина возврата может быть извлечена следующим образом:

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();
}

Что вернет Optional строки «myFunction принимает только аргументы, которые больше или равны 5».

Первоначально опубликовано на www.dhondt.tech.