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) {
bytes4 selector = bytes4(reason);
if (selector == IOkrw.UnauthorizedMinter.selector) {
(address caller, address authorizedMinter) = abi.decode(reason[4:], (address, address));
emit Unauthorized(caller, authorizedMinter);
} else if (selector == IOkrw.InvalidAddress.selector) {
(address invalidAddr) = abi.decode(reason[4:], (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();