[멋쟁이 사자처럼 블록체인 스쿨 3기] 23-05-15

임형석·2023년 5월 15일
0

Solidity


require

솔리디티에서는 에러 핸들링을 위해 require 라는 함수를 사용한다.

require 함수로 문제가 되거나 의도하지 않은 상황을 제어할 수 있다.

require(bool, string memory) 의 input 만 지원한다.

다음의 예시 코드를 보자.

    uint a = 1;

    function require1() public {
        bool c;
        a = 5;
        require(c, "error");
    }

예시 코드에서는, 정수형 상태변수 a 를 1로 설정했다.

그리고 함수 require1 을 실행시키면 어떻게 될까?

일단 bool 값은 0 이므로 false 이고, a 를 5로 바꿔주었다.

그리고 require 함수가 c 가 true 인지 판단하는데,

c 는 false 이므로 "error" .

"error" 라는 이유를 반환하며 revert 되었다고 알려준다.

그렇다면, 직접 상태변수를 수정하는 것 말고 다른 함수를 호출해서 사용하는 경우는 어떻게 될까?

	function getA() public view returns(uint) {
        return a;
    }
	
    function setAfive() public {
        a = 5;
    }

    function require2() public {
        bool c;
        setAfive();
        require(c, "error");
    }

이렇게 require2 함수에서 setAfive 함수를 호출해서 사용하는데,

"error" 라는 함수를 반환하며 initial state 로 돌아가게 된다.

결론은, 상태변수를 직접 변경하거나 다른 함수를 호출하는 경우에도 되돌린다.

require 의 조건을 2개를 만든다거나,

if 문안에 require 를 사용하는 응용도 가능하다.

    // require 조건 2개
    function Require3(uint _n) public pure returns(bool) {
        require(_n%3==0 && _n>12, "Nope");
        return true;
    }

    // if문 안의 require
    function Require4(uint _a) public pure returns(uint) {
        if(_a%3==0) {
            require(_a%3!=0, "nope"); 
        } else if(_a%3==1) {
            return _a%3;
        } else {
            return _a%3;
        }
    }

constructor

constructor 는 생성자로, 컨트랙트를 배포 하자마자 실행되는 특별한 함수이다.

쉽게 생각하면 컨트랙트의 배포와 동시에 초깃값을 설정하는 함수라고 볼 수 있겠다.

input 값은 없으며, constructor 에는 if, payable, require 등의 문법을 사용할 수 있다.

constructor() payable { 
        payable(this).transfer(msg.value); // 배포할 때 msg.value 만큼 contract 에게 입금
        owner = payable(msg.sender); // 배포하는 지갑 주소가 바로 owner 로 설정
    }

위와 같이 owner 라는 변수를 컨트랙의 배포와 동시에 선언하면서, owner 만이 이 컨트랙트를 관리하게 만들 수 있다.

아래 코드처럼 require 함수와 같이 사용해, 이 함수의 실행자(msg.sender)가 owner(컨트랙트의 배포자) 일때만 두번째 줄의 코드를 실행한다.

function withdraw() public {
        require(msg.sender == owner, "owner only");
        owner.transfer(address(this).balance);
    }

여기서 헷갈리는 부분이 있었는데.

  1. address(this) : 컨트랙트의 주소를 불러옴.
  2. .balance : 지갑이 보유한 밸런스.
  3. msg.value : remix 에서 아래를 뜻함.

그래서 construct 함수를 이용해서 컨트랙트를 배포하자마자 컨트랙트에 돈을 보낼 수 있었다.


modifier

modifier 는 에러핸들러 require 와 같이 쓰이는 경우가 많다. 그리고 다른 함수에 들어가 이 modifier 를 실행이 가능하다.

modifier 는 매개변수가 있어도, 없어도 문제가 없다.

_; 는 다른 함수가 실행될 곳을 지정해주는데, 아래 코드에서 require 의 윗줄이나 아랫줄, 또는 두줄에 여러번 혹은 모두 쓰일 수도 있다.

다른 함수에서 modifier 를 사용할 땐, 함수의 속성을 설정하는 부분에 사용할 modifier 의 이름을 입력해주면 된다.

modifier 이름(_매개변수){
	require();
    _;
}

function a() modifier 이름 public {
	b;
}

_; 여기에서 함수가 실행된다고 했는데, 어떻게 실행되는지 해보았다.

a 를 정수 5의 상태변수로 선언하고

require 함수의 위 아래 함수를 두 번씩 실행하는 modifier 를 선언했다.

그리고 plusA() 함수에서 checkA modifier 를 가져와 실행한다.

contract checkModifier{

    uint a = 5;

    modifier checkA {
        a++;
        a++;
        require(a >= 5, "please check A value.");
        a++;
        a++;
   }
   
    function plusA() checkA public {
        a++;
    }

    function getA() public view returns(uint){
        return a;
    }
}

plusA() 함수를 실행한 결과는 9이다.

함수가 실행되는 순서를 보면 다음과 같다.

  1. getA() 실행
  2. checkA 첫번째(plusA) 함수 실행, a=6
  3. checkA 두번째(plusA) 함수 실행, a=7
  4. require. a >= 5 확인
  5. checkA 세번째(plusA) 함수 실행, a=8
  6. checkA 네번째(plusA) 함수 실행, a=9
  7. a 값 9 반환, 종료.

조금 더, 직관적으로 코드를 해석해보면 이렇게도 생각할 수 있다.

uint a=5;

modifier checkA {
        a++;
        a++;
        require(a >= 5, "please check A value.");
        a++;
        a++;
   }

만약 함수를 실행하는 중, require 를 통과하지 못한다면 어떻게 될까?

원래 require 를 통과하지 못한다면, 모든 값은 함수를 실행하기 이전인 initial state 로 돌아가게 된다. 가스비도 소모되지 않는다.

modifier 를 이용한 다른 함수의 안에서도 마찬가지일까?

이렇게 a를 6으로 선언하고, minusA 를 실행해보았다.

contract checkModifier{

    uint a = 6;

    modifier checkA {
        _;
        require(a > 5, "please check A value.");
        _;
    }

    function minusA() checkA public {
        a--;
    }

    function getA() public view returns(uint){
        return a;
    }
}

아래 사진처럼 경고 문구가 뜬다. require 조건을 통과하지 못했고, 값은 initial state 상태로 돌아간다고 되어있다.

modifier checkA {
        require(a > 5, "please check A value.");
        _;
    }

이렇게 require 윗줄을 지우고 실행시키면, 정상적으로 실행된다.

그리고 minusA 함수가 실행되면서 a 값이 5로 바뀌게 된다.

이렇게 require 를 약간 우회하는 느낌으로 사용할 수도 있을 것 같다.

이렇게 modifier 함수 사용 방법을 알아보았고, 응용에는 require 와 함께 에러핸들링 할때가 제일 적합할 것 같다.

0개의 댓글