IOkrw.mint

mint(address recipient, uint256 amount) external returns (bool)

Mints amount of OKRW (in atokrw, the 18-decimal base denom) and credits it to recipient. Only the authorized minter address configured in the x/okrw module parameters can call this successfully; any other caller reverts with the module-owned UnauthorizedMinter(address caller, address authorizedMinter) custom error. Zero-address recipients and zero amounts revert with the shared IPrecompile.InvalidAddress(string bad) and IPrecompile.InvalidAmount(string amount) typed errors respectively (note: both carry a string payload — the offending address or amount rendered as text — not the raw address/uint256). The source's plain string reverts for a nil or negative amount ("amount must not be nil", "amount must not be negative") cannot be reached: the ABI decoder always yields a non-negative uint256, even from a low-level call.

Parameters

Name Type Required Description
recipient address ✓ The address to receive the newly minted OKRW. Must not be the zero address — passing 0x0 reverts with IPrecompile.InvalidAddress(bad) where bad is the string form of the zero address.
amount uint256 ✓ Amount to mint, denominated in atokrw (18 decimals; 1 OKRW = 10^18 atokrw — see okrw-precompile-overview). Must be strictly greater than zero. A zero amount reverts with IPrecompile.InvalidAmount(amount) where the payload is the decimal string of the amount. The source's nil / negative checks ("amount must not be nil", "amount must not be negative") are unreachable: the ABI decoder always yields a non-negative uint256, even from a low-level call.

Returns

Type: bool

Returns true on success. Failures revert with a typed custom error rather than returning false.

Errors

Code Name Description
UnauthorizedMinter UnauthorizedMinter Reverts when msg.sender is not the configured minter address. Encoded as UnauthorizedMinter(address caller, address authorizedMinter) so clients can display both the offending caller and the currently-authorized minter. Owned by IOkrw.
InvalidAddress InvalidAddress Reverts when recipient is the zero address (or when the sender's address cannot be encoded). Inherited from IPrecompile and encoded as InvalidAddress(string bad) — the payload is the address rendered as a hex string, not the raw address type. Clients previously decoded this as InvalidAddress(address); update ABI fragments to the new string shape.
InvalidAmount InvalidAmount Reverts when amount is exactly zero. Inherited from IPrecompile and encoded as InvalidAmount(string amount) — the payload is the amount rendered as a decimal string, not the raw uint256.
ABISetupFailed ABISetupFailed Reverts when the calldata cannot be decoded against the precompile's ABI. Inherited from IPrecompile as ABISetupFailed(string reason): an unknown 4-byte selector gives the reason no method with id: 0x…, and arguments too short to decode give the decoder's message, for example abi: cannot marshal in to go type: length insufficient 32 require 64. An unknown selector does not revert with UnknownMethod, although IOkrw inherits it. Rarely surfaces from Solidity — usually indicates a mismatched or stale ABI.
InvalidNumberOfArgs InvalidNumberOfArgs Inherited from IPrecompile as InvalidNumberOfArgs(uint256 expected, uint256 got), but mint does not revert with it for a wrong argument count: calldata too short for both arguments reverts with ABISetupFailed, and bytes after amount are ignored.
SDKUnauthorized SDKUnauthorized Inherited from IPrecompile. Surfaces when the underlying chain-layer authorization check rejects the call for a reason other than the minter mismatch (which is reported as UnauthorizedMinter).

Examples

Basic minting from an authorized minter contract

The precompile-side authorization check compares msg.sender to the configured minter, so Treasury's own address must be set as the minter for this call to succeed.

// 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");
    }
}

Catching typed errors on the client with viem (updated error shapes)

After the shared-error refactor InvalidAddress and InvalidAmount are inherited from IPrecompile and carry a single string argument. Update any ABI fragments that previously declared them as InvalidAddress(address) / InvalidAmount(uint256); otherwise decodeErrorResult cannot match the 4-byte selector and the revert surfaces as an unrecognised error.

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;
  }
}

Handling typed errors inside Solidity via try/catch

The 4-byte selector is what drives dispatch, and each of these three selectors is stable. What changed is the ARGUMENT type of InvalidAddress / InvalidAmount — they now decode as string, so any off-chain consumer that ABI-decodes the tail must be updated even though the on-chain selector dispatch keeps working.

// 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)) }
            }
        }
    }
}
ESC
Type to search