Agent Precompile

component identity

Discovery + indexed reverse lookup — resolves the ERC-8004 IdentityRegistry / ReputationRegistry addresses and answers getAgentIds(wallet); failures surface through the shared IPrecompile error set.

The Agent precompile at 0x100000000000000000000000000000000000000A is a thin view-only surface. It exposes getParams() (returning the ERC-8004 identityRegistry and reputationRegistry addresses this chain resolves) and getAgentIds(wallet, pageRequest) (a paginated reverse lookup from a wallet to its agent IDs, read from a chain-side index). All other agent operations — register, attest, revoke, metadata — go directly to the ERC-8004 IdentityRegistry preinstall at 0x8004000000000000000000000000000000000001.

The Solidity interface

Straight from IAgent.sol. Note that IAgent inherits IPrecompile, so it does not declare its own error types — every failure uses the shared surface described below.
import {PageRequest, PageResponse} from "cosmos-evm-contracts/precompiles/common/Types.sol";
import {IPrecompile} from "../common/interfaces/IPrecompile.sol";

interface IAgent is IPrecompile {
    struct Params {
        address identityRegistry;
        address reputationRegistry;
    }

    function getParams() external view returns (Params memory);

    function getAgentIds(address wallet, PageRequest calldata pageRequest)
        external
        view
        returns (uint256[] memory agentIds, PageResponse calldata pageResponse);
}

Why the precompile exists

Two problems it solves. First, dApps should not hard-code per-network addresses for the ERC-8004 registries — getParams() returns them at runtime. Second, iterating IdentityRegistry storage to find every agent belonging to a wallet is prohibitively expensive on-chain; the precompile reads from a chain-side reverse index that is maintained on every agent registration and transfer. That is the sole reason getAgentIds lives on a precompile at all — every other agent operation calls the standard registry directly.

Error surface — shared with all Maroo precompiles

IAgent inherits IPrecompile. Argument-shape failures surface as typed errors from the shared set: InvalidNumberOfArgs(uint256 expected, uint256 got) for wrong arity, InvalidAddress(string bad) for a non-address in the wallet slot, InvalidPageRequest(string method, uint256 index, string value) for a malformed pagination tuple, and UnknownMethod(string methodName) for an unrecognized selector. Underlying chain-index failures surface as QueryFailed(string queryMethod, string reason), normalized through the shared SDK error registry when the reason maps to a known SDK code. See precompile-shared-errors for the full list — a single ABI covers every revert shape.
import { decodeErrorResult } from "viem";

const iPrecompileErrorsAbi = [
  { type: "error", name: "InvalidNumberOfArgs",
    inputs: [{ name: "expected", type: "uint256" }, { name: "got", type: "uint256" }] },
  { type: "error", name: "InvalidAddress", inputs: [{ name: "bad", type: "string" }] },
  { type: "error", name: "InvalidPageRequest",
    inputs: [
      { name: "method", type: "string"  },
      { name: "index",  type: "uint256" },
      { name: "value",  type: "string"  },
    ] },
  { type: "error", name: "QueryFailed",
    inputs: [{ name: "queryMethod", type: "string" }, { name: "reason", type: "string" }] },
  { type: "error", name: "UnknownMethod", inputs: [{ name: "methodName", type: "string" }] },
] as const;

try {
  // await publicClient.readContract({ ... })
} catch (err: any) {
  const decoded = decodeErrorResult({ abi: iPrecompileErrorsAbi, data: err.data });
  console.error(decoded.errorName, decoded.args);
}

Reading IdentityRegistry after resolving addresses

Once getParams() gives you the IdentityRegistry address, all other operations use it directly — the precompile never re-exposes registration, transfer, or metadata calls. Combine the two calls to page through a wallet's agent IDs and then hydrate each one from the registry.
import { createPublicClient, http } from "viem";

const AGENT_PRECOMPILE = "0x100000000000000000000000000000000000000A" as const;
const agentPrecompileAbi = [
  { type: "function", name: "getParams", stateMutability: "view",
    inputs: [],
    outputs: [{ type: "tuple", components: [
      { name: "identityRegistry",   type: "address" },
      { name: "reputationRegistry", type: "address" },
    ]}] },
] as const;

const publicClient = createPublicClient({ transport: http("https://rpc-testnet.maroo.io") });
const { identityRegistry } = await publicClient.readContract({
  address: AGENT_PRECOMPILE,
  abi: agentPrecompileAbi,
  functionName: "getParams",
});
// Now call `identityRegistry` directly for register / transfer / metadata.
console.log("IdentityRegistry:", identityRegistry);
ESC
Type to search