IOkrw.mint
mint(address recipient, uint256 amount) external returns (bool) amount만큼의 OKRW를 발행하여 recipient에게 입금합니다. 금액 단위는 소수점 18자리 base denom인 atokrw입니다. x/okrw 모듈 파라미터에 지정된 발행자 주소만 호출에 성공하며, 그 외 호출자는 모듈 소유 커스텀 오류 UnauthorizedMinter(address caller, address authorizedMinter)로 revert됩니다. recipient가 0 주소이거나 amount가 0이면 공통 오류 IPrecompile.InvalidAddress(string bad)와 IPrecompile.InvalidAmount(string amount)로 각각 revert됩니다. 두 오류 모두 페이로드가 address 또는 uint256이 아니라 문자열로 표현된 값이라는 점에 주의합니다. 소스에는 amount가 nil이거나 음수일 때의 일반 문자열 revert("amount must not be nil", "amount must not be negative")도 있지만, ABI 디코더는 저수준 호출에서도 항상 음수가 아닌 uint256을 돌려주므로 이 경로에는 도달하지 않습니다.
파라미터
| 이름 | 타입 | 필수 | 설명 |
|---|---|---|---|
recipient | address | ✓ | 새로 발행된 OKRW를 받을 주소입니다. 0 주소여서는 안 되며, 0x0을 전달하면 IPrecompile.InvalidAddress(bad)로 revert됩니다. bad는 0 주소를 문자열로 표현한 값입니다. |
amount | uint256 | ✓ | 발행할 수량이며 atokrw(소수점 18자리, 1 OKRW = 10^18 atokrw — okrw-precompile-overview 참고) 단위입니다. 반드시 0보다 커야 합니다. 0을 전달하면 IPrecompile.InvalidAmount(amount)로 revert되며 페이로드는 금액을 10진수 문자열로 표현한 값입니다. 소스의 nil·음수 검사("amount must not be nil", "amount must not be negative")에는 도달하지 않습니다. ABI 디코더는 저수준 호출에서도 항상 음수가 아닌 uint256을 돌려줍니다. |
반환값
bool 성공 시 true를 반환합니다. 실패는 false 반환이 아니라 커스텀 오류로 revert됩니다.
에러
| 코드 | 이름 | 설명 |
|---|---|---|
UnauthorizedMinter | UnauthorizedMinter | msg.sender가 지정된 발행자 주소가 아닐 때 revert됩니다. UnauthorizedMinter(address caller, address authorizedMinter) 형태로 인코딩되어 있어, 클라이언트가 실패한 호출자와 현재 지정된 발행자를 함께 표시할 수 있습니다. IOkrw가 소유하는 오류입니다. |
InvalidAddress | InvalidAddress | recipient가 0 주소일 때(또는 발신자 주소를 인코딩할 수 없을 때) revert됩니다. IPrecompile에서 상속되며 InvalidAddress(string bad) 형태로 인코딩됩니다. 페이로드는 address 타입이 아니라 16진수 문자열로 표현된 주소입니다. 이전에는 InvalidAddress(address)로 디코딩했으므로 ABI 조각을 새 string 형태로 갱신합니다. |
InvalidAmount | InvalidAmount | amount가 정확히 0일 때 revert됩니다. IPrecompile에서 상속되며 InvalidAmount(string amount) 형태로 인코딩됩니다. 페이로드는 uint256이 아니라 금액을 10진수 문자열로 표현한 값입니다. |
ABISetupFailed | ABISetupFailed | calldata를 프리컴파일 ABI로 디코딩할 수 없을 때 revert됩니다. IPrecompile의 ABISetupFailed(string reason)을 상속합니다. 알 수 없는 4바이트 selector이면 사유가 no method with id: 0x…이고, 인자가 짧아 디코딩할 수 없으면 디코더의 메시지(예: abi: cannot marshal in to go type: length insufficient 32 require 64)가 사유가 됩니다. IOkrw가 UnknownMethod도 상속하지만, 알 수 없는 selector는 이 오류로 revert되지 않습니다. Solidity에서 노출되는 경우는 드물며, 보통 ABI가 불일치하거나 오래된 상황을 의미합니다. |
InvalidNumberOfArgs | InvalidNumberOfArgs | IPrecompile의 InvalidNumberOfArgs(uint256 expected, uint256 got)을 상속하지만, mint는 인자 개수가 틀려도 이 오류로 revert되지 않습니다. calldata가 두 인자를 담기에 짧으면 ABISetupFailed로 revert되고, amount 뒤에 붙은 바이트는 무시됩니다. |
SDKUnauthorized | SDKUnauthorized | IPrecompile에서 상속됩니다. 하부 체인 계층의 권한 검사가 발행자 불일치(별도의 UnauthorizedMinter로 보고) 외의 이유로 호출을 거절할 때 노출됩니다. |
예제
지정된 발행자 컨트랙트에서 기본 발행
프리컴파일 측 권한 검사가 msg.sender와 지정된 발행자를 비교하므로, 이 호출이 성공하려면 Treasury 자신의 주소가 발행자로 지정되어 있어야 합니다.
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.28;
import "@maroo-chain/contracts/precompiles/okrw/IOkrw.sol";
import {IPrecompile} from "@maroo-chain/contracts/precompiles/common/interfaces/IPrecompile.sol";
contract Treasury {
address constant OKRW_PRECOMPILE = 0x1000000000000000000000000000000000000001;
IOkrw okrw = IOkrw(OKRW_PRECOMPILE);
/// @notice Mint OKRW to `to`. This contract's address must be the
/// authorized minter in the x/okrw module parameters.
function mintTo(address to, uint256 amount) external {
// 10_000_000 OKRW == 10_000_000 * 10**18 atokrw when caller passes ether units.
bool ok = okrw.mint(to, amount);
require(ok, "OKRW mint returned false");
}
} viem으로 클라이언트에서 커스텀 오류 처리 (갱신된 오류 형태)
공유 오류 리팩터링 이후 InvalidAddress와 InvalidAmount는 IPrecompile에서 상속되며 단일 string 인자를 가집니다. 이전에 InvalidAddress(address) / InvalidAmount(uint256)로 선언한 ABI 조각을 갱신해야 합니다. 그렇지 않으면 decodeErrorResult가 4바이트 selector를 매칭하지 못하고 revert가 인식되지 않은 오류로 노출됩니다.
import { BaseError, ContractFunctionRevertedError, createWalletClient, http, parseEther, decodeErrorResult } from "viem";
import { privateKeyToAccount } from "viem/accounts";
const OKRW = "0x1000000000000000000000000000000000000001" as const;
// NOTE: InvalidAddress / InvalidAmount now carry a `string` payload, not
// the old `address` / `uint256` — they come from IPrecompile after the
// shared-error refactor. UnauthorizedMinter is still owned by IOkrw.
const okrwAbi = [
{ type: "function", name: "mint", stateMutability: "nonpayable",
inputs: [{ name: "recipient", type: "address" }, { name: "amount", type: "uint256" }],
outputs: [{ type: "bool" }] },
{ type: "error", name: "UnauthorizedMinter",
inputs: [{ name: "caller", type: "address" }, { name: "authorizedMinter", type: "address" }] },
{ type: "error", name: "InvalidAddress", inputs: [{ name: "bad", type: "string" }] },
{ type: "error", name: "InvalidAmount", inputs: [{ name: "amount", type: "string" }] },
{ type: "error", name: "ABISetupFailed", inputs: [{ name: "reason", type: "string" }] },
] as const;
const wallet = createWalletClient({
account: privateKeyToAccount(process.env.MINTER_KEY as `0x${string}`),
transport: http("https://rpc-testnet.maroo.io"),
});
try {
await wallet.writeContract({
address: OKRW,
abi: okrwAbi,
functionName: "mint",
// TODO: replace with the real treasury recipient before production.
args: ["0x8F3ac2B1d9E74c05A6B18FE27Dc4913e5A0F7b62", parseEther("10000000")],
});
} catch (err) {
// viem keeps the revert data on the ContractFunctionRevertedError in the cause chain, not on err.
const revert = err instanceof BaseError ? err.walk((e) => e instanceof ContractFunctionRevertedError) : null;
if (revert instanceof ContractFunctionRevertedError && revert.raw) {
const decoded = decodeErrorResult({ abi: okrwAbi, data: revert.raw });
// decoded.errorName is one of UnauthorizedMinter | InvalidAddress | InvalidAmount,
// or ABISetupFailed for calldata the precompile cannot decode (e.g. a stale ABI).
console.error(`OKRW.mint reverted: ${decoded.errorName}`, decoded.args);
} else {
// Not a contract revert (network, nonce, signing): rethrow.
throw err;
}
} Solidity의 try/catch로 커스텀 오류 처리
분기의 근거인 4바이트 selector는 여전히 안정적입니다. 달라진 부분은 InvalidAddress / InvalidAmount의 인자 타입으로, 이제 string으로 디코드됩니다. 온체인 selector 분기는 그대로 동작하지만, 페이로드를 ABI 디코드하는 오프체인 소비자는 갱신이 필요합니다.
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.28;
import "@maroo-chain/contracts/precompiles/okrw/IOkrw.sol";
import {IPrecompile} from "@maroo-chain/contracts/precompiles/common/interfaces/IPrecompile.sol";
// InvalidAddress / InvalidAmount are INHERITED from IPrecompile, so they are
// referenced through IPrecompile — IOkrw.InvalidAddress does not compile.
contract SafeMinter {
IOkrw constant okrw = IOkrw(0x1000000000000000000000000000000000000001);
event MintFailed(bytes4 selector);
function safeMint(address to, uint256 amount) external {
try okrw.mint(to, amount) returns (bool ok) {
require(ok, "mint returned false");
} catch (bytes memory raw) {
bytes4 sel;
assembly { sel := mload(add(raw, 32)) }
if (sel == IOkrw.UnauthorizedMinter.selector) {
emit MintFailed(sel);
} else if (sel == IPrecompile.InvalidAddress.selector) {
// Now InvalidAddress(string bad) — inherited from IPrecompile.
emit MintFailed(sel);
} else if (sel == IPrecompile.InvalidAmount.selector) {
// Now InvalidAmount(string amount) — inherited from IPrecompile.
emit MintFailed(sel);
} else {
// Unknown revert — bubble up.
assembly { revert(add(raw, 32), mload(raw)) }
}
}
}
}