Low-level call이 revert되었을 때 retun 값

frenchkebab·2023년 5월 16일
0

Web3

목록 보기
1/8
post-thumbnail

low-level call은 보통 다음과 같이 작성된다.

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

data에는 bytes의 형태로 return된 값이 저장되게 된다.

근데 만약 revert가 나면 어떻게 될까?

정상적으로 return하는 경우

contract Caller {
    function caller(address _target) external {
        (bool success, bytes memory data) = _target.call(abi.encodeWithSignature("reverter()"));
        console.log(success);
        console.log(string(data));
    } 
}

contract Callee {
    function reverter() external returns(string memory) {
        return "Return Value";
    }
}
true
Return Value

revert되는 경우

contract Caller {
    function caller(address _target) external {
        (bool success, bytes memory data) = _target.call(abi.encodeWithSignature("reverter()"));
        console.log(success);
        console.logBytes(data);
    } 
}

contract Callee {
    function reverter() external returns(string memory) {
        return "Return Value";
    }
}
false
0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000e526576657274206d657373616765000000000000000000000000000000000000

뭔가 return이 되기는 한다. 한번 분석해보자.

08c379a0 // bytes4(keccak256("Error(string)"))
0000000000000000000000000000000000000000000000000000000000000020 // location
000000000000000000000000000000000000000000000000000000000000000e // length
526576657274206d657373616765000000000000000000000000000000000000 // "Revert message"

bytes 혹은 string 타입의 경우 calldata가 저렇게 3등분으로 나누어지게 된다.

revert string만 뽑아내기

(bool success, bytes memory data) = address(target).delegatecall(callee);
if (success == false) {
    assembly {
        let ptr := mload(0x40) // memory pointer
        let size := returndatasize() // data 길이
        returndatacopy(ptr, 0, size) // ptr에 data 길이 0~size를 store
        revert(ptr, size) // ptr부터 size만큼 길이를 revert
    }
}
return result;

이렇게 low-level call에서 발생한 revert sring을 받아와서,
high-level call에서의 revert처럼 만들 수 있다.

(하지만 보통은 그냥 require(success, "Call failed") 와 같이 처리한다)

assembly에 대해서는 https://www.evm.codes/ 를 참고하면 자세히 알 수 있다.

profile
Blockchain Dev Journey

0개의 댓글