[Solidity] try/catch

Seokhun Yoon·2022년 5월 20일
1
post-thumbnail
post-custom-banner

Try/Catch 특징

Solidity에서도 try/catch가 존재한다. 그 특징들에 대해서 알아보자.

특징
1. assert/revert/require 에러 핸들링
try/catch문 안에서 assert/revert/require로 인한 에러가 나면, catch로 넘어가지 않고 개발자가 의도한 에러로 판단하여 정상적으로 프로그램을 종료한다.
try/catch문 밖에서 assert/revert/require로 인한 에러가 나면, catch로 넘어가서 에러를 핸들할 수 있다.

2. catch의 3가지 종류
catch Error(string memory reason) { ... } : revert 나 require를 통해 생성된 에러
catch Panic(uint errorCode) { ... } : assert를 통해 생성된 에러 (예: 0으로 나누면 errorcode로 0x12를 리턴)
catch(bytesmemoryLowLevelData) { ... } : 로우 레벨 에러

3. 어디서 사용하는가?
(1) 외부 스마트 컨트랙트 함수를 호출할 때 : 다른 스마트 컨트랙트를 인스턴스화 한 뒤, 인스턴스로 함수 호출할 때 사용
(2) 외부 스마트 컨트랙트를 생성할 때 : 다른 스마트 컨트랙트를 인스턴스화 생성할 때 사용함
(3) 스마트 컨트랙트 내에서 함수를 부를 때 : this를 통해 try/catch를 사용함


Try/catch 사용해보기

아래 상황에 따라 만든 예시 코드를 remix에 넣고 테스트를 해보면 어떻게 에러가 핸들링되는지, 그리고 어떤 상황에서 catch로 넘어가지 않고 에러가 발생하는지 알 수 있다.

중간중간에 주석으로 revert()함수를 넣어두었다. 하나씩 풀어보면서 어떻게 결과가 나오는지 확인해보자.

assert() 에러에 대한 코드는 이전포스팅을 참고하세요!

(1) 외부 스마트 컨트랙트 함수를 호출할 때

catch 뒤에 Error, Panic 그리고 LowLevelError로 나눠서 각각 에러를 핸들링 할 수 있다.

contract math {
  function division(uint256 _num1, uint256 _num2) public pure returns (uint256) {
    // 나누기 함수
    require(_num1<10, "num1 should not be more than 10");
    return _num1/_num2;
  }
}

contract runner {
  event catchErr(string _name, string _err);
  event catchPanic(string _name, uint256 _err);
  event catchLowLevelErr(string _name, bytes _err);
  
  // 외부 컨트랙트 math를 인스턴스화 하기
  math public mathInstance = new math();
  

  function playTryCatch(uint256 _num1, uint256 _num2) public returns(uint256, bool) {
    try mathInstance.division(_num1, _num2) returns(uint256 value) {
      return(value, true);

    } catch Error(string memory _err) { // num1이 10보다 크면?
      emit catchErr("revert/require", _err);
      return(0,false);

    } catch Panic(uint256 _errorCode) { // num1에 10보다 작은 수를, 그리고 num2에 0을 넣으면?
      emit catchPanic("assertError/Panic", _errorCode); // num2가 0일때 에러코드는 0x12
      return (0, false);

    } catch (bytes memory _errorCode) {
      emit catchLowLevelErr("LowLevelError", _errorCode);
      return (0, false);
    }
  }
}

(2) 외부 스마트 컨트랙트를 생성할 때

catch 뒤에 Error, Panic 그리고 매개변수를 받지 않고 에러를 핸들링한다.

contract character {
  string private name;
  uint256 private power;

  constructor (string memory _name, uint256 _power) {
    // revert("error"); // 여기서 에러를 내면 어떻게 될까?
    name = _name;
    power = _power;
  }
}

contract runner {
  event catchOnly(string _name, string _err);
  
  function playTryCatch(string memory _name, uint256 _power) public returns(bool successOrFail) {
    // revert("errors outside of the try/catch block"); // 여기서 에러를 내면 어떻게 될까?
    try new character(_name, _power) { // try/catch에서 catch 컨트랙트를 직접 인스턴스화
      // revert("errors in the try/catch block"); // 여기서 에러를 내면 어떻게 될까?
      return(true);
      
    } catch {
      emit catchOnly("catch", "Errors");
      return (false);
    }
  }
}

(3) 스마트 컨트랙트 내에서 함수를 부를 때

catch 뒤에 Error, Panic 그리고 매개변수를 받지 않고 에러를 핸들링한다.

contract runner2 {
  event catchOnly(string _name, string _err);
  
  function simple() public returns(uint256) {
    // revert("error"); // 여기서 에러를 내면 어떻게 될까?
    return 4;
  }

  function playTryCatch(string memory _name, uint256 _power) public returns(uint256, bool) {
    try this.simple() returns(uint256 _value) { // this를 통해 내부 함수 불러옴
      return(_value, true);
    } catch {
      emit catchOnly("catch", "Errors");
      return (0, false);
    }
  }
}





참고 사이트

profile
블록체인 개발자를 꿈꾸다
post-custom-banner

1개의 댓글

comment-user-thumbnail
2022년 12월 5일

잘 참고하고 갑니다!

답글 달기