OKRW Precompile

component core

Native-token mint surface for the KRW-pegged stablecoin (OKRW). Single permissioned method: mint(address, uint256).

The OKRW precompile is one of Maroo's core native precompiles, deployed at the fixed address 0x1000000000000000000000000000000000000001. It exposes a single permissioned method — mint — that the configured minter address uses to issue new OKRW. Transfers, balance reads, and allowances are NOT on this precompile: OKRW is the native gas token, so those actions use the standard EVM value semantics (msg.value, address.balance, eth_getBalance). The interface IOkrw is IPrecompile, so every shared error inherited from IPrecompile (invalid argument counts, unknown methods, SDK-level errors, EVM boundary errors) may also surface from this precompile in addition to the module-owned UnauthorizedMinter.

Architecture

graph TD
    A[dApp / Designated Minter] -- "mint(to, amount)" --> B[IOkrw Precompile @ 0x10...01];
    B -- Mint event --> C[Indexers / Wallets];
    A2[Any dApp] -- "getParams()" --> B;

    classDef evm fill:#0096AA,stroke:#0096AA,color:#fff;
    classDef precompile fill:#FF8C50,stroke:#FF8C50,color:#fff;
    class A,C,A2 evm;
    class B precompile;

The designated minter calls mint on the IOkrw precompile, which emits a Mint event; getParams exposes the configured minter and mint denom. OKRW then moves as native value like any EVM balance.

Interface at a glance

The full Solidity interface fits on one screen. Note the is IPrecompile inheritance — that is where every shared error such as InvalidAddress(string), InvalidAmount(string), UnknownMethod, and InvalidNumberOfArgs comes from.
import {IPrecompile} from "@maroo-chain/contracts/precompiles/common/interfaces/IPrecompile.sol";

interface IOkrw is IPrecompile {
    error UnauthorizedMinter(address caller, address authorizedMinter);

    function getParams() external view returns (OkrwParams memory params);
    function mint(address recipient, uint256 amount) external returns (bool);

    event Mint(address indexed minter, address indexed recipient, uint256 amount);
}

struct OkrwParams {
    address minter;
    string mintDenom;
}

Authorization model

Only the address stored in OkrwParams.minter (a chain-level parameter rotated through governance) may call mint. Any other caller reverts with the module-owned UnauthorizedMinter(caller, authorizedMinter) custom error, which carries both the offending caller and the currently-authorized minter for observability. Rotating the minter is a consortium-governance action and is out of scope for dApp integrations — the minter address is stable at runtime.
// Read the currently-authorized minter at runtime.
OkrwParams memory params = IOkrw(0x1000000000000000000000000000000000000001).getParams();
address minter = params.minter;      // authorized minter
string memory denom = params.mintDenom; // "aokrw"

Error surface after the shared-error refactor

The precompile no longer declares its own InvalidAddress(address) / InvalidAmount(uint256) errors. Those two revert with IPrecompile.InvalidAddress(string bad) and IPrecompile.InvalidAmount(string amount) respectively — the payload is a string rendering of the offending value, not the raw type. Beyond those, the inherited errors that may surface from a mint call include InvalidNumberOfArgs, UnknownMethod, SDKUnauthorized, and EventEmitFailed. See precompile-shared-errors for the full catalog and decoding notes. Nil or negative amount inputs (only reachable via low-level ABI abuse) revert as plain strings ("amount must not be nil", "amount must not be negative") and are not decodable typed errors.
// Minimal ABI to decode any revert from OKRW.mint after the refactor.
const okrwErrorsAbi = [
  { 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: "InvalidNumberOfArgs",
    inputs: [{ name: "expected", type: "uint256" }, { name: "got", type: "uint256" }] },
  { type: "error", name: "UnknownMethod", inputs: [{ name: "methodName", type: "string" }] },
  { type: "error", name: "SDKUnauthorized", inputs: [] },
] as const;

Denomination

amount is expressed in aokrw, the 18-decimal base denom of OKRW (1 OKRW = 10^18 aokrw). Minting 10,000,000 OKRW is therefore 10_000_000 10*18 in the call. The mintDenom field of OkrwParams reports the on-chain denom string ("aokrw" in production) — use it if a component needs to compose an operation on the non-EVM value layer.
import {IOkrw} from "@maroo-chain/contracts/precompiles/okrw/IOkrw.sol";

IOkrw okrw = IOkrw(0x1000000000000000000000000000000000000001);
address recipient = 0x8F3ac2B1d9E74c05A6B18FE27Dc4913e5A0F7b62;

// 10,000,000 OKRW in aokrw base units.
uint256 amount = 10_000_000 ether; // 10_000_000 * 10**18
okrw.mint(recipient, amount);

Event emission

A successful mint emits Mint(address indexed minter, address indexed recipient, uint256 amount) as a normal EVM log. Indexers can subscribe with eth_getLogs filtered by the precompile address. If the internal event emission fails after the state change (rare — infrastructure fault), the precompile reverts with the inherited EventEmitFailed(string eventKind, string reason) so the whole call is rolled back atomically.
ESC
Type to search