try / catch 고급 활용
외부 호출이 실패하는 이유는 한 가지가 아닙니다. 상대가 require/revert("문자열") 로 되돌렸을 수도, 커스텀 error 로 되돌렸을 수도, assert 실패 같은 Panic 이 났을 수도 있습니다. catch 는 이 실패의 종류 에 따라 여러 갈래로 나눠 잡습니다. 덕분에 왜 실패했는지에 맞춰 다르게 대응할 수 있습니다.
catch 의 갈래들
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
interface IFeed {
function getPrice() external returns (uint256);
}
contract Consumer {
event Ok(uint256 price);
event StringError(string reason);
event PanicError(uint256 code);
event LowLevelError(bytes data);
function read(IFeed feed) public {
try feed.getPrice() returns (uint256 price) {
emit Ok(price);
} catch Error(string memory reason) {
// require / revert("...") 처럼 문자열 사유가 있는 실패
emit StringError(reason);
} catch Panic(uint256 code) {
// assert 실패, 0 나눗셈, 배열 범위 초과 등
emit PanicError(code);
} catch (bytes memory data) {
// 그 외 (커스텀 에러 등) 모든 실패를 원시 바이트로
emit LowLevelError(data);
}
}
}각 갈래의 의미
catch Error(string memory reason):revert("...")나require(false, "...")처럼Error(string)으로 인코딩된 실패를 잡고, 그 문자열 사유를 꺼냅니다.catch Panic(uint256 code):assert실패, 0 나눗셈, 배열 범위 초과 등Panic(uint256)으로 인코딩된 내부 오류를 잡고, 원인을 나타내는 코드를 받습니다.catch (bytes memory data): 위에서 잡히지 않은 그 외 모든 실패(대표적으로 커스텀error)를 원시 바이트로 잡습니다. 가장 포괄적인 안전망입니다.
catch 를 나열하는 순서
세분화된 catch(Error, Panic)를 먼저 두고, 포괄적인 catch (bytes ...) 를 맨 아래 두어야 합니다. 그래야 각 실패가 가장 구체적인 갈래에서 처리됩니다.
커스텀 error 로 되돌려진 실패는 Error/Panic 어디에도 걸리지 않고 catch (bytes memory data)
로만 잡힙니다. 그 안에서 선택자를 비교해 어떤 커스텀 에러인지 식별할 수 있습니다.
외부 호출의 실패를 조용히 흡수하면 호출 측은 무엇이 잘못됐는지 모를 수 있습니다. catch 안에서
이벤트를 남기거나 상태를 기록해 추적 가능하게 하세요.