AGENT_OKRW_TRANSFER_LIMIT_POLICY

component compliance

Per-transaction OKRW transfer cap for agent wallets — the limit is read from the agent's on-chain TransferLimit metadata, and applies both when the agent is the direct sender and (globally) when the agent is the initiating principal.

Caps a single OKRW transfer originating from an agent wallet using a limit stored as on-chain metadata on the ERC-8004 IdentityRegistry — specifically getMetadata(agentId, "TransferLimit"). The policy struct itself has no configurable fields; the cap is whatever the agent's owner has written into the metadata slot. The evaluator attributes the transfer to the agent both when the agent is the direct msg.sender and, under global-scope policies, when the agent is the initiating principal forwarded by the PCL proxy hook — so a contract that pulls funds from an agent principal still hits the same cap. If the resulting attributed value exceeds the configured TransferLimit, the transaction reverts with ExceededAgentTransferLimit(maxLimit, value).

Parameter struct

The template has no user-configurable parameters. Solidity disallows empty structs, so AgentOkrwTransferLimitPolicy carries a single reserved field that the chain ignores. Encode zero and move on:
import { AgentOkrwTransferLimitPolicy, PolicySet } from "@maroo-chain/contracts/precompiles/pcl/IPcl.sol";

AgentOkrwTransferLimitPolicy memory p = AgentOkrwTransferLimitPolicy({ reserved: 0 });

PolicySet memory ps = PolicySet({
    templateId: "AGENT_OKRW_TRANSFER_LIMIT_POLICY",
    policy: abi.encode(p),
    selector: ""
});

Where the limit comes from

The cap is not stored on the PolicySet; it is stored per-agent on the ERC-8004 IdentityRegistry preinstall at 0x8004000000000000000000000000000000000001. Look up the numeric string keyed by "TransferLimit" — the value is an OKRW amount denominated in aokrw (18-decimal base units, so 10,000,000 OKRW = 10000000000000000000000000 aokrw). The agent's owner writes it via the IdentityRegistry's metadata setter. If the metadata slot is missing or invalid, the evaluator reverts with the plain string reason "invalid agent transfer limit metadata" (or "invalid agent transfer limit metadata: <reason>") — which is not a decodable typed error.

Who is attributed the transfer

The evaluator runs in the after-execution stage and inspects each OKRW transfer produced by the transaction. It attributes the transfer to the agent in two cases:

  • Direct sender: the agent wallet itself calls a function that moves OKRW out (typical MAWS flow).
  • Global-scope principal: a contract performs the outbound transfer, but the PCL proxy hook has forwarded the initiating principal (the wallet that entered the regulated path). When that principal is the agent, the global-scope evaluation still attributes the transfer to the agent.


Under contract-scope evaluation, only the direct sender attribution applies — contract-scoped policies do not re-attribute to the principal for this template.

Failure semantics

If the attributed value exceeds TransferLimit, the transaction reverts with the typed custom error ExceededAgentTransferLimit(uint256 maxLimit, uint256 value). Decode it client-side to display the offending amount and the configured cap:
import { decodeErrorResult } from "viem";
import { pclAbi } from "./abis";

try {
  await wallet.writeContract({ /* agent-initiated OKRW transfer of 10_000_000 OKRW */ });
} catch (err: any) {
  if (err?.data) {
    const decoded = decodeErrorResult({ abi: pclAbi, data: err.data });
    if (decoded.errorName === "ExceededAgentTransferLimit") {
      const [maxLimit, value] = decoded.args as [bigint, bigint];
      console.error(`agent transfer limit exceeded: max=${maxLimit} value=${value}`);
    }
  }
}

Composing with other caps

This template only reads agent metadata; it does not evaluate attestations, denylists, or periodic windows. For a full compliance surface, layer it with other templates in the same PolicyConfig.policies array — for example, add DENYLIST_POLICY to block sanctioned counterparties and OKRW_EAS_PERIODIC_VOLUME_LIMIT_POLICY to enforce a 10,000,000 OKRW / 24 h regulatory reporting threshold on un-attested transfers. Because each PolicySet is evaluated independently, a single agent-initiated transfer of 10,000,000 OKRW can be rejected either by its agent metadata cap or by an unrelated periodic volume cap — whichever fires first.
ESC
Type to search