OKRW 프리컴파일 오류 처리 방법
Solidity 스마트 컨트랙트와 Ethers.js 기반 클라이언트 애플리케이션에서 OKRW 프리컴파일의 사용자 정의 오류를 효과적으로 처리하는 방법을 안내합니다.
사전 요구사항
- Solidity
try/catch에 대한 기본 지식. - JavaScript
try/catch와 Ethers.js에 익숙할 것.
온체인: Solidity에서 오류 처리
Solidity의
try/catch 문은 스마트 컨트랙트에서 외부 호출 오류를 처리하는 핵심 수단입니다. 로우 레벨 catch (bytes memory reason) 절을 만들어 원시 revert 데이터를 검사할 수 있습니다. 이어서 4바이트 선택자로 어떤 사용자 정의 오류인지 식별하고 인수를 디코딩합니다.// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.18;
import "./IOkrw.sol";
contract ErrorHandler {
IOkrw constant okrw = IOkrw(0x1000000000000000000000000000000000000001);
event MintAttemptFailed(string reason);
event Unauthorized(address caller, address expected);
function attemptMint(address recipient, uint256 amount) external {
try okrw.mint(recipient, amount) {
// 성공 시 로직
} catch (bytes memory reason) {
// bytes memory slicing isn't supported in Solidity, so use assembly
// to extract the 4-byte selector, then manually copy bytes for abi.decode.
bytes4 selector;
assembly { selector := mload(add(reason, 32)) }
if (selector == IOkrw.UnauthorizedMinter.selector) {
bytes memory args = new bytes(reason.length - 4);
for (uint i = 0; i < args.length; i++) args[i] = reason[i + 4];
(address caller, address authorizedMinter) = abi.decode(args, (address, address));
emit Unauthorized(caller, authorizedMinter);
} else if (selector == IOkrw.InvalidAddress.selector) {
bytes memory args = new bytes(reason.length - 4);
for (uint i = 0; i < args.length; i++) args[i] = reason[i + 4];
(address invalidAddr) = abi.decode(args, (address));
emit MintAttemptFailed("Invalid address provided");
} else {
revert("Unknown precompile error");
}
}
}
} 참고: 로우 레벨 `catch`를 사용하면 부모 함수의 실행을 중단하지 않고 다양한 오류 유형에 다르게 대응할 수 있어 제어 폭이 가장 넓습니다.
오프체인: Ethers.js로 오류 처리
프론트엔드나 백엔드에서 프리컴파일을 호출하면 Ethers.js 같은 라이브러리가 revert 시 예외를 발생시킵니다. 사용자 정의 오류 데이터는 잡힌 객체의
error.data 필드에서 사용할 수 있습니다. 컨트랙트의 ABI 인터페이스로 이 데이터를 오류 이름과 인수를 담은 가독성 있는 객체로 파싱할 수 있습니다.const { ethers } = require("ethers");
const okrwPrecompileAddress = "0x1000000000000000000000000000000000000001";
const okrwAbi = [
"function mint(address recipient, uint256 amount) external returns (bool)",
"error UnauthorizedMinter(address caller, address authorizedMinter)",
"error InvalidAddress(address addr)"
];
// 'unauthorizedSigner'가 공인된 발행자가 아닌 계정의 ethers.Signer라고 가정합니다.
const okrwContract = new ethers.Contract(okrwPrecompileAddress, okrwAbi, unauthorizedSigner);
async function main() {
try {
await okrwContract.mint("0xRecipientAddress...", ethers.parseEther("1"));
} catch (e) {
if (e.data) {
const decodedError = okrwContract.interface.parseError(e.data);
console.log(`Transaction failed with custom error: ${decodedError.name}`);
if (decodedError.name === 'UnauthorizedMinter') {
const [caller, authorizedMinter] = decodedError.args;
console.log(`Caller ${caller} is not the authorized minter ${authorizedMinter}.`);
}
} else {
console.error("An unexpected error occurred:", e.message);
}
}
}
main();