[Solidity] call function

bolee·2022년 12월 23일
1

솔리디티(Solidity)

목록 보기
7/8
post-thumbnail
post-custom-banner

call function

  • call()address 데이터 타입 내에 존재하는 메서드로 다른 컨트랙트와 상호작용하는 저수준(low lever) 함수다.
  • call()를 이용해 송금을 하거나 다른 스마트 컨트랙트의 함수를 호출할 수 있다.
  • 가변적인 가스(gas)가 발생한다.
    • send()trasfer()의 경우 2,300 gas를 발생시킨다.
  • 이스탄불 하드포크(2019년 12월) 이후 gas 가격 상승에 따른 다른 스마트 컨트랙트 상호 작용 시 call() 사용을 권장한다.
    • send()trasfer()이 소비하는 2,300 gas 로는 다른 스마트 컨트랙트을 작동하기에는 부족할 수 있기 때문이다.
  • re-entrancy(재진입) 공격 위험이 있기 때문에, Check Effects Interactions Pattern을 사용해야 한다.
  • 유형 검사, 함수 존재 확인 및 인수 패킹을 우회하기 때문에 fallback 함수 호출을 통해서 이더를 보낼경우에 추천하지만, 존재하는 함수를 호출하는 방법으로는 추천하지 않는다.

사용 형태

call() 은 다양한 형태로 사용된다.

call 함수를 이용한 non-payable 함수 호출

function myFunction(uint _x, address _addr) public returns(uint, uint) {
    // do something
    return (a, b);
}

// function signature string should not have any spaces
(bool success, bytes memory result) = addr.call(abi.encodeWithSignature("myFunction(uint,address)", 10, msg.sender));

다른 컨트랙트의 함수를 호출할 때 사용되는 형태이다.
반환값은 함수 호출의 성공, 실패를 반환하는 bool과 호출한 함수의 반환값을 담는 bytes이며, abi.decode()로 변환하여 사용 가능하다.

(bool success, ) = addr.call(abi.encodeWithSignature("myFunction(uint,address)", 10, msg.sender));

호출한 함수의 반환값이 존재하지 않는다면 위와 같이 bytes를 생략해도 된다.

(bool success, bytes memory result) = addr.call("");

call에 전달된 함수가 존재하지 않으면 해당 스마트 컨트랙트의 fallback 함수가 호출된다.
만약, fallback 함수가 구현되지 않았다면, call()는 false를 반환한다.

(bool success, bytes memory result) = addr.call{gas: 1000000}(abi.encodeWithSignature("myFunction(uint,address)", 10, msg.sender));

위와 같이 호출할 함수에서 사용될 가스비를 지정해 줄 수도 있지만, 가스비를 하드코딩하는 것은 권장되지 않는다.
만약 사용할 가스비를 지정하지 않는다면, call() 함수전에 남아 있는 모든 가스가 호출할 함수로 공급된다.

call 함수를 이용한 payable 함수 호출

function myFunction(uint _x, address _addr) public payable returns(uint, uint) {
    // do something
    return (a, b);
}

(bool success, bytes memory result) = addr.call{value: msg.value}(abi.encodeWithSignature("myFunction(uint,address)", 10, msg.sender));

value를 통해 이더를 호출할 수 있다.

(bool success, bytes memory result) = addr.call{value: msg.sender}("");

call에 전달된 함수가 존재하지 않으면 해당 스마트 컨트랙트의 receive 함수가 호출된다.
만약, receive 함수가 구현되지 않았다면 fallback 함수가 호출되고, fallback 함수가 구현되지 않았다면 call()는 false를 반환한다.

(bool success, bytes memory result) = addr.call{value: msg.value, gas: 1000000}(abi.encodeWithSignature("myFunction(uint,address)", 10, msg.sender));

마찬가지로 권장되지 않지만 가스비를 하드코딩하는 것도 가능하다.

예제

아래 컨트랙트를 call()을 이용해 호출하는 예제들이다.

contract callee {
	event JustFallback(string _str);
    event JustReceive(string _str);
    
    function addNumber(uint256 _num1, uint256 _num2) public pure returns (uint256) {
    	return _num1 + _num2;
    }
    
    fallback() external {
    	emit JustFallback("JustFallback is called");
    }
    
    received() external {
    	emit JustReceive("JustReceive is called");
    }
}

call() 이용한 송금

contract callSend {
	function transferEther(address payable _to) public payable {
    	(bool success, ) = _to.call{value: msg.value}("");
        require(sucess, "failed to transfer ether");
    }
}

call() 이용한 외부 컨트랙트 함수 호출

contract callFunction {
	event calledFunction(bool _success, bytes _data);
    
    function callMethod(address _contractAddress, uint256 _num1, uint256 _num2) public {
    	(bool success, btyes memory data) = _contractAddress.call(
        	abi.encodeWithSignature("addNumber(uint256, uint256)", _num1, _num2);
        );
        require(success, "failed to call outer function");
        emit calledFunction(success, data);
    }
}

참고 자료

post-custom-banner

0개의 댓글